From 3854c3a0bd5e4e9c12a20a5779ec337c7b6c9c46 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 11:17:52 +0200 Subject: [PATCH 001/150] Initiales Repo mit .gitignore --- .gitignore | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6010df8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Build +build/ +build-*/ +dist/ + +# Lokale Konfiguration (WLAN, Token, Signier-Key) +config.local.cmake +*.pem + +# Specs und Pläne bleiben lokal +docs/superpowers/ + +# Python +__pycache__/ +*.pyc +.venv/ +.pytest_cache/ + +.DS_Store From 23df6b9d29b9ccc07d8b9136f9e64c7fea5564f5 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 12:19:33 +0200 Subject: [PATCH 002/150] build: .superpowers/ ignorieren --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6010df8..6b87ed4 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ __pycache__/ .pytest_cache/ .DS_Store + +# Arbeitsdateien der Subagent-Ausführung +.superpowers/ From 4e7ff78fc658c5c11dde72d4fe817746420f61dc Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 12:23:04 +0200 Subject: [PATCH 003/150] test: Host-Testumgebung mit Unity, SHA256 und CRC --- src/util/nl_crc.c | 25 ++++++++++++ src/util/nl_crc.h | 13 ++++++ src/util/nl_sha256.c | 96 ++++++++++++++++++++++++++++++++++++++++++++ src/util/nl_sha256.h | 18 +++++++++ test/CMakeLists.txt | 26 ++++++++++++ test/test_crc.c | 36 +++++++++++++++++ test/test_sha256.c | 83 ++++++++++++++++++++++++++++++++++++++ tools/test_host.sh | 7 ++++ 8 files changed, 304 insertions(+) create mode 100644 src/util/nl_crc.c create mode 100644 src/util/nl_crc.h create mode 100644 src/util/nl_sha256.c create mode 100644 src/util/nl_sha256.h create mode 100644 test/CMakeLists.txt create mode 100644 test/test_crc.c create mode 100644 test/test_sha256.c create mode 100755 tools/test_host.sh diff --git a/src/util/nl_crc.c b/src/util/nl_crc.c new file mode 100644 index 0000000..5f9e7b9 --- /dev/null +++ b/src/util/nl_crc.c @@ -0,0 +1,25 @@ +#include "util/nl_crc.h" + +uint32_t nl_crc32(uint32_t crc, const void *data, size_t len) +{ + const uint8_t *p = (const uint8_t *)data; + crc = ~crc; + while (len--) { + crc ^= *p++; + for (int k = 0; k < 8; k++) + crc = (crc >> 1) ^ (0xEDB88320u & (0u - (crc & 1u))); + } + return ~crc; +} + +uint8_t nl_crc8(const void *data, size_t len) +{ + const uint8_t *p = (const uint8_t *)data; + uint8_t crc = 0; + while (len--) { + crc ^= *p++; + for (int k = 0; k < 8; k++) + crc = (uint8_t)((crc & 0x80u) ? (crc << 1) ^ 0x07u : (crc << 1)); + } + return crc; +} diff --git a/src/util/nl_crc.h b/src/util/nl_crc.h new file mode 100644 index 0000000..dedf962 --- /dev/null +++ b/src/util/nl_crc.h @@ -0,0 +1,13 @@ +#ifndef NL_CRC_H +#define NL_CRC_H + +#include +#include + +/* CRC-32 (IEEE 802.3, reflected, poly 0xEDB88320). Start with crc = 0; chainable. */ +uint32_t nl_crc32(uint32_t crc, const void *data, size_t len); + +/* CRC-8 (poly 0x07, init 0x00, no reflection). */ +uint8_t nl_crc8(const void *data, size_t len); + +#endif diff --git a/src/util/nl_sha256.c b/src/util/nl_sha256.c new file mode 100644 index 0000000..a9a79ed --- /dev/null +++ b/src/util/nl_sha256.c @@ -0,0 +1,96 @@ +#include "util/nl_sha256.h" + +#include + +#define ROR(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) + +static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +static void transform(nl_sha256_ctx *c, const uint8_t *p) +{ + uint32_t w[64]; + uint32_t a, b, cc, d, e, f, g, h; + int i; + + for (i = 0; i < 16; i++) + w[i] = ((uint32_t)p[4 * i] << 24) | ((uint32_t)p[4 * i + 1] << 16) | + ((uint32_t)p[4 * i + 2] << 8) | (uint32_t)p[4 * i + 3]; + for (i = 16; i < 64; i++) { + uint32_t s0 = ROR(w[i - 15], 7) ^ ROR(w[i - 15], 18) ^ (w[i - 15] >> 3); + uint32_t s1 = ROR(w[i - 2], 17) ^ ROR(w[i - 2], 19) ^ (w[i - 2] >> 10); + w[i] = w[i - 16] + s0 + w[i - 7] + s1; + } + + a = c->state[0]; b = c->state[1]; cc = c->state[2]; d = c->state[3]; + e = c->state[4]; f = c->state[5]; g = c->state[6]; h = c->state[7]; + + for (i = 0; i < 64; i++) { + uint32_t S1 = ROR(e, 6) ^ ROR(e, 11) ^ ROR(e, 25); + uint32_t ch = (e & f) ^ (~e & g); + uint32_t t1 = h + S1 + ch + K[i] + w[i]; + uint32_t S0 = ROR(a, 2) ^ ROR(a, 13) ^ ROR(a, 22); + uint32_t maj = (a & b) ^ (a & cc) ^ (b & cc); + uint32_t t2 = S0 + maj; + h = g; g = f; f = e; e = d + t1; + d = cc; cc = b; b = a; a = t1 + t2; + } + + c->state[0] += a; c->state[1] += b; c->state[2] += cc; c->state[3] += d; + c->state[4] += e; c->state[5] += f; c->state[6] += g; c->state[7] += h; +} + +void nl_sha256_init(nl_sha256_ctx *c) +{ + static const uint32_t H0[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + memcpy(c->state, H0, sizeof H0); + c->bitlen = 0; + c->buflen = 0; +} + +void nl_sha256_update(nl_sha256_ctx *c, const void *data, size_t len) +{ + const uint8_t *p = (const uint8_t *)data; + while (len--) { + c->buf[c->buflen++] = *p++; + if (c->buflen == 64) { + transform(c, c->buf); + c->bitlen += 512; + c->buflen = 0; + } + } +} + +void nl_sha256_final(nl_sha256_ctx *c, uint8_t out[32]) +{ + uint32_t i = c->buflen; + + c->bitlen += (uint64_t)c->buflen * 8u; + c->buf[i++] = 0x80; + if (i > 56) { + while (i < 64) + c->buf[i++] = 0; + transform(c, c->buf); + i = 0; + } + while (i < 56) + c->buf[i++] = 0; + for (int k = 0; k < 8; k++) + c->buf[63 - k] = (uint8_t)(c->bitlen >> (8 * k)); + transform(c, c->buf); + + for (int k = 0; k < 8; k++) { + out[4 * k] = (uint8_t)(c->state[k] >> 24); + out[4 * k + 1] = (uint8_t)(c->state[k] >> 16); + out[4 * k + 2] = (uint8_t)(c->state[k] >> 8); + out[4 * k + 3] = (uint8_t)(c->state[k]); + } +} diff --git a/src/util/nl_sha256.h b/src/util/nl_sha256.h new file mode 100644 index 0000000..e99f7a9 --- /dev/null +++ b/src/util/nl_sha256.h @@ -0,0 +1,18 @@ +#ifndef NL_SHA256_H +#define NL_SHA256_H + +#include +#include + +typedef struct { + uint32_t state[8]; + uint64_t bitlen; + uint8_t buf[64]; + uint32_t buflen; +} nl_sha256_ctx; + +void nl_sha256_init(nl_sha256_ctx *c); +void nl_sha256_update(nl_sha256_ctx *c, const void *data, size_t len); +void nl_sha256_final(nl_sha256_ctx *c, uint8_t out[32]); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..cab7b54 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.20) +project(ninlight_host_tests C) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +include(FetchContent) +FetchContent_Declare(unity + GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity.git + GIT_TAG v2.6.0) +FetchContent_MakeAvailable(unity) + +enable_testing() +set(NL_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../src) + +function(nl_test name) + add_executable(${name} ${ARGN}) + target_include_directories(${name} PRIVATE ${NL_SRC} ${CMAKE_CURRENT_SOURCE_DIR}) + target_compile_options(${name} PRIVATE -Wall -Wextra -Werror) + target_compile_definitions(${name} PRIVATE NINLIGHT_HOST_TEST=1) + target_link_libraries(${name} PRIVATE unity m) + add_test(NAME ${name} COMMAND ${name}) +endfunction() + +nl_test(test_sha256 test_sha256.c ${NL_SRC}/util/nl_sha256.c) +nl_test(test_crc test_crc.c ${NL_SRC}/util/nl_crc.c) diff --git a/test/test_crc.c b/test/test_crc.c new file mode 100644 index 0000000..bebff01 --- /dev/null +++ b/test/test_crc.c @@ -0,0 +1,36 @@ +#include "unity.h" +#include "util/nl_crc.h" + +void setUp(void) {} +void tearDown(void) {} + +static void test_crc32_check_value(void) +{ + TEST_ASSERT_EQUAL_HEX32(0xCBF43926u, nl_crc32(0, "123456789", 9)); +} + +static void test_crc32_empty_is_zero(void) +{ + TEST_ASSERT_EQUAL_HEX32(0u, nl_crc32(0, "", 0)); +} + +static void test_crc32_chaining(void) +{ + uint32_t c = nl_crc32(0, "1234", 4); + TEST_ASSERT_EQUAL_HEX32(0xCBF43926u, nl_crc32(c, "56789", 5)); +} + +static void test_crc8_check_value(void) +{ + TEST_ASSERT_EQUAL_HEX8(0xF4, nl_crc8("123456789", 9)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_crc32_check_value); + RUN_TEST(test_crc32_empty_is_zero); + RUN_TEST(test_crc32_chaining); + RUN_TEST(test_crc8_check_value); + return UNITY_END(); +} diff --git a/test/test_sha256.c b/test/test_sha256.c new file mode 100644 index 0000000..2d6712f --- /dev/null +++ b/test/test_sha256.c @@ -0,0 +1,83 @@ +#include +#include +#include "unity.h" +#include "util/nl_sha256.h" + +void setUp(void) {} +void tearDown(void) {} + +static void digest_hex(const void *msg, size_t len, char out[65]) +{ + uint8_t d[32]; + nl_sha256_ctx c; + nl_sha256_init(&c); + nl_sha256_update(&c, msg, len); + nl_sha256_final(&c, d); + for (int i = 0; i < 32; i++) + sprintf(out + 2 * i, "%02x", d[i]); +} + +static void test_empty(void) +{ + char h[65]; + digest_hex("", 0, h); + TEST_ASSERT_EQUAL_STRING("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", h); +} + +static void test_abc(void) +{ + char h[65]; + digest_hex("abc", 3, h); + TEST_ASSERT_EQUAL_STRING("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", h); +} + +static void test_two_blocks(void) +{ + const char *m = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + char h[65]; + digest_hex(m, strlen(m), h); + TEST_ASSERT_EQUAL_STRING("248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", h); +} + +static void test_million_a(void) +{ + uint8_t d[32]; + char h[65]; + nl_sha256_ctx c; + nl_sha256_init(&c); + for (int i = 0; i < 1000000; i++) + nl_sha256_update(&c, "a", 1); + nl_sha256_final(&c, d); + for (int i = 0; i < 32; i++) + sprintf(h + 2 * i, "%02x", d[i]); + TEST_ASSERT_EQUAL_STRING("cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", h); +} + +static void test_streaming_equals_oneshot(void) +{ + uint8_t buf[1000], a[32], b[32]; + nl_sha256_ctx c; + for (int i = 0; i < 1000; i++) + buf[i] = (uint8_t)(i * 7); + nl_sha256_init(&c); + nl_sha256_update(&c, buf, sizeof buf); + nl_sha256_final(&c, a); + nl_sha256_init(&c); + for (size_t off = 0; off < sizeof buf; off += 37) { + size_t n = sizeof buf - off < 37 ? sizeof buf - off : 37; + nl_sha256_update(&c, buf + off, n); + } + nl_sha256_final(&c, b); + TEST_ASSERT_EQUAL_UINT8_ARRAY(a, b, 32); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_empty); + RUN_TEST(test_abc); + RUN_TEST(test_two_blocks); + RUN_TEST(test_million_a); + RUN_TEST(test_streaming_equals_oneshot); + return UNITY_END(); +} diff --git a/tools/test_host.sh b/tools/test_host.sh new file mode 100755 index 0000000..62edf66 --- /dev/null +++ b/tools/test_host.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Builds and runs all host unit tests. +set -euo pipefail +ROOT=$(cd "$(dirname "$0")/.." && pwd) +cmake -S "$ROOT/test" -B "$ROOT/build/host" -G Ninja >/dev/null +cmake --build "$ROOT/build/host" +ctest --test-dir "$ROOT/build/host" --output-on-failure "$@" From 1b0501179d10721a44f4347cace70d4aafd0247c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 12:52:26 +0200 Subject: [PATCH 004/150] build: W600-SDK importiert, Firmware-Build und Hello am EVK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docker-Build mit gcc-arm-none-eabi 4.9-2014q4 (statt Homebrew-Cask) verwendet, Dockerfile/tools/docker.sh dafür aus Task 13 vorgezogen. cmake/sdk.cmake schont gcc 4.9 (kennt -fcommon/-Wno-error=... nicht). Zwei zusätzliche SDK-Stubs (EarlyInit, pre_gpio_config) in src/sys/sdk_stubs.c ergänzt, siehe sdk/PATCHES.md. --- CMakeLists.txt | 49 + cmake/arm-none-eabi.cmake | 24 + cmake/image.cmake | 20 + cmake/sdk.cmake | 49 + cmake/version.cmake | 12 + config.local.cmake.example | 8 + docker/Dockerfile | 12 + sdk/Bin/secboot.img | Bin 0 -> 26980 bytes sdk/Include/App/wm_at_ri_init.h | 79 + sdk/Include/App/wm_cloud.h | 481 ++ sdk/Include/App/wm_crypto.h | 91 + sdk/Include/App/wm_dhcp_server.h | 91 + sdk/Include/App/wm_dlna_dmr.h | 247 + sdk/Include/App/wm_dns_server.h | 66 + sdk/Include/App/wm_http_client.h | 486 ++ sdk/Include/App/wm_netif.h | 15 + sdk/Include/App/wm_netif2.0.3.h | 400 ++ sdk/Include/App/wm_ntp.h | 80 + sdk/Include/App/wm_webserver.h | 65 + sdk/Include/App/wm_wifi_oneshot.h | 284 + sdk/Include/Driver/wm_7816.h | 242 + sdk/Include/Driver/wm_adc.h | 297 + sdk/Include/Driver/wm_cpu.h | 94 + sdk/Include/Driver/wm_dma.h | 248 + sdk/Include/Driver/wm_efuse.h | 288 + sdk/Include/Driver/wm_flash.h | 203 + sdk/Include/Driver/wm_flash_map.h | 84 + sdk/Include/Driver/wm_fls_gd25qxx.h | 60 + sdk/Include/Driver/wm_gpio.h | 190 + sdk/Include/Driver/wm_gpio_afsel.h | 510 ++ sdk/Include/Driver/wm_hostspi.h | 299 + sdk/Include/Driver/wm_hspi.h | 264 + sdk/Include/Driver/wm_i2c.h | 155 + sdk/Include/Driver/wm_i2s.h | 581 ++ sdk/Include/Driver/wm_internal_flash.h | 239 + sdk/Include/Driver/wm_io.h | 195 + sdk/Include/Driver/wm_iouart.h | 138 + sdk/Include/Driver/wm_irq.h | 177 + sdk/Include/Driver/wm_lcd.h | 245 + sdk/Include/Driver/wm_pmu.h | 239 + sdk/Include/Driver/wm_pwm.h | 415 ++ sdk/Include/Driver/wm_rtc.h | 109 + sdk/Include/Driver/wm_timer.h | 139 + sdk/Include/Driver/wm_uart.h | 463 ++ sdk/Include/Driver/wm_watchdog.h | 99 + sdk/Include/Net/wm_socket.h | 17 + sdk/Include/Net/wm_socket2.0.3.h | 422 ++ sdk/Include/Net/wm_sockets.h | 15 + sdk/Include/Net/wm_sockets2.0.3.h | 1174 ++++ sdk/Include/OS/wm_config_gcc.inc | 3 + sdk/Include/OS/wm_os_config.h | 19 + sdk/Include/OS/wm_os_config.inc | 3 + sdk/Include/OS/wm_osal.h | 715 +++ sdk/Include/Platform/aes.h | 186 + sdk/Include/Platform/wm_crypto_hard.h | 563 ++ sdk/Include/Platform/wm_fwup.h | 343 ++ sdk/Include/Platform/wm_mem.h | 201 + sdk/Include/Platform/wm_params.h | 615 ++ sdk/Include/Platform/wm_wl_mbox.h | 115 + sdk/Include/Platform/wm_wl_task.h | 233 + sdk/Include/Platform/wm_wl_timers.h | 102 + sdk/Include/WiFi/litepoint.h | 90 + sdk/Include/WiFi/wm_ieee80211.h | 2019 +++++++ sdk/Include/WiFi/wm_ieee80211_gcc.h | 1682 ++++++ sdk/Include/WiFi/wm_wifi.h | 1045 ++++ sdk/Include/list.h | 309 + sdk/Include/random.h | 80 + sdk/Include/wm_config.h | 83 + sdk/Include/wm_debug.h | 128 + sdk/Include/wm_include.h | 87 + sdk/Include/wm_log.h | 150 + sdk/Include/wm_ram_config.h | 31 + sdk/Include/wm_regs.h | 917 +++ sdk/Include/wm_type_def.h | 155 + sdk/Include/wm_wifi_config.h | 36 + sdk/Lib/GNU/libwlan.a | Bin 0 -> 447222 bytes sdk/PATCHES.md | 16 + sdk/Platform/Boot/gcc/core_cm3.h | 1627 +++++ sdk/Platform/Boot/gcc/misc.c | 225 + sdk/Platform/Boot/gcc/retarget.c | 93 + sdk/Platform/Boot/gcc/startup.S | 256 + sdk/Platform/Common/CMakeLists.txt | 72 + sdk/Platform/Common/Params/wm_param.c | 1351 +++++ sdk/Platform/Common/Params/wm_param.h | 124 + sdk/Platform/Common/crypto/wm_crypto_hard.c | 2696 +++++++++ sdk/Platform/Common/fwup/wm_fwup.c | 1005 ++++ sdk/Platform/Common/mem/wm_mem.c | 765 +++ sdk/Platform/Common/task/wm_wl_mbox.c | 160 + sdk/Platform/Common/task/wm_wl_task.c | 164 + sdk/Platform/Common/task/wm_wl_timers.c | 180 + sdk/Platform/Common/utils/utils.c | 1093 ++++ sdk/Platform/Common/wm_common.c | 11 + sdk/Platform/Drivers/7816/wm_7816.c | 291 + sdk/Platform/Drivers/CMakeLists.txt | 64 + sdk/Platform/Drivers/adc/wm_adc.c | 517 ++ sdk/Platform/Drivers/cpu/wm_cpu.c | 86 + sdk/Platform/Drivers/dma/wm_dma.c | 405 ++ sdk/Platform/Drivers/efuse/wm_efuse.c | 644 ++ sdk/Platform/Drivers/flash/wm_fls.c | 577 ++ sdk/Platform/Drivers/flash/wm_fls_gd25qxx.c | 307 + sdk/Platform/Drivers/gpio/wm_gpio.c | 411 ++ sdk/Platform/Drivers/gpio/wm_gpio_afsel.c | 1012 ++++ sdk/Platform/Drivers/hspi/wm_hspi.c | 493 ++ sdk/Platform/Drivers/i2c/wm_i2c.c | 319 + sdk/Platform/Drivers/i2s/wm_i2s.c | 584 ++ .../Drivers/internalflash/wm_internal_fls.c | 1408 +++++ sdk/Platform/Drivers/io/wm_io.c | 277 + sdk/Platform/Drivers/iouart/wm_iouart.c | 270 + sdk/Platform/Drivers/irq/wm_irq.c | 149 + sdk/Platform/Drivers/lcd/wm_lcd.c | 187 + sdk/Platform/Drivers/pmu/wm_pmu.c | 332 ++ sdk/Platform/Drivers/pwm/wm_pwm.c | 919 +++ sdk/Platform/Drivers/rtc/wm_rtc.c | 170 + sdk/Platform/Drivers/spi/wm_hostspi.c | 1425 +++++ sdk/Platform/Drivers/spi/wm_spi_hal.h | 303 + sdk/Platform/Drivers/timer/wm_timer.c | 319 + sdk/Platform/Drivers/uart/wm_uart.c | 1600 +++++ sdk/Platform/Drivers/watchdog/wm_watchdog.c | 160 + sdk/Platform/Drivers/wm_driver.c | 26 + sdk/Platform/Inc/bitops.h | 116 + sdk/Platform/Inc/libtommath.h | 192 + sdk/Platform/Inc/md5.h | 52 + sdk/Platform/Inc/misc.h | 206 + sdk/Platform/Inc/os.h | 47 + sdk/Platform/Inc/rc4.h | 38 + sdk/Platform/Inc/sha1.h | 46 + sdk/Platform/Inc/sha256.h | 53 + sdk/Platform/Inc/tls_common.h | 253 + sdk/Platform/Inc/tls_ieee80211.h | 12 + sdk/Platform/Inc/tls_wireless.h | 434 ++ sdk/Platform/Inc/utils.h | 39 + sdk/Platform/Inc/wm_dbg.h | 93 + sdk/Platform/Sys/CMakeLists.txt | 43 + sdk/Platform/Sys/tls_sys.c | 581 ++ sdk/Platform/Sys/tls_sys.h | 22 + sdk/Platform/Sys/wm_main.c | 402 ++ sdk/Src/App/OTA/CMakeLists.txt | 43 + sdk/Src/App/OTA/wm_http_fwup.c | 237 + sdk/Src/App/OTA/wm_http_fwup.h | 35 + sdk/Src/App/OTA/wm_socket_fwup.c | 157 + sdk/Src/App/OTA/wm_socket_fwup.h | 23 + sdk/Src/App/dhcpserver/CMakeLists.txt | 42 + sdk/Src/App/dhcpserver/dhcp_server.c | 1102 ++++ sdk/Src/App/dhcpserver/dhcp_server.h | 163 + sdk/Src/App/dnsserver/CMakeLists.txt | 42 + sdk/Src/App/dnsserver/dns_server.c | 390 ++ sdk/Src/App/dnsserver/dns_server.h | 128 + sdk/Src/App/httpclient/CMakeLists.txt | 46 + sdk/Src/App/httpclient/HTTPClient.c | 3790 ++++++++++++ sdk/Src/App/httpclient/HTTPClient.h | 318 + sdk/Src/App/httpclient/HTTPClientAuth.c | 673 +++ sdk/Src/App/httpclient/HTTPClientAuth.h | 93 + sdk/Src/App/httpclient/HTTPClientCommon.h | 42 + sdk/Src/App/httpclient/HTTPClientString.c | 433 ++ sdk/Src/App/httpclient/HTTPClientString.h | 23 + sdk/Src/App/httpclient/HTTPClientWrapper.c | 754 +++ sdk/Src/App/httpclient/HTTPClientWrapper.h | 121 + sdk/Src/App/httpclient/wm_http_compile.c | 8 + sdk/Src/App/httpclient/wm_httpclient_if.c | 307 + sdk/Src/App/httpclient/wm_httpclient_task.c | 235 + sdk/Src/Network/CMakeLists.txt | 91 + sdk/Src/Network/api2.0.3/tls_netconn.c | 1685 ++++++ sdk/Src/Network/api2.0.3/tls_netconn.h | 93 + sdk/Src/Network/api2.0.3/tls_sockets.c | 122 + sdk/Src/Network/lwip2.0.3.c | 54 + sdk/Src/Network/lwip2.0.3/FILES | 15 + sdk/Src/Network/lwip2.0.3/Filelists.mk | 181 + sdk/Src/Network/lwip2.0.3/api/api_lib.c | 1010 ++++ sdk/Src/Network/lwip2.0.3/api/api_msg.c | 1953 ++++++ sdk/Src/Network/lwip2.0.3/api/err.c | 115 + sdk/Src/Network/lwip2.0.3/api/netbuf.c | 246 + sdk/Src/Network/lwip2.0.3/api/netdb.c | 413 ++ sdk/Src/Network/lwip2.0.3/api/netifapi.c | 221 + sdk/Src/Network/lwip2.0.3/api/pppapi.c | 370 ++ sdk/Src/Network/lwip2.0.3/api/sockets.c | 2860 +++++++++ sdk/Src/Network/lwip2.0.3/api/tcpip.c | 552 ++ sdk/Src/Network/lwip2.0.3/apps/httpd/fs.c | 179 + .../Network/lwip2.0.3/apps/httpd/fs/404.html | 21 + .../lwip2.0.3/apps/httpd/fs/img/sics.gif | Bin 0 -> 724 bytes .../lwip2.0.3/apps/httpd/fs/index.html | 47 + sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.c | 298 + sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.h | 50 + sdk/Src/Network/lwip2.0.3/apps/httpd/httpd.c | 2629 +++++++++ .../lwip2.0.3/apps/httpd/httpd_structs.h | 114 + .../apps/httpd/makefsdata/makefsdata | 97 + .../apps/httpd/makefsdata/makefsdata.c | 1033 ++++ .../apps/httpd/makefsdata/readme.txt | 13 + .../Network/lwip2.0.3/apps/lwiperf/lwiperf.c | 661 +++ sdk/Src/Network/lwip2.0.3/apps/mdns/mdns.c | 2028 +++++++ sdk/Src/Network/lwip2.0.3/apps/mqtt/mqtt.c | 1376 +++++ .../lwip2.0.3/apps/netbiosns/netbiosns.c | 367 ++ .../CCodeGeneration/CCodeGeneration.csproj | 67 + .../LwipMibCompiler/CCodeGeneration/CFile.cs | 54 + .../CCodeGeneration/CGenerator.cs | 119 + .../LwipMibCompiler/CCodeGeneration/Code.cs | 56 + .../CCodeGeneration/CodeContainerBase.cs | 139 + .../CCodeGeneration/CodeElement.cs | 41 + .../CCodeGeneration/Comment.cs | 75 + .../CCodeGeneration/EmptyLine.cs | 64 + .../CCodeGeneration/Function.cs | 129 + .../CCodeGeneration/FunctionDeclaration.cs | 114 + .../CCodeGeneration/IfThenElse.cs | 137 + .../LwipMibCompiler/CCodeGeneration/PP_If.cs | 67 + .../CCodeGeneration/PP_Ifdef.cs | 76 + .../CCodeGeneration/PP_Include.cs | 71 + .../CCodeGeneration/PP_Macro.cs | 59 + .../CCodeGeneration/PlainText.cs | 49 + .../Properties/AssemblyInfo.cs | 36 + .../LwipMibCompiler/CCodeGeneration/Switch.cs | 146 + .../CCodeGeneration/VariableDeclaration.cs | 82 + .../CCodeGeneration/VariablePrototype.cs | 73 + .../CCodeGeneration/VariableType.cs | 130 + .../snmp/LwipMibCompiler/LwipMibCompiler.sln | 47 + .../LwipMibCompiler/LwipMibCompiler.csproj | 73 + .../LwipMibCompiler/Program.cs | 477 ++ .../Properties/AssemblyInfo.cs | 36 + .../LwipMibCompiler/app.config | 3 + .../LwipMibViewer/FormMain.Designer.cs | 166 + .../LwipMibCompiler/LwipMibViewer/FormMain.cs | 217 + .../LwipMibViewer/FormMain.resx | 298 + .../LwipMibViewer/LwipMibViewer.csproj | 94 + .../LwipMibCompiler/LwipMibViewer/Program.cs | 51 + .../LwipMibViewer/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 63 + .../LwipMibViewer/Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 26 + .../Properties/Settings.settings | 7 + .../LwipMibCompiler/LwipMibViewer/app.config | 3 + .../LwipSnmpCodeGeneration/IRestriction.cs | 120 + .../LwipSnmpCodeGeneration/LwipSnmp.cs | 199 + .../LwipSnmpCodeGeneration.csproj | 72 + .../LwipSnmpCodeGeneration/MibCFile.cs | 196 + .../LwipSnmpCodeGeneration/MibHeaderFile.cs | 129 + .../Properties/AssemblyInfo.cs | 36 + .../LwipSnmpCodeGeneration/SnmpMib.cs | 97 + .../LwipSnmpCodeGeneration/SnmpNode.cs | 119 + .../SnmpScalarAggregationNode.cs | 293 + .../SnmpScalarArrayNode.cs | 105 + .../LwipSnmpCodeGeneration/SnmpScalarNode.cs | 395 ++ .../SnmpScalarNodeBits.cs | 121 + .../SnmpScalarNodeCounter64.cs | 72 + .../SnmpScalarNodeInt.cs | 86 + .../SnmpScalarNodeObjectIdentifier.cs | 90 + .../SnmpScalarNodeOctetString.cs | 118 + .../SnmpScalarNodeTruthValue.cs | 66 + .../SnmpScalarNodeUint.cs | 91 + .../LwipSnmpCodeGeneration/SnmpTableNode.cs | 332 ++ .../LwipSnmpCodeGeneration/SnmpTreeNode.cs | 241 + .../Mibs/IANA-ADDRESS-FAMILY-NUMBERS-MIB | 131 + .../LwipMibCompiler/Mibs/IANA-CHARSET-MIB | 345 ++ .../Mibs/IANA/IANA-ITU-ALARM-TC-MIB | 333 ++ .../Mibs/IANA/IANA-LANGUAGE-MIB | 127 + .../LwipMibCompiler/Mibs/IANA/IANA-MALLOC-MIB | 67 + .../LwipMibCompiler/Mibs/IANA/IANA-MAU-MIB | 770 +++ .../Mibs/IANA/IANA-PRINTER-MIB | 1319 +++++ .../Mibs/IANA/IANA-RTPROTO-MIB | 92 + .../Mibs/IANA/IANATn3270eTC-MIB | 306 + .../LwipMibCompiler/Mibs/IANA/IANAifType-MIB | 572 ++ .../apps/snmp/LwipMibCompiler/Mibs/IF-MIB | 1899 ++++++ .../LwipMibCompiler/Mibs/INET-ADDRESS-MIB | 421 ++ .../apps/snmp/LwipMibCompiler/Mibs/IP-MIB | 5254 +++++++++++++++++ .../apps/snmp/LwipMibCompiler/Mibs/RFC-1212 | 75 + .../apps/snmp/LwipMibCompiler/Mibs/RFC-1215 | 34 + .../snmp/LwipMibCompiler/Mibs/RFC1065-SMI | 132 + .../snmp/LwipMibCompiler/Mibs/RFC1155-SMI | 129 + .../snmp/LwipMibCompiler/Mibs/RFC1158-MIB | 1493 +++++ .../snmp/LwipMibCompiler/Mibs/RFC1213-MIB | 2621 ++++++++ .../snmp/LwipMibCompiler/Mibs/SNMPv2-CONF | 318 + .../apps/snmp/LwipMibCompiler/Mibs/SNMPv2-MIB | 903 +++ .../apps/snmp/LwipMibCompiler/Mibs/SNMPv2-SMI | 352 ++ .../apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TC | 786 +++ .../apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TM | 194 + .../apps/snmp/LwipMibCompiler/Mibs/TCP-MIB | 829 +++ .../apps/snmp/LwipMibCompiler/Mibs/UDP-MIB | 579 ++ .../SharpSnmpLib/Mib/DisplayHint.cs | 84 + .../Elements/Entities/AgentCapabilities.cs | 29 + .../Mib/Elements/Entities/EntityBase.cs | 46 + .../Mib/Elements/Entities/IEntity.cs | 62 + .../Mib/Elements/Entities/ModuleCompliance.cs | 23 + .../Mib/Elements/Entities/ModuleIdentity.cs | 10 + .../Elements/Entities/NotificationGroup.cs | 22 + .../Mib/Elements/Entities/NotificationType.cs | 11 + .../Mib/Elements/Entities/ObjectGroup.cs | 22 + .../Mib/Elements/Entities/ObjectIdentity.cs | 21 + .../Mib/Elements/Entities/ObjectType.cs | 336 ++ .../Elements/Entities/OidValueAssignment.cs | 30 + .../SharpSnmpLib/Mib/Elements/Exports.cs | 56 + .../SharpSnmpLib/Mib/Elements/IDeclaration.cs | 18 + .../SharpSnmpLib/Mib/Elements/IElement.cs | 35 + .../Mib/Elements/ITypeReferrer.cs | 10 + .../SharpSnmpLib/Mib/Elements/Imports.cs | 81 + .../SharpSnmpLib/Mib/Elements/ImportsFrom.cs | 60 + .../SharpSnmpLib/Mib/Elements/TrapType.cs | 48 + .../Mib/Elements/Types/BaseType.cs | 54 + .../Mib/Elements/Types/BitsType.cs | 26 + .../SharpSnmpLib/Mib/Elements/Types/Choice.cs | 35 + .../Mib/Elements/Types/ITypeAssignment.cs | 6 + .../Mib/Elements/Types/IntegerType.cs | 117 + .../Mib/Elements/Types/IpAddressType.cs | 21 + .../SharpSnmpLib/Mib/Elements/Types/Macro.cs | 34 + .../Elements/Types/ObjectIdentifierType.cs | 11 + .../Mib/Elements/Types/OctetStringType.cs | 31 + .../Mib/Elements/Types/OpaqueType.cs | 11 + .../Mib/Elements/Types/Sequence.cs | 46 + .../Mib/Elements/Types/SequenceOf.cs | 23 + .../Mib/Elements/Types/TextualConvention.cs | 238 + .../Mib/Elements/Types/TypeAssignment.cs | 147 + .../Mib/Elements/Types/UnsignedType.cs | 103 + .../SharpSnmpLib/Mib/IModule.cs | 81 + .../SharpSnmpLib/Mib/ISymbolEnumerator.cs | 9 + .../LwipMibCompiler/SharpSnmpLib/Mib/Lexer.cs | 581 ++ .../SharpSnmpLib/Mib/MaxAccess.cs | 17 + .../SharpSnmpLib/Mib/MibDocument.cs | 57 + .../SharpSnmpLib/Mib/MibException.cs | 113 + .../SharpSnmpLib/Mib/MibModule.cs | 294 + .../SharpSnmpLib/Mib/MibResolver.cs | 97 + .../SharpSnmpLib/Mib/MibTree.cs | 105 + .../SharpSnmpLib/Mib/MibTreeNode.cs | 112 + .../SharpSnmpLib/Mib/MibTypesResolver.cs | 216 + .../SharpSnmpLib/Mib/ObjectIdentifier.cs | 54 + .../SharpSnmpLib/Mib/Status.cs | 17 + .../SharpSnmpLib/Mib/Symbol.cs | 357 ++ .../SharpSnmpLib/Mib/SymbolList.cs | 146 + .../SharpSnmpLib/Mib/ValueMap.cs | 103 + .../SharpSnmpLib/Mib/ValueRange.cs | 76 + .../SharpSnmpLib/Properties/AssemblyInfo.cs | 61 + .../Properties/Resources.Designer.cs | 63 + .../SharpSnmpLib/Properties/Resources.resx | 120 + .../SharpSnmpLib/SharpSnmpLib.Mib.csproj | 139 + .../LwipMibCompiler/SharpSnmpLib/license.txt | 458 ++ .../SharpSnmpLib/sharpsnmplib.snk | Bin 0 -> 596 bytes .../example/compile_udp_mib.sh | 1 + sdk/Src/Network/lwip2.0.3/apps/snmp/README | 38 + .../Network/lwip2.0.3/apps/snmp/snmp_asn1.c | 749 +++ .../Network/lwip2.0.3/apps/snmp/snmp_asn1.h | 108 + .../Network/lwip2.0.3/apps/snmp/snmp_core.c | 1349 +++++ .../lwip2.0.3/apps/snmp/snmp_core_priv.h | 76 + .../Network/lwip2.0.3/apps/snmp/snmp_mib2.c | 116 + .../lwip2.0.3/apps/snmp/snmp_mib2_icmp.c | 182 + .../apps/snmp/snmp_mib2_interfaces.c | 375 ++ .../lwip2.0.3/apps/snmp/snmp_mib2_ip.c | 743 +++ .../lwip2.0.3/apps/snmp/snmp_mib2_snmp.c | 227 + .../lwip2.0.3/apps/snmp/snmp_mib2_system.c | 377 ++ .../lwip2.0.3/apps/snmp/snmp_mib2_tcp.c | 594 ++ .../lwip2.0.3/apps/snmp/snmp_mib2_udp.c | 357 ++ .../Network/lwip2.0.3/apps/snmp/snmp_msg.c | 1668 ++++++ .../Network/lwip2.0.3/apps/snmp/snmp_msg.h | 194 + .../lwip2.0.3/apps/snmp/snmp_netconn.c | 121 + .../lwip2.0.3/apps/snmp/snmp_pbuf_stream.c | 156 + .../lwip2.0.3/apps/snmp/snmp_pbuf_stream.h | 73 + .../Network/lwip2.0.3/apps/snmp/snmp_raw.c | 100 + .../Network/lwip2.0.3/apps/snmp/snmp_scalar.c | 220 + .../Network/lwip2.0.3/apps/snmp/snmp_table.c | 343 ++ .../lwip2.0.3/apps/snmp/snmp_threadsync.c | 219 + .../Network/lwip2.0.3/apps/snmp/snmp_traps.c | 445 ++ sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3.c | 136 + .../lwip2.0.3/apps/snmp/snmpv3_dummy.c | 145 + .../lwip2.0.3/apps/snmp/snmpv3_mbedtls.c | 331 ++ .../Network/lwip2.0.3/apps/snmp/snmpv3_priv.h | 66 + sdk/Src/Network/lwip2.0.3/apps/sntp/sntp.c | 727 +++ .../Network/lwip2.0.3/apps/tftp/tftp_server.c | 417 ++ sdk/Src/Network/lwip2.0.3/core/alg.c | 2076 +++++++ sdk/Src/Network/lwip2.0.3/core/def.c | 222 + sdk/Src/Network/lwip2.0.3/core/dns.c | 1573 +++++ sdk/Src/Network/lwip2.0.3/core/inet_chksum.c | 609 ++ sdk/Src/Network/lwip2.0.3/core/init.c | 386 ++ sdk/Src/Network/lwip2.0.3/core/ip.c | 153 + sdk/Src/Network/lwip2.0.3/core/ipv4/autoip.c | 527 ++ sdk/Src/Network/lwip2.0.3/core/ipv4/dhcp.c | 2017 +++++++ sdk/Src/Network/lwip2.0.3/core/ipv4/etharp.c | 1206 ++++ sdk/Src/Network/lwip2.0.3/core/ipv4/icmp.c | 397 ++ sdk/Src/Network/lwip2.0.3/core/ipv4/igmp.c | 800 +++ sdk/Src/Network/lwip2.0.3/core/ipv4/ip4.c | 1087 ++++ .../Network/lwip2.0.3/core/ipv4/ip4_addr.c | 331 ++ .../Network/lwip2.0.3/core/ipv4/ip4_frag.c | 864 +++ sdk/Src/Network/lwip2.0.3/core/ipv4/ip_frag.c | 897 +++ sdk/Src/Network/lwip2.0.3/core/ipv6/dhcp6.c | 50 + sdk/Src/Network/lwip2.0.3/core/ipv6/ethip6.c | 118 + sdk/Src/Network/lwip2.0.3/core/ipv6/icmp6.c | 350 ++ sdk/Src/Network/lwip2.0.3/core/ipv6/inet6.c | 53 + sdk/Src/Network/lwip2.0.3/core/ipv6/ip6.c | 1122 ++++ .../Network/lwip2.0.3/core/ipv6/ip6_addr.c | 294 + .../Network/lwip2.0.3/core/ipv6/ip6_frag.c | 805 +++ sdk/Src/Network/lwip2.0.3/core/ipv6/mld6.c | 588 ++ sdk/Src/Network/lwip2.0.3/core/ipv6/nd6.c | 2102 +++++++ sdk/Src/Network/lwip2.0.3/core/mem.c | 779 +++ sdk/Src/Network/lwip2.0.3/core/memp.c | 496 ++ sdk/Src/Network/lwip2.0.3/core/netif.c | 1274 ++++ sdk/Src/Network/lwip2.0.3/core/pbuf.c | 1442 +++++ sdk/Src/Network/lwip2.0.3/core/raw.c | 521 ++ sdk/Src/Network/lwip2.0.3/core/stats.c | 169 + sdk/Src/Network/lwip2.0.3/core/sys.c | 107 + sdk/Src/Network/lwip2.0.3/core/tcp.c | 2296 +++++++ sdk/Src/Network/lwip2.0.3/core/tcp_in.c | 1840 ++++++ sdk/Src/Network/lwip2.0.3/core/tcp_out.c | 1680 ++++++ sdk/Src/Network/lwip2.0.3/core/timeouts.c | 437 ++ sdk/Src/Network/lwip2.0.3/core/timers.c | 572 ++ sdk/Src/Network/lwip2.0.3/core/udp.c | 1208 ++++ sdk/Src/Network/lwip2.0.3/include/arch/cc.h | 78 + sdk/Src/Network/lwip2.0.3/include/arch/perf.h | 8 + .../Network/lwip2.0.3/include/arch/sys_arch.h | 49 + sdk/Src/Network/lwip2.0.3/include/lwip/alg.h | 66 + sdk/Src/Network/lwip2.0.3/include/lwip/api.h | 400 ++ .../Network/lwip2.0.3/include/lwip/apps/FILES | 2 + .../Network/lwip2.0.3/include/lwip/apps/fs.h | 103 + .../lwip2.0.3/include/lwip/apps/httpd.h | 236 + .../lwip2.0.3/include/lwip/apps/httpd_opts.h | 323 + .../lwip2.0.3/include/lwip/apps/lwiperf.h | 84 + .../lwip2.0.3/include/lwip/apps/mdns.h | 69 + .../lwip2.0.3/include/lwip/apps/mdns_opts.h | 74 + .../lwip2.0.3/include/lwip/apps/mdns_priv.h | 66 + .../lwip2.0.3/include/lwip/apps/mqtt.h | 244 + .../lwip2.0.3/include/lwip/apps/mqtt_opts.h | 103 + .../lwip2.0.3/include/lwip/apps/netbiosns.h | 43 + .../include/lwip/apps/netbiosns_opts.h | 59 + .../lwip2.0.3/include/lwip/apps/snmp.h | 128 + .../lwip2.0.3/include/lwip/apps/snmp_core.h | 364 ++ .../lwip2.0.3/include/lwip/apps/snmp_mib2.h | 78 + .../lwip2.0.3/include/lwip/apps/snmp_opts.h | 293 + .../lwip2.0.3/include/lwip/apps/snmp_scalar.h | 113 + .../lwip2.0.3/include/lwip/apps/snmp_table.h | 134 + .../include/lwip/apps/snmp_threadsync.h | 114 + .../lwip2.0.3/include/lwip/apps/snmpv3.h | 90 + .../lwip2.0.3/include/lwip/apps/sntp.h | 76 + .../lwip2.0.3/include/lwip/apps/sntp_opts.h | 173 + .../lwip2.0.3/include/lwip/apps/tftp_opts.h | 105 + .../lwip2.0.3/include/lwip/apps/tftp_server.h | 94 + sdk/Src/Network/lwip2.0.3/include/lwip/arch.h | 315 + .../Network/lwip2.0.3/include/lwip/autoip.h | 99 + .../Network/lwip2.0.3/include/lwip/debug.h | 170 + sdk/Src/Network/lwip2.0.3/include/lwip/def.h | 153 + sdk/Src/Network/lwip2.0.3/include/lwip/dhcp.h | 143 + .../Network/lwip2.0.3/include/lwip/dhcp6.h | 58 + sdk/Src/Network/lwip2.0.3/include/lwip/dns.h | 130 + sdk/Src/Network/lwip2.0.3/include/lwip/err.h | 119 + .../Network/lwip2.0.3/include/lwip/errno.h | 193 + .../Network/lwip2.0.3/include/lwip/etharp.h | 128 + .../Network/lwip2.0.3/include/lwip/ethip6.h | 68 + sdk/Src/Network/lwip2.0.3/include/lwip/icmp.h | 110 + .../Network/lwip2.0.3/include/lwip/icmp6.h | 70 + sdk/Src/Network/lwip2.0.3/include/lwip/igmp.h | 115 + sdk/Src/Network/lwip2.0.3/include/lwip/inet.h | 176 + .../lwip2.0.3/include/lwip/inet_chksum.h | 105 + sdk/Src/Network/lwip2.0.3/include/lwip/init.h | 100 + sdk/Src/Network/lwip2.0.3/include/lwip/ip.h | 319 + sdk/Src/Network/lwip2.0.3/include/lwip/ip4.h | 111 + .../Network/lwip2.0.3/include/lwip/ip4_addr.h | 227 + .../Network/lwip2.0.3/include/lwip/ip4_frag.h | 100 + sdk/Src/Network/lwip2.0.3/include/lwip/ip6.h | 93 + .../Network/lwip2.0.3/include/lwip/ip6_addr.h | 285 + .../Network/lwip2.0.3/include/lwip/ip6_frag.h | 120 + .../Network/lwip2.0.3/include/lwip/ip_addr.h | 410 ++ .../Network/lwip2.0.3/include/lwip/ip_frag.h | 95 + .../Network/lwip2.0.3/include/lwip/lwipopts.h | 134 + sdk/Src/Network/lwip2.0.3/include/lwip/mem.h | 83 + sdk/Src/Network/lwip2.0.3/include/lwip/memp.h | 155 + sdk/Src/Network/lwip2.0.3/include/lwip/mld6.h | 99 + sdk/Src/Network/lwip2.0.3/include/lwip/nd6.h | 85 + .../Network/lwip2.0.3/include/lwip/netbuf.h | 118 + .../Network/lwip2.0.3/include/lwip/netdb.h | 153 + .../Network/lwip2.0.3/include/lwip/netif.h | 490 ++ .../Network/lwip2.0.3/include/lwip/netifapi.h | 140 + sdk/Src/Network/lwip2.0.3/include/lwip/opt.h | 2877 +++++++++ sdk/Src/Network/lwip2.0.3/include/lwip/pbuf.h | 265 + .../Network/lwip2.0.3/include/lwip/pppapi.h | 151 + .../lwip2.0.3/include/lwip/priv/api_msg.h | 216 + .../lwip2.0.3/include/lwip/priv/memp_priv.h | 183 + .../lwip2.0.3/include/lwip/priv/memp_std.h | 146 + .../lwip2.0.3/include/lwip/priv/nd6_priv.h | 144 + .../lwip2.0.3/include/lwip/priv/tcp_priv.h | 507 ++ .../lwip2.0.3/include/lwip/priv/tcpip_priv.h | 170 + .../lwip2.0.3/include/lwip/prot/autoip.h | 78 + .../lwip2.0.3/include/lwip/prot/dhcp.h | 183 + .../Network/lwip2.0.3/include/lwip/prot/dns.h | 140 + .../lwip2.0.3/include/lwip/prot/etharp.h | 91 + .../lwip2.0.3/include/lwip/prot/ethernet.h | 170 + .../lwip2.0.3/include/lwip/prot/icmp.h | 91 + .../lwip2.0.3/include/lwip/prot/icmp6.h | 170 + .../lwip2.0.3/include/lwip/prot/igmp.h | 90 + .../Network/lwip2.0.3/include/lwip/prot/ip.h | 51 + .../Network/lwip2.0.3/include/lwip/prot/ip4.h | 127 + .../Network/lwip2.0.3/include/lwip/prot/ip6.h | 169 + .../lwip2.0.3/include/lwip/prot/mld6.h | 70 + .../Network/lwip2.0.3/include/lwip/prot/nd6.h | 277 + .../Network/lwip2.0.3/include/lwip/prot/tcp.h | 97 + .../Network/lwip2.0.3/include/lwip/prot/udp.h | 68 + sdk/Src/Network/lwip2.0.3/include/lwip/raw.h | 118 + sdk/Src/Network/lwip2.0.3/include/lwip/sio.h | 142 + sdk/Src/Network/lwip2.0.3/include/lwip/snmp.h | 213 + .../Network/lwip2.0.3/include/lwip/sockets.h | 602 ++ .../Network/lwip2.0.3/include/lwip/stats.h | 491 ++ sdk/Src/Network/lwip2.0.3/include/lwip/sys.h | 455 ++ sdk/Src/Network/lwip2.0.3/include/lwip/tcp.h | 440 ++ .../Network/lwip2.0.3/include/lwip/tcpip.h | 108 + .../Network/lwip2.0.3/include/lwip/timeouts.h | 125 + .../Network/lwip2.0.3/include/lwip/timers.h | 106 + sdk/Src/Network/lwip2.0.3/include/lwip/udp.h | 184 + .../Network/lwip2.0.3/include/netif/etharp.h | 3 + .../lwip2.0.3/include/netif/ethernet.h | 85 + .../lwip2.0.3/include/netif/ethernetif.h | 58 + .../Network/lwip2.0.3/include/netif/lowpan6.h | 86 + .../lwip2.0.3/include/netif/lowpan6_opts.h | 70 + .../Network/lwip2.0.3/include/netif/ppp/ccp.h | 156 + .../lwip2.0.3/include/netif/ppp/chap-md5.h | 36 + .../lwip2.0.3/include/netif/ppp/chap-new.h | 192 + .../lwip2.0.3/include/netif/ppp/chap_ms.h | 44 + .../Network/lwip2.0.3/include/netif/ppp/eap.h | 169 + .../Network/lwip2.0.3/include/netif/ppp/ecp.h | 50 + .../lwip2.0.3/include/netif/ppp/eui64.h | 94 + .../Network/lwip2.0.3/include/netif/ppp/fsm.h | 175 + .../lwip2.0.3/include/netif/ppp/ipcp.h | 126 + .../lwip2.0.3/include/netif/ppp/ipv6cp.h | 183 + .../Network/lwip2.0.3/include/netif/ppp/lcp.h | 171 + .../lwip2.0.3/include/netif/ppp/magic.h | 122 + .../lwip2.0.3/include/netif/ppp/mppe.h | 173 + .../include/netif/ppp/polarssl/arc4.h | 81 + .../include/netif/ppp/polarssl/des.h | 92 + .../include/netif/ppp/polarssl/md4.h | 97 + .../include/netif/ppp/polarssl/md5.h | 96 + .../include/netif/ppp/polarssl/sha1.h | 96 + .../Network/lwip2.0.3/include/netif/ppp/ppp.h | 690 +++ .../lwip2.0.3/include/netif/ppp/ppp_impl.h | 629 ++ .../lwip2.0.3/include/netif/ppp/ppp_opts.h | 593 ++ .../lwip2.0.3/include/netif/ppp/pppapi.h | 137 + .../lwip2.0.3/include/netif/ppp/pppcrypt.h | 136 + .../lwip2.0.3/include/netif/ppp/pppdebug.h | 80 + .../lwip2.0.3/include/netif/ppp/pppoe.h | 179 + .../lwip2.0.3/include/netif/ppp/pppol2tp.h | 201 + .../lwip2.0.3/include/netif/ppp/pppos.h | 118 + .../lwip2.0.3/include/netif/ppp/upap.h | 123 + .../Network/lwip2.0.3/include/netif/ppp/vj.h | 161 + .../Network/lwip2.0.3/include/netif/slipif.h | 87 + .../Network/lwip2.0.3/include/posix/errno.h | 33 + .../Network/lwip2.0.3/include/posix/netdb.h | 33 + .../lwip2.0.3/include/posix/sys/socket.h | 33 + sdk/Src/Network/lwip2.0.3/netif/FILES | 24 + sdk/Src/Network/lwip2.0.3/netif/ethernet.c | 314 + sdk/Src/Network/lwip2.0.3/netif/ethernetif.c | 428 ++ sdk/Src/Network/lwip2.0.3/netif/lowpan6.c | 1193 ++++ .../Network/lwip2.0.3/netif/ppp/PPPD_FOLLOWUP | 473 ++ sdk/Src/Network/lwip2.0.3/netif/ppp/auth.c | 2510 ++++++++ sdk/Src/Network/lwip2.0.3/netif/ppp/ccp.c | 1740 ++++++ .../Network/lwip2.0.3/netif/ppp/chap-md5.c | 126 + .../Network/lwip2.0.3/netif/ppp/chap-new.c | 677 +++ sdk/Src/Network/lwip2.0.3/netif/ppp/chap_ms.c | 962 +++ sdk/Src/Network/lwip2.0.3/netif/ppp/demand.c | 465 ++ sdk/Src/Network/lwip2.0.3/netif/ppp/eap.c | 2423 ++++++++ sdk/Src/Network/lwip2.0.3/netif/ppp/ecp.c | 191 + sdk/Src/Network/lwip2.0.3/netif/ppp/eui64.c | 56 + sdk/Src/Network/lwip2.0.3/netif/ppp/fsm.c | 799 +++ sdk/Src/Network/lwip2.0.3/netif/ppp/ipcp.c | 2418 ++++++++ sdk/Src/Network/lwip2.0.3/netif/ppp/ipv6cp.c | 1533 +++++ sdk/Src/Network/lwip2.0.3/netif/ppp/lcp.c | 2790 +++++++++ sdk/Src/Network/lwip2.0.3/netif/ppp/magic.c | 294 + sdk/Src/Network/lwip2.0.3/netif/ppp/mppe.c | 412 ++ .../Network/lwip2.0.3/netif/ppp/multilink.c | 609 ++ .../lwip2.0.3/netif/ppp/polarssl/README | 22 + .../lwip2.0.3/netif/ppp/polarssl/arc4.c | 101 + .../lwip2.0.3/netif/ppp/polarssl/des.c | 422 ++ .../lwip2.0.3/netif/ppp/polarssl/md4.c | 281 + .../lwip2.0.3/netif/ppp/polarssl/md5.c | 300 + .../lwip2.0.3/netif/ppp/polarssl/sha1.c | 335 ++ sdk/Src/Network/lwip2.0.3/netif/ppp/ppp.c | 1647 ++++++ sdk/Src/Network/lwip2.0.3/netif/ppp/pppapi.c | 427 ++ .../Network/lwip2.0.3/netif/ppp/pppcrypt.c | 66 + sdk/Src/Network/lwip2.0.3/netif/ppp/pppoe.c | 1191 ++++ .../Network/lwip2.0.3/netif/ppp/pppol2tp.c | 1131 ++++ sdk/Src/Network/lwip2.0.3/netif/ppp/pppos.c | 875 +++ sdk/Src/Network/lwip2.0.3/netif/ppp/upap.c | 677 +++ sdk/Src/Network/lwip2.0.3/netif/ppp/utils.c | 959 +++ sdk/Src/Network/lwip2.0.3/netif/ppp/vj.c | 695 +++ sdk/Src/Network/lwip2.0.3/netif/slipif.c | 555 ++ sdk/Src/Network/lwip2.0.3/netif/wm_ethernet.c | 477 ++ sdk/Src/Network/lwip2.0.3/sys_arch.c | 439 ++ sdk/Src/OS/RTOS/CMakeLists.txt | 51 + sdk/Src/OS/RTOS/include/FreeRTOS.h | 481 ++ sdk/Src/OS/RTOS/include/FreeRTOSConfig.h | 118 + sdk/Src/OS/RTOS/include/StackMacros.h | 168 + sdk/Src/OS/RTOS/include/croutine.h | 746 +++ sdk/Src/OS/RTOS/include/mpu_wrappers.h | 135 + sdk/Src/OS/RTOS/include/portable.h | 391 ++ sdk/Src/OS/RTOS/include/portmacro.h | 171 + sdk/Src/OS/RTOS/include/portmacro.inc | 115 + sdk/Src/OS/RTOS/include/projdefs.h | 77 + sdk/Src/OS/RTOS/include/rtoslist.h | 308 + sdk/Src/OS/RTOS/include/rtosqueue.h | 1287 ++++ sdk/Src/OS/RTOS/include/rtostimers.h | 954 +++ sdk/Src/OS/RTOS/include/semphr.h | 724 +++ sdk/Src/OS/RTOS/include/task.h | 1317 +++++ sdk/Src/OS/RTOS/ports/port_m3.c | 403 ++ sdk/Src/OS/RTOS/ports/port_m3_gcc.c | 352 ++ sdk/Src/OS/RTOS/source/croutine.c | 376 ++ sdk/Src/OS/RTOS/source/heap_2.c | 288 + sdk/Src/OS/RTOS/source/heap_3.c | 122 + sdk/Src/OS/RTOS/source/heap_4.c | 600 ++ sdk/Src/OS/RTOS/source/list.c | 194 + sdk/Src/OS/RTOS/source/queue.c | 1605 +++++ sdk/Src/OS/RTOS/source/rtostimers.c | 721 +++ sdk/Src/OS/RTOS/source/tasks.c | 2737 +++++++++ sdk/Src/OS/RTOS/wm_osal_rtos.c | 1213 ++++ sdk/Src/OS/RTOS/wm_rtos.c | 32 + sdk/ld/link_w600_2m.ld | 154 + sdk/tools/wm_tool.c | 4956 ++++++++++++++++ src/app_main.c | 22 + src/sys/fwup_hook_default.c | 10 + src/sys/nl_fwup_hook.h | 12 + src/sys/sdk_stubs.c | 14 + tools/docker.sh | 7 + tools/flash.sh | 13 + tools/monitor.sh | 5 + 610 files changed, 224089 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/arm-none-eabi.cmake create mode 100644 cmake/image.cmake create mode 100644 cmake/sdk.cmake create mode 100644 cmake/version.cmake create mode 100644 config.local.cmake.example create mode 100644 docker/Dockerfile create mode 100644 sdk/Bin/secboot.img create mode 100644 sdk/Include/App/wm_at_ri_init.h create mode 100644 sdk/Include/App/wm_cloud.h create mode 100644 sdk/Include/App/wm_crypto.h create mode 100644 sdk/Include/App/wm_dhcp_server.h create mode 100644 sdk/Include/App/wm_dlna_dmr.h create mode 100644 sdk/Include/App/wm_dns_server.h create mode 100644 sdk/Include/App/wm_http_client.h create mode 100644 sdk/Include/App/wm_netif.h create mode 100644 sdk/Include/App/wm_netif2.0.3.h create mode 100644 sdk/Include/App/wm_ntp.h create mode 100644 sdk/Include/App/wm_webserver.h create mode 100644 sdk/Include/App/wm_wifi_oneshot.h create mode 100644 sdk/Include/Driver/wm_7816.h create mode 100644 sdk/Include/Driver/wm_adc.h create mode 100644 sdk/Include/Driver/wm_cpu.h create mode 100644 sdk/Include/Driver/wm_dma.h create mode 100644 sdk/Include/Driver/wm_efuse.h create mode 100644 sdk/Include/Driver/wm_flash.h create mode 100644 sdk/Include/Driver/wm_flash_map.h create mode 100644 sdk/Include/Driver/wm_fls_gd25qxx.h create mode 100644 sdk/Include/Driver/wm_gpio.h create mode 100644 sdk/Include/Driver/wm_gpio_afsel.h create mode 100644 sdk/Include/Driver/wm_hostspi.h create mode 100644 sdk/Include/Driver/wm_hspi.h create mode 100644 sdk/Include/Driver/wm_i2c.h create mode 100644 sdk/Include/Driver/wm_i2s.h create mode 100644 sdk/Include/Driver/wm_internal_flash.h create mode 100644 sdk/Include/Driver/wm_io.h create mode 100644 sdk/Include/Driver/wm_iouart.h create mode 100644 sdk/Include/Driver/wm_irq.h create mode 100644 sdk/Include/Driver/wm_lcd.h create mode 100644 sdk/Include/Driver/wm_pmu.h create mode 100644 sdk/Include/Driver/wm_pwm.h create mode 100644 sdk/Include/Driver/wm_rtc.h create mode 100644 sdk/Include/Driver/wm_timer.h create mode 100644 sdk/Include/Driver/wm_uart.h create mode 100644 sdk/Include/Driver/wm_watchdog.h create mode 100644 sdk/Include/Net/wm_socket.h create mode 100644 sdk/Include/Net/wm_socket2.0.3.h create mode 100644 sdk/Include/Net/wm_sockets.h create mode 100644 sdk/Include/Net/wm_sockets2.0.3.h create mode 100644 sdk/Include/OS/wm_config_gcc.inc create mode 100644 sdk/Include/OS/wm_os_config.h create mode 100644 sdk/Include/OS/wm_os_config.inc create mode 100644 sdk/Include/OS/wm_osal.h create mode 100644 sdk/Include/Platform/aes.h create mode 100644 sdk/Include/Platform/wm_crypto_hard.h create mode 100644 sdk/Include/Platform/wm_fwup.h create mode 100644 sdk/Include/Platform/wm_mem.h create mode 100644 sdk/Include/Platform/wm_params.h create mode 100644 sdk/Include/Platform/wm_wl_mbox.h create mode 100644 sdk/Include/Platform/wm_wl_task.h create mode 100644 sdk/Include/Platform/wm_wl_timers.h create mode 100644 sdk/Include/WiFi/litepoint.h create mode 100644 sdk/Include/WiFi/wm_ieee80211.h create mode 100644 sdk/Include/WiFi/wm_ieee80211_gcc.h create mode 100644 sdk/Include/WiFi/wm_wifi.h create mode 100644 sdk/Include/list.h create mode 100644 sdk/Include/random.h create mode 100644 sdk/Include/wm_config.h create mode 100644 sdk/Include/wm_debug.h create mode 100644 sdk/Include/wm_include.h create mode 100644 sdk/Include/wm_log.h create mode 100644 sdk/Include/wm_ram_config.h create mode 100644 sdk/Include/wm_regs.h create mode 100644 sdk/Include/wm_type_def.h create mode 100644 sdk/Include/wm_wifi_config.h create mode 100644 sdk/Lib/GNU/libwlan.a create mode 100644 sdk/PATCHES.md create mode 100644 sdk/Platform/Boot/gcc/core_cm3.h create mode 100644 sdk/Platform/Boot/gcc/misc.c create mode 100644 sdk/Platform/Boot/gcc/retarget.c create mode 100644 sdk/Platform/Boot/gcc/startup.S create mode 100644 sdk/Platform/Common/CMakeLists.txt create mode 100644 sdk/Platform/Common/Params/wm_param.c create mode 100644 sdk/Platform/Common/Params/wm_param.h create mode 100644 sdk/Platform/Common/crypto/wm_crypto_hard.c create mode 100644 sdk/Platform/Common/fwup/wm_fwup.c create mode 100644 sdk/Platform/Common/mem/wm_mem.c create mode 100644 sdk/Platform/Common/task/wm_wl_mbox.c create mode 100644 sdk/Platform/Common/task/wm_wl_task.c create mode 100644 sdk/Platform/Common/task/wm_wl_timers.c create mode 100644 sdk/Platform/Common/utils/utils.c create mode 100644 sdk/Platform/Common/wm_common.c create mode 100644 sdk/Platform/Drivers/7816/wm_7816.c create mode 100644 sdk/Platform/Drivers/CMakeLists.txt create mode 100644 sdk/Platform/Drivers/adc/wm_adc.c create mode 100644 sdk/Platform/Drivers/cpu/wm_cpu.c create mode 100644 sdk/Platform/Drivers/dma/wm_dma.c create mode 100644 sdk/Platform/Drivers/efuse/wm_efuse.c create mode 100644 sdk/Platform/Drivers/flash/wm_fls.c create mode 100644 sdk/Platform/Drivers/flash/wm_fls_gd25qxx.c create mode 100644 sdk/Platform/Drivers/gpio/wm_gpio.c create mode 100644 sdk/Platform/Drivers/gpio/wm_gpio_afsel.c create mode 100644 sdk/Platform/Drivers/hspi/wm_hspi.c create mode 100644 sdk/Platform/Drivers/i2c/wm_i2c.c create mode 100644 sdk/Platform/Drivers/i2s/wm_i2s.c create mode 100644 sdk/Platform/Drivers/internalflash/wm_internal_fls.c create mode 100644 sdk/Platform/Drivers/io/wm_io.c create mode 100644 sdk/Platform/Drivers/iouart/wm_iouart.c create mode 100644 sdk/Platform/Drivers/irq/wm_irq.c create mode 100644 sdk/Platform/Drivers/lcd/wm_lcd.c create mode 100644 sdk/Platform/Drivers/pmu/wm_pmu.c create mode 100644 sdk/Platform/Drivers/pwm/wm_pwm.c create mode 100644 sdk/Platform/Drivers/rtc/wm_rtc.c create mode 100644 sdk/Platform/Drivers/spi/wm_hostspi.c create mode 100644 sdk/Platform/Drivers/spi/wm_spi_hal.h create mode 100644 sdk/Platform/Drivers/timer/wm_timer.c create mode 100644 sdk/Platform/Drivers/uart/wm_uart.c create mode 100644 sdk/Platform/Drivers/watchdog/wm_watchdog.c create mode 100644 sdk/Platform/Drivers/wm_driver.c create mode 100644 sdk/Platform/Inc/bitops.h create mode 100644 sdk/Platform/Inc/libtommath.h create mode 100644 sdk/Platform/Inc/md5.h create mode 100644 sdk/Platform/Inc/misc.h create mode 100644 sdk/Platform/Inc/os.h create mode 100644 sdk/Platform/Inc/rc4.h create mode 100644 sdk/Platform/Inc/sha1.h create mode 100644 sdk/Platform/Inc/sha256.h create mode 100644 sdk/Platform/Inc/tls_common.h create mode 100644 sdk/Platform/Inc/tls_ieee80211.h create mode 100644 sdk/Platform/Inc/tls_wireless.h create mode 100644 sdk/Platform/Inc/utils.h create mode 100644 sdk/Platform/Inc/wm_dbg.h create mode 100644 sdk/Platform/Sys/CMakeLists.txt create mode 100644 sdk/Platform/Sys/tls_sys.c create mode 100644 sdk/Platform/Sys/tls_sys.h create mode 100644 sdk/Platform/Sys/wm_main.c create mode 100644 sdk/Src/App/OTA/CMakeLists.txt create mode 100644 sdk/Src/App/OTA/wm_http_fwup.c create mode 100644 sdk/Src/App/OTA/wm_http_fwup.h create mode 100644 sdk/Src/App/OTA/wm_socket_fwup.c create mode 100644 sdk/Src/App/OTA/wm_socket_fwup.h create mode 100644 sdk/Src/App/dhcpserver/CMakeLists.txt create mode 100644 sdk/Src/App/dhcpserver/dhcp_server.c create mode 100644 sdk/Src/App/dhcpserver/dhcp_server.h create mode 100644 sdk/Src/App/dnsserver/CMakeLists.txt create mode 100644 sdk/Src/App/dnsserver/dns_server.c create mode 100644 sdk/Src/App/dnsserver/dns_server.h create mode 100644 sdk/Src/App/httpclient/CMakeLists.txt create mode 100644 sdk/Src/App/httpclient/HTTPClient.c create mode 100644 sdk/Src/App/httpclient/HTTPClient.h create mode 100644 sdk/Src/App/httpclient/HTTPClientAuth.c create mode 100644 sdk/Src/App/httpclient/HTTPClientAuth.h create mode 100644 sdk/Src/App/httpclient/HTTPClientCommon.h create mode 100644 sdk/Src/App/httpclient/HTTPClientString.c create mode 100644 sdk/Src/App/httpclient/HTTPClientString.h create mode 100644 sdk/Src/App/httpclient/HTTPClientWrapper.c create mode 100644 sdk/Src/App/httpclient/HTTPClientWrapper.h create mode 100644 sdk/Src/App/httpclient/wm_http_compile.c create mode 100644 sdk/Src/App/httpclient/wm_httpclient_if.c create mode 100644 sdk/Src/App/httpclient/wm_httpclient_task.c create mode 100644 sdk/Src/Network/CMakeLists.txt create mode 100644 sdk/Src/Network/api2.0.3/tls_netconn.c create mode 100644 sdk/Src/Network/api2.0.3/tls_netconn.h create mode 100644 sdk/Src/Network/api2.0.3/tls_sockets.c create mode 100644 sdk/Src/Network/lwip2.0.3.c create mode 100644 sdk/Src/Network/lwip2.0.3/FILES create mode 100644 sdk/Src/Network/lwip2.0.3/Filelists.mk create mode 100644 sdk/Src/Network/lwip2.0.3/api/api_lib.c create mode 100644 sdk/Src/Network/lwip2.0.3/api/api_msg.c create mode 100644 sdk/Src/Network/lwip2.0.3/api/err.c create mode 100644 sdk/Src/Network/lwip2.0.3/api/netbuf.c create mode 100644 sdk/Src/Network/lwip2.0.3/api/netdb.c create mode 100644 sdk/Src/Network/lwip2.0.3/api/netifapi.c create mode 100644 sdk/Src/Network/lwip2.0.3/api/pppapi.c create mode 100644 sdk/Src/Network/lwip2.0.3/api/sockets.c create mode 100644 sdk/Src/Network/lwip2.0.3/api/tcpip.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/fs.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/fs/404.html create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/fs/img/sics.gif create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/fs/index.html create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.h create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/httpd.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/httpd_structs.h create mode 100755 sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/makefsdata create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/makefsdata.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/readme.txt create mode 100644 sdk/Src/Network/lwip2.0.3/apps/lwiperf/lwiperf.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/mdns/mdns.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/mqtt/mqtt.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/netbiosns/netbiosns.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CCodeGeneration.csproj create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CFile.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CGenerator.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Code.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CodeContainerBase.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CodeElement.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Comment.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/EmptyLine.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Function.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/FunctionDeclaration.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/IfThenElse.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_If.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Ifdef.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Include.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Macro.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PlainText.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Properties/AssemblyInfo.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Switch.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariableDeclaration.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariablePrototype.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariableType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler.sln create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/LwipMibCompiler.csproj create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/Program.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/Properties/AssemblyInfo.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/app.config create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.Designer.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.resx create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/LwipMibViewer.csproj create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Program.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/AssemblyInfo.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Resources.Designer.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Resources.resx create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Settings.Designer.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Settings.settings create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/app.config create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/IRestriction.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/LwipSnmp.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/LwipSnmpCodeGeneration.csproj create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/MibCFile.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/MibHeaderFile.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/Properties/AssemblyInfo.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpMib.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpNode.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarAggregationNode.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarArrayNode.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNode.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeBits.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeCounter64.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeInt.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeObjectIdentifier.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeOctetString.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeTruthValue.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeUint.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpTableNode.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpTreeNode.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA-ADDRESS-FAMILY-NUMBERS-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA-CHARSET-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-ITU-ALARM-TC-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-LANGUAGE-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-MALLOC-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-MAU-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-PRINTER-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-RTPROTO-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANATn3270eTC-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANAifType-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IF-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/INET-ADDRESS-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IP-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC-1212 create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC-1215 create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1065-SMI create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1155-SMI create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1158-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1213-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-CONF create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-SMI create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TC create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TM create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/TCP-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/UDP-MIB create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/DisplayHint.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/AgentCapabilities.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/EntityBase.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/IEntity.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ModuleCompliance.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ModuleIdentity.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/NotificationGroup.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/NotificationType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectGroup.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectIdentity.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/OidValueAssignment.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Exports.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/IDeclaration.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/IElement.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/ITypeReferrer.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Imports.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/ImportsFrom.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/TrapType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/BaseType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/BitsType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Choice.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/ITypeAssignment.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/IntegerType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/IpAddressType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Macro.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/ObjectIdentifierType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/OctetStringType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/OpaqueType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Sequence.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/SequenceOf.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/TextualConvention.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/TypeAssignment.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/UnsignedType.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/IModule.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ISymbolEnumerator.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Lexer.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MaxAccess.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibDocument.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibException.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibModule.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibResolver.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTree.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTreeNode.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTypesResolver.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ObjectIdentifier.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Status.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Symbol.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/SymbolList.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ValueMap.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ValueRange.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/AssemblyInfo.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/Resources.Designer.cs create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/Resources.resx create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/SharpSnmpLib.Mib.csproj create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/license.txt create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/sharpsnmplib.snk create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/example/compile_udp_mib.sh create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/README create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_asn1.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_asn1.h create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_core.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_core_priv.h create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_icmp.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_interfaces.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_ip.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_snmp.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_system.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_tcp.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_udp.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_msg.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_msg.h create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_netconn.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_pbuf_stream.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_pbuf_stream.h create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_raw.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_scalar.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_table.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_threadsync.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_traps.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_dummy.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_mbedtls.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_priv.h create mode 100644 sdk/Src/Network/lwip2.0.3/apps/sntp/sntp.c create mode 100644 sdk/Src/Network/lwip2.0.3/apps/tftp/tftp_server.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/alg.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/def.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/dns.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/inet_chksum.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/init.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ip.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/autoip.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/dhcp.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/etharp.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/icmp.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/igmp.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/ip4.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/ip4_addr.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/ip4_frag.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv4/ip_frag.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/dhcp6.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/ethip6.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/icmp6.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/inet6.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/ip6.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/ip6_addr.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/ip6_frag.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/mld6.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/ipv6/nd6.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/mem.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/memp.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/netif.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/pbuf.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/raw.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/stats.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/sys.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/tcp.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/tcp_in.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/tcp_out.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/timeouts.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/timers.c create mode 100644 sdk/Src/Network/lwip2.0.3/core/udp.c create mode 100644 sdk/Src/Network/lwip2.0.3/include/arch/cc.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/arch/perf.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/arch/sys_arch.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/alg.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/api.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/FILES create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/fs.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/httpd.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/httpd_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/lwiperf.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns_priv.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/mqtt.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/mqtt_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/netbiosns.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/netbiosns_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_core.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_mib2.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_scalar.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_table.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_threadsync.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmpv3.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/sntp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/sntp_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/tftp_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/apps/tftp_server.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/arch.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/autoip.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/debug.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/def.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/dhcp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/dhcp6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/dns.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/err.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/errno.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/etharp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ethip6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/icmp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/icmp6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/igmp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/inet.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/inet_chksum.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/init.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip4.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip4_addr.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip4_frag.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip6_addr.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip6_frag.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip_addr.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/ip_frag.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/lwipopts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/mem.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/memp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/mld6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/nd6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/netbuf.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/netdb.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/netif.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/netifapi.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/opt.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/pbuf.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/pppapi.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/priv/api_msg.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/priv/memp_priv.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/priv/memp_std.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/priv/nd6_priv.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/priv/tcp_priv.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/priv/tcpip_priv.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/autoip.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/dhcp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/dns.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/etharp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/ethernet.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/icmp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/icmp6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/igmp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip4.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/mld6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/nd6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/tcp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/prot/udp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/raw.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/sio.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/snmp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/sockets.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/stats.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/sys.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/tcp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/tcpip.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/timeouts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/timers.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/lwip/udp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/etharp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ethernet.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ethernetif.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/lowpan6.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/lowpan6_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/ccp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap-md5.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap-new.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap_ms.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/eap.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/ecp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/eui64.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/fsm.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/ipcp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/ipv6cp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/lcp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/magic.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/mppe.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/arc4.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/des.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/md4.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/md5.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/sha1.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp_impl.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp_opts.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppapi.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppcrypt.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppdebug.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppoe.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppol2tp.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppos.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/upap.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/ppp/vj.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/netif/slipif.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/posix/errno.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/posix/netdb.h create mode 100644 sdk/Src/Network/lwip2.0.3/include/posix/sys/socket.h create mode 100644 sdk/Src/Network/lwip2.0.3/netif/FILES create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ethernet.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ethernetif.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/lowpan6.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/PPPD_FOLLOWUP create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/auth.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/ccp.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/chap-md5.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/chap-new.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/chap_ms.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/demand.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/eap.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/ecp.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/eui64.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/fsm.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/ipcp.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/ipv6cp.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/lcp.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/magic.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/mppe.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/multilink.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/README create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/arc4.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/des.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/md4.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/md5.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/sha1.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/ppp.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/pppapi.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/pppcrypt.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/pppoe.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/pppol2tp.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/pppos.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/upap.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/utils.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/ppp/vj.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/slipif.c create mode 100644 sdk/Src/Network/lwip2.0.3/netif/wm_ethernet.c create mode 100644 sdk/Src/Network/lwip2.0.3/sys_arch.c create mode 100644 sdk/Src/OS/RTOS/CMakeLists.txt create mode 100644 sdk/Src/OS/RTOS/include/FreeRTOS.h create mode 100644 sdk/Src/OS/RTOS/include/FreeRTOSConfig.h create mode 100644 sdk/Src/OS/RTOS/include/StackMacros.h create mode 100644 sdk/Src/OS/RTOS/include/croutine.h create mode 100644 sdk/Src/OS/RTOS/include/mpu_wrappers.h create mode 100644 sdk/Src/OS/RTOS/include/portable.h create mode 100644 sdk/Src/OS/RTOS/include/portmacro.h create mode 100644 sdk/Src/OS/RTOS/include/portmacro.inc create mode 100644 sdk/Src/OS/RTOS/include/projdefs.h create mode 100644 sdk/Src/OS/RTOS/include/rtoslist.h create mode 100644 sdk/Src/OS/RTOS/include/rtosqueue.h create mode 100644 sdk/Src/OS/RTOS/include/rtostimers.h create mode 100644 sdk/Src/OS/RTOS/include/semphr.h create mode 100644 sdk/Src/OS/RTOS/include/task.h create mode 100644 sdk/Src/OS/RTOS/ports/port_m3.c create mode 100644 sdk/Src/OS/RTOS/ports/port_m3_gcc.c create mode 100644 sdk/Src/OS/RTOS/source/croutine.c create mode 100644 sdk/Src/OS/RTOS/source/heap_2.c create mode 100644 sdk/Src/OS/RTOS/source/heap_3.c create mode 100644 sdk/Src/OS/RTOS/source/heap_4.c create mode 100644 sdk/Src/OS/RTOS/source/list.c create mode 100644 sdk/Src/OS/RTOS/source/queue.c create mode 100644 sdk/Src/OS/RTOS/source/rtostimers.c create mode 100644 sdk/Src/OS/RTOS/source/tasks.c create mode 100644 sdk/Src/OS/RTOS/wm_osal_rtos.c create mode 100644 sdk/Src/OS/RTOS/wm_rtos.c create mode 100644 sdk/ld/link_w600_2m.ld create mode 100644 sdk/tools/wm_tool.c create mode 100644 src/app_main.c create mode 100644 src/sys/fwup_hook_default.c create mode 100644 src/sys/nl_fwup_hook.h create mode 100644 src/sys/sdk_stubs.c create mode 100755 tools/docker.sh create mode 100755 tools/flash.sh create mode 100755 tools/monitor.sh diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f21e29c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.16) + +if(EXISTS ${CMAKE_SOURCE_DIR}/config.local.cmake) + include(${CMAKE_SOURCE_DIR}/config.local.cmake) +endif() + +project(ninlight C ASM) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(NINLIGHT_DEVICES evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw) +set(NINLIGHT_DEVICE "" CACHE STRING "Target device") +if(NOT NINLIGHT_DEVICE IN_LIST NINLIGHT_DEVICES) + message(FATAL_ERROR "Set -DNINLIGHT_DEVICE=") +endif() + +if(NOT DEFINED NINLIGHT_TOKEN) + set(NINLIGHT_TOKEN "aabbccddeeff0011223344") +endif() + +include(cmake/version.cmake) +include(cmake/sdk.cmake) +include(cmake/image.cmake) + +set(NL_FW_SOURCES + src/app_main.c + src/util/nl_sha256.c + src/util/nl_crc.c + src/sys/fwup_hook_default.c + src/sys/sdk_stubs.c) + +set(NL_FW ninlight_${NINLIGHT_DEVICE}) +add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS}) +set_target_properties(${NL_FW} PROPERTIES SUFFIX ".elf") +target_compile_definitions(${NL_FW} PRIVATE + NINLIGHT_DEVICE_ID="${NINLIGHT_DEVICE}" + NINLIGHT_FW_VERSION="${NINLIGHT_FW_VERSION}" + NINLIGHT_BUILD_DATE="${NINLIGHT_BUILD_DATE}" + NINLIGHT_TOKEN_DEFAULT="${NINLIGHT_TOKEN}") +target_link_options(${NL_FW} PRIVATE + -static -nostartfiles -T${SDK_DIR}/ld/link_w600_2m.ld + -Wl,-Map=${CMAKE_BINARY_DIR}/${NL_FW}.map) +target_link_libraries(${NL_FW} PRIVATE + -Wl,--whole-archive ${SDK_DIR}/Lib/GNU/libwlan.a -Wl,--no-whole-archive m) +nl_add_images(${NL_FW}) + +message(STATUS "ninlight ${NINLIGHT_FW_VERSION} for ${NINLIGHT_DEVICE}") diff --git a/cmake/arm-none-eabi.cmake b/cmake/arm-none-eabi.cmake new file mode 100644 index 0000000..cc77aa8 --- /dev/null +++ b/cmake/arm-none-eabi.cmake @@ -0,0 +1,24 @@ +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Official ARM GNU toolchain (ships newlib). In Docker the compiler is found via PATH. +file(GLOB _nl_arm_bins "/Applications/ArmGNUToolchain/*/arm-none-eabi/bin") +list(SORT _nl_arm_bins ORDER DESCENDING) +set(_nl_arm_default "") +if(_nl_arm_bins) + list(GET _nl_arm_bins 0 _nl_arm_default) +endif() +set(NL_ARM_TOOLCHAIN_BIN "${_nl_arm_default}" CACHE PATH "bin directory of the ARM GNU toolchain (empty = PATH)") + +if(NL_ARM_TOOLCHAIN_BIN) + set(_nl_prefix "${NL_ARM_TOOLCHAIN_BIN}/arm-none-eabi-") +else() + set(_nl_prefix "arm-none-eabi-") +endif() + +set(CMAKE_C_COMPILER "${_nl_prefix}gcc") +set(CMAKE_ASM_COMPILER "${_nl_prefix}gcc") +set(CMAKE_OBJCOPY "${_nl_prefix}objcopy" CACHE INTERNAL "") +set(CMAKE_SIZE "${_nl_prefix}size" CACHE INTERNAL "") +set(CMAKE_EXE_LINKER_FLAGS_INIT "--specs=nosys.specs") diff --git a/cmake/image.cmake b/cmake/image.cmake new file mode 100644 index 0000000..7cfb8fe --- /dev/null +++ b/cmake/image.cmake @@ -0,0 +1,20 @@ +# Host tool wm_tool packs the firmware into .img / _gz.img / .fls. +set(NL_WM_TOOL ${CMAKE_BINARY_DIR}/wm_tool) +add_custom_command(OUTPUT ${NL_WM_TOOL} + COMMAND cc -O2 -w -o ${NL_WM_TOOL} ${SDK_DIR}/tools/wm_tool.c -lpthread + DEPENDS ${SDK_DIR}/tools/wm_tool.c + COMMENT "Building host tool wm_tool") +add_custom_target(nl_wm_tool DEPENDS ${NL_WM_TOOL}) + +# 2M layout: update area 0x100000, run area 0x10100 (same as the old Klyqa images). +function(nl_add_images target) + add_dependencies(${target} nl_wm_tool) + set(out ${CMAKE_BINARY_DIR}/${target}) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_OBJCOPY} --output-target=binary -S -g -x -X -R .sbss -R .bss -R .reginfo -R .stack + $ ${out}.bin + COMMAND ${NL_WM_TOOL} -b ${out}.bin -sb ${SDK_DIR}/Bin/secboot.img -fc compress -it 2M + -ua 100000 -ra 10100 -df -o ${out} + COMMAND ${CMAKE_SIZE} $ + COMMENT "Packing ${target}.fls and ${target}_gz.img") +endfunction() diff --git a/cmake/sdk.cmake b/cmake/sdk.cmake new file mode 100644 index 0000000..4fc16be --- /dev/null +++ b/cmake/sdk.cmake @@ -0,0 +1,49 @@ +# W600 SDK: compiler flags, include paths and the SDK object libraries. +set(SDK_DIR ${CMAKE_SOURCE_DIR}/sdk) + +set(NL_ARCH_FLAGS "-mthumb -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs") +set(CMAKE_C_FLAGS "${NL_ARCH_FLAGS} --function-sections --data-sections -std=gnu99 -fno-builtin -DGCC_COMPILE=1 -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__") +set(CMAKE_C_FLAGS_RELEASE "-Os") +set(CMAKE_C_FLAGS_DEBUG "-Og -g3") +set(CMAKE_ASM_FLAGS "${NL_ARCH_FLAGS} -mthumb-interwork") + +# The SDK predates the GCC 10/14 defaults (-fno-common, implicit declarations are errors). +# gcc 4.9 (the Docker build toolchain, see docker/Dockerfile) does not know these options and +# already defaults to common symbols / warnings-only, so only add them for newer host compilers. +# Directory-level options are inherited by the SDK subdirectories below. +if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10) + add_compile_options( + -fcommon + -Wno-error=implicit-function-declaration + -Wno-error=incompatible-pointer-types + -Wno-error=int-conversion + -Wno-error=implicit-int + -Wno-error=return-mismatch) +endif() + +include_directories( + ${SDK_DIR}/Include ${SDK_DIR}/Include/App ${SDK_DIR}/Include/Net ${SDK_DIR}/Include/WiFi + ${SDK_DIR}/Include/OS ${SDK_DIR}/Include/Driver ${SDK_DIR}/Include/Platform + ${SDK_DIR}/Src/OS/RTOS/include + ${SDK_DIR}/Platform/Boot/gcc ${SDK_DIR}/Platform/Common/Params ${SDK_DIR}/Platform/Common/task + ${SDK_DIR}/Platform/Common/mem ${SDK_DIR}/Platform/Common/fwup ${SDK_DIR}/Platform/Common/utils + ${SDK_DIR}/Platform/Common/crypto ${SDK_DIR}/Platform/Inc ${SDK_DIR}/Platform/Sys + ${SDK_DIR}/Src/App/OTA ${SDK_DIR}/Src/App/dhcpserver ${SDK_DIR}/Src/App/dnsserver ${SDK_DIR}/Src/App/httpclient + ${SDK_DIR}/Src/Network/lwip2.0.3/include ${SDK_DIR}/Src/Network/api2.0.3 + ${CMAKE_SOURCE_DIR}/src) + +foreach(dir Platform/Common Platform/Drivers Platform/Sys Src/Network Src/OS/RTOS + Src/App/dhcpserver Src/App/dnsserver Src/App/httpclient Src/App/OTA) + string(REPLACE "/" "_" bindir ${dir}) + add_subdirectory(${SDK_DIR}/${dir} sdk_${bindir}) +endforeach() + +set(NL_SDK_OBJECTS + $ $ $ + $ $ $ + $ $ $) + +set(NL_SDK_BOOT_SOURCES + ${SDK_DIR}/Platform/Boot/gcc/startup.S + ${SDK_DIR}/Platform/Boot/gcc/retarget.c + ${SDK_DIR}/Platform/Boot/gcc/misc.c) diff --git a/cmake/version.cmake b/cmake/version.cmake new file mode 100644 index 0000000..f0aa868 --- /dev/null +++ b/cmake/version.cmake @@ -0,0 +1,12 @@ +execute_process( + COMMAND git describe --tags --always --dirty + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE NINLIGHT_FW_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) +if(NOT NINLIGHT_FW_VERSION) + set(NINLIGHT_FW_VERSION "0.0.0-dev") +endif() +string(REGEX REPLACE "^v" "" NINLIGHT_FW_VERSION "${NINLIGHT_FW_VERSION}") +# The OTA trailer reserves 16 bytes incl. terminator for the version. +string(SUBSTRING "${NINLIGHT_FW_VERSION}" 0 15 NINLIGHT_FW_VERSION) +string(TIMESTAMP NINLIGHT_BUILD_DATE "%Y-%m-%d") diff --git a/config.local.cmake.example b/config.local.cmake.example new file mode 100644 index 0000000..3265eb4 --- /dev/null +++ b/config.local.cmake.example @@ -0,0 +1,8 @@ +# Copy to config.local.cmake (gitignored) and adjust. +# set(NINLIGHT_TOKEN "change-me-please") +# Used from plan 2 on: +# set(NINLIGHT_WIFI_SSID "MyWifi") +# set(NINLIGHT_WIFI_PASSWORD "secret") +# set(NINLIGHT_AP_PASSWORD "ninlight") +# Used from plan 3/4 on (path to the old RSA signing key): +# set(NINLIGHT_MIGRATION_KEY "/path/to/fw_sign.pem") diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..73c3bc3 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,12 @@ +# Build image: Ubuntu 20.04 (x86_64) with gcc-arm-none-eabi 4.9-2014q4, the toolchain the +# old Klyqa firmware (and libwlan.a) was built with. On Apple Silicon it runs emulated. +FROM ubuntu:20.04 +ARG ARM_TOOLCHAIN=https://launchpad.net/gcc-arm-embedded/4.9/4.9-2014-q4-major/+download/gcc-arm-none-eabi-4_9-2014q4-20141203-linux.tar.bz2 +ENV DEBIAN_FRONTEND=noninteractive +RUN dpkg --add-architecture i386 && apt-get update && apt-get -y --no-install-recommends install \ + ca-certificates build-essential libc6:i386 bzip2 git cmake ninja-build wget python3 && \ + wget -q $ARM_TOOLCHAIN -O /tmp/tc.tar.bz2 && tar -xf /tmp/tc.tar.bz2 -C /usr/local/ && rm /tmp/tc.tar.bz2 && \ + git config --system --add safe.directory /work && \ + apt-get clean && rm -rf /var/lib/apt/lists/* +ENV PATH=$PATH:/usr/local/gcc-arm-none-eabi-4_9-2014q4/bin +WORKDIR /work diff --git a/sdk/Bin/secboot.img b/sdk/Bin/secboot.img new file mode 100644 index 0000000000000000000000000000000000000000..6bd3fe63627d9b27eb58cf7b2ef9d4ee89ebcf1b GIT binary patch literal 26980 zcmc(|dwf$x`aeGBw(>5&u+B9uXnwF+epcIgbxAb;M0ByObEQ$sx8dM}8YE@Rk zP3!7vynM8NaJ6{b>UyCFD^+Xj3JRir8j6b4t_N2sw7YH!EjhiO-+N9%DX!no@B81k zyiU%{GtWFT^UQOenR(`!;YCHMWeNUlgygLv6X$rx|x_`WlWHSmdNj%*2lkpGJ%W;j3XkJ>P`o69*UE6~AZI$T^e*1!BLVQBHit(1!n5qx1PG8c5ltcYr zYb7%f1-Dc0kgQ<W}9m)NJJenMnON`WmTcRIU1W~Bwz9B|Mi%;klml<}-Ry+)122$OoQ!y_?q` z1lImDV@P?(|2ual5q^+&M&TU!=Ug&@lT2RTqfbjpyW9FvvD`)`#kNz(LF*^ooJJ!N zf%m!7uS+MB`hrAgJQ>;|x!T1`7 zgL>&@w9rBMBGsxPmxq&fxG984R4yl{MQGO+AN!sN+)nUi6YbHy4xRAy2zPvQta~_- z2uy$k;%tw(NXR=#>>OGoptea0gYYVmhK&kSm*q`Y&Jb$(|t z9u1!T#`&EO#*ok!ykj5@I^*bE{|hegx90B?zmN$#_cU`suKB08T@W8t_OIxZP!pwN zI!EW@3-VF!{Z{Mv|D|>FPtUx7#z@&lXQxs4ziHpRvs*ThP9x>Vs5br}ztTLKGqi5j zJxX}%u+hJth3(aCr~$#khG7?=s*QJzC-NjFr+Q9{Q}Snfx=hnS4kx ziSxx~mN2ypx!r`Z5osDpMoG(usbnhQI)zHq--b;MR3f2sg~1MD3%R=InmjF<3QvfB zvu9$8%(+a1XpgC4juSe`CYeN*VU0{Z5n3^GLy zD&d=lm!y|MV;w|kR8t+)P`dk7x}ip&N@tunNh|>UPeA`L==&OJpS(Zx>pmd?jmV0O z$OM$|H^Xu0lhKeHhB@gimo;Ar9~wH>E0a&j<(tKa#j#?#cu{>rH%ZX67;1L*`ft=6 zru=I>DL<}+Z&W6Rs*0+M*`pNJ(}CE$^3K%4LuXmk(-2=fM6!4(f0^RoS}0rzuYzn;T6d$2Gp*>eEUlCxb0$(Flulb*Y5lM`+)AhLA~(`+PAkTvm&!YzGHOB^W=VRPI(F1-!Z$!Zjr8*p zmpX221&kw}`J{=H1h2`Hl$MpY(Rxc6#*AEjJB1v!((%INTbS-nC%Mt_f_~ZMkMGop z3E~rC8pb_hXoXIU55xsx1BQSex^Y&0N=;!yT75G3PByZK6R17~*FPVmytO*a_)Z-7 z?h1o6H(GN45-~o&3UPtUU0M0Dfh$~wz@;u{zCKVcmG^9%2&`E8EyV{I;LLQz24=Y~ zD>MYAyYzwC!qli#UkqIT(hG4Xs`Ms-o7{h#t`6Z&_3yz4wGvGCHYPrrNmy;B_^ z@Wjssh(EcS6~~LFy@_ChVr)Bx+=65$Rks z;RWN1?edJk%a9rpXq58^aoiF7Yp-uAcUae@Cp!^hTmRbY&&X*l?@XC}@JuXEvJxbG zH|jTdotq2o?2qZvsDG>uW3mjZ8Hl;*|2Djc3uiki%#^{(ypp*^Igywfxr z8U!VP@8js)$0XTCYCj1S7L1hS0F7}Mlr%e9k_~v5MN3KtmS%XeM0urgoX#FkcCMHWNQrC{?XFAlC0#E2s1Cx+FoITBgvvW>`v-803++q?^(`CkHa*;F=N|AL^yvqTa<7kc^|LphCv%M|g z=rmr1V^yM-sYALTLccsjYtD$g*EfeRAWq*N%sU}%!{F7hTkoYaFi*O#aR6~>r5msN zt{YYN~H)*5NYc9i}DDQQwXv=Ui(98`W#Qy2BM<4sG%tlJZ(rhbRiZ`IV& zzOSRHtw?P_xE7dSAq*-Nhwxq?eF5mn4-Vn?0NTZu(wg&!_q$l{XP~nma4sbDT`YZq z5XQX>zV#!dH6!gKH7%)clJp@$v~?c<+Mq&X`reDcvi3yYLOrO*S!f$~qTHQ;(l);3 zA`(Ugl<|ukBh`WO8=z6A()d|^1@D)D{Tkl2YV9!d_Amndtp$u!UDn~-qZRYS80nR6 zo^Yb*6(AAz;Ta;y>&1$MN{~|i*6G`qmD=J?XH{^a*Ohkp_Lc3j?`qNn$bx+D0vQ5o zMR?&fZ?SA67o~aAwIl?soi!GC+D*l=L1Jr{U(qt2cDW9}M2I`RVHq8TXuL%xekMyN zQTi2f79sU4#&1*J+3!eGj#yhp4%dpN>~rbaYWnJQGWjg#bzjQXtLw_hcXcAA)J!P} zwN5q=caEBgt#Mk|0E6#lUq_`CF~SDkEdFAO45!}wB!7Zk}TP2farAU;i3 zrwPPJ>{qr@qo}E2JenGkrU}rN_MoM=ucTuS`<0Qzws3W5g^9MrY31H1#(Qj?Q?^xj ztybCbN)$!A~_>P1Wmzu12ossC1W!@tpMMEEEDZR)^! zSSjDK^GR{L*yYlByIh?2XBYiaJ6#w%AcrUMPNlQ+rg0M0v?oBLx>$esACLm#by|;O zJ@g~s&g)r;2ugT(uz6RlQ&+;zSX_3Zm8r>hYMx`T?1~Xdl~y!4rr~Q}F}|KIHX9X|9gaybUyO zA4N0i0-AfP>$(WDJW6>-7tzy}lcs}b;>jkF>Mj>_ms4!+B}H)}TeGQ{`Hp>#NV!Qn^f;BqabpL8X&^*chc_0G(Ks&Uon6N1;34#Pc*BBXsI+ zy!YZ+f`{r>s&}cbrC;GCBPH^A!PY`?qcsfLaf{yr$GBjxE5@UQPYjWq$p#)%$k)0V zU1RXyfd1R1#Y4HZ6PA{@dY@iRvN%{yxYvs?zExTSd2$;k=||#9p4`q!F(dKay^n4T z1LJ&--Ms-oVxaY(=HgC2?TkD9e9_UlN9SQJV`Br((@au%Phc%q2aE0iM>H*h7>80} zZ$Y`*5xVIpR|^^eF0S`UPh9U)%MZ>y7^Oin(`_{W3j-L-9NPAF`FV_04-E+7cOC6= zU`D(AbXL1uV~)ay_Z`3x3IgokT+mMmBAtU3YdT^i4$f(A9 zJK8I6qF1P%IH&Qp^`DaqE#yKQ${^%4ld@W0nK4e<0lXd2{4YfFKac!>8kK+Lf9A)y z5Xt`p@Scd~e=M5+QRM&QsQhIk`8l=x1VYljln1%V`mBtG-p4nxN8j&GPvE}8Djsd4 zvoSIu3%c$;(6}#3`<^K6yFmNyQMAX8(AKE5V~EeaK%QXIV7_A`|Kfj2zZ>JyAon2l zJz}8$y`xg^wvL(7+e&+$!_0)Mh&yduKr+YBRV;dbOlG2}L9cz%A46hVn5i0!ct))f z{!4$M_(q4m%fMgbYL~gObgcbSHfix~$KELZh*(Qh9%2knE^rYX9^?~3$0(LmA#&)bq|%9P+ZX&Puw ziy)nmnqkZ6jr|DvvbeQ|nv&E9rYvs2*g5k`7K9p}aHlwLQ?%+mSeBE=ouN zrIct1-Lb_vh|I+`IajxZ` zp7=kNF{e!FvAh%M@m+l(KedXleP!+<%PH6K+*r{?d1$ygb!cC_Z`On3|Jl z<9chM9d<1Lc5Xz0pJ^9ff;{@Dyf!kKlQj&i=BomJtF7dBK}!6^<~_ zzJ-mCpv*dq*AJs@1f%IcTlCTcfboUD2W}OuQTp8o-AiGP?!z>VrYPY?>`wk2w$d2! z4#4h+w&%8Jdsd-6x6<6|OmP?%ZaPyZWCdU=qPU(Y?hU}baTIQ=ipvmcJFG-ml{C$| z0wE&YgkLuErX5~U6WXNY*Rz=(HGIE99Cru*|Fy+%_!XrbG>Slz&Ms&VULLK-rKrbc zqw4XvvVYes#AikEW<>D{fH!><-u((EQXMfliZ?Ne=Kx;rD7;&g(oT|P2gDx56rz~v zz|0thxsYO(CIgbJVj2#Qjbe@grdh?*NVh=;4~H+MxRr5$#i_X3!v>me)l*KX7?Tto zAUZX5;$e-N${;ld^Ni8D;+#I&0DW;zr}z;*r&p-Xp=&xGI^}u1Yw%<|LP*97gwUFV zhsR>)e0&ROsz4bP(KcQgt@%9Eoc7N-iI9dr-9@IH!g_xWpp1hH7URw5m!W252oVR{ zR)V+Oc!iqI1pVo9#~OS`y&Xij3|JRMpG)wjk8h4|&tp5WdzgKdq=f&a)K<>|be6kz z6~*L@IZG#V)~H*c+{_eoMx8guPQu=oUFpPvvzO_TO4B8=&^?8Aut1WjQWTNcSvV z!{NgN8p&(jze_Xu?KaY+nXG9eHeO^y1%oEkOcOFg+r;nmVb6bbzntSWb z=1eD!@b|ts)GB4_*{}wF6awsf*wxzo9}uz!)~PBJbj?ZZyDQ;elmYCR(3KL++28V- z%bXwC*CtKWLKT6&bRN0~wkb9LPip>uBL8+kmNd|H62)e*Q$hFlpH}nqcRY0g{B+0T zD*PANwjx$XCbRgoX{`y|iyM9zl#Vu#Cc0i~KC{i7)xZdBXxcETXC#t$1`(;nm`Bn*}Qms^`w<8~dw#$!fkezse_wK6M8VTCU22vTvsKg-1kx&;Z$) zr`!Z9E9b#}ev{{dl*^Ut)fAJOTSGlcVwLbQMW`YUIY|8+NG{i=&t<=7y7P%|jsdX* zH8zWcJ1CEOqWjv|E1W=A>OxhFz7SgdM|hGbyc3~!DYZ~S426C>0zrb2v`_^xKUP4H1LfK%@c8KmhW@ca?@UxsL{q3h6Z6Qk)Y$r(xCj(pi8 z@XQhT47t6u1n{bv@8E>pyw<%Fm7=cqh+2R=kqBLfn z@e+&Y6MN!(>!o%Nv1g^(J?9?dd>*hQDK?6u{|y{Y#fkVGVBZsdAy)W>P#+Pp^d^>C zN8vf*=DpaL6UR1Wg*Z+tF|bq6P$?dp2rG5yg#jOWgV!-V8;hOri@}?9b|%51o(Nq- zcW+6i(e^K3J~F@{)QQl81NilJx=ANPPPsppP3-gaOrE}l%}Wc?8ISX<(N+B((W_aLW4?QCi(`O)@riOj` zv=O*Zg+p&TLp+IK2ndZ}ggA08r$EEVd4=>EEfH;eO|1(rw#dmWZS%)?)0mm1sUPE= zE2;AzwEub3ZM#}G%8lpF*9|dR0$nFLLw@qaD4H}ril!Pfvy^X-xVL~Wd;2-tCUxKA z-G0Wlkx~!6-LKd-xJ(GWj?jJd`wqR*KNda-o172KEzuXtb`zyA2Ret&pVqpP^Eofo zB$o`Xwwimef827HYa4bTBVkp4slEfIJtPaG30aAjXe62=bFk979Q8K!_!HO`uM2j< zwq+x6nls{cK&=)LF90RHPoa9DH01Ot;SYuvx&`!6qOcS+%U${TOWeu$T3x5JbUt#m zn{s=R%Ur1SEOZgtkNBR;-IQB&Bztg}6^h+Pc!DASaMOcjESI@tCrwU=~E3uxt0a4 za`?`a@G`XKPWT}x;VWRfsYTffhSStGnI@2|9GA26Q-WUfa3$k0!&Sm%YRz(o&1fs< zHKR5ps8e~lVPLg&JotVDW2g^$h0+|0u{sYW6vM{XjNJBNok|Q$OA$OcN zE40x`TL51zd&d*CGFV(fqYeuH@Zl{`ZD=s`;f|bPOU{n2lo&y6;R( zsW}fGDtXjHrPdjdD7vetgfB)rZ&j#nZ^yh{3I86oe;Q@JFdixRy)jmxD~~_8l9Ata+6Eb= z61v5;&25JKY#ou&O(QZoV~7#%S5trQx>tp6aNXk;fK4Taa@;-?kV< zjJB;AkpM!hOECHx+!upu|vzg(qzqgvVxYATgix`!SEo8>rl*L<_MMI7Aq zgS`F;7G)O=h@7q~?(P6yT>AN__ff?YV*+s(w1nk{AjY^A#+2`v#^d1#>+!JO5nm1s!? zYQFqF>SGhBFZILuK|WGTo+U4qcaAS4FMf6=Hug}j|C&A61DYbv9PzACSH()ipQdM+ zV0R8aG=%GlWehR{d*ANjv+!()rT7Ibyl4zvoJ|SqRjK(~AKks{ZUxP#9}Tt9)R(?> z-jC)QB25twU{*-|66MF~Uh()nWP7TnO|R+N#F6cqkgr73P41MESx_Wo{xaQ&pa-^GLu_gA7v*Y`2Dv)BP=LZA1O zrXKV=6Z)(lns%xjEbn>7$A(t)(XWidZn@v%?A*7!3wd|13+NllH~9Ut zazssgIhrQ)wYWCHr)9g+g47x%Tl(DPN8YEDFA)zYpSqe6do+rz@7t%QZUtNkZ&E&1 zQ-2>#?Ux%}TR`hBrBTIN3lCZPRZ_nIW{UOb`>@T;BuaRtve(UY?PpwG>XAji{1=qn zX#K2yPRt4knCa9bHl_a^S4%z<%Im*E(oDJRD77!V>8bz)>|41=*M8#k#)z+i<}A>> zM0puBKT~PWRJHVag`JWRgv~P)+b{I^XOx0+R;U?sDL+c60eq-0UU~-afj;L2>CH$F z0RFGO$JH8iL9!!XV$`2?!e*@J{@{k)0lj*xkJz@kzCcaC?%Sf`xAxIqx>GLi`OHTG z40gSwHc{*M^oSHTd$_>>3arwuSV(tq^^mk*7e=4roN`;6=mvg z$^!4?5TW~^E!e%ON2sU8L;@?_q{g*^`l{Uo-eiF{Zw*t4oFrFT$L964-+-QbMTKO_ zOWoD0l<=R_SStKEDy>(~T?o4>`hS?l7Di)_sJXnhgBK(BGiIe6N8fu9A@`M)aeG9*bceO2>ni`>wtr9&$NTczfeBl z{!Fdgl`?i$68B+V%Y^30``jP9j0hEjR+CEWGBuwr^CLGM12+%CJEe-w@Qf`)z-+_! zhT!sjAXNLl5Dxg-1%B->f@^Jz?M;^zWoF24xW2$UO~$v)z2o9avU&)<+gDVmN!Hq4 zaygM6EB_g`o)=wv5!S*_WmEO@uHEoUQZO%KZO^%U2=^(qwr5-(gnATWdzy~fp?@Rv zgsTFfPGBn29&^n{_$M{|h|7iW-+}S4D-WUX5PHz%KRsJFZhz5Mt9_1w_4 zFf~H`ESEo@d5(lGR%XJll7wb~FQq-iOtl-eq1-z_wVMIj%~|huaZZ=}U}4xG@!%RmxfsIGE1c~MzJb8mR`bOE)|`3sJiI4sUhMMh`F0Prm9Rp3?vlf0($>g#PQHL1Sn{AoIFgRV zM3Y8B3G8Il#fjZE zLrI8tRya+?X2s+;Cou4yG&l?9k;+=)uhYz~#-6C@LwN!wRz`PM9gh3e%-= z!ZgV!OqG&^$x^Z~Ny-v(rF`JDNlBd1t~FC}TdXvp@kvlIFltA?E(B7{71vC_<6 z{;6#CH{d*oCg@v*wN8Z(kmSIpvUR7fThQ`?!;k$!zan_oIo6$ATesG`*0J{Q&=&h; zgUMfga#I_^xy$|4b(^ufQ+;wXwSSoMzs0aSM|ufM&XH-sHt@8TWa z+)|fMgK%zXwGZ_)p=S9qYE`%#lISe-*kH-s=N4AMKhCxvR)F{Ej>(HAXlg;#emvGm zuzmQNT{bV{*-^d8A0rXr13;8ao$Y-W8#+Cp&a39cP)|$#WAU%pCn=J=u+uoj8K43y z8__#v1ceP$o95%JOtN06Za(vnX>W0E5cbh3SN)&yoBXaW^EJ1=+d$Y*_JGvzD%$rd zEaa||m(i-15p!C1AasXK@P6D?;rSEN_)@wiqwU|0SYV(#RD(EenZxsz*P-@UL(7xc z6Jl(C#3({SbBCO&pC(;-KaTd64r#^gP>wcN#6C~xk|8?gwGUJyJPRphYRWdmroi7` zCvCy3ZYzO(I5c61j*CY(_O>bE%lol%#L7<^RKhRgX~r{kKnb&0^Vx>)LGHvMohXJF zW(D=>iI z)z-z6d)m_SG{>%XnPe`&N{d_pd2GNY($i2=2dNMLiJ>jx*t~=`{S?jj@b}Eu1TacZ z!6-e2Jyzc1L>y}b9~UUc>48%8e}6ywtn>7xp3P01By&N-jY+#))b|)PY_2N zEbgZ}GSU0KIgww?;ctwrK`{~*f-m}9>{Kh^mH`KT9~#=Ew&exvK#YSdn5jIVrWVxn zRm|P#nXx+5W#J%flX7Q7AOeD=`9>%GM zc=e2frY*jvw%6~WacA*AAgiA}q_|2Vx#oH!dE8!hAd!6JrsXDJ)JzB@)HobpwyJf&Nsaw&Br6-U#97cMjA5-> zgH@8D>kmvq4e9!ryn}T_KKXe3m#2)(dFUG`yBVE9;XLz3Hv^AIe>BI-BRLGeN_jq- z%UMkG>6rFY#xXbx$L<&)2Q&l3&kV49`zanU1BGn|Y$saETH&GP>SD=bDRYXDmV|to z+5rZ6m`!AWO$H6XcnU)vrk))jNgpDQ7kP{+Wku)P{bXB&_u7O%s5@P2E%$U0!X2Z# zUi#F!B7+KjICXDPstQ|fxeMuIkI|hSlKI*Ivn6JLnWh_H6Eks|$tg0S4xCqfR7|m~ z=rWQoq4Be@pS2aX_Ek>4#MJVC&UZ)wHiOemwg-WIFR(T6UuK`sJfV>mxrsn&QOO%8 zEufNTIyAoh6e}#S)D*?G&3B^})DKl?rBKFXd8CNI?=Mq;;8Un<6bS z()JTtD_05Dacij#r+q<~0-F0*DHM;lMAGq{f?|Y9oJz%6{9AP5e}hhKueHKd7@<>X zCz&`MY1%QMd4vpTcB0Q2Hj~&XFN+i|z|~w1Z&H$lJ>}43)h0kHv1(sz6R{(Sa}M$N zwW)CbV!$i2LIi1gR}%Ed!r9rCdD)ff-lG2?_8+N!XkZ1~hD7rG7OdN1p+iEXpYEk7 zeXuuR^eIcXETN%C&4!rV}gU%OIUFs zWac^P((%RexyhE0B^I< zy1sYRod}=WQUVUE+^@8WudDEz`d&xtwhX_fwttz5MfvfP8ei0h)u!~!fE?lnOt-=N zR-OcnsW&jtXEgM1&fLz2Y=4@|c0EW)=+eHstzub6+fyp#S+pMLPn-C-8lQ}v=f;zR z(A&f1>i(*LlTtY(oBFSp_eicy;%3nxkN`6UoSS0!ek(Y45jZz0=JD4b#+%Fj~wc ztE~jH0`f$4eFHtw_kBaJypcclKEnL>?<1^j+uQ3eXTPiKB~{BhHd>jQhl=Z4Gt~N4 zMC*@MLOLuetW<`#8BPvSz9oQfCh!e@W}!kEtF6#1IZ>6N>1s|YQ*mlMSB_QtEJw{p z(1$TkW;U4X|s)4>q10ew^SNr)#c9Z+ED67^(y2>8gB=kX$3SCdsjh*O%F5Wo_>R}mv!xEPLG>{(qLy@5EFV4`vGQu` ztHph7k-6)`J7JTg-^T<40qXgDC-U<31f|kE|5Ed`pNheWf?I+Vf|JRhLLq#q;`n_K=TicM;V%Y% zbRS3|(2CQ00*Vq;PYue~CuQ_%Z%RNCy8N^e&xa z-_i0~cOh|>-P#ITINRtA?tw24cUarSN6$9mbZ^Gw*1hTe)WZDNaLWS7Agz~}E;4he zv>#`?B6%K*=2$sMVUMY_KN)&7N*gfy-z*f9;Phf)zB6@m6#q)K1f<)X7Os@mcq;Wl z8k;9gk(3VFGL2(GFjB@{;But?1*rdBY8g)q-T52!zb#5V{XgnY>rJ^BE1_lV-qefo zJyBd<#pOzM!ch3XhGQ^qZQd0VICMNBwN==coFBXYc)hy5xvzzTH@~yYaQOYyC)3O4 z1(TWoHl@s^{Xa0jR5%{#kyXFhBYCZLT^##C>h^T#^N%%MMuyp*oWg_-3>1q_wM{ug z5nj`}UV_%qvS{znvp)WGXDLaY9l8eS_y)Vff8f zFlvc0lK)H0nK08N@XexYE7)u=Xjhd)TS?E>NBAv|;`e0m+bdCiH-DhYf9v@=V+V3! zgwH!aInNFA0OV`oNwtpa2C?^e_0Q*O2Ykw7a(389Qh$i@W3XT4aj$@zO2$wW&ORMmi47t)}?A$pZ{iA`=VuiI#L#u!i7WUq_A2Vi}>83tr80zs0q?o z$@CoroMQoW7vq={q#P4ej%f}% zQX*%eDd)7Unc@z$zCR75so(btYOH<0ky6|1%Ys}u(5A697}SLM(g$kU{}U!5u+IURg5ImG#Prb zJX9KMlyul@($x4h{#Z6xf_8m2sG*kFK=3Ehv_#aaD`(T5yWDp}zg|N5z4lJkGCZ?i zV_U0QgY)6-0{>j>?3o&$hRvl~oFk5Trd(Z@Cj}A%rhpgLhvfQOYKOw_4YJZ|yx+x% zban~}4t5NMHG{;lrsX<}Pnm=8Jr5++6j_s=xYnJBy%ai6)~WMk4!^d9wZUw7yD?L= zZDVSZ>sNr%V}l*VA8(BR0s^Vb(n4LCQU_EY&Ei zbTh(h2VXdTnLDY*m@HJY4(9tsInNxYUNVE>+xtkiK@!rO%S-_}is3wS-5|WX69cQQ zLbX4=t~m7l^6EsKLRu2^c&8PPcT7C|!$}e{=H}*3y!74A@xtG+aBt6<)7`ltLb79| zRmZ8+B%_6}koT{)8YZ7@n+reC>SE(jH>`J#24buA4u$_T$Vm&1GivzR!MUgri504K zj@Wjgy0%*vyHw=5d4>m-Iq*j&$F9yPJbpyxcYe^lI05&0gcbt>ez{2hSeb!V4iEfJ z)VhYk;|C2W|LB12xa~M0s!o6|Sn%@~7AJgDsBfp?&Xp@}TC--oqu|QoTw&c^cige+ zj$4Flrcb#z;@vrtKJB;D7fs9a-F~OAa^3PfSK8nVdY7+q`TCWDcjfZRmFuDoqVO4A zxn5Y|UF8$juc2iKcip*iov>m}<;oEk(rCVw>(;F~myP(hrW5L0H)l41&vt|(rYJ`~ z=;qay^HJVV4mS)_-dvOOhNR0}i~T5qJF$G=#ytZ%+iGwlFhIGnVBo){)!2=}twr)` z_id_NACxt=RjOR?!_L;tNNdLGU?98{+F_%#PP$Sml=7tU(9}XT@6a4uUQ0r!mAj?k z@ZCep?-TBWSFlWBC$Veof$fY4uqt|PmLj!|L5}N@`kk^2IhGAk{CoPgz{a%<(65w* z67C_97a)89JLwh3NuN2AP;ElaYXJXHaU!R4$c6A<6Sx{ zTV7Zdg)dsUemxcYD3Qfg!ml6~+9WoBcK)6m`=!_`x?~3J5br!e+AdNHnB-*jQuHR zveP$5fIptzi@fZURtkHrBz105TB0XWj6frpDJA1nXvCM4!)aay9_G|X;}zPAc(*`{ z^kat>;|CzWsP`dJJOVuG5fuQGzHy5e@M!37Djt{?uik@pBL{m3)%LhQ1~=4^oH|kP zlJ1X+mQQ0rm+pqtRtxZm*Ekr&&vw+p4|Ts;|Q+Nj>C;;dRFx zCc=*$^wyutTW(o(L{9eCr4Wu7buD!%&1X0#!oB`{R-&;nWQ%xX8QV(d>9#sxb7b=A{!SbC7YA)5Y12Zd38+4Fg7RbS|VaOH5s z*Ip0@!s~`Ni=Nvma3dfi1aL~=G3qG|d-}ulmNjqzx1(hLMf~iHSx4_aVGifxju$$r zY!)ru42f&WSKG-P6#)ON*KhKR#qhtX0Oj6+W|W@%u9~kkU&QT15uQ++vMEAED|)R- zEw6O(vD&U!%-P_va&!qO?e8U~Eh5gXGxV;erQ%RHQ{9I;UpLf;bUIFVl(p=vCXV7DdRAStUDo;@=9uQgb3(*(v)gFF9Ge%!cSye2XI2$+xz@f4}0k>8q&prSiP@1vPdvvA& zTRTE?3h*r#(4=^nRRZg@D$_RTmVq!29BRSEO@M%w zfVy2fz}R|0_XE(qTBW;CD$MLwQ~Sdk6~^`pQr|?XsHT=kofzBwkc!J;J?Mzy-3Gj$ zfL8;&0u^tnbR2c6LrH&+mXwK-s5jn&z|q65e-z;thJ||C7Do66`-N{d6lNZdQY;3= z{{hCDDE1c!KQw}U2-pYc9OKsH4J^RD9F2YCF1(n(DWZKo01W*< z*Z`PoMPcUq=(#r7RNn)&6p76nAADD(iQ5W;JJtL=&Zro02LYx0CUWU-#r-+oF#>^o z=MCf%M3d^XsDyJBt+=cnJH}Pb*ag}FT)G=XZgpCPo2WHNxG7T49|q2A!ha2TR#2U4 z3O)l~<%3r?NHXn}pZep`3z67B_~E`7skYhy?wo`8;VWs{^T@nZFO#*^`Etp_7(J_T z)*=$Omu#DFywvU~uD7S1gZS0c7QFPpJpK}^m$7egK3{S_4SVjJmzr@7>z?^p8Fr7m z{;qkZD3-Nk9SwVU$u|OfzGSruqY`=>>VRD`4&I(U_D)XYoxe{u6?jKMab`rb0#L4C zY5j^{!IuHf7Z{{v^Q@GLM$=EuWOZt|Yv+9;^TkVkg}ngS#)99~FB*ki3ha5nCdCHn z%29AirA&oKd;|Wl6B;t7R@@<$VMcm!pYv?OTA`CMED(zjpW27>Yw!!b!VT{Pz%E5B zyN|)11ds9hQH6%|2tA~R;t<-bhT;+Wy&9rkcU5Z0gwUO8C=sDM)KC&atCTGCvVfBu z88{PRWHBd!hk`)t_}-z}nDUSJ(Q8I50rQvrkFBf~>soOuN%c8a|w z?B5LH6Ankems&4TKKr&XQ6@YH{v`qNWi zdJb%U|oZL}7{1N=4kDm*>JiCY`MZ%BN3&qheQU&iTJ zK=T?dfu{UXW~n2e!T7jM`nTBG|h{({onfY0V~W; zI4}^t3a9__+&f?^#LT{bBK>L~ci>B`_D8-wTYu!+vm<37Jf+_j)dK=b{TBKr4R9sA z7$tE*3(eiqKr+q{FiQ1Mh*C^Sc`do~qGa+zvugKMBt2^*EN@t57USlS0 zKUV+xDY;lN>p~8#ABoz@XH6r<5wRrJ&|4)=i&xRV;u>7+s8_jQ`A2^!m;YU#jpoauC2Fmbpzq2`(Wvg*5Pu6 zeeqoQHrPez?7^qrAsE4ZpwBPPDPvpKgc$N-2gSsg7+xly3wh;8o@++ssT|3}l5Ln9 zZc$Is)v7!hPkDmzpETK|$qjz$69-OA!D^p|DeWs{*uB+xb3AU+9SG+}DQi&kS)dFL zxiZqiNLWiXt+uk0+prhG;zZfWbb)erlYYu1m8TO&c&e!pJY0>(lPPHt%7Cp$@K|BT z5@lVJ8pntWy<}oX(570tKJO!rzXv0J^x6!p;`=a*%QH8XdU0MgFS+SW&=SN- zuiVOb4asj5CpVROG{K2pt(aKKcpToMlpV#^rrSNf#^v5s)HAEpfv{=MeACv>MI84v zycD_#Q`qEAPc7!YUReA|H_;R};TC1cjydEn=UUEVnBl9&wVcN>;RZy`jr*eCz`jW6 zseaRk^qxK3_9#H^i{eENeJI?7*`&96PX4>u*cUo-im?Ue2pB_8cO1w4Xe6h4j+P|1 zyxLtvQXK42VJ3C}Z=rX83StiCOsW02YZ$kYo=uy8dt~^Fj&@v>_DzRFJk}xHO8b|V zL-$Qh-n(+AiQ`^PY8%w z-B~d)($0Z1r!@mfBtO4uu}O4WiK91Y7bn8w2PG82yS0I+f0OY$36FpWV~WPKXM0RS zkee9SYC6ClOv-q^SZ)#CPkBEwpD;DjZ^6`Psc((Zv@Ep7V8(#a6*r+L;turG)R^G; zFcW+-y*rqUnTKYg^K`!76w(+;XsjQYmwbr*@S93&nh-uj5HF;mOJ3^c*qSE94;9ki zOA>M0HpVwUKf5`^`xnUECH!ps9;!Xbn|eEv(yni!Fee_`_QNq*Diuo8MMIOaR4SC7 zi-soAwXpzNvHtp#$rZxvTKv)weK+;=zH?(IyCJLT9Hg`<`CxK$<4KeA#?H-vpDT-p z@8eaaP3vIx1xy_-ZC~XaZj*(Ym@+`iYSGNO?H>qW&}O=?`Dj$D=pTK?>o^Xn=ID5+m}) zHx)LrB6*JL3CuEtLYx#Op@j|d9-5AGuYyrD!5dU3)}_=I$9-2IQvF8LVhGeY$Anz{bo8-oqQm2W9UiiH`v5&Tw*ceaqDaM4} z5`1$Ob+V9}RepRE^S}iinUvA=ZvU2EO~1A$t6zKe24_;!xRPD{n|oP6aGpn--jvy| z9W3oM!YTHePHsMKL@!Nlskx*{f&5qqdx&>8z<2lD!LXG@#j#rj&9`byIcRNu#5 z*Qqn|7>$_Nl&)C)Uk2C>t}Y$loC<%VFJcEYU#vy{e%uj@+mVZK8#}(iN5CoE@vMa%6D_ z7TM87NbPc=s#(V#5hA@eftRRWvdB6T(M@+_hJRIAPaDNDd>z5ksW|yU1Vgx~F#XT? zUl4k}k5S>tvNoDmxXF?JYB!@j^L->Ad{uQUtaNC{Lk#ZLCM0H_Z%>?fe`(~-PzTnF zSIVY(sx5qt^mO~PwTeP;|9=Q`2#x6UJ}MGNauXdhXiRQR_9mBB7ss?1yESx_!A(R( zmW3AV^rsm-O1SGoX4PCPyPU6reQ?j?JHI@|Od~e!6I%DZrC-8pG7)zvV2wSLuoL(A zlb;nu!J0HQQ>I@p2~FC27gC#*w?!>kB`y*v{cU3MPGL_U&QCR-6rAV<9iA9GR7PTL zp%~^(@y(OX`q9rHMc*7w<*p$1g|C90jA`i${{!C`A*MaJL8^t=G_~+?fzp|3hbLB?S zfEC6vPzh3v>mB0&PKT0asuw&=kZK zum$l2%{6Cps$rLM>{2ajv#elq9d5rRW`iZ}?xd6?Thg?oqNL?Xo02}R zOJkT#48t-ShGTRL&lnjKlgtQAKI3FO>sH;m>ZaMcmfXX+M{>W(J(?72F|w98HuoZn zMzADENmnFoOj3kIg;6w0jFO;aC|4*O6{SGW&eQ!_7?YTM?UiQqsTqBgdQyAym|Whx zwc3!n?Vgv$XyVPyIVQ74NXok9d-Ir^$C*DIN;VHaU@@0eS`DR3&}%s4R>KlX0#de3@>DV(b?iRRC2 zjQOt@v*zt(8uMM3ab__^YfkuDYYyJ7Ge34zm*uj?jF}N*-kT9)Ue~5GPhF!kcYmWb zzi-i+AGw?}uM{=rxwBdG#hHwKI$l4D0cyY|PLw>KKie?1sy{_J?X{Yxg7%dO3Q zKDQ?K>D)l>qq)_&TXX%n8*;01*XR0jZ_oASR_1zgug_glRIsGr9gFUI%MF}m4`+#U zS}Hlqhn(H4vE0U4KH=4^R*$y{NfbW{Fet<^Yy1#^V<(-%-e6^%#E#D^G}UDH#r0T GsQ({WTT;FN literal 0 HcmV?d00001 diff --git a/sdk/Include/App/wm_at_ri_init.h b/sdk/Include/App/wm_at_ri_init.h new file mode 100644 index 0000000..3faf440 --- /dev/null +++ b/sdk/Include/App/wm_at_ri_init.h @@ -0,0 +1,79 @@ +/** + * @file wm_at_ri_init.h + * + * @brief AT_RI task and interface resource initial Module + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_AT_RI_H +#define WM_AT_RI_H + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup AT_RI_APIs AT_RI APIs + * @brief AT_RI command APIs + */ + +/** + * @addtogroup AT_RI_APIs + * @{ + */ + +/** + * @brief This function is used to initialize hostif task + used by AT&RI Command + * + * @param None + * + * @retval 0 success + * @retval other failed + * + * @note Usually the system will call this api at power on. + */ +int tls_hostif_init(void); + +/** + * @brief This function is used to initialize high speed SPI + * + * @param None + * + * @retval 0 success + * @retval other failed + * + * @note Users can decide to call this api or not according to his application. + */ +int tls_hspi_init(void); + +/** + * @brief This function is used to initialize UART + * + * @param None + * + * @return None + * + * @note Usually the system will call this api at power on. + */ +void tls_uart_init(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_AT_RI_H */ + diff --git a/sdk/Include/App/wm_cloud.h b/sdk/Include/App/wm_cloud.h new file mode 100644 index 0000000..a3b5f07 --- /dev/null +++ b/sdk/Include/App/wm_cloud.h @@ -0,0 +1,481 @@ +/** + * @file wm_cloud.h + * + * @brief Cloud Interface API + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM_CLOUD_H__ +#define __WM_CLOUD_H__ + +#include "wm_config.h" +#if TLS_CONFIG_CLOUD +#include "wm_sockets.h" +#if TLS_CONFIG_HTTP_CLIENT_SECURE +#include "matrixsslApi.h" +#endif + +#define SERVER_STATE_OFFLINE 0 +#define SERVER_STATE_ONLINE 1 + +#define SOCKET_TCP 1 +#define SOCKET_CLINET 2 +#define SOCKET_SSL 4 + +/** + * ¿ª·¢Õß×Ô¶¨ÒåsocketµÄÐÅÏ¢£¬Ä¿Ç°sock_type²»Ö§³ÖTcp server£¬ + * ¿ÉÖ§³Öudp¡¢tcp clientÒÔ¼°sslµÄtcpÁ¬½Ó + */ +typedef struct _SOCKET{ + int socket_num; /**< socketºÅ£¬socket½¨Á¢³É¹¦ºó£¬ÏµÍ³ÌîÈ룬 + ¿ª·¢Õß³õʼ»¯Ê±²»±ØÌîд */ + struct sockaddr * sock_addr;/**< socketµØÖ·£¬udpʱ£¬±íʾ±¾µØµØÖ·£¬ + tcpʱ£¬±íʾ¶Ô¶ËµØÖ· */ + u8 sock_type; /**< socketÀàÐͶ¨Ò壬Ŀǰ½öµÍÈý¸öbitÓÐЧ£¬ + ¾ßÌå²é¿´SOCKET_xxx¶¨Òå */ + u8 connected; /**< socketÁ¬½Ó±êÖ¾£¬½ötcp clientÓÐЧ£¬ + Ϊ1±íʾÁ¬½Ó³É¹¦ */ + void * user_param; /**< ¿ª·¢Õß×Ô¶¨ÒåÐÅÏ¢£¬ÓÃÀ´Çø·ÖÊÇ·ñΪ¿ª·¢Õß + ×Ô¶¨Òåsocket */ +#if TLS_CONFIG_HTTP_CLIENT_SECURE + ssl_t * ssl; /**< sslÁ¬½ÓÐÅÏ¢£¬½öSOCKET_SSLʱÓÐЧ */ +#endif +}SOCKET; + +/** ¿ª·¢Õß×Ô¶¨Òåsocket½ÓÊÕÊý¾ÝµÄ¸ñʽ */ +typedef struct CReadData{ + SOCKET *socket; /**< socketÏà¹ØÐÅÏ¢ */ + char * read_data; /**< ½ÓÊÕµÄÊý¾Ý */ + u32 data_len; /**< ½ÓÊÕÊý¾Ý³¤¶È */ + struct sockaddr_in sin_recv;/**< ¶Ô¶ËsocketµØÖ·£¬½öudpʱÓÐЧ */ +}CloudReadData; + +#if TLS_CONFIG_CLOUD_KII +/** ÔÆ¶Ë»Øµ÷Êý¾Ý¸ñʽ */ +typedef struct _Cloud_Callback_data +{ + char* bucketName;/**< bucketÃû³Æ */ + char* objectId; /**< ¶ÔÏóid */ + char* jsonObject;/**< ¶ÔÏóÊý¾Ý£¬Í¨³£ÓÃjson¸ñʽ±íʾ */ +} CloudCallbackData, *PCloudCallbackData; +/** ÉèÖÃÉÏ´«Ôƶ˻ص÷£¬errcodeΪ0±íʾÉÏ´«³É¹¦£¬dataΪÉÏ´«Êý¾Ý£¬ +Èç¹ûÉÏ´«³É¹¦£¬¿ÉÒÔ´Ódata->objectIdµÃµ½ÉÏ´«ÔƵÄÊý¾Ý¶ÔÏóid */ +typedef void (*upload_callback)(PCloudCallbackData data, int errcode); +/** ÉÏ´«Ôƶ˵ÄÊý¾Ý¸ñʽ */ +typedef struct _CloudData +{ + char* bucketName; /**< bucketÃû³Æ */ + char* jsonObject; /**< ¶ÔÏóÊý¾Ý£¬Í¨³£ÓÃjson¸ñʽ±íʾ */ + upload_callback cb; /**< ÉÏ´«½á¹û»Øµ÷ */ +} CloudData,*PCloudData; + +/** ³õʼ»¯ÔƲÎÊý¶¨Òå */ +typedef struct _Cloud_Init_arg +{ + char* site; /**< ·þÎñÆ÷µØÖ·£¬Ä¿Ç°¿É´«Èë"CN" "US" "JP" "SG" */ + char* appId; /**< Ó¦ÓÃID */ + char* appKey; /**< Ó¦ÓÃKey */ + char* deviceType; /**< É豸ÀàÐÍ */ + char* venderId; /**< É豸ID£¬Í¨³£ÎªÉ豸µÄMACµØÖ· */ + char* modelName; /**< Ä£¿éÃû³Æ£¬×Ô¶¨Òå×Ö·û´®£¬OTAÍÆË͵ÄbucketName¸ñʽΪ */ + char* password; /**< É豸ÃÜÂë */ + char* recvDataBucket; /**< Éè±¸ÍÆË͵ÄbucketName */ +} CloudInitArg, *PCloudInitArg; +#endif + +#if TLS_CONFIG_CLOUD_JD +/** ÔÆ²Ù×÷ÀàÐÍö¾Ù */ +enum cloud_opt_type +{ + UPLOAD_REQ, /**< ÉÏ´«Êý¾ÝÇëÇó */ + UPLOAD_RESP, /**< ÉÏ´«Êý¾ÝÏìÓ¦ */ + CONTROL_REQ, /**< ÔÆ¶Ë¿ØÖÆÃüÁîÇëÇó */ + CONTROL_RESP, /**< ¿ØÖÆÃüÁîÏìÓ¦ */ + SNAPSHOT_REQ, /**< ÔÆ¶Ëץȡ¿ìÕÕ£¨×´Ì¬£©ÇëÇó */ + SNAPSHOT_RESP /**< ¿ìÕÕ£¨×´Ì¬£©ÏìÓ¦ */ +}; + +/** + * ÔÆ¶ËͨÐÅÊý¾Ý½á¹¹£¬ÐèҪעÒâÒ»ÏÂÁ½µã: + * 1)×÷Ϊtls_cloud_upload_data·½·¨²ÎÊý: + * ÉÏ´«Êý¾ÝÇëÇ󡢿ØÖÆÃüÁîÏìÓ¦¡¢¿ìÕÕ£¨×´Ì¬£©ÏìÓ¦½Ó¿Ú£¬ + * ¶ÔÓÚÉÏ´«Êý¾ÝÇëÇó£¬Èç¹û´Ë½Ó¿Ú·µ»Øºó£¬data->optΪUPLOAD_RESP£¬ + * Ôòʵ¼ÊÉÏͬ²½ÏìÓ¦ÁËÉÏ´«ÃüÁ´Ëʱdata->result¼´ÎªÉÏ´«Êý¾ÝºóÔÆ¶Ë·µ»Ø½á¹û¡£ + * CloudDataÐèÒªµ÷ÓÃÕß×Ô¼º·ÖÅäÄڴ棬×Ô¼ºÊÍ·Å¡£ + * 2)×÷Ϊcloud_callback»Øµ÷²ÎÊý: + * ÉÏ´«Êý¾ÝÏìÓ¦¡¢Ôƶ˿ØÖÆÃüÁîÏÂ´ï¡¢ÔÆ¶Ëץȡ¿ìÕÕ£¨×´Ì¬£©ÇëÇóʱ£¬ + * cloud_callback»Øµ÷½Ó¿Ú±»µ÷Óᣠ+ * ¶ÔÓÚÔÆ¶Ë¿ØÖÆÃüÁîÇëÇó¡¢ÔƶËץȡ¿ìÕÕÇëÇó£¬Èç¹ûÔڴ˻ص÷½Ó¿Ú·µ»Øºó£¬ + * data->optΪCONTROL_RESP»òSNAPSHOT_RESP£¬Ôòʵ¼ÊÉÏͬ²½ÏìÓ¦ÁË´ËÃüÁ + * ´Ëʱdata->result¼´Îª¿ØÖÆÃüÁîÏìÓ¦½á¹û£¬data->valuesÖаüº¬ÁË¿ìÕÕÏìÓ¦½á¹û¡£ + * ͬ²½ÏìÓ¦SNAPSHOT_RESPʱ£¬namesºÍvalues²¿·ÖµÄÄÚ´æÒÔ¼°Í¬²½ÏìÓ¦CONTROL_RESPʱ£¬ + * data->control_resp²¿·ÖµÄÄÚ´æÓÉ¿ª·¢ÕßÉêÇ룬ÔÚÔÆÊµÏÖÖÐÊÍ·Å¡£ + */ +typedef struct _CloudData +{ + enum cloud_opt_type opt;/**< ²Ù×÷ÀàÐÍö¾Ù */ + int result; /**< ÔÆ¶ËÏìÓ¦ÉÏ´«Êý¾Ý»òÉ豸ÏìÓ¦ÔÆ¶Ë¿ØÖƽá¹û */ + char* control_resp; /**< ÔÆ¶Ë¿ØÖÆÃüÁîʧ°ÜÔ­Òò */ + char** names; /**< ÉÏ´«Êý¾ÝÇëÇó¡¢Ôƶ˿ØÖÆÇëÇó¡¢ + ÔÆ¶Ë¿ìÕÕÇëÇó»ò¿ìÕÕÏìÓ¦µÄ×Ö¶ÎÃû */ + char** values; /**< ÉÏ´«Êý¾ÝÇëÇó¡¢Ôƶ˿ØÖÆÇëÇ󡢿ìÕÕÏìÓ¦µÄÖµ */ + int cnt; /**< ×ֶθöÊý */ + void * arg; /**< ÔÆ¶Ë·¢Ë͹ýÀ´µÄ²ÎÊý£¬ÐèÒªÔ­·â²»¶¯»Ø´«¸øÔÆ¶Ë */ +} CloudData,*PCloudData, CloudCallbackData, *PCloudCallbackData; +/** ³õʼ»¯ÔƲÎÊý¶¨Ò壬Ӧ¸Ã´«Èëproduct_uuid */ +typedef struct _Cloud_Init_arg +{ + char *product_uuid; /**< ²úÆ·UUID */ +} CloudInitArg, *PCloudInitArg; +#endif +#if TLS_CONFIG_CLOUD_GAGENT + +/** ÔÆ¶ËͨÐÅÊý¾Ý½á¹¹ */ +typedef struct _CloudData +{ + int32 totalcap; /**< È«¾Ö»º´æÇøµÄ´óС 4k+128byte */ + int32 remcap; /**< Êý¾Ý°üÍ·µÄÈßÓ೤¶È 128byte */ + uint8 *allbuf; /**< Êý¾ÝÆðʼµØÖ· */ + int32 bufcap; /**< Êý¾ÝÇøÓòµÄ´óС 4K */ + + uint32 type; /**< ÏûÏ¢ÌåÀàÐÍ */ + uint8 *phead; /**< Êý¾Ý°üÍ·ÆðʼµØÖ· */ + uint8 *ppayload; /**< ÒµÎñÂß¼­Í·ÆðʼµØÖ· */ + uint8 *pend; /**< Êý¾Ý½áÊøµØÖ· */ +} CloudData,*PCloudData, CloudCallbackData, *PCloudCallbackData; + +/** ³õʼ»¯ÔƲÎÊý¶¨Òå */ +typedef struct _Cloud_Init_arg +{ + char* product_key; /**< ²úÆ·±êʶÂë */ + char* wifi_soft_ver; /**< Èí¼þ°æ±¾ºÅ */ + char* wifi_hard_ver; /**< Ó²¼þ°æ±¾ºÅ */ +} CloudInitArg, *PCloudInitArg; +#endif +#if TLS_CONFIG_CLOUD_360 +/** + * ÉèÖÃÉÏ´«Ôƶ˻ص÷£¬errcodeΪ0±íʾÉÏ´«³É¹¦£¬user_paramΪ + * µ÷ÓÃtls_cloud_upload_dataʱ´«ÈëµÄCloudDataÖеÄuser_param + */ +typedef void (*upload_callback)(void* user_param, int errcode); + +/** ÔÆ¶Ë»Øµ÷Êý¾Ý¸ñʽ */ +typedef struct _Cloud_Callback_data +{ + const uint8_t* data; /**< Êý¾Ý */ + size_t datalen; /**< Êý¾Ý³¤¶È */ +}CloudCallbackData, *PCloudCallbackData; + +/** ÉÏ´«Ôƶ˵ÄÊý¾Ý¸ñʽ */ +typedef struct _CloudData +{ + void* user_param; /**< Óû§²ÎÊý£¬ÔÚÉÏ´«»Øµ÷Öд«Èë´Ë²ÎÊý */ + const uint8_t* data; /**< Êý¾Ý */ + size_t datalen; /**< Êý¾Ý³¤¶È */ + upload_callback cb; /**< ÉÏ´«½á¹û»Øµ÷ */ +} CloudData,*PCloudData; + +/** ³õʼ»¯ÔƲÎÊý¶¨Òå */ +typedef struct _Cloud_Init_arg +{ + int product_id; /**< ²úÆ·ID */ + unsigned char product_aes[16]; /**< ²úÆ·AESÃÜÔ¿ */ + char *device_name; /**< É豸Ãû³Æ */ +} CloudInitArg, *PCloudInitArg; +#endif + +#if TLS_CONFIG_CLOUD_QQ + +/** + * ÉèÖÃÉÏ´«Ôƶ˻ص÷£¬errcodeΪ0±íʾÉÏ´«³É¹¦£¬cookieΪ×ÔÔö + * µÄÐòºÅ£¬ºÍÔÚµ÷ÓÃtls_cloud_upload_dataʱµÃµ½µÄCloudDataÖÐ + * µÄpCookieÏàͬ + * + */ +typedef void (*upload_callback)(int32_t err_code, uint32_t cookie); + +/** ÔÆ¶Ë»Øµ÷Êý¾Ý¸ñʽ */ +typedef struct _Cloud_Callback_data { + uint32_t property_id; /**< ÊôÐÔID */ + uint8_t *property_value; /**< ÊôÐÔvalue£¬ÀàÐÍΪ×Ö½ÚÊý×é */ + uint32_t property_value_len; /**< ÊôÐÔvalueµÄ³¤¶È */ + uint32_t seq; /**< ²Ù×÷ÐòºÅ */ + int32_t ret_code; /**< ÊÕµ½cc datapoint£¬»Ø¸´ACKʱÉèÖôËÖµ¹©H5Ò³ÃæÊ¹Óà */ +} CloudCallbackData, *PCloudCallbackData; + +/** ÉÏ´«Ôƶ˵ÄÊý¾Ý¸ñʽ */ +typedef struct _CloudData +{ + CloudCallbackData *datapoints; /**< Êý¾Ý½á¹¹ */ + uint32_t datapoints_count; /**< ¸öÊý */ + upload_callback cb; /**< ÉÏ´«½á¹û»Øµ÷ */ + uint32_t *pCookie; /**< ×ÔÔöµÄÐòºÅ */ +}CloudData,*PCloudData; + +/** ³õʼ»¯ÔƲÎÊý¶¨Òå */ +typedef struct _Cloud_Init_arg{ + /* Ó²¼þÐÅÏ¢ */ + char* device_name; /**< É豸Ãû³Æ,'\0'½áβ×Ö·û´®³¤¶È²»³¬¹ý32×Ö½Ú£¬ + nettypeΪ1ʱ±ØÐëÓëÍøÕ¾×¢²áµÄdevicenameÒ»Ö */ + char* sn; /**< É豸ÐòÁкÅ,'\0'½áβ×Ö·û´®³¤¶È±ØÐëÊÇ16×Ö½Ú */ + char* license; /**< É豸LICENSE */ + int32_t product_id; /**< ÿһ¸ö³§É̵ÄÿһÖÖÀàÐ͵ÄÉ豸¶ÔÓ¦µÄÒ»¸öid */ + int32_t product_version; /**< ²úÆ·µÄ°æ±¾ÐÅÏ¢£¬ÓÃÓÚOTA£¬Ä¬ÈÏÌî0 */ + /** ÃÜÔ¿ÐÅÏ¢ */ + uint8_t* client_pub_key; /**< client¹«Ô¿£¬Ã¿Ì¨É豸Éú³ÉºóÉÕ¼µ½É豸 */ + int32_t client_pub_key_len; + uint8_t* auth_key; /**< authkey£¬Ã¿Ì¨É豸Éú³ÉºóÉÕ¼µ½É豸 */ + int32_t auth_key_len; +} CloudInitArg, *PCloudInitArg; +#endif +#if TLS_CONFIG_CLOUD_ABLECLOUD + +/** ABLECLOUDÔÆ¶ËͨÐÅÊý¾Ý½á¹¹ */ +typedef struct _CloudData +{ + u8 msg_id; /**< ÏûÏ¢ID£¬ÏìÓ¦ÏûÏ¢Í¬ÔÆ¶ËÏ·¢µÄÏûÏ¢ååID£¬Ö÷¶¯ÉÏ´«ÈÎÒâ */ + u8 msg_code; /**< ÏûÏ¢Code£¬ÏìÓ¦ÏûÏ¢Í¬ÔÆ¶ËÏ·¢µÄÏûÏ¢code£¬ + Ö÷¶¯ÉÏ´«ÐèÒª´óÓÚ200 */ + u8 *data; /**< Êý¾Ý */ + uint32 datalen; /**< Êý¾Ý³¤¶È */ +} CloudData,*PCloudData, CloudCallbackData, *PCloudCallbackData; + +/** ³õʼ»¯ÔƲÎÊý¶¨Òå */ +typedef struct _Cloud_Init_arg +{ + u32 major_domain; /**< Ö÷ÓòID */ + u16 sub_domain; /**< ×ÓÓòID */ + u8 private_key[112]; /**< ²úƷ˽Կ */ + u32 product_version; /**< ²úÆ·µÄ°æ±¾ÐÅÏ¢£¬ÓÃÓÚOTA */ +} CloudInitArg, *PCloudInitArg; +#endif +#if TLS_CONFIG_CLOUD_ELIAN +enum +{ + DATA_TYPE_BINARY = 0, /**< ¶þ½øÖÆ */ + DATA_TYPE_TEXT = 1, /**< Îı¾¸ñʽ */ + DATA_TYPE_NV = 2, /**< ÒÔ¿Õ¸ñ·ÖÀëµÄÃüÁî¼Ó²ÎÊýµÄ¸ñʽ£¬ + È磺power 34¡£ */ + DATA_TYPE_EL_CMD = 3, /**< Ϊ4×Ö½ÚÕûÊýÃüÁîId+4×Ö½ÚÕûÊýµÄÖµ£¬ + È磺140 (µçԴ״̬Éϱ¨£¬14ΪÕûÊý£» + 0ҲΪÕûÊý£¬±íʾ״̬£©¡£ */ + DATA_TYPE_INFRA = 6, /**< ºìÍâ¸ñʽ */ + DATA_TYPE_STATUS_REQ = 7,/**< É豸״̬ÇëÇó */ + DATA_TYPE_EL_CMD2 = 8 /**< Ϊ4×Ö½ÚÕûÊýÃüÁîId+×Ö·û´®¸ñʽµÄÖµ£¬ + È磺140 (µçԴ״̬Éϱ¨£¬14ΪÕûÊý£» + 0Ϊ×Ö·û´®£¬±íʾ״̬)¡£ */ +}; + +/** ÔÆ¶ËͨÐÅÊý¾Ý½á¹¹ */ +typedef struct _CloudData +{ + const uint8_t* data; /**< Êý¾Ý */ + size_t datalen; /**< Êý¾Ý³¤¶È */ + u8 datatype; +} CloudData,*PCloudData, CloudCallbackData, *PCloudCallbackData; + +/** ³õʼ»¯ÔƲÎÊý¶¨Òå */ +typedef struct _Cloud_Init_arg +{ + char* sn; /**< É豸ÐòÁкÅ,'\0'½áβ×Ö·û´®³¤¶È + ±ØÐëÊÇ13×Ö½Ú */ +} CloudInitArg, *PCloudInitArg; +#endif + +/** É豸ÔÚÔÆ¶Ë×¢²áºóµÃµ½µÄ¹ØÓÚÉ豸µÄÐÅÏ¢ */ +typedef struct _Cloud_Dev_info{ + u8* device_id; /**< É豸ID */ + u8* access_token; /**< É豸token£¬ºÍÉ豸IDÒ»Æð¹¹³ÉÉ豸·ÃÎÊÔÆµÄƾ֤ */ +}CloudDeviceInfo, *PCloudDeviceInfo; + + +/** + * @brief ÉèÖÃÔÆµÄ»Øµ÷ + * + * @param[in] data + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +typedef int (*cloud_callback)(PCloudCallbackData data); + +/** + * @brief ÉèÖÃÔÆ×´Ì¬µÄ»Øµ÷£¬statusΪ1±íʾÉ豸ÔÚÏߣ¬statusΪ0±íʾ + É豸²»ÔÚÏß + * + * @param[in] status + * + * @retval 0 ±íʾÉ豸²»ÔÚÏß + * @retval 1 ±íʾÉ豸ÔÚÏß + * + * @note None + */ +typedef void (*cloud_online_status_callback)(int status); + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup cloud_APIs cloud APIs + * @brief cloud APIs + */ + +/** + * @addtogroup cloud_APIs + * @{ + */ + +/** + * @brief ³õʼ»¯ÔÆ£¬user task³õʼ»¯Ê±µ÷Ó㬠+ ¶Ô¾©¶«ÔÆ£¬argΪproduct_uuid£¬ + ¶ÔÆäËüÔÆargÇë²é¿´CloudInitArg½á¹¹¶¨Òå + * + * @param[in] arg + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_cloud_init(PCloudInitArg arg); + +/** + * @brief ×¢ÏúÔÆ£¬É豸ÏÂÏßʱµ÷Óã¬Ïú»Ù×ÊÔ´¡£blockΪ0ʱ£¬´Ëº¯ÊýÁ¢ + ¼´·µ»Ø£¬×¢ÏúÔÆÔÚºǫִ́ÐУ¬blockΪ1ʱ£¬ÔÆ×¢ÏúÍê³Éºó´Ëº¯Êý + ·µ»Ø¡£ + * + * @param[in] block ÊÇ·ñ×èÈû£¬Îª1±íʾ£¬Ö±µ½ÔÆ×¢ÏúÍê³É·µ»Ø£¬ + Ϊ0±íʾ£¬Á¢¼´·µ»Ø£¬ÔÆ×¢ÏúÔÚºǫִ́ÐÐÍê³É + * + * @return None + * + * @note None + */ +void tls_cloud_finish(u8 block); + +/** + * @brief É豸ÏòÔÆÉÏ´«Êý¾Ý£¬°üÀ¨¶Ô¿ØÖÆÃüÁîµÄÏìÓ¦£¬ÉÏ´«×´Ì¬ÒÔ¼°Éè + ±¸¿ìÕÕµÈ + * + * @param[in] data + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_cloud_upload_data(PCloudData data); + +/** + * @brief ÉèÖÃÔÆµÄ»Øµ÷½Ó¿Ú£¬ÊÇÉ豸½ÓÊÕÔÆµÄ¿ØÖÆÃüÁî£¬ÍÆËÍÏûÏ¢µÈµÄ + Ψһ½Ó¿Ú + * + * @param[in] callback + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_cloud_set_callback(cloud_callback callback); + +/** + * @brief ÉèÖÃÔÆ×´Ì¬µÄ»Øµ÷½Ó¿Ú£¬É豸ÉÏÏߣ¬ÏÂÏßʱ»Øµ÷´Ë½Ó¿Ú + * + * @param[in] callback + * + * @return None + * + * @note None + */ +void tls_cloud_set_online_status_callback(cloud_online_status_callback callback); + +/** + * @brief »ñµÃÉ豸ÔÚÔÆ¶Ë×¢²áºóµÄÉ豸Ïà¹ØÐÅÏ¢£¬ + ¾ßÌå²é¿´CloudDeviceInfo½á¹¹¶¨Òå + * + * @param[in] info + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_cloud_get_device_info(PCloudDeviceInfo info); + +/* +* ÒÔÏÂextern¿ªÍ·µÄº¯Êý£¬ÐèÒª¿ª·¢ÕßʵÏÖ£¬¾ßÌåʵÏֲ鿴wm_lsd_device_found.c£¬´ËΪÎÒÃÇдµÄÒ»¸ö¾ÖÓòÍøÉ豸·¢Ïֵļòµ¥ÊµÏÖ£¬ +* ¿ª·¢Õß¿ÉÒÔ¸ù¾ÝÐèÒª£¬Ð´×Ô¼ºµÄÉ豸·¢ÏÖʵÏÖ£¬»òÕßÊǾÖÓòÍøÍ¨ÐÅ¿ØÖƵȡ£ +*/ + +/** + * @brief »ñµÃ¿ª·¢×Ô¶¨ÒåµÄsocketÐÅÏ¢£¬countΪ×î´óÖ§³ÖµÄsocketÊý£¬ + ¸ù¾Ý²»Í¬µÄÔÆ´Ë²ÎÊýÓÐËù²»Í¬ + * + * @param[in] sockets + * @param[in] count + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +extern int tls_cloud_get_custom_sockets(SOCKET** sockets, int count); + +/** + * @brief ¿ª·¢Õß×Ô¶¨Òåsocket½ÓÊÕÊý¾ÝµÄ´¦Àíº¯Êý£¬´Ëº¯ÊýÔÚsocket½Ó + ÊÕµ½Êý¾Ýʱ±»µ÷Ó㬠+ ¿ª·¢Õ߿ɸù¾ÝSOCKETÖеÄuser_paramÅжÏÊÇ·ñΪ×Ô¶¨Òåsocket + £¬È»ºó×öÏàÓ¦µÄ´¦Àí + * + * @param[in] data + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +extern int tls_cloud_socket_data_handler(CloudReadData* data); + + +/** + * @brief ͨ¹ýsocket·¢ËÍÊý¾Ý£¬ÆäÖÐtoºÍtolenÖ»ÓÐudpʱÓÐЧ²¢ÇÒ±ØÐë + £¬tcpʱto²ÎÊý´«ÈëNULL + * + * @param[in] *sock + * @param[in] *data + * @param[in] size + * @param[in] flags + * @param[in] *to + * @param[in] tolen + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_cloud_socket_sendto(SOCKET *sock, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen); + +/** + * @} + */ + +/** + * @} + */ + +#endif //TLS_CONFIG_CLOUD +#endif + diff --git a/sdk/Include/App/wm_crypto.h b/sdk/Include/App/wm_crypto.h new file mode 100644 index 0000000..0ca0ed6 --- /dev/null +++ b/sdk/Include/App/wm_crypto.h @@ -0,0 +1,91 @@ +/** + * @file wm_crypto.h + * + * @brief crypto driver module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_CRYPTO_H +#define WM_CRYPTO_H + +/** + * @brief Encrypt plain data by 128 AES crypto + * @param[in] key the key for encryption + * @param[in] iv the IV value for encryption + * @param[in] data where the plain data stored + * @param[in] data_len length of the plain data to be encrypted + * @retval 0 finish Encryption successfully + * @retval -1 Error + * @note Encrypted data will be placed into the plain @data area + * + */ + int aes_128_cbc_encrypt (const u8 *key, const u8 *iv, u8 *data, size_t data_len) ; + +/** + * @brief Decrypt data by 128 AES crypto + * @param[in] key the key for encryption + * @param[in] iv the IV value for encryption + * @param[in] data where the plain data stored + * @param[in] data_len length of the plain data to be decrypted + * @retval 0 finish Decryption successfully + * @retval -1 Error + * @note plain data will be placed into the encrypted @data area + * + */ +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); + +/** + * @brief XOR RC4 stream to given data with skip-stream-start + * @param[in] key RC4 key + * @param[in] keylen RC4 key length + * @param[in] data data to be XOR'ed with RC4 stream + * @param[in] data_len length of the plain data to be encrypted + * @retval 0 finish Encryption/Decryption successfully + * @retval -1 Error + * @note this function should be used for Encryption & Decryption both For the Encryption, the plain @data + * will be replaced by the encrypted output, and vice versa; + */ +int rc4(const u8 *key, size_t keylen, u8 *data, size_t data_len); + + +/** + * @brief MD5 hash for data vector + * @param[in] addr Pointers to the data area + * @param[in] len Lengths of the data block + * @param[in] mac Buffer for the hash (16 bytes) + * @retval 0 finish caculation successfully + * @retval -1 Error + * @note + */ +int md5(const u8 *addr, int len, u8 *mac); + +/** + * @brief HMAC-MD5 over data buffer (RFC 2104) + * @param[in] key Key for HMAC operations + * @param[in] keylen Length of the key in bytes + * @param[in] data data to be caculated + * @param[in] data_len Lengths of the data block + * @param[in] mac Buffer for the hash (16 bytes) + * @retval 0 finish caculation successfully + * @retval -1 Error + * @note + */ +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); + +/** + * @brief SHA1 hash for data vector + * @param[in] addr Pointers to the data area + * @param[in] len Lengths of the data block + * @param[in] mac Buffer for the hash (16 bytes) + * @retval 0 finish caculation successfully + * @retval -1 Error + * @note + */ + +int sha1(const u8 *addr, int len, u8 *mac); + +#endif + diff --git a/sdk/Include/App/wm_dhcp_server.h b/sdk/Include/App/wm_dhcp_server.h new file mode 100644 index 0000000..8ad72a9 --- /dev/null +++ b/sdk/Include/App/wm_dhcp_server.h @@ -0,0 +1,91 @@ +/** + * @file wm_dhcp_server.h + * + * @brief DHCP SERVER + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_DHCP_SERVER_H +#define WM_DHCP_SERVER_H + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup DHCPS_APIs DHCPS APIs + * @brief DHCP server APIs + */ + +/** + * @addtogroup DHCPS_APIs + * @{ + */ + +/** + * @brief This function is used to start DHCP Server for a network + interface + * + * @param None + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +s8 tls_dhcps_start(void); + +/** + * @brief This function is used to stop DHCP server + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_dhcps_stop(void); + +/** + * @brief This function is used to get station's IP address by + MAC address + * + * @param[in] *mac STA's MAC address + * + * @retval ip_addr STA's IP address + * @retval NULL Not found match IP with MAC address + * + * @note None + */ +ip_addr_t *tls_dhcps_getip(const u8 *mac); + +/** + * @brief This function is used to set DHCP server's DNS address + * + * @param[in] numdns the index of the DNS server to set must be 0 or 1 + * + * @return None + * + * @note None + */ +void tls_dhcps_setdns(u8 numdns); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_DHCP_SERVER_H */ + diff --git a/sdk/Include/App/wm_dlna_dmr.h b/sdk/Include/App/wm_dlna_dmr.h new file mode 100644 index 0000000..a4bf282 --- /dev/null +++ b/sdk/Include/App/wm_dlna_dmr.h @@ -0,0 +1,247 @@ +/** + * @file wm_dlna_dmr.h + * + * @brief DLNA DMR API + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM__DLNA_DMR_H_ +#define __WM__DLNA_DMR_H_ + +enum dmr_control_type +{ + CONTROL_GET = 0, + CONTROL_SET +}; + +/** + * @brief This Function prototype for action callback functions + * + * @param[in] None + * + * @return None + * + * @note None + */ +typedef void (*dmr_action_callback)(void); + +/** + * @brief This Function prototype for control callback functions + * + * @param[in] type to get or set control + * @param[in] channel the channel to control + * @param[in] value the value of getting or wanting to set + * + * @return None + * + * @note None + */ +typedef void (*dmr_control_callback)(enum dmr_control_type type, const char* channel, int* value); + +/** + * @brief This Function prototype for getting the playing progress + * + * @param[in] totlen The total length of the playing media + * + * @returns the progress(0.0 ~ 1.0) of having been played + * + * @note None + */ +typedef float (*dmr_play_progress_callback)(int totlen); + +/** + * @brief This Function prototype for downloading media data finis- + hed + * + * @param[in] buf The downloaded data this time + * @param[in] datalen The length of buffer data + * + * @retval None + * + * @note None + */ +typedef void (*dmr_download_finish_callback)(char * buf, int datalen); + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup DLNA_APIs DLNA APIs + * @brief DLNA DMR APIs + */ + +/** + * @addtogroup DLNA_APIs + * @{ + */ + +/** + * @brief This function is used to register play callback which would + be called while starting to play + * + * @param[in] callback the play callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_play_callback(dmr_action_callback callback); + +/** + * @brief This function is used to register stop callback which would + be called while to stop + * + * @param[in] callback the stop callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_stop_callback(dmr_action_callback callback); + +/** + * @brief This function is used to register seek callback which would + be called while to seek + * + * @param[in] callback the seek callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_seek_callback(dmr_action_callback callback); + +/** + * @brief This function is used to register pause callback which + would be called while to pause + * + * @param[in] callback the pause callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_pause_callback(dmr_action_callback callback); + +/** + * @brief This function is used to register play progress callback + which would be called while to get play progress + * + * @param[in] callback the play progress callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_play_progress_callback(dmr_play_progress_callback callback); + +/** + * @brief This function is used to register mute control callback + which would be called while to get or set mute control + * + * @param[in] callback the mute control callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_mute_callback(dmr_control_callback callback); + +/** + * @brief This function is used to register volume control callback + which would be called while to get or set volume control + * + * @param[in] callback the volume control callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_volume_callback(dmr_control_callback callback); + +/** + * @brief This function is used to register volumedb control callback + which would be called while to get or set volumedb + control + * + * @param[in] callback the volumedb control callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_volumedb_callback(dmr_control_callback callback); + +/** + * @brief This function is used to register loudness control callback + which would be called while to get or set loudness + control + * + * @param[in] callback the loudness control callback + * + * @return None + * + * @note None + */ +void tls_dmr_set_loudness_callback(dmr_control_callback callback); + +/** + * @brief This function is used to initialize the media render + * + * @param[in] *friendly_name friendly name of the media render + * @param[in] *uuid uuid of the media render + * + * @retval 0 success + * @retval minus failed + * + * @note None + */ +int tls_dmr_init(const char *friendly_name, const char *uuid); + +/** + * @brief This function is uesd to destroy the media render + * + * @param[in] None + * + * @retval 0 success + * @retval minus failed + * + * @note None + */ +int tls_dmr_destroy(void); + +/** + * @brief This function is used to trigger to download data from media + server, the download_callback would be called when + downloading finished + * + * @param[in] dest Destination address to download data + * @param[in] downsize download size of this time + * @param[in] download_callback download finished callback + * + * @retval 0 success + * @retval minus failed + * + * @note None + */ +int tls_dmr_download_data(char * dest, int downsize, dmr_download_finish_callback download_callback); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* __WM__DLNA_DMR_H_ */ + diff --git a/sdk/Include/App/wm_dns_server.h b/sdk/Include/App/wm_dns_server.h new file mode 100644 index 0000000..298b896 --- /dev/null +++ b/sdk/Include/App/wm_dns_server.h @@ -0,0 +1,66 @@ +/** + * @file wm_dns_server.h + * + * @brief DNS SERVER + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_DNS_SERVER_H +#define WM_DNS_SERVER_H + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup DNSS_APIs DNSS APIs + * @brief DNS server APIs + */ + +/** + * @addtogroup DNSS_APIs + * @{ + */ + +/** + * @brief This function is used to start DNS service + * + * @param[in] *dnsname Specify the server's dns name + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +s8 tls_dnss_start(u8 *dnsname); + +/** + * @brief This function is used to stop DNS service + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_dnss_stop(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_DNS_SERVER_H */ + diff --git a/sdk/Include/App/wm_http_client.h b/sdk/Include/App/wm_http_client.h new file mode 100644 index 0000000..8fcd221 --- /dev/null +++ b/sdk/Include/App/wm_http_client.h @@ -0,0 +1,486 @@ +/** + * @file wm_http_client.h + * + * @brief Http client APIs + * + * @author wanghf + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_HTTP_CLIENT_H +#define WM_HTTP_CLIENT_H + +#include "wm_config.h" +#include "wm_type_def.h" +#ifdef BOOL +#undef BOOL +#endif +#ifdef UCHAR +#undef UCHAR +#endif +#ifdef CHAR +#undef CHAR +#endif +#ifdef UINT16 +#undef UINT16 +#endif +#ifdef INT16 +#undef INT16 +#endif +#ifdef UINT32 +#undef UINT32 +#endif +#ifdef INT32 +#undef INT32 +#endif +#ifdef UINT64 +#undef UINT64 +#endif +#ifdef INT64 +#undef INT64 +#endif +#ifdef ULONG +#undef ULONG +#endif +#ifdef LONG +#undef LONG +#endif +#define VOID void +typedef int BOOL; +typedef unsigned char UCHAR; +//typedef signed char CHAR; +typedef char CHAR; +typedef unsigned short UINT16; +typedef signed short INT16; +typedef unsigned int UINT32; +typedef signed int INT32; +typedef unsigned long long UINT64; +typedef long long INT64; +typedef unsigned long ULONG; +typedef signed long LONG; + + +/* HTTP Status, API Return codes */ +/** HTTP Success status */ +#define HTTP_CLIENT_SUCCESS 0 +/** Unknown error */ +#define HTTP_CLIENT_UNKNOWN_ERROR 1 +/** an Invalid handle or possible bad pointer was passed to a function */ +#define HTTP_CLIENT_ERROR_INVALID_HANDLE 2 +/** Buffer too small or a failure while in memory allocation */ +#define HTTP_CLIENT_ERROR_NO_MEMORY 3 +/** an attempt to use an invalid socket handle was made */ +#define HTTP_CLIENT_ERROR_SOCKET_INVALID 4 +/** Can't send socket parameters */ +#define HTTP_CLIENT_ERROR_SOCKET_CANT_SET 5 +/** Error while resolving host name */ +#define HTTP_CLIENT_ERROR_SOCKET_RESOLVE 6 +/** Error while connecting to the remote server */ +#define HTTP_CLIENT_ERROR_SOCKET_CONNECT 7 +/** socket time out error */ +#define HTTP_CLIENT_ERROR_SOCKET_TIME_OUT 8 +/** Error while receiving data */ +#define HTTP_CLIENT_ERROR_SOCKET_RECV 9 +/** Error while sending data */ +#define HTTP_CLIENT_ERROR_SOCKET_SEND 10 +/** Error while receiving the remote HTTP headers */ +#define HTTP_CLIENT_ERROR_HEADER_RECV 11 +/** Could not find element within header */ +#define HTTP_CLIENT_ERROR_HEADER_NOT_FOUND 12 +/** The headers search clue was too large for the internal API buffer */ +#define HTTP_CLIENT_ERROR_HEADER_BIG_CLUE 13 +/** No content length was specified for the outgoing data. the caller should + specify chunking mode in the session creation */ +#define HTTP_CLIENT_ERROR_HEADER_NO_LENGTH 14 +/** The HTTP chunk token that was received from the server was too big and possibly wrong */ +#define HTTP_CLIENT_ERROR_CHUNK_TOO_BIG 15 +/** Could not authenticate with the remote host */ +#define HTTP_CLIENT_ERROR_AUTH_HOST 16 +/** Could not authenticate with the remote proxy */ +#define HTTP_CLIENT_ERROR_AUTH_PROXY 17 +/** Bad or not supported HTTP verb was passed to a function */ +#define HTTP_CLIENT_ERROR_BAD_VERB 18 +/** a function received a parameter that was too large */ +#define HTTP_CLIENT_ERROR_LONG_INPUT 19 +/** The session state prevents the current function from proceeding */ +#define HTTP_CLIENT_ERROR_BAD_STATE 20 +/** Could not parse the chunk length while in chunked transfer */ +#define HTTP_CLIENT_ERROR_CHUNK 21 +/** Could not parse curtail elements from the URL (such as the host name, HTTP prefix act') */ +#define HTTP_CLIENT_ERROR_BAD_URL 22 +/** Could not detect key elements in the received headers */ +#define HTTP_CLIENT_ERROR_BAD_HEADER 23 +/** Error while attempting to resize a buffer */ +#define HTTP_CLIENT_ERROR_BUFFER_RSIZE 24 +/** Authentication schema is not supported */ +#define HTTP_CLIENT_ERROR_BAD_AUTH 25 +/** The selected authentication schema does not match the server response */ +#define HTTP_CLIENT_ERROR_AUTH_MISMATCH 26 +/** an element was missing while parsing the digest authentication challenge */ +#define HTTP_CLIENT_ERROR_NO_DIGEST_TOKEN 27 +/** Digest algorithem could be MD5 or MD5-sess other types are not supported */ +#define HTTP_CLIENT_ERROR_NO_DIGEST_ALG 28 +/** Binding error */ +#define HTTP_CLIENT_ERROR_SOCKET_BIND 29 +/** Tls negotiation error */ +#define HTTP_CLIENT_ERROR_TLS_NEGO 30 +/** Feature is not (yet) implemented */ +#define HTTP_CLIENT_ERROR_NOT_IMPLEMENTED 64 +/** HTTP end of stream message */ +#define HTTP_CLIENT_EOS 1000 + +// HTTP Session flags (Public flags) +#define HTTP_CLIENT_FLAG_KEEP_ALIVE 0x00000001 // Set the keep alive header +#define HTTP_CLIENT_FLAG_SEND_CHUNKED 0x00000002 // The outgoing should chunked +#define HTTP_CLIENT_FLAG_NO_CACHE 0x00000004 // Set the no cache header +#define HTTP_CLIENT_FLAG_ASYNC 0x00000008 // Currently not implemented +#define HTTP_CLIENT_FLAG_MULTIPART_FORM 0x00000010 // The outgoing should multipart/form-data + +// HTTP Type Definitions +typedef UINT32 HTTP_SESSION_HANDLE; +typedef UINT32 HTTP_CLIENT_SESSION_FLAGS; +/****************************************************************************** +* +* Section : HTTP API structures +* +******************************************************************************/ + +/* HTTP Type Definitions */ +/** http seesion handle */ +typedef u32 tls_http_session_handle_t; +/** http seesion flags */ +typedef u32 tls_http_session_flags_t; + +/** HTTP Supported authentication methods */ +typedef enum _HTTP_AUTH_SCHEMA +{ + AuthSchemaNone = 0, + AuthSchemaBasic, + AuthSchemaDigest, + AuthSchemaKerberos, + AuthNotSupported + +} HTTP_AUTH_SCHEMA; +/** HTTP supported verbs */ +typedef enum _HTTP_VERB +{ + VerbGet = 0, + VerbHead, + VerbPost, + VerbPut, + VerbFwup, + VerbNotSupported + // Note: others verb such as connect and put are currently not supported + +} HTTP_VERB; +/** Data structure that the caller can request at any time that will include + some information regarding the session */ +typedef struct _HTTP_CLIENT +{ + UINT32 HTTPStatusCode; // HTTP Status code (200 OK) + UINT32 RequestBodyLengthSent; // Total bytes sent (body only) + UINT32 ResponseBodyLengthReceived; // Total bytes received (body only) + UINT32 TotalResponseBodyLength; // as extracted from the “content-length" header + UINT32 HttpState; +} HTTP_CLIENT; + +/** HTTP parameters */ +typedef struct _HTTPParameters +{ + CHAR* Uri; + CHAR* ProxyHost; + UINT32 UseProxy ; + UINT32 ProxyPort; + UINT32 Verbose; + CHAR* UserName; + CHAR* Password; + HTTP_AUTH_SCHEMA AuthType; + +} HTTPParameters; + +#if TLS_CONFIG_HTTP_CLIENT_TASK +/** the callback function of http clent for received */ +typedef void (*http_client_recv_callback_fn)(HTTP_SESSION_HANDLE pSession, CHAR * data, UINT32 totallen, UINT32 datalen); +/** the callback function of http clent for err */ +typedef void (*http_client_err_callback_fn)(HTTP_SESSION_HANDLE pSession, int err); + +/** message of the http client */ +typedef struct _http_client_msg +{ + HTTP_SESSION_HANDLE pSession; + HTTPParameters param; + HTTP_VERB method; + CHAR* sendData; + UINT32 dataLen; + http_client_recv_callback_fn recv_fn; + http_client_err_callback_fn err_fn; +} http_client_msg; +#endif + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup HTTPC_APIs HTTPC APIs + * @brief HTTP client APIs + */ + +/** + * @addtogroup HTTPC_APIs + * @{ + */ + + +/****************************************************************************** +* +* Section : HTTP API public interface +* +******************************************************************************/ + +/** + * @brief Allocate memory for a new HTTP Session + * + * @param[in] Flags HTTP Session internal API flags, 0 should be passed here + * + * @retval 0 failed + * @retval other HTTP Session handle + * + * @note None + */ +HTTP_SESSION_HANDLE HTTPClientOpenRequest (HTTP_CLIENT_SESSION_FLAGS Flags); +/** + * @brief Closes the active connection and free the corresponding memory + * + * @param[in] *pSession HTTP Session handle + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientCloseRequest (HTTP_SESSION_HANDLE *pSession); +/** + * @brief Sets the HTTP authentication schema + * + * @param[in] pSession HTTP Session handle + * @param[in] AuthSchema HTTP Supported authentication methods + * @param[in] *pReserved Reserved parameter + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientSetAuth (HTTP_SESSION_HANDLE pSession, HTTP_AUTH_SCHEMA AuthSchema, void *pReserved); +/** + * @brief Sets credentials for the target host + * + * @param[in] pSession HTTP Session handle + * @param[in] *pUserName User name + * @param[in] *pPassword Password + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientSetCredentials (HTTP_SESSION_HANDLE pSession, CHAR *pUserName, CHAR *pPassword); +/** + * @brief Sets all the proxy related parameters + * + * @param[in] pSession HTTP Session handle + * @param[in] *pProxyName The host name + * @param[in] nPort The proxy port number + * @param[in] *pUserName User name for proxy authentication (can be null) + * @param[in] *pPassword User password for proxy authentication (can be null) + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientSetProxy (HTTP_SESSION_HANDLE pSession, CHAR *pProxyName, UINT16 nPort, CHAR *pUserName, CHAR *pPassword); +/** + * @brief Sets the HTTP verb for the outgoing request + * + * @param[in] pSession HTTP Session handle + * @param[in] HttpVerb HTTP supported verbs + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientSetVerb (HTTP_SESSION_HANDLE pSession, HTTP_VERB HttpVerb); +/** + * @brief Add headers into the outgoing request + * + * @param[in] pSession HTTP Session + * @param[in] *pHeaderName The Header name + * @param[in] *pHeaderData The header data + * @param[in] nInsert Reserved, could be any + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientAddRequestHeaders (HTTP_SESSION_HANDLE pSession, CHAR *pHeaderName, CHAR *pHeaderData, BOOL nInsert); +/** + * @brief This function builds the request headers, performs a DNS resolution, + * opens the connection (if it was not opened yet by a previous request + * or if it has closed) and sends the request headers + * + * @param[in] pSession HTTP Session handle + * @param[in] *pUrl The requested URL + * @param[in] *pData Data to post to the server + * @param[in] nDataLength Length of posted data + * @param[in] TotalLength Valid only when http method is post + * TRUE: Post data to http server. + * FALSE: In a post request without knowing the total + * length in advance so return error or use chunking. + * @param[in] nTimeout Operation timeout + * @param[in] nClientPort Client side port 0 for none + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientSendRequest (HTTP_SESSION_HANDLE pSession, CHAR *pUrl, VOID *pData, UINT32 nDataLength, BOOL TotalLength, UINT32 nTimeout,UINT32 nClientPort); +/** + * @brief Write data to the remote server + * + * @param[in] pSession HTTP Session handle + * @param[in] *pBuffer Data to write to the server + * @param[in] nBufferLength Length of wtitten data + * @param[in] nTimeout Timeout for the operation + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientWriteData (HTTP_SESSION_HANDLE pSession, VOID *pBuffer, UINT32 nBufferLength, UINT32 nTimeout); +/** + * @brief Receives the response header on the connection and parses it. + * Performs any required authentication. + * + * @param[in] pSession HTTP Session handle + * @param[in] nTimeout Timeout for the operation + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientRecvResponse (HTTP_SESSION_HANDLE pSession, UINT32 nTimeout); +/** + * @brief Read data from the server. Parse out the chunks data + * + * @param[in] pSession HTTP Session handle + * @param[out] *pBuffer A pointer to a buffer that will be filled with the servers response + * @param[in] nBytesToRead The size of the buffer (numbers of bytes to read) + * @param[in] nTimeout Operation timeout in seconds + * @param[out] *nBytesRecived Count of the bytes that were received in this operation + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientReadData (HTTP_SESSION_HANDLE pSession, VOID *pBuffer, UINT32 nBytesToRead, UINT32 nTimeout, UINT32 *nBytesRecived); +/** + * @brief Fill the users structure with the session information + * + * @param[in] pSession HTTP Session handle + * @param[out] *HTTPClient The session information + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientGetInfo (HTTP_SESSION_HANDLE pSession, HTTP_CLIENT *HTTPClient); +/** + * @brief Initiate the headr searching functions and find the first header + * + * @param[in] pSession HTTP Session handle + * @param[in] *pSearchClue Search clue + * @param[out] *pHeaderBuffer A pointer to a buffer that will be filled with the header name and value + * @param[out] *nLength Count of the bytes that were received in this operation + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientFindFirstHeader (HTTP_SESSION_HANDLE pSession, CHAR *pSearchClue,CHAR *pHeaderBuffer, UINT32 *nLength); +/** + * @brief Find the next header. + * + * @param[in] pSession HTTP Session handle + * @param[out] *pHeaderBuffer A pointer to a buffer that will be filled with the header name and value + * @param[out] *nLength Count of the bytes that were received in this operation + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientGetNextHeader (HTTP_SESSION_HANDLE pSession, CHAR *pHeaderBuffer, UINT32 *nLength); +/** + * @brief Terminate a headers search session + * + * @param[in] pSession HTTP Session handle + * + * @retval HTTP_CLIENT_SUCCESS success + * @retval other failed + * + * @note None + */ +UINT32 HTTPClientFindCloseHeader (HTTP_SESSION_HANDLE pSession); + +#if TLS_CONFIG_HTTP_CLIENT_TASK +/** + * @brief initialize task of the http client + * + * @param None + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int http_client_task_init(void); + +/** + * @brief post message to the task of http client + * + * @param[in] msg pointer to the message + * + * @retval ERR_OK success + * @retval other failed + * + * @note None + */ +int http_client_post(http_client_msg * msg); +#endif /* TLS_CONFIG_HTTP_CLIENT_TASK */ + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_HTTP_CLIENT_H */ + diff --git a/sdk/Include/App/wm_netif.h b/sdk/Include/App/wm_netif.h new file mode 100644 index 0000000..6fcc488 --- /dev/null +++ b/sdk/Include/App/wm_netif.h @@ -0,0 +1,15 @@ +/** + * @file wm_netif.h + * + * @brief ETHERNET INIT Interface + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_NETIF_H +#define WM_NETIF_H +#include "wm_config.h" +#include "wm_netif2.0.3.h" +#endif /* WM_NETIF_H */ + diff --git a/sdk/Include/App/wm_netif2.0.3.h b/sdk/Include/App/wm_netif2.0.3.h new file mode 100644 index 0000000..55b6797 --- /dev/null +++ b/sdk/Include/App/wm_netif2.0.3.h @@ -0,0 +1,400 @@ +/** + * @file wm_netif2.0.3.h + * + * @brief netif203 module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_NETIF2_0_3_H +#define WM_NETIF2_0_3_H + +#include "wm_config.h" +#include "wm_type_def.h" +#include "wm_sockets.h" +#include "wm_wifi.h" +#include "wm_params.h" + +/** MACRO for callback EVENT to join AP or create soft-AP successfully */ +#define NETIF_WIFI_JOIN_SUCCESS 0x1 +/** MACRO for callback EVENT to fail to join AP */ +#define NETIF_WIFI_JOIN_FAILED 0x2 +/** MACRO for callback EVENT to disconnect from AP or destroy soft-AP */ +#define NETIF_WIFI_DISCONNECTED 0x3 +/** MACRO for callbck EVENT to get IP address */ +#define NETIF_IP_NET_UP 0x4 +/** MACRO for callback EVNET to create AP successfully */ +#define NETIF_WIFI_SOFTAP_SUCCESS 0x5 +/** MACRO for callback EVNET to create soft-AP failed */ +#define NETIF_WIFI_SOFTAP_FAILED 0x6 +/** MACRO for callback EVNET to close soft-AP */ +#define NETIF_WIFI_SOFTAP_CLOSED 0x7 +/** MACRO for callback EVNET to inform soft ap's net */ +#define NETIF_IP_NET2_UP 0x8 + +#define NETIF_IPV6_NET_UP 0x9 + +/** These are the values for ip_addr_t.type */ +#define IPADDR_TYPE_V4 0U +#define IPADDR_TYPE_V6 6U +#define IPADDR_TYPE_ANY 46U + +#define IPV6_ADDR_MAX_NUM 3 + +#if 0 +struct ip_addr { + u32_t addr; +}; + +typedef struct ip_addr ip_addr_t; +#endif +#if 0 +struct ip4_addr { + u32_t addr; +}; +typedef struct ip4_addr ip4_addr_t; + +struct ip6_addr { + u32_t addr[4]; +}; +typedef struct ip6_addr ip6_addr_t; + +#if (TLS_CONFIG_IPV4 && TLS_CONFIG_IPV6) +typedef struct _ip_addr { + union { + ip6_addr_t ip6; + ip4_addr_t ip4; + } u_addr; + u8_t type; +} ip_addr_t; +#else +#if TLS_CONFIG_IPV4 +typedef ip4_addr_t ip_addr_t; +#else +typedef ip6_addr_t ip_addr_t; +#endif +#endif +#endif +struct tls_ethif { + ip_addr_t ip_addr; + ip_addr_t netmask; + ip_addr_t gw; +#if TLS_CONFIG_IPV6 + ip_addr_t ip6_addr[IPV6_ADDR_MAX_NUM]; +#endif + ip_addr_t dns1; + ip_addr_t dns2; + u8 status; //0:net down; 1:net up +#if TLS_CONFIG_IPV6 + u8 ipv6_status[IPV6_ADDR_MAX_NUM]; //0:net down; 1:net up +#endif +}; + +//type defination of netif status changed callback. +typedef void (*tls_netif_status_event_fn)(u8 status); + +/** + * @brief This function is used to initialize TCP/IP Stack + * + * @param[in] None + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_ethernet_init(void); + +/** + * @brief This function is used to get IP information stored in + tls_ethif struct + * + * @param[in] None + * + * @retval tls_ethif * Pointer to struct tls_ethif + * + * @note None + */ +struct tls_ethif * tls_netif_get_ethif(void); + +/** + * @brief This function is used to set tls_ethif status + * + * @param[in] status net status, 0-up, 1-down + * + * @return None + * + * @note None + */ +void tls_netif_set_status(u8 status); + +/** + * @brief This function is used to start DHCP Client + * + * @param[in] None + * + * @retval 0 success + * @retval Minus failed + * + * @note None + */ +err_t tls_dhcp_start(void); + +/** + * @brief This function is used to stop DHCP client + * + * @param[in] None + * + * @retval 0 success + * @retval Minus failed + * + * @note None + */ +err_t tls_dhcp_stop(void); + +/** + * @brief This function is used to change IP information + * + * @param[in] *ipaddr IP address + * @param[in] *netmask netmask + * @param[in] *gw default gateway + * + * @retval 0 success + * @retval Minus failed + * + * @note None + */ +err_t tls_netif_set_addr(ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw); + +/** + * @brief This function is used to set dns servers + * + * @param[in] numdns index of the DNS server to set + must be < DNS_MAX_SERVERS + * @param[in] *dnsserver IP address of the DNS server to set + * + * @return None + * + * @note None + */ +void tls_netif_dns_setserver(u8 numdns, ip_addr_t *dnsserver); + +/** + * @brief This function is used to bring up an interface,available + for processing traffic + * + * @param[in] None + * + * @retval 0 success + * @retval Minus failed + * + * @note None + */ +err_t tls_netif_set_up(void); + +/** + * @brief This function is used to bring down an interface,disabling + any traffic processing + * + * @param[in] None + * + * @retval 0 success + * @retval Minus failed + * + * @note None + */ +err_t tls_netif_set_down(void); + +/** + * @brief This function is used to add netif status changed callback + to event list,if exists, do nothing + * + * @param[in] event_fn pointer to tls_netif_status_event_fn + * + * @retval 0 success + * @retval Minus failed + * + * @note None + */ +err_t tls_netif_add_status_event(tls_netif_status_event_fn event_fn); + +/** + * @brief This function is used to remove netif status changed + callback function from event list,if not exists, do nothing + * + * @param[in] event_fn pointer to tls_netif_status_event_fn + * + * @retval 0 success + * @retval Minus failed + * + * @note None + */ +err_t tls_netif_remove_status_event(tls_netif_status_event_fn event_fn); + +struct netif *tls_get_netif(void); + +#if TLS_CONFIG_AP +/** + * @brief Start DHCP Server for a network interface + * * + * @retval DHCPS_ERR_SUCCESS - No error + * @retval DHCPS_ERR_MEM - Out of memory + * @retval DHCPS_ERR_LINKDOWN - The NI is inactive + * + * @note None + */ +INT8S tls_dhcps_start(void); + +/** + * @brief This function is used to stop DHCP Server + * + * @param[in] None + * + * @retval None + * + * @note None + */ +void tls_dhcps_stop(void); + +/** + * @brief Start the dns server's service + * * + * @retval DHCPS_ERR_SUCCESS - No error + * @retval DHCPS_ERR_MEM - Out of memory + * @retval DHCPS_ERR_LINKDOWN - The NI is inactive + * @retval DNSS_ERR_PARAM - Input parameter error + * + * @note None + */ +INT8S tls_dnss_start(INT8U * DnsName); + +/** + * @brief Stop the dns server's service + * + * @param[in] None + * + * @retval None + * + * @note None + */ +void tls_dnss_stop(void); + +/** + * @brief Get station's ip address by mac address + * + * @param[in] mac station's mac address + * + * @retval ip_addr station's ip address + * + * @note None + */ +ip_addr_t *tls_dhcps_getip(const u8_t *mac); + +/** + * @brief Get station's mac address by ip address + * + * @param[in] ip station's ip address + * + * @retval u8* station's mac address + * + * @note None + */ +u8 *tls_dhcps_getmac(const ip_addr_t *ip); +#endif //TLS_CONFIG_AP + +#if TLS_CONFIG_RMMS +/** + * @brief Start remote manager server. + * * + * @retval DHCPS_ERR_SUCCESS - No error + * @retval DHCPS_ERR_MEM - Out of memory + * @retval DHCPS_ERR_LINKDOWN - The NIF is inactive + * + * @note None + */ +INT8S tls_rmms_start(void); + +/** + * @brief Disable remote manager server + * + * @param[in] None + * + * @retval None + * + * @note None + */ +void tls_rmms_stop(void); +#endif + +#if TLS_CONFIG_AP +/** + * @brief This is used to bring up an interface for APSTA,available + for processing traffic + * + * @param[in] None + * + * @retval 0 success + * @retval Minus failed + * + * @note Can only be used at APSTA mode + */ +err_t tls_netif2_set_up(void); + +/** + * @brief This function is used to bring down an interface for APSTA, disabling + any traffic processing + * + * @param[in] None + * + * @retval 0 success + * @retval Minus failed + * + * @note Can only be used at APSTA mode + */ +err_t tls_netif2_set_down(void); + +/** + * @brief This function is used to change IP information for + a network interface for APSTA + * + * @param[in] *ipaddr IP address + * @param[in] *netmask netmask + * @param[in] *gw default gateway + * + * @retval 0 success + * @retval Minus failed + * + * @note Can only be used at APSTA mode + */ +err_t tls_netif2_set_addr(ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw); +/*************************************************************************** +* Function: tls_dhcps_setdns +* Description: Set dhcp server's dns address. +* +* Input: numdns: the index of the DNS server to set must be less than DNS_MAX_SERVERS +* +* Output: None +* +* Return: None +* +* Date : 2015-3-10 +****************************************************************************/ +/** + * @brief Set dhcp server's dns address + * + * @param[in] numdns the index of the DNS server to set must be less than DNS_MAX_SERVERS + * + * @retval None + * + * @note Can only be used at APSTA mode + */ +void tls_dhcps_setdns(u8_t numdns); +#endif + +#endif //WM_NETIF_H diff --git a/sdk/Include/App/wm_ntp.h b/sdk/Include/App/wm_ntp.h new file mode 100644 index 0000000..0b2b69b --- /dev/null +++ b/sdk/Include/App/wm_ntp.h @@ -0,0 +1,80 @@ +/** + * @file wm_ntp.h + * + * @brief ntp module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_NTP_H +#define WM_NTP_H + +#include "wm_type_def.h" + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup NTP_APIs NTP APIs + * @brief NTP APIs + */ + +/** + * @addtogroup NTP_APIs + * @{ + */ + +/** + * @brief This function is used to get network time. + * + * @param None + * + * @retval time value + * + * @note None + */ +u32 tls_ntp_client(void); + +/** + * @brief This function is used to set ntp servers. + * + * @param[in] *ipaddr xxx.xxx.xxx.xxx + * @param[in] server_no max num is three + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_ntp_set_server(char *ipaddr, int server_no); + +/** + * @brief This function is used to query params of the ntp servers + * + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_ntp_query_sntpcfg(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_NTP_H */ + diff --git a/sdk/Include/App/wm_webserver.h b/sdk/Include/App/wm_webserver.h new file mode 100644 index 0000000..7c3b7cd --- /dev/null +++ b/sdk/Include/App/wm_webserver.h @@ -0,0 +1,65 @@ +/** + * @file wm_webserver.h + * + * @brief WEB SERVER + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef __WEBSERVER_H__ +#define __WEBSERVER_H__ + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup WEB_APIs WEB APIs + * @brief WEB server APIs + */ + +/** + * @addtogroup WEB_APIs + * @{ + */ + +/** + * @brief This function is used to start WEB SERVER service + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_webserver_init(void); + +/** + * @brief This function is used to deinit WEB SERVER service + * + * @param[in] None + * + * @return None + * + * @note None + */ +void httpd_deinit(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /*__WEBSERVER_H__*/ + diff --git a/sdk/Include/App/wm_wifi_oneshot.h b/sdk/Include/App/wm_wifi_oneshot.h new file mode 100644 index 0000000..00923e4 --- /dev/null +++ b/sdk/Include/App/wm_wifi_oneshot.h @@ -0,0 +1,284 @@ +/** + * @file wm_wifi_oneshot.h + * + * @brief Wi-Fi OneShot + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_WIFI_ONESHOT_H +#define WM_WIFI_ONESHOT_H +#include +#include +#include +#include +#if (GCC_COMPILE==1) +#include "wm_ieee80211_gcc.h" +#else +#include +#endif +#include "wm_config.h" + +/** DEBUG USE MAC FILTER START */ +#define CONFIG_ONESHOT_MAC_FILTER 0 +extern int tls_filter_module_srcmac(u8 *mac); + + +/** after onshot configuration, add random time to connect Wi-Fi */ +#define CONFIG_CONNECT_RANDOMTIME_AFTER_ONESHOT 0 + + + +#define ONESHOT_ON 1 +#define ONESHOT_OFF 0 + +/* ONE SHOT */ +/** UDP MULTICAST ONE SHOT */ +#define TLS_CONFIG_UDP_ONE_SHOT ONESHOT_OFF +/** JD ONESHOT */ +#define TLS_CONFIG_UDP_JD_ONESHOT (ONESHOT_OFF&& TLS_CONFIG_UDP_ONE_SHOT) +/** WinnerMicro ONSHOT */ +#define TLS_CONFIG_UDP_LSD_ONESHOT (ONESHOT_OFF&& TLS_CONFIG_UDP_ONE_SHOT) + +#define TLS_CONFIG_UDP_LSD_SPECIAL (ONESHOT_OFF&& TLS_CONFIG_UDP_ONE_SHOT) + +#define TLS_CONFIG_ONESHOT_DELAY_SPECIAL ONESHOT_OFF + +/** AP ONESHOT */ +#define TLS_CONFIG_AP_MODE_ONESHOT (ONESHOT_OFF&&TLS_CONFIG_AP) +#define TLS_CONFIG_WEB_SERVER_MODE (CFG_OFF&&TLS_CONFIG_AP_MODE_ONESHOT) +#define TLS_CONFIG_SOCKET_MODE (CFG_OFF &&TLS_CONFIG_AP_MODE_ONESHOT && TLS_CONFIG_SOCKET_RAW) + + +/** AIRKISS ONESHOT */ +#define TLS_CONFIG_AIRKISS_MODE_ONESHOT (ONESHOT_OFF && TLS_CONFIG_UDP_ONE_SHOT) +#define AIRKISS_USE_SELF_WRITE 0 + +typedef enum{ + ONESHOT_SCAN_START, + ONESHOT_SCAN_FINISHED, + ONESHOT_SWITCH_CHANNEL, + ONESHOT_STOP_TMP_CHAN_SWITCH, + ONESHOT_STOP_CHAN_SWITCH, + ONESHOT_HANDSHAKE_TIMEOUT, + ONESHOT_RECV_TIMEOUT, + ONESHOT_RECV_ERR, + ONESHOT_STOP_DATA_CLEAR, + ONESHOT_NET_UP, + AP_SOCK_S_MSG_SOCKET_RECEIVE_DATA, + AP_WEB_S_MSG_RECEIVE_DATA, + AP_SOCK_S_MSG_SOCKET_CREATE, + AP_SOCK_S_MSG_WJOIN_FAILD, +}ONESHOT_MSG_ENUM; + +/** + * @defgroup APP_APIs APP APIs + * @brief APP APIs + */ + +/** + * @addtogroup APP_APIs + * @{ + */ + +/** + * @defgroup Oneshot_APIs Oneshot APIs + * @brief Wi-Fi oneshot APIs + */ + +/** + * @addtogroup Oneshot_APIs + * @{ + */ + +/** + * @brief Handle wild packets coming from the air. + * + * @param[in] *hdr point to ieee80211 data header + * @param[in] data_len data len of ieee80211 data + * + * @retval no mean + * + * @note None + */ +u8 tls_wifi_dataframe_recv(struct ieee80211_hdr *hdr, u32 data_len); + +#if TLS_CONFIG_AIRKISS_MODE_ONESHOT +/** + * @brief This function is used to acknowledge app when airkiss process is done. + * + * @param[in] None + * + * @return None + * + * @note None + */ +void oneshot_airkiss_send_reply(void); +/** + * @brief This function is used to deal with airkiss's + wild packet + * + * @param[in] *data ieee80211 packet + * @param[in] data_len packet length + * + * @return None + * + * @note None + */ +void tls_airkiss_recv(u8 *data, u16 data_len); +/** + * @brief This function is used to start airkiss + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_airkiss_start(void); +/** + * @brief This function is used to stop airkiss + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_airkiss_stop(void); + +/** + * @brief This function is used to change channel for airkiss + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_oneshot_airkiss_change_channel(void); +#endif /*TLS_CONFIG_AIRKISS_MODE_ONESHOT*/ + +/** + * @brief This function is used to init oneshot task + * + * @param[in] None + * + * @return None + * + * @note Not in use now + */ +int wm_oneshot_task_init(void); + +/** + * @brief This function is used to stop oneshot timer + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_oneshot_switch_channel_tim_stop(struct ieee80211_hdr *hdr); + +/** + * @brief This function is used to stop oneshot temp timer + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_oneshot_switch_channel_tim_temp_stop(void); + +/** + * @brief Judge bssid&ssid if match + * + * @param[in] *ssid ssid to be compared + * @param[in] ssid_len ssid length + * @param[in] bssid bssid to be compared + * + * @retval 1: bssid&ssid match + * @retval 0: bssid&ssid not match + * + * @note None + */ +int tls_oneshot_is_ssid_bssid_match(u8 *ssid, u8 ssid_len, u8 *bssid); + +/** + * @brief Find channel according to ssid + * + * @param[in] *ssid ssid to be compared + * @param[in] ssid_len ssid length + * @param[out] chlist chlist to be add according to ssid info + * + * @retval None + * + * @note None + */ +void tls_oneshot_find_chlist(u8 *ssid, u8 ssid_len, u16 *chlist); + +/** + * @} + */ +void wm_oneshot_netif_status_event(u8 status ); + +/** + * @brief This function is used to set oneshot config mode. + * + * @param[in] flag, 0:one shot config + * 1:softap socket config + * 2:softap webserver config + * @param[out] None + * + * @retval None + * + * @note None + */ +void tls_wifi_set_oneshot_config_mode(u8 flag); + +/** + * @brief This function is used to get oneshot config mode. + * + * @param[in] None + * + * @param[out] None + * + * @retval 0: one shot config + * 1: softap socket config + * 2: softap webserver config + * + * @note None + */ +u8 tls_wifi_get_oneshot_config_mode(void); + +#if TLS_CONFIG_WEB_SERVER_MODE +/** + * @brief This function is used to send web config msg to oneshot task. + * + * @param[in] None + * + * @param[out] None + * + * @retval None + * + * @note None + */ +void tls_oneshot_send_web_connect_msg(void); +#endif + + + +/** + * @} + */ + +/** + * @} + */ + +#endif /*WM_WIFI_ONESHOT_H*/ + diff --git a/sdk/Include/Driver/wm_7816.h b/sdk/Include/Driver/wm_7816.h new file mode 100644 index 0000000..c7ac640 --- /dev/null +++ b/sdk/Include/Driver/wm_7816.h @@ -0,0 +1,242 @@ +/**************************************************************************//** + * @file wm_7816.h + * @author + * @version + * @date + * @brief + * @copyright (c) 2014 Winner Microelectronics Co., Ltd. All rights reserved. + *****************************************************************************/ +#ifndef WM_7816_H_ +#define WM_7816_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "wm_regs.h" +#include "wm_type_def.h" +#include "wm_io.h" + +#define WM_SC_RST_PIN WM_IO_PB_23 //(23) +#define WM_SC_PWR_PIN WM_IO_PB_29 //(29) + +#define WM_SC_DEFAULT_FD (372) + +typedef struct sc_io_map_ { + enum tls_io_name clk_pin_num; + uint32_t clk_opt; + enum tls_io_name io_pin_num; + uint32_t io_opt; + uint8_t initialed; +} sc_io_map; + +extern sc_io_map sc_io; + +/** + * @brief + * This function is used to config the pin in gpio or 7816 mode for the 7816 power on timing + * + * @param[in] mode : 1--gpio mode ; 0--7816 mode + * + * @retval + */ +void wm_sc_io_clk_config(uint8_t mode); + +/** + * @brief + * close af to use as gpio + * @retval + */ +void wm_sc_powerInit(void); + +/** + * @brief + * power on the 7816 device if power is controled by GPIO + * @retval + */ +void wm_sc_poweron(void); + +/** + * @brief + * power off the 7816 device if power is controled by GPIO + * @retval + */ +void wm_sc_poweroff(void); + +/** + * @brief + * drive the reset gpio in low level + * @retval + */ +void wm_sc_rst_low(void); + +/** + * @brief + * driver the reset gpio in high level + * @retval + */ +void wm_sc_rst_high(void); + +/** + * @brief + * hotrest the 7816 device obey the 7816-3 timing + * @retval + */ +void wm_sc_hotreset(void); + +/** + * @brief + * colreset the 7816 device obey the 7816-3 timing + * @retval + */ +void wm_sc_colreset(void); + +/** + * @brief + * deactive the 7816 device obey the 7816-3 timing + * @retval + */ +void wm_sc_deactive(void); + +/** + * @brief + * This function is used to config the block guard time param in 7816 mode + * @param[in] bgt : the value of blcok guard time will be set + * @retval + */ +void wm_sc_set_bgt(uint8_t bgt); + +/** + * @brief + * This function is used to config the tx retry count when detect err signal + * @param[in] count : the value of retry time will be set 7 for max + * @retval + */ +void wm_sc_tx_retry_times(uint8_t count); + +/** + * @brief + * This function is used to config the rx retry count when detect parity error + * @param[in] count : the value of retry time will be set 7 for max + * @retval + */ +void wm_sc_rx_retry_times(uint8_t count); + +/** + * @brief + * This function is used to config the etu param + * @param[in] etu : the value of etu will be set + * @retval + */ +void wm_sc_set_etu(uint16_t etu); + +/** + * @brief + * This function config the module clock freq + * @param[in] freq : the value of clock freq + * @retval + */ +void wm_sc_set_frequency(uint32_t freq); + +/** + * @brief + * config recv or not when parity error + * @param[in] bl : 1--- recv + * 0--- don't recv + * @retval + */ +static inline void wm_sc_parity_recv(bool bl) +{ + tls_bitband_write(HR_UART2_LINE_CTRL, 9, bl); +} + +/** + * @brief + * select the model in 7816 or uart function + * @param[in] bl : 1---7816 mode + * 0---uart mode + * @retval + */ +static inline void wm_sc_7816_mode(bool bl) +{ + tls_bitband_write(HR_UART2_LINE_CTRL, 24, bl); +} + +/** + * @brief + * This function is used to config the guard time param + * @param[in] bwt : the value of the guard time will be set + * @retval + */ +static inline void wm_sc_set_guardtime(uint8_t gt) +{ + tls_reg_write32(HR_UART2_GUARD_TIME, gt); +} + +/** + * @brief + * This function is used to config the CWT or BWT param + * @param[in] bwt : the value of CWT or BWT will be set + * @retval + */ +static inline void wm_sc_set_bcwt(uint32_t bwt) +{ + bwt = (bwt > 0xFFFFFF) ? 0xFFFFFF : bwt; + tls_reg_write32(HR_UART2_WAIT_TIME, bwt); +} + +/** + * @brief + * module errsignal int enable or disable + * @param[in] bl : 1---enable + * 0---disable + * @retval + */ +static inline void wm_sc_tx_errsignal_mask(bool bl) +{ + tls_bitband_write(HR_UART2_INT_MASK, 9, bl); +} + +/** + * @brief + * config the module protol + * @param[in] bl : 1--- T1 protocol + * 0--- T0 protocol + * @retval + */ +static inline void wm_sc_set_protocol(bool bl) +{ + tls_bitband_write(HR_UART2_LINE_CTRL, 8, bl); +} + +/** + * @brief + * get the module protol + * @retval + * 1--- T1 protocol + * 0--- T0 protocol + */ +static inline uint8_t wm_sc_get_protocol() +{ + return tls_bitband_read(HR_UART2_LINE_CTRL, 8); +} + +/** + * @brief + * smart card clock output enable or disable + * @param[in] bl : 0---enable; + * 1---disable; + * @retval + */ +static inline void wm_sc_clk_enable(bool bl) +{ + tls_bitband_write(HR_UART2_LINE_CTRL, 10, bl); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sdk/Include/Driver/wm_adc.h b/sdk/Include/Driver/wm_adc.h new file mode 100644 index 0000000..be1a68b --- /dev/null +++ b/sdk/Include/Driver/wm_adc.h @@ -0,0 +1,297 @@ +/** + * @file wm_adc.h + * + * @brief ADC Driver Module + * + * @author + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ + + +#ifndef WM_ADC_H +#define WM_ADC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wm_type_def.h" + +/** ADC MACRO */ +//ÿ´ÎÆô¶¯dmaÖ®ºó£¬ÐèÒªÒ»¶ÎÎȶ¨Ê±¼ä£¬ËùÒԲɼ¯µ½µÄÊý¾ÝÇ°ÃæµÄ12¸öbyte²»Îȶ¨£¬ÒªÉáÈ¥ +#define ADC_DEST_BUFFER_DMA (u32)0x20028000 +#define ADC_DEST_BUFFER_SIZE 65532 +#define SAMPLE_NUM_PER_CHANNEL 20 + + + +#define CONFIG_ADC_CAL_OFFSET_TEMP12 (1<<23) +#define CONFIG_ADC_G_CTRL12 (1<<22) +#define CONFIG_ADC_CMP_POL (1<<21) +#define CONFIG_ADC_REF_SEL (1<<20) +#define CONFIG_ADC_BUF_BYPASS (1<<19) +#define CONFIG_ADC_DMA_MASK (0xFF<<11) +#define CONFIG_ADC_EN_CAL (1<<10) +#define CONFIG_ADC_INT_CMP (1<<9) +#define CONFIG_ADC_INT (1<<8) +#define CONFIG_ADC_CMP_INT_MASK (1<<7) +#define CONFIG_ADC_INT_MASK (1<<6) +#define CONFIG_ADC_TEMP_ON (1<<5) +#define CONFIG_ADC_START (1<<4) +#define CONFIG_ADC_CHL_MASK (0xF) + +#define CONFIG_ADC_CHL_OFFSET (0x0E) +#define CONFIG_ADC_CHL_VOLT (0x0D) +#define CONFIG_ADC_CHL_TEMP (0x0C) + + + + +#define CONFIG_ADC_INPUT_CMP_VAL(n) ((n&0x3FFF)<<14) +#define CONFIG_ADC_VCM(n) (((u32)n&0x3F)<<26) +#define CONFIG_ADC_G_TEMP12(n) ((n&0x03)<<24) + + + +#define ADC_INT_TYPE_ADC 0 +#define ADC_INT_TYPE_DMA 1 +#define ADC_INT_TYPE_ADC_COMP 2 + +#define ADC_REFERENCE_EXTERNAL 0 //Íⲿ²Î¿¼ +#define ADC_REFERENCE_INTERNAL 1 //ÄÚ²¿²Î¿¼ + +typedef struct adc_st{ + u8 dmachannel; + void (*adc_cb)(u16 *buf, u16 len); + void (*adc_bigger_cb)(u16 *buf, u16 len); + void (*adc_dma_cb)(u16 *buf,u16 len); + u16 valuelen; /*dma ²ÉÑùÊý¾Ý³¤¶È*/ + u16 offset; +}ST_ADC; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup ADC_Driver_APIs ADC Driver APIs + * @brief ADC driver APIs + */ + +/** + * @addtogroup ADC_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to init ADC + * + * @param[in] ifusedma if use dma + * @param[in] dmachannel dma channel + * + * @return None + * + * @note If the requested dma channel is already used by other task, system will auto use other dma channel. + */ +void tls_adc_init(u8 ifusedma,u8 dmachannel); + + +/** + * @brief This function is used to register interrupt callback function + * + * @param[in] inttype interrupt type + * ADC_INT_TYPE_ADC adc interrupt,user get adc result from the callback function. + * ADC_INT_TYPE_DMA dma interrupt,dma transfer the adc result to the user's buffer. + * @param[in] callback interrupt callback function + * + * @return None + * + * @note None + */ +void tls_adc_irq_register(int inttype, void (*callback)(u16 *buf, u16 len)); + +/** + * @brief This function is used to clear the interrupt source + * + * @param[in] inttype interrupt type + * ADC_INT_TYPE_ADC adc interrupt,user get adc result from the callback function. + * ADC_INT_TYPE_DMA dma interrupt,dma transfer the adc result to the user's buffer. + * ADC_INT_TYPE_ADC_COMP adc compare with setting data + * + * @return None + * + * @note None + */ +void tls_adc_clear_irq(int inttype); + +/** + * @brief This function is used to start adc,use dma for transfer data + * + * @param[in] channel adc channel,from 0 to 3 is single input;4 and 5 is differential input + * + * @param[in] length byte data length,is an integer multiple of half word,need <= 0x500 + * + * @return None + * + * @note None + */ +void tls_adc_start_with_dma(int Channel, int Length); + +/** + * @brief This function is used to start adc,use cpu + * + * @param[in] channel adc channel,from 0 to 3 is single input;4 and 5 is differential input + * + * @return None + * + * @note None + */ +void tls_adc_start_with_cpu(int Channel); + +/** + * @brief This function is used to enable input buffer calibration + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_adc_enable_calibration_buffer_offset(void); + +/** + * @brief This function is used to sample voltage using cpu + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_adc_voltage_start_with_cpu(void); + +/** + * @brief This function is used to sample temperature using cpu + * + * @param[in] calenflag 1:enable calibration , 0: disable calibration + * + * @return None + * + * @note None + */ +void tls_adc_temp_offset_with_cpu(u8 calenflag); + +/** + * @brief This function is used to read adc result + * + * @param[in] None + * + * @return The value of adc result + * + * @note The 14th bit of the return value is the symbol bit + */ +u16 tls_read_adc_result(void); + +/** + * @brief This function is used to stop the adc + * + * @param[in] ifusedma 1:use dma, 0:not use dma + * + * @return None + * + * @note None + */ +void tls_adc_stop(int ifusedma); + +/** + * @brief This function is used to config adc compare register + * + * @param[in] cmp_data compare data + * + * @param[in] cmp_pol compare polarity + * + * @return None + * + * @note None + */ +void tls_adc_config_cmp_reg(int cmp_data, int cmp_pol); + +/** + * @brief This function is used to set adc reference source + * + * @param[in] ref + * ADC_REFERENCE_EXTERNAL + * ADC_REFERENCE_INTERNAL + * + * @return None + * + * @note None + */ +void tls_adc_reference_sel(int ref); + +/** + * @brief This function is used to set clock division + * + * @param[in] div + * + * @return None + * + * @note None + */ +void tls_adc_set_clk(int div); + +void signedToUnsignedData(u16 *adcValue, u16 *offset); + +/** + * @brief This function is used to set buffer bypass + * + * @param[in] isset 1: buffer bypass, 0: buffer work + * + * @return None + * + * @note None + */ +void tls_adc_buffer_bypass_set(u8 isset); + +/** + * @brief This function is used to start compare + * + * @param[in] Channel sample channel + * + * @param[in] cmp_data compare data + * + * @param[in] cmp_pol compare polarity + * + * @return None + * + * @note None + */ + void tls_adc_cmp_start(int Channel, int cmp_data, int cmp_pol); + +u16 adc_get_offset(void); +u32 adc_get_interTemp(void); +u16 adc_get_inputVolt(u8 channel); +u16 adc_get_interVolt(void); +u32 adc_temp(void); + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* end of WM_ADC_H */ + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ diff --git a/sdk/Include/Driver/wm_cpu.h b/sdk/Include/Driver/wm_cpu.h new file mode 100644 index 0000000..0c2c76e --- /dev/null +++ b/sdk/Include/Driver/wm_cpu.h @@ -0,0 +1,94 @@ +/** + * @file wm_cpu.h + * + * @brief cpu driver module + * + * @author dave + * + * @copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_CPU_H +#define WM_CPU_H +#ifdef __cplusplus +extern "C" { +#endif + +/** cpu clock: 80Mhz */ +#define CPU_CLK_80M 0 +/** cpu clock: 40Mhz */ +#define CPU_CLK_40M 1 +#define CPU_CLK_16M 2 + +#define W600_PLL_CLK_MHZ 160 + + + +#define UNIT_MHZ (1000000) + + +typedef struct{ + u32 apbclk; + u32 cpuclk; + u32 wlanclk; +}tls_sys_clk; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup CPU_CLOCK_Driver_APIs CPU_CLOCK Driver APIs + * @brief CPU_CLOCK driver APIs + */ + +/** + * @addtogroup CPU_CLOCK_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to set cpu clock + * + * @param[in] clk select cpu clock + clk == CPU_CLK_80M 80M + clk == CPU_CLK_40M 40M + * + * @return None + * + * @note None + */ +void tls_sys_clk_set(u32 clk); + + +/** + * @brief This function is used to get cpu clock + * + * @param[out] *sysclk point to the addr for system clk output + * + * @return None + * + * @note None + */ +void tls_sys_clk_get(tls_sys_clk *sysclk); + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* end of WM_CPU_H */ + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ + diff --git a/sdk/Include/Driver/wm_dma.h b/sdk/Include/Driver/wm_dma.h new file mode 100644 index 0000000..777811f --- /dev/null +++ b/sdk/Include/Driver/wm_dma.h @@ -0,0 +1,248 @@ +/** + * @file wm_dma.h + * + * @brief DMA Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM_DMA_H_ +#define __WM_DMA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define TLS_DMA_SEL_UART_RX 0 +#define TLS_DMA_SEL_UART_TX 1 +#define TLS_DMA_SEL_PWM_CAP0 2 +#define TLS_DMA_SEL_PWM_CAP1 3 +#define TLS_DMA_SEL_LSSPI_RX 4 +#define TLS_DMA_SEL_LSSPI_TX 5 +#define TLS_DMA_SEL_SDADC_CH0 6 +#define TLS_DMA_SEL_SDADC_CH1 7 +#define TLS_DMA_SEL_SDADC_CH2 8 +#define TLS_DMA_SEL_SDADC_CH3 9 +#define TLS_DMA_SEL_SDADC_CH4 10 +#define TLS_DMA_SEL_SDADC_CH5 11 +#define TLS_DMA_SEL_SDADC_CH6 12 +#define TLS_DMA_SEL_SDADC_CH7 13 +#define TLS_DMA_SEL_I2S_RX 14 +#define TLS_DMA_SEL_I2S_TX 15 + + +#define TLS_DMA_FLAGS_HARD_MODE (1 << 0) +#define TLS_DMA_FLAGS_CHAIN_MODE (1 << 1) +#define TLS_DMA_FLAGS_CHANNEL_SEL(n) ((n) << 2) +#define TLS_DMA_FLAGS_CHAIN_LINK_EN (1 << 6) +#define TLS_DMA_FLAGS_CHANNEL_VALID (1 << 7) + + +#define TLS_DMA_DESC_VALID (1U << 31) +#define TLS_DMA_DESC_CTRL_SRC_ADD_INC (1 << 0) +#define TLS_DMA_DESC_CTRL_DEST_ADD_INC (1 << 2) +#define TLS_DMA_DESC_CTRL_DATA_SIZE_BYTE (0 << 4) +#define TLS_DMA_DESC_CTRL_DATA_SIZE_SHORT (1 << 4) +#define TLS_DMA_DESC_CTRL_DATA_SIZE_WORD (2 << 4) +#define TLS_DMA_DESC_CTRL_BURST_SIZE1 (0 << 6) +#define TLS_DMA_DESC_CTRL_BURST_SIZE4 (1 << 6) +#define TLS_DMA_DESC_CTRL_TOTAL_BYTES(n) ((n) << 7) + + +/* dma interrupt flags */ +#define TLS_DMA_IRQ_BURST_DONE (1 << 0) +#define TLS_DMA_IRQ_TRANSFER_DONE (1 << 1) +#define TLS_DMA_IRQ_BOTH_DONE (TLS_DMA_IRQ_BURST_DONE | TLS_DMA_IRQ_TRANSFER_DONE) + +struct tls_dma_descriptor { + unsigned int valid; + unsigned int dma_ctrl; + unsigned int src_addr; + unsigned int dest_addr; + struct tls_dma_descriptor *next; /**< next dms descriptor */ +}; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup DMA_Driver_APIs DMA Driver APIs + * @brief DMA driver APIs + */ + +/** + * @addtogroup DMA_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to clear dma interrupt flag. + * + * @param[in] ch Channel no.[0~7] + * @param[in] flags Flags setted to TLS_DMA_IRQ_BURST_DONE, TLS_DMA_IRQ_TRANSFER_DONE, TLS_DMA_IRQ_BOTH_DONE. + * + * @return None + * + * @note None + */ +void tls_dma_irq_clr(unsigned char ch, unsigned char flags); + + +/** + * @brief This function is used to register dma interrupt callback function. + * + * @param[in] ch Channel no.[0~7] + * @param[in] callback is the dma interrupt call back function. + * @param[in] arg the param of the callback function. + * @param[in] flags Flags setted to TLS_DMA_IRQ_BURST_DONE, TLS_DMA_IRQ_TRANSFER_DONE, TLS_DMA_IRQ_BOTH_DONE. + * + * @return None + * + * @note None + */ +void tls_dma_irq_register(unsigned char ch, void (*callback)(void *p), void *arg, unsigned char flags); + + +/** + * @brief This function is used to Wait until DMA operation completes + * + * @param[in] ch DMA channel no.[0~7] + * + * @return 0 completed + * @return -1 failed + * + * @note None + */ +int tls_dma_wait_complt(unsigned char ch); + + +/** + * @brief This function is used to Start the DMA controller by Wrap + * + * @param[in] autoReload Does restart when current transfer complete? + * @param[in] ch Channel no.[0~7] + * @param[in] pDmaDesc Pointer to DMA channel descriptor structure. + * + * @retval Always STATUS_SUCCESS. + * + * @note + * DMA Descriptor: + * +--------------------------------------------------------------+ + * |Vld[31] | RSV | + * +--------------------------------------------------------------+ + * | RSV | Dma_Ctrl[16:0] | + * +--------------------------------------------------------------+ + * | Src_Addr[31:0] | + * +--------------------------------------------------------------+ + * | Dest_Addr[31:0] | + * +--------------------------------------------------------------+ + * | Next_Desc_Add[31:0] | + * +--------------------------------------------------------------+ + */ +unsigned char tls_dma_start_by_wrap(unsigned char ch, struct tls_dma_descriptor *dma_desc, + unsigned char auto_reload, unsigned short src_zize, + unsigned short dest_zize); + + +/** + * @brief This function is used for Starting the DMA controller + * + * @param[in] ch Channel no.[0~7] + * @param[in] pDmaDesc Pointer to DMA channel descriptor structure. + * @param[in] autoReload Does restart when current transfer complete? + * + * @retval 0 success + * @retval 1 failed + * + * @note + * DMA Descriptor: + * +--------------------------------------------------------------+ + * |Vld[31] | RSV | + * +--------------------------------------------------------------+ + * | RSV | Dma_Ctrl[16:0] | + * +--------------------------------------------------------------+ + * | Src_Addr[31:0] | + * +--------------------------------------------------------------+ + * | Dest_Addr[31:0] | + * +--------------------------------------------------------------+ + * | Next_Desc_Add[31:0] | + * +--------------------------------------------------------------+ + */ +unsigned char tls_dma_start(unsigned char ch, struct tls_dma_descriptor *dma_desc, + unsigned char auto_reload); + +/** + * @brief This function is used to To stop current DMA channel transfer + * + * @param[in] ch channel no. to be stopped + * + * @retval 0 success + * @retval 1 failed + * + * @note If channel stop, DMA_CHNL_CTRL_CHNL_ON bit in DMA_CHNLCTRL_REG is cleared. + */ +unsigned char tls_dma_stop(unsigned char ch); + + +/** + * @brief This function is used to Request a free dma channel. + * If ch is 0, the function will select a random free channel, + * else return the selected channel no. if free. + * + * @param[in] ch channel no. + * @param[in] flags flags setted to selected channel + * + * @return Channel no. that is free now + * + * @note Channel no. that is free now + */ +unsigned char tls_dma_request(unsigned char ch, unsigned char flags); + + +/** + * @brief This function is used to Free the DMA channel when not use + * + * @param[in] ch channel no. that is ready to free + * + * @return None + * + * @note None + */ +void tls_dma_free(unsigned char ch); + + +/** + * @brief This function is used to Initialize DMA Control + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_dma_init(void); + + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* end of __WM_DMA_H_ */ + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ + diff --git a/sdk/Include/Driver/wm_efuse.h b/sdk/Include/Driver/wm_efuse.h new file mode 100644 index 0000000..7c9e791 --- /dev/null +++ b/sdk/Include/Driver/wm_efuse.h @@ -0,0 +1,288 @@ +/** + * @file wm_efuse.h + * + * @brief virtual efuse Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_EFUSE_H +#define WM_EFUSE_H + +#define TLS_EFUSE_STATUS_OK (0) +#define TLS_EFUSE_STATUS_EINVALID (1) +#define TLS_EFUSE_STATUS_EIO (2) +#define TLS_EFUSE_STATUS_ERR (-1) + + +enum { + CMD_MAC = 0x01, + CMD_TX_DC, + CMD_RX_DC, + CMD_TX_IQ_GAIN, + CMD_RX_IQ_GAIN, + CMD_TX_IQ_PHASE, + CMD_RX_IQ_PHASE, + CMD_TX_GAIN, + CMD_ALL, +}; + +#define VCG_ADDR (FT_MAGICNUM_ADDR + sizeof(FT_PARAM_ST)+4) +#define VCG_LEN (4) +//#define TX_GAIN_NEW_ADDR (VCG_ADDR+VCG_LEN) +#define TX_GAIN_LEN (28*3) + +/** +* @brief This function is used to init ft param. +* +* @param[in] None +* +* @retval TRUE init success +* @retval FALSE init failed +*/ +int tls_ft_param_init(void); + + +/** +* @brief This function is used to write ft_param. +* +* @param[in] opnum ft cmd +* @param[in] data data pointer +* @param[in] len the length written into "data" +* +* @retval 0 set success +* @retval -1 set failed +*/ +int tls_ft_param_set(unsigned int opnum, void *data, unsigned int len); + +/** +* @brief This function is used to read ft_param. +* +* @param[in] opnum ft cmd +* @param[in] data data pointer +* @param[in] len len to read data +* +* @retval 0 get success +* @retval -1 get failed +*/ +int tls_ft_param_get(unsigned int opnum, void *data, unsigned int rdlen); + + +/** +* @brief This function is used to get mac addr +* +* @param[in] mac mac addr,6 byte +* +* @retval 0 get success +* @retval -1 get failed +*/ +int tls_get_mac_addr(u8 *mac); + +/** +* @brief This function is used to set mac addr +* +* @param[in] mac mac addr,6 byte +* +* @retval 0 set success +* @retval -1 set failed +*/ +int tls_set_mac_addr(u8 *mac); + +/** +* @brief This function is used to get tx gain +* +* @param[in] txgain tx gain,12 byte +* +* @retval 0 get success +* @retval -1 get failed +*/ +int tls_get_tx_gain(u8 *txgain); + +/** +* @brief This function is used to set tx gain +* +* @param[in] txgain tx gain,12 byte +* +* @retval 0 set success +* @retval -1 set failed +*/ +int tls_set_tx_gain(u8 *txgain); + +/** +* @brief This function is used to get tx lod +* +* @param[in] txlo tx lod +* +* @retval 0 get success +* @retval -1 get failed +*/ +int tls_get_tx_lo(u8 *txlo); + +/** +* @brief This function is used to set tx lod +* +* @param[in] txlo tx lod +* +* @retval 0 set success +* @retval -1 set failed +*/ +int tls_set_tx_lo(u8 *txlo); + +/** +* @brief This function is used to get tx iq gain +* +* @param[in] txGain +* +* @retval 0 get success +* @retval -1 get failed +*/ +int tls_get_tx_iq_gain(u8 *txGain); + +/** +* @brief This function is used to set tx iq gain +* +* @param[in] txGain +* +* @retval 0 set success +* @retval -1 set failed +*/ +int tls_set_tx_iq_gain(u8 *txGain); + +/** +* @brief This function is used to get rx iq gain +* +* @param[in] rxGain +* +* @retval 0 get success +* @retval -1 get failed +*/ +int tls_get_rx_iq_gain(u8 *rxGain); + +/** +* @brief This function is used to set rx iq gain +* +* @param[in] rxGain +* +* @retval 0 set success +* @retval -1 set failed +*/ +int tls_set_rx_iq_gain(u8 *rxGain); + +/** +* @brief This function is used to get tx iq phase +* +* @param[in] txPhase +* +* @retval 0 get success +* @retval -1 get failed +*/ +int tls_get_tx_iq_phase(u8 *txPhase); + +/** +* @brief This function is used to set tx iq phase +* +* @param[in] txPhase +* +* @retval 0 set success +* @retval -1 set failed +*/ +int tls_set_tx_iq_phase(u8 *txPhase); + +/** +* @brief This function is used to get rx iq phase +* +* @param[in] rxPhase +* +* @retval 0 get success +* @retval -1 get failed +*/ +int tls_get_rx_iq_phase(u8 *rxPhase); + +/** +* @brief This function is used to set rx iq phase +* +* @param[in] rxPhase +* +* @retval 0 set success +* @retval -1 set failed +*/ +int tls_set_rx_iq_phase(u8 *rxPhase); + +/** +* @brief This function is used to set/get freq err +* +* @param[in] freqerr +* @param[in] flag 1-set 0-get +*/ +int tls_freq_err_op(u8 *freqerr, u8 flag); + +/** +* @brief This function is used to set/get vcg ctrl +* +* @param[in] vcg +* @param[in] flag 1-set 0-get +* +*/ +int tls_rf_vcg_ctrl_op(u8 *vcg, u8 flag); + +/** +* @brief This function is used to get chip ID +* +* @param[out] chip_id +* +*/ +int tls_get_chipid(u8 chip_id[16]); + +/** +* @brief This function is used for a delay of several seconds +* +* @param[in] seconds +* +* @retval 0 +*/ +unsigned int tls_sleep(unsigned int seconds); + +/** +* @brief This function is used for a delay of several milliseconds +* +* @param[in] msec +* +* @retval 0 +*/ +int tls_msleep(unsigned int msec); +/** +* @brief This function is used for a delay of several microseconds +* +* @param[in] usec +* +* @retval 0 +*/ +int tls_usleep(unsigned int usec); + +/********************************************************************************************************** +* Description: This function is used to get uuid +* +* Arguments : *uuid +* +* Returns : 0 get success +* -1 get failed +**********************************************************************************************************/ +int tls_get_uuid(u8 *uuid, u8 len); + + +/********************************************************************************************************** +* Description: This function is used to get uuid +* +* Arguments : *uuid :data to be written to otp area +* len : data len to be written +* iflock : if lock the otp area,1-lock, once lock,can not unlock!!! +* +* Returns : 0 get success +* -1 get failed +**********************************************************************************************************/ +int tls_set_uuid(u8 *uuid, u8 len, u8 iflock); + + +#endif /* WM_EFUSE_H */ + diff --git a/sdk/Include/Driver/wm_flash.h b/sdk/Include/Driver/wm_flash.h new file mode 100644 index 0000000..0ae13ed --- /dev/null +++ b/sdk/Include/Driver/wm_flash.h @@ -0,0 +1,203 @@ +/** + * @file wm_flash.h + * + * @brief flash Driver module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_FLASH_H +#define WM_FLASH_H + +#include "wm_type_def.h" +#include "wm_osal.h" + +#define TLS_FLS_STATUS_OK (0) +#define TLS_FLS_STATUS_EINVAL (1) +#define TLS_FLS_STATUS_EBUSY (2) +#define TLS_FLS_STATUS_EPERM (3) +#define TLS_FLS_STATUS_ENOSUPPORT (4) +#define TLS_FLS_STATUS_EEXIST (5) +#define TLS_FLS_STATUS_ENOMEM (6) +#define TLS_FLS_STATUS_EOVERFLOW (7) +#define TLS_FLS_STATUS_ENODEV (8) +#define TLS_FLS_STATUS_EDEV (9) +#define TLS_FLS_STATUS_EIO (10) +#define TLS_FLS_STATUS_ENODRV (11) + +#define TLS_FLS_PARAM_TYPE_ID (0) +#define TLS_FLS_PARAM_TYPE_SIZE (1) +#define TLS_FLS_PARAM_TYPE_PAGE_SIZE (2) +#define TLS_FLS_PARAM_TYPE_PROG_SIZE (3) +#define TLS_FLS_PARAM_TYPE_SECTOR_SIZE (4) + +#define TLS_FLS_FLAG_UNDER_PROTECT (1<<0) +#define TLS_FLS_FLAG_FAST_READ (1<<1) +#define TLS_FLS_FLAG_AAAI (1<<2) + +#define FLS_CMD_READ_DEV_ID (0x9F) // read device id //(0x9f) + +/** + * @struct fls_list list + */ +struct fls_list +{ + struct fls_list *next; + struct fls_list *prev; +}; + + +/** + * @struct tls_fls_drv flash driver + */ +struct tls_fls_drv +{ + struct fls_list drv_list; + u32 id; + u32 total_size; + u32 page_size; + u32 program_size; + u32 sector_size; + u32 clock; + u8 mode; + u8 cs_active; + u8 flags; + int (*read) (u32, u8 *, u32); + int (*fast_read) (u32, u8 *, u32); + int (*page_write) (u32, u8 *); + int (*erase) (u32); + int (*chip_erase) (void); + int (*probe)(u32 id); + void (*remove) (void); +}; + +/** + * @struct tls_fls flash + */ +struct tls_fls +{ + struct fls_list fls_drvs; + struct tls_fls_drv *current_drv; + tls_os_sem_t *fls_lock; +}; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup SPIFLASH_Driver_APIs SPIFLASH Driver APIs + * @brief SPIFLASH driver APIs + */ + +/** + * @addtogroup SPIFLASH_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to initial flash module structer. + * + * @param[in] None + * + * @retval TLS_FLS_STATUS_OK if init sucsess + * @retval TLS_FLS_STATUS_EBUSY if already inited + * @retval TLS_FLS_STATUS_ENOMEM if memory allocation failed + * + * @note None + */ +int tls_spifls_init(void); + +/** + * @brief This function is used to read the id of the flash. + * + * @param[in] id Buffer to hold the id that been read. + * + * @retval TLS_FLS_STATUS_OK if read sucsess + * @retval TLS_FLS_STATUS_EIO if read fail + * + * @note None + */ +int tls_spifls_read_id(u32 * id); + +/** + * @brief This function is used to read data from the flash. + * + * @param[in] addr Specifies the starting address to read from + * @param[in] buf Pointer to a byte array that is to be written. + * @param[in] len length to read. + * + * @retval TLS_FLS_STATUS_OK if read success + * @retval TLS_FLS_STATUS_EIO if read fail + * @retval TLS_FLS_STATUS_EPERM if flash driver module not beed installed + * @retval TLS_FLS_STATUS_ENODRV if the current spi flash driver not installed + * @retval TLS_FLS_STATUS_EINVAL if arguments is invalid + * + * @note None + */ +int tls_spifls_read(u32 addr, u8 * buf, u32 len); + + +/** + * @brief This function is used to write data into the flash. + * + * @param[in] addr Specifies the starting address to write to. + * @param[in] buf Pointer to a byte array that holds the data to be written. + * @param[in] len length to write. + * + * @retval TLS_FLS_STATUS_OK if write flash success + * @retval TLS_FLS_STATUS_EPERM if flash struct point is null + * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed + * @retval TLS_FLS_STATUS_EINVAL if argument is invalid + * @retval TLS_FLS_STATUS_EIO if io error + * @retval TLS_FLS_STATUS_ENOMEM if memory allocation fail + * + * @note None + */ +int tls_spifls_write(u32 addr, u8 * buf, u32 len); + +/** + * @brief This function is used to erase one sector of the flash. + * + * @param[in] sector Specifies the sector number to be erased.. + * + * @retval TLS_FLS_STATUS_OK if write flash success + * @retval TLS_FLS_STATUS_EPERM if flash struct point is null + * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed + * @retval TLS_FLS_STATUS_EINVAL if argument is invalid + * @retval TLS_FLS_STATUS_EIO if io error + * + * @note None + */ +int tls_spifls_erase(u32 sector); + +/** + * @brief This function is used to erase the flash. + * + * @param[in] None + * + * @retval TLS_FLS_STATUS_OK if init sucsess + * @retval TLS_FLS_STATUS_EBUSY already inited + * @retval TLS_FLS_STATUS_ENOMEM memory error + * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed + * + * @note None + */ +int tls_spifls_chip_erase(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_FLASH_H */ diff --git a/sdk/Include/Driver/wm_flash_map.h b/sdk/Include/Driver/wm_flash_map.h new file mode 100644 index 0000000..6c5ff72 --- /dev/null +++ b/sdk/Include/Driver/wm_flash_map.h @@ -0,0 +1,84 @@ +/** + * @file wm_flash_map.h + * + * @brief flash zone map + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM_FLASH_MAP_H__ +#define __WM_FLASH_MAP_H__ + +/**FLASH MAP**/ + +/**Flash Base Address */ +#define FLASH_BASE_ADDR (0x08000000UL) + +#define FLASH_1M_END_ADDR (0x08100000UL) + +/**Run-time image header area*/ +#define CODE_RUN_HEADER_ADDR (0x08010000UL) +#define CODE_RUN_HEADER_AREA_LEN (0x100) + +/**Run-time image area*/ +#define CODE_RUN_START_ADDR (0x08010100UL) + +/******************************************************** +******************LAYOUT For 1M Flash********************** + *Reserved 0x8000000-0x8010000 64Kbyte + *Code 0x8010100-0x808FFFF 512Kbyte - 256 byte + *Update 0x8090000-0x80EFFFF 384Kbyte + *User 0x80F0000-0x80FBFFF 48Kbyte + *Parameter 0x80FC000-0x80FFFFF 16Kbyte +********************************************************* + +**************LAYOUT For 2M Flash as 1M LAYOUT************** + *Reserved 0x8000000-0x8010000 64Kbyte + *Code 0x8010100-0x80FFFFF 896Kbyte -256byte + *Old User Area 0x80F0000-0x80FBFFF 48Kbyte + *Parameter 0x80FC000-0x80FFFFF 16Kbyte + *Update 0x8100000-0x81AFFFF 704Kbyte + *EXT User 0x81B0000-0x81FFFFF 320Kbyte +********************************************************* + +******************LAYOUT For 2M Flash********************* + *Reserved 0x8000000-0x8010000 64Kbyte + *Code 0x8010100-0x80FFFFF 960Kbyte -256byte + *Update 0x8100000-0x81BFFFF 768Kbyte + *User 0x81C0000-0x81FBFFF 240Kbyte + *Parameter 0x81FC000-0x81FFFFF 16Kbyte +********************************************************* +*********************************************************/ + +/**Run-time image area size*/ +extern unsigned int CODE_RUN_AREA_LEN; + +/**Area can be used by User in 1M position*/ +extern unsigned int USER_ADDR_START; +extern unsigned int TLS_FLASH_PARAM_DEFAULT; +extern unsigned int USER_AREA_LEN; +extern unsigned int USER_ADDR_END; + + +/**Upgrade image header area & System parameter area */ +extern unsigned int CODE_UPD_HEADER_ADDR; +extern unsigned int TLS_FLASH_PARAM1_ADDR; +extern unsigned int TLS_FLASH_PARAM2_ADDR; +extern unsigned int TLS_FLASH_PARAM_RESTORE_ADDR; + +/**Upgrade image area*/ +extern unsigned int CODE_UPD_START_ADDR; +extern unsigned int CODE_UPD_AREA_LEN; + +/**Area can be used by User in 2M position*/ +extern unsigned int EX_USER_ADDR_START; +extern unsigned int EX_USER_AREA_LEN; +extern unsigned int EX_USER_ADDR_END; + +extern unsigned int TLS_FLASH_END_ADDR; + +#define SIGNATURE_WORD 0xa0ffff9f + +#endif /*__WM_CONFIG_H__*/ + diff --git a/sdk/Include/Driver/wm_fls_gd25qxx.h b/sdk/Include/Driver/wm_fls_gd25qxx.h new file mode 100644 index 0000000..5b99805 --- /dev/null +++ b/sdk/Include/Driver/wm_fls_gd25qxx.h @@ -0,0 +1,60 @@ +/** + * @file wm_fls_gd25qxx.h + * + * @brief wm gd25qxx flash driver + * + * @author dave + * + * @copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef TLS_EXSPIFLS_H +#define TLS_EXSPIFLS_H + +#define SPI_SCLK (10000000) /** 10MHz. */ +#define FLASH_TOTAL_SIZE (1024*1024) +#define PAGE_SIZE 256 +#define PROGRAM_SIZE 256 +#define PAGE_ADDR_OFFSET 8 +#define SECTOR_SIZE 4096 + + +/** + * command code define. + */ +#define EXSPIFLASH_WRITE_ENABLE (0x06) /** Global write enable */ +#define EXSPIFLASH_WRITE_DISABLE (0x04) /** Global write disable */ +#define EXSPIFLASH_READ_SR1 (0x05) /** Read flash status register s0~s7 */ +#define EXSPIFLASH_READ_SR2 (0x35) /** Read flash status register s8~s15 */ +#define EXSPIFLASH_WRITE_SR (0x01) /** Write flash status register s0~s15 */ +#define EXSPIFLASH_PAGE_PROGRAM (0x02) /** program one page */ +#define EXSPIFLASH_DATA_READ (0x03) /** read data from specified address */ +#define EXSPIFLASH_DATA_FAST_READ (0x0b) /** fast read data from specified address */ +#define EXSPIFLASH_SECTOR_ERASE (0x20) /** Sector erase */ +#define EXSPIFLASH_BLOCK32_ERASE (0x52) /** 32KB Block erase(128 pages) */ +#define EXSPIFLASH_BLOCK64_ERASE (0xd8) /** 64kb Block erase(256 pages) */ +#define EXSPIFLASH_CHIP_ERASE (0xc7) /** Chip erase */ +#define EXSPIFLASH_FLASH_DEVICEID (0x90) /** Read flash manufacturer/device ID */ +#define EXSPIFLASH_FLASH_ID (0x9f) /** Read flash ID */ + + +#define FLASH_STATUS_BUSY (1 << 0) +#define FLASH_STATUS_WEL (1 << 1) + +/** + * @brief This function is used to install gd25qxx driver. + * + * @param[in] None + * + * @retval TLS_FLS_STATUS_OK if write flash success + * @retval TLS_FLS_STATUS_EPERM if flash struct point is null + * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed + * @retval TLS_FLS_STATUS_EINVAL if argument is invalid + * @retval TLS_FLS_STATUS_EIO if io error + * @retval TLS_FLS_STATUS_EEXIST if driver is already existed + * + * @note None + */ +int tls_spifls_drv_install(void); + +#endif /* TLS_FLS_GD25QXX_H */ diff --git a/sdk/Include/Driver/wm_gpio.h b/sdk/Include/Driver/wm_gpio.h new file mode 100644 index 0000000..b859272 --- /dev/null +++ b/sdk/Include/Driver/wm_gpio.h @@ -0,0 +1,190 @@ +/** + * @file wm_gpio.h + * + * @brief GPIO Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_GPIO_H +#define WM_GPIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wm_type_def.h" +#include "wm_io.h" + +/** gpio interrupte callback function */ +typedef void (*tls_gpio_irq_callback)(void *arg); + +/** Indicating gpio direction */ +enum tls_gpio_dir { + WM_GPIO_DIR_OUTPUT, /**< output */ + WM_GPIO_DIR_INPUT /**< input */ +}; + +/** Indicating gpio attribute */ +enum tls_gpio_attr { + WM_GPIO_ATTR_FLOATING, /**< floating status */ + WM_GPIO_ATTR_PULLHIGH, /**< pull high */ + WM_GPIO_ATTR_PULLLOW /**< pull low */ +}; + +/** Indicating gpio interrupt trigger type */ +enum tls_gpio_irq_trig { + WM_GPIO_IRQ_TRIG_RISING_EDGE, /**< rising edge arises the interrupt */ + WM_GPIO_IRQ_TRIG_FALLING_EDGE, /**< falling edge arises the interrupt */ + WM_GPIO_IRQ_TRIG_DOUBLE_EDGE, /**< both rising edge and falling edge arise the interrupt */ + WM_GPIO_IRQ_TRIG_HIGH_LEVEL, /**< high power level arises the interrupt */ + WM_GPIO_IRQ_TRIG_LOW_LEVEL /**< low power level arises the interrupt */ +}; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup GPIO_Driver_APIs GPIO Driver APIs + * @brief GPIO driver APIs + */ + +/** + * @addtogroup GPIO_Driver_APIs + * @{ + */ + + +/** + * @brief This function is used to config gpio function + * + * @param[in] gpio_pin gpio pin num + * @param[in] dir gpio direction + * @param[in] attr gpio attribute + * + * @return None + * + * @note + * All gpio's attribute can only be set as WM_GPIO_ATTR_FLOATING or WM_GPIO_ATTR_PULLHIGH on W600/W601. + */ +void tls_gpio_cfg(enum tls_io_name gpio_pin, enum tls_gpio_dir dir, enum tls_gpio_attr attr); + + +/** + * @brief This function is used to read gpio status + * + * @param[in] gpio_pin gpio pin num + * + * @retval 0 low level + * @retval 1 high level + * + * @note None + */ +u8 tls_gpio_read(enum tls_io_name gpio_pin); + + +/** + * @brief This function is used to modify gpio status + * + * @param[in] gpio_pin gpio pin num + * @param[in] value electrical level + * 0: low level + * 1: high level + * + * @return None + * + * @note None + */ +void tls_gpio_write(enum tls_io_name gpio_pin, u8 value); + + +/** + * @brief This function is used to config gpio interrupt + * + * @param[in] gpio_pin gpio pin num + * @param[in] mode interrupt trigger type + * + * @return None + * + * @note None + */ +void tls_gpio_irq_enable(enum tls_io_name gpio_pin, enum tls_gpio_irq_trig mode); + + +/** + * @brief This function is used to disable gpio interrupt + * + * @param[in] gpio_pin gpio pin num + * + * @return None + * + * @note None + */ +void tls_gpio_irq_disable(enum tls_io_name gpio_pin); + + +/** + * @brief This function is used to get gpio interrupt status + * + * @param[in] gpio_pin gpio pin num + * + * @retval 0 no interrupt happened + * @retval 1 interrupt happened + * + * @note None + */ +u8 tls_get_gpio_irq_status(enum tls_io_name gpio_pin); + + +/** + * @brief This function is used to clear gpio interrupt flag + * + * @param[in] gpio_pin gpio pin num + * + * @return None + * + * @note None + */ +void tls_clr_gpio_irq_status(enum tls_io_name gpio_pin); + + +/** + * @brief This function is used to register gpio interrupt + * + * @param[in] gpio_pin gpio pin num + * @param[in] callback the gpio interrupt call back function + * @param[in] arg parammeter for the callback + * + * @return None + * + * @note + * gpio callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_gpio_isr_register(enum tls_io_name gpio_pin, + tls_gpio_irq_callback callback, + void *arg); + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* end of WM_GPIO_H */ + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ diff --git a/sdk/Include/Driver/wm_gpio_afsel.h b/sdk/Include/Driver/wm_gpio_afsel.h new file mode 100644 index 0000000..96e46ab --- /dev/null +++ b/sdk/Include/Driver/wm_gpio_afsel.h @@ -0,0 +1,510 @@ +/** + * @file wm_gpio_afsel.h + * + * @brief GPIO Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_IO_MUX_H +#define WM_IO_MUX_H + +#ifdef __cplusplus +extern "C" { +#endif +#include "wm_gpio.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_osal.h" +#include "tls_common.h" + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup IOMUX_Driver_APIs IOMUX Driver APIs + * @brief IOMUX driver APIs + */ + +/** + * @addtogroup IOMUX_Driver_APIs + * @{ + */ + +/** + * @brief config the pins used for highspeed spi + * @param numsel: config highspeed spi pins multiplex relation,valid para 0,1 + * 0: hspi0 + * hspi_int PB14 + * hspi_cs PB15 + * hspi_ck PB16 + * hspi_di PB17 + * hspi_do PB18 + * 1: hspi1 + * hspi_int PB08 + * hspi_cs PB09 + * hspi_ck PB10 + * hspi_di PB11 + * hspi_do PB12 + * @return None + */ +void wm_hspi_gpio_config(uint8_t numsel); + +/** + * @brief config the pins used for spi ck + * @param io_name: config spi ck pins name + * WM_IO_PA_01 + * WM_IO_PB_16 + * WM_IO_PB_27 + * WM_IO_PA_11 + * + * @return None + */ +void wm_spi_ck_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for spi cs + * @param io_name: config spi cs pins name + * WM_IO_PA_02 + * WM_IO_PB_15 + * WM_IO_PB_00 + * WM_IO_PB_07 + * WM_IO_PA_12 + * + * @return None + */ +void wm_spi_cs_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for spi di + * @param io_name: config spi di pins name + * WM_IO_PA_03 + * WM_IO_PB_17 + * WM_IO_PB_01 + * WM_IO_PA_05 + * WM_IO_PA_10 + * + * @return None + */ +void wm_spi_di_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for spi do + * @param io_name: config spi do pins name + * WM_IO_PB_18 + * WM_IO_PB_02 + * WM_IO_PA_04 + * WM_IO_PA_09 + * WM_IO_PA_10 + * + * @return None + */ +void wm_spi_do_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for sdio ck dat0 dat1 dat2 dat3 + * @param numsel: config sdio ck dat0 dat1 dat2 dat3 pins multiplex relation,valid para 0 + * 0: + * sdio_ck PB08 + * sdio_dat0 PB09 + * sdio_dat1 PB10 + * sdio_dat2 PB11 + * sdio_dat3 PB12 + * + * @return None + */ +void wm_sdio_config(uint8_t numsel); + +/** + * @brief config the pins used for sdio cmd + * @param io_name: config sdio cmd pins name + * WM_IO_PA_06 + * WM_IO_PB_07 + * WM_IO_PB_13 + * + * @return None + */ +void wm_sdio_cmd_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart0 tx + * @param io_name: config uart0 tx pins name + * WM_IO_PA_04 + * WM_IO_PA_08 + * WM_IO_PB_07 + * WM_IO_PB_26 + * + * @return None + */ +void wm_uart0_tx_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart0 rx + * @param io_name: config uart0 rx pins name + * WM_IO_PA_05 + * WM_IO_PA_09 + * WM_IO_PB_06 + * WM_IO_PB_25 + * + * @return None + */ +void wm_uart0_rx_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart0 rts + * @param io_name: config uart0 rts pins name + * WM_IO_PA_13 + * WM_IO_PB_04 + * WM_IO_PB_24 + * + * @return None + */ +void wm_uart0_rts_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart0 cts + * @param io_name: config uart0 cts pins name + * WM_IO_PA_14 + * WM_IO_PB_05 + * WM_IO_PB_23 + * + * @return None + */ +void wm_uart0_cts_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart1 tx + * @param io_name: config uart1 tx pins name + * WM_IO_PB_12 + * WM_IO_PB_18 + * + * @return None + */ +void wm_uart1_tx_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart1 rx + * @param io_name: config uart1 rx pins name + * WM_IO_PB_11 + * WM_IO_PB_17 + * + * @return None + */ +void wm_uart1_rx_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart1 rts + * @param io_name: config uart1 rts pins name + * WM_IO_PB_10 + * + * @return None + */ +void wm_uart1_rts_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart1 cts + * @param io_name: config uart1 cts pins name + * WM_IO_PB_09 + * + * @return None + */ +void wm_uart1_cts_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart2 tx or 7816-io + * @param io_name: config uart2 tx or 7816-io pins name + * WM_IO_PA_01 + * WM_IO_PA_11 + * WM_IO_PB_02 + * WM_IO_PB_20 + * + * @return None + */ +void wm_uart2_tx_scio_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart2 rx + * @param io_name: config uart2 rx pins name + * WM_IO_PA_00 + * WM_IO_PA_10 + * WM_IO_PB_03 + * WM_IO_PB_19 + * WM_IO_PB_30 + * + * @return None + */ +void wm_uart2_rx_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart2 rts or 7816-clk + * @param io_name: config uart2 rts or 7816-clk pins name + * WM_IO_PA_02 + * WM_IO_PA_12 + * WM_IO_PB_01 + * WM_IO_PB_21 + * WM_IO_PB_06 + * + * @return None + */ +void wm_uart2_rts_scclk_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for uart2 cts + * @param io_name: config uart2 cts pins name + * WM_IO_PA_03 + * WM_IO_PB_28 + * WM_IO_PB_00 + * WM_IO_PB_22 + * + * @return None + */ +void wm_uart2_cts_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2s master ck + * @param io_name: config i2s master ck pins name + * WM_IO_PA_04 + * WM_IO_PA_08 + * WM_IO_PB_08 + * + * @return None + */ +void wm_i2s_m_ck_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2s master ws + * @param io_name: config i2s master ws pins name + * WM_IO_PA_13 + * WM_IO_PA_09 + * WM_IO_PB_10 + * + * @return None + */ +void wm_i2s_m_ws_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2s master do + * @param io_name: config i2s master do pins name + * WM_IO_PA_03 + * WM_IO_PA_07 + * WM_IO_PB_09 + * + * @return None + */ +void wm_i2s_m_do_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2s slave ck + * @param io_name: config i2s slave ck pins name + * WM_IO_PA_15 + * WM_IO_PA_11 + * WM_IO_PB_15 + * WM_IO_PB_04 + * + * @return None + */ +void wm_i2s_s_ck_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2s slave ws + * @param io_name: config i2s slave ws pins name + * WM_IO_PA_06 + * WM_IO_PA_12 + * WM_IO_PB_16 + * WM_IO_PB_05 + * + * @return None + */ +void wm_i2s_s_ws_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2s slave di + * @param io_name: config i2s slave di pins name + * WM_IO_PA_14 + * WM_IO_PA_10 + * WM_IO_PB_14 + * WM_IO_PB_03 + * + * @return None + */ +void wm_i2s_s_di_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2s mclk + * @param io_name: config i2s mclk pins name + * WM_IO_PA_02 + * WM_IO_PB_28 + * WM_IO_PB_31 + * + * @return None + */ +void wm_i2s_mclk_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2s extclk + * @param io_name: config i2s extclk pins name + * WM_IO_PA_05 + * + * @return None + */ +void wm_i2s_extclk_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2c scl + * @param io_name: config i2c scl pins name + * WM_IO_PA_06 + * WM_IO_PA_08 + * WM_IO_PB_13 + * WM_IO_PB_11 + * WM_IO_PB_21 + * + * @return None + */ +void wm_i2c_scl_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for i2c sda + * @param io_name: config i2c sda pins name + * WM_IO_PA_15 + * WM_IO_PA_07 + * WM_IO_PB_14 + * WM_IO_PB_12 + * WM_IO_PB_22 + * + * @return None + */ +void wm_i2c_sda_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for pwm1 + * @param io_name: config pwm1 pins name + * WM_IO_PA_00 + * WM_IO_PB_18 + * WM_IO_PB_05 + * WM_IO_PA_05 + * WM_IO_PB_19 + * WM_IO_PB_30 + * + * @return None + */ +void wm_pwm1_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for pwm2 + * @param io_name: config pwm2 pins name + * WM_IO_PA_01 + * WM_IO_PB_17 + * WM_IO_PB_04 + * WM_IO_PA_07 + * WM_IO_PB_13 + * WM_IO_PB_20 + * + * @return None + */ +void wm_pwm2_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for pwm3 + * @param io_name: config pwm3 pins name + * WM_IO_PA_02 + * WM_IO_PB_16 + * WM_IO_PB_03 + * WM_IO_PA_08 + * WM_IO_PB_21 + * + * @return None + */ +void wm_pwm3_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for pwm4 + * @param io_name: config pwm4 pins name + * WM_IO_PA_03 + * WM_IO_PB_15 + * WM_IO_PB_02 + * WM_IO_PA_09 + * WM_IO_PB_22 + * WM_IO_PB_06 + * + * @return None + */ +void wm_pwm4_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for pwm5 + * @param io_name: config pwm5 pins name + * WM_IO_PA_04 + * WM_IO_PB_14 + * WM_IO_PB_01 + * WM_IO_PA_10 + * WM_IO_PB_23 + * WM_IO_PB_08 + * + * @return None + */ +void wm_pwm5_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for pwm break + * @param io_name: config pwm break pins name + * WM_IO_PB_24 + * WM_IO_PB_29 + * WM_IO_PB_00 + * WM_IO_PA_11 + * + * @return None + */ +void wm_pwmbrk_config(enum tls_io_name io_name); + +/** + * @brief config the pins used for swd + * @param enable: enable or disable chip swd function + * 1: enable + * 0: disable + * + * @return None + */ +void wm_swd_config(bool enable); + +/** + * @brief config the pins used for adc + * @param Channel: the channel that shall be used + * 0~07: single-ended input + * 8~11: differential input + * + * @return None + */ +void wm_adc_config(u8 Channel); + +/** + * @brief disable all the gpio af + * + * @return None + * + * @note This function must call before anyothers for configure + * gpio Alternate functions + */ +void wm_gpio_af_disable(void); +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* end of WM_IO_MUX_H */ + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ + diff --git a/sdk/Include/Driver/wm_hostspi.h b/sdk/Include/Driver/wm_hostspi.h new file mode 100644 index 0000000..b3aeb49 --- /dev/null +++ b/sdk/Include/Driver/wm_hostspi.h @@ -0,0 +1,299 @@ +/** + * @file wm_hostspi.h + * + * @brief host spi Driver Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_HOST_SPI_H +#define WM_HOST_SPI_H + +#include "wm_type_def.h" +#include "list.h" +#include "wm_osal.h" +#include "wm_ram_config.h" + + +#define SPI_USE_DMA + +#define SPI_TX_DMA_MASK 0x01 +#define SPI_RX_DMA_MASK 0x02 + +#define SPI_DMA_CMD_ADDR ((u32)(MASTER_SPI_DMA_ADDR)) +#define SPI_DMA_CMD_MAX_SIZE (0x20) + +#define SPI_DMA_BUF_ADDR ((u32)(SPI_DMA_CMD_ADDR + SPI_DMA_CMD_MAX_SIZE)) +// #define SPI_DMA_BUF_MAX_SIZE 8160 +#define SPI_DMA_BUF_MAX_SIZE (1024 - 32) + + +// #define SPI_DMA_MAX_TRANS_SIZE 4092 +#define SPI_DMA_MAX_TRANS_SIZE 512 + +/** + * error code. + */ +#define TLS_SPI_STATUS_OK (0) +#define TLS_SPI_STATUS_EINVAL (-1) +#define TLS_SPI_STATUS_ENOMEM (-2) +#define TLS_SPI_STATUS_EBUSY (-3) +#define TLS_SPI_STATUS_ESHUTDOWN (-4) +#define TLS_SPI_STATUS_EPERM (-5) +#define TLS_SPI_STATUS_ECLKNOSUPPORT (-6) +#define TLS_SPI_STATUS_EMODENOSUPPORT (-7) + +#define SPI_MASTER_FIFO_SIZE (32) + +/** + * the SPI master controller's configuration data. + */ + /** configuration data. */ +#define SPI_CPHA (0x01) /** clock phase. */ +#define SPI_CPOL (0x02) /** clock polarity. */ +#define TLS_SPI_MODE_0 (0|0) /** motorola mode. */ +#define TLS_SPI_MODE_1 (0|SPI_CPHA) +#define TLS_SPI_MODE_2 (SPI_CPOL|0) +#define TLS_SPI_MODE_3 (SPI_CPOL|SPI_CPHA) +#define TLS_SPI_CS_LOW 0x00 /** chipselect active low. */ +#define TLS_SPI_CS_HIGH 0x01 /** chipselect active high. */ +#define TLS_SPI_FCLK_MIN (1000) /** minimum work clock rate(Hz). */ +#define TLS_SPI_FCLK_MAX (APB_CLK/2) /** maximum work clock rate(Hz). */ + + +/** default configuration data. */ +#define SPI_DEFAULT_SPEED (2000000) /** default clock rate is 2MHz. */ +#define SPI_DEFAULT_MODE (TLS_SPI_MODE_0) /** default mode MODE_0. */ +#define SPI_CS_ACTIVE_MODE (TLS_SPI_CS_LOW) /** default chipselect mode is active low. */ +#define SPI_CS_INACTIVE_MODE (TLS_SPI_CS_HIGH) + +/** SPI transaction message status. */ +#define SPI_MESSAGE_STATUS_IDLE (0) +#define SPI_MESSAGE_STATUS_INPROGRESS (1) +#define SPI_MESSAGE_STATUS_DONE (2) + +/**slave type*/ +#define SPI_SLAVE_FLASH 0 /**flash */ +#define SPI_SLAVE_CARD 1 /** SD card */ +#define SPI_SLAVE_CONTROL_PIN 0 +/**transfer type*/ +#define SPI_BYTE_TRANSFER 0 /**byte transfer*/ +#define SPI_WORD_TRANSFER 1 /**word transfer*/ +#define SPI_DMA_TRANSFER 2 /** DMA transfer */ + +/** + * a read/write buffer pair + * + * SPI transfers always write the same number of bytes as they read. + * If the transmit buffer is null, zeroes will be shifted out while + * filling rx_buf. If the receive buffer is null, the data shifted in + * will be discarded. + */ +struct tls_spi_transfer +{ + struct dl_list transfer_list; /**< transfers are sequenced through + tls_spi_message.transfers. */ + + const void *tx_buf; /**< data to be written, or NULL. */ + void *rx_buf; /**< data to be read, or NULL. */ + u32 len; /**< size of rx and tx buffers (in bytes). */ + u32 delay_usecs; /**< microseconds to delay after this transfer. */ +}; + + +/** + * one multi-segment SPI transaction + * + * A struct tls_spi_message is used to execute an atomic sequence of data + * transfers, each represented by a struct tls_spi_transfer. The sequence + * is "atomic" in the sense that no other spi_message may use that SPI bus + * until that sequence completes. + */ +struct tls_spi_message +{ + struct dl_list queue; /**< transaction messages are sequenced through + tls_spi_port.wait_queue. */ + + struct dl_list transfers; /**< list of transfer segments in this transaction. */ + void (*complete) (void *); /**< called to report transaction completions. */ + void *context; /**< the argument to complete() when it's called. */ + u32 status; /**< transaction message status. */ +}; + +/** + * driver structure to SPI master controller + * + * This data structure presents the SPI master controller's configuration + * data. The device attached to this SPI master controller share the same + * transfer mode, chipselect mode and clock rate. And this structure maintains + * a queue of tls_spi_message transactions and uses this tls_spi_message transaction + * to access to the SPI device. For each such message it queues, it calls the message's + * completion function when the transaction completes. + */ +struct tls_spi_port +{ + u32 speed_hz; /**< clock rate to be used. */ + u8 cs_active; /**< chipselect mode, maybe active low or active + high. */ + u8 mode; /**< SPI transfer mode: mode_0(CPHA=0, CHOL=0), + mode_1(CPHA=0, CHOL=1), mode_2(CPHA=1, + CHOL=0), mode_3(CPHA=1, CHOL=1). */ + u8 reconfig; + + struct dl_list wait_queue; /**< wait list of transaction messages. */ + tls_os_queue_t *lock; + + tls_os_queue_t *msg_queue; /**< notify the schedule thread that there's + transaction message queued. */ + struct tls_spi_message *current_message; /**< current transaction message + in-progressing. */ + u32 current_remaining_transfer; /**< remaining transfer segments count in + current transaction message. */ + + struct tls_spi_transfer *current_transfer; /**< current transfer segment + in-progressing. */ + u32 current_remaining_bytes; /**< remaining data length in current + transfer segment. */ + + u8 transtype; /**< transfer type */ +}; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup HOSTSPI_Driver_APIs HOSTSPI Driver APIs + * @brief HOSTSPI driver APIs + */ + +/** + * @addtogroup HOSTSPI_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to initialize the SPI master driver. + * + * @param[in] None + * + * @retval TLS_SPI_STATUS_OK if initialize success + * @retval TLS_SPI_STATUS_EBUSY if SPI is already initialized + * @retval TLS_SPI_STATUS_ENOMEM if malloc SPI memory fail + * + * @note None + */ +int tls_spi_init(void); + + +/** + * @brief This function is used to setup the spi controller. + * + * @param[in] mode is CPOL and CPHA type defined in TLS_SPI_MODE_0 to TLS_SPI_MODE_3 + * @param[in] cs_active is cs mode, defined as TLS_SPI_CS_LOW or TLS_SPI_CS_HIGH + * @param[in] fclk is spi clock,the unit is HZ. + * + * @retval TLS_SPI_STATUS_OK if setup success + * @retval TLS_SPI_STATUS_EMODENOSUPPORT if mode is not support + * @retval TLS_SPI_STATUS_EINVAL if cs_active is not support + * @retval TLS_SPI_STATUS_ECLKNOSUPPORT if fclk is not support + * + * @note None + */ +int tls_spi_setup(u8 mode, u8 cs_active, u32 fclk); + +/** + * @brief This function is used to synchronous write data by SPI. + * + * @param[in] buf data to be sent. + * @param[in] len data length. + * + * @retval TLS_SPI_STATUS_OK if write success. + * @retval TLS_SPI_STATUS_EINVAL if argument is invalid. + * @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory. + * @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed. + * + * @note None + */ +int tls_spi_write(const u8 * buf, u32 len); + +/** + * @brief This function is used to synchronously read data from SPI. + * + * @param[in] buf is the buffer for saving SPI data. + * @param[in] len is the data length. + * + * @retval TLS_SPI_STATUS_OK if write success. + * @retval TLS_SPI_STATUS_EINVAL if argument is invalid. + * @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory. + * @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed. + * + * @note None + */ +int tls_spi_read(u8 * buf, u32 len); + +/** + * @brief This function is used to synchronously write command and then read data from SPI. + * + * @param[in] txbuf is the write data buffer. + * @param[in] n_tx is the write data length. + * @param[in] rxbuf is the read data buffer. + * @param[in] n_rx is the read data length. + * + * @retval TLS_SPI_STATUS_OK if write success. + * @retval TLS_SPI_STATUS_EINVAL if argument is invalid. + * @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory. + * @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver has not been installed. + * + * @note None + */ +int tls_spi_read_with_cmd(const u8 * txbuf, u32 n_tx, u8 * rxbuf, u32 n_rx); + +/** + * @brief This function is used to synchronous write 32bit command then write data from SPI. + * + * @param[in] cmd is the command data. + * @param[in] n_cmd is the command len,can not bigger than four + * @param[in] txbuf is the write data buffer. + * @param[in] n_tx is the write data length. + * + * @retval TLS_SPI_STATUS_OK if write success. + * @retval TLS_SPI_STATUS_EINVAL if argument is invalid. + * @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory. + * @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed. + * + * @note None + */ +int tls_spi_write_with_cmd(const u8 * cmd, u32 n_cmd, const u8 * txbuf, u32 n_tx); + +/** + * @brief This function is used to set SPI transfer mode. + * + * @param[in] type is the transfer type. + * SPI_BYTE_TRANSFER ->byte transfer; + * SPI_WORD_TRANSFER ->word transfer; + * SPI_DMA_TRANSFER ->DMA transfer; + + * + * @return None + * + * @note None + */ +void tls_spi_trans_type(u8 type); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_HOST_SPI_H */ diff --git a/sdk/Include/Driver/wm_hspi.h b/sdk/Include/Driver/wm_hspi.h new file mode 100644 index 0000000..f3ba8e7 --- /dev/null +++ b/sdk/Include/Driver/wm_hspi.h @@ -0,0 +1,264 @@ +/** + * @file wm_hspi.h + * + * + * @brief High speed spi slave Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_HSPI_H +#define WM_HSPI_H + +#include "wm_type_def.h" +#include "wm_ram_config.h" + +#define HSPI_TX_MEM_MALLOC 0 /** tx mem dynamic malloc*/ + +#define HSPI_IO_REUSE_NUM 0 +#define SDIO_IO_REUSE_NUM 2 +#define HSPI_INTERFACE_SPI 2 /** spi interface*/ +#define HSPI_INTERFACE_SDIO 3 /** sdio interface*/ + +/**rx message*/ +#define HSPI_RX_CMD_MSG 1 +#define HSPI_RX_DATA_MSG 2 + +/**spi/sdio buffer£¬Wraper controller can only access the address after the 0x60000*/ +#define HSPI_TXBUF_NUM 2 +#define HSPI_TX_DESC_NUM HSPI_TXBUF_NUM +#define HSPI_RXBUF_NUM 3//10 +#define HSPI_RX_DESC_NUM HSPI_RXBUF_NUM +#define HSPI_TXBUF_SIZE 1500 +#define HSPI_RXBUF_SIZE 1500 + +#define HSPI_TX_DESC_SIZE sizeof(struct tls_hspi_tx_desc) +#define HSPI_RX_DESC_SIZE sizeof(struct tls_hspi_rx_desc) + +/***************************************************************************** + * sdio/hspi sram partition + * total size : 0x61800 - 0x67FFFF (26KB) + ******************************************************************************/ +/* HSPI txbuf zone */ +#define HSPI_TXBUF_BASE_ADDR ((u32)(SLAVE_HSPI_SDIO_ADDR)) + +#if HSPI_TX_MEM_MALLOC +#define HSPI_TXBUF_TOTAL_SIZE 0 +#else +#define HSPI_TXBUF_TOTAL_SIZE (HSPI_TXBUF_SIZE * HSPI_TXBUF_NUM) +#endif +/** HSPI tx desc zone */ +#define HSPI_TX_DESC_BASE_ADDR ((u32)(HSPI_TXBUF_BASE_ADDR + HSPI_TXBUF_TOTAL_SIZE)) +#define HSPI_TX_DESC_TOTAL_SIZE (HSPI_TX_DESC_SIZE * HSPI_TX_DESC_NUM) //28*3=84 +/** HSPI rxbuf zone */ +#define HSPI_RXBUF_BASE_ADDR ((u32)(HSPI_TX_DESC_BASE_ADDR + HSPI_TX_DESC_TOTAL_SIZE)) +#define HSPI_RXBUF_TOTAL_SIZE (HSPI_RXBUF_NUM * HSPI_RXBUF_SIZE) //4500 +/** HSPI rx desc zone */ +#define HSPI_RX_DESC_BASE_ADDR ((u32)(HSPI_RXBUF_BASE_ADDR + HSPI_RXBUF_TOTAL_SIZE)) +#define HSPI_RX_DESC_TOTAL_SIZE (HSPI_RX_DESC_SIZE * HSPI_RX_DESC_NUM) //36 + +#if 0 +#define SDIO_CMD_RXBUF_SIZE (0x100) +#define SDIO_CMD_RXBUF_ADDR (0x00068000 - SDIO_CMD_RXBUF_SIZE) +#define SDIO_CIS1_ADDR (SDIO_CMD_RXBUF_ADDR - 0x80) +#define SDIO_CIS0_ADDR (SDIO_CIS1_ADDR - 0x80) +#else +#define SDIO_CIS0_ADDR (HSPI_RX_DESC_BASE_ADDR + HSPI_RX_DESC_TOTAL_SIZE) //128 +#define SDIO_CIS1_ADDR (SDIO_CIS0_ADDR + 0x80) //128 +#define SDIO_CMD_RXBUF_ADDR (SDIO_CIS1_ADDR + 0x80) +#define SDIO_CMD_RXBUF_SIZE 256//(1500) //256 +#endif +#define CIS_FUN0_ADDR SDIO_CIS0_ADDR +#define CIS_FUN1_ADDR SDIO_CIS1_ADDR + +#define FN0_TPL_FUNCID (CIS_FUN0_ADDR + 0x00) +#define FN0_TPL_FUNCE (CIS_FUN0_ADDR + 0x04) +#define FN0_TPL_FUNCE_MAXBLK (CIS_FUN0_ADDR + 0x08) +#define FN0_TPL_MANFID_MID (CIS_FUN0_ADDR + 0x0C) +#define FN0_TPL_END (CIS_FUN0_ADDR + 0x10) + +#define FN1_TPL_FUNCID (CIS_FUN1_ADDR + 0x00) +#define FN1_TPL_FUNCE (CIS_FUN1_ADDR + 0x04) +#define FN1_TPL_FUNCE_VER (CIS_FUN1_ADDR + 0x08) +#define FN1_TPL_FUNCE_NSN (CIS_FUN1_ADDR + 0x0C) +#define FN1_TPL_FUNCE_CSASIZE (CIS_FUN1_ADDR + 0x10) +#define FN1_TPL_FUNCE_OCR (CIS_FUN1_ADDR + 0x14) +#define FN1_TPL_FUNCE_MINPWR (CIS_FUN1_ADDR + 0x18) +#define FN1_TPL_FUNCE_STANDBY (CIS_FUN1_ADDR + 0x1C) +#define FN1_TPL_FUNCE_OPTBW (CIS_FUN1_ADDR + 0x20) +#define FN1_TPL_FUNCE_NTIMEOUT (CIS_FUN1_ADDR + 0x24) +#define FN1_TPL_FUNCE_AVGPWR (CIS_FUN1_ADDR + 0x28) +#define FN1_TPL_END (CIS_FUN1_ADDR + 0x30) + +/**Definition of SDIO interrupt in the system*/ +#define SDIO_RX_DATA_INT (0UL) +#define SDIO_TX_DATA_INT (1UL) +#define SDIO_RX_CMD_INT (2UL) +#define SDIO_TX_CMD_INT (3UL) + +/**IO control register*/ +#define HR_IOCTL_GP_SDIO HR_IOCTL_GP_SDIO_I2C + + +/** SDIO interrupt bit definition */ +#define SDIO_WP_INT_SRC_CMD_DOWN (1UL<<3) +#define SDIO_WP_INT_SRC_CMD_UP (1UL<<2) +#define SDIO_WP_INT_SRC_DATA_DOWN (1UL<<1) +#define SDIO_WP_INT_SRC_DATA_UP (1UL<<0) + + +/** Definition of send data descriptor structure */ +struct tls_hspi_tx_desc { + volatile u32 valid_ctrl; + u32 buf_info; + u32 buf_addr[3]; + u32 next_desc_addr; +#if HSPI_TX_MEM_MALLOC + u32 txbuf_addr; /**< txbuf addr, pbuf and buf_addr[0] are different */ +#endif +}; + +/** Definition of receive data descriptor structure */ +struct tls_hspi_rx_desc { + u32 valid_ctrl; + u32 buf_addr; + u32 next_desc_addr; +}; + + +/** struct tls_slave_hspi */ +struct tls_slave_hspi { + u8 ifusermode; + + s16 (*rx_cmd_callback)(char *buf); + + s16 (*rx_data_callback)(char *buf); + + s16 (*tx_data_callback)(char *buf); + + struct tls_hspi_tx_desc *curr_tx_desc; /**< Upstream data management */ + + struct tls_hspi_rx_desc *curr_rx_desc; /**< Downlink data management */ + +#if HSPI_TX_MEM_MALLOC + u8 txdoneflag; /**< tx done falg*/ +#endif +}; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup HSPI_Driver_APIs HSPI Driver APIs + * @brief HSPI driver APIs + */ + +/** + * @addtogroup HSPI_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to initial HSPI register. + * + * @param[in] None + * + * @retval 0 success + * @retval other failed + * + * @note When the system is initialized, the function has been called, so users can not call this function. + */ +int tls_slave_spi_init(void); + +/** + * @brief This function is used to enable or disable user mode. + * + * @param[in] ifenable TRUE or FALSE + * + * @return None + * + * @note If the user enables the user mode, RICM instruction in the system will not be used by SPI. + * If the user wants to use the SPI interface as other use, need to enable the user mode. + * This function must be called before the register function. + */ +void tls_set_hspi_user_mode(u8 ifenable); + + +/** + * @brief This function is used to set high speed interface type. + * + * @param[in] type is the interface type. HSPI_INTERFACE_SPI or HSPI_INTERFACE_SDIO + * + * @return None + * + * @note None + */ +void tls_set_high_speed_interface_type(int type); + + +/** + * @brief This function is used to register hspi rx command interrupt. + * + * @param[in] rx_cmd_callback is the hspi rx interrupt call back function. + * + * @return None + * + * @note None + */ +void tls_hspi_rx_cmd_callback_register(s16 (*rx_cmd_callback)(char *buf)); + + +/** + * @brief This function is used to register hspi rx data interrupt. + * + * @param[in] rx_data_callback is the hspi rx interrupt call back function. + * + * @return None + * + * @note None + */ +void tls_hspi_rx_data_callback_register(s16 (*rx_data_callback)(char *buf)); + +/** + * @brief This function is used to register hspi tx data interrupt. + * + * @param[in] tx_data_callback is the hspi tx interrupt call back function. + * + * @return None + * + * @note None + */ +void tls_hspi_tx_data_callback_register(s16 (*tx_data_callback)(char *buf)); + + +/** + * @brief This function is used to transfer data. + * + * @param[in] txbuf is a buf for saving user data. + * @param[in] len is the data length. + * + * @retval transfer data len success + * @retval 0 failed + * + * @note None + */ +int tls_hspi_tx_data(char *txbuf, int len); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_HSPI_H */ diff --git a/sdk/Include/Driver/wm_i2c.h b/sdk/Include/Driver/wm_i2c.h new file mode 100644 index 0000000..0d6c4b4 --- /dev/null +++ b/sdk/Include/Driver/wm_i2c.h @@ -0,0 +1,155 @@ +/**************************************************************************//** + * @file wm_i2c.h + * @author + * @version + * @date + * @brief + * @copyright (c) 2014 Winner Microelectronics Co., Ltd. All rights reserved. + *****************************************************************************/ + +#ifndef __WM_I2C_H +#define __WM_I2C_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wm_regs.h" +#include "wm_type_def.h" +typedef struct +{ + __IO uint32_t PRER_LO; + __IO uint32_t PRER_HI; + __IO uint32_t CTRL; + __O uint32_t TX_RX; + __O uint32_t CR_SR; + __I uint32_t TXR; + __I uint32_t CR; +}I2C_T; +#define I2C ((I2C_T *)(HR_I2C_BASE_ADDR)) + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup I2C_Driver_APIs I2C Driver APIs + * @brief I2C driver APIs + */ + +/** + * @addtogroup I2C_Driver_APIs + * @{ + */ + +/** + * @brief Init the I2C module + * + * @param freq I2C reference clock frequency in Hz that will be used + * + * @retval none + * + * @note + * the value must be between 100k and 400k + */ +void tls_i2c_init(u32 freq); + +#define wm_i2c_int_mask(bl) do { tls_bitband_write(HR_I2C_CTRL, 6, bl);}while(0); + +/** + * @brief send stop signal + * + */ +void tls_i2c_stop(void); + +/** + * @brief Waiting for ack signal + * @retval + * - \ref WM_FAILED + * - \ref WM_SUCCESS + */ +int tls_i2c_wait_ack(void); + +/** + * @brief Writes the data to data register of I2C module + * when ifstart one the start signal will be sent followed by the data + * when ifstart zero only the data will be send + * + * @param[in] data the data will be write to the data register of I2C module + * @param[in] ifstart when one send start signal, when zero don't + * + * @retval + * + */ +void tls_i2c_write_byte(u8 data, u8 ifstart); + +/** + * @brief Get the data stored in data register of I2C module + * + * @param[in] ifack when one send ack after reading the data register,when zero don't + * @param[in] ifstop when one send stop signal after read, when zero do not send stop + * + * @retval + * the received data + */ +u8 tls_i2c_read_byte(u8 ifack, u8 ifstop); + +/** + * @brief This function is used to register i2c transfer done callback function. + * + * @param[in] done is the i2c transfer done callback function. + * + * @retval None + * + * @note None + */ +void wm_i2c_transfer_done_register(void (*done)(void)); + +/** + * @brief Start writing through int mode + * + * @param[in] devaddr the device address + * @param[in] wordaddr when one send stop signal after read, when zero do not send stop + * @param[in] buf the address point where data shoule be stored + * @param[in] len the length of data will be received + * + * @retval + * - \ref WM_FAILED + * - \ref WM_SUCCESS + */ +int wm_i2c_start_write_it(uint8_t devaddr, uint8_t wordaddr, uint8_t * buf, uint16_t len); + +/** + * @brief start read through int mode + * + * @param[in] devaddr the device address + * @param[in] wordaddr when one send stop signal after read, when zero do not send stop + * @param[in] buf the address point where data shoule be stored + * @param[in] len the length of data will be received + * + * @retval + * - \ref WM_FAILED + * - \ref WM_SUCCESS + */ +int wm_i2c_start_read_it(uint8_t devaddr, uint8_t wordaddr, uint8_t * buf, uint16_t len); +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ diff --git a/sdk/Include/Driver/wm_i2s.h b/sdk/Include/Driver/wm_i2s.h new file mode 100644 index 0000000..a7a236b --- /dev/null +++ b/sdk/Include/Driver/wm_i2s.h @@ -0,0 +1,581 @@ +/**************************************************************************//** + * @file wm_i2s.h + * @author + * @version + * @date + * @brief + * + * @copyright (c) 2014 Winner Microelectronics Co., Ltd. All rights reserved. + *****************************************************************************/ + +#ifndef __WM_I2S_H +#define __WM_I2S_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wm_type_def.h" +#include "wm_regs.h" +#include "wm_osal.h" +#include "misc.h" +#include "wm_dma.h" + +#define I2S_CTRL_FORMAT_Pos (24) /*!< CTRL : FORMAT Position */ +#define I2S_CTRL_FORMAT_MASK (3UL<CTRL |= I2S_CTRL_EN_MASK) + +/** + * @brief Disable I2S module + * + */ +#define TLS_I2S_DISABLE() (I2S->CTRL &= ~I2S_CTRL_EN_MASK) + +/** + * @brief Enable clock of I2S module + * + */ +#define TLS_I2S_CLK_ENABLE() \ + do { \ + tls_bitband_write(HR_CLK_BASE_ADDR, HR_CLK_I2S_GATE_Pos , 1); \ + } while(0) + +/** + * @brief Disnable clock of I2S module + * + */ +#define TLS_I2S_CLK_DISABLE() \ + do { \ + tls_bitband_write(HR_CLK_BASE_ADDR, HR_CLK_I2S_GATE_Pos , 0); \ + } while(0) + +/** + * @brief Enable transmit of I2S module + * + */ +#define TLS_I2S_TX_ENABLE() \ + do { \ + tls_bitband_write(HR_I2S_CTRL, I2S_CTRL_TXE_Pos, 1); \ + } while(0) + +/** + * @brief Disable transmit of I2S module + * + */ +#define TLS_I2S_TX_DISABLE() \ + do { \ + tls_bitband_write(HR_I2S_CTRL, I2S_CTRL_TXE_Pos, 0); \ + } while(0) + +/** + * @brief Enable receive of I2S module + * + */ +#define TLS_I2S_RX_ENABLE() \ + do { \ + tls_bitband_write(HR_I2S_CTRL, I2S_CTRL_RXE_Pos, 1); \ + } while(0) + +/** + * @brief Disable receive of I2S module + * + */ +#define TLS_I2S_RX_DISABLE() \ + do { \ + tls_bitband_write(HR_I2S_CTRL, I2S_CTRL_RXE_Pos, 0); \ + } while(0) + +/** + * @brief Clear receive FIFO + * + */ +#define TLS_I2S_RX_FIFO_CLEAR() \ + do { \ + tls_bitband_write(HR_I2S_CTRL, I2S_CTRL_RX_CLR_Pos, 1); \ + } while(0) + +/** + * @brief Clear transmit FIFO + * + */ +#define TLS_I2S_TX_FIFO_CLEAR() \ + do { \ + tls_bitband_write(HR_I2S_CTRL, I2S_CTRL_TX_CLR_Pos, 1); \ + } while(0) + +/** + * @brief Enable transmit DMA request + * + */ +#define TLS_I2S_TXDMA_ENABLE() (I2S->CTRL |= I2S_CTRL_TXDMA_MASK) + +/** + * @brief Disnable transmit DMA request + * + */ +#define TLS_I2S_TXDMA_DISABLE() (I2S->CTRL &= ~I2S_CTRL_TXDMA_MASK) + +/** + * @brief Enable receive DMA request + * + */ +#define TLS_I2S_RXDMA_ENABLE() (I2S->CTRL |= I2S_CTRL_RXDMA_MASK) + +/** + * @brief Disnable receive DMA request + * + */ +#define TLS_I2S_RXDMA_DISABLE() (I2S->CTRL &= ~I2S_CTRL_RXDMA_MASK) + +/** + * @brief Left channel zero-cross detection enable + * + */ +#define TLS_I2S_LZC_ENABLE() (I2S->CTRL |= I2S_CTRL_LZCEN_MASK) + +/** + * @brief Left channel zero-cross detection disable + * + */ +#define TLS_I2S_LZC_DISABLE() (I2S->CTRL &= ~I2S_CTRL_LZCEN_MASK) + +/** + * @brief Right channel zero-cross detection enable + * + */ +#define TLS_I2S_RZC_ENABLE() (I2S->CTRL |= I2S_CTRL_RZCEN_MASK) + +/** + * @brief Right channel zero-cross detection disable + * + */ +#define TLS_I2S_RZC_DISABLE() (I2S->CTRL &= ~I2S_CTRL_RZCEN_MASK) + +/** @brief Enable transmit mute + * + */ +#define TLS_I2S_TX_MUTE_ENABLE() (I2S->CTRL |= I2S_CTRL_MUTE_MASK) + +/** + * @brief Disable transmit mute + * + */ +#define TLS_I2S_TX_MUTE_DISABLE() (I2S->CTRL &= ~ I2S_CTRL_MUTE_MASK) + +/** + * @brief Set receive FIFO threshold level + * @param[in] level The value set to Rx threshold level + * + */ +static __inline__ void tls_i2s_set_rxth(uint8_t level) +{ + if (level > 8) + { + level = 3; + } + I2S->CTRL &= ~ I2S_CTRL_RXTH_MASK; + I2S->CTRL |= ((level - 1)< 8) + level = 4; + I2S->CTRL &= ~ I2S_CTRL_TXTH_MASK; + I2S->CTRL |= (level<CTRL &= ~ I2S_CTRL_CHSEL_MASK; + I2S->CTRL |= channel_sel; +} + +/** + * @brief Enables or disables the specified I2S interrupts + * @param[in] i2s_int_mask specifies the I2S interrupt sources to be enabled or disabled. + * This parameter can be one of the following values: + * - \ref I2S_INT_MASK_LZC + * - \ref I2S_INT_MASK_RZC + * - \ref I2S_INT_MASK_TXDONE + * - \ref I2S_INT_MASK_TXTH + * - \ref I2S_INT_MASK_TXOV + * - \ref I2S_INT_MASK_TXUD + * - \ref I2S_INT_MASK_RXDONE + * - \ref I2S_INT_MASK_RXTH + * - \ref I2S_INT_MASK_RXOV + * - \ref I2S_INT_MASK_RXUD + * @param[in] enable when set 1 disable interrupt, 0 enable + * @retval None + */ +static __inline__ void tls_i2s_int_config(uint32_t i2s_int_mask, uint8_t enable) +{ + if(enable) + { + I2S->INT_MASK &= ~i2s_int_mask; + } + else + { + I2S->INT_MASK |= i2s_int_mask; + } +} + +/** + * @brief Enable master clock (MCLK). + * @param[in] clock The target MCLK clock rate. + * @retval actual MCLK clock rate + * @details Set the master clock rate according to u32BusClock parameter and enable master clock output. + * The actual master clock rate may be different from the target master clock rate. + * The real master clock rate will be returned for reference. + */ +uint32_t tls_i2s_mclk_enable(uint32_t clock); + +/** + * @brief disable master clock (MCLK). + * + */ +void tls_i2s_mclk_disable(void); + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /** __WM_I2S_H*/ + + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ diff --git a/sdk/Include/Driver/wm_internal_flash.h b/sdk/Include/Driver/wm_internal_flash.h new file mode 100644 index 0000000..2b0cc46 --- /dev/null +++ b/sdk/Include/Driver/wm_internal_flash.h @@ -0,0 +1,239 @@ +/** + * @file wm_internal_flash.h + * + * @brief inter flash driver + * + * @author dave + * + * @copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_INTERNAL_FLASH_H +#define WM_INTERNAL_FLASH_H + +#include "wm_type_def.h" +#include "wm_osal.h" + +typedef volatile unsigned char vu8; +typedef volatile unsigned short vu16; +typedef volatile unsigned long vu32; + +#define M8(adr) (*((vu8 *) (adr))) +#define M16(adr) (*((vu16*) (adr))) +#define M32(adr) (*((vu32*) (adr))) + +#define INSIDE_FLS_BLOCK_SIZE (0x10000UL) +#define INSIDE_FLS_SECTOR_SIZE (0x1000UL) +#define INSIDE_FLS_PAGE_SIZE 256 + + +#define INSIDE_FLS_BASE_ADDR 0x8000000UL +#define INSIDE_FLS_SECBOOT_ADDR (INSIDE_FLS_BASE_ADDR + 0x02000) + + +enum TYPE_FLASH_ID{ + SPIFLASH_MID_GD = 0xC8, + SPIFLASH_MID_ESMT = 0x1C, + SPIFLASH_MID_PUYA = 0x85, + SPIFLASH_MID_WINBOND = 0xEF, +}; + + +/** + * @typedef struct Flash Registers + */ +typedef struct +{ + vu32 ACR; /**< offset 0x000 */ + vu32 KEYR; /**< offset 0x004 */ + vu32 SR; /**< offset 0x008 */ + vu32 CR; /**< offset 0x00C */ + vu32 AR; /**< offset 0x010 */ +} FLASH_TypeDef; + +#define FLASH_HS 0x00000001 + + /** Flash Keys */ +#define RDPRT_KEY 0x5AA5 +#define FLASH_KEY1 0x57696E6E +#define FLASH_KEY2 0x65724D69 +#define FLASH_KEY3 0x63726F21 + + /** Flash Control Register definitions */ +#define FLASH_PG 0x00000001 +#define FLASH_PER 0x00000002 +#define FLASH_MER 0x00000004 +#define FLASH_STRT 0x00000008 +#define FLASH_LOCK 0x00000020 +#define FLASH_ERRIE 0x00000040 +#define FLASH_EOPIE 0x00000080 + + /** Flash Status Register definitions */ +#define FLASH_BSY 0x00000001 +#define FLASH_PGERR 0x00000002 +#define FLASH_EOP 0x00000004 + + +#define TLS_FLS_STATUS_OK (0) +#define TLS_FLS_STATUS_EINVAL (1) +#define TLS_FLS_STATUS_EBUSY (2) +#define TLS_FLS_STATUS_EPERM (3) +#define TLS_FLS_STATUS_ENOSUPPORT (4) +#define TLS_FLS_STATUS_EEXIST (5) +#define TLS_FLS_STATUS_ENOMEM (6) +#define TLS_FLS_STATUS_EOVERFLOW (7) +#define TLS_FLS_STATUS_ENODEV (8) +#define TLS_FLS_STATUS_EDEV (9) +#define TLS_FLS_STATUS_EIO (10) +#define TLS_FLS_STATUS_ENODRV (11) + +#define TLS_FLS_PARAM_TYPE_ID (0) +#define TLS_FLS_PARAM_TYPE_SIZE (1) +#define TLS_FLS_PARAM_TYPE_PAGE_SIZE (2) +#define TLS_FLS_PARAM_TYPE_PROG_SIZE (3) +#define TLS_FLS_PARAM_TYPE_SECTOR_SIZE (4) + +#define TLS_FLS_FLAG_UNDER_PROTECT (1<<0) +#define TLS_FLS_FLAG_FAST_READ (1<<1) +#define TLS_FLS_FLAG_AAAI (1<<2) + +/** + * @struct tls_inside_fls + */ +struct tls_inside_fls +{ + tls_os_sem_t *fls_lock; +}; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup INTERNAL_FLASH_Driver_APIs INTERNAL_FLASH Driver APIs + * @brief INTERNAL_FLASH driver APIs + */ + +/** + * @addtogroup INTERNAL_FLASH_Driver_APIs + * @{ + */ + + +/** + * @brief This function is used to unlock flash protect area [0x0~0x2000]. + * + * @param None + * + * @return 0 success + * -1 fail + * + * @note None + */ +int tls_flash_unlock(void); + +/** + * @brief This function is used to lock flash protect area [0x0~0x2000]. + * + * @param None + * + * @return 0 success + * -1 fail + * + * @note None + */ +int tls_flash_lock(void); + + +/** + * @brief This function is used to get the flash semaphore. + * + * @param None + * + * @return None + * + * @note None + */ +void tls_fls_sem_lock(void); + +/** + * @brief This function is used to release the flash semaphore. + * + * @param None + * + * @return None + * + * @note None + */ +void tls_fls_sem_unlock(void); + + +/** + * @brief This function is used to initial flash module structer. + * + * @param[in] None + * + * @retval TLS_FLS_STATUS_OK if init sucsess + * @retval TLS_FLS_STATUS_EBUSY already inited + * @retval TLS_FLS_STATUS_ENOMEM memory error + * + * @note None + */ +int tls_fls_init(void); + +/** + * @brief This function is used to read data from the flash. + * + * @param[in] addr Specifies the starting address to read from. + * @param[in] buf Specified the address to save the readback data. + * @param[in] len Specifies the length of the data to read. + * + * @retval TLS_FLS_STATUS_OK if read sucsess + * @retval TLS_FLS_STATUS_EPERM if flash driver module not beed installed + * @retval TLS_FLS_STATUS_EINVAL if arguments invalid + * @retval TLS_FLS_STATUS_EIO if read fail + * + * @note None + */ +int tls_fls_read(u32 addr, u8 * buf, u32 len); + + +/** + * @brief This function is used to write data into the flash. + * + * @param[in] addr Specifies the starting address to write to + * @param[in] buf Pointer to a byte array that is to be written + * @param[in] len Specifies the length of the data to be written + * + * @retval TLS_FLS_STATUS_OK if write flash success + * @retval TLS_FLS_STATUS_EPERM if flash struct point is null + * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed + * @retval TLS_FLS_STATUS_EINVAL if argument is invalid + * @retval TLS_FLS_STATUS_EIO if io error + * + * @note None + */ +int tls_fls_write(u32 addr, u8 * buf, u32 len); + + +/** + * @brief This function is used to erase the appointed sector + * + * @param[in] sector sector num of the flash, 4K bytes every sector + * + * @retval TLS_FLS_STATUS_OK if read sucsess + * @retval other if read fail + * + * @note None + */ +int tls_fls_erase(u32 sector); + +void writeSRLockBit(unsigned char srbitlock); + +#endif /* WM_INTERNAL_FLASH_H */ diff --git a/sdk/Include/Driver/wm_io.h b/sdk/Include/Driver/wm_io.h new file mode 100644 index 0000000..b8a0a55 --- /dev/null +++ b/sdk/Include/Driver/wm_io.h @@ -0,0 +1,195 @@ +/** + * @file wm_io.h + * + * @brief IO Driver Module + * + * @author lilm + * + * @copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_IO_H +#define WM_IO_H + + +#define TLS_IO_AB_OFFSET (0x40011200 - 0x40010C00) + +/** io name */ +enum tls_io_name { + WM_IO_PA_00 = 0, /**< gpio a0 */ + WM_IO_PA_01, /**< gpio a1 */ + WM_IO_PA_02, /**< gpio a2 */ + WM_IO_PA_03, /**< gpio a3 */ + WM_IO_PA_04, /**< gpio a4 */ + WM_IO_PA_05, /**< gpio a5 */ + WM_IO_PA_06, /**< gpio a6 */ + WM_IO_PA_07, /**< gpio a7 */ + WM_IO_PA_08, /**< gpio a8 */ + WM_IO_PA_09, /**< gpio a9 */ + WM_IO_PA_10, /**< gpio a10 */ + WM_IO_PA_11, /**< gpio a11 */ + WM_IO_PA_12, /**< gpio a12 */ + WM_IO_PA_13, /**< gpio a13 */ + WM_IO_PA_14, /**< gpio a14 */ + WM_IO_PA_15, /**< gpio a15 */ + + WM_IO_PB_00, /**< gpio b0 */ + WM_IO_PB_01, /**< gpio b1 */ + WM_IO_PB_02, /**< gpio b2 */ + WM_IO_PB_03, /**< gpio b3 */ + WM_IO_PB_04, /**< gpio b4 */ + WM_IO_PB_05, /**< gpio b5 */ + WM_IO_PB_06, /**< gpio b6 */ + WM_IO_PB_07, /**< gpio b7 */ + WM_IO_PB_08, /**< gpio b8 */ + WM_IO_PB_09, /**< gpio b9 */ + WM_IO_PB_10, /**< gpio b10 */ + WM_IO_PB_11, /**< gpio b11 */ + WM_IO_PB_12, /**< gpio b12 */ + WM_IO_PB_13, /**< gpio b13 */ + WM_IO_PB_14, /**< gpio b14 */ + WM_IO_PB_15, /**< gpio b15 */ + WM_IO_PB_16, /**< gpio b16 */ + WM_IO_PB_17, /**< gpio b17 */ + WM_IO_PB_18, /**< gpio b18 */ + WM_IO_PB_19, /**< gpio b19 */ + WM_IO_PB_20, /**< gpio b20 */ + WM_IO_PB_21, /**< gpio b21 */ + WM_IO_PB_22, /**< gpio b22 */ + WM_IO_PB_23, /**< gpio b23 */ + WM_IO_PB_24, /**< gpio b24 */ + WM_IO_PB_25, /**< gpio b25 */ + WM_IO_PB_26, /**< gpio b26 */ + WM_IO_PB_27, /**< gpio b27 */ + WM_IO_PB_28, /**< gpio b28 */ + WM_IO_PB_29, /**< gpio b29 */ + WM_IO_PB_30, /**< gpio b30 */ + WM_IO_PB_31 /**< gpio b31 */ +}; + +/** option 1 of the io */ +#define WM_IO_OPTION1 1 +/** option 2 of the io */ +#define WM_IO_OPTION2 2 +/** option 3 of the io */ +#define WM_IO_OPTION3 3 +/** option 4 of the io */ +#define WM_IO_OPTION4 4 +/** option 5 of the io */ +#define WM_IO_OPTION5 5 +/** option 6 of the io */ +#define WM_IO_OPTION6 6 + + +/* io option1 */ +#define WM_IO_OPT1_I2C_DAT WM_IO_OPTION1 +#define WM_IO_OPT1_PWM1 WM_IO_OPTION1 +#define WM_IO_OPT1_PWM2 WM_IO_OPTION1 +#define WM_IO_OPT1_PWM3 WM_IO_OPTION1 +#define WM_IO_OPT1_PWM4 WM_IO_OPTION1 +#define WM_IO_OPT1_PWM5 WM_IO_OPTION1 +#define WM_IO_OPT1_UART0_RXD WM_IO_OPTION1 +#define WM_IO_OPT1_UART0_TXD WM_IO_OPTION1 +#define WM_IO_OPT1_PWM_BRAKE WM_IO_OPTION1 +#define WM_IO_OPT1_I2S_M_EXTCLK WM_IO_OPTION1 +#define WM_IO_OPT1_SPI_M_DO WM_IO_OPTION1 +#define WM_IO_OPT1_SPI_M_DI WM_IO_OPTION1 +#define WM_IO_OPT1_SPI_M_CS WM_IO_OPTION1 +#define WM_IO_OPT1_SPI_M_CK WM_IO_OPTION1 +#define WM_IO_OPT1_I2S_S_RL WM_IO_OPTION1 +#define WM_IO_OPT1_I2S_S_SCL WM_IO_OPTION1 +#define WM_IO_OPT1_I2S_S_SDA WM_IO_OPTION1 +#define WM_IO_OPT1_I2S_M_RL WM_IO_OPTION1 +#define WM_IO_OPT1_I2S_M_SCL WM_IO_OPTION1 +#define WM_IO_OPT1_I2S_M_SDA WM_IO_OPTION1 +#define WM_IO_OPT1_JTAG_RST WM_IO_OPTION1 +#define WM_IO_OPT1_JTAG_TDO WM_IO_OPTION1 +#define WM_IO_OPT1_JTAG_TDI WM_IO_OPTION1 +#define WM_IO_OPT1_JTAG_TCK_SWDCK WM_IO_OPTION1 +#define WM_IO_OPT1_JTAG_TMS_SWDAT WM_IO_OPTION1 +#define WM_IO_OPT1_UART1_RXD WM_IO_OPTION1 +#define WM_IO_OPT1_UART1_TXD WM_IO_OPTION1 +#define WM_IO_OPT1_UART1_RTS WM_IO_OPTION1 +#define WM_IO_OPT1_UART1_CTS WM_IO_OPTION1 +#define WM_IO_OPT1_SDIO_DAT WM_IO_OPTION1 + +/* io option2 */ +#define WM_IO_OPT2_PWM1 WM_IO_OPTION2 +#define WM_IO_OPT2_PWM2 WM_IO_OPTION2 +#define WM_IO_OPT2_PWM3 WM_IO_OPTION2 +#define WM_IO_OPT2_PWM4 WM_IO_OPTION2 +#define WM_IO_OPT2_PWM5 WM_IO_OPTION2 +#define WM_IO_OPT2_SPI_M_DO WM_IO_OPTION2 +#define WM_IO_OPT2_SPI_M_DI WM_IO_OPTION2 +#define WM_IO_OPT2_SPI_M_CS WM_IO_OPTION2 +#define WM_IO_OPT2_SPI_M_CK WM_IO_OPTION2 +#define WM_IO_OPT2_I2C_SCL WM_IO_OPTION2 +#define WM_IO_OPT2_I2S_M_EXTCLK WM_IO_OPTION2 +#define WM_IO_OPT2_UART1_RXD WM_IO_OPTION2 +#define WM_IO_OPT2_UART1_TXD WM_IO_OPTION2 +#define WM_IO_OPT2_UART1_RTS WM_IO_OPTION2 +#define WM_IO_OPT2_UART1_CTS WM_IO_OPTION2 +#define WM_IO_OPT2_I2C_DAT WM_IO_OPTION2 +#define WM_IO_OPT2_PWM_BRAKE WM_IO_OPTION2 +#define WM_IO_OPT2_UART0_RTS WM_IO_OPTION2 +#define WM_IO_OPT2_UART0_CTS WM_IO_OPTION2 +#define WM_IO_OPT2_SDIO_DAT WM_IO_OPTION2 +#define WM_IO_OPT2_HSPI_CK WM_IO_OPTION2 +#define WM_IO_OPT2_HSPI_INT WM_IO_OPTION2 +#define WM_IO_OPT2_HSPI_CS WM_IO_OPTION2 +#define WM_IO_OPT2_HSPI_DI WM_IO_OPTION2 +#define WM_IO_OPT2_HSPI_DO WM_IO_OPTION2 + +/* io option3 */ +#define WM_IO_OPT3_UART0_RXD WM_IO_OPTION3 +#define WM_IO_OPT3_UART0_TXD WM_IO_OPTION3 +#define WM_IO_OPT3_UART0_RTS WM_IO_OPTION3 +#define WM_IO_OPT3_UART0_CTS WM_IO_OPTION3 +#define WM_IO_OPT3_SPI_M_DO WM_IO_OPTION3 +#define WM_IO_OPT3_SPI_M_DI WM_IO_OPTION3 +#define WM_IO_OPT3_SPI_M_CS WM_IO_OPTION3 +#define WM_IO_OPT3_SDIO_CK WM_IO_OPTION3 +#define WM_IO_OPT3_SDIO_CMD WM_IO_OPTION3 +#define WM_IO_OPT3_SDIO_DAT WM_IO_OPTION3 + +/* io option4 */ +#define WM_IO_OPT4_I2S_M_MCLK WM_IO_OPTION4 +#define WM_IO_OPT4_I2S_M_RL WM_IO_OPTION4 +#define WM_IO_OPT4_I2S_M_SCL WM_IO_OPTION4 +#define WM_IO_OPT4_I2S_M_SDA WM_IO_OPTION4 + +/* io option5 */ +#define WM_IO_OPT5_GPIO WM_IO_OPTION5 + +/* io option6 */ +#define WM_IO_OPT6_ADC WM_IO_OPTION6 +#define WM_IO_OPT6_LCD_COM WM_IO_OPTION6 +#define WM_IO_OPT6_LCD_SEG WM_IO_OPTION6 + + +/** + * @brief This function is used to config io function + * + * @param[in] name io name + * @param[in] option io function option, value is WM_IO_OPT*_*, also is WM_IO_OPTION1~6 + * + * @return None + * + * @note None + */ +void tls_io_cfg_set(enum tls_io_name name, u8 option); + + +/** + * @brief This function is used to get io function config + * + * @param[in] name io name + * + * @retval WM_IO_OPTION1~6 Mapping io function + * + * @note None + */ +int tls_io_cfg_get(enum tls_io_name name); + + +#endif /* end of WM_IO_H */ + diff --git a/sdk/Include/Driver/wm_iouart.h b/sdk/Include/Driver/wm_iouart.h new file mode 100644 index 0000000..1a8c1bd --- /dev/null +++ b/sdk/Include/Driver/wm_iouart.h @@ -0,0 +1,138 @@ +/***************************************************************************** +* +* File Name : wm_iouart.h +* +* Description: IO uart Driver Module +* +* Copyright (c) 2014 Winner Microelectronics Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2015-8-13 +*****************************************************************************/ +#ifndef WM_IO_UART_H +#define WM_IO_UART_H + +#include "wm_uart.h" +#include "wm_io.h" + + +#define IO_UART_FOR_PRINT 0 + +#define IO_UART_TX WM_IO_PB_08 +#define IO_UART_RX WM_IO_PB_09 + +#define IO_UART_HIGH 1 +#define IO_UART_LOW 0 + +#define TLS_IO_UART_RX_BUF_SIZE 1024 +#define TLS_IO_UART_TX_BUF_SIZE 1024 + +#define IO_UART_RATE_MUL 8 //²ÉÑùÂÊ=¸ÃÖµ*²¨ÌØÂÊ + +#define IO_UART_ONEBITE_SAMPLE_NUM 2 +#define IO_UART_ONEBYTE_BIT 10 + +typedef struct tls_io_uart{ + struct tls_uart_circ_buf recv; + int timercnt; + u8 bit[IO_UART_ONEBITE_SAMPLE_NUM]; //ÿ¸öbit²ÉÑù3´Î£¬È»ºóÅÐ¶Ï¸ßµÍµçÆ½ + u8 bitcnt; //µ¥¸öbit²ÉÑù´ÎÊý + u8 bitnum; //bitºÅ£¬Ò»¸öbyte¹²ÓÐ10¸öbit(1¸öÆðʼλ+8¸öÊý¾Ýλ+1¸öֹͣλ) + u8 ifrx; //ÊÇ·ñÕýÔÚrx + u8 iftx; //ÊÇ·ñÕýÔÚtx + u8 timerid; + +}tls_io_uart_t; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup IOUART_Driver_APIs IOUART Driver APIs + * @brief IOUART driver APIs + */ + +/** + * @addtogroup IOUART_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to initialize uart io + * + * @param[in] bandrate iouart baudrate + * + * @return WM_SUCCESS success + * @return WM_FAILED fail + * + * @note None + */ +int tls_iouart_init(int bandrate); + +/** + * @brief This function is used to restore uart io cfg + * + * @param[in] None + * + * @return WM_SUCCESS success + * + * @note None + */ +int tls_iouart_destroy(void); + +/** + * @brief This function is used to read data from uart io + * + * @param[in] buf is the user buffer + * @param[in] readsize is the user read size + * + * @return >0 copy data size + * @return WM_FAILED fail + * + * @note None + */ +int tls_iouart_read(u8 * buf, int bufsize); + +/** + * @brief This function is used to transfer data synchronously. + * + * @param[in] buf is a buf for saving user data + * @param[in] writesize is the user data length + * + * @retval WM_SUCCESS tx success + * @retval WM_FAILED tx failed + * + * @note None + */ +int tls_iouart_write(u8 * buf, int bufsize); + +/** + * @brief This function is used to output a character to the serial port. + * + * @param[in] ch character to be output + * + * @retval ch + * + * @note None + */ +int tls_iouart_output_char(int ch); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_IO_UART_H */ + diff --git a/sdk/Include/Driver/wm_irq.h b/sdk/Include/Driver/wm_irq.h new file mode 100644 index 0000000..9ade84e --- /dev/null +++ b/sdk/Include/Driver/wm_irq.h @@ -0,0 +1,177 @@ +/** + * @file wm_irq.h + * + * @brief interupt driver module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_IRQ_H +#define WM_IRQ_H + +#include "wm_type_def.h" + +/* Number of interrupts. */ + +#define INTR_CNT 41 + +#define SDIO_UP_DATA_INT (0UL) +#define SDIO_DOWN_DATA_INT (1UL) +#define SDIO_UP_CMD_INT (2UL) +#define SDIO_DOWN_CMD_INT (3UL) +#define MAC_INT (4UL) + +#define RX_INT (6UL) +#define TX_MGMT_COMPLETE_INT (7UL) +#define TX_DATA_COMPLETE_INT (8UL) +#define PMU_TIMER1_INT (9UL) +#define PMU_TIMER0_INT (10UL) +#define PMU_GPIO_WAKEUP_INT (11UL) +#define PMU_SDIO_WAKEUP_INT (12UL) +#define DMA0_INT (13UL) +#define DMA1_INT (14UL) +#define DMA2_INT (15UL) +#define DMA3_INT (16UL) +#define DMA4_INT (17UL) +#define DMA_BURST_INT (18UL) +#define I2C_INT (19UL) +#define ADC_INT (20UL) +#define SPI0_INT (21UL) +#define SPI1_INT (22UL) +#define UART0_INT (23UL) +#define UART1_INT (24UL) +#define GPIO_INT (25UL) +#define TIMER0_INT (26UL) +#define TIMER1_INT (27UL) +#define TIMER2_INT (28UL) +#define TIMER3_INT (29UL) +#define TIMER4_INT (30UL) +#define TIMER5_INT (31UL) +#define WATCHDOG_INT (32UL) +#define PMU_BOD_INT (33UL) +#define FLASH_INT (34UL) +#define PWM_INT (35UL) +#define I2S_INT (36UL) +#define PMU_RTC_INT (37UL) +#define RSA_INT (38UL) +#define CRYPTION_INT (39UL) +#define GPIOB_INT (40UL) +#define UART2_INT (41UL) + +#define IRQ_MAX_SYSCALL_PRIORITY 0x0B + +typedef void (*intr_handler_func) (void *); + +/** + * @typedef struct tls_irq_handler + */ +typedef struct tls_irq_handler +{ + void (*handler) (void *); + void *data; +// const char *name; + u32 counter; +} tls_irq_handler_t; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup IRQ_Driver_APIs IRQ Driver APIs + * @brief IRQ driver APIs + */ + +/** + * @addtogroup IRQ_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to initial system interrupt. + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_irq_init(void); + +/** + * @brief This function is used to register interrupt handler function. + * + * @param[in] vec_no interrupt NO + * @param[in] handler + * @param[in] *data + * + * @return None + * + * @note None + */ +void tls_irq_register_handler(u8 vec_no, intr_handler_func handler, void *data); + + +/** + * @brief This function is used to enable interrupt. + * + * @param[in] vec_no interrupt NO + * + * @return None + * + * @note None + */ +void tls_irq_enable(u8 vec_no); + +/** + * @brief This function is used to set interrupt priority. + * + * @param[in] vec_no interrupt no + * @param[in] priority priority + * + * @return None + * + * @note None + */ +void tls_irq_set_priority(u8 vec_no, u8 priority); + +/** + * @brief This function is used to disable interrupt. + * + * @param[in] vec_no interrupt NO + * + * @return None + * + * @note None + */ +void tls_irq_disable(u8 vec_no); + + +/** + * @brief This function is used to get the isr count. + * + * @param[in] None + * + * @retval count + * + * @note None + */ +u8 tls_get_isr_count(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_IRQ_H */ diff --git a/sdk/Include/Driver/wm_lcd.h b/sdk/Include/Driver/wm_lcd.h new file mode 100644 index 0000000..33e412e --- /dev/null +++ b/sdk/Include/Driver/wm_lcd.h @@ -0,0 +1,245 @@ +/** + * @file wm_lcd.h + * + * @brief LCD Driver Module + * + * @author dave + * + * @copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ + +#ifndef __WM_LCD_H +#define __WM_LCD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wm_regs.h" +#include + +/** + * @brief LCD Register Declaration + * + */ +typedef struct +{ + /** Control Register */ + __IO uint32_t CTRL; + /** Refresh Rate Prescaler Register */ + __IO uint32_t FRAMECNT; + /** Data Register */ + __IO uint32_t COM01; + /** Data Register */ + __IO uint32_t COM23; + /** Data Register */ + __IO uint32_t COM45; + /** Data Register */ + __IO uint32_t COM67; + /** LCD IO Control Register */ + __IO uint32_t IO_EN; + +} LCD_T; +/** LCD base pointer */ +#define LCD ((LCD_T *)HR_LCD_REG_BASE) + +typedef enum +{ + /** Static (2 levels) */ + BIAS_STATIC = LCD_BIAS_STATIC, + /** 1/2 Bias (3 levels) */ + BIAS_ONEHALF = LCD_BIAS_ONEHALF, + /** 1/3 Bias (4 levels) */ + BIAS_ONETHIRD = LCD_BIAS_ONETHIRD, + /** 1/4 Bias (4 levels) */ + BIAS_ONEFOURTH = LCD_BIAS_ONEFOURTH, +} LCD_BiasDef; + +typedef enum +{ + /** VLCD 2.7v */ + VLCD27 = LCD_VLCD_27, + /** VLCD 2.9v */ + VLCD29 = LCD_VLCD_29, + /** VLCD 3.1v */ + VLCD31 = LCD_VLCD_31, + /** VLCD 3.3v */ + VLCD33 = LCD_VLCD_33, +} LCD_VlcdDef; + +typedef enum +{ + /** max configuration is 8x16 */ + MODE8x16 = LCD_CR_MODE_8X16, + /** max configuration is 4x20 */ + MODE4x20 = LCD_CR_MODE_4x20, +} LCD_ModeDef; + +typedef enum +{ + /** Static (segments can be multiplexed with LCD_COM[0]) */ + DUTY_STATIC = LCD_DUTY_STATIC, + /** 1/2 Duty cycle (segments can be multiplexed with LCD_COM[0:1]) */ + DUTY_ONEHALF = LCD_DUTY_ONEHALF, + /** 1/3 Duty cycle (segments can be multiplexed with LCD_COM[0:2]) */ + DUTY_ONETHIRD = LCD_DUTY_ONETHIRD, + /** 1/4 Duty cycle (segments can be multiplexed with LCD_COM[0:3]) */ + DUTY_ONEFOURTH = LCD_DUTY_ONEFOURTH, + /** 1/5 Duty cycle (segments can be multiplexed with LCD_COM[0:4]) */ + DUTY_ONEFIFTH = LCD_DUTY_ONEFIFTH, + /** 1/6 Duty cycle (segments can be multiplexed with LCD_COM[0:5]) */ + DUTY_ONESIXTH = LCD_DUTY_ONESIXTH, + /** 1/7 Duty cycle (segments can be multiplexed with LCD_COM[0:6]) */ + DUTY_ONESEVENTH = LCD_DUTY_ONESEVENTH, + /** 1/8 Duty cycle (segments can be multiplexed with LCD_COM[0:7]) */ + DUTY_ONEEIGHTH = LCD_DUTY_ONEEIGHTH, +} LCD_DutyDef; + +typedef struct tls_lcd_options +{ + bool enable; + /** 8x16 or 4x20*/ + LCD_ModeDef mode; + /** Bias configuration */ + LCD_BiasDef bias; + /** Duty configuration */ + LCD_DutyDef duty; + /** Vlcd configuration */ + LCD_VlcdDef vlcd; + /** Fresh rate configuration */ + uint16_t fresh_rate; +} tls_lcd_options_t; + + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup LCD_Driver_APIs LCD Driver APIs + * @brief LCD driver APIs + */ + +/** + * @addtogroup LCD_Driver_APIs + * @{ + */ + +/** + * @brief initialize the LCD module + * param[in] opts LCD Structure pointer + */ +void tls_lcd_init(tls_lcd_options_t *opts); + +/** + * @brief Initialize LCD Frame Counter + * @param[in] freq LCD reference refresh frequency in Hz that will be used + * + */ +void tls_lcd_fresh_rate(uint16_t freq); + +/** + * @brief Turn on or clear a segment + * @param[in] com Which COM line to update + * @param[in] bit Bit index of which field to change + * @param[in] enable When one will set segment, when zero will clear segment + * @note Before this function be called, the module must have been intialized + */ +void tls_lcd_seg_set(int com, int bit, int on_off); + +/** + * @brief Select the voltage of LCD module + * @param[in] vlcd This parameter can be one of the following values: + * - \ref VLCD27 + * - \ref VLCD29 + * - \ref VLCD31 + * - \ref VLCD33 + */ +void tls_lcd_vlcd_sel(LCD_VlcdDef vlcd); + +/** + * @brief Set the duty of LCD module + * @param[in] duty This parameter can be one of the following values: + * - \ref DUTY_STATIC + * - \ref DUTY_ONEHALF + * - \ref DUTY_ONETHIRD + * - \ref DUTY_ONEFOURTH + * - \ref DUTY_ONEFIFTH + * - \ref DUTY_ONESIXTH + * - \ref DUTY_ONESEVENTH + * - \ref DUTY_ONEEIGHTH + * + */ +void tls_lcd_duty_set(LCD_DutyDef duty); + +/** + * @brief Set the bias of LCD module + * @param[in] duty This parameter can be one of the following values: + * - \ref BIAS_STATIC + * - \ref BIAS_ONEHALF + * - \ref BIAS_ONETHIRD + * - \ref BIAS_ONEFOURTH + * + */ +void tls_lcd_bias_set(LCD_BiasDef bias); + +/** + * @brief Enable or disable the LCD module gpio + * + * @param[in] val Each bit of the value corresponds to a LCD gpio, set 1 gpio output enable ,0 output disabled + * + */ +void tls_lcd_io_config(unsigned int val); + +/** + * @brief Enable or disable clock of LCD module + * @param[in] enable When one enable the clock of LCD module, when zero disable + */ +#define TLS_LCD_CLK_ENABLE(enable) \ + do { \ + tls_bitband_write(HR_CLK_BASE_ADDR, HR_CLK_LCD_GATE_Pos, enable); \ + } while(0) + + +/** + * @brief Enable or disable the LCD module + * @param[in] enable When one enable the LCD module, when zero disable + * + */ +#define TLS_LCD_ENABLE(enable) \ + do { \ + tls_bitband_write(HR_LCD_CR, LCD_CR_EN_Pos, enable); \ + } while(0) + +/** + * @brief Enable or disable the LCD module + * @param[in] enable When one close LCD module, when zero open the LCD module + * + */ +#define TLS_LCD_POWERDOWM(enable) \ + do { \ + tls_bitband_write(HR_LCD_CR, LCD_CR_PD_Pos, enable); \ + } while(0) + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif + + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ diff --git a/sdk/Include/Driver/wm_pmu.h b/sdk/Include/Driver/wm_pmu.h new file mode 100644 index 0000000..b91d69d --- /dev/null +++ b/sdk/Include/Driver/wm_pmu.h @@ -0,0 +1,239 @@ +/** + * @file wm_pmu.h + * + * @brief pmu driver module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_PMU_H +#define WM_PMU_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "wm_type_def.h" + +typedef void (*tls_pmu_irq_callback)(void *arg); + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup PMU_Driver_APIs PMU Driver APIs + * @brief PMU driver APIs + */ + +/** + * @addtogroup PMU_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to register pmu timer1 interrupt + * + * @param[in] callback the pmu timer1 interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * pmu timer1 callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_pmu_timer1_isr_register(tls_pmu_irq_callback callback, void *arg); + + +/** + * @brief This function is used to register pmu timer0 interrupt + * + * @param[in] callback the pmu timer0 interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * pmu timer0 callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_pmu_timer0_isr_register(tls_pmu_irq_callback callback, void *arg); + + +/** + * @brief This function is used to register pmu gpio interrupt + * + * @param[in] callback the pmu gpio interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * pmu gpio callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_pmu_gpio_isr_register(tls_pmu_irq_callback callback, void *arg); + + +/** + * @brief This function is used to register pmu sdio interrupt + * + * @param[in] callback the pmu sdio interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * pmu sdio callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_pmu_sdio_isr_register(tls_pmu_irq_callback callback, void *arg); + + +/** + * @brief This function is used to select pmu clk + * + * @param[in] bypass pmu clk whether or not use bypass mode + * 1 pmu clk use 32K by 40MHZ + * other pmu clk 32K by calibration circuit + * + * @return None + * + * @note None + */ +void tls_pmu_clk_select(u8 bypass); + + +/** + * @brief This function is used to start pmu timer0 + * + * @param[in] second vlaue of timer0 count[s] + * + * @return None + * + * @note None + */ +void tls_pmu_timer0_start(u16 second); + + +/** + * @brief This function is used to stop pmu timer0 + * + * @param None + * + * @return None + * + * @note None + */ +void tls_pmu_timer0_stop(void); + + + +/** + * @brief This function is used to start pmu timer1 + * + * @param[in] second vlaue of timer1 count[ms] + * + * @return None + * + * @note None + */ +void tls_pmu_timer1_start(u16 msec); + + +/** + * @brief This function is used to stop pmu timer1 + * + * @param None + * + * @return None + * + * @note None + */ +void tls_pmu_timer1_stop(void); + + + +/** + * @brief This function is used to start pmu goto standby + * + * @param None + * + * @return None + * + * @note None + */ +void tls_pmu_standby_start(void); + + +typedef enum { + TLS_PERIPHERAL_TYPE_I2C = (1 << 0), + TLS_PERIPHERAL_TYPE_UART0 = (1 << 1), + TLS_PERIPHERAL_TYPE_UART1 = (1 << 2), + TLS_PERIPHERAL_TYPE_LSPI = (1 << 3), + TLS_PERIPHERAL_TYPE_DMA = (1 << 4), + TLS_PERIPHERAL_TYPE_RFCFG = (1 << 5), + TLS_PERIPHERAL_TYPE_TIMER = (1 << 6), + TLS_PERIPHERAL_TYPE_GPIO = (1 << 7), + TLS_PERIPHERAL_TYPE_SDADC = (1 << 8), + TLS_PERIPHERAL_TYPE_PWM = (1 << 9), + TLS_PERIPHERAL_TYPE_LCD = (1 << 10), + TLS_PERIPHERAL_TYPE_I2S = (1 << 11), + TLS_PERIPHERAL_TYPE_RSA = (1 << 12), + TLS_PERIPHERAL_TYPE_GPSEC = (1 << 13), + TLS_PERIPHERAL_TYPE_7816 = (1 << 14) +}tls_peripheral_type_s; + +/** + * @brief This function is used to close peripheral's clock + * + * @param[in] devices peripherals + * + * @return None + * + * @note None + */ +void tls_close_peripheral_clock(tls_peripheral_type_s devices); + +/** + * @brief This function is used to open peripheral's clock + * + * @param[in] devices peripherals + * + * @return None + * + * @note None + */ +void tls_open_peripheral_clock(tls_peripheral_type_s devices); + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* end of WM_PMU_H */ + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ + + diff --git a/sdk/Include/Driver/wm_pwm.h b/sdk/Include/Driver/wm_pwm.h new file mode 100644 index 0000000..93875fd --- /dev/null +++ b/sdk/Include/Driver/wm_pwm.h @@ -0,0 +1,415 @@ +/** + * @file wm_pwm.h + * + * @brief pwm driver module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_PWM_H +#define WM_PWM_H + +#include "wm_type_def.h" + + +/** pwm channel's maximum number */ +#define PWM_CHANNEL_MAX_NUM 5 + +/** pwm work mode for signal generate */ +enum tls_pwm_out_mode +{ + WM_PWM_OUT_MODE_BRAKE = 0, /**< brake mode */ + WM_PWM_OUT_MODE_ALLSYC, /**< all synchronous mode */ + WM_PWM_OUT_MODE_2SYC, /**< two channel synchronous mode */ + WM_PWM_OUT_MODE_MC, /**< complementary mode */ + WM_PWM_OUT_MODE_INDPT /**< independent mode */ +}; + +/** interrupt type for capture mode */ +enum tls_pwm_cap_int_type{ + WM_PWM_CAP_RISING_EDGE_INT, /**< rising edge arises the interrupt */ + WM_PWM_CAP_FALLING_EDGE_INT, /**< falling edge arises the interrupt */ + WM_PWM_CAP_RISING_FALLING_EDGE_INT, /**< both rising edge and falling edge arise the interrupt */ + WM_PWM_CAP_DMA_INT /**< dma request */ +}; + +/** pwm output status */ +enum tls_pwm_out_en_state{ + WM_PWM_OUT_EN_STATE_TRI, /**< set tristate status */ + WM_PWM_OUT_EN_STATE_OUT /**< set output status */ +}; + +/** pwm count mode */ +enum tls_pwm_cnt_type{ + WM_PWM_CNT_TYPE_EDGE_ALLGN_CAP, /**< edge alignment(only capture mode) */ + WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT, /**< edge alignment(only output mode) */ + WM_PWM_CNT_TYPE_CENTER_ALIGN /**< central alignment */ +}; + +/** pwm cycle type */ +enum tls_pwm_loop_type{ + WM_PWM_LOOP_TYPE_SINGLE, /**< single mode */ + WM_PWM_LOOP_TYPE_LOOP /**< auto load */ +}; + +/** pwm waveform inversion mode */ +enum tls_pwm_waveform_inversion{ + WM_PWM_WAVEFORM_NOINVERSION, /**< not inverse */ + WM_PWM_WAVEFORM_INVERSION /**< inversion */ +}; + +/** pwm output level in the brake mode */ +enum tls_pwm_brake_out_level{ + WM_PWM_BRAKE_OUT_HIGH, /**< output high level */ + WM_PWM_BRAKE_OUT_LOW /**< output low level */ +}; + +/** pwm initial parameters */ +typedef struct _pwm_init_param{ + enum tls_pwm_out_mode mode; /**< work mode */ + u8 channel; /**< channel id 0~4 */ + u16 clkdiv; /**< clock divided value */ + u8 period; /**< period value(output frequency F = CLK/CLK_DIV/PERIOD) */ + u8 duty; /**< duty radio (range 0~255, high level or low level by out_inversion decided */ + bool dten; /**< enable dead zone time (ENABLE or DISABLE) */ + u8 dtclkdiv; /**< dead zone clock divided value (0~3) */ + u8 dtcnt; /**< period number of dead zone time (0~255) */ + enum tls_pwm_cnt_type cnt_type; /**< count type */ + enum tls_pwm_loop_type loop_type; /**< cycle type */ + bool inverse_en; /**< output is inverse */ + u8 pnum; /**< generate interrupt after pnum period */ + bool pnum_int; /**< period interrupt is enable */ +}pwm_init_param; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup PWM_Driver_APIs PWM Driver APIs + * @brief PWM driver APIs + */ + +/** + * @addtogroup PWM_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to register the pwm interrupt callback function + * + * @param[in] callback the pwm interrupt callback function + * + * @return None + * + * @note None + */ +void tls_pwm_isr_register(void (*callback)(void)); + +/** + * @brief This function is used to set duty radio + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] duty Number of active levels + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_duty_config(u8 channel, u8 duty); + +/** + * @brief This function is used to set frequency + * + * @param[in] channel pwm channel NO., range form 0 to 4 + * @param[in] clkdiv clock divider, range 0 to 65535 + * @param[in] period the number of the counting clock cycle + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_freq_config(u8 channel, u16 clkdiv, u8 period); + +/** + * @brief This function is used to set the output mode + * + * @param[in] channel pwm channel NO., range form 0 to 4 + * @param[in] mode pwm work mode for signal generate + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_out_mode_config(u8 channel, enum tls_pwm_out_mode mode); + +/** + * @brief This function is used to set the counting mode + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] cnt_type counting mode + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_cnt_type_config(u8 channel, enum tls_pwm_cnt_type cnt_type); + +/** + * @brief This function is used to set whether to loop + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] loop_mode whether to loop + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_loop_mode_config(u8 channel, enum tls_pwm_loop_type loop_mode); + +/** + * @brief This function is used to set whether to inverse the output + + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] en ENABLE or DISABLE + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_out_inverse_cmd(u8 channel, bool en); + +/** + * @brief This function is used to set the number of period to be generated + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] pnum the number of period to be generated,range from 0 to 255 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_stoptime_by_period_config(u8 channel, u8 pnum); + +/** + * @brief This function is used to set output enable + * + * @param[in] channel pwm channel NO.,channel 0 or channel 4 + * @param[in] en ENABLE or DISABLE + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_output_en_cmd(u8 channel, bool en); + +/** + * @brief This function is used to set the dead time + * + * @param[in] channel pwm channel NO.,channel 0 or channel 2 + * @param[in] dten whether enalbe the dead time, ENABLE or DISABLE + * @param[in] dtclkdiv dead zone clock divider, range 0 to 3 + * @param[in] dtcnt the number of the counting clock cycle, range 0 to 255 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_deadzone_config(u8 channel, bool dten, u8 dtclkdiv, u8 dtcnt); + +/** + * @brief This function is used to set whether to inverse the capture input + * + * @param[in] channel pwm channel NO.,channel 0 or channel 4 + * @param[in] en ENABLE or DISABLE + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_capture_inverse_cmd(u8 channel, bool en); + +/** + * @brief This function is used to set break mode + * + * @param[in] channel pwm channel NO.,channel 0 or channel 4 + * @param[in] en whether enable the break mode,ENABLE or DISABLE + * @param[in] brok when break + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_brake_mode_config(u8 channel, bool en, enum tls_pwm_brake_out_level brok); + +/** + * @brief This function is used to enable the capture mode + * + * @param[in] channel pwm channel NO.,channel 0 or channel 4 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_capture_mode_config(u8 channel); + +/** + * @brief This function is used to set the interrupt about the number of period + * + * @param[in] channel pwm channel,range from 0 to 4 + * @param[in] en enble or disable + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_stoptime_irq_cmd(u8 channel, bool en); + +/** + * @brief This function is used to set the interrupt about the capture + * + * @param[in] channel pwm channel,channel 0 or channel 4 + * @param[in] int_type interrupt type + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_capture_irq_type_config(u8 channel, enum tls_pwm_cap_int_type int_type); + +/** + * @brief This function is used to initial pwm(out mode) + * + * @param[in] pwm_param structure containing the initialization parameters + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_out_init(pwm_init_param pwm_param); + +/** + * @brief This function is used to initial pwm(capture mode) + * + * @param[in] channel pwm channel, channel 0 or channel 4 + * @param[in] clkdiv clock divider, range 0 to 65535 + * @param[in] inverse_en whether the input signal is reversed + * @param[in] int_type interrupt type + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_cap_init(u8 channel, u16 clkdiv, bool inverse_en, enum tls_pwm_cap_int_type int_type); + +/** + * @brief This function is used to start pwm + * + * @param[in] channel pwm channel, range from 0 to 4 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_start(u8 channel); + +/** + * @brief This function is used to set pwm's frequency + * + * @param[in] channel pwm channel no, range form 0 to 4 + * @param[in] freq frequency, range from 1 to 156250 + * + * @return None + * + * @note None + */ +void tls_pwm_freq_set(u8 channel, u32 freq); + +/** + * @brief This function is used to set duty radio + * + * @param[in] channel pwm channel NO., range form 0 to 4 + * @param[in] duty duty radio, range from 0 to 255 + * + * @return None + * + * @note None + */ +void tls_pwm_duty_set(u8 channel, u8 duty); + +/** + * @brief This function is used to initial pwm + * + * @param[in] channel pwm channel, range from 0 to 4 + * @param[in] freq is a pointer to frequency, freq range from 1 to 156250 + * @param[in] duty is a pointer to duty radio, duty range from 0 to 255 + * @param[in] pnum period num,range from 0 to 255 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_init(u8 channel,u32 freq, u8 duty, u8 pnum); + +/** + * @brief This function is used to initial pwm + * + * @param[in] channel pwm channel, range from 0 to 4 + * @param[in] freq freq range from 1 to 156250 + * @param[in] duty duty range from 0 to 255 + * @param[in] pnum period num,range from 0 to 255 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_ineverse_init(u8 channel,u32 freq, u8 duty, u8 pnum); + +/** + * @brief This function is used to stop pwm + * + * @param[in] channel pwm channel, range from 0 to 4 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_stop(u8 channel); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_PWM_H */ diff --git a/sdk/Include/Driver/wm_rtc.h b/sdk/Include/Driver/wm_rtc.h new file mode 100644 index 0000000..513f21b --- /dev/null +++ b/sdk/Include/Driver/wm_rtc.h @@ -0,0 +1,109 @@ +/** + * @file wm_rtc.h + * + * @brief rtc Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_RTC_H +#define WM_RTC_H + +#include +#include "wm_type_def.h" + +/** rtc interrupt callback */ +typedef void (*tls_rtc_irq_callback)(void *arg); + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup RTC_Driver_APIs RTC Driver APIs + * @brief RTC driver APIs + */ + +/** + * @addtogroup RTC_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to set pmu rtc time + * + * @param[in] tblock time value + * + * @return None + * + * @note None + */ +void tls_set_rtc(struct tm *tblock); + +/** + * @brief This function is used to get pmu rtc time + * + * @param[out] tblock time value + * + * @return None + * + * @note None + */ +void tls_get_rtc(struct tm *tblock); + +/** + * @brief This function is used to register pmu rtc interrupt + * + * @param[in] callback the rtc interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * User does not need to clear the interrupt flag. + * Rtc callback function is called in interrupt, + * so do not operate the critical data in the callback fuuction. + * Sending messages to other tasks to handle is recommended. + */ +void tls_rtc_isr_register(tls_rtc_irq_callback callback, void *arg); + +/** + * @brief This function is used to start pmu rtc timer + * + * @param[in] tblock timer value + * + * @return None + * + * @note None + */ +void tls_rtc_timer_start(struct tm *tblock); + +/** + * @brief This function is used to stop pmu rtc timer + * + * @param None + * + * @return None + * + * @note This function also is used to clear rtc timer interrupt + */ +void tls_rtc_timer_stop(void); + + +/** + * @} + */ + +/** + * @} + */ + +#endif + diff --git a/sdk/Include/Driver/wm_timer.h b/sdk/Include/Driver/wm_timer.h new file mode 100644 index 0000000..f1ee993 --- /dev/null +++ b/sdk/Include/Driver/wm_timer.h @@ -0,0 +1,139 @@ +/** + * @file wm_timer.h + * + * @brief Timer Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_TIMER_H +#define WM_TIMER_H + +/** invalid timer id */ +#define WM_TIMER_ID_INVALID 0xFF +#define TIMER_MS_UNIT_FLAG 1 +#define TIMER_US_UNIT_FLAG 0 + +/** timer interrupt callback */ +typedef void (*tls_timer_irq_callback)(void *arg); + +/** timer unit */ +enum tls_timer_unit{ + TLS_TIMER_UNIT_US = 0, /**< microsecond level(us) */ + TLS_TIMER_UNIT_MS /**< millisecond level(ms) */ +}; + +/** timer configuration */ +struct tls_timer_cfg { + enum tls_timer_unit unit; /**< timer accuracy */ + u32 timeout; /**< timeout period */ + bool is_repeat; /**< cycle timer */ + tls_timer_irq_callback callback; /**< timeout callback function */ + void *arg; /**< parameter fot the timeout callback function */ +}; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup TIMER_Driver_APIs TIMER Driver APIs + * @brief TIMER driver APIs + */ + +/** + * @addtogroup TIMER_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to create a timer + * + * @param[in] cfg timer configuration + * + * @retval WM_TIMER_ID_INVALID failed + * @retval other timer id + * + * @note + * User does not need to clear the interrupt flag. + * Rtc callback function is called in interrupt, + * so do not operate the critical data in the callback fuuction. + * Sending messages to other tasks to handle is recommended. + */ +u8 tls_timer_create(struct tls_timer_cfg *cfg); + +/** + * @brief This function is used to start a timer + * + * @param[in] timer_id timer id + * + * @return None + * + * @note None + */ +void tls_timer_start(u8 timer_id); + +/** + * @brief This function is used to stop a timer + * + * @param[in] timer_id timer id + * + * @return None + * + * @note None + */ +void tls_timer_stop(u8 timer_id); + +/** + * @brief This function is used to change a timer wait time + * + * @param[in] timer_id timer id[0~5] + * + * @param[in] newtime new wait time + * + * @retval None + * + * @note If the timer does not start, this function will start the timer + */ +void tls_timer_change(u8 timer_id, u32 newtime); + +/** + * @brief This function is used to delete a timer + * + * @param[in] timer_id timer id + * + * @return None + * + * @note None + */ +void tls_timer_destroy(u8 timer_id); + +/** + * @brief This function is create a delay to elapse + * + * @param[timeout] timeout the value written into TMR0_PRD + * @param[in] m_flag millisecond flag in TIMER0 + * + * @return 0 + * + * @note None + */ +int tls_delay_via_timer(unsigned int timeout, unsigned int m_flag); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_TIMER_H */ + diff --git a/sdk/Include/Driver/wm_uart.h b/sdk/Include/Driver/wm_uart.h new file mode 100644 index 0000000..08f5dc7 --- /dev/null +++ b/sdk/Include/Driver/wm_uart.h @@ -0,0 +1,463 @@ +/** + * @file wm_uart.h + * + * @brief uart Driver Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_UART_H +#define WM_UART_H +#include "list.h" +//#include "wm_regs.h" +#include "wm_type_def.h" +#include "wm_osal.h" + +#define TLS_UART_RX_BUF_SIZE 4096 +#define TLS_UART_TX_BUF_SIZE 4096 +#define WAKEUP_CHARS 256 + +#define MBOX_MSG_UART_RX 1 +#define MBOX_MSG_UART_TX 2 + + +/** baud rate definition */ +#define UART_BAUDRATE_B600 600 +#define UART_BAUDRATE_B1200 1200 +#define UART_BAUDRATE_B1800 1800 +#define UART_BAUDRATE_B2400 2400 +#define UART_BAUDRATE_B4800 4800 +#define UART_BAUDRATE_B9600 9600 +#define UART_BAUDRATE_B19200 19200 +#define UART_BAUDRATE_B38400 38400 +#define UART_BAUDRATE_B57600 57600 +#define UART_BAUDRATE_B115200 115200 +#define UART_BAUDRATE_B230400 230400 +#define UART_BAUDRATE_B460800 460800 +#define UART_BAUDRATE_B921600 921600 +#define UART_BAUDRATE_B1000000 1000000 +#define UART_BAUDRATE_B1250000 1250000 +#define UART_BAUDRATE_B1500000 1500000 +#define UART_BAUDRATE_B2000000 2000000 + +#define UART_RX_INT_FLAG (UIS_RX_FIFO | UIS_RX_FIFO_TIMEOUT | UIS_BREAK |\ + UIS_OVERRUN | UIS_FRM_ERR | UIS_PARITY_ERR) +#define UART_RX_ERR_INT_FLAG (UIS_BREAK | UIS_FRM_ERR | \ + UIS_PARITY_ERR) + +#define UART_TX_INT_FLAG (UIS_TX_FIFO | UIS_TX_FIFO_EMPTY) + + +#ifndef UART0_INT +#define UART0_INT (23UL) +#endif +#ifndef UART1_INT +#define UART1_INT (24UL) +#endif + +/** return count in buffer. */ +#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1)) + +/** Return space available, 0..size-1. We always leave one free char + as a completely full buffer has head == tail, which is the same as + empty. */ +#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size)) + +/** Return count up to the end of the buffer. Carefully avoid + accessing head and tail more than once, so they can change + underneath us without returning inconsistent results. */ +#define CIRC_CNT_TO_END(head,tail,size) \ + ({int end = (size) - (tail); \ + int n = ((head) + end) & ((size)-1); \ + n < end ? n : end;}) + +/** Return space available up to the end of the buffer. */ +#define CIRC_SPACE_TO_END(head,tail,size) \ + ({int end = (size) - 1 - (head); \ + int n = (end + (tail)) & ((size)-1); \ + n <= end ? n : end+1;}) + +#define CIRC_SPACE_TO_END_FULL(head,tail,size) \ + ({int end = (size) - 1 - (head); \ + int n = (end + (tail)) & ((size)-1); \ + n < end ? n : end+1;}) + +#define uart_circ_empty(circ) ((circ)->head == (circ)->tail) +#define uart_circ_chars_pending(circ) \ + (CIRC_CNT((circ)->head, (circ)->tail, TLS_UART_TX_BUF_SIZE)) + +/** + * @struct tls_uart_baud_rate baudrate define + */ +struct tls_uart_baud_rate +{ + u32 baud_rate; + u16 ubdiv; + u16 ubdiv_frac; +}; + + +/** + * @enum uart number enum + */ +enum +{ + TLS_UART_0 = 0, + TLS_UART_1 = 1, + TLS_UART_2 = 2, +}; + + +/** + * @typedef enum TLS_UART_PMODE Parity Mode + */ +typedef enum TLS_UART_PMODE +{ + TLS_UART_PMODE_DISABLED = 0, /**< No Parity */ + TLS_UART_PMODE_ODD = 1, /**< Odd Parity */ + TLS_UART_PMODE_EVEN = 2, /**< Even Parity */ + TLS_UART_PMODE_MARK = 3, /**< The parity bit is always 1. */ + TLS_UART_PMODE_SPACE = 4, /**< The parity bit is always 0. */ +} TLS_UART_PMODE_T; + +/** + * @typedef enum TLS_UART_CHSIZE Character Size + */ +typedef enum TLS_UART_CHSIZE +{ + TLS_UART_CHSIZE_5BIT = (0x00 << 0), /**< Character size: 5 bit */ + TLS_UART_CHSIZE_6BIT = (0x01 << 0), /**< Character size: 6 bit */ + TLS_UART_CHSIZE_7BIT = (0x02 << 0), /**< Character size: 7 bit */ + TLS_UART_CHSIZE_8BIT = (0x03 << 0), /**< Character size: 8 bit */ +} TLS_UART_CHSIZE_T; + +/** + * @typedef enum TLS_UART_FLOW_CTRL_MODE flow control mode + */ +typedef enum TLS_UART_FLOW_CTRL_MODE +{ + TLS_UART_FLOW_CTRL_NONE, + TLS_UART_FLOW_CTRL_HARDWARE, +} TLS_UART_FLOW_CTRL_MODE_T; + +/** + * @typedef enum TLS_UART_RX_FLOW_CTRL_FLAG flow control rx flag + */ +typedef enum TLS_UART_RX_FLOW_CTRL_FLAG +{ + TLS_UART_RX_DISABLE, + TLS_UART_RX_ENABLE, +} TLS_UART_RX_FLOW_CTRL_FLAG_T; + +/** + * @typedef enum TLS_UART_STOPBITS + */ +typedef enum TLS_UART_STOPBITS +{ + TLS_UART_ONE_STOPBITS, + TLS_UART_TWO_STOPBITS, +} TLS_UART_STOPBITS_T; + + +/** + * @typedef enum TLS_UART_STATUS + */ +typedef enum TLS_UART_STATUS +{ + TLS_UART_STATUS_OK, + TLS_UART_STATUS_ERROR, +} TLS_UART_STATUS_T; + + +/** + * @typedef enum TLS_UART_MODE operation mode + */ +typedef enum TLS_UART_MODE +{ + TLS_UART_MODE_POLL, /**< uart operation mode: poll */ + TLS_UART_MODE_INT, /**< uart operation mode: interrupt mode */ +} TLS_UART_MODE_T; + +/** + * @struct tls_uart_icount + */ +struct tls_uart_icount +{ + u32 cts; + u32 dsr; + u32 rng; + u32 dcd; + u32 rx; + u32 tx; + u32 frame; + u32 overrun; + u32 parity; + u32 brk; + u32 buf_overrun; +}; + + + +/** + * @typedef struct tls_uart_options + */ +typedef struct tls_uart_options +{ + u32 baudrate; /**< Set baud rate of the UART */ + + TLS_UART_CHSIZE_T charlength; /**< Number of bits to transmit as a character (5 to 8). */ + + TLS_UART_PMODE_T paritytype; /**< Parity type */ + + TLS_UART_FLOW_CTRL_MODE_T flow_ctrl; /**< Flow control type */ + + TLS_UART_STOPBITS_T stopbits; /**< Number of stop bits */ + +} tls_uart_options_t; + + +/** + * @typedef struct tls_uart_circ_buf + */ +typedef struct tls_uart_circ_buf +{ + u8 *buf; + u32 head; + u32 tail; +} tls_uart_circ_buf_t; + +/** + * @typedef struct TLS_UART_REGS + */ +typedef struct TLS_UART_REGS +{ + u32 UR_LC; /**< line control register */ + u32 UR_FC; /**< flow control register */ + u32 UR_DMAC; /**< dma control register */ + u32 UR_FIFOC; /**< fifo control register */ + u32 UR_BD; /**< baud rate register */ + u32 UR_INTM; /**< interrupt mask register */ + u32 UR_INTS; /**< interrupt source register */ + u32 UR_FIFOS; /**< fifo status register */ + u32 UR_TXW; /**< tx windows register */ + u32 UR_RES0; + u32 UR_RES1; + u32 UR_RES2; + u32 UR_RXW; /**< rx windows register */ +} TLS_UART_REGS_T; + + +/** + * @typedef struct tls_uart_port + */ +typedef struct tls_uart_port +{ + u32 uart_no; /**< uart number: 0 or 1 */ + + u32 uart_irq_no; /**< uart interrupt number */ + + u32 plus_char_cnt; + + TLS_UART_MODE_T uart_mode; /**< uart work mode: interrupt mode or poll mode */ + + struct tls_uart_options opts; /**< uart config parameters */ + + int fcStatus; /**< flow ctrl status,0 closed ,1 opened */ + + enum TLS_UART_RX_FLOW_CTRL_FLAG rxstatus; + + u32 tx_fifofull; /**< uart tx fifo trigger level */ + + TLS_UART_REGS_T volatile *regs; /**< uart registers struct pointer */ + + struct tls_uart_icount icount; /**< uart statistics information */ + + struct tls_uart_circ_buf recv; /**< uart ring buffer */ + +// struct tls_uart_circ_buf xmit; + + struct dl_list tx_msg_pending_list; + + struct dl_list tx_msg_to_be_freed_list; + + u8 hw_stopped; + + tls_os_sem_t *tx_sem; + + char *buf_ptr; + + u16 buf_len; + + s16(*rx_callback) (u16 len); + + s16(*tx_callback) (struct tls_uart_port * port); + + bool tx_dma_on; +} tls_uart_port_t; + +/** + * @typedef struct tls_uart_tx_msg + */ +typedef struct tls_uart_tx_msg +{ + struct dl_list list; + char *buf; + u16 buflen; + u16 offset; + void (*finish_callback) (void *arg); + void *callback_arg; +} tls_uart_tx_msg_t; + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup UART_Driver_APIs UART Driver APIs + * @brief UART driver APIs + */ + +/** + * @addtogroup UART_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to initial uart port. + * + * @param[in] uart_no is the uart number. TLS_UART_0 or TLS_UART_1 + * @param[in] opts is the uart setting options,if this param is NULL,this function will use the default options. + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note When the system is initialized, the function has been called, so users can not call the function. + */ +int tls_uart_port_init(u16 uart_no, tls_uart_options_t * opts, u8 modeChoose); + + +/** + * @brief This function is used to register uart rx interrupt. + * + * @param[in] uart_no TLS_UART_0 or TLS_UART_1 + * @param[in] rx_callback is the uart rx interrupt call back function. + * + * @return None + * + * @note None + */ +void tls_uart_rx_callback_register(u16 uart_no, s16(*rx_callback) (u16 len)); + + +/** + * @brief This function is used to copy circular buffer data to user buffer. + * + * @param[in] uart_no is the uart numer + * @param[in] buf is the user buffer + * @param[in] readsize is the user read size + * + * @retval copy data size + * + * @note None + */ +int tls_uart_read(u16 uart_no, u8 * buf, u16 readsize); + + +/** + * @brief This function is used to transfer data synchronously. + * + * @param[in] uart_no is the uart number + * @param[in] buf is a buf for saving user data + * @param[in] writesize is the user data length + * + * @retval WM_SUCCESS tx success + * @retval WM_FAILED tx failed + * + * @note None + */ +int tls_uart_write(u16 uart_no, char *buf, u16 writesize); + + +/** + * @brief This function is used to transfer data with DMA. + * + * @param[in] buf is a buf for saving user data + * @param[in] writesize is the user data length + * @param[in] cmpl_callback function point,when the transfer is completed, the function will be called. + * @param[in] uart_no is the uart number + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note Only uart1 support DMA transfer. + */ +int tls_uart_dma_write(char *buf, u16 writesize, void (*cmpl_callback) (void *p), u16 uart_no); + + +/** + * @brief This function is used to set uart parity. + * + * @param[in] uart_no is the uart number + * @param[in] paritytype is a parity type defined in TLS_UART_PMODE_T + * + * @retval WM_SUCCESS if setting success + * @retval WM_FAILED if setting fail + * + * @note None + */ +int tls_uart_set_parity(u16 uart_no, TLS_UART_PMODE_T paritytype); + + +/** + * @brief This function is used to set uart baudrate. + * + * @param[in] uart_no is the uart number + * @param[in] baudrate is the baudrate user want used,the unit is HZ. + * + * @retval WM_SUCCESS if setting success + * @retval WM_FAILED if setting fail + * + * @note None + */ +int tls_uart_set_baud_rate(u16 uart_no, u32 baudrate); + +/** + * @brief This function is used to set uart stop bits. + * + * @param[in] uart_no is the uart number + * @param[in] stopbits is a stop bit type defined in TLS_UART_STOPBITS_T + * + * @retval WM_SUCCESS if setting success + * @retval WM_FAILED if setting fail + * + * @note None + */ +int tls_uart_set_stop_bits(u16 uart_no, TLS_UART_STOPBITS_T stopbits); + +/** + * @brief This function is used to output a character to the serial port. + * + * @param[in] ch character to be output + * + * @retval ch + * + * @note None + */ +int tls_uart_output_char(int ch); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_UART_H */ diff --git a/sdk/Include/Driver/wm_watchdog.h b/sdk/Include/Driver/wm_watchdog.h new file mode 100644 index 0000000..5e37e8e --- /dev/null +++ b/sdk/Include/Driver/wm_watchdog.h @@ -0,0 +1,99 @@ +/** + * @file wm_watchdog.h + * + * @brief watchdog Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_WATCHDOG_H +#define WM_WATCHDOG_H + +/** + * @defgroup Driver_APIs Driver APIs + * @brief Driver APIs + */ + +/** + * @addtogroup Driver_APIs + * @{ + */ + +/** + * @defgroup WDG_Driver_APIs WDG Driver APIs + * @brief WDG driver APIs + */ + +/** + * @addtogroup WDG_Driver_APIs + * @{ + */ + +/** + * @brief This function is used to clear watchdog irq in case watchdog reset. + * + * @param None + * + * @return None + * + * @note None + */ +void tls_watchdog_clr(void); + +/** + * @brief This function is used to init and start the watchdog. + * + * @param[in] usec microseconds + * + * @return None + * + * @note None + */ +void tls_watchdog_init(u32 usec); + +/** + * @brief This function is used to start calculating elapsed time. + * + * @param[in] None + * + * @return elapsed time, unit:millisecond + * + * @note None + */ +void tls_watchdog_start_cal_elapsed_time(void); + + +/** + * @brief This function is used to stop calculating & return elapsed time. + * + * @param[in] none + * + * @return elapsed time, unit:millisecond + * + * @note None + */ +u32 tls_watchdog_stop_cal_elapsed_time(void); + + +/** + * @brief This function is used to reset the system. + * + * @param None + * + * @return None + * + * @note None + */ +void tls_sys_reset(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_WATCHDOG_H */ + diff --git a/sdk/Include/Net/wm_socket.h b/sdk/Include/Net/wm_socket.h new file mode 100644 index 0000000..d060d84 --- /dev/null +++ b/sdk/Include/Net/wm_socket.h @@ -0,0 +1,17 @@ +/** + * @file wm_socket.h + * + * @brief socket Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_SOCKET_H +#define WM_SOCKET_H +#include "wm_config.h" +#include "wm_socket2.0.3.h" + + +#endif diff --git a/sdk/Include/Net/wm_socket2.0.3.h b/sdk/Include/Net/wm_socket2.0.3.h new file mode 100644 index 0000000..d35e153 --- /dev/null +++ b/sdk/Include/Net/wm_socket2.0.3.h @@ -0,0 +1,422 @@ +/** + * @file wm_socket2.0.3.h + * + * @brief socket203 Module + * + * @author dave + * + * @copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_SOCKET2_0_3_H +#define WM_SOCKET2_0_3_H + +#include "wm_type_def.h" +#include "wm_netif.h" + +//socket state defination +#define NETCONN_STATE_NONE 0 +#define NETCONN_STATE_WAITING 1 +#define NETCONN_STATE_CONNECTED 2 +#define NETCONN_STATE_CLOSED 3 + +//socket event defination +#define NET_EVENT_TCP_JOINED 0 +#define NET_EVENT_TCP_DISCONNECT 1 +#define NET_EVENT_TCP_CONNECTED 2 +#define NET_EVENT_TCP_CONNECT_FAILED 3 +#define NET_EVENT_UDP_START 4 +#define NET_EVENT_UDP_START_FAILED 5 + +#define TLS_MAX_SOCKET_NUM 8 +#define TLS_MAX_NETCONN_NUM 20 + +/** Main packet buffer struct */ +struct pbuf { + /** next pbuf in singly linked pbuf chain */ + struct pbuf *next; + + /** pointer to the actual data in the buffer */ + void *payload; + + /** + * total length of this buffer and all next buffers in chain + * belonging to the same packet. + * + * For non-queue packet chains this is the invariant: + * p->tot_len == p->len + (p->next? p->next->tot_len: 0) + */ + u16_t tot_len; + + /** length of this buffer */ + u16_t len; + + /** pbuf_type as u8_t instead of enum to save space */ + u8_t /*pbuf_type*/ type; + + /** misc flags */ + u8_t flags; + + /** + * the reference count always equals the number of pointers + * that refer to this pbuf. This can be pointers from an application, + * the stack itself, or pbuf->next pointers from a chain. + */ + u16_t ref; +}; + +/** +* @brief This Function prototype for tcp error callback functions. Called when +* receives a RST or is unexpectedly closed for any other reason. +* The corresponding socket is already freed when this callback is called! +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @param[in] err Error code to indicate why the socket has been closed +* ERR_ABRT: aborted through returning ERR_ABRT from within others +* callback functions +* ERR_RST: the connection was reset by the remote host +*/ +typedef void (*socket_err_fn)(u8 skt_num, err_t err); + +/** +* @brief This Function prototype for socket receive callback functions. Called when data has +* been received. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @param[in] p The received data (or NULL when the connection has been closed!) +* +* @param[in] err An error code if there has been an error receiving, always be ERR_OK +* when cs mode is udp. +* +* @retval The return value is only valid for tcp receive, for upd it means nothing. +* ERR_OK: Return this value after handling the received data. +* ERR_ABRT: Only return ERR_ABRT if you want to abort the socket from within the +* callback function! +*/ +typedef err_t (*socket_recv_fn)(u8 skt_num, struct pbuf *p, err_t err); + +/** +* @brief This Function prototype for socket srce ip callback functions. Called when data has +* been received. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @param[in] datalen The received data length +* +* @param[in] ipsrc source ip addr +* +* @param[in] port source port +* +* @param[in] err An error code if there has been an error receiving, always be ERR_OK +* when cs mode is udp. +* +* @retval The return value is only valid for UDP receive, for udp it means nothing. +* ERR_OK: Return this value after handling the received data. +* ERR_ABRT: Only return ERR_ABRT if you want to abort the socket from within the +* callback function! +*/ +typedef err_t (*socket_recv_ip_rpt_fn)(u8 skt_num, u16 datalen, u8 *ipsrc, u16 port, err_t err); + +/** +* @brief This Function prototype for tcp connected callback functions. Called when +* connected to the remote side. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @param[in] err An unused error code, always ERR_OK currently. +* +* @retval ERR_OK: Return this value after handling your logic. +* @retval ERR_ABRT: Only return ERR_ABRT if you want to abort the socket from within the +* callback function! +*/ +typedef err_t (*socket_connected_fn)(u8 skt_num, err_t err); + +/** +* @brief This Function prototype for tcp poll callback functions. Called periodically. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @retval ERR_OK: Try to do something periodically. +* @retval ERR_ABRT: Only return ERR_ABRT if you want to abort the socket from within the +* callback function! +*/ +typedef err_t (*socket_poll_fn)(u8 skt_num); + +/** +* @brief This Function prototype for tcp accept callback functions. Called when a new +* connection can be accepted on a listening tcp. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @param[in] err An error code if there has been an error accepting. +* +* @retval ERR_OK: Return this value after handling your logic. +* @retval ERR_ABRT: Only return ERR_ABRT if you want to abort the socket from within the +* callback function! +*/ +typedef err_t (*socket_accept_fn)(u8 skt_num, err_t err); + +/** +* @brief This Function prototype for socket state changed callback functions. Called when socket +* the sockte's state changed. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @param[in] event Is the event number, see socket event defination. +* +* @param[in] state Is the socket state, see socket state defination. +*/ +typedef void(*socket_state_changed_fn)(u8 skt_num, u8 event, u8 state); + +/** Definitions for error constants. */ +typedef enum { +/** No error, everything OK. */ + ERR_OK = 0, +/** Out of memory error. */ + ERR_MEM = -1, +/** Buffer error. */ + ERR_BUF = -2, +/** Timeout. */ + ERR_TIMEOUT = -3, +/** Routing problem. */ + ERR_RTE = -4, +/** Operation in progress */ + ERR_INPROGRESS = -5, +/** Illegal value. */ + ERR_VAL = -6, +/** Operation would block. */ + ERR_WOULDBLOCK = -7, +/** Address in use. */ + ERR_USE = -8, +/** Already connecting. */ + ERR_ALREADY = -9, +/** Conn already established.*/ + ERR_ISCONN = -10, +/** Not connected. */ + ERR_CONN = -11, +/** Low-level netif error */ + ERR_IF = -12, + +/** Connection aborted. */ + ERR_ABRT = -13, +/** Connection reset. */ + ERR_RST = -14, +/** Connection closed. */ + ERR_CLSD = -15, +/** Illegal argument. */ + ERR_ARG = -16 +} err_enum_t; + +enum tls_socket_protocol{ + SOCKET_PROTO_TCP, /* TCP Protocol */ + SOCKET_PROTO_UDP, /* UDP Protocol */ +}; + +enum tls_socket_cs_mode{ + SOCKET_CS_MODE_CLIENT, /* Client mode */ + SOCKET_CS_MODE_SERVER, /* Server mode */ +}; + +struct tls_socket_desc { + enum tls_socket_cs_mode cs_mode; /* Server mode or Client mode, Only for tcp protocol is valid */ + enum tls_socket_protocol protocol; /* TCP Protocol or UDP Protocol */ + ip_addr_t ip_addr; /* Remote ip address, for tcp client mode is remote server's ip address; for tcp server mode can be any address. */ + /* for udp is remote server's ip address */ + u16 port; /* port, for tcp client mode is remote server's port; for tcp server mode is local listen port . + for udp is remote server's port */ + u16 localport; /* local port, for udp and tcp client is local listen port, for tcp server means nothing, tcp server always listen at port */ + char host_name[32]; /* remote host name, not support for now */ + u8 host_len; /* the length of host name */ + u32 timeout; /* poll timeout, not implemented for now */ + socket_err_fn errf; /* a pointer to socket_err_fn */ + socket_recv_fn recvf; /* a pointer to socket_recv_fn */ + socket_connected_fn connf; /* a pointer to socket_connected_fn */ + socket_poll_fn pollf; /* a pointer to socket_poll_fn */ + socket_accept_fn acceptf; /* a pointer to socket_accept_fn */ + socket_state_changed_fn state_changed; /* a pointer to socket_state_changed_fn */ + socket_recv_ip_rpt_fn recvwithipf; /*recv skt info report*/ +}; + +/** +* @brief This function is called by your application code to create a socket. +* +* @param[in] skd Is a pointer to an tls_socket_desc. +* +* @retval ERR_OK If create socket successfully. +* negative number If an error was detected. +*/ +int tls_socket_create(struct tls_socket_desc * skd); + +/** +* @brief This function is called by your application code to send data by the socket. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @param[in] pdata Is a pointer to the data which need to be send by the socket. +* +* @param[in] len The data's length. +* +* @retval ERR_OK If send data successfully. +* negative number If an error was detected. +*/ +int tls_socket_send(u8 skt_num, void *pdata, u16 len); + +/** +* @brief This function is called by your application code to close the socket, and the related resources would be released. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @retval ERR_OK If close socket successfully. +* negative number If an error was detected. +*/ +int tls_socket_close(u8 skt_num); + +struct tls_skt_status_ext_t { + u8 socket; + u8 status; + enum tls_socket_protocol protocol; + u8 host_ipaddr[4]; + u16 remote_port; + u16 local_port; +}; + +struct tls_skt_status_t { + u32 socket_cnt; + struct tls_skt_status_ext_t skts_ext[1]; +}; +/** +* @brief This function is called by your application code to get the socket status of specified socket num. +* +* @param[in] skt_num Is the socket number that returned by tls_socket_create function. +* +* @param[in] buf Is a pointer to the data contains the socket status, if the socket is server, also contains it's client's status. +* +* @param[in] len The buf's length. At least, the len should be bigger than sizeof(struct tls_skt_status_t). +* +* @retval ERR_OK If send data successfully. +* negative number If an error was detected. +*/ +int tls_socket_get_status(u8 skt_num, u8 *buf, u32 bufsize); + +/** +* @brief This function is called by your application code to send data by udp socket. +* +* @param[in] localport This function will search all created sockets, if there is a socket whose localport equals this value and it's protocol is udp, +* then send the data by this socket, otherwise, nothing to send. +* +* @param[in] ip_addr Is the remote ip address. +* +* @param[in] port Is the remote port which upd send to. +* +* @param[in] pdata Is a pointer to the data which need to be send by the socket. +* +* @param[in] len The data's length. +* @retval ERR_OK If send data successfully. +* negative number If an error was detected. +*/ +int tls_socket_udp_sendto(u16 localport, u8 *ip_addr, u16 port, void *pdata, u16 len); + +/** + * @ingroup pbuf + * Enumeration of pbuf layers + */ +typedef enum { + /** Includes spare room for transport layer header, e.g. UDP header. + * Use this if you intend to pass the pbuf to functions like udp_send(). + */ + PBUF_TRANSPORT, + /** Includes spare room for IP header. + * Use this if you intend to pass the pbuf to functions like raw_send(). + */ + PBUF_IP, + /** Includes spare room for link layer header (ethernet header). + * Use this if you intend to pass the pbuf to functions like ethernet_output(). + * @see PBUF_LINK_HLEN + */ + PBUF_LINK, + /** Includes spare room for additional encapsulation header before ethernet + * headers (e.g. 802.11). + * Use this if you intend to pass the pbuf to functions like netif->linkoutput(). + * @see PBUF_LINK_ENCAPSULATION_HLEN + */ + PBUF_RAW_TX, + /** Use this for input packets in a netif driver when calling netif->input() + * in the most common case - ethernet-layer netif driver. */ + PBUF_RAW +} pbuf_layer; + +/** + * @ingroup pbuf + * Enumeration of pbuf types + */ +typedef enum { + /** pbuf data is stored in RAM, used for TX mostly, struct pbuf and its payload + are allocated in one piece of contiguous memory (so the first payload byte + can be calculated from struct pbuf). + pbuf_alloc() allocates PBUF_RAM pbufs as unchained pbufs (although that might + change in future versions). + This should be used for all OUTGOING packets (TX).*/ + PBUF_RAM, + /** pbuf data is stored in ROM, i.e. struct pbuf and its payload are located in + totally different memory areas. Since it points to ROM, payload does not + have to be copied when queued for transmission. */ + PBUF_ROM, + /** pbuf comes from the pbuf pool. Much like PBUF_ROM but payload might change + so it has to be duplicated when queued before transmitting, depending on + who has a 'ref' to it. */ + PBUF_REF, + /** pbuf payload refers to RAM. This one comes from a pool and should be used + for RX. Payload can be chained (scatter-gather RX) but like PBUF_RAM, struct + pbuf and its payload are allocated in one piece of contiguous memory (so + the first payload byte can be calculated from struct pbuf). + Don't use this for TX, if the pool becomes empty e.g. because of TCP queuing, + you are unable to receive TCP acks! */ + PBUF_POOL +} pbuf_type; + +/** +* @brief This Function allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). +* +* The actual memory allocated for the pbuf is determined by the +* layer at which the pbuf is allocated and the requested size +* (from the size parameter). +* +* @param[in] l layer flag to define header size +* @param[in] length size of the pbuf's payload +* @param[in] type this parameter decides how and where the pbuf +* +* @retval The allocated pbuf. If multiple pbufs where allocated, this +* is the first pbuf of a pbuf chain. +*/ +struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type); + +/** +* @brief This Function for release the buffer that you receive within the socket_recv_fn callback function. +* Attention please: If you return ERR_OK in the socket_recv_fn callback function, you must call this +* function to release the buffer by yourself. Otherwise, the buffer do not need be +* released by your code. +* +* @param[in] p The buffer you received in the socket_recv_fn callback function. +* +* @retval The number of de-allocated pbufs +*/ +u8 pbuf_free(struct pbuf *p); + +/** +* @brief This Function for copy (part of) the contents of a packet buffer to an application supplied buffer. +* +* @param[in] p the pbuf from which to copy data. +* +* @param[in] dataptr the application supplied buffer +* +* @param[in] len length of data to copy (dataptr must be big enough). No more +* than buf->tot_len will be copied, irrespective of len +* +* @param[in] offset offset into the packet buffer from where to begin copying len bytes +* +* @retval The number of bytes copied, or 0 on failure +*/ +u16_t pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +#endif + diff --git a/sdk/Include/Net/wm_sockets.h b/sdk/Include/Net/wm_sockets.h new file mode 100644 index 0000000..fa54581 --- /dev/null +++ b/sdk/Include/Net/wm_sockets.h @@ -0,0 +1,15 @@ +/** + * @file wm_sockets.h + * + * @brief socket apis + * + * @author winnermicro + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_SOCKET_API_H +#define WM_SOCKET_API_H +#include "wm_config.h" +#include "wm_sockets2.0.3.h" +#endif + diff --git a/sdk/Include/Net/wm_sockets2.0.3.h b/sdk/Include/Net/wm_sockets2.0.3.h new file mode 100644 index 0000000..aa2376c --- /dev/null +++ b/sdk/Include/Net/wm_sockets2.0.3.h @@ -0,0 +1,1174 @@ +/** + * @file wm_sockets2.0.3.h + * + * @brief sockets2.0.3 apis + * + * @author winnermicro + * + * @copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_SOCKET_API2_0_3_H +#define WM_SOCKET_API2_0_3_H + +#include +#include "wm_type_def.h" +#include "wm_config.h" + +/** If your port already typedef's sa_family_t, define SA_FAMILY_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(sa_family_t) && !defined(SA_FAMILY_T_DEFINED) +typedef u8_t sa_family_t; +#endif +/** If your port already typedef's in_port_t, define IN_PORT_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(in_port_t) && !defined(IN_PORT_T_DEFINED) +typedef u16_t in_port_t; +#endif + +/** If your port already typedef's in_addr_t, define IN_ADDR_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(in_addr_t) && !defined(IN_ADDR_T_DEFINED) +typedef u32_t in_addr_t; +#endif + +struct in_addr { + in_addr_t s_addr; +}; + +struct in6_addr { + union { + u32_t u32_addr[4]; + u8_t u8_addr[16]; + } un; +#define s6_addr un.u8_addr +}; + +/** 255.255.255.255 */ +#define IPADDR_NONE ((u32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((u32_t)0x00000000UL) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ((u32_t)0xffffffffUL) + +/** 255.255.255.255 */ +#define INADDR_NONE IPADDR_NONE +/** 127.0.0.1 */ +#define INADDR_LOOPBACK IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define INADDR_ANY IPADDR_ANY +/** 255.255.255.255 */ +#define INADDR_BROADCAST IPADDR_BROADCAST + +/** This macro can be used to initialize a variable of type struct in6_addr + to the IPv6 wildcard address. */ +#define IN6ADDR_ANY_INIT {{{0,0,0,0}}} +/** This macro can be used to initialize a variable of type struct in6_addr + to the IPv6 loopback address. */ +#define IN6ADDR_LOOPBACK_INIT {{{0,0,0,PP_HTONL(1)}}} +/** This variable is initialized by the system to contain the wildcard IPv6 address. */ +extern const struct in6_addr in6addr_any; + +#if TLS_CONFIG_IPV4 +/** members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; +#define SIN_ZERO_LEN 8 + char sin_zero[SIN_ZERO_LEN]; +}; +#endif /* TLS_CONFIG_IPV4 */ + +#if TLS_CONFIG_IPV6 +struct sockaddr_in6 { + u8_t sin6_len; /* length of this structure */ + sa_family_t sin6_family; /* AF_INET6 */ + in_port_t sin6_port; /* Transport layer port # */ + u32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + u32_t sin6_scope_id; /* Set of interfaces for scope */ +}; +#endif /* TLS_CONFIG_IPV6 */ + +struct sockaddr { + u8_t sa_len; + sa_family_t sa_family; + char sa_data[14]; +}; + +struct sockaddr_storage { + u8_t s2_len; + sa_family_t ss_family; + char s2_data1[2]; + u32_t s2_data2[3]; +#if TLS_CONFIG_IPV6 + u32_t s2_data3[3]; +#endif /* TLS_CONFIG_IPV6 */ +}; + +struct hostent { + char *h_name; /* Official name of the host. */ + char **h_aliases; /* A pointer to an array of pointers to alternative host names, + terminated by a null pointer. */ + int h_addrtype; /* Address type. */ + int h_length; /* The length, in bytes, of the address. */ + char **h_addr_list; /* A pointer to an array of pointers to network addresses (in + network byte order) for the host, terminated by a null pointer. */ +#define h_addr h_addr_list[0] /* for backward compatibility */ +}; + +struct sockaddr_store { + u8_t s2_len; + sa_family_t ss_family; + char s2_data1[2]; + u32_t s2_data2[3]; +#if TLS_CONFIG_IPV6 + u32_t s2_data3[3]; +#endif /* TLS_CONFIG_IPV6 */ +}; + +/** If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef u32_t socklen_t; +#endif + +struct lwip_sock; + +/** Socket protocol types (TCP/UDP/RAW) */ +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +/** + * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c) + */ +#define SO_REUSEADDR 0x0004 /* Allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ + + +/** + * Additional options, not kept in so_options. + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_DONTLINGER ((int)(~SO_LINGER)) +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ +#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ +#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ +#define SO_NO_CHECK 0x100a /* don't create UDP checksum */ + +/** + * Structure used for manipulating linger option. + */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time in seconds */ +}; + +/** + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xfff /* options for socket level */ + + +#define AF_UNSPEC 0 +#define AF_INET 2 +#if TLS_CONFIG_IPV6 +#define AF_INET6 10 +#else /* TLS_CONFIG_IPV6 */ +#define AF_INET6 AF_UNSPEC +#endif /* TLS_CONFIG_IPV6 */ +#define PF_INET AF_INET +#define PF_INET6 AF_INET6 +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_ICMP 1 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#if TLS_CONFIG_IPV6 +#define IPPROTO_IPV6 41 +#define IPPROTO_ICMPV6 58 +#endif /* TLS_CONFIG_IPV6 */ +#define IPPROTO_UDPLITE 136 +#define IPPROTO_RAW 255 + +/** Flags we can use with send and recv. */ +#define MSG_PEEK 0x01 /* Peeks at an incoming message */ +#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */ +#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */ +#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */ +#define MSG_MORE 0x10 /* Sender will send more */ + + +/** + * Options for level IPPROTO_IP + */ +#define IP_TOS 1 +#define IP_TTL 2 + + +/* + * Options for level IPPROTO_TCP + */ +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */ +#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ +#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ +#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ + +#if TLS_CONFIG_IPV6 +/** + * Options for level IPPROTO_IPV6 + */ +#define IPV6_CHECKSUM 7 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */ +#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ +#endif /* TLS_CONFIG_IPV6 */ + +/** + * Options for level IPPROTO_UDPLITE + */ +#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ + +#if TLS_CONFIG_IGMP +/** + * Options and types for UDP multicast traffic handling + */ +#define IP_MULTICAST_TTL 5 +#define IP_MULTICAST_IF 6 +#define IP_MULTICAST_LOOP 7 +#endif /* TLS_CONFIG_IGMP */ + +#if 1// TLS_CONFIG_IGMP +/** + * Options and types related to multicast membership + */ +#define IP_ADD_MEMBERSHIP 3 +#define IP_DROP_MEMBERSHIP 4 + +typedef struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ip_mreq; + +#if TLS_CONFIG_IPV6 +#define IPV6_JOIN_GROUP 38 +#define IPV6_LEAVE_GROUP 39 +typedef struct ipv6_mreq { + struct in6_addr ipv6mr_multiaddr; /* IP multicast address of group */ + int ipv6mr_interface; /* index of interface */ +} ipv6_mreq; +#endif +#endif /* TLS_CONFIG_IGMP */ + +/** + * @brief The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * @brief The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. + */ +#define IPTOS_PREC_MASK 0xe0 +#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* + * @brief Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * lwip_ioctl only supports FIONREAD and FIONBIO, for now + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#if !defined(FIONREAD) || !defined(FIONBIO) +#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000UL /* no parameters */ +#define IOC_OUT 0x40000000UL /* copy out parameters */ +#define IOC_IN 0x80000000UL /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) +#endif /* !defined(FIONREAD) || !defined(FIONBIO) */ + +#ifndef FIONREAD +#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ +#endif +#ifndef FIONBIO +#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ +#endif + +/** Socket I/O Controls: unimplemented */ +#ifndef SIOCSHIWAT +#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ +#endif + +/** commands for fnctl */ +#ifndef F_GETFL +#define F_GETFL 3 +#endif +#ifndef F_SETFL +#define F_SETFL 4 +#endif + +/** File status flags and file access modes for fnctl, + these are bits in an int. */ +#ifndef O_NONBLOCK +#define O_NONBLOCK 1 /* nonblocking I/O */ +#endif +#ifndef O_NDELAY +#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */ +#endif + +#ifndef SHUT_RD + #define SHUT_RD 0 + #define SHUT_WR 1 + #define SHUT_RDWR 2 +#endif + +/** FD_SET used for lwip_select */ +#ifndef FD_SET +#undef FD_SETSIZE + +#ifndef LWIP_SOCKET_OFFSET +#define LWIP_SOCKET_OFFSET 0 +#endif + +#ifndef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 20 +#endif + +/** Make FD_SETSIZE match NUM_SOCKETS in socket.c */ +#define FD_SETSIZE MEMP_NUM_NETCONN +#define FDSETSAFESET(n, code) do { \ + if (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0)) { \ + code; }} while(0) +#define FDSETSAFEGET(n, code) (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0) ?\ + (code) : 0) +#define FD_SET(n, p) FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] |= (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define FD_CLR(n, p) FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] &= ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define FD_ISSET(n,p) FDSETSAFEGET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] & (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p))) + +typedef struct fd_set +{ + unsigned char fd_bits [(FD_SETSIZE+7)/8]; +} fd_set; + +#elif LWIP_SOCKET_OFFSET +#error LWIP_SOCKET_OFFSET does not work with external FD_SET! +#endif /* FD_SET */ + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#ifndef LWIP_TIMEVAL_PRIVATE +#define LWIP_TIMEVAL_PRIVATE 1 +#endif + +#if LWIP_TIMEVAL_PRIVATE +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* LWIP_TIMEVAL_PRIVATE */ +int accept(int s, struct sockaddr *addr, socklen_t *addrlen); + +int bind(int s, const struct sockaddr *name, socklen_t namelen); + +int shutdown(int s, int how); + +int closesocket(int s); + +int connect(int s, const struct sockaddr *name, socklen_t namelen); + +int getsockname(int s, struct sockaddr *name, socklen_t *namelen); + +int getpeername(int s, struct sockaddr *name, socklen_t *namelen); + +int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen); + +int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen); + +int listen(int s, int backlog); + +int recv(int s, void *mem, size_t len, int flags); + +int recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen); + +int send(int s, const void *data, size_t size, int flags); + +int sendto(int s, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen); + +int socket(int domain, int type, int protocol); + +int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout); + +int ioctlsocket(int s, long cmd, void *argp); + +int fcntl(int s, int cmd, int val); + +struct hostent* gethostbyname(const char *name); + +/** @ingroup socket */ +#define read(s,mem,len,fl) recv(s,mem,len,fl) +/** @ingroup socket */ +#define write(s,dataptr,len,fl) send(s,dataptr,len,fl) +/** @ingroup socket */ +#define close(s) closesocket(s) +/** @ingroup socket */ +#define ioctl(s,cmd,argp) ioctlsocket(s,cmd,argp) + +u32_t ipaddr_addr(const char *cp); + +#ifdef htonl +#undef htonl +#endif /* htonl */ +#ifdef ntohl +#undef ntohl +#endif /* ntohl */ +#ifdef htons +#undef htons +#endif /* htons */ +#ifdef ntohs +#undef ntohs +#endif /* ntohs */ + +#define htons(n) (((n & 0xff) << 8) | ((n & 0xff00) >> 8)) +#define htonl(n) (((n & 0xff) << 24) |\ + ((n & 0xff00) << 8) |\ + ((n & 0xff0000UL) >> 8) |\ + ((n & 0xff000000UL) >> 24)) +#define ntohs(n) htons(n) +#define ntohl(n) htonl(n) + +/** Create u32_t value from bytes */ +#define LWIP_MAKEU32(a,b,c,d) (((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff)) +#define PP_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \ + (((x) & 0x0000ff00UL) << 8) | \ + (((x) & 0x00ff0000UL) >> 8) | \ + (((x) & 0xff000000UL) >> 24)) + +#if TLS_CONFIG_IPV4 +/** This is the aligned version of ip4_addr_t, + used as local variable, on the stack, etc. */ +struct ip4_addr { + u32_t addr; +}; + +/** ip4_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip4_addr_t as well as on ip4_addr_p_t. */ +typedef struct ip4_addr ip4_addr_t; + +/** Set an IP address given by the four byte-parts */ +#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->addr = PP_HTONL(LWIP_MAKEU32(a,b,c,d)) + +/** MEMCPY-like copying of IP addresses where addresses are known to be + * 16-bit-aligned if the port is correctly configured (so a port could define + * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */ +#ifndef IPADDR2_COPY +#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip4_addr_t)) +#endif + +/** Copy IP address - faster than ip4_addr_set: no NULL check */ +#define ip4_addr_copy(dest, src) ((dest).addr = (src).addr) +/** Safely copy one IP address to another (src may be NULL) */ +#define ip4_addr_set(dest, src) ((dest)->addr = \ + ((void *)(src) == NULL ? 0 : \ + (src)->addr)) +/** Set complete address to zero */ +#define ip4_addr_set_zero(ipaddr) ((ipaddr)->addr = 0) +/** Set address to IPADDR_ANY (no need for lwip_htonl()) */ +#define ip4_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY) +/** Set address to loopback address */ +#define ip4_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK)) +/** Check if an address is in the loopback region */ +#define ip4_addr_isloopback(ipaddr) (((ipaddr)->addr & PP_HTONL(IP_CLASSA_NET)) == PP_HTONL(((u32_t)IP_LOOPBACKNET) << 24)) +/** Safely copy one IP address to another and change byte order + * from host- to network-order. */ +#define ip4_addr_set_hton(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0:\ + lwip_htonl((src)->addr))) +/** IPv4 only: set the IP address given as an u32_t */ +#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32)) +/** IPv4 only: get the IP address as an u32_t */ +#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr) + +/** Get the network address by combining host address with netmask */ +#define ip4_addr_get_network(target, host, netmask) do { ((target)->addr = ((host)->addr) & ((netmask)->addr)); } while(0) + +/** + * @brief Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip4_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) +#define ip4_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr) + +#define ip4_addr_isany_val(addr1) ((addr1).addr == IPADDR_ANY) +#define ip4_addr_isany(addr1) ((addr1) == NULL || ip4_addr_isany_val(*(addr1))) + +#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr) +u8_t ip4_addr_netmask_valid(u32_t netmask); + +#define ip4_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL)) + +#define ip4_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL)) + +#define ip4_addr_debug_print_parts(debug, a, b, c, d) \ + LWIP_DEBUGF(debug, ("%" U16_F ".%" U16_F ".%" U16_F ".%" U16_F, a, b, c, d)) +#define ip4_addr_debug_print(debug, ipaddr) \ + ip4_addr_debug_print_parts(debug, \ + (u16_t)((ipaddr) != NULL ? ip4_addr1_16(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? ip4_addr2_16(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? ip4_addr3_16(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? ip4_addr4_16(ipaddr) : 0)) +#define ip4_addr_debug_print_val(debug, ipaddr) \ + ip4_addr_debug_print_parts(debug, \ + ip4_addr1_16(&(ipaddr)), \ + ip4_addr2_16(&(ipaddr)), \ + ip4_addr3_16(&(ipaddr)), \ + ip4_addr4_16(&(ipaddr))) + +/** Get one byte from the 4-byte address */ +#define ip4_addr1(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[0]) +#define ip4_addr2(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[1]) +#define ip4_addr3(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[2]) +#define ip4_addr4(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[3]) +/** These are cast to u16_t, with the intent that they are often arguments + * to printf using the U16_F format from cc.h. */ +#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr)) +#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr)) +#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr)) +#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr)) + +#define IP4ADDR_STRLEN_MAX 16 + +/** For backwards compatibility */ +#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr) + +u32_t ipaddr_addr(const char *cp); +int ip4addr_aton(const char *cp, ip4_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip4addr_ntoa(const ip4_addr_t *addr); +char *ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen); + + +/** directly map this to the lwip internal functions */ +#define inet_addr(cp) ipaddr_addr(cp) +#define inet_aton(cp, addr) ip4addr_aton(cp, (ip4_addr_t*)addr) +#define inet_ntoa(addr) ipaddr_ntoa((ip_addr_t*)&(addr)) +#endif + +#if TLS_CONFIG_IPV6 +/** This is the aligned version of ip6_addr_t, + used as local variable, on the stack, etc. */ +struct ip6_addr { + u32_t addr[4]; +}; + +/** IPv6 address */ +typedef struct ip6_addr ip6_addr_t; + +/** Set an IPv6 partial address given by byte-parts */ +#define IP6_ADDR_PART(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = PP_HTONL(LWIP_MAKEU32(a,b,c,d)) + +/** Set a full IPv6 address by passing the 4 u32_t indices in network byte order + (use PP_HTONL() for constants) */ +#define IP6_ADDR(ip6addr, idx0, idx1, idx2, idx3) do { \ + (ip6addr)->addr[0] = idx0; \ + (ip6addr)->addr[1] = idx1; \ + (ip6addr)->addr[2] = idx2; \ + (ip6addr)->addr[3] = idx3; } while(0) + +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0])) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[1]) >> 16) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[1])) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[2]) >> 16) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[2])) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[3]) >> 16) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[3])) & 0xffff)) + +/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */ +#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3];}while(0) +/** Safely copy one IPv6 address to another (src may be NULL) */ +#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \ + (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \ + (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \ + (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0) + +/** Set complete address to zero */ +#define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = 0;}while(0) + +/** Set address to ipv6 'any' (no need for lwip_htonl()) */ +#define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr) +/** Set address to ipv6 loopback address */ +#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) +/** Safely copy one IPv6 address to another and change byte order + * from host- to network-order. */ +#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : lwip_htonl((src)->addr[0]); \ + (dest)->addr[1] = (src) == NULL ? 0 : lwip_htonl((src)->addr[1]); \ + (dest)->addr[2] = (src) == NULL ? 0 : lwip_htonl((src)->addr[2]); \ + (dest)->addr[3] = (src) == NULL ? 0 : lwip_htonl((src)->addr[3]);}while(0) + + +/** + * @brief Determine if two IPv6 address are on the same network. + * + * @arg addr1 IPv6 address 1 + * @arg addr2 IPv6 address 2 + * @return !0 if the network identifiers of both address match + */ +#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1])) + +#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3])) + +#define ip6_get_subnet_id(ip6addr) (lwip_htonl((ip6addr)->addr[2]) & 0x0000ffffUL) + +#define ip6_addr_isany_val(ip6addr) (((ip6addr).addr[0] == 0) && \ + ((ip6addr).addr[1] == 0) && \ + ((ip6addr).addr[2] == 0) && \ + ((ip6addr).addr[3] == 0)) +#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || ip6_addr_isany_val(*(ip6addr))) + +#define ip6_addr_isloopback(ip6addr) (((ip6addr)->addr[0] == 0UL) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL)) + +#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL)) + +#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL)) + +#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL)) + +#define ip6_addr_isipv4mappedipv6(ip6addr) (((ip6addr)->addr[0] == 0) && ((ip6addr)->addr[1] == 0) && (((ip6addr)->addr[2]) == PP_HTONL(0x0000FFFFUL))) + +#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) +#define ip6_addr_multicast_transient_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL)) +#define ip6_addr_multicast_prefix_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL)) +#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL)) +#define ip6_addr_multicast_scope(ip6addr) ((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xf) +#define IP6_MULTICAST_SCOPE_RESERVED 0x0 +#define IP6_MULTICAST_SCOPE_RESERVED0 0x0 +#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL 0x1 +#define IP6_MULTICAST_SCOPE_LINK_LOCAL 0x2 +#define IP6_MULTICAST_SCOPE_RESERVED3 0x3 +#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL 0x4 +#define IP6_MULTICAST_SCOPE_SITE_LOCAL 0x5 +#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL 0x8 +#define IP6_MULTICAST_SCOPE_GLOBAL 0xe +#define IP6_MULTICAST_SCOPE_RESERVEDF 0xf +#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff010000UL)) +#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff020000UL)) +#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff040000UL)) +#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff050000UL)) +#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff080000UL)) +#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff0e0000UL)) + +/** @todo define get/set for well-know multicast addresses, e.g. ff02::1 */ +#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) +#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + +#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL))) +#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0) + +#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) ) + +#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \ + (ip6addr)->addr[3] = (PP_HTONL(0xff000000UL) | (if_id));}while(0) + +#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + ((ip6addr)->addr[3] == (PP_HTONL(0xff000000UL) | (sn_addr)->addr[3]))) + +/* IPv6 address states. */ +#define IP6_ADDR_INVALID 0x00 +#define IP6_ADDR_TENTATIVE 0x08 +#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */ +#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */ +#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */ +#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */ +#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */ +#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */ +#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */ +#define IP6_ADDR_VALID 0x10 /* This bit marks an address as valid (preferred or deprecated) */ +#define IP6_ADDR_PREFERRED 0x30 +#define IP6_ADDR_DEPRECATED 0x10 /* Same as VALID (valid but not preferred) */ + +#define IP6_ADDR_TENTATIVE_COUNT_MASK 0x07 /* 1-7 probes sent */ + +#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID) +#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE) +#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */ +#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED) +#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED) + +#define ip6_addr_debug_print_parts(debug, a, b, c, d, e, f, g, h) \ + LWIP_DEBUGF(debug, ("%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F, \ + a, b, c, d, e, f, g, h)) +#define ip6_addr_debug_print(debug, ipaddr) \ + ip6_addr_debug_print_parts(debug, \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0)) +#define ip6_addr_debug_print_val(debug, ipaddr) \ + ip6_addr_debug_print_parts(debug, \ + IP6_ADDR_BLOCK1(&(ipaddr)), \ + IP6_ADDR_BLOCK2(&(ipaddr)), \ + IP6_ADDR_BLOCK3(&(ipaddr)), \ + IP6_ADDR_BLOCK4(&(ipaddr)), \ + IP6_ADDR_BLOCK5(&(ipaddr)), \ + IP6_ADDR_BLOCK6(&(ipaddr)), \ + IP6_ADDR_BLOCK7(&(ipaddr)), \ + IP6_ADDR_BLOCK8(&(ipaddr))) + +#define IP6ADDR_STRLEN_MAX 46 + +int ip6addr_aton(const char *cp, ip6_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip6addr_ntoa(const ip6_addr_t *addr); +char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen); + +#endif + +/** @ingroup ipaddr + * IP address types for use in ip_addr_t.type member. + * @see tcp_new_ip_type(), udp_new_ip_type(), raw_new_ip_type(). + */ +enum lwip_ip_addr_type { + /** IPv4 */ + IPADDR_TYPE_V4 = 0U, + /** IPv6 */ + IPADDR_TYPE_V6 = 6U, + /** IPv4+IPv6 ("dual-stack") */ + IPADDR_TYPE_ANY = 46U +}; + +#if TLS_CONFIG_IPV4&&TLS_CONFIG_IPV6 +/** + * @ingroup ipaddr + * A union struct for both IP version's addresses. + * ATTENTION: watch out for its size when adding IPv6 address scope! + */ +typedef struct ip_addr { + union { + ip6_addr_t ip6; + ip4_addr_t ip4; + } u_addr; + /** @ref lwip_ip_addr_type */ + u8_t type; +} ip_addr_t; + +extern const ip_addr_t ip_addr_any_type; + +/** @ingroup ip4addr */ +#define IPADDR4_INIT(u32val) { { { { u32val, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_V4 } +/** @ingroup ip4addr */ +#define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d))) +/** @ingroup ip6addr */ +#define IPADDR6_INIT(a, b, c, d) { { { { a, b, c, d } } }, IPADDR_TYPE_V6 } +/** @ingroup ip6addr */ +#define IPADDR6_INIT_HOST(a, b, c, d) { { { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } }, IPADDR_TYPE_V6 } + +/** @ingroup ipaddr */ +#define IP_IS_ANY_TYPE_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_ANY) +/** @ingroup ipaddr */ +#define IPADDR_ANY_TYPE_INIT { { { { 0ul, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_ANY } + +/** @ingroup ip4addr */ +#define IP_IS_V4_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V4) +/** @ingroup ip6addr */ +#define IP_IS_V6_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V6) +/** @ingroup ip4addr */ +#define IP_IS_V4(ipaddr) (((ipaddr) == NULL) || IP_IS_V4_VAL(*(ipaddr))) +/** @ingroup ip6addr */ +#define IP_IS_V6(ipaddr) (((ipaddr) != NULL) && IP_IS_V6_VAL(*(ipaddr))) + +#define IP_SET_TYPE_VAL(ipaddr, iptype) do { (ipaddr).type = (iptype); }while(0) +#define IP_SET_TYPE(ipaddr, iptype) do { if((ipaddr) != NULL) { IP_SET_TYPE_VAL(*(ipaddr), iptype); }}while(0) +#define IP_GET_TYPE(ipaddr) ((ipaddr)->type) + +#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) (IP_GET_TYPE(&pcb->local_ip) == IP_GET_TYPE(ipaddr)) +#define IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr) (IP_IS_ANY_TYPE_VAL(pcb->local_ip) || IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) + +/** @ingroup ip6addr + * Convert generic ip address to specific protocol version + */ +#define ip_2_ip6(ipaddr) (&((ipaddr)->u_addr.ip6)) +/** @ingroup ip4addr + * Convert generic ip address to specific protocol version + */ +#define ip_2_ip4(ipaddr) (&((ipaddr)->u_addr.ip4)) + +/** @ingroup ip4addr */ +#define IP_ADDR4(ipaddr,a,b,c,d) do { IP4_ADDR(ip_2_ip4(ipaddr),a,b,c,d); \ + IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V4); } while(0) +/** @ingroup ip6addr */ +#define IP_ADDR6(ipaddr,i0,i1,i2,i3) do { IP6_ADDR(ip_2_ip6(ipaddr),i0,i1,i2,i3); \ + IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V6); } while(0) +/** @ingroup ip6addr */ +#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3) IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3)) + +/** @ingroup ipaddr */ +#define ip_addr_copy(dest, src) do{ IP_SET_TYPE_VAL(dest, IP_GET_TYPE(&src)); if(IP_IS_V6_VAL(src)){ \ + ip6_addr_copy(*ip_2_ip6(&(dest)), *ip_2_ip6(&(src))); }else{ \ + ip4_addr_copy(*ip_2_ip4(&(dest)), *ip_2_ip4(&(src))); }}while(0) +/** @ingroup ip6addr */ +#define ip_addr_copy_from_ip6(dest, src) do{ \ + ip6_addr_copy(*ip_2_ip6(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V6); }while(0) +/** @ingroup ip4addr */ +#define ip_addr_copy_from_ip4(dest, src) do{ \ + ip4_addr_copy(*ip_2_ip4(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V4); }while(0) +/** @ingroup ip4addr */ +#define ip_addr_set_ip4_u32(ipaddr, val) do{if(ipaddr){ip4_addr_set_u32(ip_2_ip4(ipaddr), val); \ + IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ip4addr */ +#define ip_addr_get_ip4_u32(ipaddr) (((ipaddr) && IP_IS_V4(ipaddr)) ? \ + ip4_addr_get_u32(ip_2_ip4(ipaddr)) : 0) +/** @ingroup ipaddr */ +#define ip_addr_set(dest, src) do{ IP_SET_TYPE(dest, IP_GET_TYPE(src)); if(IP_IS_V6(src)){ \ + ip6_addr_set(ip_2_ip6(dest), ip_2_ip6(src)); }else{ \ + ip4_addr_set(ip_2_ip4(dest), ip_2_ip4(src)); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_set_ipaddr(dest, src) ip_addr_set(dest, src) +/** @ingroup ipaddr */ +#define ip_addr_set_zero(ipaddr) do{ \ + ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, 0); }while(0) +/** @ingroup ip5addr */ +#define ip_addr_set_zero_ip4(ipaddr) do{ \ + ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }while(0) +/** @ingroup ip6addr */ +#define ip_addr_set_zero_ip6(ipaddr) do{ \ + ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }while(0) +/** @ingroup ipaddr */ +#define ip_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_any(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }else{ \ + ip4_addr_set_any(ip_2_ip4(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_loopback(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }else{ \ + ip4_addr_set_loopback(ip_2_ip4(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_set_hton(dest, src) do{if(IP_IS_V6(src)){ \ + ip6_addr_set_hton(ip_2_ip6(ipaddr), (src)); IP_SET_TYPE(dest, IPADDR_TYPE_V6); }else{ \ + ip4_addr_set_hton(ip_2_ip4(ipaddr), (src)); IP_SET_TYPE(dest, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_get_network(target, host, netmask) do{if(IP_IS_V6(host)){ \ + ip4_addr_set_zero(ip_2_ip4(target)); IP_SET_TYPE(target, IPADDR_TYPE_V6); } else { \ + ip4_addr_get_network(ip_2_ip4(target), ip_2_ip4(host), ip_2_ip4(netmask)); IP_SET_TYPE(target, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_netcmp(addr1, addr2, mask) ((IP_IS_V6(addr1) && IP_IS_V6(addr2)) ? \ + 0 : \ + ip4_addr_netcmp(ip_2_ip4(addr1), ip_2_ip4(addr2), mask)) +/** @ingroup ipaddr */ +#define ip_addr_cmp(addr1, addr2) ((IP_GET_TYPE(addr1) != IP_GET_TYPE(addr2)) ? 0 : (IP_IS_V6_VAL(*(addr1)) ? \ + ip6_addr_cmp(ip_2_ip6(addr1), ip_2_ip6(addr2)) : \ + ip4_addr_cmp(ip_2_ip4(addr1), ip_2_ip4(addr2)))) +/** @ingroup ipaddr */ +#define ip_addr_isany(ipaddr) ((IP_IS_V6(ipaddr)) ? \ + ip6_addr_isany(ip_2_ip6(ipaddr)) : \ + ip4_addr_isany(ip_2_ip4(ipaddr))) +/** @ingroup ipaddr */ +#define ip_addr_isany_val(ipaddr) ((IP_IS_V6_VAL(ipaddr)) ? \ + ip6_addr_isany_val(*ip_2_ip6(&(ipaddr))) : \ + ip4_addr_isany_val(*ip_2_ip4(&(ipaddr)))) +/** @ingroup ipaddr */ +#define ip_addr_isbroadcast(ipaddr, netif) ((IP_IS_V6(ipaddr)) ? \ + 0 : \ + ip4_addr_isbroadcast(ip_2_ip4(ipaddr), netif)) +/** @ingroup ipaddr */ +#define ip_addr_ismulticast(ipaddr) ((IP_IS_V6(ipaddr)) ? \ + ip6_addr_ismulticast(ip_2_ip6(ipaddr)) : \ + ip4_addr_ismulticast(ip_2_ip4(ipaddr))) +/** @ingroup ipaddr */ +#define ip_addr_isloopback(ipaddr) ((IP_IS_V6(ipaddr)) ? \ + ip6_addr_isloopback(ip_2_ip6(ipaddr)) : \ + ip4_addr_isloopback(ip_2_ip4(ipaddr))) +/** @ingroup ipaddr */ +#define ip_addr_islinklocal(ipaddr) ((IP_IS_V6(ipaddr)) ? \ + ip6_addr_islinklocal(ip_2_ip6(ipaddr)) : \ + ip4_addr_islinklocal(ip_2_ip4(ipaddr))) +#define ip_addr_debug_print(debug, ipaddr) do { if(IP_IS_V6(ipaddr)) { \ + ip6_addr_debug_print(debug, ip_2_ip6(ipaddr)); } else { \ + ip4_addr_debug_print(debug, ip_2_ip4(ipaddr)); }}while(0) +#define ip_addr_debug_print_val(debug, ipaddr) do { if(IP_IS_V6_VAL(ipaddr)) { \ + ip6_addr_debug_print_val(debug, *ip_2_ip6(&(ipaddr))); } else { \ + ip4_addr_debug_print_val(debug, *ip_2_ip4(&(ipaddr))); }}while(0) +/** @ingroup ipaddr */ +#define ipaddr_ntoa(addr) (((addr) == NULL) ? "NULL" : \ + ((IP_IS_V6(addr)) ? ip6addr_ntoa(ip_2_ip6(addr)) : ip4addr_ntoa(ip_2_ip4(addr)))) +/** @ingroup ipaddr */ +#define ipaddr_ntoa_r(addr, buf, buflen) (((addr) == NULL) ? "NULL" : \ + ((IP_IS_V6(addr)) ? ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen) : ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen))) +int ipaddr_aton(const char *cp, ip_addr_t *addr); + +/** @ingroup ipaddr */ +#define IPADDR_STRLEN_MAX IP6ADDR_STRLEN_MAX + +/** @ingroup ipaddr */ +#define ip4_2_ipv4_mapped_ipv6(ip6addr, ip4addr) do { \ + (ip6addr)->addr[3] = (ip4addr)->addr; \ + (ip6addr)->addr[2] = PP_HTONL(0x0000FFFFUL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[0] = 0; } while(0); + +/** @ingroup ipaddr */ +#define unmap_ipv4_mapped_ipv6(ip4addr, ip6addr) \ + (ip4addr)->addr = (ip6addr)->addr[3]; + +#define IP46_ADDR_ANY(type) (((type) == IPADDR_TYPE_V6)? IP6_ADDR_ANY : IP4_ADDR_ANY) + +#else +#define IP_ADDR_PCB_VERSION_MATCH(addr, pcb) 1 +#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) 1 + +#if TLS_CONFIG_IPV4 +typedef ip4_addr_t ip_addr_t; +#define IPADDR4_INIT(u32val) { u32val } +#define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d))) +#define IP_IS_V4_VAL(ipaddr) 1 +#define IP_IS_V6_VAL(ipaddr) 0 +#define IP_IS_V4(ipaddr) 1 +#define IP_IS_V6(ipaddr) 0 +#define IP_IS_ANY_TYPE_VAL(ipaddr) 0 +#define IP_SET_TYPE_VAL(ipaddr, iptype) +#define IP_SET_TYPE(ipaddr, iptype) +#define IP_GET_TYPE(ipaddr) IPADDR_TYPE_V4 +#define ip_2_ip4(ipaddr) (ipaddr) +#define IP_ADDR4(ipaddr,a,b,c,d) IP4_ADDR(ipaddr,a,b,c,d) + +#define ip_addr_copy(dest, src) ip4_addr_copy(dest, src) +#define ip_addr_copy_from_ip4(dest, src) ip4_addr_copy(dest, src) +#define ip_addr_set_ip4_u32(ipaddr, val) ip4_addr_set_u32(ip_2_ip4(ipaddr), val) +#define ip_addr_get_ip4_u32(ipaddr) ip4_addr_get_u32(ip_2_ip4(ipaddr)) +#define ip_addr_set(dest, src) ip4_addr_set(dest, src) +#define ip_addr_set_ipaddr(dest, src) ip4_addr_set(dest, src) +#define ip_addr_set_zero(ipaddr) ip4_addr_set_zero(ipaddr) +#define ip_addr_set_zero_ip4(ipaddr) ip4_addr_set_zero(ipaddr) +#define ip_addr_set_any(is_ipv6, ipaddr) ip4_addr_set_any(ipaddr) +#define ip_addr_set_loopback(is_ipv6, ipaddr) ip4_addr_set_loopback(ipaddr) +#define ip_addr_set_hton(dest, src) ip4_addr_set_hton(dest, src) +#define ip_addr_get_network(target, host, mask) ip4_addr_get_network(target, host, mask) +#define ip_addr_netcmp(addr1, addr2, mask) ip4_addr_netcmp(addr1, addr2, mask) +#define ip_addr_cmp(addr1, addr2) ip4_addr_cmp(addr1, addr2) +#define ip_addr_isany(ipaddr) ip4_addr_isany(ipaddr) +#define ip_addr_isany_val(ipaddr) ip4_addr_isany_val(ipaddr) +#define ip_addr_isloopback(ipaddr) ip4_addr_isloopback(ipaddr) +#define ip_addr_islinklocal(ipaddr) ip4_addr_islinklocal(ipaddr) +#define ip_addr_isbroadcast(addr, netif) ip4_addr_isbroadcast(addr, netif) +#define ip_addr_ismulticast(ipaddr) ip4_addr_ismulticast(ipaddr) +#define ip_addr_debug_print(debug, ipaddr) ip4_addr_debug_print(debug, ipaddr) +#define ip_addr_debug_print_val(debug, ipaddr) ip4_addr_debug_print_val(debug, ipaddr) +#define ipaddr_ntoa(ipaddr) ip4addr_ntoa(ipaddr) +#define ipaddr_ntoa_r(ipaddr, buf, buflen) ip4addr_ntoa_r(ipaddr, buf, buflen) +#define ipaddr_aton(cp, addr) ip4addr_aton(cp, addr) + +#define IPADDR_STRLEN_MAX IP4ADDR_STRLEN_MAX + +#define IP46_ADDR_ANY(type) (IP4_ADDR_ANY) +#else +typedef ip6_addr_t ip_addr_t; +#define IPADDR6_INIT(a, b, c, d) { { a, b, c, d } } +#define IPADDR6_INIT_HOST(a, b, c, d) { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } +#define IP_IS_V4_VAL(ipaddr) 0 +#define IP_IS_V6_VAL(ipaddr) 1 +#define IP_IS_V4(ipaddr) 0 +#define IP_IS_V6(ipaddr) 1 +#define IP_IS_ANY_TYPE_VAL(ipaddr) 0 +#define IP_SET_TYPE_VAL(ipaddr, iptype) +#define IP_SET_TYPE(ipaddr, iptype) +#define IP_GET_TYPE(ipaddr) IPADDR_TYPE_V6 +#define ip_2_ip6(ipaddr) (ipaddr) +#define IP_ADDR6(ipaddr,i0,i1,i2,i3) IP6_ADDR(ipaddr,i0,i1,i2,i3) +#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3) IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3)) + +#define ip_addr_copy(dest, src) ip6_addr_copy(dest, src) +#define ip_addr_copy_from_ip6(dest, src) ip6_addr_copy(dest, src) +#define ip_addr_set(dest, src) ip6_addr_set(dest, src) +#define ip_addr_set_ipaddr(dest, src) ip6_addr_set(dest, src) +#define ip_addr_set_zero(ipaddr) ip6_addr_set_zero(ipaddr) +#define ip_addr_set_zero_ip6(ipaddr) ip6_addr_set_zero(ipaddr) +#define ip_addr_set_any(is_ipv6, ipaddr) ip6_addr_set_any(ipaddr) +#define ip_addr_set_loopback(is_ipv6, ipaddr) ip6_addr_set_loopback(ipaddr) +#define ip_addr_set_hton(dest, src) ip6_addr_set_hton(dest, src) +#define ip_addr_get_network(target, host, mask) ip6_addr_set_zero(target) +#define ip_addr_netcmp(addr1, addr2, mask) 0 +#define ip_addr_cmp(addr1, addr2) ip6_addr_cmp(addr1, addr2) +#define ip_addr_isany(ipaddr) ip6_addr_isany(ipaddr) +#define ip_addr_isany_val(ipaddr) ip6_addr_isany_val(ipaddr) +#define ip_addr_isloopback(ipaddr) ip6_addr_isloopback(ipaddr) +#define ip_addr_islinklocal(ipaddr) ip6_addr_islinklocal(ipaddr) +#define ip_addr_isbroadcast(addr, netif) 0 +#define ip_addr_ismulticast(ipaddr) ip6_addr_ismulticast(ipaddr) +#define ip_addr_debug_print(debug, ipaddr) ip6_addr_debug_print(debug, ipaddr) +#define ip_addr_debug_print_val(debug, ipaddr) ip6_addr_debug_print_val(debug, ipaddr) +#define ipaddr_ntoa(ipaddr) ip6addr_ntoa(ipaddr) +#define ipaddr_ntoa_r(ipaddr, buf, buflen) ip6addr_ntoa_r(ipaddr, buf, buflen) +#define ipaddr_aton(cp, addr) ip6addr_aton(cp, addr) + +#define IPADDR_STRLEN_MAX IP6ADDR_STRLEN_MAX + +#define IP46_ADDR_ANY(type) (IP6_ADDR_ANY) +#endif +#endif + + +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN IP4ADDR_STRLEN_MAX +#endif +#if TLS_CONFIG_IPV6 +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN IP6ADDR_STRLEN_MAX +#endif +#endif + +#if TLS_CONFIG_IPV4 && TLS_CONFIG_IPV6 +/** @ingroup socket */ +#define inet_ntop(af,src,dst,size) \ + (((af) == AF_INET6) ? ip6addr_ntoa_r((const ip6_addr_t*)(src),(dst),(size)) \ + : (((af) == AF_INET) ? ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL)) +/** @ingroup socket */ +#define inet_pton(af,src,dst) \ + (((af) == AF_INET6) ? ip6addr_aton((src),(ip6_addr_t*)(dst)) \ + : (((af) == AF_INET) ? ip4addr_aton((src),(ip4_addr_t*)(dst)) : 0)) +#elif TLS_CONFIG_IPV4 /* TLS_CONFIG_IPV4 && TLS_CONFIG_IPV6 */ +#define inet_ntop(af,src,dst,size) \ + (((af) == AF_INET) ? ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL) +#define inet_pton(af,src,dst) \ + (((af) == AF_INET) ? ip4addr_aton((src),(ip4_addr_t*)(dst)) : 0) +#else /* TLS_CONFIG_IPV4 && TLS_CONFIG_IPV6 */ +#define inet_ntop(af,src,dst,size) \ + (((af) == AF_INET6) ? ip6addr_ntoa_r((const ip6_addr_t*)(src),(dst),(size)) : NULL) +#define inet_pton(af,src,dst) \ + (((af) == AF_INET6) ? ip6addr_aton((src),(ip6_addr_t*)(dst)) : 0) +#endif /* TLS_CONFIG_IPV4 && TLS_CONFIG_IPV6 */ + +extern void print_ipaddr(ip_addr_t *ip); + +struct netif *wm_ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src); +#endif diff --git a/sdk/Include/OS/wm_config_gcc.inc b/sdk/Include/OS/wm_config_gcc.inc new file mode 100644 index 0000000..84f2007 --- /dev/null +++ b/sdk/Include/OS/wm_config_gcc.inc @@ -0,0 +1,3 @@ +@.equ __TLS_OS_UCOS, 0 @;//¶þѡһ +@.equ __TLS_OS_FREERTOS, 1 + diff --git a/sdk/Include/OS/wm_os_config.h b/sdk/Include/OS/wm_os_config.h new file mode 100644 index 0000000..9113e7f --- /dev/null +++ b/sdk/Include/OS/wm_os_config.h @@ -0,0 +1,19 @@ +/** + * @file wm_os_config.h + * + * @brief WM OS select freertos or ucos + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef __WM_OS_CONFIG_H__ +#define __WM_OS_CONFIG_H__ +#define OS_CFG_ON 1 +#define OS_CFG_OFF 0 + +#define TLS_OS_UCOS OS_CFG_OFF /*UCOSII need to modify wm_config.inc*/ +#undef TLS_OS_FREERTOS +#define TLS_OS_FREERTOS OS_CFG_ON /*FreeRTOS need to modify wm_config.inc*/ +#endif diff --git a/sdk/Include/OS/wm_os_config.inc b/sdk/Include/OS/wm_os_config.inc new file mode 100644 index 0000000..04d29a2 --- /dev/null +++ b/sdk/Include/OS/wm_os_config.inc @@ -0,0 +1,3 @@ +;__TLS_OS_UCOS EQU 1 ;¶þѡһ +__TLS_OS_FREERTOS EQU 1 + END \ No newline at end of file diff --git a/sdk/Include/OS/wm_osal.h b/sdk/Include/OS/wm_osal.h new file mode 100644 index 0000000..545691a --- /dev/null +++ b/sdk/Include/OS/wm_osal.h @@ -0,0 +1,715 @@ +/** + * @file wm_osal.h + * + * @brief WM OS adapter layer + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_OSAL_H +#define WM_OSAL_H + +#include +#include +#include "wm_config.h" +#include "wm_type_def.h" + +/** OS TICK Frequency ,this value needs to keep the same as OS self-defined tick value*/ +extern const unsigned int HZ; + +/** Type definition of os_time_t */ +typedef long os_time_t; +/** Structure definition of os_time */ +struct os_time { + os_time_t sec; + os_time_t usec; +}; +/** ENUMERATION of OS */ +enum TLS_OS_TYPE{ + OS_UCOSII = 0, + OS_FREERTOS = 1, + OS_MAX_NUM +}; + +/** TYPE definition of OS_STK */ +#ifdef OS_STK +#undef OS_STK +#endif +typedef unsigned int OS_STK; + +/** TYPE definition of OS_CPU_SR */ +#ifdef OS_CPU_SR +#undef OS_CPU_SR +#endif +typedef unsigned int OS_CPU_SR; +/** TYPE definition of tls_os_task_t */ +typedef void * tls_os_task_t; +/** TYPE definition of tls_os_timer_t */ +typedef void tls_os_timer_t; +/** TYPE definition of tls_os_sem_t */ +typedef void tls_os_sem_t; +/** TYPE definition of tls_os_queue_t */ +typedef void tls_os_queue_t; +/** TYPE definition of tls_os_mailbox_t */ +typedef void tls_os_mailbox_t; +/** TYPE definition of tls_os_mutex_t */ +typedef void tls_os_mutex_t; +/** TYPE definition of TLS_OS_TIMER_CALLBACK */ +typedef void (*TLS_OS_TIMER_CALLBACK)(void *ptmr, void *parg); + +/** MACRO definition of TIMER ONE times */ +#define TLS_OS_TIMER_OPT_ONE_SHORT 1u +/** MACRO definition of TIMER PERIOD */ +#define TLS_OS_TIMER_OPT_PERIOD 2u + +/** ENUMERATION definition of OS STATUS */ +typedef enum tls_os_status { + TLS_OS_SUCCESS = 0, + TLS_OS_ERROR, + TLS_OS_ERR_TIMEOUT, +} tls_os_status_t; + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup OS_APIs OS APIs + * @brief Operate system APIs + */ + +/** + * @addtogroup OS_APIs + * @{ + */ + +/** + * @brief This function is used to register OS tick timer irq + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_os_timer_init(void); + +/** + * @brief This function is used to tick handler + * + * @param[in] *p argument + * + * @return None + * + * @note None + */ +void tls_os_time_tick(void *p); + +/** + * @brief This function is used to initialize OS common resource + * + * @param[in] *arg + * + * @return None + * + * @note None + */ +void tls_os_init(void *arg); + +/** + * @brief This function is used to start task scheduler + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_os_start_scheduler(void); + +/** + * @brief This function is used to get OS type + * + * @param[in] None + * + * @retval 0 OS_UCOSII + * @retval 1 OS_FREERTOS + * + * @note May not be used by now. + */ +int tls_os_get_type(void); + +/** + * @brief This function is used to create a task. Tasks can either be created prior to + the start of multitasking or by a running task. + A task cannot be created in an ISR. + * + * @param[in] *task pointer to the task + * @param[in] name the task's name + * @param[in] entry the task's entry function + * @param[in] param pointer to an optional data area which can be + used to pass parameters to the task when the + task first executes. Where the task is + concerned it thinks it was invoked and passed + the argument 'param' as follows: + void Task (void *param) + { + for (;;) { + Task code; + } + } + * @param[in] *stk_start pointer to the task's bottom of stack. Address must be between 0x20000000 and 0x20028000 + * @param[in] stk_size the size of the stack in number of elements. + If OS_STK is set to INT8U, + 'stk_size' corresponds to the number of bytes + available. + If OS_STK is set to INT16U, 'stk_size' contains + the number of 16-bit entries available. + Finally, if OS_STK is set to INT32U, + 'stk_size' contains the number of 32-bit entries + available on the stack. + * @param[in] prio the task's priority. A unique priority MUST be + assigned to each task and the lower the number, + the higher the priority. + * @param[in] flag contains additional information about the behavior of the task + * + * @retval TLS_OS_SUCCESS the call was successful. + * @retval TLS_OS_ERROR failed + * + * @note 1) Stack Range: [stk_start, stk_start + stk_size) must be between 0x20000000 and 0x20028000 + * 2) task stack: Using static memory like array, not using dynamic memory. + * 3) And if you use static memory like array (large size) as data storage in your application, + * we suggest you change it to dynamic memory by tls_mem_alloc. + */ +tls_os_status_t tls_os_task_create(tls_os_task_t *task, + const char* name, + void (*entry)(void* param), + void* param, + u8 *stk_start, + u32 stk_size, + u32 prio, + u32 flag); + +/** + * @brief This function allows you to delete a task. The calling + task can delete itself by its own priority number. + The deleted task is returned to the dormant state + and can be re-activated by creating the deleted task + again. + * + * @param[in] prio task priority to delete + * @param[in] (*freefun)(void) function to free resource + * + * @retval TLS_OS_SUCCESS the call is successful + * @retval TLS_OS_ERROR failed + * + * @note Generally, you do not need to call this function in your application. + */ +tls_os_status_t tls_os_task_del(u8 prio, void (*freefun)(void)); + + +/** + * @brief This function creates a mutual exclusion semaphore + * + * @param[in] prio the priority to use when accessing the mutual + exclusion semaphore. In other words, when the + semaphore is acquired and a higher priority task + attempts to obtain the semaphore then the + priority of the task owning the semaphore is + raised to this priority. It is assumed that + you will specify a priority that is LOWER in + value than ANY of the tasks competing for the + mutex. + * @param[in] **mutex pointer to the event control clock (OS_EVENT) + associated with the created mutex. + * + * @retval TLS_OS_SUCCESS the call was successful + * @retval TLS_OS_ERROR failed + * + * @note 1) The LEAST significant 8 bits of '.OSEventCnt' are used + to hold the priority number of the task owning the mutex + or 0xFF if no task owns the mutex. + 2) The MOST significant 8 bits of '.OSEventCnt' are used to + hold the priority number to use to reduce priority + inversion. + */ +tls_os_status_t tls_os_mutex_create(u8 prio, tls_os_mutex_t **mutex); + +/** + * @brief This function deletes a mutual exclusion semaphore and + readies all tasks pending on the it + * + * @param[in] *mutex pointer to the event control block associated + with the desired mutex + * + * @retval TLS_OS_SUCCESS The call was successful and the mutex + was deleted + * @retval TLS_OS_ERROR failed + * + * @note 1) This function must be used with care. Tasks that would + normally expect the presence of the mutex MUST check the + return code of OSMutexPend(). + 2) This call can potentially disable interrupts for a long + time. The interrupt disable time is directly + proportional to the number of tasks waiting on the mutex. + 3) Because ALL tasks pending on the mutex will be readied, + you MUST be careful because the resource(s) will no + longer be guarded by the mutex. + 4) IMPORTANT: In the 'OS_DEL_ALWAYS' case, we assume that + the owner of the Mutex (if there is one) is ready-to-run + and is thus NOT pending on another kernel object or has + delayed itself.In other words, if a task owns the mutex + being deleted,that task will be made ready-to-run at + its original priority. + */ + tls_os_status_t tls_os_mutex_delete(tls_os_mutex_t *mutex); + +/** + * @brief This function waits for a mutual exclusion semaphore + * + * @param[in] *mutex pointer to the event control block + associated with the desired mutex + * @param[in] wait_time an optional timeout period (in clock ticks). + If non-zero, your task will wait for the resource + up to the amount of time specified by + this argument. + If you specify 0, however, your task will wait + forever at the specified mutex or, + until the resource becomes available. + * + * @retval TLS_OS_SUCCESS The call was successful and your task + owns the mutex + * @retval TLS_OS_ERROR failed + * + * @note 1) The task that owns the Mutex MUST NOT pend on + any other event while it owns the mutex. + 2) You MUST NOT change the priority of the task + that owns the mutex + */ + tls_os_status_t tls_os_mutex_acquire(tls_os_mutex_t *mutex, + u32 wait_time); + +/** + * @brief This function releases a mutual exclusion semaphore + * + * @param[in] *mutex pointer to the event control block + associated with the desired mutex + * + * @retval TLS_OS_SUCCESS The call was successful and the mutex was signaled. + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_mutex_release(tls_os_mutex_t *mutex); + +/** + * @brief This function creates a semaphore + * + * @param[in] **sem pointer to the event control block (OS_EVENT) + associated with the created semaphore + * @param[in] cnt the initial value for the semaphore. + If the value is 0, no resource is available + (or no event has occurred). + You initialize the semaphore to a non-zero value + to specify how many resources are available + (e.g. if you have 10 resources, you would + initialize the semaphore to 10). + * + * @retval TLS_OS_SUCCESS success,The call was successful + * @retval TLS_OS_ERROR failed + * + * @note None + */ +tls_os_status_t tls_os_sem_create(tls_os_sem_t **sem, u32 cnt); + + +/** + * @brief This function deletes a semaphore and readies all tasks + pending on this semaphore. + * + * @param[in] *sem pointer to the event control block associated + with the desired semaphore + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +tls_os_status_t tls_os_sem_delete(tls_os_sem_t *sem); + +/** + * @brief This function waits for a semaphore + * + * @param[in] *sem pointer to the event control block + associated with the desired semaphore + * @param[in] wait_time an optional timeout period (in clock ticks). + If non-zero, your task will wait for the + resource up to the amount of time specified + by this argument.If you specify 0, however, + your task will wait forever at the specified + semaphore or, until the resource becomes + available (or the event occurs). + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_sem_acquire(tls_os_sem_t *sem, + u32 wait_time); + +/** + * @brief This function signals a semaphore + * + * @param[in] *sem pointer to the event control block associated + with the desired semaphore + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_sem_release(tls_os_sem_t *sem); + +/** + * @brief This function sets the semaphore count to the value specified + as an argument.Typically,this value would be 0.You + would typically use this function when a semaphore is + used as a signaling mechanism and, you want to reset + the count value. + * + * @param[in] *sem pointer to the event control block + * @param[in] cnt the new value for the semaphore count. You would + pass 0 to reset the semaphore count. + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_sem_set(tls_os_sem_t *sem, u16 cnt); + + +/** + * @brief This function creates a message queue if free event cont + rol blocks are available + * + * @param[in] **queue pointer to the event control clock (OS_EVENT) + associated with the created queue + * @param[in] queue_size the number of elements in the storage area + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_queue_create(tls_os_queue_t **queue, u32 queue_size); + +/** + * @brief This function deletes a message queue and readies all + tasks pending on the queue + * + * @param[in] *queue pointer to the event control block associated + with the desired queue + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_queue_delete(tls_os_queue_t *queue); + +/** + * @brief This function sends a message to a queue + * + * @param[in] *queue pointer to the event control block + associated with the desired queue + * @param[in] *msg pointer to the message to send. + * @param[in] msg_size message size + * + * @retval 0 success + * @retval other failed + * + * @note None + */ + tls_os_status_t tls_os_queue_send(tls_os_queue_t *queue, + void *msg, + u32 msg_size); + +/** + * @brief This function is used to flush the contents of the message + queue. + * + * @param[in] *queue + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +tls_os_status_t tls_os_queue_flush(tls_os_queue_t *queue); + + +/** + * @brief This function waits for a message to be sent to a queue + * + * @param[in] *queue pointer to the event control block associated + with the desired queue + * @param[in] **msg pointer to the message received + * @param[in] msg_size message size + * @param[in] wait_time an optional timeout period (in clock ticks). + If non-zero, your task will wait for a message + to arrive at the queue up to the amount of time + specified by this argument. If you specify 0, + however, your task will wait forever at the + specified queue or, until a message arrives. + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_queue_receive(tls_os_queue_t *queue,void **msg, + u32 msg_size, + u32 wait_time); + +/** + * @brief This function creates a message mailbox if free event + control blocks are available. + * + * @param[in] **mailbox pointer to the event control clock (OS_EVENT) + associated with the created mailbox + * @param[in] mailbox_size size + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_mailbox_create(tls_os_mailbox_t **mailbox, u32 mailbox_size); + +/** + * @brief This function deletes a mailbox and readies all of the tasks + pending on the this mailbox. + * + * @param[in] *mailbox pointer to the event control block + associated with the desired mailbox. + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_mailbox_delete(tls_os_mailbox_t *mailbox); + +/** + * @brief This function sends a message to a mailbox + * + * @param[in] *mailbox pointer to the event control block associated + with the desired mailbox + * @param[in] *msg pointer to the message to send. + You MUST NOT send a NULL pointer + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_mailbox_send(tls_os_mailbox_t *mailbox,void *msg); + +/** + * @brief This function waits for a message to be sent to a mailbox + + * + * @param[in] *mailbox pointer to the event control block associated + with the desired mailbox + * @param[in] **msg pointer to the message received + * @param[in] wait_time an optional timeout period (in clock ticks). + If non-zero, your task will wait for a message + to arrive at the mailbox up to the amount of + time specified by this argument. + If you specify 0, however, your task will wait + forever at the specified mailbox or, + until a message arrives. +* + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_mailbox_receive(tls_os_mailbox_t *mailbox, + void **msg, + u32 wait_time); + +/** + * @brief This function is used by your application to obtain the + current value of the 32-bit counter which keeps track + of the number of clock ticks since the system starts. + * + * @param[in] None + * + * @retval current value of OSTime + * + * @note None + */ + u32 tls_os_get_time(void); + +/** + * @brief This function is used to disable interrupts by preserving + the state of interrupts + * + * @param[in] None + * + * @retval cpu_sr + * + * @note None + */ + u32 tls_os_set_critical(void); + +/** + * @brief This function is used to enable interrupts by preserving + the state of interrupts + * + * @param[in] cpu_sr + * + * @return None + * + * @note None + */ + void tls_os_release_critical(u32 cpu_sr); + + +/** + * @brief This function is called by your application code to create + a timer + * + * @param[in] **timer pointer to an OS_TMR data structure. + This is the 'handle' that your application will + use to reference the timer created + * @param[in] callback pointer to a callback function that will + be called when the timer expires. The callback + function must be declared as follows + void MyCallback (OS_TMR *ptmr, void *p_arg); + * @param[in] *callback_arg argument (a pointer) that is passed to + the callback function when it is called + * @param[in] period The 'period' being repeated for the timer. + If you specified 'OS_TMR_OPT_PERIODIC' as + an option, when the timer expires, it will + automatically restart with the same period. + * @param[in] repeat if repeat + * @param[in] *name pointer to an ASCII string that is used to + name the timer. Names are useful for + debugging. + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ + tls_os_status_t tls_os_timer_create(tls_os_timer_t **timer, + TLS_OS_TIMER_CALLBACK callback, + void *callback_arg, + u32 period, + bool repeat, + u8 *name); + +/** + * @brief This function is called by your application code to start + a timer. + * + * @param[in] *timer pointer to an OS_TMR + * + * @return None + * + * @note None + */ + void tls_os_timer_start(tls_os_timer_t *timer); + +/* +************************************************************************************************************************ +* CHANGE A TIMER WAIT TIME +* +* Description: This function is called by your application code to change a timer wait time. +* +* Arguments : timer Is a pointer to an OS_TMR +* +* ticks is the wait time +************************************************************************************************************************ +*/ + void tls_os_timer_change(tls_os_timer_t *timer, u32 ticks); + +/** + * @brief This function is called by your application code to stop + a timer + * + * @param[in] *timer pointer to the timer to stop. + * + * @return None + * + * @note None + */ + void tls_os_timer_stop(tls_os_timer_t *timer); + +/** + * @brief This function is called by your application code to delete + a timer + * + * @param[in] *timer pointer to the timer to delete + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +tls_os_status_t tls_os_timer_delete(tls_os_timer_t *timer); + +/** + * @brief This function is called to delay execution of the currently + running task until the specified number of system + ticks expires. This, of course, directly equates to + delaying the current task for some time to expire. + There will be no delay if the specified delay is 0. + If the specified delay is greater than 0 then, + a context switch will executed. + * + * @param[in] ticks the time delay that the task will be suspended + in number of clock 'ticks'.Note that by specifying 0, + the task will not be delayed. + * + * @return None + * + * @note None + */ + void tls_os_time_delay(u32 ticks); + + +/** + * @brief This function is used to display all the tasks' detail status. + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_os_disp_task_stat_info(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* end of WM_OSAL_H */ + diff --git a/sdk/Include/Platform/aes.h b/sdk/Include/Platform/aes.h new file mode 100644 index 0000000..12833cf --- /dev/null +++ b/sdk/Include/Platform/aes.h @@ -0,0 +1,186 @@ +/** + * @file aes.h + * @brief AES functions + * @copyright (c) 2003-2006, Jouni Malinen + * + * @note This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AES_H +#define AES_H +//#include "crypto/cryptoApi.h" +#include +#include "wm_type_def.h" +#define USE_AES +#define PSPUBLIC +#ifndef CRYPT_INVALID_KEYSIZE +#define CRYPT_INVALID_KEYSIZE -21 +#endif +#ifndef MAXBLOCKSIZE +#define MAXBLOCKSIZE 24 +#endif + +#define STORE32H(x, y) { \ +(y)[0] = (unsigned char)(((x)>>24)&255); \ +(y)[1] = (unsigned char)(((x)>>16)&255); \ +(y)[2] = (unsigned char)(((x)>>8)&255); \ +(y)[3] = (unsigned char)((x)&255); \ +} + +#define LOAD32H(x, y) { \ +x = ((unsigned long)((y)[0] & 255)<<24) | \ +((unsigned long)((y)[1] & 255)<<16) | \ +((unsigned long)((y)[2] & 255)<<8) | \ +((unsigned long)((y)[3] & 255)); \ +} + +#define byte(x, n) (((x) >> (8 * (n))) & 255) + +#define ROR(x, y) \ + ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | \ + ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) + +#define psTraceCrypto(x) + +/******************************************************************************/ +/* + Universal return codes +*/ +#define PS_SUCCESS 0 +#define PS_FAILURE -1 + +/* NOTE: Failure return codes MUST be < 0 */ +/* NOTE: The range for core error codes should be between -2 and -29 */ +#define PS_ARG_FAIL -6 /* Failure due to bad function param */ +#define PS_PLATFORM_FAIL -7 /* Failure as a result of system call error */ +#define PS_MEM_FAIL -8 /* Failure to allocate requested memory */ +#define PS_LIMIT_FAIL -9 /* Failure on sanity/limit tests */ +#define PS_UNSUPPORTED_FAIL -10 /* Unimplemented feature error */ +#define PS_DISABLED_FEATURE_FAIL -11 /* Incorrect #define toggle for feature */ +#define PS_PROTOCOL_FAIL -12 /* A protocol error occurred */ +#define PS_TIMEOUT_FAIL -13 /* A timeout occurred and MAY be an error */ +#define PS_INTERRUPT_FAIL -14 /* An interrupt occurred and MAY be an error */ +#define PS_PENDING -15 /* In process. Not necessarily an error */ +#define PS_EAGAIN -16 /* Try again later. Not necessarily an error */ + +#define PS_TRUE 1 +#define PS_FALSE 0 + +//typedef int int32; //µ÷ÓÃwm_type_def.h +//typedef unsigned int uint32; +//typedef short int16; +//typedef unsigned short uint16; +//typedef unsigned char uint8; + +//typedef long long int64; +//typedef unsigned long long uint64; +//#if (GCC_COMPILE==0) +//typedef unsigned int size_t; +//#endif + + +/******************************************************************************/ +#ifdef USE_AES +/******************************************************************************/ + + +#ifndef USE_AES_CBC_EXTERNAL +typedef struct { + unsigned int eK[64], dK[64]; + int Nr; +} psAesKey_t; + +typedef struct { + int blocklen; + unsigned char IV[16]; + psAesKey_t key; +#if defined(USE_AES_GCM) || defined(USE_AES_CCM) + unsigned char EncCtr[16]; + unsigned char CtrBlock[16]; +#endif +#ifdef USE_AES_GCM + unsigned char gInit[16]; + unsigned int TagTemp[4]; + unsigned char Hash_SubKey[16]; + unsigned int ProcessedBitCount[4]; + unsigned int InputBufferCount; + unsigned int OutputBufferCount; + union + { + unsigned char Buffer[128]; + unsigned int BufferAlignment; + } Input; +#endif /* USE_AES_GCM */ +#ifdef USE_AES_CCM + unsigned int ccmTagTemp[16 / sizeof(unsigned int)]; /* 32 */ + union + { + /* Used for formatting IV. */ + unsigned char Temporary[16]; + /* Used for processing Mac. */ + unsigned char Y0[16]; + } u; /* 48 */ +#endif /* USE_AES_CCM */ +} psAesCipher_t; + +typedef union { +#ifdef USE_AES + psAesCipher_t aes; +#endif +} psAesCipherContext_t; + +#endif /* USE_AES_CBC_EXTERNAL */ + +#endif /* USE_AES */ + +#if 0 +void * aes_encrypt_init(const u8 *key, size_t len); +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +void aes_encrypt_deinit(void *ctx); +void * aes_decrypt_init(const u8 *key, size_t len); +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +void aes_decrypt_deinit(void *ctx); +#endif + +#ifndef USE_AES +#error "Must Enable USE_AES in cryptoConfig.h!" +#endif + +#ifdef USE_AES +/******************************************************************************/ +/* + CBC Mode AES +*/ +PSPUBLIC int psAesInit(psAesCipherContext_t *ctx, unsigned char *IV, + unsigned char *key, unsigned int keylen); +PSPUBLIC int psAesDecrypt(psAesCipherContext_t *ctx, unsigned char *ct, + unsigned char *pt, unsigned int len); +PSPUBLIC int psAesEncrypt(psAesCipherContext_t *ctx, unsigned char *pt, + unsigned char *ct, unsigned int len); +/* + Block Mode AES +*/ +PSPUBLIC int psAesInitKey(const unsigned char *key, unsigned int keylen, + psAesKey_t *skey); +PSPUBLIC void psAesEncryptBlock(const unsigned char *pt, unsigned char *ct, + psAesKey_t *skey); +PSPUBLIC void psAesDecryptBlock(const unsigned char *ct, unsigned char *pt, + psAesKey_t *skey); + + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +#endif + +#endif /* AES_H */ + diff --git a/sdk/Include/Platform/wm_crypto_hard.h b/sdk/Include/Platform/wm_crypto_hard.h new file mode 100644 index 0000000..2009185 --- /dev/null +++ b/sdk/Include/Platform/wm_crypto_hard.h @@ -0,0 +1,563 @@ +/** + * @file wm_crypto_hard.h + * + * @brief hard crypto module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_CRYPTO_HARD_H +#define WM_CRYPTO_HARD_H + +#include "wm_type_def.h" +#include "aes.h" + +#ifndef min + #define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif /* min */ + +/** The base address of the registers of encryption/decryption module. */ +#define HR_CRYPTO_BASE_ADDR 0x40003200 +/** The address of the source text address register. */ +#define HR_CRYPTO_SRC_ADDR (HR_CRYPTO_BASE_ADDR + 0x00) +/** The address of the encrypted/decrypted text address register. */ +#define HR_CRYPTO_DEST_ADDR (HR_CRYPTO_BASE_ADDR + 0x04) +/** The address of the configuration register. */ +#define HR_CRYPTO_SEC_CFG (HR_CRYPTO_BASE_ADDR + 0x08) +/** The address of the control register. */ +#define HR_CRYPTO_SEC_CTRL (HR_CRYPTO_BASE_ADDR + 0x0C) +/** The address of the key0 register. */ +#define HR_CRYPTO_KEY0 (HR_CRYPTO_BASE_ADDR + 0x10) +/** The address of the key1 register. */ +#define HR_CRYPTO_KEY1 (HR_CRYPTO_BASE_ADDR + 0x14) +/** The address of the key2 register. */ +#define HR_CRYPTO_KEY2 (HR_CRYPTO_BASE_ADDR + 0x18) +/** The address of the key3 register. */ +#define HR_CRYPTO_KEY3 (HR_CRYPTO_BASE_ADDR + 0x1C) +/** The address of the key4 register. */ +#define HR_CRYPTO_KEY4 (HR_CRYPTO_BASE_ADDR + 0x20) +/** The address of the key5 register. */ +#define HR_CRYPTO_KEY5 (HR_CRYPTO_BASE_ADDR + 0x24) + +/** The address of the IV0 register. */ +#define HR_CRYPTO_IV0 (HR_CRYPTO_BASE_ADDR + 0x28) +/** The address of the IV1 register. */ +#define HR_CRYPTO_IV1 (HR_CRYPTO_BASE_ADDR + 0x20) + +/** The address of the encrypted/decrypted status register. */ +#define HR_CRYPTO_SEC_STS (HR_CRYPTO_BASE_ADDR + 0x30) + +/** The address of the SHA1/MD5 digest0 register. */ +#define HR_CRYPTO_SHA1_DIGEST0 (HR_CRYPTO_BASE_ADDR + 0x34) +/** The address of the SHA1/MD5 digest1 register. */ +#define HR_CRYPTO_SHA1_DIGEST1 (HR_CRYPTO_BASE_ADDR + 0x38) +/** The address of the SHA1/MD5 digest2 register. */ +#define HR_CRYPTO_SHA1_DIGEST2 (HR_CRYPTO_BASE_ADDR + 0x3C) +/** The address of the SHA1/MD5 digest3 register. */ +#define HR_CRYPTO_SHA1_DIGEST3 (HR_CRYPTO_BASE_ADDR + 0x40) +/** The address of the SHA1 digest4 and CRC in/output register. */ +#define HR_CRYPTO_SHA1_DIGEST4 (HR_CRYPTO_BASE_ADDR + 0x44) +/** The address of the RNG RESULT register. */ +#define HR_CRYPTO_RNG_RESULT (HR_CRYPTO_BASE_ADDR + 0x48) + + +/** The address of the CRC in register. */ +#define HR_CRYPTO_CRC_KEY HR_CRYPTO_SHA1_DIGEST4 +/** The address of the CRC output register. */ +#define HR_CRYPTO_CRC_RESULT HR_CRYPTO_SHA1_DIGEST4 + +#define ERR_CRY_OK 0 ///< No error, everything OK. +#define ERR_FAILURE -1 /* failure */ +#define ERR_ARG_FAIL -6 /* Failure due to bad function param */ + +//CRC +#define OUTPUT_REFLECT 1 +#define INPUT_REFLECT 2 + +typedef enum __CRYPTO_METHOD +{ + CRYPTO_METHOD_RSV=0, + CRYPTO_METHOD_RC4, + CRYPTO_METHOD_SHA1, + CRYPTO_METHOD_AES, + CRYPTO_METHOD_DES, + CRYPTO_METHOD_3DES, + CRYPTO_METHOD_CRC, ///< CRC + CRYPTO_METHOD_MD5 ///< MD5 +}CRYPTO_METHOD; + + /** + * The enum of the encryption/decryption way. + */ +typedef enum __CRYPTO_WAY +{ + CRYPTO_WAY_ENCRYPT=0, ///< Encryption + CRYPTO_WAY_DECRYPT ///< Decryption +}CRYPTO_WAY; + +typedef enum __CRYPTO_RNG_SWITCH +{ + CRYPTO_RNG_SWITCH_16 = 0, + CRYPTO_RNG_SWITCH_32 +}CRYPTO_RNG_SWITCH; + + /** + * The enum of the encryption/decryption mode, only for (aes des 3des). + */ +typedef enum __CRYPTO_MODE +{ + CRYPTO_MODE_ECB=0, ///< ECB + CRYPTO_MODE_CBC, ///< CBC + CRYPTO_MODE_CTR ///< CTR, only for AES +}CRYPTO_MODE; + /** + * The enum of the CRC type. + */ +typedef enum __CRYPTO_CRC_TYPE +{ + CRYPTO_CRC_TYPE_8=0, ///< 8 bits CRC + CRYPTO_CRC_TYPE_16_MODBUS, ///< 16 bits Modbus CRC + CRYPTO_CRC_TYPE_16_CCITT, ///< 16 bits CCITT CRC + CRYPTO_CRC_TYPE_32 ///< 32 bits CRC +}CRYPTO_CRC_TYPE; + + /** + * The struct of the CRC context. + */ +typedef struct { + u32 state; ///< The initial value input and result value output. + u8 type; ///< The type of CRC, refernec the CRYPTO_CRC_TYPE enum. + u8 mode; ///< The mode of CRC, bit0 means output reflection and bit1 means input reflection. +}psCrcContext_t; + +#if 1 + +struct hsha1_state { +#ifdef HAVE_NATIVE_INT64 + uint64 length; +#else + u32 lengthHi; + u32 lengthLo; +#endif /* HAVE_NATIVE_INT64 */ + u32 state[5], curlen; + unsigned char buf[64]; +}; +struct hmd5_state { +#ifdef HAVE_NATIVE_INT64 + uint64 length; +#else + u32 lengthHi; + u32 lengthLo; +#endif /* HAVE_NATIVE_INT64 */ + u32 state[4], curlen; + unsigned char buf[64]; +}; + +typedef union { + struct hsha1_state sha1; + struct hmd5_state md5; +} hsDigestContext_t; + +typedef u32 hstm_digit; +typedef struct { + s16 used, alloc, sign; + hstm_digit *dp; +} hstm_int; + +struct sha1_state { +#ifdef HAVE_NATIVE_INT64 + u64 length; +#else + u32 lengthHi; + u32 lengthLo; +#endif /* HAVE_NATIVE_INT64 */ + u32 state[5], curlen; + unsigned char buf[64]; +}; + +struct md5_state { +#ifdef HAVE_NATIVE_INT64 + u64 length; +#else + u32 lengthHi; + u32 lengthLo; +#endif /* HAVE_NATIVE_INT64 */ + u32 state[4], curlen; + unsigned char buf[64]; +}; + +typedef union { + struct sha1_state sha1; + struct md5_state md5; +} psDigestContext_t; + +typedef struct { + unsigned char state[256]; + u32 byteCount; + unsigned char x; + unsigned char y; +} psRc4Key_t; +#define DES3_KEY_LEN 24 +#define DES3_IV_LEN 8 +#define DES_KEY_LEN 8 + +typedef struct { + u32 ek[3][32], dk[3][32]; +} psDes3Key_t; + +/* + A block cipher CBC structure + */ +typedef struct { + s32 blocklen; + unsigned char IV[8]; + psDes3Key_t key; +} des3_CBC; + +typedef union { + psRc4Key_t arc4; + des3_CBC des3; + psAesCipher_t aes; +} psCipherContext_t; + +#endif + +/** + * @brief This function is used to stop random produce. + * + * @param[in] None + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_random_stop(void); + + +/** + * @brief This function initializes random digit seed and BIT number. + * + * @param[in] seed The random digit seed. + * @param[in] rng_switch The random digit bit number. (0: 16bit 1:32bit) + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_random_init(u32 seed, CRYPTO_RNG_SWITCH rng_switch); + + +/** + * @brief This function is used to get random digit content. + * + * @param[in] out Pointer to the output of random digit. + * @param[in] len The random digit bit number will output. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_random_bytes(unsigned char *out, u32 len); + + +/** + * @brief This function initializes a RC4 encryption algorithm, + * i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] key Pointer to the key. + * @param[in] keylen the length of key. + * + * @retval 0 success + * @retval other failed + + * + * @note The first parameter ctx must be a structure which is allocated externally. + * And all of Context parameters in the initializing methods should be allocated externally too. + */ +int tls_crypto_rc4_init(psCipherContext_t * ctx, const unsigned char *key, u32 keylen); + +/** + * @brief This function encrypts a variable length data stream according to RC4. + * The RC4 algorithm it generates a "keystream" which is simply XORed with the plaintext to produce the ciphertext stream. + * Decryption is exactly the same as encryption. This function also decrypts a variable length data stream according to RC4. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] in Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length. + * @param[in] out Pointer to the resulting ciphertext data stream. + * @param[in] len Length of the plaintext data stream in octets. + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned, and it needs to operate in words, not in bytes. + */ +int tls_crypto_rc4(psCipherContext_t * ctx, unsigned char *in, unsigned char *out, u32 len); + +/** + * @brief This function initializes a AES encryption algorithm, i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] IV Pointer to the Initialization Vector + * @param[in] key Pointer to the key. + * @param[in] keylen the length of key. + * @param[in] cbc the encryption mode, AES supports ECB/CBC/CTR modes. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_aes_init(psCipherContext_t * ctx, const unsigned char *IV, const unsigned char *key, u32 keylen, CRYPTO_MODE cbc); + +/** + * @brief This function encrypts or decrypts a variable length data stream according to AES. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] in Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length. + * @param[in] out Pointer to the resulting ciphertext data stream. + * @param[in] len Length of the plaintext data stream in octets. + * @param[in] dec The cryption way which indicates encryption or decryption. + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned, and it needs to operate in words, not in bytes. + */ +int tls_crypto_aes_encrypt_decrypt(psCipherContext_t * ctx, unsigned char *in, unsigned char *out, u32 len, CRYPTO_WAY dec); + +/** + * @brief This function initializes a 3DES encryption algorithm, i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] IV Pointer to the Initialization Vector + * @param[in] key Pointer to the key. + * @param[in] keylen the length of key. + * @param[in] cbc the encryption mode, 3DES supports ECB/CBC modes. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_3des_init(psCipherContext_t * ctx, const unsigned char *IV, const unsigned char *key, u32 keylen, CRYPTO_MODE cbc); + +/** + * @brief This function encrypts or decrypts a variable length data stream according to 3DES. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] in Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length. + * @param[in] out Pointer to the resulting ciphertext data stream. + * @param[in] len Length of the plaintext data stream in octets. + * @param[in] dec The cryption way which indicates encryption or decryption. + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned, and it needs to operate in words, not in bytes. + */ +int tls_crypto_3des_encrypt_decrypt(psCipherContext_t * ctx, unsigned char *in, unsigned char *out, u32 len, CRYPTO_WAY dec); + +/** + * @brief This function initializes a DES encryption algorithm, i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] IV Pointer to the Initialization Vector + * @param[in] key Pointer to the key. + * @param[in] keylen the length of key. + * @param[in] cbc the encryption mode, DES supports ECB/CBC modes. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_des_init(psCipherContext_t * ctx, const unsigned char *IV, const unsigned char *key, u32 keylen, CRYPTO_MODE cbc); + +/** + * @brief This function encrypts or decrypts a variable length data stream according to DES. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] in Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length. + * @param[in] out Pointer to the resulting ciphertext data stream. + * @param[in] len Length of the plaintext data stream in octets. + * @param[in] dec The cryption way which indicates encryption or decryption. + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned, and it needs to operate in words, not in bytes. + */ +int tls_crypto_des_encrypt_decrypt(psCipherContext_t * ctx, unsigned char *in, unsigned char *out, u32 len, CRYPTO_WAY dec); + +/** + * @brief This function initializes a CRC algorithm, i.e. fills the psCrcContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the CRC Context. + * @param[in] key The initialization key. + * @param[in] crc_type The CRC type, supports CRC8/CRC16 MODBUS/CRC16 CCITT/CRC32 + * @param[in] mode Set input or outpu reflect. + * @param[in] dec The cryption way which indicates encryption or decryption. + * see OUTPUT_REFLECT + * see INPUT_REFLECT + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_crc_init(psCrcContext_t * ctx, u32 key, CRYPTO_CRC_TYPE crc_type, u8 mode); + +/** + * @brief This function updates the CRC value with a variable length bytes. + * This function may be called as many times as necessary, so the message may be processed in blocks. + * + * @param[in] ctx Pointer to the CRC Context. + * @param[in] in Pointer to a variable length bytes + * @param[in] len The bytes 's length + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned. + */ +int tls_crypto_crc_update(psCrcContext_t * ctx, unsigned char *in, u32 len); + +/** + * @brief This function ends a CRC operation and produces a CRC value. + * + * @param[in] ctx Pointer to the CRC Context. + * @param[in] crc_val Pointer to the CRC value. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_crc_final(psCrcContext_t * ctx, u32 *crc_val); + +/** + * @brief This function initializes Message-Diggest context for usage in SHA1 algorithm, starts a new SHA1 operation and writes a new Digest Context. + * + * @param[in] md Pointer to the SHA1 Digest Context. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +void tls_crypto_sha1_init(psDigestContext_t * md); + +/** + * @brief Process a message block using SHA1 algorithm. + * This function performs a SHA1 block update operation. It continues an SHA1 message-digest operation, + * by processing InputLen-byte length message block pointed to by buf, and by updating the SHA1 context pointed to by md. + * This function may be called as many times as necessary, so the message may be processed in blocks. + * + * @param[in] md Pointer to the SHA1 Digest Context. + * @param[in] buf InputLen-byte length message block + * @param[in] len The buf 's length + * + * @returnl None + * + * @note None + */ +void tls_crypto_sha1_update(psDigestContext_t * md, const unsigned char *buf, u32 len); + +/** + * @brief This function ends a SHA1 operation and produces a Message-Digest. + * This function finalizes SHA1 algorithm, i.e. ends an SHA1 Message-Digest operation, + * writing the Message-Digest in the 20-byte buffer pointed to by hash in according to the information stored in context. + * + * @param[in] md Pointer to the SHA1 Digest Context. + * @param[in] hash Pointer to the Message-Digest + * + * @retval 20 success, return the hash size. + * @retval <0 failed + + * + * @note None + */ +int tls_crypto_sha1_final(psDigestContext_t * md, unsigned char *hash); + +/** + * @brief This function initializes Message-Diggest context for usage in MD5 algorithm, starts a new MD5 operation and writes a new Digest Context. + * This function begins a MD5 Message-Diggest Algorithm, i.e. fills the psDigestContext_t structure pointed to by md with necessary data. + * MD5 is the algorithm which takes as input a message of arbitrary length and produces as output a 128-bit "fingerprint" or "message digest" of the input. + * It is conjectured that it is computationally infeasible to produce two messages having the same message digest, + * or to produce any message having a given prespecified target message digest. + * + * @param[in] md MD5 Digest Context. + * + * @return None + * + * @note None + */ +void tls_crypto_md5_init(psDigestContext_t * md); + +/** + * @brief Process a message block using MD5 algorithm. + * This function performs a MD5 block update operation. It continues an MD5 message-digest operation, + * by processing InputLen-byte length message block pointed to by buf, and by updating the MD5 context pointed to by md. + * This function may be called as many times as necessary, so the message may be processed in blocks. + * + * @param[in] md MD5 Digest Context. + * @param[in] buf InputLen-byte length message block + * @param[in] len The buf 's length + * + * @return None + * + * @note None + */ +void tls_crypto_md5_update(psDigestContext_t * md, const unsigned char *buf, u32 len); + +/** + * @brief This function ends a MD5 operation and produces a Message-Digest. + * This function finalizes MD5 algorithm, i.e. ends an MD5 Message-Digest operation, + * writing the Message-Digest in the 16-byte buffer pointed to by hash in according to the information stored in context. + * + * @param[in] md MD5 Digest Context. + * @param[in] hash the Message-Digest + * + * @retval 16 success, return the hash size. + * @retval <0 failed + * + * @note None + */ +int tls_crypto_md5_final(psDigestContext_t * md, unsigned char *hash); + +/** + * @brief This function implements the large module power multiplication algorithm. + * res = a**e (mod n) + * + * @param[in] a Pointer to a bignumber. + * @param[in] e Pointer to a bignumber. + * @param[in] n Pointer to a bignumber. + * @param[out] res Pointer to the result bignumber. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_exptmod(hstm_int *a, hstm_int *e, hstm_int *n, hstm_int *res); + +/** + * @brief This function initializes the encryption module. + * + * @param None + * + * @return None + * + * @note None + */ +void tls_crypto_init(void); + +#endif + + diff --git a/sdk/Include/Platform/wm_fwup.h b/sdk/Include/Platform/wm_fwup.h new file mode 100644 index 0000000..f3fc399 --- /dev/null +++ b/sdk/Include/Platform/wm_fwup.h @@ -0,0 +1,343 @@ +/** + * @file wm_fwup.h + * + * @brief Firmware upgrade + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#ifndef WM_FWUP_H +#define WM_FWUP_H +#include "wm_osal.h" +#include "list.h" + +/** firmware update status */ +#define TLS_FWUP_STATUS_OK (0) +#define TLS_FWUP_STATUS_EINVALID (1) +#define TLS_FWUP_STATUS_EMEM (2) +#define TLS_FWUP_STATUS_EPERM (3) +#define TLS_FWUP_STATUS_EBUSY (4) +#define TLS_FWUP_STATUS_ESESSIONID (5) +#define TLS_FWUP_STATUS_EIO (6) +#define TLS_FWUP_STATUS_ESIGNATURE (7) +#define TLS_FWUP_STATUS_ECRC (8) +#define TLS_FWUP_STATUS_EUNDEF (9) + +/** firmware signature */ +#define TLS_FWUP_IMAGE_SIGNATURE_WORD 0x64365577 +#define TLS_FWUP_IMAGE_SIGNATURE_WORD_B 0x62161900 + +/** firmware block size for one time */ +#define TLS_FWUP_BLK_SIZE 512 + +/** firmware update request status */ +#define TLS_FWUP_REQ_STATUS_IDLE (0) +#define TLS_FWUP_REQ_STATUS_BUSY (1) +#define TLS_FWUP_REQ_STATUS_SUCCESS (2) +#define TLS_FWUP_REQ_STATUS_FIO (3) +#define TLS_FWUP_REQ_STATUS_FSIGNATURE (4) +#define TLS_FWUP_REQ_STATUS_FMEM (5) +#define TLS_FWUP_REQ_STATUS_FCRC (6) +#define TLS_FWUP_REQ_STATUS_FCOMPLETE (7) + +/** firmware update state */ +#define TLS_FWUP_STATE_UNDEF (0xffff) +#define TLS_FWUP_STATE_BUSY (1 << 0) +#define TLS_FWUP_STATE_COMPLETE (1 << 1) +#define TLS_FWUP_STATE_ERROR_IO (1 << 2) +#define TLS_FWUP_STATE_ERROR_SIGNATURE (1 << 3) +#define TLS_FWUP_STATE_ERROR_MEM (1 << 4) +#define TLS_FWUP_STATE_ERROR_CRC (1 << 5) + +#define TLS_FWUP_STATE_ERROR (TLS_FWUP_STATE_ERROR_IO | TLS_FWUP_STATE_ERROR_SIGNATURE | TLS_FWUP_STATE_ERROR_MEM | TLS_FWUP_STATE_ERROR_CRC) + +/** update type 0:firmware, 1: data */ +#define TLS_FWUP_DEST_SPECIFIC_FIRMWARE (0) +#define TLS_FWUP_DEST_SPECIFIC_DATA (1) + +enum IMAGE_TYPE_ENUM{ + IMG_TYPE_OLD_PLAIN = 0, + IMG_TYPE_FLASHBIN = 1, + IMG_TYPE_SECBOOT = 2, + IMG_TYPE_NEW_PLAIN = 3 +}; +enum { + NOT_ZIP_FILE = 0, + ZIP_FILE = 1 +}; +typedef struct __T_BOOTER +{ + unsigned int magic_no; + unsigned short img_type; + unsigned short zip_type; /** image type zip flag, 0: non-zip, 1:zip*/ + unsigned int run_img_addr; /** run area image start address */ + unsigned int run_img_len; /** run area image length */ + unsigned int run_org_checksum; /** run area image checksum */ + unsigned int upd_img_addr; /** upgrade area image start address*/ + unsigned int upd_img_len; /** upgrade area image length*/ + unsigned int upd_checksum; /** upgrade area image checksum */ + unsigned int upd_no; + unsigned char ver[16]; + unsigned int hd_checksum; +} T_BOOTER; + +/** Structure for firmware image header */ +struct tls_fwup_image_hdr { + u32 magic; + u8 crc8; + u8 dest_specific; + u16 dest_offset; // unit: 4KB, valid when dest_specific is TRUE + u32 file_len; + char time[4]; +}; + +/** Structure for one packet data */ +struct tls_fwup_block { + u16 number; //0~Sum-1 + u16 sum; + u8 data[TLS_FWUP_BLK_SIZE]; + u32 crc32; + u8 pad[8]; +}; + +/** Enumeration for image soure when firmware update */ +enum tls_fwup_image_src { + TLS_FWUP_IMAGE_SRC_LUART = 0, /**< LOW SPEED UART */ + TLS_FWUP_IMAGE_SRC_HUART, /**< HIGH SPEED UART */ + TLS_FWUP_IMAGE_SRC_HSPI, /**< HIGH SPEED SPI */ + TLS_FWUP_IMAGE_SRC_WEB /**< WEB SERVER */ +}; + +/** Structure for firmware update request */ +struct tls_fwup_request { + struct dl_list list; + u8 *data; + u32 data_len; + int status; + void (*complete)(struct tls_fwup_request *request, void *arg); + void *arg; +}; + +/** Structure for firmware update */ +struct tls_fwup { + struct dl_list wait_list; + tls_os_sem_t *list_lock; + + bool busy; + enum tls_fwup_image_src current_image_src; + u16 current_state; + u32 current_session_id; + + u32 received_len; + u32 total_len; + u32 updated_len; + u32 program_base; + u32 program_offset; + s32 received_number; +}; + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup FWUP_APIs FWUP APIs + * @brief firmware upgrade APIs + */ + +/** + * @addtogroup FWUP_APIs + * @{ + */ + +/** + * @brief This function is used to initialize firmware update task + * + * @param[in] None + * + * @retval TLS_FWUP_STATUS_OK initial success + * @retval TLS_FWUP_STATUS_EBUSY already initialed + * @retval TLS_FWUP_STATUS_EMEM memory error + * @note None + */ +int tls_fwup_init(void); + +/** + * @brief This function is used to enter firmware update progress. + * + * @param[in] image_src image file's source, + from TLS_FWUP_IMAGE_SRC_LUART, + TLS_FWUP_IMAGE_SRC_WEB,TLS_FWUP_IMAGE_SRC_HUART, + TLS_FWUP_IMAGE_SRC_HSPI + * + * @retval non-zero successfully, return session id + * @retval 0 failed + * + * @note None + */ +u32 tls_fwup_enter(enum tls_fwup_image_src image_src); + +/** + * @brief This function is used to exit firmware update progress. + * + * @param[in] session_id session identity of firmware update progress + * + * @retval TLS_FWUP_STATUS_OK exit success + * @retval TLS_FWUP_STATUS_EPERM globle param is not initialed + * @retval TLS_FWUP_STATUS_ESESSIONID error session id + * @retval TLS_FWUP_STATUS_EBUSY update state is busy + * + * @note None + */ +int tls_fwup_exit(u32 session_id); + +/** + * @brief This function is used to start update progress + * + * @param[in] session_id current sessin id + * @param[in] *data the data want to update + * @param[in] data_len data length + * + * @retval TLS_FWUP_STATUS_OK updade success + * @retval TLS_FWUP_STATUS_EPERM globle param is not initialed + * @retval TLS_FWUP_STATUS_ESESSIONID error session id + * @retval TLS_FWUP_STATUS_EINVALID invalid param + * @retval TLS_FWUP_STATUS_EMEM memory error + * @retval TLS_FWUP_STATUS_EIO write flash error + * @retval TLS_FWUP_STATUS_ECRC crc error + * @retval TLS_FWUP_STATUS_ESIGNATURE signature error + * @retval TLS_FWUP_STATUS_EUNDEF other error + * + * @note None + */ +int tls_fwup_request_sync(u32 session_id, u8 *data, u32 data_len); + +/** + * @brief This function is used to get current update status + * + * @param[in] session_id current sessin id + * + * @retval current state TLS_FWUP_STATUS_OK to TLS_FWUP_STATUS_EUNDEF + * + * @note None + */ +u16 tls_fwup_current_state(u32 session_id); + +/** + * @brief This function is used to reset the update information + * + * @param[in] session_id current sessin id + * + * @retval TLS_FWUP_STATUS_OK reset success + * @retval TLS_FWUP_STATUS_EPERM globle param is not initialed + * @retval TLS_FWUP_STATUS_ESESSIONID error session id + * @retval TLS_FWUP_STATUS_EBUSY update state is busy + * + * @note None + */ +int tls_fwup_reset(u32 session_id); + +/** + * @brief This function is used to clear error update state + * + * @param[in] session_id current sessin id + * + * @retval TLS_FWUP_STATUS_OK reset success + * + * @note None + */ +int tls_fwup_clear_error(u32 session_id); + +/** + * @brief This function is used to set update state to + TLS_FWUP_STATE_ERROR_CRC + * + * @param[in] session_id current sessin id + * + * @retval TLS_FWUP_STATUS_OK set success + * @retval TLS_FWUP_STATUS_EPERM globle param is not initialed + * @retval TLS_FWUP_STATUS_ESESSIONID error session id + * + * @note None + */ +int tls_fwup_set_crc_error(u32 session_id); + +/** + * @brief This function is used to get progress's status + * + * @param[in] None + * + * @retval TRUE busy + * @retval FALSE idle + * + * @note None + */ +int tls_fwup_get_status(void); + +/** + * @brief This function is used to set update packet number + * + * @param[in] number + * + * @retval TLS_FWUP_STATUS_OK success + * @retval TLS_FWUP_STATE_UNDEF failed + * + * @note None + */ +int tls_fwup_set_update_numer(int number); + +/** + * @brief This function is used to get received update packet number + + * + * @param[in] None + * + * @retval return current packet number + * + * @note None + */ +int tls_fwup_get_current_update_numer(void); + + +/** + * @brief This function is used to get current session id + * + * @param[in] None + * + * @retval non-zoro session id + * @retval 0 error + * + * @note None + */ +int tls_fwup_get_current_session_id(void); + +/** + * @brief This function is used to check image header + * + * @param[in] None + * + * @retval TRUE: success + * @retval FALSE: failure + * + * @note None + */ +int tls_fwup_img_header_check(T_BOOTER *img_param); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_FWUP_H */ + diff --git a/sdk/Include/Platform/wm_mem.h b/sdk/Include/Platform/wm_mem.h new file mode 100644 index 0000000..2c0b8c3 --- /dev/null +++ b/sdk/Include/Platform/wm_mem.h @@ -0,0 +1,201 @@ +/***************************************************************************** +* +* File Name : wm_mem.h +* +* Description: memory manager Module +* +* Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-6-12 +*****************************************************************************/ + + +#ifndef WM_MEM_H +#define WM_MEM_H + + +#include "wm_type_def.h" +//#define WM_MEM_DEBUG 1 +#if WM_MEM_DEBUG + +#include "list.h" + +#define MEM_HEADER_PATTERN 0x76028412 +#define MEM_TAILER_PATTERN 0x83395627 +#define MEM_FREED_PATTERN 0x82962503 + +extern u32 alloc_heap_mem_bytes; +extern u32 alloc_heap_mem_blk_cnt; +extern u32 alloc_heap_mem_max_size; + +// +// Note: it's important that the size of MP_MEMORY_BLOCK structure +// be multiple of 16 bytes. +// +typedef struct _MEMORY_BLOCK { + + struct dl_list list; /**< Pointer to next and previous blocks */ + char *file; /**< name of the file which is doing the allocation */ + u32 pad; /**< pad to make the size of whole structure multiple of 16 bytes */ + u32 line; /**< line number where allocated */ + u32 length; /**< ulong index of trailer (=(length/4)-1 relative to data start */ + u32 header_pattern; /**< To help detect underflows. A trailer is also added to find overflows */ +} MEMORY_BLOCK, *PMEMORY_BLOCK; +typedef struct _MEMORY_PATTERN{ + u32 pattern0; + //u32 pattern1; + //u32 pattern2; + //u32 pattern3; +}MEMORY_PATTERN, *PMEMORY_PATTERN; +void mem_free_debug(void *p, char* file, int line); +#define tls_mem_free(p) mem_free_debug( p, __FILE__, __LINE__) +void *mem_alloc_debug(u32 size, char* file, int line); +void mem_free_allocated_blocks(void); +#define tls_mem_alloc(size) mem_alloc_debug(size, __FILE__, __LINE__) +void * mem_realloc_debug(void *mem_address, u32 size, char* file, int line); +#define tls_mem_realloc(mem_address, size) mem_realloc_debug(mem_address, size, __FILE__, __LINE__) + +void *mem_calloc_debug(u32 n,u32 size,char * file,int line); +#define tls_mem_calloc(n, size) mem_calloc_debug(n, size, __FILE__, __LINE__) +void tls_mem_alloc_info(void); +int is_safe_addr_debug(void* p, u32 len, char* file, int line); +#define tls_is_safe_addr(p, len) is_safe_addr_debug(p, len, __FILE__, __LINE__) +#if 1 +#define MEMCPY memcpy +#define SMEMCPY MEMCPY +#else +#define MEMCPY(dst,src,len) do { \ + if(tls_is_safe_addr(dst, len)){ \ + memcpy(dst,src,len);}}while(0) + +#define SMEMCPY(dst,src,len) do { \ + if(tls_is_safe_addr(dst, len)){ \ + memcpy(dst,src,len);}}while(0) +#endif +#else /* WM_MEM_DEBUG */ + +void * mem_alloc_debug(u32 size); +void mem_free_debug(void *p); +void * mem_realloc_debug(void *mem_address, u32 size); +void *mem_calloc_debug(u32 length, u32 size); + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup MEM_APIs MEM APIs + * @brief memory manager APIs + */ + +/** + * @addtogroup MEM_APIs + * @{ + */ + +/** + * @brief This function is called by memory allocation + * + * @param[in] size + * + * @retval NULL malloc failed + * @retval pointer pointer to the address of the allocated memory + * + * @note None + */ +#define tls_mem_alloc(size) mem_alloc_debug(size) + +/** + * @brief This function is used to free memory + * + * @param None + * + * @return None + * + * @note None + */ +#define tls_mem_free mem_free_debug + +/** + * @brief This function is used to realloc memory + * + * @param None + * + * @retval NULL realloc failed + * @retval Pointer pointer to the address of the allocated memory + * + * @note None + */ +#define tls_mem_realloc mem_realloc_debug + +/** + * @brief This function is used to calloc memory + * + * @param None + * + * @retval NULL realloc failed + * @retval Pointer pointer to the address of the allocated memory + * + * @note None + */ +#define tls_mem_calloc mem_calloc_debug + +/** + * @brief This function is used to copy memory content from one address to another address + * + * @param[in] dst pointer to destination address + * @param[in] src pointer to source address + * @param[in] len length to copy + * + * @retval dst + * + * @note None + */ +#define MEMCPY(dst,src,len) memcpy(dst,src,len) + +/** + * @brief This function is used to copy memory content from one address to another address + * + * @param[in] dst pointer to destination address + * @param[in] src pointer to source address + * @param[in] len length to copy + * + * @retval dst + * + * @note None + */ +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) + +/** + * @brief This function is used to get available memory + * + * @param[in] None + * + * @return None + * + * @note This api just can get coarse memory that is remained + */ +unsigned int tls_mem_get_avail_heapsize(void); + + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_MEM_DEBUG */ + +#endif /* TLS_MEM_H */ + diff --git a/sdk/Include/Platform/wm_params.h b/sdk/Include/Platform/wm_params.h new file mode 100644 index 0000000..079ed7e --- /dev/null +++ b/sdk/Include/Platform/wm_params.h @@ -0,0 +1,615 @@ +/** + * @file wm_params.h + * + * @brief param manager Module + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef TLS_PARAMS_H +#define TLS_PARAMS_H + +#include "wm_type_def.h" + +/*************************************************************************************** + ID | Data Type +*************************************************************************************** +TLS_PARAM_ID_SSID | struct tls_param_ssid ssid +TLS_PARAM_ID_ENCRY | u8 encry +TLS_PARAM_ID_KEY | struct tls_param_key key +TLS_PARAM_ID_IP | struct tls_param_ip ipcfg +TLS_PARAM_ID_AUTOMODE | u8 auto_mode +TLS_PARAM_ID_DEFSOCKET | N/A +TLS_PARAM_ID_BSSID | struct tls_param_bssid bssid +TLS_PARAM_ID_CHANNEL | u8 channel +TLS_PARAM_ID_CHANNEL_EN | u8 channel_enable +TLS_PARAM_ID_COUNTRY_REGION | u8 wireless_region +TLS_PARAM_ID_WPROTOCOL | u8 wireless_protocol +TLS_PARAM_ID_ADHOC_AUTOCREATE | N/A +TLS_PARAM_ID_ROAMING | u8 auto_roam +TLS_PARAM_ID_AUTO_RETRY_CNT | N/A +TLS_PARAM_ID_WBGR | struct tls_param_bgr wbgr +TLS_PARAM_ID_USRINTF | N/A +TLS_PARAM_ID_AUTO_TRIGGER_LENGTH | N/A +TLS_PARAM_ID_DEBUG_MODE | N/A +TLS_PARAM_ID_HARDVERSION | struct tls_param_hardware_version hardware_v +TLS_PARAM_ID_BRDSSID | u8 ssid_broadcast_enable +TLS_PARAM_ID_DNSNAME | u8 local_dnsname[32] +TLS_PARAM_ID_DEVNAME | u8 local_device_name[32] +TLS_PARAM_ID_PSM | u8 auto_powersave +TLS_PARAM_ID_ORAY_CLIENT | N/A +TLS_PARAM_ID_UPNP | N/A +TLS_PARAM_ID_UART | struct tls_param_uart uart_cfg +TLS_PARAM_ID_WPS | struct tls_param_wps wps +TLS_PARAM_ID_AUTO_TRIGGER_PERIOD | N/A +TLS_PARAM_ID_ESCAPE_CHAR | N/A +TLS_PARAM_ID_ESCAPE_PERIOD | N/A +TLS_PARAM_ID_IO_MODE | N/A +TLS_PARAM_ID_CMD_MODE | N/A +TLS_PARAM_ID_PASSWORD | u8 PassWord[6] +TLS_PARAM_ID_CHANNEL_LIST | u16 channellist +TLS_PARAM_ID_WEBS_CONFIG | struct tls_webs_cfg WebsCfg +TLS_PARAM_ID_ONESHOT_CFG | u8 oneshotflag +TLS_PARAM_ID_SHA1 | N/A +TLS_PARAM_ID_ORIGIN_KEY | N/A +TLS_PARAM_ID_ORIGIN_SSID | N/A +TLS_PARAM_ID_AUTO_RECONNECT | u8 auto_reconnect +***************************************************************************************/ + +#define TLS_PARAM_STATUS_OK (0) +#define TLS_PARAM_STATUS_EINVALID (1) +#define TLS_PARAM_STATUS_EMEM (2) +#define TLS_PARAM_STATUS_EIO (3) +#define TLS_PARAM_STATUS_EPERM (4) +#define TLS_PARAM_STATUS_EINVALIDID (5) + +/** MACRO of Magic number initial value */ +#define TLS_PARAM_INIT_MAGIC (0xffffffff) +/** MACRO of Magic number valid value */ +#define TLS_PARAM_MAGIC (0x4947414d) + +/** MACRO of parameter partition number */ +#define TLS_PARAM_PARTITION_NUM (2) + +/** MACRO of parameter identity */ +#define TLS_PARAM_ID_ALL (-1) +#define TLS_PARAM_ID_SSID (0) +#define TLS_PARAM_ID_ENCRY (1) +#define TLS_PARAM_ID_KEY (2) +#define TLS_PARAM_ID_IP (3) +#define TLS_PARAM_ID_AUTOMODE (4) +#define TLS_PARAM_ID_DEFSOCKET (5) +#define TLS_PARAM_ID_BSSID (6) +#define TLS_PARAM_ID_CHANNEL (7) +#define TLS_PARAM_ID_CHANNEL_EN (8) +#define TLS_PARAM_ID_COUNTRY_REGION (9) +#define TLS_PARAM_ID_WPROTOCOL (10) +#define TLS_PARAM_ID_ADHOC_AUTOCREATE (11) +#define TLS_PARAM_ID_ROAMING (12) +#define TLS_PARAM_ID_AUTO_RETRY_CNT (13) +#define TLS_PARAM_ID_WBGR (14) +#define TLS_PARAM_ID_USRINTF (15) +#define TLS_PARAM_ID_AUTO_TRIGGER_LENGTH (16) +#define TLS_PARAM_ID_DEBUG_MODE (17) +#define TLS_PARAM_ID_HARDVERSION (18) +#define TLS_PARAM_ID_BRDSSID (19) +#define TLS_PARAM_ID_DNSNAME (20) +#define TLS_PARAM_ID_DEVNAME (21) +#define TLS_PARAM_ID_PSM (22) +#define TLS_PARAM_ID_ORAY_CLIENT (23) +#define TLS_PARAM_ID_UPNP (24) +#define TLS_PARAM_ID_UART (26) +#define TLS_PARAM_ID_WPS (27) +#define TLS_PARAM_ID_AUTO_TRIGGER_PERIOD (28) +#define TLS_PARAM_ID_ESCAPE_CHAR (29) +#define TLS_PARAM_ID_ESCAPE_PERIOD (30) +#define TLS_PARAM_ID_IO_MODE (31) +#define TLS_PARAM_ID_CMD_MODE (32) +#define TLS_PARAM_ID_PASSWORD (33) +#define TLS_PARAM_ID_CHANNEL_LIST (34) +#define TLS_PARAM_ID_WEBS_CONFIG (35) +#define TLS_PARAM_ID_ONESHOT_CFG (36) +#define TLS_PARAM_ID_SHA1 (37) +#define TLS_PARAM_ID_ORIGIN_KEY (38) +#define TLS_PARAM_ID_ORIGIN_SSID (39) +#define TLS_PARAM_ID_AUTO_RECONNECT (40) +#define TLS_PARAM_ID_QUICK_CONNECT (41) +#define TLS_PARAM_ID_KEY_CHANGE (42) +#define TLS_PARAM_ID_SSID_CHANGE (43) +#define TLS_PARAM_ID_SOFTAP_SSID (44) +#define TLS_PARAM_ID_SOFTAP_PSK (45) +#define TLS_PARAM_ID_SOFTAP_ENCRY (46) +#define TLS_PARAM_ID_SOFTAP_KEY (47) +#define TLS_PARAM_ID_SOFTAP_IP (48) +#define TLS_PARAM_ID_SOFTAP_CHANNEL (49) +#define TLS_PARAM_ID_SOFTAP_WBGR (50) + +#define TLS_PARAM_ID_SNTP_SERVER1 (51) +#define TLS_PARAM_ID_SNTP_SERVER2 (52) +#define TLS_PARAM_ID_SNTP_SERVER3 (53) +#define TLS_PARAM_ID_TEM_OFFSET (54) + +#define TLS_PARAM_ID_MAX (55) +/** MACRO of Physical moe of Ieee802.11 */ +#define TLS_PARAM_PHY_11BG_MIXED (0) +#define TLS_PARAM_PHY_11B (1) +#define TLS_PARAM_PHY_11BGN_MIXED (2) +/** MACRO of Ieee802.11 Tx Rate for parameter set */ +#define TLS_PARAM_TX_RATEIDX_1M (0) +#define TLS_PARAM_TX_RATEIDX_2M (1) +#define TLS_PARAM_TX_RATEIDX_5_5M (2) +#define TLS_PARAM_TX_RATEIDX_11M (3) +#define TLS_PARAM_TX_RATEIDX_6M (4) +#define TLS_PARAM_TX_RATEIDX_9M (5) +#define TLS_PARAM_TX_RATEIDX_12M (6) +#define TLS_PARAM_TX_RATEIDX_18M (7) +#define TLS_PARAM_TX_RATEIDX_24M (8) +#define TLS_PARAM_TX_RATEIDX_36M (9) +#define TLS_PARAM_TX_RATEIDX_48M (10) +#define TLS_PARAM_TX_RATEIDX_54M (11) +#define TLS_PARAM_TX_RATEIDX_MCS0 (12) +#define TLS_PARAM_TX_RATEIDX_MCS1 (13) +#define TLS_PARAM_TX_RATEIDX_MCS2 (14) +#define TLS_PARAM_TX_RATEIDX_MCS3 (15) +#define TLS_PARAM_TX_RATEIDX_MCS4 (16) +#define TLS_PARAM_TX_RATEIDX_MCS5 (17) +#define TLS_PARAM_TX_RATEIDX_MCS6 (18) +#define TLS_PARAM_TX_RATEIDX_MCS7 (19) +#define TLS_PARAM_TX_RATEIDX_MCS8 (20) +#define TLS_PARAM_TX_RATEIDX_MCS9 (21) +#define TLS_PARAM_TX_RATEIDX_MCS10 (22) +#define TLS_PARAM_TX_RATEIDX_MCS11 (23) +#define TLS_PARAM_TX_RATEIDX_MCS12 (24) +#define TLS_PARAM_TX_RATEIDX_MCS13 (25) +#define TLS_PARAM_TX_RATEIDX_MCS14 (26) +#define TLS_PARAM_TX_RATEIDX_MCS15 (27) +#define TLS_PARAM_TX_RATEIDX_MCS32 (28) + + +/** MACRO of SSID Broadcast */ +#define TLS_PARAM_SSIDBRD_DISABLE (0) +#define TLS_PARAM_SSIDBRD_ENABLE (1) + +#define TLS_PARAM_ROAM_DISABLE (0) +#define TLS_PARAM_ROAM_ENABLE (1) + +/** MACRO of Power Saving Mode */ +#define TLS_PARAM_PSM_DISABLE (0) +#define TLS_PARAM_PSM_ENABLE (1) + +#define TLS_PARAM_DDNS_DISABLE (0) +#define TLS_PARAM_DDNS_ENABLE (1) + +#define TLS_PARAM_AUTO_CREATE_ADHOC_DISABLE (0) +#define TLS_PARAM_AUTO_CREATE_ADHOC_ENABLE (1) + +#define TLS_PARAM_WPS_DISABLE (0) +#define TLS_PARAM_WPS_ENABLE (1) +#define TLS_PARAM_WPS_MODE_PIN (0) +#define TLS_PARAM_WPS_MODE_PBC (1) +#define TLS_PARAM_WPS_FLAG_NEGOTIATED (1 << 0) + +#define TLS_PARAM_UPNP_DISABLE (0) +#define TLS_PARAM_UPNP_ENABLE (1) + +/** MACRO of IEEE802.11 Work MODE */ +#define TLS_PARAM_IEEE80211_INFRA (1) +#define TLS_PARAM_IEEE80211_ADHOC (2) +#define TLS_PARAM_IEEE80211_SOFTAP (4) + +/** MACRO of Command or Transparent Mode */ +#define TLS_PARAM_MANUAL_MODE (0) +#define TLS_PARAM_AUTO_MODE (1) + +#define TLS_PARAM_ALWAYS_ONLINE_ENABLE (1) +#define TLS_PARAM_ALWAYS_ONLINE_DISABLE (0) + +/** MACRO of DHCP ENABLE or DISABLE */ +#define TLS_PARAM_DHCP_ENABLE (1) +#define TLS_PARAM_DHCP_DISABLE (0) + +/** MACRO of COUNTRY CODE for channel number */ +#define TLS_PARAM_REGION_0_BG_BAND (0) /* 1-11 */ +#define TLS_PARAM_REGION_1_BG_BAND (1) /* 1-13 */ +#define TLS_PARAM_REGION_2_BG_BAND (2) /* 10-11 */ +#define TLS_PARAM_REGION_3_BG_BAND (3) /* 10-13 */ +#define TLS_PARAM_REGION_4_BG_BAND (4) /* 14 */ +#define TLS_PARAM_REGION_5_BG_BAND (5) /* 1-14 */ +#define TLS_PARAM_REGION_6_BG_BAND (6) /* 3-9 */ +#define TLS_PARAM_REGION_7_BG_BAND (7) /* 5-13 */ +#define TLS_PARAM_REGION_MAXIMUM_BG_BAND TLS_PARAM_REGION_7_BG_BAND + +/** MACRO of ENCRYPT TYPE */ +#define TLS_PARAM_ENCRY_OPEN (0) +#define TLS_PARAM_ENCRY_WEP64 (1) +#define TLS_PARAM_ENCRY_WEP128 (2) +#define TLS_PARAM_ENCRY_WPA_PSK_TKIP (3) +#define TLS_PARAM_ENCRY_WPA_PSK_AES (4) +#define TLS_PARAM_ENCRY_WPA2_PSK_TKIP (5) +#define TLS_PARAM_ENCRY_WPA2_PSK_AES (6) + +/** MACRO of USER INTERFACE TYPE */ +#define TLS_PARAM_USR_INTF_LUART (0) +#define TLS_PARAM_USR_INTF_HUART (1) +#define TLS_PARAM_USR_INTF_HSPI (2) +#define TLS_PARAM_USR_INTF_HSDIO (3) + +/** MACRO of UART BAUDRATE */ +#define TLS_PARAM_UART_BAUDRATE_B600 (600) +#define TLS_PARAM_UART_BAUDRATE_B1200 (1200) +#define TLS_PARAM_UART_BAUDRATE_B1800 (1800) +#define TLS_PARAM_UART_BAUDRATE_B2400 (2400) +#define TLS_PARAM_UART_BAUDRATE_B4800 (4800) +#define TLS_PARAM_UART_BAUDRATE_B9600 (9600) +#define TLS_PARAM_UART_BAUDRATE_B19200 (19200) +#define TLS_PARAM_UART_BAUDRATE_B38400 (38400) +#define TLS_PARAM_UART_BAUDRATE_B57600 (57600) +#define TLS_PARAM_UART_BAUDRATE_B115200 (115200) +#define TLS_PARAM_HUART_BAUDRATE_B230400 (230400) +#define TLS_PARAM_HUART_BAUDRATE_B460800 (460800) +#define TLS_PARAM_HUART_BAUDRATE_B921600 (921600) +#define TLS_PARAM_HUART_BAUDRATE_B1000000 (1000000) +#define TLS_PARAM_HUART_BAUDRATE_B1250000 (1250000) +#define TLS_PARAM_HUART_BAUDRATE_B1500000 (1500000) +#define TLS_PARAM_HUART_BAUDRATE_B2000000 (2000000) + +/** MACRO of UART PARITY */ +#define TLS_PARAM_UART_PARITY_NONE (0) +#define TLS_PARAM_UART_PARITY_EVEN (1) +#define TLS_PARAM_UART_PARITY_ODD (2) +#define TLS_PARAM_UART_PARITY_MARK (3) +#define TLS_PARAM_UART_PARITY_SPACE (4) + +/** MACRO of UART STOP BITS */ +#define TLS_PARAM_UART_STOPBITS_1BITS (0) +#define TLS_PARAM_UART_STOPBITS_2BITS (1) + +/** MACRO of UART FLOW CONTROL */ +#define TLS_PARAM_UART_FLOW_DISABLE (0) +#define TLS_PARAM_UART_FLOW_ENABLE (1) +#define TLS_PARAM_HSPI_MAX_SCLK (33000000) +/** MACRO of SOCKET PARAMETER */ +#define TLS_PARAM_SOCKET_TCP (0) +#define TLS_PARAM_SOCKET_UDP (1) +#define TLS_PARAM_SOCKET_CLIENT (0) +#define TLS_PARAM_SOCKET_SERVER (1) +#define TLS_PARAM_SOCKET_DEFAULT_PORT (60000) + +/** Structure of hardware parameter */ +struct tls_param_hardware_version { + u8 mark; + u8 main; + u8 sub; + u8 material; + u8 year; + u8 week; + u8 res[2]; +}; + +/** Structure of specified BSSID parameter */ +struct tls_param_bssid { + u8 bssid_enable; + u8 res; + u8 bssid[6]; +}; + +/** Structure of SSID parameter */ +struct tls_param_ssid { + u8 ssid[32]; + u32 ssid_len; +}; + +/** Structure of TEM parameter */ +struct tls_param_tem_offset { + u8 offset_len; + s32 offset; +}; + +/** Structure of KEY parameter */ +struct tls_param_key { + u8 psk[64]; + u8 key_length; + u8 key_index; + u8 key_format; + u8 res; +}; + +/** Structure of original KEY parameter */ +struct tls_param_original_key { + u8 psk[64]; + u32 key_length; +}; + +/** Structure of SHA1 KEY parameter */ +struct tls_param_sha1{ + u8 psk_set; + u8 psk[32]; +}; + +/** Structure of 802.11 bgn rate parameter */ +struct tls_param_bgr { + u8 bg; + u8 max_rate; + u8 res[2]; +}; + +/** Structure of IP parameter */ +struct tls_param_ip { + u8 dhcp_enable; + u8 res[3]; + u8 ip[4]; + u8 netmask[4]; + u8 gateway[4]; + u8 dns1[4]; + u8 dns2[4]; +}; + +/** Structure of uart parameter */ +struct tls_param_uart { + u32 baudrate; + u8 stop_bits; + u8 parity; + u8 flow; + u8 charsize; +}; + +/** Structure of socket parameter */ +struct tls_param_socket { + u8 protocol; + u8 client_or_server; + u16 port_num; + u8 host[32]; +}; + +struct tls_param_wps { + u8 wps_enable; + u8 mode; + u8 flag; + u8 res; + u8 pin[8]; +}; + +/** Structure of oray client */ +struct tls_param_oray_client { + u32 oray_client_enable; + u8 oray_client_user[32]; + u8 oray_client_pass[32]; +}; + +/** Structure of web server parameter */ +struct tls_webs_cfg { + u8 AutoRun; + u8 Reserved; + u16 PortNum; +}; + +/** Structure of user default parameter */ +typedef struct tls_user_param{ + + u8 wireless_protocol; + u8 auto_mode; + u8 dhcp_enable; + u8 user_port_mode; + + u8 ip[4]; + u8 netmask[4]; + u8 gateway[4]; + u8 dns[4]; + + u32 baudrate; + + u8 socket_protocol; + u8 socket_client_or_server; + u16 socket_port_num; + u8 socket_host[32]; + u8 auto_powersave; + u8 PassWord[6]; +}ST_Wm_User_Param; + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup Param_APIs Param APIs + * @brief Param manager APIs + */ + +/** + * @addtogroup Param_APIs + * @{ + */ + +/** + * @brief This function is used to initialize parameter's manage module + * + * @param[in] None + * + * @retval TLS_PARAM_STATUS_OK init success + * @retval TLS_PARAM_STATUS_EMEM memory error + * @retval TLS_PARAM_STATUS_EIO io error + * @retval TLS_PARAM_STATUS_EPERM magic number error + * + * @note None + */ +int tls_param_init(void); + +/** + * @brief This function is used to load the system's default + parameters + * + * @param[in] None + * + * @return None + * + * @note This function read user defined parameters first, + if wrong, all the parameters restore factory settings + */ +void tls_param_load_factory_default(void); + +/** + * @brief This function is used to set one system parameter by its id + * + * @param[in] id param id,from TLS_PARAM_ID_SSID + to (TLS_PARAM_ID_MAX - 1) + * @param[in] *argv store parameters + * @param[in] to_flash whether the parameter is written to flash, + 1:write to flash,0:only write memory + * + * @retval TLS_PARAM_STATUS_OK set success + * @retval TLS_PARAM_STATUS_EINVALID invalid param + * + * @note None + */ +int tls_param_set(int id, void *argv, bool to_flash); + +/** + * @brief This function is used to get one system parameter by its id. + * + * @param[in] id param id,from TLS_PARAM_ID_SSID + to (TLS_PARAM_ID_MAX - 1) + * @param[in] *argv store parameters + * @param[in] from_flash whether the parameter is readed from flash, + 1 read from flash,0 read from memory + * + * @retval TLS_PARAM_STATUS_OK success + * @retval TLS_PARAM_STATUS_EINVALID invalid param + * + * @note None + */ +int tls_param_get(int id, void *argv, bool from_flash); + +/** + * @brief This function is used to write parameter into flash + * + * @param[in] id param id,from TLS_PARAM_ID_ALL + to (TLS_PARAM_ID_MAX - 1) + * + * @retval TLS_PARAM_STATUS_OK success + * @retval TLS_PARAM_STATUS_EINVALID invalid param + * @retval TLS_PARAM_STATUS_EIO read or write flash error + * + * @note None + */ +int tls_param_to_flash(int id); + +/** + * @brief This function is used to recovery the parameters from + the backup area to the parameter area,and load them into ram + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_restore_param_from_backup(void); + +/** + * @brief This function is used to load default parametes into ram + + * + * @param[in] None + * + * @retval TLS_PARAM_STATUS_OK set success + * @retval TLS_PARAM_STATUS_EINVALID parameter wrong + * @retval TLS_PARAM_STATUS_EIO read or write falsh error + * + * @note This function read user defined parameters first, + if wrong, all the parameters restore factory settings + */ +int tls_param_to_default(void); + +/** + * @brief This function is used to write user's parameters into the + flash + * + * @param[in] None + * + * @retval TLS_PARAM_STATUS_OK success + * @retval other failed + * + * @note None + */ +int tls_param_save_user_default(void); + +/** + * @brief This function is used to modify user's default parameters, + then write them into flash + * + * @param[in] *user_param + * + * @retval TLS_PARAM_STATUS_OK success + * @retval other failed + * + * @note None + */ +int tls_param_save_user(struct tls_user_param *user_param); + +/** + * @brief This function is used to get updp mode.(updp:update user + default parameters) + * + * @param[in] None + * + * @retval update mode + * + * @note None + */ +u8 tls_param_get_updp_mode(void); + +/** + * @brief This function is used to set updp mode + * + * @param[in] mode + * + * @return None + * + * @note None + */ +void tls_param_set_updp_mode(u8 mode); + +/** + * @brief This function is used to get program param + * + * @param[in] *pBase pointer to save base address + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_param_get_program_param(u32 *pBase); + +/** + * @brief This function is used to set program base,image len,and + checksum + * + * @param[in] base base address + * @param[in] totallen total length of the image + * @param[in] checksum checksum value of the image + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_param_set_program_param(u32 base, u32 totallen, u32 checksum); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_PARAM_H */ + diff --git a/sdk/Include/Platform/wm_wl_mbox.h b/sdk/Include/Platform/wm_wl_mbox.h new file mode 100644 index 0000000..7d12bba --- /dev/null +++ b/sdk/Include/Platform/wm_wl_mbox.h @@ -0,0 +1,115 @@ +/** + * @file wm_wl_mbox.h + * + * @brief mailbox (mbox) APIs + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef __TLS_WL_MBOX_H__ +#define __TLS_WL_MBOX_H__ + +#include "wm_type_def.h" +#include "wm_osal.h" + +/** max value of time out */ +#define SYS_ARCH_TIMEOUT 0xffffffffUL + +/** pointer to the mailbox */ +typedef tls_os_queue_t * tls_mbox_t; + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup MBOX_APIs MBOX APIs + * @brief Software mailbox APIs + */ + +/** + * @addtogroup MBOX_APIs + * @{ + */ + +/** + * @brief Create a malibox + * + * @param[out] *mbox pointer to the mailbox + * @param[in] size size of mailbox + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +s8 tls_mbox_new(tls_mbox_t *mbox, int size); + +/** + * @brief Check if an mbox is valid/allocated + * + * @param[in] mbox pointer to the mailbox + * + * @retval 0 invalid + * @retval 1 valid + * + * @note None + */ +int tls_mbox_valid(tls_mbox_t mbox); + +/** + * @brief Sends a message to a mailbox + * + * @param[in] mbox pointer to the mailbox + * @param[in] *msg pointer to the message to be post + * + * @return None + * + * @note None + */ +void tls_mbox_post(tls_mbox_t mbox, void *msg); + +/** + * @brief Posts the msg to the mailbox. + * + * @param[in] mbox pointer to the mailbox + * @param[in] *msg pointer to the message to be post + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note this function have to block until the "msg" is really posted. + */ +s8 tls_mbox_trypost(tls_mbox_t mbox, void *msg); + +/** + * @brief Waits for a message within specified time + * + * @param[in] mbox pointer to the mailbox + * @param[out] **msg pointer to the message to be received + * @param[in] timeout the specified time + * + * @retval SYS_ARCH_TIMEOUT time out + * @retval other time of elapsed + * + * @note None + */ +u32 tls_arch_mbox_fetch(tls_mbox_t mbox, void **msg, u32 timeout); + +/** + * @} + */ + +/** + * @} + */ + +#endif + diff --git a/sdk/Include/Platform/wm_wl_task.h b/sdk/Include/Platform/wm_wl_task.h new file mode 100644 index 0000000..f089ce5 --- /dev/null +++ b/sdk/Include/Platform/wm_wl_task.h @@ -0,0 +1,233 @@ +/** + * @file wm_wl_task.h + * + * @brief task APIs + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef __TLS_WL_TASK_H__ +#define __TLS_WL_TASK_H__ + +#include "wm_type_def.h" +#include "wm_wl_mbox.h" +#include "wm_wl_timers.h" +//#include "ithread.h" + +#define TLS_TASK_START_PRIO 0 +#define TASK_WL_PRIO 9 +#define TASK_WL_PRIO_MAX 12 +#define TLS_SUPPLICANT_TASK_PRIO (TASK_WL_PRIO_MAX + 1) +#define TLS_SUPPLICANT_TIMER_TASK_PRIO (TASK_WL_PRIO_MAX + 2) +#define TLS_LWIP_TASK_PRIO (TASK_WL_PRIO_MAX + 3) +#define TLS_SYS_TASK_PRIO (TASK_WL_PRIO_MAX + 5) +#define TLS_HOSTIF_TASK_PRIO (TASK_WL_PRIO_MAX + 6) +#define TLS_SPI_SCHEDULER_TASK_PRIO (TASK_WL_PRIO_MAX + 7) +#define TLS_FWUP_TASK_PRIO (TASK_WL_PRIO_MAX + 8) +#define TLS_HTTP_CLIENT_TASK_PRIO (TASK_WL_PRIO_MAX + 9) +#define AP_SOCKET_S_TASK_PRIO (TASK_WL_PRIO_MAX + 10) +#define TLS_UPNP_TASK_PRIO (TASK_WL_PRIO_MAX + 11) +// #define TLS_ONESHOT_TASK_PRIO (TASK_WL_PRIO_MAX + 15) +// #define TLS_ONESHOT_SPEC_TASK_PRIO (TASK_WL_PRIO_MAX + 16) + + +#define TLS_MBOX_ALL_COUNT 8 +#define TLS_MBOX_ID_WL_TASK 0 +#define TLS_MBOX_ID_HOSTIF_TASK 1 +#define TLS_MBOX_ID_JDCLOUD_SERVER 2 +#define TLS_MBOX_ID_JDCLOUD_DATA 3 +#define TLS_MBOX_ID_UPNP_HD 4 +#define TLS_MBOX_ID_UPNP_COMMON 5 +#define TLS_MBOX_ID_UPNP_GENA 6 +#define TLS_MBOX_ID_UPNP_MINISERVER 7 + +#define TLS_TIMEO_ALL_COUONT 9 +#define TLS_TIMEO_ID_NULL 0 +#define TLS_TIMEO_ID_WL_TASK 1 +#define TLS_TIMEO_ID_HOSTIF_TASK 2 +#define TLS_TIMEO_ID_JDCLOUD_SERVER 3 +#define TLS_TIMEO_ID_JDCLOUD_DATA 4 +#define TLS_TIMEO_ID_UPNP_HD 5 +#define TLS_TIMEO_ID_UPNP_COMMON 6 +#define TLS_TIMEO_ID_UPNP_GENA 7 +#define TLS_TIMEO_ID_UPNP_MINISERVER 8 + +#define TLS_MSG_ALL_COUONT 9 +#define TLS_MSG_ID_TX_MGMT_CMPLT 0 +#define TLS_MSG_ID_MLME_TASK 1 +#define TLS_MSG_ID_UART_SENT_FREE 2 +#define TLS_MSG_ID_UART0_RX 3 +#define TLS_MSG_ID_HSPI_RX_CMD 4 +#define TLS_MSG_ID_HSPI_RX_DATA 5 +#define TLS_MSG_ID_HSPI_TX_DATA 6 +#define TLS_MSG_ID_TX_DATA_CMPLT 7 +#define TLS_MSG_ID_UART1_RX 8 + +/** pointer to the semaphore */ +typedef tls_os_sem_t * tls_sem_t; + +/** Thread start routine */ +typedef void *(*start_routine)(void *arg); + +/** message type of task */ +enum task_msg_type { + TASK_MSG_TIMEOUT, + TASK_MSG_UNTIMEOUT, + TASK_MSG_CALLBACK_WITH_BLOCK, + TASK_MSG_CALLBACK, + TASK_MSG_CALLBACK_STATIC, + TASK_MSG_NULL +}; + +/** message of task */ +struct task_msg { + enum task_msg_type type; + tls_sem_t *sem; + union { + struct { + start_routine function; + void *ctx; + } cb; + struct { + start_routine function; + void *ctx; + u8 cnt; + } cbs; + struct { + u32 msecs; + tls_timeout_handler h; + void *arg; + } tmo; + } msg; +}; + +/** task parameters */ +struct task_parameter{ + u8 task_id; /**< task ID */ + const char * name; /**< task name */ + u8 *stk_start; /**< start address of task stack */ + u32 stk_size; /**< size of task stack */ + u8 mbox_size; /**< size of mailbox */ + u8 mbox_id; /**< mailbox ID */ + u8 timeo_id; /**< timer ID */ +}; + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup Task_APIs Task APIs + * @brief Software task APIs + */ + +/** + * @addtogroup Task_APIs + * @{ + */ + +/** + * @brief Task initialized + * + * @param None + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +s8 tls_wl_task_init(void); + +/** + * @brief Running the task + * + * @param[in] *task_param pointer to the task parameters + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +s8 tls_wl_task_run(struct task_parameter *task_param); + +/** + * @brief Running the callback function + * + * @param[in] *task_param pointer to the task parameters + * @param[in] function the callback function + * @param[in] *ctx parameter of the callback function + * @param[in] block + * @param[in] msg_id + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +s8 tls_wl_task_callback_static(struct task_parameter *task_param, + start_routine function, void *ctx, u8 block, u8 msg_id); + +/** + * @brief Running the callback function + * + * @param[in] *task_param pointer to the task parameters + * @param[in] function the callback function + * @param[in] *ctx parameter of the callback function + * @param[in] block + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +s8 tls_wl_task_callback(struct task_parameter *task_param, + start_routine function, void *ctx, u8 block); + +/** + * @brief Add a timer to the task + * + * @param[in] *task_param pointer to the task parameters + * @param[in] msecs timer value + * @param[in] h the callback function + * @param[in] *arg parameter of the callback function + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +s8 tls_wl_task_add_timeout(struct task_parameter *task_param, u32 msecs, + tls_timeout_handler h, void *arg); + +/** + * @brief Stop or delay the timer to expire. + * + * @param[in] *task_param pointer to the task parameters + * @param[in] h the callback function + * @param[in] *arg parameter of the callback function + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +s8 tls_wl_task_untimeout(struct task_parameter *task_param, + tls_timeout_handler h, void *arg); + +/** + * @} + */ + +/** + * @} + */ + +#endif + diff --git a/sdk/Include/Platform/wm_wl_timers.h b/sdk/Include/Platform/wm_wl_timers.h new file mode 100644 index 0000000..b756569 --- /dev/null +++ b/sdk/Include/Platform/wm_wl_timers.h @@ -0,0 +1,102 @@ +/** + * @file wm_wl_timers.h + * + * @brief task APIs + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef __TLS_WL_TIMERS_H__ +#define __TLS_WL_TIMERS_H__ + +#include "wm_type_def.h" +#include "wm_wl_mbox.h" + +/** callback function of time out */ +typedef void (* tls_timeout_handler)(void *arg); + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup Timer_APIs Timer APIs + * @brief Software timer APIs + */ + +/** + * @addtogroup Timer_APIs + * @{ + */ + +/** + * @brief Create a one-shot timer (aka timeout) + * + * @param[in] timeo_assigned timer NO. by assigned + * @param[in] msecs time in milliseconds after that the timer should expire + * @param[in] handler callback function that would be called by the timeout + * @param[in] *arg callback argument that would be passed to handler + * + * @return None + * + * @note While waiting for a message using sys_timeouts_mbox_fetch() + */ +void tls_timeout_p(u8 timeo_assigned, u32 msecs, tls_timeout_handler handler, void *arg); + +/** + * @brief Go through timeout list (for this task only) and remove the first + * matching entry, even though the timeout has not been triggered yet + * + * @param[in] timeo_assigned timer NO. by assigned + * @param[in] handler callback function that would be called by the timeout + * @param[in] *arg callback argument that would be passed to handler + * + * @return None + * + * @note None + */ +void tls_untimeout_p(u8 timeo_assigned, tls_timeout_handler handler, void *arg); + +/** + * @brief Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed + * + * @param[in] timeo_assigned timer NO. by assigned + * @param[in] mbox the mbox to fetch the message from + * @param[out] **msg the place to store the message + * + * @return None + * + * @note None + */ +void tls_timeouts_mbox_fetch_p(u8 timeo_assigned, tls_mbox_t mbox, void **msg); + +/** + * @brief Initialize the timer + * + * @param None + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +s8 tls_wl_timer_init(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif + diff --git a/sdk/Include/WiFi/litepoint.h b/sdk/Include/WiFi/litepoint.h new file mode 100644 index 0000000..58d943b --- /dev/null +++ b/sdk/Include/WiFi/litepoint.h @@ -0,0 +1,90 @@ +#ifndef __LITEPOINT_H +#define __LITEPOINT_H +#include "wm_type_def.h" +typedef struct _ltpt_rx_info +{ + volatile int cnt_total; + volatile int cnt_good; + volatile int cnt_bad; + volatile unsigned char valid; + volatile unsigned char snr; + volatile unsigned char rcpi; + volatile unsigned char rate; + volatile unsigned char channel; + volatile unsigned char bandwidth; + volatile unsigned char bprocess; +}ltpt_rx_info; + +typedef struct _ltpt_tx_info +{ + volatile int cnt_total; + volatile int packetcount; + volatile int psdulen; + /* input parameters */ + volatile unsigned char txgain; + volatile unsigned char datarate; + volatile unsigned char gimode; + volatile unsigned char greenfield; + volatile unsigned char rifs; + volatile unsigned char bprocess; + volatile unsigned char channel; + volatile unsigned char longpreamble; +}ltpt_tx_info; + + +enum Rate +{ + S2M = 0x0000, + S5M5 = 0x0001, + S11M = 0x0002, + L1M = 0x0003, + L2M = 0x0004, + L5M5 = 0x0005, + L11M = 0x0006, + R06M = 0x0100, + R09M = 0x0101, + R12M = 0x0102, + R18M = 0x0103, + R24M = 0x0104, + R36M = 0x0105, + R48M = 0x0106, + R54M = 0x0107, + MCS0 = 0x0200, + MCS1 = 0x0201, + MCS2 = 0x0202, + MCS3 = 0x0203, + MCS4 = 0x0204, + MCS5 = 0x0205, + MCS6 = 0x0206, + MCS7 = 0x0207, + MCS8 = 0x0208, + MCS9 = 0x0209, + MCS10 = 0x0210, + MCS11 = 0x0211, + MCS12 = 0x0212, + MCS13 = 0x0213, + MCS14 = 0x0214, + MCS15 = 0x0215, + MCS32 = 0x0232, +}; + +extern volatile int g_ltpt_testmode; +extern ltpt_rx_info *g_ltpt_rxinfo; +extern ltpt_tx_info *g_ltpt_txinfo; +extern unsigned char hed_rf_txgainmap[]; +extern const unsigned int hed_bbp_value[]; + +void tls_litepoint_start(void); +void tls_tx_send_litepoint(void); +void tls_set_test_channel(u8 channel, u8 bandwidth); +void tls_tx_litepoint_test_start(u32 Packetcnt, u16 Psdulen, u32 Gain, u32 TxRate, u8 GiMode, u8 Gf, u8 Rifs); +void tls_txrx_litepoint_test_stop(void); +void tls_txrx_litepoint_clear_data(void); +int tls_tx_litepoint_test_get_totalsnd(void); +void tls_rx_litepoint_test_start(u32 Channel, u32 BandWidth); +void tls_rx_litepoint_test_result(u32 *total, u32 *goodcnt, u32 *badcnt); +void tls_tx_litepoint_param_update(u8 Gain, u8 TxRate); +void tls_rx_litepoint_test_rate(u8 rate); +void tls_rx_litepoint_pwr_result(u32 *valid, u32 *snr, u32 *rcpi); +void tls_tx_litepoint_period(u32 period); +#endif diff --git a/sdk/Include/WiFi/wm_ieee80211.h b/sdk/Include/WiFi/wm_ieee80211.h new file mode 100644 index 0000000..1a246f8 --- /dev/null +++ b/sdk/Include/WiFi/wm_ieee80211.h @@ -0,0 +1,2019 @@ +/** + * @file wm_ieee80211.h + * + * @brief IEEE802.11 protocol type define + * + * @author winnermicro + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_IEEE80211_H +#define WM_IEEE80211_H + +/* + * DS bit usage + * + * TA = transmitter address + * RA = receiver address + * DA = destination address + * SA = source address + * + * ToDS FromDS A1(RA) A2(TA) A3 A4 Use + * ----------------------------------------------------------------- + * 0 0 DA SA BSSID - IBSS/DLS + * 0 1 DA BSSID SA - AP -> STA + * 1 0 BSSID SA DA - AP <- STA + * 1 1 RA TA DA SA unspecified (WDS) + */ + +#define FCS_LEN 4 + +#define IEEE80211_FCTL_VERS 0x0003 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_PROTECTED 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + +#define IEEE80211_FTYPE_MGMT 0x0000 +#define IEEE80211_FTYPE_CTL 0x0004 +#define IEEE80211_FTYPE_DATA 0x0008 + +/* management */ +#define IEEE80211_STYPE_ASSOC_REQ 0x0000 +#define IEEE80211_STYPE_ASSOC_RESP 0x0010 +#define IEEE80211_STYPE_REASSOC_REQ 0x0020 +#define IEEE80211_STYPE_REASSOC_RESP 0x0030 +#define IEEE80211_STYPE_PROBE_REQ 0x0040 +#define IEEE80211_STYPE_PROBE_RESP 0x0050 +#define IEEE80211_STYPE_BEACON 0x0080 +#define IEEE80211_STYPE_ATIM 0x0090 +#define IEEE80211_STYPE_DISASSOC 0x00A0 +#define IEEE80211_STYPE_AUTH 0x00B0 +#define IEEE80211_STYPE_DEAUTH 0x00C0 +#define IEEE80211_STYPE_ACTION 0x00D0 + +/* control */ +#define IEEE80211_STYPE_BACK_REQ 0x0080 +#define IEEE80211_STYPE_BACK 0x0090 +#define IEEE80211_STYPE_PSPOLL 0x00A0 +#define IEEE80211_STYPE_RTS 0x00B0 +#define IEEE80211_STYPE_CTS 0x00C0 +#define IEEE80211_STYPE_ACK 0x00D0 +#define IEEE80211_STYPE_CFEND 0x00E0 +#define IEEE80211_STYPE_CFENDACK 0x00F0 + +/* data */ +#define IEEE80211_STYPE_DATA 0x0000 +#define IEEE80211_STYPE_DATA_CFACK 0x0010 +#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 +#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 +#define IEEE80211_STYPE_NULLFUNC 0x0040 +#define IEEE80211_STYPE_CFACK 0x0050 +#define IEEE80211_STYPE_CFPOLL 0x0060 +#define IEEE80211_STYPE_CFACKPOLL 0x0070 +#define IEEE80211_STYPE_QOS_DATA 0x0080 +#define IEEE80211_STYPE_QOS_DATA_CFACK 0x0090 +#define IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0 +#define IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0 +#define IEEE80211_STYPE_QOS_NULLFUNC 0x00C0 +#define IEEE80211_STYPE_QOS_CFACK 0x00D0 +#define IEEE80211_STYPE_QOS_CFPOLL 0x00E0 +#define IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0 + + +#define IEEE80211_STA_DEFAULT_LISTEN_INTERVAL 3 +#define IEEE80211_STA_MIN_LISTEN_INTERVAL 1 + +/* miscellaneous IEEE 802.11 constants */ +#define IEEE80211_MAX_FRAG_THRESHOLD 2352 +#define IEEE80211_MAX_RTS_THRESHOLD 2353 +#define IEEE80211_MAX_AID 2007 +#define IEEE80211_MAX_TIM_LEN 251 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + 802.11e clarifies the figure in section 7.1.2. The frame body is + up to 2304 octets long (maximum MSDU size) plus any crypt overhead. */ +#define IEEE80211_MAX_DATA_LEN 2304 +/* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */ +#define IEEE80211_MAX_FRAME_LEN 2352 + +#define IEEE80211_MAX_SSID_LEN 32 + +#define IEEE80211_MAX_MESH_ID_LEN 32 + +#define IEEE80211_QOS_CTL_LEN 2 +#define IEEE80211_QOS_CTL_TID_MASK 0x000F +#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 + +#define IEEE80211_HT_CTL_LEN 4 + +/* U-APSD queue for WMM IEs sent by AP */ +#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) + +/* U-APSD queues for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f + +/* U-APSD max SP length for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5 + +/* Parsed Information Elements */ +struct ieee802_11_elems { + u8 *ie_start; + size_t total_len; + +/* pointers to IEs */ + u8 *ssid; + u8 *supp_rates; + u8 *fh_params; + u8 *ds_params; + u8 *cf_params; + u8 *tim; + u8 *ibss_params; + u8 *country_elem; + u8 *challenge; + u8 *erp_info; + u8 *ext_supp_rates; + u8 *wpa_ie; + u8 *rsn_ie; + u8 *wmm; /* WMM Information or Parameter Element */ + u8 *wmm_param; + struct ieee80211_ht_cap *ht_cap_elem; + struct ieee80211_ht_info *ht_info_elem; + u8 *wmm_tspec; + u8 *wps_ie; + u8 *power_cap; + u8 *supp_channels; + u8 *mdie; + u8 *ftie; + u8 *timeout_int; + u8 *ch_switch_elem; + u8 *ht_capabilities; + u8 *ht_operation; + u8 *vendor_ht_cap; + u8 *p2p; + u8 *link_id; + u8 *pwr_constr_elem; + u8 *interworking; + + u8 ssid_len; + u8 supp_rates_len; + u8 fh_params_len; + u8 ds_params_len; + u8 cf_params_len; + u8 tim_len; + u8 ibss_params_len; + u8 challenge_len; + u8 erp_info_len; + u8 ext_supp_rates_len; + u8 ch_switch_elem_len; + u8 wpa_ie_len; + u8 rsn_ie_len; + u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ + u8 wmm_param_len; + u8 wmm_tspec_len; + u8 wps_ie_len; + u8 power_cap_len; + u8 supp_channels_len; + u8 mdie_len; + u8 ftie_len; + u8 timeout_int_len; + u8 ht_capabilities_len; + u8 pwr_constr_elem_len; + u8 country_elem_len; + u8 ht_operation_len; + u8 vendor_ht_cap_len; + u8 p2p_len; + u8 interworking_len; + +}; + + __packed struct ieee80211_hdr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u8 addr4[6]; +}; + + __packed struct ieee80211_hdr_3addr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; +}; + + __packed struct ieee80211_qos_hdr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u16 qos_ctrl; +}; + +typedef enum _phy_type{ + phy_80211_b, + phy_80211_bg, + phy_80211_bgn, + phy_80211_n, + phy_80211_max +} phy_type; + + +#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr_3addr)) + +/** + * @defgroup Wi-Fi_APIs Wi-Fi APIs + * @brief Wi-Fi related APIs + */ + +/** + * @addtogroup Wi-Fi_APIs + * @{ + */ + +/** + * @defgroup IEEE80211_APIs IEEE80211 APIs + * @brief IEEE80211 APIs + */ + +/** + * @addtogroup IEEE80211_APIs + * @{ + */ + +/** + * @brief check if IEEE80211_FCTL_TODS is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no tods + * @retval other has tods + * + * @note None + */ +static __inline int ieee80211_has_tods(u16 fc) +{ + return (fc & IEEE80211_FCTL_TODS) != 0; +} + +/** + * @brief check if IEEE80211_FCTL_FROMDS is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no fromds + * @retval other has fromds + * + * @note None + */ +static __inline int ieee80211_has_fromds(u16 fc) +{ + return (fc & IEEE80211_FCTL_FROMDS) != 0; +} + +/** + * @brief check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no a4 + * @retval other has a4 + * + * @note None + */ +static __inline int ieee80211_has_a4(u16 fc) +{ + u16 tmp = IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS; + return (fc & tmp) == tmp; +} + +/** + * @brief check if IEEE80211_FCTL_MOREFRAGS is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no morefrags + * @retval other has morefrags + * + * @note None + */ +static __inline int ieee80211_has_morefrags(u16 fc) +{ + return (fc & IEEE80211_FCTL_MOREFRAGS) != 0; +} + +/** + * @brief check if IEEE80211_FCTL_RETRY is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no retry + * @retval other has retry + * + * @note None + */ +static __inline int ieee80211_has_retry(u16 fc) +{ + return (fc & IEEE80211_FCTL_RETRY) != 0; +} + +/** + * @brief check if IEEE80211_FCTL_PM is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no pm + * @retval other has pm + * + * @note None + */ +static __inline int ieee80211_has_pm(u16 fc) +{ + return (fc & IEEE80211_FCTL_PM) != 0; +} + +/** + * @brief check if IEEE80211_FCTL_MOREDATA is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no moredata + * @retval other has moredata + * + * @note None + */ +static __inline int ieee80211_has_moredata(u16 fc) +{ + return (fc & IEEE80211_FCTL_MOREDATA) != 0; +} + +/** + * @brief check if IEEE80211_FCTL_PROTECTED is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no protected + * @retval other has protected + * + * @note None + */ +static __inline int ieee80211_has_protected(u16 fc) +{ + return (fc & IEEE80211_FCTL_PROTECTED) != 0; +} + +/** + * @brief check if IEEE80211_FCTL_ORDER is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 no order + * @retval other has order + * + * @note None + */ +static __inline int ieee80211_has_order(u16 fc) +{ + return (fc & IEEE80211_FCTL_ORDER) != 0; +} + +/** + * @brief check if type is IEEE80211_FTYPE_MGMT + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is mgmt + * @retval other is mgmt + * + * @note None + */ +static __inline int ieee80211_is_mgmt(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT; +} + +/** + * @brief check if type is IEEE80211_FTYPE_CTL + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is ctl + * @retval other is ctl + * + * @note None + */ +static __inline int ieee80211_is_ctl(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL; +} + +/** + * @brief check if type is IEEE80211_FTYPE_DATA + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is data + * @retval other is data + * + * @note None + */ +static __inline int ieee80211_is_data(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA; +} + +/** + * @brief check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is qos data + * @retval other is qos data + * + * @note None + */ +static __inline int ieee80211_is_data_qos(u16 fc) +{ +/* + * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need + * to check the one bit + */ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_STYPE_QOS_DATA)) == + (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA); +} + +/** + * @brief check if type is IEEE80211_FTYPE_DATA and has data + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is present data + * @retval other is present data + * + * @note None + */ +static __inline int ieee80211_is_data_present(u16 fc) +{ +/* + * mask with 0x40 and test that that bit is clear to only return TRUE + * for the data-containing substypes. + */ + return (fc & (IEEE80211_FCTL_FTYPE | 0x40)) == (IEEE80211_FTYPE_DATA); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ + + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is assoc req + * @retval other is assoc req + * + * @note None + */ +static __inline int ieee80211_is_assoc_req(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is assoc resp + * @retval other is assoc resp + * + * @note None + */ +static __inline int ieee80211_is_assoc_resp(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is reassoc req + * @retval other is reassoc req + * + * @note None + */ +static __inline int ieee80211_is_reassoc_req(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is reassoc resp + * @retval other is reassoc resp + * + * @note None + */ +static __inline int ieee80211_is_reassoc_resp(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is probe req + * @retval other is probe req + * + * @note None + */ +static __inline int ieee80211_is_probe_req(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is probe resp + * @retval other is probe resp + * + * @note None + */ +static __inline int ieee80211_is_probe_resp(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is beacon + * @retval other is beacon + * + * @note None + */ +static __inline int ieee80211_is_beacon(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is atim + * @retval other is atim + * + * @note None + */ +static __inline int ieee80211_is_atim(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is disassoc + * @retval other is disassoc + * + * @note None + */ +static __inline int ieee80211_is_disassoc(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is auth + * @retval other is auth + * + * @note None + */ +static __inline int ieee80211_is_auth(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is deauth + * @retval other is deauth + * + * @note None + */ +static __inline int ieee80211_is_deauth(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); +} + +/** + * @brief check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is action + * @retval other is action + * + * @note None + */ +static __inline int ieee80211_is_action(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); +} + +/** + * @brief check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is back req + * @retval other is back req + * + * @note None + */ +static __inline int ieee80211_is_back_req(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); +} + +/** + * @brief check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is back + * @retval other is back + * + * @note None + */ +static __inline int ieee80211_is_back(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); +} + +/** + * @brief check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is pspoll + * @retval other is pspoll + * + * @note None + */ +static __inline int ieee80211_is_pspoll(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); +} + +/** + * @brief check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is rts + * @retval other is rts + * + * @note None + */ +static __inline int ieee80211_is_rts(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); +} + +/** + * @brief check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is cts + * @retval other is cts + * + * @note None + */ +static __inline int ieee80211_is_cts(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); +} + +/** + * @brief check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is ack + * @retval other is ack + * + * @note None + */ +static __inline int ieee80211_is_ack(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK); +} + +/** + * @brief check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is cfend + * @retval other is cfend + * + * @note None + */ +static __inline int ieee80211_is_cfend(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND); +} + +/** + * @brief check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is cfendack + * @retval other is cfendack + * + * @note None + */ +static __inline int ieee80211_is_cfendack(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK); +} + +/** + * @brief check if frame is a regular (non-QoS) nullfunc frame + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is nullfunc + * @retval other is nullfunc + * + * @note None + */ +static __inline int ieee80211_is_nullfunc(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC); +} + +/** + * @brief check if frame is a QoS nullfunc frame + * + * @param[in] fc frame control bytes in little-endian byteorder + * + * @retval 0 not is qos nullfunc + * @retval other is qos nullfunc + * + * @note None + */ +static __inline int ieee80211_is_qos_nullfunc(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * struct ieee80211_quiet_ie + * + * This structure refers to "Quiet information element" + */ + __packed struct ieee80211_quiet_ie { + u8 count; + u8 period; + u16 duration; + u16 offset; +}; + +/** + * struct ieee80211_msrment_ie + * + * This structure refers to "Measurement Request/Report information element" + */ + __packed struct ieee80211_msrment_ie { + u8 token; + u8 mode; + u8 type; + u8 request[1]; +}; + +/** + * struct ieee80211_channel_sw_ie + * + * This structure refers to "Channel Switch Announcement information element" + */ + __packed struct ieee80211_channel_sw_ie { + u8 mode; + u8 new_ch_num; + u8 count; +}; + +/** + * struct ieee80211_tim + * + * This structure refers to "Traffic Indication Map information element" + */ + __packed struct ieee80211_tim_ie { + u8 dtim_count; + u8 dtim_period; + u8 bitmap_ctrl; +/* variable size: 1 - 251 bytes */ + u8 virtual_map[1]; +}; + +/** + * struct ieee80211_rann_ie + * + * This structure refers to "Root Announcement information element" + */ + __packed struct ieee80211_rann_ie { + u8 rann_flags; + u8 rann_hopcount; + u8 rann_ttl; + u8 rann_addr[6]; + u32 rann_seq; + u32 rann_metric; +}; + +#define WLAN_SA_QUERY_TR_ID_LEN 2 + +struct ieee80211_mgmt { + u16 frame_control; + u16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + u16 seq_ctrl; + union { + struct { + u16 auth_alg; + u16 auth_transaction; + u16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } __attribute__ ((packed)) auth; + struct { + u16 reason_code; + } __attribute__ ((packed)) deauth; + struct { + u16 capab_info; + u16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } assoc_req; + struct { + u16 capab_info; + u16 status_code; + u16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_resp, reassoc_resp; + struct { + u16 capab_info; + u16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) reassoc_req; + struct { + u16 reason_code; + } __attribute__ ((packed)) disassoc; + struct { + u64 timestamp; + u16 beacon_int; + u16 capab_info; + /* followed by some of SSID, Supported rates, FH Params, DS Params, CF + Params, IBSS Params, TIM */ + u8 variable[0]; + } __attribute__ ((packed)) beacon; + struct { + /* only variable items: SSID, Supported rates */ +#ifdef __ICCARM__ + u8 *variable; +#else + u8 variable[0]; +#endif + } __attribute__ ((packed)) probe_req; + struct { + u64 timestamp; + u16 beacon_int; + u16 capab_info; + /* followed by some of SSID, Supported rates, FH Params, DS Params, CF + Params, IBSS Params */ + u8 variable[0]; + } __attribute__ ((packed)) probe_resp; + struct { + u8 category; + union { + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[0]; + } __attribute__ ((packed)) wme_action; + struct{ + u8 action_code; + u8 element_id; + u8 length; + struct ieee80211_channel_sw_ie sw_elem; + } __attribute__ ((packed)) chan_switch; + struct{ + u8 action_code; + u8 channel_width; + } __attribute__ ((packed)) chan_width; + struct{ + u8 action_code; + u8 dialog_token; + u8 element_id; + u8 length; + struct ieee80211_msrment_ie msr_elem; + } __attribute__ ((packed)) measurement; + struct{ + u8 action_code; + u8 dialog_token; + u16 capab; + u16 timeout; + u16 start_seq_num; + } __attribute__ ((packed)) addba_req; + struct{ + u8 action_code; + u8 dialog_token; + u16 status; + u16 capab; + u16 timeout; + } __attribute__ ((packed)) addba_resp; + struct{ + u8 action_code; + u16 params; + u16 reason_code; + } __attribute__ ((packed)) delba; + struct{ + u8 action_code; + /* capab_info for open and confirm, reason for close */ + u16 aux; + /* Followed in plink_confirm by status code, AID and supported + rates, and directly by supported rates in plink_open and + plink_close */ + u8 variable[0]; + } __attribute__ ((packed)) plink_action; + struct{ + u8 action_code; + u8 variable[0]; + } __attribute__ ((packed)) mesh_action; + struct { + u8 action; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } __attribute__ ((packed)) sa_query; + struct { + u8 action; + u8 smps_control; + } __attribute__ ((packed)) ht_smps; + } u; + } __attribute__ ((packed)) action; + } u; +} __attribute__ ((packed)); + +#if TLS_CONFIG_11N +/* mgmt header + 1 byte category code */ +#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) + +#endif + +/* Management MIC information element (IEEE 802.11w) */ +struct ieee80211_mmie { + u8 element_id; + u8 length; + u16 key_id; + u8 sequence_number[6]; + u8 mic[8]; +} __attribute__ ((packed)); + +/* Control frames */ +struct ieee80211_rts { + u16 frame_control; + u16 duration; + u8 ra[6]; + u8 ta[6]; +} __attribute__ ((packed)); + +struct ieee80211_cts { + u16 frame_control; + u16 duration; + u8 ra[6]; +} __attribute__ ((packed)); + + struct ieee80211_pspoll { + u16 frame_control; + u16 aid; + u8 bssid[6]; + u8 ta[6]; +} __attribute__ ((packed)); + +/** + * struct ieee80211_bar - HT Block Ack Request + * + * This structure refers to "HT BlockAckReq" as + * described in 802.11n draft section 7.2.1.7.1 + */ +struct ieee80211_bar { + u16 frame_control; + u16 duration; + u8 ra[6]; + u8 ta[6]; + u16 control; + u16 start_seq_num; +} __attribute__ ((packed)); + +/* 802.11 BAR control masks */ +#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004 + + +#define IEEE80211_HT_MCS_MASK_LEN 10 + +/** + * struct ieee80211_mcs_info - MCS information + * @rx_mask: RX mask + * @rx_highest: highest supported RX rate. If set represents + * the highest supported RX data rate in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest RX data rate supported. + * @tx_params: TX parameters + */ +struct ieee80211_mcs_info { + u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN]; + u16 rx_highest; + u8 tx_params; + u8 reserved[3]; +} __attribute__ ((packed)); + +/* 802.11n HT capability MSC set */ +#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff +#define IEEE80211_HT_MCS_TX_DEFINED 0x01 +#define IEEE80211_HT_MCS_TX_RX_DIFF 0x02 +/* value 0 == 1 stream etc */ +#define IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK 0x0C +#define IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT 2 +#define IEEE80211_HT_MCS_TX_MAX_STREAMS 4 +#define IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION 0x10 + +/* + * 802.11n D5.0 20.3.5 / 20.6 says: + * - indices 0 to 7 and 32 are single spatial stream + * - 8 to 31 are multiple spatial streams using equal modulation + * [8..15 for two streams, 16..23 for three and 24..31 for four] + * - remainder are multiple spatial streams using unequal modulation + */ +#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START 33 +#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE \ + (IEEE80211_HT_MCS_UNEQUAL_MODULATION_START / 8) + +/** + * struct ieee80211_ht_cap - HT capabilities + * + * This structure is the "HT capabilities element" as + * described in 802.11n D5.0 7.3.2.56 + */ +struct ieee80211_ht_cap { + u16 cap_info; + u8 ampdu_params_info; + +/* 16 bytes MCS information */ + struct ieee80211_mcs_info mcs; + + u16 extended_ht_cap_info; + u32 tx_BF_cap_info; + u8 antenna_selection_info; +} __attribute__ ((packed)); + +/* 802.11n HT capabilities masks (for cap_info) */ +#define IEEE80211_HT_CAP_LDPC_CODING 0x0001 +#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 +#define IEEE80211_HT_CAP_SM_PS 0x000C +#define IEEE80211_HT_CAP_SM_PS_SHIFT 2 +/* Spatial Multiplexing Power Save Modes (for capability) */ +#define IEEE80211_HT_CAP_SM_PS_STATIC 0 +#define IEEE80211_HT_CAP_SM_PS_DYNAMIC 1 +#define IEEE80211_HT_CAP_SM_PS_INVALID 2 +#define IEEE80211_HT_CAP_SM_PS_DISABLED 3 + +#define IEEE80211_HT_CAP_GRN_FLD 0x0010 +#define IEEE80211_HT_CAP_SGI_20 0x0020 +#define IEEE80211_HT_CAP_SGI_40 0x0040 +#define IEEE80211_HT_CAP_TX_STBC 0x0080 +#define IEEE80211_HT_CAP_RX_STBC 0x0300 +#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8 +#define IEEE80211_HT_CAP_RX_STBC_DISABLE 0 +#define IEEE80211_HT_CAP_RX_STBC_1 1 +#define IEEE80211_HT_CAP_RX_STBC_2 2 +#define IEEE80211_HT_CAP_RX_STBC_3 3 + +#define IEEE80211_HT_CAP_DELAY_BA 0x0400 +#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 +#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 +#define IEEE80211_HT_CAP_RESERVED 0x2000 +#define IEEE80211_HT_CAP_40MHZ_INTOLERANT 0x4000 +#define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000 + +/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */ +#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03 +#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C +#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2 + +/* + * Maximum length of AMPDU that the STA can receive. + * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets) + */ +enum ieee80211_max_ampdu_length_exp { + IEEE80211_HT_MAX_AMPDU_8K = 0, + IEEE80211_HT_MAX_AMPDU_16K = 1, + IEEE80211_HT_MAX_AMPDU_32K = 2, + IEEE80211_HT_MAX_AMPDU_64K = 3 +}; + +/* 802.11n HT extended capability */ +#define IEEE80211_HT_EXT_CAP_HTC 0x0400 +#define IEEE80211_HT_EXT_CAP_RD 0x0800 + + +#define IEEE80211_HT_MAX_AMPDU_FACTOR 13 + +/* Minimum MPDU start spacing */ +enum ieee80211_min_mpdu_spacing { + IEEE80211_HT_MPDU_DENSITY_NONE = 0, /* No restriction */ + IEEE80211_HT_MPDU_DENSITY_0_25 = 1, /* 1/4 usec */ + IEEE80211_HT_MPDU_DENSITY_0_5 = 2, /* 1/2 usec */ + IEEE80211_HT_MPDU_DENSITY_1 = 3, /* 1 usec */ + IEEE80211_HT_MPDU_DENSITY_2 = 4, /* 2 usec */ + IEEE80211_HT_MPDU_DENSITY_4 = 5, /* 4 usec */ + IEEE80211_HT_MPDU_DENSITY_8 = 6, /* 8 usec */ + IEEE80211_HT_MPDU_DENSITY_16 = 7 /* 16 usec */ +}; + +#define IEEE80211_HT_MPDU_DENSITY_SHIFT 2 +/** + * struct ieee80211_ht_info - HT information + * + * This structure is the "HT information element" as + * described in 802.11n D5.0 7.3.2.58 + */ +/* ELEMENTID = 61, HT Operation element in 11n7.3.5.57 +*/ + + __packed struct ieee80211_ht_info { + u8 control_chan; + u8 ht_param; + u16 operation_mode; + u16 stbc_param; + u8 basic_set[16]; +}; + +/* for ht_param */ +#define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03 +#define IEEE80211_HT_PARAM_CHA_SEC_NONE 0x00 +#define IEEE80211_HT_PARAM_CHA_SEC_ABOVE 0x01 +#define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03 +#define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04 +#define IEEE80211_HT_PARAM_RIFS_MODE 0x08 +#define IEEE80211_HT_PARAM_SPSMP_SUPPORT 0x10 +#define IEEE80211_HT_PARAM_SERV_INTERVAL_GRAN 0xE0 + +/* for operation_mode */ +#define IEEE80211_HT_OP_MODE_PROTECTION 0x0003 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONE 0 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER 1 +#define IEEE80211_HT_OP_MODE_PROTECTION_20MHZ 2 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3 +#define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004 +#define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010 + +/* for stbc_param */ +#define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040 +#define IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT 0x0080 +#define IEEE80211_HT_STBC_PARAM_STBC_BEACON 0x0100 +#define IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT 0x0200 +#define IEEE80211_HT_STBC_PARAM_PCO_ACTIVE 0x0400 +#define IEEE80211_HT_STBC_PARAM_PCO_PHASE 0x0800 + + +/* block-ack parameters */ +#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002 +#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C +#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0 +#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000 +#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800 + +/* + * A-PMDU buffer sizes + * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) + */ +#define IEEE80211_MIN_AMPDU_BUF 0x8 +#define IEEE80211_MAX_AMPDU_BUF 0x40 + + +/* Spatial Multiplexing Power Save Modes (for capability) */ +#define WLAN_HT_CAP_SM_PS_STATIC 0 +#define WLAN_HT_CAP_SM_PS_DYNAMIC 1 +#define WLAN_HT_CAP_SM_PS_INVALID 2 +#define WLAN_HT_CAP_SM_PS_DISABLED 3 + +/* for SM power control field lower two bits */ +#define WLAN_HT_SMPS_CONTROL_DISABLED 0 +#define WLAN_HT_SMPS_CONTROL_STATIC 1 +#define WLAN_HT_SMPS_CONTROL_DYNAMIC 3 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FT 2 +#define WLAN_AUTH_LEAP 128 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) +#define WLAN_CAPABILITY_CF_POLLABLE (1<<2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3) +#define WLAN_CAPABILITY_PRIVACY (1<<4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) +#define WLAN_CAPABILITY_PBCC (1<<6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) + +/* 802.11h */ +#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) +#define WLAN_CAPABILITY_QOS (1<<9) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) +#define WLAN_CAPABILITY_DSSS_OFDM (1<<13) +/* measurement */ +#define IEEE80211_SPCT_MSR_RPRT_MODE_LATE (1<<0) +#define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE (1<<1) +#define IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED (1<<2) + +#define IEEE80211_SPCT_MSR_RPRT_TYPE_BASIC 0 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA 1 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI 2 + + +/* 802.11g ERP information element */ +#define WLAN_ERP_NON_ERP_PRESENT (1<<0) +#define WLAN_ERP_USE_PROTECTION (1<<1) +#define WLAN_ERP_BARKER_PREAMBLE (1<<2) + +/* WLAN_ERP_BARKER_PREAMBLE values */ +enum { + WLAN_ERP_PREAMBLE_SHORT = 0, + WLAN_ERP_PREAMBLE_LONG = 1, +}; + +/* Status codes */ +enum ieee80211_statuscode { + WLAN_STATUS_SUCCESS = 0, + WLAN_STATUS_UNSPECIFIED_FAILURE = 1, + WLAN_STATUS_CAPS_UNSUPPORTED = 10, + WLAN_STATUS_REASSOC_NO_ASSOC = 11, + WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12, + WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13, + WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14, + WLAN_STATUS_CHALLENGE_FAIL = 15, + WLAN_STATUS_AUTH_TIMEOUT = 16, + WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17, + WLAN_STATUS_ASSOC_DENIED_RATES = 18, +/* 802.11b */ + WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19, + WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20, + WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21, +/* 802.11h */ + WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22, + WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23, + WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24, +/* 802.11g */ + WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25, + WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26, + + WLAN_STATUS_ASSOC_DENIED_NO_HT = 27, +/* 802.11w */ + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY = 30, + WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION = 31, +/* 802.11i */ + WLAN_STATUS_INVALID_IE = 40, + WLAN_STATUS_INVALID_GROUP_CIPHER = 41, + WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42, + WLAN_STATUS_INVALID_AKMP = 43, + WLAN_STATUS_UNSUPP_RSN_VERSION = 44, + WLAN_STATUS_INVALID_RSN_IE_CAP = 45, + WLAN_STATUS_CIPHER_SUITE_REJECTED = 46, +/* 802.11e */ + WLAN_STATUS_UNSPECIFIED_QOS = 32, + WLAN_STATUS_ASSOC_DENIED_NOBANDWIDTH = 33, + WLAN_STATUS_ASSOC_DENIED_LOWACK = 34, + WLAN_STATUS_ASSOC_DENIED_UNSUPP_QOS = 35, + WLAN_STATUS_REQUEST_DECLINED = 37, + WLAN_STATUS_INVALID_QOS_PARAM = 38, + WLAN_STATUS_CHANGE_TSPEC = 39, + WLAN_STATUS_WAIT_TS_DELAY = 47, + WLAN_STATUS_NO_DIRECT_LINK = 48, + WLAN_STATUS_STA_NOT_PRESENT = 49, + WLAN_STATUS_STA_NOT_QSTA = 50, + WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE = 51, +}; + + +/* Reason codes */ +enum ieee80211_reasoncode { + WLAN_REASON_UNSPECIFIED = 1, + WLAN_REASON_PREV_AUTH_NOT_VALID = 2, + WLAN_REASON_DEAUTH_LEAVING = 3, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4, + WLAN_REASON_DISASSOC_AP_BUSY = 5, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7, + WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8, + WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9, +/* 802.11h */ + WLAN_REASON_DISASSOC_BAD_POWER = 10, + WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11, +/* 802.11i */ + WLAN_REASON_INVALID_IE = 13, + WLAN_REASON_MIC_FAILURE = 14, + WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, + WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16, + WLAN_REASON_IE_DIFFERENT = 17, + WLAN_REASON_INVALID_GROUP_CIPHER = 18, + WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19, + WLAN_REASON_INVALID_AKMP = 20, + WLAN_REASON_UNSUPP_RSN_VERSION = 21, + WLAN_REASON_INVALID_RSN_IE_CAP = 22, + WLAN_REASON_IEEE8021X_FAILED = 23, + WLAN_REASON_CIPHER_SUITE_REJECTED = 24, +/* 802.11e */ + WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32, + WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33, + WLAN_REASON_DISASSOC_LOW_ACK = 34, + WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP = 35, + WLAN_REASON_QSTA_LEAVE_QBSS = 36, + WLAN_REASON_QSTA_NOT_USE = 37, + WLAN_REASON_QSTA_REQUIRE_SETUP = 38, + WLAN_REASON_QSTA_TIMEOUT = 39, + WLAN_REASON_QSTA_CIPHER_NOT_SUPP = 45, +}; + + +/* Information Element IDs */ +enum ieee80211_eid { + WLAN_EID_SSID = 0, + WLAN_EID_SUPP_RATES = 1, + WLAN_EID_FH_PARAMS = 2, + WLAN_EID_DS_PARAMS = 3, + WLAN_EID_CF_PARAMS = 4, + WLAN_EID_TIM = 5, + WLAN_EID_IBSS_PARAMS = 6, + WLAN_EID_CHALLENGE = 16, + + WLAN_EID_COUNTRY = 7, + WLAN_EID_HP_PARAMS = 8, + WLAN_EID_HP_TABLE = 9, + WLAN_EID_REQUEST = 10, + + WLAN_EID_QBSS_LOAD = 11, + WLAN_EID_EDCA_PARAM_SET = 12, + WLAN_EID_TSPEC = 13, + WLAN_EID_TCLAS = 14, + WLAN_EID_SCHEDULE = 15, + WLAN_EID_TS_DELAY = 43, + WLAN_EID_TCLAS_PROCESSING = 44, + WLAN_EID_QOS_CAPA = 46, +/* 802.11s */ + WLAN_EID_MESH_CONFIG = 113, + WLAN_EID_MESH_ID = 114, + WLAN_EID_LINK_METRIC_REPORT = 115, + WLAN_EID_CONGESTION_NOTIFICATION = 116, +/* Note that the Peer Link IE has been replaced with the similar Peer + Management IE. We will keep the former definition until mesh code is + changed to comply with latest 802.11s drafts. */ + WLAN_EID_PEER_LINK = 55, /* no longer in 802.11s drafts */ + WLAN_EID_PEER_MGMT = 117, + WLAN_EID_CHAN_SWITCH_PARAM = 118, + WLAN_EID_MESH_AWAKE_WINDOW = 119, + WLAN_EID_BEACON_TIMING = 120, + WLAN_EID_MCCAOP_SETUP_REQ = 121, + WLAN_EID_MCCAOP_SETUP_RESP = 122, + WLAN_EID_MCCAOP_ADVERT = 123, + WLAN_EID_MCCAOP_TEARDOWN = 124, + WLAN_EID_GANN = 125, + WLAN_EID_RANN = 126, + WLAN_EID_PREQ = 130, + WLAN_EID_PREP = 131, + WLAN_EID_PERR = 132, + WLAN_EID_PXU = 137, + WLAN_EID_PXUC = 138, + WLAN_EID_AUTH_MESH_PEER_EXCH = 139, + WLAN_EID_MIC = 140, + + WLAN_EID_PWR_CONSTRAINT = 32, + WLAN_EID_PWR_CAPABILITY = 33, + WLAN_EID_TPC_REQUEST = 34, + WLAN_EID_TPC_REPORT = 35, + WLAN_EID_SUPPORTED_CHANNELS = 36, + WLAN_EID_CHANNEL_SWITCH = 37, + WLAN_EID_MEASURE_REQUEST = 38, + WLAN_EID_MEASURE_REPORT = 39, + WLAN_EID_QUIET = 40, + WLAN_EID_IBSS_DFS = 41, + + WLAN_EID_ERP_INFO = 42, + WLAN_EID_EXT_SUPP_RATES = 50, + + WLAN_EID_HT_CAPABILITY = 45, + WLAN_EID_HT_INFORMATION = 61, + + WLAN_EID_RSN = 48, + WLAN_EID_MMIE = 76, + WLAN_EID_WPA = 221, + WLAN_EID_GENERIC = 221, + WLAN_EID_VENDOR_SPECIFIC = 221, + WLAN_EID_QOS_PARAMETER = 222, + + WLAN_EID_AP_CHAN_REPORT = 51, + WLAN_EID_NEIGHBOR_REPORT = 52, + WLAN_EID_RCPI = 53, + WLAN_EID_BSS_AVG_ACCESS_DELAY = 63, + WLAN_EID_ANTENNA_INFO = 64, + WLAN_EID_RSNI = 65, + WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66, + WLAN_EID_BSS_AVAILABLE_CAPACITY = 67, + WLAN_EID_BSS_AC_ACCESS_DELAY = 68, + WLAN_EID_RRM_ENABLED_CAPABILITIES = 70, + WLAN_EID_MULTIPLE_BSSID = 71, + WLAN_EID_BSS_COEX_2040 = 72, + WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74, + WLAN_EID_EXT_CAPABILITY = 127, + + WLAN_EID_MOBILITY_DOMAIN = 54, + WLAN_EID_FAST_BSS_TRANSITION = 55, + WLAN_EID_TIMEOUT_INTERVAL = 56, + WLAN_EID_RIC_DATA = 57, + WLAN_EID_RIC_DESCRIPTOR = 75, + + WLAN_EID_DSE_REGISTERED_LOCATION = 58, + WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59, + WLAN_EID_EXT_CHANSWITCH_ANN = 60, + + WLAN_EID_TIME_ZONE = 98, + WLAN_EID_LINK_ID = 101, + WLAN_EID_INTERWORKING = 107, + WLAN_EID_ADV_PROTO = 108, + WLAN_EID_ROAMING_CONSORTIUM = 111, + WLAN_EID_EXT_CAPAB = 127, + +}; + +/* Action category code */ +enum ieee80211_category { + WLAN_CATEGORY_SPECTRUM_MGMT = 0, + WLAN_CATEGORY_QOS = 1, + WLAN_CATEGORY_DLS = 2, + WLAN_CATEGORY_BACK = 3, + WLAN_CATEGORY_PUBLIC = 4, + WLAN_CATEGORY_HT = 7, + WLAN_CATEGORY_SA_QUERY = 8, + WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, + WLAN_CATEGORY_WMM = 17, + WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, + WLAN_CATEGORY_VENDOR_SPECIFIC = 127, +}; + +/* SPECTRUM_MGMT action code */ +enum ieee80211_spectrum_mgmt_actioncode { + WLAN_ACTION_SPCT_MSR_REQ = 0, + WLAN_ACTION_SPCT_MSR_RPRT = 1, + WLAN_ACTION_SPCT_TPC_REQ = 2, + WLAN_ACTION_SPCT_TPC_RPRT = 3, + WLAN_ACTION_SPCT_CHL_SWITCH = 4, +}; + +/* HT action codes */ +enum ieee80211_ht_actioncode { + WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0, + WLAN_HT_ACTION_SMPS = 1, + WLAN_HT_ACTION_PSMP = 2, + WLAN_HT_ACTION_PCO_PHASE = 3, + WLAN_HT_ACTION_CSI = 4, + WLAN_HT_ACTION_NONCOMPRESSED_BF = 5, + WLAN_HT_ACTION_COMPRESSED_BF = 6, + WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7, +}; + +/* Security key length */ +enum ieee80211_key_len { + WLAN_KEY_LEN_WEP40 = 5, + WLAN_KEY_LEN_WEP104 = 13, + WLAN_KEY_LEN_CCMP = 16, + WLAN_KEY_LEN_TKIP = 32, + WLAN_KEY_LEN_AES_CMAC = 16, +}; + +/* + * IEEE 802.11-2007 7.3.2.9 Country information element + * + * Minimum length is 8 octets, ie len must be evenly + * divisible by 2 + */ + +/* Although the spec says 8 I'm seeing 6 in practice */ +#define IEEE80211_COUNTRY_IE_MIN_LEN 6 + +/* + * For regulatory extension stuff see IEEE 802.11-2007 + * Annex I (page 1141) and Annex J (page 1147). Also + * review 7.3.2.9. + * + * When dot11RegulatoryClassesRequired is TRUE and the + * first_channel/reg_extension_id is >= 201 then the IE + * compromises of the 'ext' struct represented below: + * + * - Regulatory extension ID - when generating IE this just needs + * to be monotonically increasing for each triplet passed in + * the IE + * - Regulatory class - index into set of rules + * - Coverage class - index into air propagation time (Table 7-27), + * in microseconds, you can compute the air propagation time from + * the index by multiplying by 3, so index 10 yields a propagation + * of 10 us. Valid values are 0-31, values 32-255 are not defined + * yet. A value of 0 inicates air propagation of <= 1 us. + * + * See also Table I.2 for Emission limit sets and table + * I.3 for Behavior limit sets. Table J.1 indicates how to map + * a reg_class to an emission limit set and behavior limit set. + */ +#define IEEE80211_COUNTRY_EXTENSION_ID 201 + +/* + * Channels numbers in the IE must be monotonically increasing + * if dot11RegulatoryClassesRequired is not TRUE. + * + * If dot11RegulatoryClassesRequired is TRUE consecutive + * subband triplets following a regulatory triplet shall + * have monotonically increasing first_channel number fields. + * + * Channel numbers shall not overlap. + * + * Note that max_power is signed. + */ + __packed struct ieee80211_country_ie_triplet { + __packed union { + __packed struct { + u8 first_channel; + u8 num_channels; + s8 max_power; + } chans; + __packed struct { + u8 reg_extension_id; + u8 reg_class; + u8 coverage_class; + } ext; + } u; +}; + +enum ieee80211_timeout_interval_type { + WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */ , + WLAN_TIMEOUT_KEY_LIFETIME = 2 /* 802.11r */ , + WLAN_TIMEOUT_ASSOC_COMEBACK = 3 /* 802.11w */ , +}; + +/* BACK action code */ +enum ieee80211_back_actioncode { + WLAN_ACTION_ADDBA_REQ = 0, + WLAN_ACTION_ADDBA_RESP = 1, + WLAN_ACTION_DELBA = 2, +}; + +/* BACK (block-ack) parties */ +enum ieee80211_back_parties { + WLAN_BACK_RECIPIENT = 0, + WLAN_BACK_INITIATOR = 1, + WLAN_BACK_TIMER = 2, +}; + +/* SA Query action */ +enum ieee80211_sa_query_action { + WLAN_ACTION_SA_QUERY_REQUEST = 0, + WLAN_ACTION_SA_QUERY_RESPONSE = 1, +}; + + +/* A-MSDU 802.11n */ +#define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080 + +/* cipher suite selectors */ +#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 +#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 +#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 +/* reserved: 0x000FAC03 */ +#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 +#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 + +/* AKM suite selectors */ +#define WLAN_AKM_SUITE_8021X 0x000FAC01 +#define WLAN_AKM_SUITE_PSK 0x000FAC02 + +#define WLAN_MAX_KEY_LEN 32 + +#define WLAN_PMKID_LEN 16 + +/** + * @defgroup Wi-Fi_APIs Wi-Fi APIs + * @brief Wi-Fi related APIs + */ + +/** + * @addtogroup Wi-Fi_APIs + * @{ + */ + +/** + * @defgroup IEEE80211_APIs IEEE80211 APIs + * @brief IEEE80211 APIs + */ + +/** + * @addtogroup IEEE80211_APIs + * @{ + */ + +/** + * @brief get pointer to qos control bytes + * + * @param[in] *hdr the frame + * + * @return pointer to qos control bytes + * + * @note + * The qos ctrl bytes come after the frame_control, duration, seq_num + * and 3 or 4 addresses of length ETH_ALEN. + * 3 addr: 2 + 2 + 2 + 3*6 = 24 + * 4 addr: 2 + 2 + 2 + 4*6 = 30 + */ +static __inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return (u8 *) hdr + 30; + else + return (u8 *) hdr + 24; +} + +/** + * @brief get pointer to SA + * + * @param[in] *hdr the frame + * + * @return SA address + * + * @note + * Given an 802.11 frame, this function returns the offset + * to the source address (SA). It does not verify that the + * header is long enough to contain the address, and the + * header must be long enough to contain the frame control + * field. + */ +static __inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return hdr->addr4; + if (ieee80211_has_fromds(hdr->frame_control)) + return hdr->addr3; + return hdr->addr2; +} + +/** + * @brief get pointer to DA + * + * @param[in] *hdr the frame + * + * @return DA address + * + * @note + * Given an 802.11 frame, this function returns the offset + * to the destination address (DA). It does not verify that + * the header is long enough to contain the address, and the + * header must be long enough to contain the frame control + * field. + */ +static __inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_tods(hdr->frame_control)) + return hdr->addr3; + else + return hdr->addr1; +} + +/** + * @brief check if frame is a robust management frame + * + * @param[in] *hdr the frame (buffer must include at least the first octet of payload) + * + * @retval TRUE yes + * @retval FALSE no + * + * @note None + */ +static __inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_disassoc(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control)) + return TRUE; + + if (ieee80211_is_action(hdr->frame_control)) { + u8 *category; + + /* + * Action frames, excluding Public Action frames, are Robust + * Management Frames. However, if we are looking at a Protected + * frame, skip the check since the data may be encrypted and + * the frame has already been found to be a Robust Management + * Frame (by the other end). + */ + if (ieee80211_has_protected(hdr->frame_control)) + return TRUE; + category = ((u8 *) hdr) + 24; + return *category != WLAN_CATEGORY_PUBLIC && + *category != WLAN_CATEGORY_HT && + *category != WLAN_CATEGORY_VENDOR_SPECIFIC; + } + + return FALSE; +} + +/** + * @brief get channel frequency + * + * @param[in] channel the FHSS channel + * + * @retval -1 failed + * @retval other frequency + * + * @note + * Convert IEEE802.11 FHSS channel to frequency (MHz) + * Ref IEEE 802.11-2007 section 14.6 + */ +static __inline int ieee80211_fhss_chan_to_freq(int channel) +{ + if ((channel > 1) && (channel < 96)) + return channel + 2400; + else + return -1; +} + +/** + * @brief get channel + * + * @param[in] freq the channels frequency + * + * @retval -1 failed + * @retval other channel + * + * @note + * Convert frequency (MHz) to IEEE802.11 FHSS channel + * Ref IEEE 802.11-2007 section 14.6 + */ +static __inline int ieee80211_freq_to_fhss_chan(int freq) +{ + if ((freq > 2401) && (freq < 2496)) + return freq - 2400; + else + return -1; +} + +/** + * @brief get channel center frequency + * + * @param[in] channel the DSSS channel + * + * @retval -1 failed + * @retval other frequency + * + * @note + * Convert IEEE802.11 DSSS channel to the center frequency (MHz). + * Ref IEEE 802.11-2007 section 15.6 + */ +static __inline int ieee80211_dsss_chan_to_freq(int channel) +{ + if ((channel > 0) && (channel < 14)) + return 2407 + (channel * 5); + else if (channel == 14) + return 2484; + else + return -1; +} + +/** + * @brief get channel + * + * @param[in] freq the frequency + * + * @retval -1 failed + * @retval other channel + * + * @note + * Convert frequency (MHz) to IEEE802.11 DSSS channel + * Ref IEEE 802.11-2007 section 15.6 + * This routine selects the channel with the closest center frequency. + */ +static __inline int ieee80211_freq_to_dsss_chan(int freq) +{ + if ((freq >= 2410) && (freq < 2475)) + return (freq - 2405) / 5; + else if ((freq >= 2482) && (freq < 2487)) + return 14; + else + return -1; +} + +/** Convert IEEE802.11 HR DSSS channel to frequency (MHz). Ref IEEE 802.11-2007 section 18.4.6.2. + The channels and frequencies are the same as those defined for DSSS. */ +#define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan) +/** Convert IEEE802.11 frequency (MHz) to HR DSSS channel. Ref IEEE 802.11-2007 section 18.4.6.2. + The channels and frequencies are the same as those defined for DSSS. */ +#define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq) + +/** Convert IEEE802.11 ERP channel to frequency (MHz). Ref IEEE 802.11-2007 section 19.4.2 */ +#define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan) +/** Convert IEEE802.11 frequency (MHz) to ERP channel. Ref IEEE 802.11-2007 section 19.4.2 */ +#define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq) + +/** + * @brief get channel center frequency + * + * @param[in] s_freq starting frequency == (dotChannelStartingFactor/2) MHz + * @param[in] channel the OFDM channel + * + * @retval -1 failed + * @retval other frequency + * + * @note + * Convert IEEE802.11 OFDM channel to center frequency (MHz) + * Ref IEEE 802.11-2007 section 17.3.8.3.2 + */ +static __inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel) +{ + if ((channel > 0) && (channel <= 200) && (s_freq >= 4000)) + return s_freq + (channel * 5); + else + return -1; +} + +/** + * @brief get channel + * + * @param[in] s_freq starting frequency == (dotChannelStartingFactor/2) MHz + * @param[in] freq the frequency + * + * @retval -1 failed + * @retval other channel + * + * @note + * Convert frequency (MHz) to IEEE802.11 OFDM channel + * Ref IEEE 802.11-2007 section 17.3.8.3.2 + * This routine selects the channel with the closest center frequency. + */ +static __inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq) +{ + if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) && (s_freq >= 4000)) + return (freq + 2 - s_freq) / 5; + else + return -1; +} + +/** + * @brief convert time units (TU) to microseconds + * + * @param[in] tu the TUs + * + * @return microseconds value + * + * @note None + */ +static __inline unsigned long ieee80211_tu_to_usec(unsigned long tu) +{ + return 1024 * tu; +} + +/** + * @brief check if AID bit is set in TIM + * + * @param[in] *tim the TIM IE + * @param[in] tim_len length of the TIM IE + * @param[in] aid the AID to look for + * + * @retval 0 AID bit not is set in TIM + * @retval other AID bit is set im TIM + * + * @note None + */ +static __inline bool ieee80211_check_tim(struct ieee80211_tim_ie *tim, + u8 tim_len, u16 aid) +{ + u8 mask; + u8 index, indexn1, indexn2; + + if (!tim || tim_len < sizeof(*tim)) + return FALSE; + + aid &= 0x3fff; + index = aid / 8; + mask = 1 << (aid & 7); + + indexn1 = tim->bitmap_ctrl & 0xfe; + indexn2 = tim_len + indexn1 - 4; + + if (index < indexn1 || index > indexn2) + return FALSE; + + index -= indexn1; + + return !!(tim->virtual_map[index] & mask); +} + +/** + * @} + */ + +/** + * @} + */ + +#endif /* end of TLS_IEEE80211_H */ diff --git a/sdk/Include/WiFi/wm_ieee80211_gcc.h b/sdk/Include/WiFi/wm_ieee80211_gcc.h new file mode 100644 index 0000000..00e82a2 --- /dev/null +++ b/sdk/Include/WiFi/wm_ieee80211_gcc.h @@ -0,0 +1,1682 @@ + + +#ifndef WM_IEEE80211_H +#define WM_IEEE80211_H +/* + * DS bit usage + * + * TA = transmitter address + * RA = receiver address + * DA = destination address + * SA = source address + * + * ToDS FromDS A1(RA) A2(TA) A3 A4 Use + * ----------------------------------------------------------------- + * 0 0 DA SA BSSID - IBSS/DLS + * 0 1 DA BSSID SA - AP -> STA + * 1 0 BSSID SA DA - AP <- STA + * 1 1 RA TA DA SA unspecified (WDS) + */ + +#define FCS_LEN 4 + +#define IEEE80211_FCTL_VERS 0x0003 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_PROTECTED 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + +#define IEEE80211_FTYPE_MGMT 0x0000 +#define IEEE80211_FTYPE_CTL 0x0004 +#define IEEE80211_FTYPE_DATA 0x0008 + +/* management */ +#define IEEE80211_STYPE_ASSOC_REQ 0x0000 +#define IEEE80211_STYPE_ASSOC_RESP 0x0010 +#define IEEE80211_STYPE_REASSOC_REQ 0x0020 +#define IEEE80211_STYPE_REASSOC_RESP 0x0030 +#define IEEE80211_STYPE_PROBE_REQ 0x0040 +#define IEEE80211_STYPE_PROBE_RESP 0x0050 +#define IEEE80211_STYPE_BEACON 0x0080 +#define IEEE80211_STYPE_ATIM 0x0090 +#define IEEE80211_STYPE_DISASSOC 0x00A0 +#define IEEE80211_STYPE_AUTH 0x00B0 +#define IEEE80211_STYPE_DEAUTH 0x00C0 +#define IEEE80211_STYPE_ACTION 0x00D0 + +/* control */ +#define IEEE80211_STYPE_BACK_REQ 0x0080 +#define IEEE80211_STYPE_BACK 0x0090 +#define IEEE80211_STYPE_PSPOLL 0x00A0 +#define IEEE80211_STYPE_RTS 0x00B0 +#define IEEE80211_STYPE_CTS 0x00C0 +#define IEEE80211_STYPE_ACK 0x00D0 +#define IEEE80211_STYPE_CFEND 0x00E0 +#define IEEE80211_STYPE_CFENDACK 0x00F0 + +/* data */ +#define IEEE80211_STYPE_DATA 0x0000 +#define IEEE80211_STYPE_DATA_CFACK 0x0010 +#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 +#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 +#define IEEE80211_STYPE_NULLFUNC 0x0040 +#define IEEE80211_STYPE_CFACK 0x0050 +#define IEEE80211_STYPE_CFPOLL 0x0060 +#define IEEE80211_STYPE_CFACKPOLL 0x0070 +#define IEEE80211_STYPE_QOS_DATA 0x0080 +#define IEEE80211_STYPE_QOS_DATA_CFACK 0x0090 +#define IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0 +#define IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0 +#define IEEE80211_STYPE_QOS_NULLFUNC 0x00C0 +#define IEEE80211_STYPE_QOS_CFACK 0x00D0 +#define IEEE80211_STYPE_QOS_CFPOLL 0x00E0 +#define IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0 + + +#define IEEE80211_STA_DEFAULT_LISTEN_INTERVAL 3 +#define IEEE80211_STA_MIN_LISTEN_INTERVAL 1 + +/* miscellaneous IEEE 802.11 constants */ +#define IEEE80211_MAX_FRAG_THRESHOLD 2352 +#define IEEE80211_MAX_RTS_THRESHOLD 2353 +#define IEEE80211_MAX_AID 2007 +#define IEEE80211_MAX_TIM_LEN 251 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + 802.11e clarifies the figure in section 7.1.2. The frame body is + up to 2304 octets long (maximum MSDU size) plus any crypt overhead. */ +#define IEEE80211_MAX_DATA_LEN 2304 +/* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */ +#define IEEE80211_MAX_FRAME_LEN 2352 + +#define IEEE80211_MAX_SSID_LEN 32 + +#define IEEE80211_MAX_MESH_ID_LEN 32 + +#define IEEE80211_QOS_CTL_LEN 2 +#define IEEE80211_QOS_CTL_TID_MASK 0x000F +#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 + +#define IEEE80211_HT_CTL_LEN 4 + +/* U-APSD queue for WMM IEs sent by AP */ +#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) + +/* U-APSD queues for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f + +/* U-APSD max SP length for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5 + +/* Parsed Information Elements */ +struct ieee802_11_elems { + u8 *ie_start; + size_t total_len; + + /* pointers to IEs */ + u8 *ssid; + u8 *supp_rates; + u8 *fh_params; + u8 *ds_params; + u8 *cf_params; + u8 *tim; + u8 *ibss_params; + u8 *country_elem; + u8 *challenge; + u8 *erp_info; + u8 *ext_supp_rates; + u8 *wpa_ie; + u8 *rsn_ie; + u8 *wmm; /* WMM Information or Parameter Element */ + u8 *wmm_param; + struct ieee80211_ht_cap *ht_cap_elem; + struct ieee80211_ht_info *ht_info_elem; + u8 *wmm_tspec; + u8 *wps_ie; + u8 *power_cap; + u8 *supp_channels; + u8 *mdie; + u8 *ftie; + u8 *timeout_int; + u8 *ch_switch_elem; + u8 *ht_capabilities; + u8 *ht_operation; + u8 *vendor_ht_cap; + u8 *p2p; + u8 *link_id; + u8 *pwr_constr_elem; + u8 *interworking; + + u8 ssid_len; + u8 supp_rates_len; + u8 fh_params_len; + u8 ds_params_len; + u8 cf_params_len; + u8 tim_len; + u8 ibss_params_len; + u8 challenge_len; + u8 erp_info_len; + u8 ext_supp_rates_len; + u8 ch_switch_elem_len; + u8 wpa_ie_len; + u8 rsn_ie_len; + u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ + u8 wmm_param_len; + u8 wmm_tspec_len; + u8 wps_ie_len; + u8 power_cap_len; + u8 supp_channels_len; + u8 mdie_len; + u8 ftie_len; + u8 timeout_int_len; + u8 ht_capabilities_len; + u8 pwr_constr_elem_len; + u8 country_elem_len; + u8 ht_operation_len; + u8 vendor_ht_cap_len; + u8 p2p_len; + u8 interworking_len; + +}; + +struct ieee80211_hdr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u8 addr4[6]; +}__attribute__((packed)); + +struct ieee80211_hdr_3addr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; +}__attribute__((packed)); + +struct ieee80211_qos_hdr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u16 qos_ctrl; +}__attribute__((packed)); +typedef enum _phy_type{ + phy_80211_b, + phy_80211_bg, + phy_80211_bgn, + phy_80211_n, + phy_80211_max +}phy_type; + + +#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr_3addr)) + +/** + * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_tods(u16 fc) +{ + return (fc & IEEE80211_FCTL_TODS) != 0; +} + +/** + * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_fromds(u16 fc) +{ + return (fc & IEEE80211_FCTL_FROMDS) != 0; +} + +/** + * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_a4(u16 fc) +{ + u16 tmp = IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS; + return (fc & tmp) == tmp; +} + +/** + * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_morefrags(u16 fc) +{ + return (fc & IEEE80211_FCTL_MOREFRAGS) != 0; +} + +/** + * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_retry(u16 fc) +{ + return (fc & IEEE80211_FCTL_RETRY) != 0; +} + +/** + * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_pm(u16 fc) +{ + return (fc & IEEE80211_FCTL_PM) != 0; +} + +/** + * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_moredata(u16 fc) +{ + return (fc & IEEE80211_FCTL_MOREDATA) != 0; +} + +/** + * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_protected(u16 fc) +{ + return (fc & IEEE80211_FCTL_PROTECTED) != 0; +} + +/** + * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_has_order(u16 fc) +{ + return (fc & IEEE80211_FCTL_ORDER) != 0; +} + +/** + * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_mgmt(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT; +} + +/** + * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_ctl(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL; +} + +/** + * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_data(u16 fc) +{ + return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA; +} + +/** + * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_data_qos(u16 fc) +{ + /* + * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need + * to check the one bit + */ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_STYPE_QOS_DATA)) == + (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA); +} + +/** + * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_data_present(u16 fc) +{ + /* + * mask with 0x40 and test that that bit is clear to only return TRUE + * for the data-containing substypes. + */ + return (fc & (IEEE80211_FCTL_FTYPE | 0x40)) == (IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_assoc_req(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); +} + +/** + * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_assoc_resp(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP); +} + +/** + * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_reassoc_req(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); +} + +/** + * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_reassoc_resp(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); +} + +/** + * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_probe_req(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); +} + +/** + * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_probe_resp(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); +} + +/** + * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_beacon(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); +} + +/** + * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_atim(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM); +} + +/** + * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_disassoc(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); +} + +/** + * ieee80211_is_auth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_auth(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); +} + +/** + * ieee80211_is_deauth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_deauth(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); +} + +/** + * ieee80211_is_action - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_action(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); +} + +/** + * ieee80211_is_back_req - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_back_req(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); +} + +/** + * ieee80211_is_back - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_back(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); +} + +/** + * ieee80211_is_pspoll - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_pspoll(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); +} + +/** + * ieee80211_is_rts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_rts(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); +} + +/** + * ieee80211_is_cts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_cts(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); +} + +/** + * ieee80211_is_ack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_ack(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK); +} + +/** + * ieee80211_is_cfend - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_cfend(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND); +} + +/** + * ieee80211_is_cfendack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_cfendack(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK); +} + +/** + * ieee80211_is_nullfunc - check if frame is a regular (non-QoS) nullfunc frame + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_nullfunc(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC); +} + +/** + * ieee80211_is_qos_nullfunc - check if frame is a QoS nullfunc frame + * @fc: frame control bytes in little-endian byteorder + */ +static __inline int ieee80211_is_qos_nullfunc(u16 fc) +{ + return (fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); +} + +/** + * struct ieee80211_quiet_ie + * + * This structure refers to "Quiet information element" + */ +struct ieee80211_quiet_ie { + u8 count; + u8 period; + u16 duration; + u16 offset; +}__attribute__((packed)); + +/** + * struct ieee80211_msrment_ie + * + * This structure refers to "Measurement Request/Report information element" + */ +struct ieee80211_msrment_ie { + u8 token; + u8 mode; + u8 type; + u8 request[1]; +}__attribute__((packed)); + +/** + * struct ieee80211_channel_sw_ie + * + * This structure refers to "Channel Switch Announcement information element" + */ +struct ieee80211_channel_sw_ie { + u8 mode; + u8 new_ch_num; + u8 count; +}__attribute__((packed)); + +/** + * struct ieee80211_tim + * + * This structure refers to "Traffic Indication Map information element" + */ +struct ieee80211_tim_ie { + u8 dtim_count; + u8 dtim_period; + u8 bitmap_ctrl; + /* variable size: 1 - 251 bytes */ + u8 virtual_map[1]; +}__attribute__((packed)); + +/** + * struct ieee80211_rann_ie + * + * This structure refers to "Root Announcement information element" + */ +struct ieee80211_rann_ie { + u8 rann_flags; + u8 rann_hopcount; + u8 rann_ttl; + u8 rann_addr[6]; + u32 rann_seq; + u32 rann_metric; +}__attribute__((packed)); + +#define WLAN_SA_QUERY_TR_ID_LEN 2 + +struct ieee80211_mgmt { + u16 frame_control; + u16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + u16 seq_ctrl; + union { + struct { + u16 auth_alg; + u16 auth_transaction; + u16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } __attribute__ ((packed)) auth; + struct { + u16 reason_code; + } __attribute__ ((packed)) deauth; + struct { + u16 capab_info; + u16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } assoc_req; + struct { + u16 capab_info; + u16 status_code; + u16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_resp, reassoc_resp; + struct { + u16 capab_info; + u16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) reassoc_req; + struct { + u16 reason_code; + } __attribute__ ((packed)) disassoc; + struct { + u64 timestamp; + u16 beacon_int; + u16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } __attribute__ ((packed)) beacon; + struct { + /* only variable items: SSID, Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) probe_req; + struct { + u64 timestamp; + u16 beacon_int; + u16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params */ + u8 variable[0]; + } __attribute__ ((packed)) probe_resp; + struct { + u8 category; + union { + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[0]; + } __attribute__ ((packed)) wme_action; + struct{ + u8 action_code; + u8 element_id; + u8 length; + struct ieee80211_channel_sw_ie sw_elem; + } __attribute__ ((packed)) chan_switch; + struct{ + u8 action_code; + u8 channel_width; + } __attribute__ ((packed)) chan_width; + struct{ + u8 action_code; + u8 dialog_token; + u8 element_id; + u8 length; + struct ieee80211_msrment_ie msr_elem; + } __attribute__ ((packed)) measurement; + struct{ + u8 action_code; + u8 dialog_token; + u16 capab; + u16 timeout; + u16 start_seq_num; + } __attribute__ ((packed)) addba_req; + struct{ + u8 action_code; + u8 dialog_token; + u16 status; + u16 capab; + u16 timeout; + } __attribute__ ((packed)) addba_resp; + struct{ + u8 action_code; + u16 params; + u16 reason_code; + } __attribute__ ((packed)) delba; + struct{ + u8 action_code; + /* capab_info for open and confirm, + * reason for close + */ + u16 aux; + /* Followed in plink_confirm by status + * code, AID and supported rates, + * and directly by supported rates in + * plink_open and plink_close + */ + u8 variable[0]; + } __attribute__ ((packed)) plink_action; + struct{ + u8 action_code; + u8 variable[0]; + } __attribute__ ((packed)) mesh_action; + struct { + u8 action; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } __attribute__ ((packed)) sa_query; + struct { + u8 action; + u8 smps_control; + } __attribute__ ((packed)) ht_smps; + } u; + } __attribute__ ((packed)) action; + } u; +} __attribute__ ((packed)); + +#if TLS_CONFIG_11N +/* mgmt header + 1 byte category code */ +#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) + +#endif + +/* Management MIC information element (IEEE 802.11w) */ +struct ieee80211_mmie { + u8 element_id; + u8 length; + u16 key_id; + u8 sequence_number[6]; + u8 mic[8]; +}__attribute__ ((packed)) ; + +/* Control frames */ +struct ieee80211_rts { + u16 frame_control; + u16 duration; + u8 ra[6]; + u8 ta[6]; +}__attribute__ ((packed)) ; + +struct ieee80211_cts { + u16 frame_control; + u16 duration; + u8 ra[6]; +}__attribute__ ((packed)) ; + + struct ieee80211_pspoll { + u16 frame_control; + u16 aid; + u8 bssid[6]; + u8 ta[6]; +}__attribute__ ((packed)) ; + +/** + * struct ieee80211_bar - HT Block Ack Request + * + * This structure refers to "HT BlockAckReq" as + * described in 802.11n draft section 7.2.1.7.1 + */ +struct ieee80211_bar { + u16 frame_control; + u16 duration; + u8 ra[6]; + u8 ta[6]; + u16 control; + u16 start_seq_num; +}__attribute__ ((packed)) ; + +/* 802.11 BAR control masks */ +#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004 + + +#define IEEE80211_HT_MCS_MASK_LEN 10 + +/** + * struct ieee80211_mcs_info - MCS information + * @rx_mask: RX mask + * @rx_highest: highest supported RX rate. If set represents + * the highest supported RX data rate in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest RX data rate supported. + * @tx_params: TX parameters + */ +struct ieee80211_mcs_info { + u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN]; + u16 rx_highest; + u8 tx_params; + u8 reserved[3]; +}__attribute__ ((packed)) ; + +/* 802.11n HT capability MSC set */ +#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff +#define IEEE80211_HT_MCS_TX_DEFINED 0x01 +#define IEEE80211_HT_MCS_TX_RX_DIFF 0x02 +/* value 0 == 1 stream etc */ +#define IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK 0x0C +#define IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT 2 +#define IEEE80211_HT_MCS_TX_MAX_STREAMS 4 +#define IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION 0x10 + +/* + * 802.11n D5.0 20.3.5 / 20.6 says: + * - indices 0 to 7 and 32 are single spatial stream + * - 8 to 31 are multiple spatial streams using equal modulation + * [8..15 for two streams, 16..23 for three and 24..31 for four] + * - remainder are multiple spatial streams using unequal modulation + */ +#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START 33 +#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE \ + (IEEE80211_HT_MCS_UNEQUAL_MODULATION_START / 8) + +/** + * struct ieee80211_ht_cap - HT capabilities + * + * This structure is the "HT capabilities element" as + * described in 802.11n D5.0 7.3.2.56 + */ +struct ieee80211_ht_cap { + u16 cap_info; + u8 ampdu_params_info; + + /* 16 bytes MCS information */ + struct ieee80211_mcs_info mcs; + + u16 extended_ht_cap_info; + u32 tx_BF_cap_info; + u8 antenna_selection_info; +}__attribute__ ((packed)) ; + +/* 802.11n HT capabilities masks (for cap_info) */ +#define IEEE80211_HT_CAP_LDPC_CODING 0x0001 +#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 +#define IEEE80211_HT_CAP_SM_PS 0x000C +#define IEEE80211_HT_CAP_SM_PS_SHIFT 2 +/* Spatial Multiplexing Power Save Modes (for capability) */ +#define IEEE80211_HT_CAP_SM_PS_STATIC 0 +#define IEEE80211_HT_CAP_SM_PS_DYNAMIC 1 +#define IEEE80211_HT_CAP_SM_PS_INVALID 2 +#define IEEE80211_HT_CAP_SM_PS_DISABLED 3 + +#define IEEE80211_HT_CAP_GRN_FLD 0x0010 +#define IEEE80211_HT_CAP_SGI_20 0x0020 +#define IEEE80211_HT_CAP_SGI_40 0x0040 +#define IEEE80211_HT_CAP_TX_STBC 0x0080 +#define IEEE80211_HT_CAP_RX_STBC 0x0300 +#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8 +#define IEEE80211_HT_CAP_RX_STBC_DISABLE 0 +#define IEEE80211_HT_CAP_RX_STBC_1 1 +#define IEEE80211_HT_CAP_RX_STBC_2 2 +#define IEEE80211_HT_CAP_RX_STBC_3 3 + +#define IEEE80211_HT_CAP_DELAY_BA 0x0400 +#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 +#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 +#define IEEE80211_HT_CAP_RESERVED 0x2000 +#define IEEE80211_HT_CAP_40MHZ_INTOLERANT 0x4000 +#define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000 + +/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */ +#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03 +#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C +#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2 + +/* + * Maximum length of AMPDU that the STA can receive. + * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets) + */ +enum ieee80211_max_ampdu_length_exp { + IEEE80211_HT_MAX_AMPDU_8K = 0, + IEEE80211_HT_MAX_AMPDU_16K = 1, + IEEE80211_HT_MAX_AMPDU_32K = 2, + IEEE80211_HT_MAX_AMPDU_64K = 3 +}; + +/* 802.11n HT extended capability */ +#define IEEE80211_HT_EXT_CAP_HTC 0x0400 +#define IEEE80211_HT_EXT_CAP_RD 0x0800 + + +#define IEEE80211_HT_MAX_AMPDU_FACTOR 13 + +/* Minimum MPDU start spacing */ +enum ieee80211_min_mpdu_spacing { + IEEE80211_HT_MPDU_DENSITY_NONE = 0, /* No restriction */ + IEEE80211_HT_MPDU_DENSITY_0_25 = 1, /* 1/4 usec */ + IEEE80211_HT_MPDU_DENSITY_0_5 = 2, /* 1/2 usec */ + IEEE80211_HT_MPDU_DENSITY_1 = 3, /* 1 usec */ + IEEE80211_HT_MPDU_DENSITY_2 = 4, /* 2 usec */ + IEEE80211_HT_MPDU_DENSITY_4 = 5, /* 4 usec */ + IEEE80211_HT_MPDU_DENSITY_8 = 6, /* 8 usec */ + IEEE80211_HT_MPDU_DENSITY_16 = 7 /* 16 usec */ +}; + +#define IEEE80211_HT_MPDU_DENSITY_SHIFT 2 +/** + * struct ieee80211_ht_info - HT information + * + * This structure is the "HT information element" as + * described in 802.11n D5.0 7.3.2.58 + */ +/* ELEMENTID = 61, HT Operation element in 11n7.3.5.57 +*/ +struct ieee80211_ht_info { + u8 control_chan; + u8 ht_param; + u16 operation_mode; + u16 stbc_param; + u8 basic_set[16]; +}__attribute__((packed)); + +/* for ht_param */ +#define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03 +#define IEEE80211_HT_PARAM_CHA_SEC_NONE 0x00 +#define IEEE80211_HT_PARAM_CHA_SEC_ABOVE 0x01 +#define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03 +#define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04 +#define IEEE80211_HT_PARAM_RIFS_MODE 0x08 +#define IEEE80211_HT_PARAM_SPSMP_SUPPORT 0x10 +#define IEEE80211_HT_PARAM_SERV_INTERVAL_GRAN 0xE0 + +/* for operation_mode */ +#define IEEE80211_HT_OP_MODE_PROTECTION 0x0003 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONE 0 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER 1 +#define IEEE80211_HT_OP_MODE_PROTECTION_20MHZ 2 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3 +#define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004 +#define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010 + +/* for stbc_param */ +#define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040 +#define IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT 0x0080 +#define IEEE80211_HT_STBC_PARAM_STBC_BEACON 0x0100 +#define IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT 0x0200 +#define IEEE80211_HT_STBC_PARAM_PCO_ACTIVE 0x0400 +#define IEEE80211_HT_STBC_PARAM_PCO_PHASE 0x0800 + + +/* block-ack parameters */ +#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002 +#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C +#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0 +#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000 +#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800 + +/* + * A-PMDU buffer sizes + * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) + */ +#define IEEE80211_MIN_AMPDU_BUF 0x8 +#define IEEE80211_MAX_AMPDU_BUF 0x40 + + +/* Spatial Multiplexing Power Save Modes (for capability) */ +#define WLAN_HT_CAP_SM_PS_STATIC 0 +#define WLAN_HT_CAP_SM_PS_DYNAMIC 1 +#define WLAN_HT_CAP_SM_PS_INVALID 2 +#define WLAN_HT_CAP_SM_PS_DISABLED 3 + +/* for SM power control field lower two bits */ +#define WLAN_HT_SMPS_CONTROL_DISABLED 0 +#define WLAN_HT_SMPS_CONTROL_STATIC 1 +#define WLAN_HT_SMPS_CONTROL_DYNAMIC 3 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FT 2 +#define WLAN_AUTH_LEAP 128 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) +#define WLAN_CAPABILITY_CF_POLLABLE (1<<2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3) +#define WLAN_CAPABILITY_PRIVACY (1<<4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) +#define WLAN_CAPABILITY_PBCC (1<<6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) + +/* 802.11h */ +#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) +#define WLAN_CAPABILITY_QOS (1<<9) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) +#define WLAN_CAPABILITY_DSSS_OFDM (1<<13) +/* measurement */ +#define IEEE80211_SPCT_MSR_RPRT_MODE_LATE (1<<0) +#define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE (1<<1) +#define IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED (1<<2) + +#define IEEE80211_SPCT_MSR_RPRT_TYPE_BASIC 0 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA 1 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI 2 + + +/* 802.11g ERP information element */ +#define WLAN_ERP_NON_ERP_PRESENT (1<<0) +#define WLAN_ERP_USE_PROTECTION (1<<1) +#define WLAN_ERP_BARKER_PREAMBLE (1<<2) + +/* WLAN_ERP_BARKER_PREAMBLE values */ +enum { + WLAN_ERP_PREAMBLE_SHORT = 0, + WLAN_ERP_PREAMBLE_LONG = 1, +}; + +/* Status codes */ +enum ieee80211_statuscode { + WLAN_STATUS_SUCCESS = 0, + WLAN_STATUS_UNSPECIFIED_FAILURE = 1, + WLAN_STATUS_CAPS_UNSUPPORTED = 10, + WLAN_STATUS_REASSOC_NO_ASSOC = 11, + WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12, + WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13, + WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14, + WLAN_STATUS_CHALLENGE_FAIL = 15, + WLAN_STATUS_AUTH_TIMEOUT = 16, + WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17, + WLAN_STATUS_ASSOC_DENIED_RATES = 18, + /* 802.11b */ + WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19, + WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20, + WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21, + /* 802.11h */ + WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22, + WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23, + WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24, + /* 802.11g */ + WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25, + WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26, + /* 802.11w */ + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY = 30, + WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION = 31, + /* 802.11i */ + WLAN_STATUS_INVALID_IE = 40, + WLAN_STATUS_INVALID_GROUP_CIPHER = 41, + WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42, + WLAN_STATUS_INVALID_AKMP = 43, + WLAN_STATUS_UNSUPP_RSN_VERSION = 44, + WLAN_STATUS_INVALID_RSN_IE_CAP = 45, + WLAN_STATUS_CIPHER_SUITE_REJECTED = 46, + /* 802.11e */ + WLAN_STATUS_UNSPECIFIED_QOS = 32, + WLAN_STATUS_ASSOC_DENIED_NOBANDWIDTH = 33, + WLAN_STATUS_ASSOC_DENIED_LOWACK = 34, + WLAN_STATUS_ASSOC_DENIED_UNSUPP_QOS = 35, + WLAN_STATUS_REQUEST_DECLINED = 37, + WLAN_STATUS_INVALID_QOS_PARAM = 38, + WLAN_STATUS_CHANGE_TSPEC = 39, + WLAN_STATUS_WAIT_TS_DELAY = 47, + WLAN_STATUS_NO_DIRECT_LINK = 48, + WLAN_STATUS_STA_NOT_PRESENT = 49, + WLAN_STATUS_STA_NOT_QSTA = 50, + WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE = 51, +}; + + +/* Reason codes */ +enum ieee80211_reasoncode { + WLAN_REASON_UNSPECIFIED = 1, + WLAN_REASON_PREV_AUTH_NOT_VALID = 2, + WLAN_REASON_DEAUTH_LEAVING = 3, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4, + WLAN_REASON_DISASSOC_AP_BUSY = 5, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7, + WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8, + WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9, + /* 802.11h */ + WLAN_REASON_DISASSOC_BAD_POWER = 10, + WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11, + /* 802.11i */ + WLAN_REASON_INVALID_IE = 13, + WLAN_REASON_MIC_FAILURE = 14, + WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, + WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16, + WLAN_REASON_IE_DIFFERENT = 17, + WLAN_REASON_INVALID_GROUP_CIPHER = 18, + WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19, + WLAN_REASON_INVALID_AKMP = 20, + WLAN_REASON_UNSUPP_RSN_VERSION = 21, + WLAN_REASON_INVALID_RSN_IE_CAP = 22, + WLAN_REASON_IEEE8021X_FAILED = 23, + WLAN_REASON_CIPHER_SUITE_REJECTED = 24, + /* 802.11e */ + WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32, + WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33, + WLAN_REASON_DISASSOC_LOW_ACK = 34, + WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP = 35, + WLAN_REASON_QSTA_LEAVE_QBSS = 36, + WLAN_REASON_QSTA_NOT_USE = 37, + WLAN_REASON_QSTA_REQUIRE_SETUP = 38, + WLAN_REASON_QSTA_TIMEOUT = 39, + WLAN_REASON_QSTA_CIPHER_NOT_SUPP = 45, +}; + + +/* Information Element IDs */ +enum ieee80211_eid { + WLAN_EID_SSID = 0, + WLAN_EID_SUPP_RATES = 1, + WLAN_EID_FH_PARAMS = 2, + WLAN_EID_DS_PARAMS = 3, + WLAN_EID_CF_PARAMS = 4, + WLAN_EID_TIM = 5, + WLAN_EID_IBSS_PARAMS = 6, + WLAN_EID_CHALLENGE = 16, + + WLAN_EID_COUNTRY = 7, + WLAN_EID_HP_PARAMS = 8, + WLAN_EID_HP_TABLE = 9, + WLAN_EID_REQUEST = 10, + + WLAN_EID_QBSS_LOAD = 11, + WLAN_EID_EDCA_PARAM_SET = 12, + WLAN_EID_TSPEC = 13, + WLAN_EID_TCLAS = 14, + WLAN_EID_SCHEDULE = 15, + WLAN_EID_TS_DELAY = 43, + WLAN_EID_TCLAS_PROCESSING = 44, + WLAN_EID_QOS_CAPA = 46, + /* 802.11s */ + WLAN_EID_MESH_CONFIG = 113, + WLAN_EID_MESH_ID = 114, + WLAN_EID_LINK_METRIC_REPORT = 115, + WLAN_EID_CONGESTION_NOTIFICATION = 116, + /* Note that the Peer Link IE has been replaced with the similar + * Peer Management IE. We will keep the former definition until mesh + * code is changed to comply with latest 802.11s drafts. + */ + WLAN_EID_PEER_LINK = 55, /* no longer in 802.11s drafts */ + WLAN_EID_PEER_MGMT = 117, + WLAN_EID_CHAN_SWITCH_PARAM = 118, + WLAN_EID_MESH_AWAKE_WINDOW = 119, + WLAN_EID_BEACON_TIMING = 120, + WLAN_EID_MCCAOP_SETUP_REQ = 121, + WLAN_EID_MCCAOP_SETUP_RESP = 122, + WLAN_EID_MCCAOP_ADVERT = 123, + WLAN_EID_MCCAOP_TEARDOWN = 124, + WLAN_EID_GANN = 125, + WLAN_EID_RANN = 126, + WLAN_EID_PREQ = 130, + WLAN_EID_PREP = 131, + WLAN_EID_PERR = 132, + WLAN_EID_PXU = 137, + WLAN_EID_PXUC = 138, + WLAN_EID_AUTH_MESH_PEER_EXCH = 139, + WLAN_EID_MIC = 140, + + WLAN_EID_PWR_CONSTRAINT = 32, + WLAN_EID_PWR_CAPABILITY = 33, + WLAN_EID_TPC_REQUEST = 34, + WLAN_EID_TPC_REPORT = 35, + WLAN_EID_SUPPORTED_CHANNELS = 36, + WLAN_EID_CHANNEL_SWITCH = 37, + WLAN_EID_MEASURE_REQUEST = 38, + WLAN_EID_MEASURE_REPORT = 39, + WLAN_EID_QUIET = 40, + WLAN_EID_IBSS_DFS = 41, + + WLAN_EID_ERP_INFO = 42, + WLAN_EID_EXT_SUPP_RATES = 50, + + WLAN_EID_HT_CAPABILITY = 45, + WLAN_EID_HT_INFORMATION = 61, + + WLAN_EID_RSN = 48, + WLAN_EID_MMIE = 76, + WLAN_EID_WPA = 221, + WLAN_EID_GENERIC = 221, + WLAN_EID_VENDOR_SPECIFIC = 221, + WLAN_EID_QOS_PARAMETER = 222, + + WLAN_EID_AP_CHAN_REPORT = 51, + WLAN_EID_NEIGHBOR_REPORT = 52, + WLAN_EID_RCPI = 53, + WLAN_EID_BSS_AVG_ACCESS_DELAY = 63, + WLAN_EID_ANTENNA_INFO = 64, + WLAN_EID_RSNI = 65, + WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66, + WLAN_EID_BSS_AVAILABLE_CAPACITY = 67, + WLAN_EID_BSS_AC_ACCESS_DELAY = 68, + WLAN_EID_RRM_ENABLED_CAPABILITIES = 70, + WLAN_EID_MULTIPLE_BSSID = 71, + WLAN_EID_BSS_COEX_2040 = 72, + WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74, + WLAN_EID_EXT_CAPABILITY = 127, + + WLAN_EID_MOBILITY_DOMAIN = 54, + WLAN_EID_FAST_BSS_TRANSITION = 55, + WLAN_EID_TIMEOUT_INTERVAL = 56, + WLAN_EID_RIC_DATA = 57, + WLAN_EID_RIC_DESCRIPTOR = 75, + + WLAN_EID_DSE_REGISTERED_LOCATION = 58, + WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59, + WLAN_EID_EXT_CHANSWITCH_ANN = 60, + + WLAN_EID_TIME_ZONE = 98, + WLAN_EID_LINK_ID = 101, + WLAN_EID_INTERWORKING = 107, + WLAN_EID_ADV_PROTO = 108, + WLAN_EID_ROAMING_CONSORTIUM = 111, + WLAN_EID_EXT_CAPAB = 127, + +}; + +/* Action category code */ +enum ieee80211_category { + WLAN_CATEGORY_SPECTRUM_MGMT = 0, + WLAN_CATEGORY_QOS = 1, + WLAN_CATEGORY_DLS = 2, + WLAN_CATEGORY_BACK = 3, + WLAN_CATEGORY_PUBLIC = 4, + WLAN_CATEGORY_HT = 7, + WLAN_CATEGORY_SA_QUERY = 8, + WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, + WLAN_CATEGORY_WMM = 17, + WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, + WLAN_CATEGORY_VENDOR_SPECIFIC = 127, +}; + +/* SPECTRUM_MGMT action code */ +enum ieee80211_spectrum_mgmt_actioncode { + WLAN_ACTION_SPCT_MSR_REQ = 0, + WLAN_ACTION_SPCT_MSR_RPRT = 1, + WLAN_ACTION_SPCT_TPC_REQ = 2, + WLAN_ACTION_SPCT_TPC_RPRT = 3, + WLAN_ACTION_SPCT_CHL_SWITCH = 4, +}; + +/* HT action codes */ +enum ieee80211_ht_actioncode { + WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0, + WLAN_HT_ACTION_SMPS = 1, + WLAN_HT_ACTION_PSMP = 2, + WLAN_HT_ACTION_PCO_PHASE = 3, + WLAN_HT_ACTION_CSI = 4, + WLAN_HT_ACTION_NONCOMPRESSED_BF = 5, + WLAN_HT_ACTION_COMPRESSED_BF = 6, + WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7, +}; + +/* Security key length */ +enum ieee80211_key_len { + WLAN_KEY_LEN_WEP40 = 5, + WLAN_KEY_LEN_WEP104 = 13, + WLAN_KEY_LEN_CCMP = 16, + WLAN_KEY_LEN_TKIP = 32, + WLAN_KEY_LEN_AES_CMAC = 16, +}; + +/* + * IEEE 802.11-2007 7.3.2.9 Country information element + * + * Minimum length is 8 octets, ie len must be evenly + * divisible by 2 + */ + +/* Although the spec says 8 I'm seeing 6 in practice */ +#define IEEE80211_COUNTRY_IE_MIN_LEN 6 + +/* + * For regulatory extension stuff see IEEE 802.11-2007 + * Annex I (page 1141) and Annex J (page 1147). Also + * review 7.3.2.9. + * + * When dot11RegulatoryClassesRequired is TRUE and the + * first_channel/reg_extension_id is >= 201 then the IE + * compromises of the 'ext' struct represented below: + * + * - Regulatory extension ID - when generating IE this just needs + * to be monotonically increasing for each triplet passed in + * the IE + * - Regulatory class - index into set of rules + * - Coverage class - index into air propagation time (Table 7-27), + * in microseconds, you can compute the air propagation time from + * the index by multiplying by 3, so index 10 yields a propagation + * of 10 us. Valid values are 0-31, values 32-255 are not defined + * yet. A value of 0 inicates air propagation of <= 1 us. + * + * See also Table I.2 for Emission limit sets and table + * I.3 for Behavior limit sets. Table J.1 indicates how to map + * a reg_class to an emission limit set and behavior limit set. + */ +#define IEEE80211_COUNTRY_EXTENSION_ID 201 + +/* + * Channels numbers in the IE must be monotonically increasing + * if dot11RegulatoryClassesRequired is not TRUE. + * + * If dot11RegulatoryClassesRequired is TRUE consecutive + * subband triplets following a regulatory triplet shall + * have monotonically increasing first_channel number fields. + * + * Channel numbers shall not overlap. + * + * Note that max_power is signed. + */ +struct ieee80211_country_ie_triplet { + union { + struct { + u8 first_channel; + u8 num_channels; + s8 max_power; + }__attribute__((packed))chans; + struct { + u8 reg_extension_id; + u8 reg_class; + u8 coverage_class; + }__attribute__((packed))ext; + }__attribute__((packed))u; +}__attribute__((packed)); + +enum ieee80211_timeout_interval_type { + WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */, + WLAN_TIMEOUT_KEY_LIFETIME = 2 /* 802.11r */, + WLAN_TIMEOUT_ASSOC_COMEBACK = 3 /* 802.11w */, +}; + +/* BACK action code */ +enum ieee80211_back_actioncode { + WLAN_ACTION_ADDBA_REQ = 0, + WLAN_ACTION_ADDBA_RESP = 1, + WLAN_ACTION_DELBA = 2, +}; + +/* BACK (block-ack) parties */ +enum ieee80211_back_parties { + WLAN_BACK_RECIPIENT = 0, + WLAN_BACK_INITIATOR = 1, + WLAN_BACK_TIMER = 2, +}; + +/* SA Query action */ +enum ieee80211_sa_query_action { + WLAN_ACTION_SA_QUERY_REQUEST = 0, + WLAN_ACTION_SA_QUERY_RESPONSE = 1, +}; + + +/* A-MSDU 802.11n */ +#define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080 + +/* cipher suite selectors */ +#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 +#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 +#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 +/* reserved: 0x000FAC03 */ +#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 +#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 + +/* AKM suite selectors */ +#define WLAN_AKM_SUITE_8021X 0x000FAC01 +#define WLAN_AKM_SUITE_PSK 0x000FAC02 + +#define WLAN_MAX_KEY_LEN 32 + +#define WLAN_PMKID_LEN 16 + +/** + * ieee80211_get_qos_ctl - get pointer to qos control bytes + * @hdr: the frame + * + * The qos ctrl bytes come after the frame_control, duration, seq_num + * and 3 or 4 addresses of length ETH_ALEN. + * 3 addr: 2 + 2 + 2 + 3*6 = 24 + * 4 addr: 2 + 2 + 2 + 4*6 = 30 + */ +static __inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return (u8 *)hdr + 30; + else + return (u8 *)hdr + 24; +} + +/** + * ieee80211_get_SA - get pointer to SA + * @hdr: the frame + * + * Given an 802.11 frame, this function returns the offset + * to the source address (SA). It does not verify that the + * header is long enough to contain the address, and the + * header must be long enough to contain the frame control + * field. + */ +static __inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return hdr->addr4; + if (ieee80211_has_fromds(hdr->frame_control)) + return hdr->addr3; + return hdr->addr2; +} + +/** + * ieee80211_get_DA - get pointer to DA + * @hdr: the frame + * + * Given an 802.11 frame, this function returns the offset + * to the destination address (DA). It does not verify that + * the header is long enough to contain the address, and the + * header must be long enough to contain the frame control + * field. + */ +static __inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_tods(hdr->frame_control)) + return hdr->addr3; + else + return hdr->addr1; +} + +/** + * ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame + * @hdr: the frame (buffer must include at least the first octet of payload) + */ +static __inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_disassoc(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control)) + return TRUE; + + if (ieee80211_is_action(hdr->frame_control)) { + u8 *category; + + /* + * Action frames, excluding Public Action frames, are Robust + * Management Frames. However, if we are looking at a Protected + * frame, skip the check since the data may be encrypted and + * the frame has already been found to be a Robust Management + * Frame (by the other end). + */ + if (ieee80211_has_protected(hdr->frame_control)) + return TRUE; + category = ((u8 *) hdr) + 24; + return *category != WLAN_CATEGORY_PUBLIC && + *category != WLAN_CATEGORY_HT && + *category != WLAN_CATEGORY_VENDOR_SPECIFIC; + } + + return FALSE; +} + +/** + * ieee80211_fhss_chan_to_freq - get channel frequency + * @channel: the FHSS channel + * + * Convert IEEE802.11 FHSS channel to frequency (MHz) + * Ref IEEE 802.11-2007 section 14.6 + */ +static __inline int ieee80211_fhss_chan_to_freq(int channel) +{ + if ((channel > 1) && (channel < 96)) + return channel + 2400; + else + return -1; +} + +/** + * ieee80211_freq_to_fhss_chan - get channel + * @freq: the channels frequency + * + * Convert frequency (MHz) to IEEE802.11 FHSS channel + * Ref IEEE 802.11-2007 section 14.6 + */ +static __inline int ieee80211_freq_to_fhss_chan(int freq) +{ + if ((freq > 2401) && (freq < 2496)) + return freq - 2400; + else + return -1; +} + +/** + * ieee80211_dsss_chan_to_freq - get channel center frequency + * @channel: the DSSS channel + * + * Convert IEEE802.11 DSSS channel to the center frequency (MHz). + * Ref IEEE 802.11-2007 section 15.6 + */ +static __inline int ieee80211_dsss_chan_to_freq(int channel) +{ + if ((channel > 0) && (channel < 14)) + return 2407 + (channel * 5); + else if (channel == 14) + return 2484; + else + return -1; +} + +/** + * ieee80211_freq_to_dsss_chan - get channel + * @freq: the frequency + * + * Convert frequency (MHz) to IEEE802.11 DSSS channel + * Ref IEEE 802.11-2007 section 15.6 + * + * This routine selects the channel with the closest center frequency. + */ +static __inline int ieee80211_freq_to_dsss_chan(int freq) +{ + if ((freq >= 2410) && (freq < 2475)) + return (freq - 2405) / 5; + else if ((freq >= 2482) && (freq < 2487)) + return 14; + else + return -1; +} + +/* Convert IEEE802.11 HR DSSS channel to frequency (MHz) and back + * Ref IEEE 802.11-2007 section 18.4.6.2 + * + * The channels and frequencies are the same as those defined for DSSS + */ +#define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan) +#define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq) + +/* Convert IEEE802.11 ERP channel to frequency (MHz) and back + * Ref IEEE 802.11-2007 section 19.4.2 + */ +#define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan) +#define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq) + +/** + * ieee80211_ofdm_chan_to_freq - get channel center frequency + * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz + * @channel: the OFDM channel + * + * Convert IEEE802.11 OFDM channel to center frequency (MHz) + * Ref IEEE 802.11-2007 section 17.3.8.3.2 + */ +static __inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel) +{ + if ((channel > 0) && (channel <= 200) && + (s_freq >= 4000)) + return s_freq + (channel * 5); + else + return -1; +} + +/** + * ieee80211_freq_to_ofdm_channel - get channel + * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz + * @freq: the frequency + * + * Convert frequency (MHz) to IEEE802.11 OFDM channel + * Ref IEEE 802.11-2007 section 17.3.8.3.2 + * + * This routine selects the channel with the closest center frequency. + */ +static __inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq) +{ + if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) && + (s_freq >= 4000)) + return (freq + 2 - s_freq) / 5; + else + return -1; +} + +/** + * ieee80211_tu_to_usec - convert time units (TU) to microseconds + * @tu: the TUs + */ +static __inline unsigned long ieee80211_tu_to_usec(unsigned long tu) +{ + return 1024 * tu; +} + +/** + * ieee80211_check_tim - check if AID bit is set in TIM + * @tim: the TIM IE + * @tim_len: length of the TIM IE + * @aid: the AID to look for + */ +static __inline bool ieee80211_check_tim(struct ieee80211_tim_ie *tim, + u8 tim_len, u16 aid) +{ + u8 mask; + u8 index, indexn1, indexn2; + + if (!tim || tim_len < sizeof(*tim)) + return FALSE; + + aid &= 0x3fff; + index = aid / 8; + mask = 1 << (aid & 7); + + indexn1 = tim->bitmap_ctrl & 0xfe; + indexn2 = tim_len + indexn1 - 4; + + if (index < indexn1 || index > indexn2) + return FALSE; + + index -= indexn1; + + return !!(tim->virtual_map[index] & mask); +} + +#endif /* end of TLS_IEEE80211_H */ diff --git a/sdk/Include/WiFi/wm_wifi.h b/sdk/Include/WiFi/wm_wifi.h new file mode 100644 index 0000000..980e481 --- /dev/null +++ b/sdk/Include/WiFi/wm_wifi.h @@ -0,0 +1,1045 @@ +/** + * @file wm_wifi.h + * + * @brief Wi-Fi API + * + * @author WinnerMicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef TLS_WIFI_FUNC_H +#define TLS_WIFI_FUNC_H + +/* Return Error definition*/ +/** invalid SSID */ +#define WM_WIFI_ERR_SSID -1 +/** invalid key */ +#define WM_WIFI_ERR_KEY -2 +/** wps is busing */ +#define WM_WIFI_WPS_BUSY -3 +/** scan is busing */ +#define WM_WIFI_SCANNING_BUSY -4 +/** station is connecting */ +#define WM_WIFI_STA_BUSY -5 + +/* error number definition */ +/** no error */ +#define WM_WIFI_ENOERR 0 +/** ap not exist */ +#define WM_WIFI_ENOAP 1 +/** pre-shared key may be incorrect */ +#define WM_WIFI_EKEY 2 +/** Low signal intensity */ +#define WM_WIFI_ELSI 3 + + +#ifndef ETH_ALEN +/** mac address length */ +#define ETH_ALEN 6 +#endif + +/* bss type definition*/ +#ifndef IEEE80211_MODE_INFRA +/** station mode */ +#define IEEE80211_MODE_INFRA 1 +/** ibss mode */ +#define IEEE80211_MODE_IBSS 2 +/** softap mode */ +#define IEEE80211_MODE_AP 4 +#endif + +/** authenticate mode : open */ +#define IEEE80211_ENCRYT_NONE 0 +/** authenticate mode : WEP40 */ +#define IEEE80211_ENCRYT_WEP40 1 +/** authenticate mode : WEP104 */ +#define IEEE80211_ENCRYT_WEP104 2 +/** authenticate mode : WPA_PSK_TKIP */ +#define IEEE80211_ENCRYT_TKIP_WPA 3 +/** authenticate mode : WPA_PSK_CCMP */ +#define IEEE80211_ENCRYT_CCMP_WPA 4 +/** authenticate mode : WPA2_PSK_TKIP */ +#define IEEE80211_ENCRYT_TKIP_WPA2 5 +/** authenticate mode : WPA2_PSK_CCMP */ +#define IEEE80211_ENCRYT_CCMP_WPA2 6 +/** authenticate mode : WPA_PSK_TKIP&AES */ +#define IEEE80211_ENCRYT_AUTO_WPA 7 +/** authenticate mode : WPA2_PSK_TKIP&AES */ +#define IEEE80211_ENCRYT_AUTO_WPA2 8 + +#ifdef TLS_CONFIG_WPS +/** length of WPS pin code */ +#define WPS_PIN_LEN 8 +#endif + +/** set auto connecting flag */ +#define WIFI_AUTO_CNT_FLAG_SET 1 +/** get auto connecting flag */ +#define WIFI_AUTO_CNT_FLAG_GET 0 + +/** disable Wi-Fi auto connecting */ +#define WIFI_AUTO_CNT_OFF 0x0 +/** enable Wi-Fi auto connecting */ +#define WIFI_AUTO_CNT_ON 0x1 +/** disable Wi-Fi auto connecting temporary */ +#define WIFI_AUTO_CNT_TMP_OFF 0x3 + +/** Wi-Fi join net successfully */ +#define WIFI_JOIN_SUCCESS 0x1 +/** Wi-Fi join net failed */ +#define WIFI_JOIN_FAILED 0x2 +/** Wi-Fi disconnected */ +#define WIFI_DISCONNECTED 0x3 +/** Wi-Fi create ap successfully */ +#define WIFI_SOFTAP_SUCCESS 0x4 +/** Wi-Fi create softap failure */ +#define WIFI_SOFTAP_FAILED 0x5 +/** Wi-Fi close softap */ +#define WIFI_SOFTAP_CLOSED 0x6 + + +enum tls_wifi_auth_mode { + WM_WIFI_AUTH_MODE_OPEN = 0, /**< authenticate mode : open */ + WM_WIFI_AUTH_MODE_WEP_AUTO = 3, /**< authenticate mode : wep (open or/and shared...) */ + WM_WIFI_AUTH_MODE_WPA_PSK_TKIP = 4, /**< authenticate mode : wpa psk rc4 */ + WM_WIFI_AUTH_MODE_WPA_PSK_CCMP = 8, /**< authenticate mode : wpa psk aes */ + WM_WIFI_AUTH_MODE_WPA_PSK_AUTO = (WM_WIFI_AUTH_MODE_WPA_PSK_TKIP | WM_WIFI_AUTH_MODE_WPA_PSK_CCMP), /**< authenticate mode : wpa psk, tkip and aes */ + WM_WIFI_AUTH_MODE_WPA2_PSK_TKIP = 16, /**< authenticate mode : wpa2 psk rc4 */ + WM_WIFI_AUTH_MODE_WPA2_PSK_CCMP = 32, /**< authenticate mode : wpa2 psk aes */ + WM_WIFI_AUTH_MODE_WPA2_PSK_AUTO = (WM_WIFI_AUTH_MODE_WPA2_PSK_TKIP | WM_WIFI_AUTH_MODE_WPA2_PSK_CCMP), /**< authenticate mode : wpa2 psk, tkip and aes */ + WM_WIFI_AUTH_MODE_WPA_WPA2_PSK_TKIP = (WM_WIFI_AUTH_MODE_WPA_PSK_TKIP | WM_WIFI_AUTH_MODE_WPA2_PSK_TKIP), + WM_WIFI_AUTH_MODE_WPA_WPA2_PSK_CCMP = (WM_WIFI_AUTH_MODE_WPA_PSK_CCMP | WM_WIFI_AUTH_MODE_WPA2_PSK_CCMP), + WM_WIFI_AUTH_MODE_WPA_WPA2_PSK_AUTO = (WM_WIFI_AUTH_MODE_WPA_PSK_AUTO | WM_WIFI_AUTH_MODE_WPA2_PSK_AUTO), /**< authenticate mode : wpa and wpa2, tkip and aes */ + WM_WIFI_AUTH_MODE_UNKNOWN = 128 +}; + +/** Wi-Fi states */ +enum tls_wifi_states { + WM_WIFI_DISCONNECTED, /**< Disconnected state */ + WM_WIFI_SCANNING, /**< Scanning for a network */ + WM_WIFI_JOINING, /**< Trying to join with a BSS/SSID */ + WM_WIFI_JOINED /**< All authentication completed */ +}; + +/** frame type of the manager */ +enum tls_wifi_mgmt_type { + WM_WIFI_MGMT_TYPE_ASSOC_REQ = 0x0000, /**< association request frame */ + WM_WIFI_MGMT_TYPE_ASSOC_RESP = 0x0010, /**< association response frame */ + WM_WIFI_MGMT_TYPE_REASSOC_REQ = 0x0020, /**< reassociation request frame */ + WM_WIFI_MGMT_TYPE_REASSOC_RESP = 0x0030, /**< reassociation response frame */ + WM_WIFI_MGMT_TYPE_PROBE_REQ = 0x0040, /**< probe request frame */ + WM_WIFI_MGMT_TYPE_PROBE_RESP = 0x0050, /**< probe response frame */ + WM_WIFI_MGMT_TYPE_BEACON = 0x0080, /**< beacon frame */ + WM_WIFI_MGMT_TYPE_ATIM = 0x0090, /**< ATIM frame */ + WM_WIFI_MGMT_TYPE_DISASSOC = 0x00A0, /**< disassociation frame */ + WM_WIFI_MGMT_TYPE_AUTH = 0x00B0, /**< authentication frame */ + WM_WIFI_MGMT_TYPE_DEAUTH = 0x00C0, /**< deauthentication frame */ + WM_WIFI_MGMT_TYPE_ACTION = 0x00D0 /**< action frame */ +}; + +/** Wi-Fi ransport rate */ +enum tls_wifi_tx_rate { + WM_WIFI_TX_RATEIDX_1M = 0, /**< 11b, 1M */ + WM_WIFI_TX_RATEIDX_2M, /**< 11b, 2M */ + WM_WIFI_TX_RATEIDX_5_5M, /**< 11b, 5.5M */ + WM_WIFI_TX_RATEIDX_11M, /**< 11b, 11M */ + WM_WIFI_TX_RATEIDX_6M, /**< 11g, 6M */ + WM_WIFI_TX_RATEIDX_9M, /**< 11g, 9M */ + WM_WIFI_TX_RATEIDX_12M, /**< 11g, 12M */ + WM_WIFI_TX_RATEIDX_18M, /**< 11g, 18M */ + WM_WIFI_TX_RATEIDX_24M, /**< 11g, 24M */ + WM_WIFI_TX_RATEIDX_36M, /**< 11g, 36M */ + WM_WIFI_TX_RATEIDX_48M, /**< 11g, 48M */ + WM_WIFI_TX_RATEIDX_54M, /**< 11g, 54M */ + WM_WIFI_TX_RATEIDX_MCS0, /**< 11n-20MHz, mcs0 */ + WM_WIFI_TX_RATEIDX_MCS1, /**< 11n-20MHz, mcs1 */ + WM_WIFI_TX_RATEIDX_MCS2, /**< 11n-20MHz, mcs2 */ + WM_WIFI_TX_RATEIDX_MCS3, /**< 11n-20MHz, mcs3 */ + WM_WIFI_TX_RATEIDX_MCS4, /**< 11n-20MHz, mcs4 */ + WM_WIFI_TX_RATEIDX_MCS5, /**< 11n-20MHz, mcs5 */ + WM_WIFI_TX_RATEIDX_MCS6, /**< 11n-20MHz, mcs6 */ + WM_WIFI_TX_RATEIDX_MCS7, /**< 11n-20MHz, mcs7 */ + WM_WIFI_TX_RATEIDX_MCS8, /**< 11n-40MHz, mcs8 */ + WM_WIFI_TX_RATEIDX_MCS9, /**< 11n-40MHz, mcs9 */ + WM_WIFI_TX_RATEIDX_MCS10, /**< 11n-40MHz, mcs10 */ + WM_WIFI_TX_RATEIDX_MCS11, /**< 11n-40MHz, mcs11 */ + WM_WIFI_TX_RATEIDX_MCS12, /**< 11n-40MHz, mcs12 */ + WM_WIFI_TX_RATEIDX_MCS13, /**< 11n-40MHz, mcs13 */ + WM_WIFI_TX_RATEIDX_MCS14, /**< 11n-40MHz, mcs14 */ + WM_WIFI_TX_RATEIDX_MCS15, /**< 11n-40MHz, mcs15 */ + WM_WIFI_TX_RATEIDX_MCS32 /**< invalid rate */ +}; + +enum tls_wifi_oneshot_result_type { + WM_WIFI_ONESHOT_TYPE_SSIDPWD, + WM_WIFI_ONESHOT_TYPE_CUSTOMDATA +}; + +enum tls_wifi_client_event_type { + WM_WIFI_CLIENT_EVENT_ONLINE, + WM_WIFI_CLIENT_EVENT_OFFLINE +}; + +enum tls_wifi_op_mode{ + STATION_MODE = 1, + SOFTAP_MODE, + STATIONAP_MODE +}; + +/** current bss information */ +struct tls_curr_bss_t{ + u8 bssid[ETH_ALEN]; /**< BSSID of connected AP */ + u8 ssid[32]; /**< SSID of connected AP */ + u8 ssid_len; /**< SSID length of connected AP */ + u8 channel; /**< channel of connected AP */ + u8 type; /**< BSS's type of connected AP, value is: + IEEE80211_MODE_INFRA, IEEE80211_MODE_IBSS, + IEEE80211_MODE_AP, IEEE80211_MODE_APSTA */ + u8 encryptype; /**< BSS's encryption type of connected AP, value is: IEEE80211_ENCRYT_NONE, + IEEE80211_ENCRYT_WEP40, IEEE80211_ENCRYT_WEP104, + IEEE80211_ENCRYT_TKIP_WPA, IEEE80211_ENCRYT_CCMP_WPA, + IEEE80211_ENCRYT_TKIP_WPA2, IEEE80211_ENCRYT_CCMP_WPA2, + IEEE80211_ENCRYT_AUTO_WPA, IEEE80211_ENCRYT_AUTO_WPA2 */ + u8 rssi; /**< single strength of AP */ +}; + +/** secret key information */ +struct tls_key_info_t{ + u8 format; /**< key format, value is: 0-hex, 1-ascii */ + u8 index; /**< key index, value is: 1-4 (only wep) */ + u8 key_len; /**< key length */ + u8 key[64]; /**< key content */ +}; + +/** Wi-Fi configuration of softap */ +struct tls_softap_info_t{ + u8 ssid[33]; /**< SSID of softap */ + u8 encrypt; /**< encryption mode of softap, value is: IEEE80211_ENCRYT_NONE, + IEEE80211_ENCRYT_WEP40, IEEE80211_ENCRYT_WEP104, + IEEE80211_ENCRYT_TKIP_WPA, IEEE80211_ENCRYT_CCMP_WPA, + IEEE80211_ENCRYT_TKIP_WPA2, IEEE80211_ENCRYT_CCMP_WPA2 */ + u8 channel; /**< channel of softap */ + struct tls_key_info_t keyinfo; /**< Password (key) of softap */ +}; + +/** ip address information */ +struct tls_ip_info_t{ + u8 ip_addr[4]; /**< IP address */ + u8 netmask[4]; /**< netmask */ + u8 dnsname[32]; /**< DNS server name */ +}; + +/** Wi-Fi configuration of ibss */ +struct tls_ibss_info_t{ + u8 ssid[33]; /**< SSID of ibss */ + u8 encrypt; /**< encryption mode of ibss, value is: IEEE80211_ENCRYT_NONE, + IEEE80211_ENCRYT_WEP40, IEEE80211_ENCRYT_WEP104, + IEEE80211_ENCRYT_TKIP_WPA, IEEE80211_ENCRYT_CCMP_WPA, + IEEE80211_ENCRYT_TKIP_WPA2, IEEE80211_ENCRYT_CCMP_WPA2, + IEEE80211_ENCRYT_AUTO_WPA, IEEE80211_ENCRYT_AUTO_WPA2 */ + u8 channel; /**< channel of ibss */ + struct tls_key_info_t keyinfo; /**< Password (key) of ibss */ +}; + +/** ip address information of ibss */ +struct tls_ibssip_info_t{ + u8 ip[4]; /**< IP address */ + u8 netmask[4]; /**< netmask */ + u8 gateway[4]; /**< gateway */ + u8 dns1[4]; /**< DNS1 IP address */ + u8 dns2[4]; /**< DNS2 IP address */ +}; + +/** bss information */ +struct tls_bss_info_t { + u8 bssid[ETH_ALEN]; /**< MAC address of AP */ + u8 mode; /**< AP type, value is: 1-ibss, 2-ess */ + u8 channel; /**< channel of AP */ + u8 privacy; /**< encryption type, @ref enum tls_wifi_auth_mode */ + u8 ssid_len; /**< SSID length */ + u8 rssi; /**< signal strength of AP, real rssi = (signed char)rssi */ + u8 ssid[32]; /**< SSID of AP */ + u32 max_data_rate; /**< maximum rate of AP, the unit is Mbps */ + bool wps_support; /**< is support WPS function */ +}; + +/** scan result */ +struct tls_scan_bss_t { + u32 count; /**< total count */ + u32 length; /**< bss info total length */ + struct tls_bss_info_t bss[1]; /**< list of bss found*/ +}; + +/** station information */ +struct tls_sta_info_t { + u8 mac_addr[ETH_ALEN]; /**< MAC address of station */ +}; + +/** 802.11 packet information from the physical layer */ +struct tls_wifi_ext_t { + u8 rssi; /**< signal strength */ +}; + +/** 802.11 mac address */ +struct tls_wifi_hdr_mac_t { + u8 da_addr[ETH_ALEN]; /**< MAC address of destination */ + u8 sa_addr[ETH_ALEN]; /**< MAC address of source */ + u8 bssid[ETH_ALEN]; /**< MAC address of AP */ +}; + +/** transport rate and gain */ +struct tls_wifi_tx_rate_t { + enum tls_wifi_tx_rate tx_rate; /**< Wi-Fi ransport rate */ + u8 tx_gain; /**< Wi-Fi ransport gain, + The caller can get the maximum gain + by using the tls_wifi_get_tx_gain_max function. */ + u8 retry_times; /**< Wi-Fi number of retransmissions, ranging from 1 to 15. */ +}; + +/** callback function of receive Wi-Fi data */ +typedef void (*tls_wifi_data_recv_callback)(u8* data, u32 data_len); + +/** callback function of receive ETHERNET data */ +typedef int (*net_rx_data_cb)(const u8 *bssid, u8 *buf, u32 buf_len); + +/** callback function of receive Wi-Fi data with some information of the physical layer */ +typedef void (*tls_wifi_data_ext_recv_callback)(u8* data, u32 data_len, struct tls_wifi_ext_t *ext); + +/** + * @defgroup Wi-Fi_APIs Wi-Fi APIs + * @brief Wi-Fi related APIs + */ + +/** + * @addtogroup Wi-Fi_APIs + * @{ + */ + +/** + * @defgroup WM_Wi-Fi_APIs WM_Wi-Fi APIs + * @brief WM_Wi-Fi APIs + */ + +/** + * @addtogroup WM_Wi-Fi_APIs + * @{ + */ + +/** + * @brief This function is used to enable/disable listen mode + * + * @param[in] enable:non-zero, disable: 0 + * + * @return None + * + * @note This function used when oneshot start. + */ +void tls_wifi_set_listen_mode(u8 enable); + +/** + * @brief This function is used to get listen mode + * + * @param None + * + * @retval 0 normal mode + * @retval non-zero listen mode + * + * @note None + */ +u8 tls_wifi_get_listen_mode(void); + +/** + * @brief This function is used to enable/disable special listen mode + * + * @param[in] enable:non-zero, disable: 0 + * + * @return None + * + * @note This function used when special oneshot start. + */ +void tls_wifi_set_special_mode(u8 enable); + +/** + * @brief This function is used to get special listen mode + * + * @param None + * + * @retval 0 normal mode + * @retval non-zero special listen mode + * + * @note None + */ +u8 tls_wifi_get_special_mode(void); + + +/** +* @brief This function is used to filter multicast frames +* @param mac: The multicast frame that we want receive or filter. +* +* @param receive: 1, receive this multicast frame +* 0, filter this multicast frame +* +* @note usage: For example: u8 mac[6]={01, 00, 5e, 7f, ff, fa},if receive +* is set to 0,the 802.11 multicast frames whose hdr->addr1 is +* [01 00 5e 7f ff fa] will be filtered. +*/ +u8 tls_filter_mcast_mac(u8 *mac, u8 receive); + +/** + * @brief This function is used to set mac filter. + * @param[in] mac: mac: to be filtered or received + * + * @param[in] receive: 1:not filter,zero:filter + * + * @param[in] clear: 1:clear all, 0:do not clear, only add new filter + * + * @return None + * + * @note usage: normally, it is used to oneshot config + */ +void tls_wifi_set_bcast_mac_filter(u8 *mac, u8 receive, u8 clear); + +/** + * @brief This function is used to restore mac filter to normal mode. + * + * @param[in] None + * + * @return None + * + * @note Normally, it is used to restore mac filter after oneshot config + */ +void tls_wifi_restore_bcast_mac_filter(void); + + +/** + * @brief This function is used to register recv wifi data callback function + * + * @param[in] callback point to receive Wi-Fi data function + * + * @return None + * + * @note None + */ +void tls_wifi_data_recv_cb_register(tls_wifi_data_recv_callback callback); + +/** + * @brief This function is used to register recv wifi extended + * data callback function + * + * @param[in] callback point to receive Wi-Fi extended data function + * + * @return None + * + * @note None + */ +void tls_wifi_data_ext_recv_cb_register(tls_wifi_data_ext_recv_callback callback); + +/** + * @brief This function is used to set oneshot config flag + * + * @param[in] flag 0: closed oneshot non-zero:open oneshot + * + * @return None + * + * @note None + */ +void tls_wifi_set_oneshot_flag(u8 flag); + +/** + * @brief This function is used to get one shot flag + * + * @param None + * + * @retval 0 oneshot closed + * @retval other oneshot open + * + * @note None + */ +int tls_wifi_get_oneshot_flag(void); + + +/********************************************************************************************************* +* Description: before calling tls_wifi_get_oneshot_ssidpwd or tls_wifi_get_oneshot_customdata, +* application should call this function to register the call back function; +* +* Arguments: callback function pointer: +* +* Return: None +* Note: callback function, suggest user send out a MSG and return immediately. +*********************************************************************************************************/ +typedef void (*tls_wifi_oneshot_result_callback)(enum tls_wifi_oneshot_result_type type); + +void tls_wifi_oneshot_result_cb_register(tls_wifi_oneshot_result_callback callback); + +/********************************************************************************************************** +* Description: This function is used to get one shot ssid and password information. +* +* Arguments : ssid, wifi name +* pwd, pwssword +* +* Returns : None +**********************************************************************************************************/ +void tls_wifi_get_oneshot_ssidpwd(u8 *ssid, u8 *pwd); + +/********************************************************************************************************** +* Description: This function is used to get one shot custom data +* +* Arguments : data, custom data +* +* Returns : None +**********************************************************************************************************/ +void tls_wifi_get_oneshot_customdata(u8 *data); + +/** + * @brief This function is used to change channel actively + * + * @param[in] chanid + * + * @return None + * + * @note Normally, it is just used in listen mode; + */ +void tls_wifi_change_chanel(u32 chanid); + +/** + * @brief This function is used to trigger scan AP + * + * @param None + * + * @retval WM_SUCCESS start scan + * @retval WM_WIFI_SCANNING_BUSY scanning + * @retval WM_FAILED failed + * + * @note If not SUCCESS, user needs to call this function again + * to trigger scan + */ +int tls_wifi_scan(void); + +/** + * @brief Before calling tls_wifi_scan() , application should call + * this function to register the call back function; + * + * @param[in] callback point to callback function + * + * @return None + * + * @note In callback function, user should send a message + * and return immediately. + * After callback called, scan result can be get by + * calling function tls_wifi_get_scan_rslt + */ +void tls_wifi_scan_result_cb_register(void (*callback)(void)); + +/** + * @brief This function is used to get scan result + * + * @param[out] buf buf to be used to store returned BSS info + * @param[in] buffer_size buf size + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note User need to alloc buffer in advance. + * One item of scan result is @ref struct tls_bss_info_t. + * Size for one item of scan result is 48Bytes; + * The buffer size depends how many items user wants. + * Compared with the previous scanning results, + * max_data_rate and wps_support fields were added, + * and the meaning of the privacy field was extended. + */ +int tls_wifi_get_scan_rslt(u8* buf, u32 buffer_size); + + +/** + * @brief This function is used to create soft ap + * + * @param[in] apinfo softap Wi-Fi configuration + * @param[in] ipinfo softap ip address + * + * @retval WM_WIFI_ERR_SSID SSID is NULL + * @retval WM_WIFI_ERR_KEY key info not correct + * @retval WM_SUCCESS soft ap create OK + * @retval WM_WIFI_STA_BUSY station is connecting + * + * @note None + */ +int tls_wifi_softap_create(struct tls_softap_info_t* apinfo, struct tls_ip_info_t* ipinfo); + + +/** + * @brief This function is used to destroy soft ap + * + * @param None + * + * @return None + * + * @note None + */ +void tls_wifi_softap_destroy(void); + +/** + * @brief This function is used to get soft ap's state + * + * @param None + * + * @return soft ap's state + * + * @note None + */ +enum tls_wifi_states tls_wifi_softap_get_state(void); + +typedef void (*tls_wifi_client_event_callback)(u8 *mac, enum tls_wifi_client_event_type event); + +/** + * @brief This function is used to register client event + * + * @param[in] client event callback + * + * @return None + * + * @note None + */ +void tls_wifi_softap_client_event_register(tls_wifi_client_event_callback callback); + +/** + * @brief This function is used to get the authed sta list + * + * @param[out] sta_num the authed's station number + * + * @param[out] buf address to store returned station list info(struct tls_sta_info_t[]) + * + * @param[in] buf_size + * + * @return None + * + * @note None + */ +void tls_wifi_get_authed_sta_info(u32 *sta_num, u8 *buf, u32 buf_size); + +/** + * @brief This function is used to create or join ibss + * + * @param[in] *ibssinfo ibss Wi-Fi configuration + * @param[in] *ipinfo ibss ip address + * + * @retval WM_SUCCESS IBSS join or create ok + * @retval WM_WIFI_ERR_SSID SSID is NULL + * @retval WM_WIFI_ERR_KEY key info not correct + * + * @note Join IBSS if existed + * Create IBSS by SSID when not existed. + */ +int tls_wifi_ibss_create(struct tls_ibss_info_t *ibssinfo, struct tls_ibssip_info_t *ipinfo); + +/** + * @brief This function is used to destroy or leave Wi-Fi network + * + * @param None + * + * @return None + * + * @note For AP, destroy soft AP + * For STA, leave the network by AP + * For IBSS, destroy or leave the IBSS network. + */ +void tls_wifi_disconnect(void); + +/** + * @brief This function is used to connect AP + * + * @param[in] *ssid Network Name to connect + * @param[in] ssid_len length of SSID + * @param[in] *pwd password to connect AP + * @param[in] pwd_len length of password + * + * @retval WM_SUCCESS config ok, wifi will start to connect AP; + * @retval WM_FAILED parameter wrong + * + * @note User should register Wi-Fi status callback function + * to get result; + * wifi_status_change_cb just return WIFI MAC layer status; + * User should register netif status callback + * to get TCP/IP layer status; + */ +int tls_wifi_connect(u8 *ssid, u8 ssid_len, u8 *pwd, u8 pwd_len); + +/** + * @brief This function is used to connect AP by BSSID + * + * @param[in] *bssid BSSID to connect + * @param[in] *pwd password to connect AP + * @param[in] pwd_len length of password + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note When SSID hided,this function can not be used. + * User should register Wi-Fi status callback function + * to get result; + * wifi_status_change_cb just return WIFI MAC layer status; + * User should register netif status callback + * to get TCP/IP layer status; + */ +int tls_wifi_connect_by_bssid(u8 *bssid, u8 *pwd, u8 pwd_len); + +/** + * @brief This function is used to connect AP by SSID an BSSID + * + * @param[in] *ssid Network Name to connect + * @param[in] ssid_len length of SSID + * @param[in] *bssid BSSID to connect with SSID + * @param[in] *pwd password to connect AP + * @param[in] pwd_len length of password + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note When SSID&BSSID is knonw, user can use this function + * to connect Wi-Fi AP. + * User should register Wi-Fi status callback function + * to get result; + * wifi_status_change_cb just return WIFI MAC layer status; + * User should register netif status callback + * to get TCP/IP layer status; + */ +int tls_wifi_connect_by_ssid_bssid(u8 *ssid, u8 ssid_len, u8 *bssid, u8 *pwd, u8 pwd_len ); + + +/** + * @brief Set auto connect mode: Enable/Disable. + * Get auto connect mode parameter + * + * @param[in] opt WIFI_AUTO_CNT_FLAG_SET or WIFI_AUTO_CNT_FLAG_GET + * @param[in] mode WIFI_AUTO_CNT_OFF WIFI_AUTO_CNT_ON + * or WIFI_AUTO_CNT_TMP_OFF + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note WIFI_AUTO_CNT_OFF Disable/d; + * WIFI_AUTO_CNT_ON Enable/d; + * WIFI_AUTO_CNT_TMP_OFF For user initiated "DISCONNECT", + * such as AT CMD; In such case, user might expect + * "disconnect witout reconnection, even in WIFI_AUTO_CNT_ON + * status; WIFI_AUTO_CNT_TMP_OFF flag just be effective + * for one time of "DISCONNECT"; + * After that or reboot, the FLAG will be set to the previous value; + */ +int tls_wifi_auto_connect_flag(u8 opt, u8* mode); + +/** + * @brief This function is used to register wifi status changed callback function + * + * @param[in] callback point to wifi status function + * + * @return None + * + * @note + * WIFI_JOIN_SUCCESS connect with wifi AP correctly in Wifi layer; + * No IP address + * WIFI_JOIN_FAILED did not connect with wifi AP; + * normally, timeout in 20s after start connection + * WIFI_DISCONNECTED STA is disconnected with AP for any case, + * such as wifi AP shut dow, Wi-Fi AP + * changed password, and so on; + */ +void tls_wifi_status_change_cb_register(void (*callback)(u8 status)); + +/** + * @brief This function is used to get AP's info + * + * @param[out] bss address where the network parameters will be write; + * + * @return None + * + * @note None + */ +void tls_wifi_get_current_bss(struct tls_curr_bss_t* bss); + +/********************************************************************************************************* + Wifi WPS API +*********************************************************************************************************/ +#ifdef TLS_CONFIG_WPS + +/** + * @brief This function generate random PIN code + * + * @param[out] pin buf to store pin code, WPS_PIN_LEN Bytes + * + * @retval WM_SUCCESS success + * @retval other failed + * + * @note None + */ +int tls_wps_get_pin(u8* pin); + +/** + * @brief set PIN code into system + * + * @param[in] pin 8bytes string + * @param[in] pin_len length of pin, should be 8 by current WPS spec + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note normally, the @pin code will be hard coded during manufacturing. should not use this function; + */ +int tls_wps_set_pin(u8* pin, u8 pin_len); + +/** + * @brief Start WPS process via PIN mode + * + * @param None + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * @retval WM_WIFI_WPS_BUSY last WPS process is not finished; + * + * @note Normally, 120s for WPS protocol, but for us, 180s totally; + * Adapter will use the PIN code in system for WPS process + * and before that, the PIN code should be input + * into AP/Registrar mannually + */ +int tls_wps_start_pin(void); + +/** + * @brief Start WPS process via PBC/PushButton mode + * + * @param None + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * @retval WM_WIFI_WPS_BUSY last WPS process is not finished; + * + * @note Normally, 120s for WPS protocol, but for us, 180s totally; + * if OK, adaptor will start the WPS connection; + * APP should register Wi-Fi/netif status callback to get result + */ +int tls_wps_start_pbc(void); +#endif + +/** + * @brief This function is used to register ETHERNET data rx callback function + * + * @param[in] callback point to receive ETHERNET data function + * + * @return None + * + * @note None + */ +void tls_ethernet_data_rx_callback(net_rx_data_cb callback); +#if TLS_CONFIG_AP_OPT_FWD +/** + * @brief This function is used to forward IP data packets + * by routing, only for APSTA mode. + * + * @param[in] callback point to receive ETHERNET data function + * + * @return None + * + * @note Only in APSTA mode, this function to deal with IP data + */ +void tls_ethernet_ip_rx_callback(net_rx_data_cb callback); +#endif + +/** + * @brief This function is used to set powersave flag + * + * @param[in] enable non-zero: enable 0: disable + * @param[in] alwaysflag 0: only once, lost when restart; !0: always + * + * @return None + * + * @note None + */ +void tls_wifi_set_psflag(bool enable, bool alwaysflag); + +/** + * @brief This function is used to get current powersave flag + * + * @param None + * + * @retval 0 power-saving disable + * @retval non-zero power-saving enable + * + * @note None + */ +u32 tls_wifi_get_psflag(void); + +/** + * @brief This function is used to send oneshot data to other sta + * + * @param[in] *dst NULL , send broadcast info + * !NULL, send unicast info + * @param[in] *ssid SSID to be sent + * @param[in] ssid_len SSID length to be sent + * + * @return None + * + * @note None + */ +void tls_wifi_send_oneshotdata(u8 *dst, const u8 *ssid, u8 ssid_len); + +/** + * @brief This function is used to get max gain by rate index + * + * @param[in] tx_rate rate index (enum) + * + * @retval Max Gain + * + * @note None + */ +u8 tls_wifi_get_tx_gain_max(enum tls_wifi_tx_rate tx_rate); + +/** + * @brief This function is used to send 802.11 management packet + * + * @param[in] type management packet type + * @param[in] *mac mac address + * @param[in] *ie information elements, if have no ie can fill NULL + * @param[in] ie_len information elements length, it's zero if ie is NULL. + * @param[in] *tx rate and gain to transfer + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_wifi_send_mgmt(enum tls_wifi_mgmt_type type, struct tls_wifi_hdr_mac_t *mac, u8 *ie, u16 ie_len, struct tls_wifi_tx_rate_t *tx); + +/** + * @brief This function is used to send an 802.11 frame + * + * @param[in] *mac mac address, it can be NULL + * @param[in] *data data packet buffer + * @param[in] data_len data packet length + * @param[in] *tx rate and gain, it can be NULL + * + * @retval 0 success + * @retval other failed + * + * @note If the @*mac is NULL, @*data should be an entire 802.11 frame. + * If the @*mac is not NULL, this function will build an 802.11 frame + * with @*mac as destination mac address and @*data as the data body. + * If the @*tx is NULL, the packet will be sent at 11B 1Mbps. + */ +int tls_wifi_send_data(struct tls_wifi_hdr_mac_t *mac, u8 *data, u16 data_len, struct tls_wifi_tx_rate_t *tx); + +#if TLS_CONFIG_AP +/** + * @brief This function is used to get authed sta list + * + * @param[out] *sta_num authed's station number + * @param[out] *buf address to store returned station list info, tls_sta_info_t + * @param[in] buf_size buffer size + * + * @return None + * + * @note None + */ +void tls_wifi_get_authed_sta_info(u32 *sta_num, u8 *buf, u32 buf_size); +#endif +/** + * @brief This function is used to get current Wi-Fi State + * + * @param None + * + * @retval Wi-Fi State(enum) + * + * @note None + */ +enum tls_wifi_states tls_wifi_get_state(void); + +/** + * @brief This function is used to get current error number during joining AP + * + * @param None + * + * @retval Error Number(WM_WIFI_ENOERR,WM_WIFI_ENOAP, + * WM_WIFI_EKEY,WM_WIFI_ELSI) + * + * @note None + */ +int tls_wifi_get_errno(void); + +/** + * @brief This function is used to print current error during joining AP + * + * @param[in] *info prefix information + * + * @return None + * + * @note None + */ +void tls_wifi_perror(const char *info); + +/** + * @brief This function is used to get wifi error + * + * @param[in] eno error number(WM_WIFI_ENOERR,WM_WIFI_ENOAP, + * WM_WIFI_EKEY,WM_WIFI_ELSI) + * + * @retval error description + * + * @note None + */ +const char *tls_wifi_get_errinfo(int eno); + + +/** + * @} + */ +void tls_wifi_set_opmode(u8 mode); + +/** + * @} + */ +u8 tls_wifi_get_opmode(void); +/** + * @brief This function is used to customize wifi tx&rx memory + * + * @param[in] startmem: memory addr, only used from 0x20028000 to 0x20048000 + * + * @param[in] txcnt: wifi tx buf cnt, non -zero value + * + * @param[in] rxcnt: wifi rx buf cnt, greater than 2 && lower than 30 + * + * @retval 0 :successfullly + * <0:failure + * + * @note None + */ +int tls_wifi_mem_cfg(u32 startmem, u8 txcnt, u8 rxcnt); + +/** + * @brief This function is used to set max sta num + * + * @param[in] ap_sta_num: can be accepted num for sta + * + * @retval 0 :successfullly + * <0:failure if ap_sta_num is 0 + * + * @note max sta num is 8, this function must be called before ap created. + */ +int tls_wifi_softap_set_sta_num(unsigned char ap_sta_num); + +/** + * @brief This function is used to deauth sta connected to softap + * + * @param[in] hwaddr: sta's mac to deauth + * + * @retval 0 :successfullly + * <0:failure if hwaddr is null + * + * @note None + */ +int tls_wifi_softap_del_station(unsigned char* hwaddr); + +/** + * @} + */ +int tls_wl_get_isr_count(void); + + +#endif /* TLS_WIFI_FUNC_H */ + diff --git a/sdk/Include/list.h b/sdk/Include/list.h new file mode 100644 index 0000000..a034c72 --- /dev/null +++ b/sdk/Include/list.h @@ -0,0 +1,309 @@ +/* + * @file list.h + * @brief Doubly-linked list + * @copyright (c) 2009, Jouni Malinen + * + * @note This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef LIST_H +#define LIST_H + +#include + +/** struct dl_list - Doubly-linked list */ +struct dl_list { + struct dl_list *next; /**< pointer to the next */ + struct dl_list *prev; /**< pointer to the previous */ +}; + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup DLIST_APIs DLIST APIs + * @brief Double listed APIs + */ + +/** + * @addtogroup DLIST_APIs + * @{ + */ + +/** + * @brief reinitialize the list + * + * @param[in] *list the list + * + * @return None + * + * @note None + */ +static __inline void dl_list_init(struct dl_list *list) +{ + list->next = list; + list->prev = list; +} + +/** + * @brief Insert a new entry after the specified head + * + * @param[in] *list list head to add it after + * @param[in] *item new entry to be added + * + * @return None + * + * @note None + */ +static __inline void dl_list_add(struct dl_list *list, struct dl_list *item) +{ + item->next = list->next; + item->prev = list; + list->next->prev = item; + list->next = item; +} + +/** + * @brief Insert a new entry before the specified head + * + * @param[in] *list list head to add it after + * @param[in] *item new entry to be added + * + * @return None + * + * @note None + */ +static __inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item) +{ + dl_list_add(list->prev, item); +} + +/** + * @brief deletes entry from list + * + * @param[in] *item the element to delete from the list + * + * @return None + * + * @note None + */ +static __inline void dl_list_del(struct dl_list *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; + item->next = NULL; + item->prev = NULL; +} + +/** + * @brief tests whether a list is empty + * + * @param[in] *list the list to test + * + * @retval 0 not empty + * @retval 1 empty + * + * @note None + */ +static __inline int dl_list_empty(struct dl_list *list) +{ + return list->next == list; +} + +/** + * @brief count length of the list + * + * @param[in] *list the list to count + * + * @return length + * + * @note None + */ +static __inline unsigned int dl_list_len(struct dl_list *list) +{ + struct dl_list *item; + int count = 0; + for (item = list->next; item != list; item = item->next) + count++; + return count; +} + +/** + * @} + */ + +/** + * @} + */ + +#ifndef offsetof +/** offset address of the struct member */ +#define offsetof(type, member) ((long) &((type *) 0)->member) +#endif + +/** + * @brief get the struct for this entry + * + * @param[in] item the &struct list_head pointer + * @param[in] type the type of the struct this is embedded in + * @param[in] member the name of the list_struct within the struct + * + * @return pointer to the struct for this entry + * + * @note None + */ +#define dl_list_entry(item, type, member) \ + ((type *) ((char *) item - offsetof(type, member))) + +/** + * @brief get the first element from a list + * + * @param[in] list the list head to take the element from + * @param[in] type the type of the struct this is embedded in + * @param[in] member the name of the list_struct within the struct + * + * @return pointer to the first element from a list + * + * @note None + */ +#define dl_list_first(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->next, type, member)) + +/** + * @brief get the last element from a list + * + * @param[in] list the list head to take the element from + * @param[in] type the type of the struct this is embedded in + * @param[in] member the name of the list_struct within the struct + * + * @return pointer to the last element from a list + * + * @note None + */ +#define dl_list_last(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->prev, type, member)) + +/** + * @brief iterate over list of given type + * + * @param[in] item a loop cursor + * @param[in] list the head for your list + * @param[in] type struct type + * @param[in] member the name of the list_struct within the struct + * + * @return None + * + * @note None + */ +#define dl_list_for_each(item, list, type, member) \ + for (item = dl_list_entry((list)->next, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.next, type, member)) + +/** + * @brief iterate over list of given type safe against removal of list entry + * + * @param[in] item a loop cursor + * @param[in] n temporary storage + * @param[in] list the head for your list + * @param[in] type struct type + * @param[in] member the name of the list_struct within the struct + * + * @return None + * + * @note None + */ +#define dl_list_for_each_safe(item, n, list, type, member) \ + for (item = dl_list_entry((list)->next, type, member), \ + n = dl_list_entry(item->member.next, type, member); \ + &item->member != (list); \ + item = n, n = dl_list_entry(n->member.next, type, member)) + +/** + * @brief iterate backwards over list of given type + * + * @param[in] item a loop cursor + * @param[in] list the head for your list + * @param[in] type struct type + * @param[in] member the name of the list_struct within the struct + * + * @return None + * + * @note None + */ +#define dl_list_for_each_reverse(item, list, type, member) \ + for (item = dl_list_entry((list)->prev, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.prev, type, member)) + +/** define the list head */ +#define DEFINE_DL_LIST(name) \ + struct dl_list name = { &(name), &(name) } + +/** + * @brief iterate over list of given type + * + * @param[in] item the type * to use as a loop cursor + * @param[in] list the head for your list + * @param[in] member the name of the list_struct within the struct + * + * @return None + * + * @note None + */ +#define __dl_list_for_each(item, list, member) \ + for (item = dl_list_entry((list)->next, typeof(*(item)), member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.next, typeof(*(item)), member)) + +/** + * @brief iterate over list of given type safe against removal of list entry + * + * @param[in] item the type * to use as a loop cursor + * @param[in] n temporary storage + * @param[in] list the head for your list + * @param[in] member the name of the list_struct within the struct + * + * @return None + * + * @note None + */ +#define __dl_list_for_each_safe(item, n, list, member) \ + for (item = dl_list_entry((list)->next, typeof(*(item)), member), \ + n = dl_list_entry(item->member.next, typeof(*(item)), member); \ + &item->member != (list); \ + item = n, n = dl_list_entry(n->member.next, typeof(*(item)), member)) + +/** + * @brief iterate backwards over list of given type + * + * @param[in] item the type * to use as a loop cursor + * @param[in] list the head for your list + * @param[in] member the name of the list_struct within the struct + * + * @return None + * + * @note None + */ +#define __dl_list_for_each_reverse(item, list, member) \ + for (item = dl_list_entry((list)->prev, typeof(*(item)), member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.prev, typeof(*(item)), member)) + +#endif /* LIST_H */ diff --git a/sdk/Include/random.h b/sdk/Include/random.h new file mode 100644 index 0000000..7872f8a --- /dev/null +++ b/sdk/Include/random.h @@ -0,0 +1,80 @@ +/* + * @file random.h + * @brief Random number generator + * @copyright (c) 2010-2011, Jouni Malinen + * + * + * @note This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * @note Alternatively, this software may be distributed under the terms of BSD + * license. + * + * @note See README and COPYING for more details. + */ + +#ifndef RANDOM_H +#define RANDOM_H + +void random_deinit(void); + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup Random_APIs Random APIs + * @brief Random APIs + */ + +/** + * @addtogroup Random_APIs + * @{ + */ + +/** + * @brief This function is used to add number to random pool + * + * @param[in] *buf number to add + * @param[in] len number count + * + * @return None + * + * @note None + */ +void random_add_randomness(const void *buf, size_t len); +/** + * @brief This function is used to get random number with len from + random pool + * + * @param[in] *buf used to save random number + * @param[in] len length of random number + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int random_get_bytes(void *buf, size_t len); + +/** + * @} + */ + +/** + * @} + */ + +#define random_mark_pool_ready() do { } while (0) +/** random pool ready */ +#define random_pool_ready() 1 + +#endif /* RANDOM_H */ + diff --git a/sdk/Include/wm_config.h b/sdk/Include/wm_config.h new file mode 100644 index 0000000..fdf1ec0 --- /dev/null +++ b/sdk/Include/wm_config.h @@ -0,0 +1,83 @@ +/** + * @file wm_config.h + * + * @brief w600 chip inferface configure + * + * @author dave + * + * @copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM_CONFIG_H__ +#define __WM_CONFIG_H__ + +#define CFG_ON 1 +#define CFG_OFF 0 + +#define WM_CONFIG_DEBUG_UART1 CFG_OFF /*PRINTF PORT USE UART1*/ +#define WM_CONFIG_DEBUG_UART2 CFG_OFF /*PRINTF PORT USE UART2*/ + +/**Driver Support**/ +#define TLS_CONFIG_HS_SPI CFG_ON /*High Speed SPI*/ +#define TLS_CONFIG_LS_SPI CFG_ON /*Low Speed SPI*/ +#define TLS_CONFIG_UART CFG_ON /*UART*/ + +/**Host Interface&Command**/ +#define TLS_CONFIG_HOSTIF CFG_OFF +#define TLS_CONFIG_AT_CMD (CFG_OFF && TLS_CONFIG_HOSTIF) +#define TLS_CONFIG_RI_CMD (CFG_ON && TLS_CONFIG_HOSTIF) +#define TLS_CONFIG_RMMS CFG_OFF + +//LWIP CONFIG +#define TLS_CONFIG_IPV4 CFG_ON //must ON +#define TLS_CONFIG_IPV6 CFG_OFF +#define TLS_CONFIG_DHCP_OPTION60 "Klyqa:W600_01" + +/** SOCKET CONFIG **/ +#define TLS_CONFIG_SOCKET_STD CFG_ON +#define TLS_CONFIG_SOCKET_RAW CFG_ON +#define TLS_CONFIG_CMD_USE_RAW_SOCKET (CFG_ON && TLS_CONFIG_SOCKET_RAW) + + + +#define TLS_CONFIG_HARD_CRYPTO CFG_ON + +#define TLS_CONFIG_USE_POLARSSL CFG_OFF +#define TLS_CONFIG_SERVER_SIDE_SSL (CFG_OFF && TLS_CONFIG_HTTP_CLIENT_SECURE) /*MUST configure TLS_CONFIG_HTTP_CLIENT_SECURE CFG_ON */ + + +/** HTTP CLIENT **/ +/* +HTTP Lib +HTTPS Lib +SSL LIB +CRYPTO +*/ +#define TLS_CONFIG_HTTP_CLIENT (CFG_ON) +#define TLS_CONFIG_HTTP_CLIENT_PROXY CFG_OFF +#define TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC CFG_OFF +#define TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST CFG_OFF +#define TLS_CONFIG_HTTP_CLIENT_AUTH (TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC || TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST) +#define TLS_CONFIG_HTTP_CLIENT_SECURE (CFG_OFF) +#define TLS_CONFIG_HTTP_CLIENT_TASK (CFG_ON && TLS_CONFIG_HTTP_CLIENT) + + + +/**IGMP**/ +#define TLS_CONFIG_IGMP CFG_ON + +/** UPNP AND DLNA**/ +#define TLS_CONFIG_UPNP CFG_OFF +#define TLS_CONFIG_DLNA CFG_OFF && TLS_CONFIG_UPNP + + +#define TLS_CONFIG_NTP CFG_OFF + + +#define VERC_DNS_OPT CFG_ON +#define VERC_LWIP_OPT CFG_ON +#include "wm_os_config.h" //if you want to use source code,please open +#include "wm_wifi_config.h" + +#include "wm_ram_config.h" +#endif /*__WM_CONFIG_H__*/ + diff --git a/sdk/Include/wm_debug.h b/sdk/Include/wm_debug.h new file mode 100644 index 0000000..7d59492 --- /dev/null +++ b/sdk/Include/wm_debug.h @@ -0,0 +1,128 @@ +/** + * @file wm_debug.h + * + * @brief debug Module APIs + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_DEBUG_H +#define WM_DEBUG_H + +#include +#include +#include "wm_config.h" +#include "wm_osal.h" + +/* 0x00000000 - 0x80000000 */ +/** Define the debugging switch: on */ +#define TLS_DBG_ON 1 +/** Define the debugging switch: off */ +#define TLS_DBG_OFF 0 + + +/* 0x0000000F - 0x00000001 */ +/** Define the debugging level: info */ +#define TLS_DBG_LEVEL_INFO TLS_DBG_OFF +/** Define the debugging level: warning */ +#define TLS_DBG_LEVEL_WARNING TLS_DBG_OFF +/** Define the debugging level: error */ +#define TLS_DBG_LEVEL_ERR TLS_DBG_OFF +/** Define the debugging level: dump */ +#define TLS_DBG_LEVEL_DUMP TLS_DBG_OFF + +/** general debug info switch, default: off */ +#define TLS_GENERAL_DBG TLS_DBG_OFF + + +#define __TLS_DBGPRT_INFO(fmt, ...) \ +do { \ + u32 time = tls_os_get_time(); \ + wm_printf("[WM_I] <%d.%02d> %s : "fmt, (time/100), (time%100), __func__ , ##__VA_ARGS__); \ +} while (0) + +#define __TLS_DBGPRT_WARNING(fmt, ...) \ +do { \ + u32 time = tls_os_get_time(); \ + wm_printf("[WM_W] <%d.%02d> %s : "fmt, (time/100), (time%100), __func__ , ##__VA_ARGS__); \ +} while (0) + +#define __TLS_DBGPRT_ERR(fmt, ...) \ +do { \ + u32 time = tls_os_get_time(); \ + wm_printf("[WM_E] <%d.%02d> %s : "fmt, (time/100), (time%100), __func__ , ##__VA_ARGS__); \ +} while (0) + + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup DEBUG_APIs DEBUG APIs + * @brief DEBUG APIs + */ + +/** + * @addtogroup DEBUG_APIs + * @{ + */ + +#if (TLS_GENERAL_DBG && TLS_DBG_LEVEL_INFO) +/** Print information of the info level */ +#define TLS_DBGPRT_INFO(f, a...) __TLS_DBGPRT_INFO(f, ##a) +#else +/** Print information of the info level */ +#define TLS_DBGPRT_INFO(f, a...) +#endif + +#if (TLS_GENERAL_DBG && TLS_DBG_LEVEL_WARNING) +/** Print information of the warning level */ +#define TLS_DBGPRT_WARNING(f, a...) __TLS_DBGPRT_WARNING(f, ##a) +#else +/** Print information of the warning level */ +#define TLS_DBGPRT_WARNING(f, a...) +#endif + +#if (TLS_GENERAL_DBG && TLS_DBG_LEVEL_ERR) +/** Print information of the error level */ +#define TLS_DBGPRT_ERR(f, a...) __TLS_DBGPRT_ERR(f, ##a) +#else +/** Print information of the error level */ +#define TLS_DBGPRT_ERR(f, a...) +#endif + +#if (TLS_GENERAL_DBG && TLS_DBG_LEVEL_DUMP) +/** + * @brief dump memory + * + * @param[in] *p pointer the memory + * @param[in] len length of memory + * + * @return None + * + * @note None + */ +void TLS_DBGPRT_DUMP(char *p, u32 len); +#else +/** Print information of the dump level */ +#define TLS_DBGPRT_DUMP(p, len) +#endif + +/** + * @} + */ + +/** + * @} + */ + +#endif /* end of WM_DEBUG_H */ + diff --git a/sdk/Include/wm_include.h b/sdk/Include/wm_include.h new file mode 100644 index 0000000..2682e2e --- /dev/null +++ b/sdk/Include/wm_include.h @@ -0,0 +1,87 @@ +/** + * @file wm_include.h + * + * @brief the configuration file of sdk + * + * @author winnermicro + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM_INCLUDE_H__ +#define __WM_INCLUDE_H__ + +/** + * @mainpage WinnerMicro SDK + * + * Quick Start of WinnerMicro SDK. + * + * + * HOW TO CODE ? + * + * Function UserMain(void) is the entrance function of the application: + * @code + * void UserMain(void) + * { + * printf("\n user task\n"); + * + * #if DEMO_CONSOLE + * CreateDemoTask(); + * #endif + * + * //user's task + * } + * @endcode + * + * + * \n + * HOW TO COMPILE ? + * + * To build with the SDK you can use the keil tools. + * Opening "Tools\Keil\Project\WM_SDK.uvproj" to compile. + * + * + * \n + * HOW TO DOWNLOAD THE FIRMWARE ? + * + * Download the "WM_W600.FLS" image + * + * This will download image which includes secboot & sdk into flash by ROM using xModem-protocol. + * @code + * Pulling down the bootmode pin and reset the device. Then UART0 will output: + * CCC... + * For details,please refer to the sdk manual. + * @endcode + * + * Download the "WM_W600_SEC.IMG" image + * + * This will download image which includes sdk by secboot using xmodem-protocol. + * @code + * Press "ESC" and then reset the device. Then UART0 will output: + * secboot running... + * CCC... + * For details,please refer to the sdk manual. + * @endcode + * + * \n + */ + +#include +#include +#include "wm_type_def.h" +#include "wm_uart.h" +#include "wm_gpio.h" +#include "wm_hostspi.h" +#include "wm_socket.h" +#include "wm_sockets.h" +#include "wm_wifi.h" +#include "wm_hspi.h" +#include "wm_pwm.h" +#include "wm_params.h" +#include "wm_osal.h" +#include "wm_netif.h" +#include "wm_efuse.h" +#include "wm_mem.h" +#include "wm_regs.h" +#include "FreeRTOS.h" + +#endif diff --git a/sdk/Include/wm_log.h b/sdk/Include/wm_log.h new file mode 100644 index 0000000..92573d1 --- /dev/null +++ b/sdk/Include/wm_log.h @@ -0,0 +1,150 @@ +/** + * @file wm_log.h + * + * @brief Log Module APIs + * + * @author dave + * + * Copyright (c) 2019 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_LOG_H +#define WM_LOG_H + +#include "elog.h" + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup DEBUG_APIs DEBUG APIs + * @brief DEBUG APIs + */ + +/** + * @addtogroup DEBUG_APIs + * @{ + */ + +/** + * @brief output assert level log information + * + * @param String format list and variable parameters + * + * @return None + * + * @note The file contains the file name, function name, + * and line number information. + */ +#define wm_log_assert log_a + +/** + * @brief output error level log information + * + * @param String format list and variable parameters + * + * @return None + * + * @note None + */ +#define wm_log_error log_e + +/** + * @brief output warning level log information + * + * @param String format list and variable parameters + * + * @return None + * + * @note None + */ +#define wm_log_warn log_w + +/** + * @brief output info level log information + * + * @param String format list and variable parameters + * + * @return None + * + * @note None + */ +#define wm_log_info log_i + +/** + * @brief output raw log information + * + * @param String format list and variable parameters + * + * @return None + * + * @note The log does not have a color format, + * and the line ending automatically adds a line break. + */ +#define wm_log_raw elog_raw + +/** + * @brief Output log information in hexadecimal and display its ascii code string + * + * @param[in] name, name for hex object, it will show on log header + * @param[in] width, hex number for every line, such as: 16, 32 + * @param[in] buf, hex buffer + * @param[in] size, buffer size + * + * @return None + * + * @note None + */ +#define wm_log_dump elog_hexdump + + +/** + * @brief output debug level log information + * + * @param String format list and variable parameters + * + * @return None + * + * @note None + */ +#define wm_log_debug log_d + +/** + * @brief output verbose level log information + * + * @param String format list and variable parameters + * + * @return None + * + * @note None + */ +#define wm_log_verbose log_v + + +/** + * @brief initialize the log system + * + * @param None + * + * @return None + * + * @note None + */ +void wm_log_init(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* end of WM_LOG_H */ + diff --git a/sdk/Include/wm_ram_config.h b/sdk/Include/wm_ram_config.h new file mode 100644 index 0000000..fd303a2 --- /dev/null +++ b/sdk/Include/wm_ram_config.h @@ -0,0 +1,31 @@ +/** + * @file wm_ram_config.h + * + * @brief WM ram model configure + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM_RAM_CONFIG_H__ +#define __WM_RAM_CONFIG_H__ + +/*OS Task Stack's lower & upper range*/ +#define TASK_STACK_USING_MEM_LOWER_RANGE (0x20000000UL) +#define TASK_STACK_USING_MEM_UPPER_RANGE (0x20028000UL) + +/*0x20000000~MASTER_SPI_DMA_ADDR for stack,heap & data*/ + +/*Master SPI use buffer when spi use dma transfer mode*/ +// #define MASTER_SPI_DMA_ADDR 0x20038000UL +#define MASTER_SPI_DMA_ADDR 0x2003BC00UL + +/*High speed SPI or SDIO buffer to exchange data*/ +// #define SLAVE_HSPI_SDIO_ADDR 0x2003A000UL +#define SLAVE_HSPI_SDIO_ADDR 0x20039C00UL + +/*Wi-Fi use buffer to exchange data*/ +#define WIFI_MEM_START_ADDR 0x2003C000UL + +#endif /*__WM_RAM_CONFIG_H__*/ + diff --git a/sdk/Include/wm_regs.h b/sdk/Include/wm_regs.h new file mode 100644 index 0000000..b00d0d1 --- /dev/null +++ b/sdk/Include/wm_regs.h @@ -0,0 +1,917 @@ +/** + * @file wm_regs.h + * + * @brief register & operation file + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef WM_REGS_H +#define WM_REGS_H + +#define __MPU_PRESENT 1 + +#define __CM3_REV 0x0201 /*!< Core Revision r2p1 */ +#define __NVIC_PRIO_BITS 4 /*!< W600 uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ + +typedef enum IRQn +{ +/****** Cortex-M3 Processor Exceptions Numbers ***************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */ + +/****** Venus specific Interrupt Numbers *******************************************************/ + SDIO_RX_IRQn = 0, /*!< SDIO Receiver Interrupt */ + SDIO_TX_IRQn = 1, /*!< SDIO Transmit Interrupt */ + SDIO_RX_CMD_IRQn = 2, /*!< SDIO Command Receiver Interrupt, Not Used */ + SDIO_TX_CMD_IRQn = 3, /*!< SDIO Command Transmit Interrupt */ + MAC_IRQn = 4, /*!< MAC Interrupt */ + Resverd_IRQn = 5, /*!< Not Used */ + SEC_RX_IRQn = 6, /*!< Wi-Fi Rx Interrupt */ + SEC_TX_MNGT_IRQn = 7, /*!< Wi-Fi Managment Tx Interrupt */ + SEC_TX_DAT_IRQn = 8, /*!< Wi-Fi Data Tx Interrupt */ + PMU_TIMER1_IRQn = 9, /*!< PMU Timer1 Interrupt */ + PMU_TIMER0_IRQn = 10, /*!< PMU Timer0 Interrupt */ + PMU_GPIO_WAKE_IRQn = 11, /*!< PMU GPIO Wake Interrupt */ + PMU_SDIO_WAKE_IRQn = 12, /*!< PMU SDIO Wake Interrupt */ + DMA_Channel0_IRQn = 13, /*!< DMA Channel 0 global Interrupt */ + DMA_Channel1_IRQn = 14, /*!< DMA Channel 1 global Interrupt */ + DMA_Channel2_IRQn = 15, /*!< DMA Channel 2 global Interrupt */ + DMA_Channel3_IRQn = 16, /*!< DMA Channel 3 global Interrupt */ + DMA_Channel4_7_IRQn = 17, /*!< DMA Channel 4~7 global Interrupt */ + DMA_BURST_IRQn = 18, /*!< DMA Burst global Interrupt */ + I2C_IRQn = 19, /*!< I2C Interrupts */ + ADC_IRQn = 20, /*!< ADC Convert Interrupts */ + SPI_LS_IRQn = 21, /*!< Low Speed SPI Interrupt */ + SPI_HS_IRQn = 22, /*!< High Speed SPI Interrupt */ + UART0_IRQn = 23, /*!< UART0 Interrupts */ + UART1_IRQn = 24, /*!< UART1 Interrupt */ + GPIOA_IRQn = 25, /*!< GPIOA Interrupt */ + TIM0_IRQn = 26, /*!< Timer0 Trigger Interrupt */ + TIM1_IRQn = 27, /*!< Timer1 Trigger Interrupt */ + TIM2_IRQn = 28, /*!< Timer2 Trigger Interrupt */ + TIM3_IRQn = 29, /*!< Timer3 Trigger Interrupt */ + TIM4_IRQn = 30, /*!< Timer4 Trigger Interrupt */ + TIM5_IRQn = 31, /*!< Timer5 Trigger Interrupt */ + WDG_IRQn = 32, /*!< WatchDog Interrupt */ + PMU_IRQn = 33, /*!< Not Used */ + FLASH_IRQn = 34, /*!< Not Used */ + PWM_IRQn = 35, /*!< PWM Interrupt */ + I2S_IRQn = 36, /*!< I2S Interrupt */ + PMU_RTC_IRQn = 37, /*!< PMU RTC Interrupt */ + RSA_IRQn = 38, /*!< RSA Interrupt */ + CRYPTION_IRQn = 39, /*!< Cryption Interrupt */ + GPIOB_IRQn = 40, /*!< GPIOB Interrupt */ + UART2_IRQn = 41, /*!< UART2&7816 Interrupt */ + +} IRQn_Type; + +#include "core_cm3.h" + +typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus; + +typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState; +#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) + +typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus; + +typedef volatile unsigned int TLS_REG; /* Hardware register definition */ + +#define APB_CLK (40000000) /* 40MHz */ + +#define DEVICE_BASE_ADDR 0x40000000 + + +/*************************************************************** + * SDIOÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_SDIO_BASE_ADDR DEVICE_BASE_ADDR +#define HR_SDIO_CIS0 (HR_SDIO_BASE_ADDR + 0x008) +#define HR_SDIO_CIS1 (HR_SDIO_BASE_ADDR + 0x00C) +#define HR_SDIO_CSA (HR_SDIO_BASE_ADDR + 0x010) +#define HR_SDIO_READ (HR_SDIO_BASE_ADDR + 0x014) +#define HR_SDIO_WRITE (HR_SDIO_BASE_ADDR + 0x018) +#define HR_SDIO_INTEN (HR_SDIO_BASE_ADDR + 0x030) +#define HR_SDIO_OCR (HR_SDIO_BASE_ADDR + 0x034) +#define HR_SDIO_CIA (HR_SDIO_BASE_ADDR + 0x024) +#define HR_SDIO_PROG (HR_SDIO_BASE_ADDR + 0x028) + +/*************************************************************** + * SPIÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_HSPI_BASE_ADDR (DEVICE_BASE_ADDR + 0x200) +#define HR_HSPI_CLEAR_FIFO (HR_HSPI_BASE_ADDR) +#define HR_HSPI_SPI_CFG (HR_HSPI_BASE_ADDR + 0x04) +#define HR_HSPI_MODE_CFG (HR_HSPI_BASE_ADDR + 0x08) +#define HR_HSPI_INT_MASK (HR_HSPI_BASE_ADDR + 0x0C) +#define HR_HSPI_INT_STTS (HR_HSPI_BASE_ADDR + 0x10) +#define HR_HSPI_RXDAT_LEN (HR_HSPI_BASE_ADDR + 0x18) + +/*************************************************************** + * SDIO WRAPPER¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_SDIO_WRAPPER_BASE_ADDR (DEVICE_BASE_ADDR + 0x300) +#define HR_SDIO_INT_SRC (HR_SDIO_WRAPPER_BASE_ADDR + 0x000) +#define HR_SDIO_INT_MASK (HR_SDIO_WRAPPER_BASE_ADDR + 0x004) +#define HR_SDIO_UPCMDVALID (HR_SDIO_WRAPPER_BASE_ADDR + 0x008) +#define HR_SDIO_DOWNCMDVALID (HR_SDIO_WRAPPER_BASE_ADDR + 0x00C) +#define HR_SDIO_TXBD_LINKEN (HR_SDIO_WRAPPER_BASE_ADDR + 0x010) +#define HR_SDIO_TXBD_ADDR (HR_SDIO_WRAPPER_BASE_ADDR + 0x014) +#define HR_SDIO_TXEN (HR_SDIO_WRAPPER_BASE_ADDR + 0x018) +#define HR_SDIO_TX_STTS (HR_SDIO_WRAPPER_BASE_ADDR + 0x01C) +#define HR_SDIO_RXBD_LINKEN (HR_SDIO_WRAPPER_BASE_ADDR + 0x020) +#define HR_SDIO_RXBD_ADDR (HR_SDIO_WRAPPER_BASE_ADDR + 0x024) +#define HR_SDIO_RXEN (HR_SDIO_WRAPPER_BASE_ADDR + 0x028) +#define HR_SDIO_RX_STTS (HR_SDIO_WRAPPER_BASE_ADDR + 0x02C) +#define HR_SDIO_CMD_ADDR (HR_SDIO_WRAPPER_BASE_ADDR + 0x030) +#define HR_SDIO_CMD_SIZE (HR_SDIO_WRAPPER_BASE_ADDR + 0x034) + +/* SDIO interrupt bit definition */ +#define SDIO_WP_INT_SRC_CMD_DOWN (1UL<<3) +#define SDIO_WP_INT_SRC_CMD_UP (1UL<<2) +#define SDIO_WP_INT_SRC_DATA_DOWN (1UL<<1) +#define SDIO_WP_INT_SRC_DATA_UP (1UL<<0) + + +/*************************************************************** + * DMAÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_DMA_BASE_ADDR (DEVICE_BASE_ADDR + 0x400) +#define HR_DMA_INT_MASK (HR_DMA_BASE_ADDR + 0x0) +#define HR_DMA_INT_SRC (HR_DMA_BASE_ADDR + 0x4) +#define HR_DMA_CHNL_SEL (HR_DMA_BASE_ADDR + 0x08) +#define HR_DMA_CHNL0_SRC_ADDR (HR_DMA_BASE_ADDR + 0x10) +#define HR_DMA_CHNL0_DEST_ADDR (HR_DMA_BASE_ADDR + 0x14) +#define HR_DMA_CHNL0_SRC_WRAP_ADDR (HR_DMA_BASE_ADDR + 0x18) +#define HR_DMA_CHNL0_DEST_WRAP_ADDR (HR_DMA_BASE_ADDR + 0x1C) +#define HR_DMA_CHNL0_WRAP_SIZE (HR_DMA_BASE_ADDR + 0x20) +#define HR_DMA_CHNL0_CHNL_CTRL (HR_DMA_BASE_ADDR + 0x24) +#define HR_DMA_CHNL0_DMA_MODE (HR_DMA_BASE_ADDR + 0x28) +#define HR_DMA_CHNL0_DMA_CTRL (HR_DMA_BASE_ADDR + 0x2C) +#define HR_DMA_CHNL0_DMA_STATUS (HR_DMA_BASE_ADDR + 0x30) +#define HR_DMA_CHNL0_LINK_DEST_ADDR (HR_DMA_BASE_ADDR + 0x34) +#define HR_DMA_CHNL0_CURRENT_DEST_ADDR (HR_DMA_BASE_ADDR + 0x38) + +#define DMA_STARTADDRESS (0x40000400) +#define DMA_INTMASK_REG (*(volatile unsigned int*)(DMA_STARTADDRESS+0x000)) +#define DMA_INTSRC_REG (*(volatile unsigned int*)(DMA_STARTADDRESS+0x004)) +#define DMA_CHNL_REG_BASE (DMA_STARTADDRESS+0x010) +#define DMA_SRCADDR_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x00)) +#define DMA_DESTADDR_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x04)) +#define DMA_SRCWRAPADDR_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x08)) +#define DMA_DESTWRAPADDR_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x0C)) +#define DMA_WRAPSIZE_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x10)) +#define DMA_CHNLCTRL_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x14)) +#define DMA_MODE_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x18)) +#define DMA_CTRL_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x1C)) +#define DMA_STATUS_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x20)) +#define DMA_CURRSRCADDR_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x24)) +#define DMA_CURRDESTADDR_REG(ch) (*(volatile unsigned int*)(DMA_CHNL_REG_BASE + 0x30 * (ch /*- 1*/) +0x28)) + +#define DMA_CHNL_CTRL_CHNL_ON (1<<0) +#define DMA_CHNL_CTRL_CHNL_OFF (1<<1) + +#define DMA_MODE_HARD_MODE (1<<0) +#define DMA_MODE_CHAIN_MODE (1<<1) + +/*************************************************************** + * ½ÚÄÜÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_PMU_BASE_ADDR (DEVICE_BASE_ADDR + 0x600) +#define HR_PMU_PS_CR (HR_PMU_BASE_ADDR + 0x00) +#define HR_PMU_TIMER0 (HR_PMU_BASE_ADDR + 0x04) +#define HR_PMU_TIMER1 (HR_PMU_BASE_ADDR + 0x08) +#define HR_PMU_RTC_CTRL1 (HR_PMU_BASE_ADDR + 0x0C) +#define HR_PMU_RTC_CTRL2 (HR_PMU_BASE_ADDR + 0x10) +#define HR_PMU_INTERRUPT_SRC (HR_PMU_BASE_ADDR + 0x14) +#define HR_PMU_INTERRUPT_MASK (HR_PMU_BASE_ADDR + 0x18) +#define HR_PMU_WLAN_STTS (HR_PMU_BASE_ADDR + 0x1C) + +/* ½ÚÄܼĴæÆ÷붨Òå */ +#define PMU_WLAN_SLEEP_EN_BIT (1UL << 0) + +#define PMU_PRETBTT_PERIOD_RSV (0xE0000000) +#define PMU_TBTT_TIMER_MASK (0x3FFFF) +#define PMU_TBTT_TIMER_SHIFT (11) +#define PMU_SLEEP_INTERVAL_MASK (0x7FF) +#define PMU_SLEEP_INTERVAL_SHIFT (0) + +#define PMU_TBTT_OFFSET_MASK (0x7FFF) +#define PMU_TBTT_OFFSET_SHIFT (17) +#define PMU_PRETBTT_TIMER_MASK (0x1FFFF) +#define PMU_PRETBTT_TIMER_SHIFT (0) +#define PMU_PRETBTT_OFFSET_DEFAULT (((30UL) << 17) | (300UL)) + +#define PMU_TIMER0_WAKEUP_EN (1UL << 3) +#define PMU_TIMER1_WAKEUP_EN (1UL << 2) +#define PMU_GPIO_WAKEUP_EN (1UL << 1) +#define PMU_SDIO_WAKEUP_EN (1UL << 0) + +#define PMU_TIMER0_NTERRUPT_SRC (1UL << 0) +#define PMU_TIMER1_NTERRUPT_SRC (1UL << 1) +#define PMU_GPIO_NTERRUPT_SRC (1UL << 2) +#define PMU_SDIO_NTERRUPT_SRC (1UL << 3) + +#define PMU_TIMER0_NTERRUPT_MASK (1UL << 0) +#define PMU_TIMER1_NTERRUPT_MASK (1UL << 1) +#define PMU_GPIO_NTERRUPT_MASK (1UL << 2) +#define PMU_SDIO_NTERRUPT_MASK (1UL << 3) + +#define PMU_WLAN_RX_IDLE_BIT (1UL << 1) +#define PMU_WLAN_TX_IDLE_BIT (1UL << 0) + +#define PMU_LDO_DAC28_BYPASS (1UL<<0) +#define PMU_LDO_SW_BYPASS (1UL<<1) +#define PMU_DLDO_BYPASS (1UL<<2) +#define PMU_LDO_PRE_BYPASS (1UL<<3) +#define PMU_LDO_ADDA_D_BYPASS (1UL<<4) +#define PMU_LDO_ADDA_A_BYPASS (1UL<<5) +#define PMU_LDO_UM_PA_BYPASS (1UL<<6) +#define PMU_LDO_CHP_BYPASS (1UL<<7) +#define PMU_LDO_DM_IF_BYPASS (1UL<<8) +#define PMU_LDO_LNA_BYPASS (1UL<<9) +#define PMU_LDO_VCO_BYPASS (1UL<<10) +#define PMU_LDO_LO_BYPASS (1UL<<11) +#define PMU_LDO_PLL_BYPASS (1UL<<15) +#define PMU_LDO_WLAN_BYPASS (1UL<<19) +#define PMU_LDO_BGR_BYPASS (1UL<<20) +#define PMU_LDO_BB_BYPASS (1UL<<21) + +/*************************************************************** + * system clock ºÍbus clock¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_CLK_BASE_ADDR (DEVICE_BASE_ADDR + 0x700) +#define HR_CLK_SOFT_CLK_MASK (HR_CLK_BASE_ADDR + 0x04) +#define HR_CLK_BBP_CLT_CTRL (HR_CLK_BASE_ADDR + 0x08) +#define HR_CLK_RST_CTL (HR_CLK_BASE_ADDR + 0x0c) +#define HR_CLK_DIV_CTL (HR_CLK_BASE_ADDR + 0x10) + +#define HR_CLK_I2S_CTL (HR_CLK_BASE_ADDR + 0x18) + +#define I2S_CLK_CTL_EXT_Pos (0) +#define I2S_CLK_CTL_MCLK_Pos (1) +#define I2S_CLK_CTL_MCLKDIV_Pos (2) +#define I2S_CLK_CTL_BCLKDIV_Pos (8) + +/*************************************************************** + * ÄÚ´æÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_MEM_BASE_ADDR (DEVICE_BASE_ADDR + 0x800) +#define HR_MEM_TXBUF_BASE_ADDR (HR_MEM_BASE_ADDR + 0x00) +#define HR_MEM_RXBUF_BASE_ADDR (HR_MEM_BASE_ADDR + 0x04) +#define HR_MEM_BUF_NUM_CFG (HR_MEM_BASE_ADDR + 0x08) +#define HR_MEM_BUF_SIZE_CFG (HR_MEM_BASE_ADDR + 0x0C) +#define HR_MEM_AGGR_CFG (HR_MEM_BASE_ADDR + 0x10) +#define HR_MEM_BUF_EN (HR_MEM_BASE_ADDR + 0x14) + + +/*************************************************************** + * ÖжÏÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_VIC_BASE_ADDR 0xFFFFF000 +#define HR_VIC_IRQ_STATUS (HR_VIC_BASE_ADDR + 0x0) +#define HR_VIC_FIQ_STATUS (HR_VIC_BASE_ADDR + 0x4) +#define HR_VIC_RAW_INTR (HR_VIC_BASE_ADDR + 0x8) +#define HR_VIC_INT_SELECT (HR_VIC_BASE_ADDR + 0xc) +#define HR_VIC_INT_ENABLE (HR_VIC_BASE_ADDR + 0x10) +#define HR_VIC_INT_EN_CLR (HR_VIC_BASE_ADDR + 0x14) +#define HR_VIC_SOFT_INT (HR_VIC_BASE_ADDR + 0x18) +#define HR_VIC_SOFT_INT_CLR (HR_VIC_BASE_ADDR + 0x1c) +#define HR_VIC_PROTECT (HR_VIC_BASE_ADDR + 0x20) +#define HR_VIC_VECT_ADDR (HR_VIC_BASE_ADDR + 0x30) +#define HR_VIC_DEF_VECT_ADDR (HR_VIC_BASE_ADDR + 0x34) +#define HR_VIC_VECT_ENABLE (HR_VIC_BASE_ADDR + 0x100) +#define HR_VIC_VECT_BASE_ADDR (HR_VIC_BASE_ADDR + 0x104) +#define HR_VIC_INT_NUM (HR_VIC_BASE_ADDR + 0x108) +#define HR_VIC_INT_PRIO0 (HR_VIC_BASE_ADDR + 0x10C) +#define HR_VIC_INT_PRIO1 (HR_VIC_BASE_ADDR + 0x110) +#define HR_VIC_INT_PRIO2 (HR_VIC_BASE_ADDR + 0x114) +#define HR_VIC_INT_PRIO3 (HR_VIC_BASE_ADDR + 0x118) + + +/* APB»ùµØÖ·*/ +#define HR_APB_BASE_ADDR 0x40010000 + +/*************************************************************** + * I2CÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_I2C_BASE_ADDR (HR_APB_BASE_ADDR) +#define HR_I2C_PRER_LO (HR_I2C_BASE_ADDR + 0x0) +#define HR_I2C_PRER_HI (HR_I2C_BASE_ADDR + 0x04) +#define HR_I2C_CTRL (HR_I2C_BASE_ADDR + 0x08) +#define HR_I2C_TX_RX (HR_I2C_BASE_ADDR + 0x0C) +#define HR_I2C_CR_SR (HR_I2C_BASE_ADDR + 0x10) +#define HR_I2C_TXR (HR_I2C_BASE_ADDR + 0x14) +#define HR_I2C_CR (HR_I2C_BASE_ADDR + 0x18) + + +#define I2C_CTRL_INT_DISABLE (0x1UL<<6) +#define I2C_CTRL_ENABLE (0x1UL<<7) + +#define I2C_CR_STA (0x1UL<<7) +#define I2C_CR_STO (0x1UL<<6) +#define I2C_CR_RD (0x1UL<<5) +#define I2C_CR_WR (0x1UL<<4) +#define I2C_CR_NAK (0x1UL<<3) +#define I2C_CR_IACK (0x1UL) + +#define I2C_SR_NAK (0x1UL<<7) +#define I2C_SR_BUSY (0x1UL<<6) +#define I2C_SR_TIP (0x1UL<<1) +#define I2C_SR_IF (0x1UL) + + +/*************************************************************** + * SD ADCÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_SD_ADC_BASE_ADDR (HR_APB_BASE_ADDR + 0x200) +#define HR_SD_ADC_RESULT_REG ((HR_SD_ADC_BASE_ADDR+0x000)) +#define HR_SD_ADC_CONFIG_REG ((HR_SD_ADC_BASE_ADDR+0x004)) + + +/*************************************************************** + * SPIÄ£¿é¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_SPI_BASE_ADDR (HR_APB_BASE_ADDR + 0x400) +#define HR_SPI_CHCFG_REG (HR_SPI_BASE_ADDR + 0x000) +#define HR_SPI_SPICFG_REG (HR_SPI_BASE_ADDR + 0x004) +#define HR_SPI_CLKCFG_REG (HR_SPI_BASE_ADDR + 0x008) +#define HR_SPI_MODECFG_REG (HR_SPI_BASE_ADDR + 0x00C) +#define HR_SPI_INT_MASK_REG (HR_SPI_BASE_ADDR + 0x010) +#define HR_SPI_INT_STATUS_REG (HR_SPI_BASE_ADDR + 0x014) +#define HR_SPI_STATUS_REG (HR_SPI_BASE_ADDR + 0x018) +#define HR_SPI_TIMEOUT_REG (HR_SPI_BASE_ADDR + 0x01C) +#define HR_SPI_TXDATA_REG (HR_SPI_BASE_ADDR + 0x020) +#define HR_SPI_TRANSLEN_REG (HR_SPI_BASE_ADDR + 0x024) +#define HR_SPI_SLV_XMIT_LEN_REG (HR_SPI_BASE_ADDR + 0x028) +#define HR_SPI_RXDATA_REG (HR_SPI_BASE_ADDR + 0x030) + + +#define SPIM_STARTADDRESS (HR_APB_BASE_ADDR + 0x400) +#define SPIM_CHCFG_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x000)) +#define SPIM_SPICFG_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x004)) +#define SPIM_CLKCFG_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x008)) +#define SPIM_MODECFG_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x00C)) +#define SPIM_INTEN_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x010)) +#define SPIM_INTSRC_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x014)) +#define SPIM_SPISTATUS_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x018)) +#define SPIM_SPITIMEOUT_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x01C)) +#define SPIM_TXDATA_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x020)) +#define SPIM_TRANSLEN_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x024)) +#define SPIM_RXDATA_REG (*(volatile unsigned int*)(SPIM_STARTADDRESS+0x030)) + + +/*RSA¼Ä´æÆ÷*/ +#define RSA_BASE_ADDRESS 0x40002200 +#define RSAXBUF (*((volatile unsigned long *) (RSA_BASE_ADDRESS + 0x0 ))) +#define RSAYBUF (*((volatile unsigned long *) (RSA_BASE_ADDRESS + 0x100 ))) +#define RSAMBUF (*((volatile unsigned long *) (RSA_BASE_ADDRESS + 0x200 ))) +#define RSADBUF (*((volatile unsigned long *) (RSA_BASE_ADDRESS + 0x300 ))) +#define RSACON (*((volatile unsigned long *) (RSA_BASE_ADDRESS + 0x400 ))) +#define RSAMC (*((volatile unsigned long *) (RSA_BASE_ADDRESS + 0x404 ))) +#define RSAN (*((volatile unsigned long *) (RSA_BASE_ADDRESS + 0x408 ))) + + + +/* Bits Definitions of "CH_CFG" */ +#define SPI_RX_INVALID_BITS(n) ((n) << 23) +#define SPI_CLEAR_FIFOS (1U << 22) +#define SPI_CONTINUE_MODE (1U << 21) +#define SPI_RX_CHANNEL_ON (1U << 20) +#define SPI_RX_CHANNEL_OFF (0U << 20) +#define SPI_TX_CHANNEL_ON (1U << 19) +#define SPI_TX_CHANNEL_OFF (0U << 19) +#define SPI_VALID_CLKS_NUM(n) ((n) << 3) +#define SPI_CS_LOW (0U << 2) +#define SPI_CS_HIGH (1U << 2) +#define SPI_FORCE_SPI_CS_OUT (1U << 1) +#define SPI_START (1U << 0) + +/* Bits Definitions of "SPI_CFG" */ +#define SPI_FRAME_FORMAT_MOTO (0U << 17) +#define SPI_FRAME_FORMAT_TI (1U << 17) +#define SPI_FRAME_FORMAT_MICROWAIRE (2U << 17) +#define SPI_TX_PIN_ALWS_DRIVER (1U << 16) +#define SPI_CS_HOLD(n) ((n) << 12) +#define CS_HOLD_1_CLKS 0 +#define CS_HOLD_2_CLKS 1 +#define CS_HOLD_4_CLKS 2 +#define CS_HOLD_8_CLKS 3 +#define CS_HOLD_16_CLKS 4 +#define CS_HOLD_32_CLKS 5 +#define CS_HOLD_64_CLKS 6 +#define CS_HOLD_127_CLKS 7 +#define SPI_CS_SETUP(n) ((n) << 9) +#define CS_SETUP_1_CLKS 0 +#define CS_SETUP_2_CLKS 1 +#define CS_SETUP_4_CLKS 2 +#define CS_SETUP_8_CLKS 3 +#define CS_SETUP_16_CLKS 4 +#define CS_SETUP_32_CLKS 5 +#define CS_SETUP_64_CLKS 6 +#define CS_SETUP_127_CLKS 7 +#define SPI_DATA_OUT_DELAY(n) ((n) << 7) +#define DATA_OUT_DELAY_1_CLKS 0 +#define DATA_OUT_DELAY_2_CLKS 1 +#define DATA_OUT_DELAY_3_CLKS 2 +#define DATA_OUT_DELAY_0_CLKS 3 +#define SPI_FRAME_DELAY(n) ((n) << 4) +#define FRAME_DELAY_1_CLKS 0 +#define FRAME_DELAY_2_CLKS 1 +#define FRAME_DELAY_4_CLKS 2 +#define FRAME_DELAY_8_CLKS 3 +#define FRAME_DELAY_16_CLKS 4 +#define FRAME_DELAY_32_CLKS 5 +#define FRAME_DELAY_64_CLKS 6 +#define FRAME_DELAY_127_CLKS 7 +#define SPI_LITTLE_ENDIAN (0 << 3) +#define SPI_BIG_ENDIAN (1 << 3) +#define SPI_SET_MASTER_SLAVE(mode) ((mode) << 2) +#define SPI_MASTER 1 +#define SPI_SLAVE 0 +#define SPI_SET_CPHA(n) ((n) << 1) +#define SPI_SET_CPOL(n) ((n) << 0) + +/* Bits Definitions of "CLK_CFG" */ +#define SPI_GET_SCLK_DIVIDER(clk) ((APB_CLK)/((clk) * 2) - 1)/*In HZ*/ +#define SPI_SCLK_DIVIDER(n) ((n) << 0) + +/* Bits Definitions of "MODE_CFG" */ +#define SPI_RX_TRIGGER_LEVEL(n) ((n) << 6) +#define SPI_TX_TRIGGER_LEVEL(n) ((n) << 2) +#define SPI_RX_DMA_ON (1 << 1) +#define SPI_RX_DMA_OFF (0 << 1) +#define SPI_TX_DMA_ON (1 << 0) +#define SPI_TX_DMA_OFF (0 << 0) + +/* Bits Definitions of SPI interrupt register(SPI_INT_MASK and SPI_INT_SOURCE) */ +#define SPI_INT_MASK_ALL 0xffU +#define SPI_INT_CLEAR_ALL 0xffU +#define SPI_INT_TIMEOUT (1U << 7) +#define SPI_INT_TRANSFER_DONE (1U << 6) +#define SPI_INT_RX_OVERRUN (1U << 5) +#define SPI_INT_RX_UNDERRUN (1U << 4) +#define SPI_INT_TX_OVERRUN (1U << 3) +#define SPI_INT_TX_UNDERRUN (1U << 2) +#define SPI_INT_RX_FIFO_RDY (1U << 1) +#define SPI_INT_TX_FIFO_RDY (1U << 0) + +/* Bits Definitions of "SPI_STATUS" */ +#define SPI_IS_BUSY(value) ((((value) & (1 << 12)) != 0) ? 1 : 0) +#define SPI_GET_RX_FIFO_CNT(value) (((value) & 0xFC0) >> 6) +#define SPI_GET_TX_FIFO_CNT(value) ((value) & 0x3F) + +/* Bits Definitions of "SPI_TIMEOUT" */ +#define SPI_TIMER_EN (1U << 31) +#define SPI_TIME_OUT(n) (((((n) * (APB_CLK)) / 1000) & ~(0x01U << 31)) << 0) + +/*************************************************************** + * UART¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_UART0_BASE_ADDR (HR_APB_BASE_ADDR + 0x800) +#define HR_UART1_BASE_ADDR (HR_APB_BASE_ADDR + 0xA00) +#define HR_UART2_BASE_ADDR (HR_APB_BASE_ADDR + 0x1C00) + +#define HR_UART0_LINE_CTRL (HR_UART0_BASE_ADDR + 0x0) +#define HR_UART0_FLOW_CTRL (HR_UART0_BASE_ADDR + 0x04) +#define HR_UART0_DMA_CTRL (HR_UART0_BASE_ADDR + 0x08) +#define HR_UART0_FIFO_CTRL (HR_UART0_BASE_ADDR + 0x0C) +#define HR_UART0_BAUD_RATE_CTRL (HR_UART0_BASE_ADDR + 0x10) +#define HR_UART0_INT_MASK (HR_UART0_BASE_ADDR + 0x14) +#define HR_UART0_INT_SRC (HR_UART0_BASE_ADDR + 0x18) +#define HR_UART0_FIFO_STATUS (HR_UART0_BASE_ADDR + 0x1C) +#define HR_UART0_TX_WIN (HR_UART0_BASE_ADDR + 0x20) +#define HR_UART0_RX_WIN (HR_UART0_BASE_ADDR + 0x30) + +#define HR_UART1_LINE_CTRL (HR_UART1_BASE_ADDR + 0x0) +#define HR_UART1_FLOW_CTRL (HR_UART1_BASE_ADDR + 0x04) +#define HR_UART1_DMA_CTRL (HR_UART1_BASE_ADDR + 0x08) +#define HR_UART1_FIFO_CTRL (HR_UART1_BASE_ADDR + 0x0C) +#define HR_UART1_BAUD_RATE_CTRL (HR_UART1_BASE_ADDR + 0x10) +#define HR_UART1_INT_MASK (HR_UART1_BASE_ADDR + 0x14) +#define HR_UART1_INT_SRC (HR_UART1_BASE_ADDR + 0x18) +#define HR_UART1_FIFO_STATUS (HR_UART1_BASE_ADDR + 0x1C) +#define HR_UART1_TX_WIN (HR_UART1_BASE_ADDR + 0x20) +#define HR_UART1_RX_WIN (HR_UART1_BASE_ADDR + 0x30) + +#define HR_UART2_LINE_CTRL (HR_UART2_BASE_ADDR + 0x0) +#define HR_UART2_FLOW_CTRL (HR_UART2_BASE_ADDR + 0x04) +#define HR_UART2_DMA_CTRL (HR_UART2_BASE_ADDR + 0x08) +#define HR_UART2_FIFO_CTRL (HR_UART2_BASE_ADDR + 0x0C) +#define HR_UART2_BAUD_RATE_CTRL (HR_UART2_BASE_ADDR + 0x10) +#define HR_UART2_INT_MASK (HR_UART2_BASE_ADDR + 0x14) +#define HR_UART2_INT_SRC (HR_UART2_BASE_ADDR + 0x18) +#define HR_UART2_FIFO_STATUS (HR_UART2_BASE_ADDR + 0x1C) +#define HR_UART2_TX_WIN (HR_UART2_BASE_ADDR + 0x20) +#define HR_UART2_RX_WIN (HR_UART2_BASE_ADDR + 0x30) +#define HR_UART2_GUARD_TIME (HR_UART2_BASE_ADDR + 0x40) +#define HR_UART2_WAIT_TIME (HR_UART2_BASE_ADDR + 0x44) +/* + * Line Control Register bits definition + * ULCON0 ULCON1 Register + */ +/* bit 0-1 : Word length */ +#define ULCON_WL5 0x00 +#define ULCON_WL6 0x01 +#define ULCON_WL7 0x02 +#define ULCON_WL8 0x03 +#define ULCON_WL_MASK 0x03 +/* bit 2 : Number of stop bits */ +#define ULCON_STOP_2 0x04 /* 2 stop bit */ +/* bit 3-4 : Parity mode */ +#define ULCON_PMD_MASK 0x18 +#define ULCON_PMD_EN 0x08 /* no parity */ +#define ULCON_PMD_ODD 0x18 /* odd parity */ +#define ULCON_PMD_EVEN 0x08 /* even parity */ +/* bit 6 uart tx enable */ +#define ULCON_TX_EN 0x40 +#define ULCON_RX_EN 0x80 +/* bit 6 : Infra-red mode */ +#define ULCON_INFRA_RED 0x80 + +/* + * auto flow control register bits definition + */ +#define UFC_ENABLE 0x01 +#define UFC_SW_RTS_SET 0x02 +#define UFC_RTS_TRIGGER_LVL_4_BYTE (0<<2) +#define UFC_RTS_TRIGGER_LVL_8_BYTE (1<<2) +#define UFC_RTS_TRIGGER_LVL_12_BYTE (2<<2) +#define UFC_RTS_TRIGGER_LVL_16_BYTE (3<<2) +#define UFC_RTS_TRIGGER_LVL_20_BYTE (4<<2) +#define UFC_RTS_TRIGGER_LVL_24_BYTE (5<<2) +#define UFC_RTS_TRIGGER_LVL_28_BYTE (6<<2) +#define UFC_RTS_TRIGGER_LVL_31_BYTE (7<<2) + +#define UFC_TX_FIFO_RESET 0x01 +#define UFC_RX_FIFO_RESET 0x02 +#define UFC_TX_FIFO_LVL_ZERO (0<<2) +#define UFC_TX_FIFO_LVL_4_BYTE (1<<2) +#define UFC_TX_FIFO_LVL_8_BYTE (2<<2) +#define UFC_TX_FIFO_LVL_16_BYTE (3<<2) +#define UFC_RX_FIFO_LVL_ZERO (0<<4) +#define UFC_RX_FIFO_LVL_4_BYTE (1<<4) +#define UFC_RX_FIFO_LVL_8_BYTE (2<<4) +#define UFC_RX_FIFO_LVL_16_BYTE (3<<4) + +/* dma control */ +#define UDMA_RX_FIFO_TIMEOUT (1<<2) +#define UDMA_RX_FIFO_TIMEOUT_SHIFT (3) + +/* + * uart interrupt source register bits definition + */ +#define UIS_TX_FIFO_EMPTY (1UL<<0) +#define UIS_TX_FIFO (1UL<<1) +#define UIS_RX_FIFO (1UL<<2) +#define UIS_RX_FIFO_TIMEOUT (1UL<<3) +#define UIS_CTS_CHNG (1UL<<4) +#define UIS_BREAK (1UL<<5) +#define UIS_FRM_ERR (1UL<<6) +#define UIS_PARITY_ERR (1UL<<7) +#define UIS_OVERRUN (1UL<<8) + +/* + * fifo status register bits definition + */ +/* bit 0-5 : tx fifo count */ +#define UFS_TX_FIFO_CNT_MASK 0x3F +/* bit 6-11 : rx fifo count */ +#define UFS_RX_FIFO_CNT_MASK 0x3C0 +#define UFS_CST_STS (1UL<<12) + +/*************************************************************** + * GPIO¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_GPIO_BASE_ADDR (HR_APB_BASE_ADDR + 0x0C00) +#define HR_GPIO_DATA (HR_GPIO_BASE_ADDR + 0x0) +#define HR_GPIO_DATA_EN (HR_GPIO_BASE_ADDR + 0x04) +#define HR_GPIO_DIR (HR_GPIO_BASE_ADDR + 0x08) +#define HR_GPIO_PULL_EN (HR_GPIO_BASE_ADDR + 0x0C) +#define HR_GPIO_AF_SEL (HR_GPIO_BASE_ADDR + 0x10) +#define HR_GPIO_AF_S1 (HR_GPIO_BASE_ADDR + 0x14) +#define HR_GPIO_AF_S0 (HR_GPIO_BASE_ADDR + 0x18) +#define HR_GPIO_IS (HR_GPIO_BASE_ADDR + 0x20) +#define HR_GPIO_IBE (HR_GPIO_BASE_ADDR + 0x24) +#define HR_GPIO_IEV (HR_GPIO_BASE_ADDR + 0x28) +#define HR_GPIO_IE (HR_GPIO_BASE_ADDR + 0x2c) +#define HR_GPIO_RIS (HR_GPIO_BASE_ADDR + 0x30) +#define HR_GPIO_MIS (HR_GPIO_BASE_ADDR + 0x34) +#define HR_GPIO_IC (HR_GPIO_BASE_ADDR + 0x38) + +#define HR_GPIOA_BASE_ADDR (HR_APB_BASE_ADDR + 0x0C00) +#define HR_GPIOA_DATA (HR_GPIOA_BASE_ADDR + 0x0) +#define HR_GPIOA_DATA_EN (HR_GPIOA_BASE_ADDR + 0x04) +#define HR_GPIOA_DATA_DIR (HR_GPIOA_BASE_ADDR + 0x08) +#define HR_GPIOA_DATA_PULLEN (HR_GPIOA_BASE_ADDR + 0x0c) +#define HR_GPIOA_AFSEL (HR_GPIOA_BASE_ADDR + 0x10) +#define HR_GPIOA_AFS1 (HR_GPIOA_BASE_ADDR + 0x14) +#define HR_GPIOA_AFS0 (HR_GPIOA_BASE_ADDR + 0x18) +#define HR_GPIOA_IS (HR_GPIOA_BASE_ADDR + 0x20) +#define HR_GPIOA_IBE (HR_GPIOA_BASE_ADDR + 0x24) +#define HR_GPIOA_IEV (HR_GPIOA_BASE_ADDR + 0x28) +#define HR_GPIOA_IE (HR_GPIOA_BASE_ADDR + 0x2C) +#define HR_GPIOA_RIS (HR_GPIOA_BASE_ADDR + 0x30) +#define HR_GPIOA_MIS (HR_GPIOA_BASE_ADDR + 0x34) +#define HR_GPIOA_IC (HR_GPIOA_BASE_ADDR + 0x38) + +#define HR_GPIOB_BASE_ADDR (HR_APB_BASE_ADDR + 0x1200) +#define HR_GPIOB_DATA (HR_GPIOB_BASE_ADDR + 0x0) +#define HR_GPIOB_DATA_EN (HR_GPIOB_BASE_ADDR + 0x04) +#define HR_GPIOB_DATA_DIR (HR_GPIOB_BASE_ADDR + 0x08) +#define HR_GPIOB_DATA_PULLEN (HR_GPIOB_BASE_ADDR + 0x0c) +#define HR_GPIOB_AFSEL (HR_GPIOB_BASE_ADDR + 0x10) +#define HR_GPIOB_AFS1 (HR_GPIOB_BASE_ADDR + 0x14) +#define HR_GPIOB_AFS0 (HR_GPIOB_BASE_ADDR + 0x18) +#define HR_GPIOB_IS (HR_GPIOB_BASE_ADDR + 0x20) +#define HR_GPIOB_IBE (HR_GPIOB_BASE_ADDR + 0x24) +#define HR_GPIOB_IEV (HR_GPIOB_BASE_ADDR + 0x28) +#define HR_GPIOB_IE (HR_GPIOB_BASE_ADDR + 0x2C) +#define HR_GPIOB_RIS (HR_GPIOB_BASE_ADDR + 0x30) +#define HR_GPIOB_MIS (HR_GPIOB_BASE_ADDR + 0x34) +#define HR_GPIOB_IC (HR_GPIOB_BASE_ADDR + 0x38) + + +#define IO_PA_BASE_ADDR (HR_APB_BASE_ADDR + 0x0C00) +#define IO_PA_DATA (*(volatile unsigned int*)(IO_PA_BASE_ADDR + 0x0)) +#define IO_PA_EN (*(volatile unsigned int*)(IO_PA_BASE_ADDR + 0x4)) +#define IO_PA_DIR (*(volatile unsigned int*)(IO_PA_BASE_ADDR + 0x8)) +#define IO_PA_REN (*(volatile unsigned int*)(IO_PA_BASE_ADDR + 0xC)) +#define IO_PA_AFSEL (*(volatile unsigned int*)(IO_PA_BASE_ADDR + 0x10)) +#define IO_PA_AFS1 (*(volatile unsigned int*)(IO_PA_BASE_ADDR + 0x14)) +#define IO_PA_AFS0 (*(volatile unsigned int*)(IO_PA_BASE_ADDR + 0x18)) + +/*************************************************************** + * TIMER¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_TIMER_BASE_ADDR (HR_APB_BASE_ADDR + 0x0E00) +#define HR_TIMER_CFG (HR_TIMER_BASE_ADDR + 0x0) +#define HR_TIMER0_5_CSR (HR_TIMER_BASE_ADDR + 0x04) +#define HR_TIMER0_PRD (HR_TIMER_BASE_ADDR + 0x08) +#define HR_TIMER1_PRD (HR_TIMER_BASE_ADDR + 0x0C) +#define HR_TIMER2_PRD (HR_TIMER_BASE_ADDR + 0x10) +#define HR_TIMER3_PRD (HR_TIMER_BASE_ADDR + 0x14) +#define HR_TIMER4_PRD (HR_TIMER_BASE_ADDR + 0x18) +#define HR_TIMER5_PRD (HR_TIMER_BASE_ADDR + 0x1C) +/* timer csr bits defintion */ +#define TLS_TIMER_MS_UNIT(n) (1UL<<(0+5*n)) +#define TLS_TIMER_ONE_TIME(n) (1UL<<(1+5*n)) +#define TLS_TIMER_EN(n) (1UL<<(2+5*n)) +#define TLS_TIMER_INT_EN(n) (1UL<<(3+5*n)) +#define TLS_TIMER_INT_CLR(n) (1UL<<(4+5*n)) + +/*************************************************************** + * WATCH DOG¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_WDG_BASE_ADDR (HR_APB_BASE_ADDR + 0x1000) +#define HR_WDG_LOAD_VALUE (HR_WDG_BASE_ADDR + 0x00) +#define HR_WDG_CUR_VALUE (HR_WDG_BASE_ADDR + 0x04) +#define HR_WDG_CTRL (HR_WDG_BASE_ADDR + 0x08) +#define HR_WDG_INT_CLR (HR_WDG_BASE_ADDR + 0x0C) +#define HR_WDG_INT_SRC (HR_WDG_BASE_ADDR + 0x10) +#define HR_WDG_INT_MIS (HR_WDG_BASE_ADDR + 0x14) +#define HR_WDG_LOCK (HR_WDG_BASE_ADDR + 0x40) + +/*************************************************************** + * IOCTL¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_IOCTL_BASE_ADDR (HR_APB_BASE_ADDR + 0x1200) +#define HR_IOCTL_DL_MODE (HR_IOCTL_BASE_ADDR + 0x0) +#define HR_IOCTL_GP_SDIO_I2C (HR_IOCTL_BASE_ADDR + 0x04) +#define HR_IOCTL_GP_JTAG (HR_IOCTL_BASE_ADDR + 0x08) +#define HR_IOCTL_GP_SPI (HR_IOCTL_BASE_ADDR + 0x0C) +#define HR_IOCTL_PE (HR_IOCTL_BASE_ADDR + 0x10) + + +#define IOCTRL_STARTADDRESS (HR_APB_BASE_ADDR + 0x1200) +#define IOCTRL_DOWNLOAD_MODE_REG (*(volatile unsigned *)(IOCTRL_STARTADDRESS+0x000)) +#define IOCTRL_PIN_CONFIG_REG1 (*(volatile unsigned *)(IOCTRL_STARTADDRESS+0x004)) +#define IOCTRL_PIN_CONFIG_REG2 (*(volatile unsigned *)(IOCTRL_STARTADDRESS+0x008)) +#define IOCTRL_PIN_CONFIG_REG3 (*(volatile unsigned *)(IOCTRL_STARTADDRESS+0x00C)) + + +/** bit field of the lcd gate control in CLK gating register */ +#define HR_CLK_LCD_GATE_Pos (10) + +/***********************************************************//** + * LCD¼Ä´æÆ÷¶¨Òå + ***************************************************************/ + +#define HR_LCD_REG_BASE (HR_APB_BASE_ADDR + 0x1600) //(0x4001 1600) +#define HR_LCD_CR (HR_LCD_REG_BASE+0x000) +#define HR_LCD_FRAME_CNT (HR_LCD_REG_BASE+0x004) +#define HR_LCD_COM0_1_SEG (HR_LCD_REG_BASE+0x008) +#define HR_LCD_COM2_3_SEG (HR_LCD_REG_BASE+0x00C) +#define HR_LCD_COM4_5_SEG (HR_LCD_REG_BASE+0x010) +#define HR_LCD_COM6_7_SEG (HR_LCD_REG_BASE+0x014) +#define HR_LCD_IO_EN (HR_LCD_REG_BASE+0x018) + +#define LCD_CR_EN_Pos (8) +#define LCD_CR_PD_Pos (9) + +#define LCD_CR_MODE_Pos (11) +#define LCD_CR_MODE_MASK (1UL<<11) +#define LCD_CR_MODE_4x20 (1UL<<11) +#define LCD_CR_MODE_8X16 (0UL<<11) + +#define LCD_EN (1UL<<8) + +#define LCD_BIAS_MASK (3UL<<6) +#define LCD_BIAS_MASK_Pos (6) +#define LCD_BIAS_ONEFOURTH (0UL<<6) +#define LCD_BIAS_ONEHALF (1UL<<6) +#define LCD_BIAS_ONETHIRD (2UL<<6) +#define LCD_BIAS_STATIC (3UL<<6) + +#define LCD_VLCD_MASK (7UL<<3) +#define LCD_VLCD_MASK_Pos (3) +#define LCD_VLCD_27 (0UL<<3) +#define LCD_VLCD_29 (1UL<<3) +#define LCD_VLCD_31 (2UL<<3) +#define LCD_VLCD_33 (3UL<<3) + + +#define LCD_DUTY_MASK (7UL<<0) +#define LCD_DUTY_MASK_Pos (0) +#define LCD_DUTY_STATIC (0UL<<0) +#define LCD_DUTY_ONEHALF (1UL<<0) +#define LCD_DUTY_ONETHIRD (2UL<<0) +#define LCD_DUTY_ONEFOURTH (3UL<<0) +#define LCD_DUTY_ONEFIFTH (4UL<<0) +#define LCD_DUTY_ONESIXTH (5UL<<0) +#define LCD_DUTY_ONESEVENTH (6UL<<0) +#define LCD_DUTY_ONEEIGHTH (7UL<<0) + + +/***********************************************************//** + * I2S¼Ä´æÆ÷¶¨Òå + ***************************************************************/ +#define HR_CLK_I2S_GATE_Pos (11) + + +#define HR_I2S_REG_BASE (HR_APB_BASE_ADDR+0x1A00) +#define HR_I2S_CTRL (HR_I2S_REG_BASE+0x000) +#define HR_I2S_INT_MASK (HR_I2S_REG_BASE+0x004) +#define HR_I2S_INT_SRC (HR_I2S_REG_BASE+0x008) +#define HR_I2S_STATUS (HR_I2S_REG_BASE+0x00C) +#define HR_I2S_TX (HR_I2S_REG_BASE+0x010) +#define HR_I2S_RX (HR_I2S_REG_BASE+0x014) + +/******************************************************************************/ +/* */ +/* PWM */ +/* */ +/******************************************************************************/ +#define HR_PWM_REG_BASE (HR_APB_BASE_ADDR+0x1800) +#define HR_PWM_CLKDIV01 (HR_PWM_REG_BASE+0x0000) +#define HR_PWM_CLKDIV23 (HR_PWM_REG_BASE+0x0004) +#define HR_PWM_CTL (HR_PWM_REG_BASE+0x0008) +#define HR_PWM_PERIOD (HR_PWM_REG_BASE+0x000C) +#define HR_PWM_PNUM (HR_PWM_REG_BASE+0x0010) +#define HR_PWM_CMPDAT (HR_PWM_REG_BASE+0x0014) +#define HR_PWM_DTCTL (HR_PWM_REG_BASE+0x0018) +#define HR_PWM_INTEN (HR_PWM_REG_BASE+0x001C) +#define HR_PWM_INTSTS (HR_PWM_REG_BASE+0x0020) +#define HR_PWM_CAPDAT (HR_PWM_REG_BASE+0x0024) +#define HR_PWM_BRKCTL (HR_PWM_REG_BASE+0x0028) +#define HR_PWM_CH4_REG1 (HR_PWM_REG_BASE+0x002C) +#define HR_PWM_CH4_REG2 (HR_PWM_REG_BASE+0x0030) +#define HR_PWM_CAP2DAT (HR_PWM_REG_BASE+0x0034) +#define HR_PWM_CAP2CTL (HR_PWM_REG_BASE+0x0038) + +/* EFUSE Register */ +#define HR_EFUSE_REG_BASE (0x0f001600) +#define HR_EFUSE_WDATA_PORT (HR_EFUSE_REG_BASE+0x000) +#define HR_EFUSE_PGM_CMD (HR_EFUSE_REG_BASE+0x004) +#define HR_EFUSE_PGM_STTS (HR_EFUSE_REG_BASE+0x008) +#define HR_EFUSE_CLEAR (HR_EFUSE_REG_BASE+0x00C) +#define HR_EFUSE_VDDQ_SETUP (HR_EFUSE_REG_BASE+0x010) + +/* PGM_CMD define */ +#define EFUSE_PGM_START (1UL) +#define EFUSE1_SELECT (1UL << 1) +#define EFUSE2_SELECT (1UL << 2) + +/* PGM STTS define */ +#define EFUSE_PGM_DONE (1UL) +#define EFUSE_FIFO_FREE (1UL << 1) + +/* CLEAR_EFUSE */ +#define EFUSE_RESET (1UL) + +/** + * @defgroup System_APIs System APIs + * @brief System APIs + */ + +/** + * @addtogroup System_APIs + * @{ + */ + +/** + * @defgroup REG_APIs REG APIs + * @brief Register operate APIs + */ + +/** + * @addtogroup REG_APIs + * @{ + */ + +/** + * @brief This function is used to write value to register or memory + * + * @param[in] reg address to be written + * @param[in] val value to write + * + * @return None + * + * @note None + */ +static __inline void tls_reg_write32(unsigned int reg, unsigned int val) +{ + *(TLS_REG *)reg = val; +} + + +/** + * @brief This function is used to read value from register or memory + * + * @param[in] reg address to read + * + * @retval val read from register or memory + * + * @note None + */ +static __inline unsigned int tls_reg_read32(unsigned int reg) +{ + unsigned int val = *(TLS_REG *)reg; + return val; +} + + +/** + * @brief This function is used to write specified bit through + bit-band alias region + * + * @param[in] addr 32-bit aligned byte address where the bit exists. + * @param[in] bit Bit position. + * @param[in] val The value that the bit is set to, 0 or 1. + * + * @return None + * + * @note Only when bit-band mechnism is supported, you can use it + */ +static __inline void tls_bitband_write(volatile unsigned int addr, unsigned int bit, unsigned int val) +{ + unsigned int temp; + + temp = (((unsigned int)(addr) & 0xF0000000) + 0x02000000) \ + +((((unsigned int)(addr)&0xFFFFF)<<5)\ + +( (unsigned int)(bit)<<2)); + + *((volatile unsigned int * )temp) = val; +} + +/** + * @brief This function is used to read a single bit from address + by bit-band alias region + * + * @param[in] addr 32-bit aligned byte address where the bit exists + * @param[in] bit Bit position + * + * @retval The value of the requested bit + * + * @note Only when bit-band mechnism is supported, you can use it + */ +static __inline unsigned int tls_bitband_read(volatile unsigned int addr, unsigned int bit) +{ + unsigned int temp; + + temp = (((unsigned int)(addr) & 0xF0000000) + 0x02000000) \ + +((((unsigned int)(addr)&0xFFFFF)<<5)\ + +( (unsigned int)(bit)<<2)); + + return *((volatile unsigned int *)temp); +} + + +/** + * @} + */ + +/** + * @} + */ + +#endif /* WM_REGS_H */ + diff --git a/sdk/Include/wm_type_def.h b/sdk/Include/wm_type_def.h new file mode 100644 index 0000000..1a75de3 --- /dev/null +++ b/sdk/Include/wm_type_def.h @@ -0,0 +1,155 @@ +/** + * @file wm_type_def.h + * + * @brief WM type redefine + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM_TYPE_DEF_H__ +#define __WM_TYPE_DEF_H__ + +#ifdef bool +#undef bool +#endif +#ifdef u8 +#undef u8 +#endif +#ifdef s8 +#undef s8 +#endif +#ifdef u16 +#undef u16 +#endif +#ifdef s16 +#undef s16 +#endif +#ifdef u32 +#undef u32 +#endif +#ifdef s32 +#undef s32 +#endif +#ifdef u64 +#undef u64 +#endif +#ifdef s64 +#undef s64 +#endif + +#ifdef u_char +#undef u_char +#endif +typedef unsigned char u_char; + +#ifdef INT8U +#undef INT8U +#endif +typedef unsigned char INT8U; + +#ifdef INT8S +#undef INT8S +#endif +typedef signed char INT8S; + +#ifndef __cplusplus +typedef unsigned char bool; +#endif +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; +typedef unsigned long long u64; +typedef long long s64; + +#ifdef ULONG +#undef ULONG +#endif +typedef unsigned long ULONG; + +#ifdef u8_t +#undef u8_t +#endif +typedef unsigned char u8_t; + +#ifdef u16_t +#undef u16_t +#endif +typedef unsigned short u16_t; + +#ifdef u32_t +#undef u32_t +#endif +typedef unsigned int u32_t; + +#ifdef s8_t +#undef s8_t +#endif +typedef signed char s8_t; + +#ifdef s16_t +#undef s16_t +#endif +typedef signed short s16_t; + +#ifdef s32_t +#undef s32_t +#endif +typedef signed int s32_t; + +typedef long long int64; +typedef int int32; +typedef short int16; +typedef char int8; +typedef unsigned long long uint64; +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + + +#if (GCC_COMPILE==0) +#ifdef size_t +#undef size_t +#endif +typedef unsigned int size_t; +#endif + +#ifdef err_t +#undef err_t +#endif +typedef signed char err_t; + + +#ifdef mem_ptr_t +#undef mem_ptr_t +#endif +typedef unsigned int mem_ptr_t; + +#ifdef TRUE +#undef TRUE +#endif +#define TRUE 1 + +#ifdef FALSE +#undef FALSE +#endif +#define FALSE 0 + +#ifndef __cplusplus +#define true 1 +#define false 0 +#endif + +#define WM_SUCCESS 0 +#define WM_FAILED -1 + +#ifndef IGNORE_PARAMETER +#define IGNORE_PARAMETER(x) ((x) = (x)) +#endif + +int wm_printf(const char *fmt,...); + +#endif diff --git a/sdk/Include/wm_wifi_config.h b/sdk/Include/wm_wifi_config.h new file mode 100644 index 0000000..650e733 --- /dev/null +++ b/sdk/Include/wm_wifi_config.h @@ -0,0 +1,36 @@ +/** + * @file wm_wifi_config.h + * + * @brief WM wifi model configure + * + * @author winnermicro + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#ifndef __WM_WIFI_CONFIG_H__ +#define __WM_WIFI_CONFIG_H__ + +#define CFG_WIFI_ON 1 +#define CFG_WIFI_OFF 0 + +/*******************WIFI INFO************************** + Below Switch Only for Reference!!! +********************************************************/ +#define TLS_CONFIG_AP CFG_WIFI_ON +#define TLS_CONFIG_11N CFG_WIFI_ON + +#define TLS_CONFIG_SOFTAP_11N CFG_WIFI_ON + +#define TLS_CONFIG_AP_BEACON_SOFT (CFG_ON && TLS_CONFIG_AP) +#define TLS_CONFIG_AP_OPT_PS (CFG_OFF && TLS_CONFIG_AP)/* SOFTAP POWER SAVE */ +#define TLS_CONFIG_AP_OPT_FWD (CFG_ON && TLS_CONFIG_AP)/* IP PACKET FORWARD */ + +#define TLS_CONFIG_WPS CFG_WIFI_OFF /* WPS&EAPOL should be enabled together */ +#define TLS_IEEE8021X_EAPOL CFG_WIFI_OFF + +#define TLS_CONFIG_1SSID_MULTI_PWD CFG_WIFI_ON + +#define TLS_CONFIG_FAST_JOIN_NET CFG_WIFI_ON + +#endif /*__WM_WIFI_CONFIG_H__*/ + diff --git a/sdk/Lib/GNU/libwlan.a b/sdk/Lib/GNU/libwlan.a new file mode 100644 index 0000000000000000000000000000000000000000..21b081d0a505ad24440e60848133d71a916ef354 GIT binary patch literal 447222 zcmeFa34ByV);@fD$wCMa0%`=rP8tx1ViLlR8j{fLi!6$`bUI0gZphMfCy=m6SX5M0 z6c@&A5FEvwfGDU4Dk>^E32rE;QCz`s5=3p6k^l45y>HyGKTB^_lz;ZQKTAD3%jF7k5!C^6@NCEPTo?i+)1yE0*7SmJU@c@>vPq z)}9SV70Uy&fBV@!P_g`xr#`4y_`LV%f68Nx>8t#gJ`?vTRzA}nZT%_UtXO~Rng5_- zH9m_MDAs@Vx#f1n`d{@tc9UY2&-U4h_5a9Y>-vdu!l&muitUd)Sx+dof8`l>u44PY z@+`SsvHe#*j~-QQ|C64tyC}Bbdj3(O*#61K?zmq0|Kuq=tk~t_AE?;>$aC#J#r|LQ zJXWIEPxO3@yqce;iHg0o=O618`@itS^u0j&PkC}4RANr_od1&&^IOlnzDmr0=Cl4K zCFcLi6JDUi{KgZT@~ZOx%Ts)x5?j4F`dNLw68qo(@pwb-%&fugqQWA#x4bA=6R1>- zv|?|BR#iSf=n05m;R%FBctc~#{gvaqHHwa?zLBb|+|r7oIZ+f9$ydc1f;Ecbs|Zzk z0>$p4Qm-fIt}6Ezc|w)$K*(L_uZ#*TC$X%mv{G>BL7q}1=`IX~+{KaT$Yo)BT%7J@eAJQjpYmt64*b@p>6!}|= z81$Yvtk@rFHBgq)>o0cCFDugvs^RW=o>G5t6xCPhF7gCCh5k~1rQaJehgAf;LA3_d zDt8Bh6@^|FU*mKadObxIGAdiuABjp7N>f}BJRw!pab2FO z7S(Q)JX#F;hajri2!&9~mDNxuZ*^8-4S3-RRR*&P{pE_k96baywOg-{kLBK?N;6qidC}hHpsE0Rpw|tRbkFy;kY@}Hcuo+vsAQH1 z5#3x=J)%KoOS&<8=n873F@}g=jP4Mus4-CurBjO$LP^<>Fe+LtM#^923C?j#)i!5W zjjo4EO^1$E1>9`vhFH{d~EXHZ1@RjK}RO^V(t zbc$QkIVri^JKrr@h=wep?bV_WfaInTs0jKOc#DmpL2r@Q4>i}tR8;y)R6#n8eV5f} z$|A|$>dMxFD(07~of;Obq#}}DCVDSQ;HKg#qDL`j(gGOy%3!mNc(583Sy6_uVoo`v z>v==fg}iQ<#j=o*-4FFaEk}vayCBsZmQrDN@D&HCK*&y6@c=&PsYR6)L1jYz0FHWe zrwbjY&*d-olqx=sGX_?PD-|L40x>kv2kXgEieBhtaaBMGV$47hm@GSHND^R4P_+yz z_d=IP1dFmo()74PbNq;D@R-#S>Rg1rK(#gp!AQ1BZ;e-U3o#O^QrJtFQ_YTvVB&f$ zMziT;gC!Gjw4SDQ1J+!~=gE|{EUJzKMNk9565&xXttF<89+5c>^%E+o2$m_tMD5U{ zwG6##(*z3V6qjU)Bq$?mz(6q}Kqj=luF{0H*1|EK6fsysiz~{#(ywJeW|Gock|Z

PY1ctV)$ zzmgD=;$W|&+0+I`jEj{}Dz$@ngJl@yG1tIUN*%qsM3`2og=NB=p+F^m zbH#qEk@^xs zsb+exPARWo=|nDdnx+$OQEt&{kw#J{NbW$TT9JAjeO{<4i5y|{%%jQFc`_-pD(Apb zBHNL4h2b{nC31_2xgI^>@dxMoVf4{#5h^>C42e)#P)<$6XrfCXmB>7AFvO{KRDil9 zfp&yURUuMnNrs8{U?vjjEGSN(!kA%3WD?Ln*-O=G71b?CD2+Oe5ZRd@^kbr6&?`~9 zv%FE7%yDB*u{uE%^J-2mw3O(m>JJVit!jmkQWZjHFDp?eYmr`!A}CA{MaO6;;+M^F zS61i^T;vbNPRpUOT+9g6X?L-= zuxb{Jcg1|8B9lflh-TAftSVnAv`94<#ld-zaZVtpa#+H-A_I~j3hyaZ?M4()bbhVh zP!wX9RcI9}8P!Pwi$RLkfLJI?3iYNOgS^BkM9eEBLGKniOR<+(lIu2|Z0cqrIm2qp zz$f~SJ6j>R| z)F{3+(Q0<305J@D=ayGR7K!YbA|lle(xNkgK#q+vzRZBhTttQYxXECsPZ?L_FtUUc zL$zr3mf2=DG3zTrYkG@x;S|8e(j8y<3vC#?=Ej0XGzsX;oIc zvD~lns^e62n=yx&LJK9|iGf`W;9SBR47M60Y7N{X zrdV)sT;?ueaZ3UE^s+@vQF$#R=qlAB0vB@gi)Xc{0I`*7u7;@1RBv^_AH)(XQfpd$ z7;840yKxv=T5v^{Gh*+qH5S?cFHHLHOVBi~sP=Y$e%HnWhPL)=kJH@7i*cVBrB9eu{Vo%(}$1rsYW5|_S@fCrPa-rYl zzd&74U=P%-?p#0w5v&cdYs7?!i7i&mA5cv?$=u)2sOsiCcXuRZfj3wY@MvqsNV|)Q zL?m;WI16PiMekL6n#xk9W-NN4*qR~13Ih`>?CBd;s;nuw&cG5x_66>IOX+{W$g&<= zwUweU>f$J}&PI*v20fa}s*9$SVn|X~Wzlp_1)?c1=q8Ff--zZCG;B1vJ+t&ZaV-6r zB@{yOhul@3KnVQ=HI4O3WLr>U;?A9BAC(fZ$y$Qf5nRPY(qV0sc`C8D0kJ{{=a-5U zXxW5nOJbm6KHI}H5Bt&cJ#)O0PCMccVJYh`Q6`MYb5EP$zA)FFoA1t@?sAQs;vSKo zKSlCVuQ?MUKlL4Hsp&+KV6zMi%pp+pQ|<<8u>#mM&}5WUVVfKcT3%dOqiC+)WE#8L znV}q!&MMy5=v@~{>VDR*UV72xBh}@buh|Qk5x%nss@pCRE=mxU&-X{Oyl!=Gz)$pn>v!pL6+PRQ%9O< z(IB%IMA?IW?~Zt4eLj?3yycJvx6%OMV$(h%#Z+glSY$85GHm7Q#HX z!f(ou6eGw6c4mJ{E9F#aEf`xX=ad;O%Bgm(=-MM~!iX87+C|Mtf?Caa6Q_wDNftG6 zNik=S@l=}LT55XHU0Dwl8sIPSi&u7h%VTV^iPI+tToouWHA}<-=&`cD`q@?Vo~J1m zI+?fJQ&@`kskAMTUXK78{nkUYD0oX85d&^AN6~1zxGsrsg-X46@ulHf&!Ph(-IB=H z!NyLDJb8-dhz=%gZA7xLI}udv_LZR$DYu0=0l{QEiUluLu5Mwx!Qd~h#tsZu#jtzm zz^o(;F+rVJR2qyXB9dT~Z{fu#%51e7K-mb;U(bm*`y6+*7_A4uK4{V;t4bj^DTOf5 z?yOOx7bu`}`G##ft!7|mME1C06CtS!53!3AA<|&Uml6hEs1vpwxMGj6lBTWKO+>j` zH;`2;8MQ!<4pmd&W=n)l9g*sww!Oy?%BN0E#Mi>lRHzuOkqcYKC zjfUp?F(omI5d@sWP-}d?zc?yyB9#^;XA7|IjBUoM=X}hNFeuvYT7<}`kv*db-7pwO zh(dWi+PnS;nKH^+l10@P`|w^d!b%#C$wc=6%<2s++6>1)F`6x{4`J0=uFoexV;;;j z9LEUa^=L$p#w4tou!SNYT*MZNb3u^_0rw1qO^cAwx3$GaG_BNMKF2T*A)8ouM7X5x zL@3f$L@10x1=DLnRH~SoR%6j(NIL(sB7 zTw<0fS&TDv?L9Q@3uMT5su3CH@F;zff#$l9becZREJh@0^GdNc2f* zGO^IYd_atSkp-?c#ei;z0M04Ix>Pw)Ko(A32(f~{l5-wx#Hl0B>QFOL6rr4@ z74r=-SXbnc#esCp>Tuk|NKqwWRuk~M#d|;{s3JH~QY`&~NmPxy3OkxuMq-W50%@(R zQkb4L>WTRsn2M@`K^|2!lc6B)%9;T7LWC@BRjMK)u@(|DMJZ63^SP>LuQL(&+LrAG z9TQ7;v3cn!F2czloJY-=Alw0}lchSDaFDwoUa5*X1v8ACWo6HDXJwQrIGYg&l!-df zN``hWE5jDGNPsMyeWP|>mE6h(=UjHnnbf~J9idH0qCJ+sQVi5pRI zR6UMO1(8b+UY11!ay6pN;?{&JN!<+7CY^H)0?_$_>TJ|VYKW)9T1oLXu^ zs7Q|;S?m~KRN^Lvig1fbM>t$!7UWZB2|8H}su4^~f_2nbIcR3R`b6<6MMY^@L8xFB zA?ogjsUE3`bJ6TLZ)GSi;%VoRY4+UydC_5Ncd85;2Rmj&Jtr$(x+9vbeN0#?wdETZ zml)tn&_JG|xmDb67CAN0vBisnFUIby4kkQLTp08hW8Bl($RN9M zBsL|Qs5}a*zRYWJP!5wdaTo=Q(7=2gg{#pHcp1`T(=km039VS_#SzoUz$#CoMo8*} znuRv-F>@H1zg$0dx+YeY6?%h{ zN{CGKdW*4*DbC1x=P8vq1rAn5R$Q)W?tC`wnDe1FA$8AE-IK)ejVNkS1q@`VIC2b? z$1C1qWtOMPKNr;##TW2FeW13ptFUj&G$49{6s<*MznCl2Q^Y!zO(Ts~B#hRL=f|+$ zSE|Spp%6&3X2wNg!rp1R?Z$CNjCD{`w~PV{C^$kmGJ!1&WDk?HQX0-y$ZiZ=(E-Lf zV7&2@+~d8TIoL@?*7fRPy`U43jLMlCjgDa?EmY*#h;q|at0JUaS|RzER>)tT**}Y8 zgLDQ8+Q63eC(5GM9BLZ-G+J?aras|)iB81| zFi8<_8u+$aO{kg`jR`4E{4)!BC=T3VEQt!k-l<>TkHGFq3(-15B)b7jzcKI_1_BCU zZ&AmX5YCzDMQ6|DYXuw|iYyK#f2#qE5ej(mdjRaM>+4AwrA0`Izm%`h%fu#+d<~`M z8iEQX8Jkb!i)2ij^!x?~y9p~Bj&{gLo+{uD2VNR!+ht5n}w| zOe?)pi!am?!lOF2o*^85hHQOHkYy1&!$mNO#w3wT44B$E3!e0e9?g(;F-@EzwE8QJtnDlhd$+ z!(tgXaAYyTBKB`EePKLeD#qlg3uwtmG*qu2QwD}zAC7!#;~GG!7c-0i?x`biOlJmYT{M*$n~N}r6c5s0#sCbN<%pC&8)PRlwp-; z$_Cns$DUh6HDHd2Qnn9~(mYta$A%0?o3KT!j!M+66;ze`tnn)EbsX%dhmPcfiV0kgt zQ&=gg3Wb!!P*F5LGgFH?pQmQT+1auX&#VGdf2p8aTlLp|*oifUaFpP$Hr3sCV+d>^ zhfRYJalE3^Uyhd&_{C&Uol}dk1gKcgz!fUO@FG0;jg0CkgXvVgba=XhZZPB%9Nubd zzB9O#=XK%18{_$OxmJS{swx!0JRky;Nx>?sh^J8G2K_(}5K4aZz=E3BBozDSX+De;QM~0r3Nukj zLKHfXK_?5Rn?%-8-BtMUR~AnFh=bjw4AqE&LR=JG&qY@ogmM_ z%83AJt^L--$jjMLagduSq!NpqP!jRc8L^#W6p5`UF-}SA?FsnRxW>bHlH#eX#JaZ1 zUs@bR5nU^qqINIR67cJ2xRDBwH!uyzj=q5@Gb3Z*z=61d2^Tb}+yx0sMviw`EEYwA zMd__XFhyC7`l)&+ZDd%St9PA4giLBWKB!Bhg79=#nk!WcS9-_19|=!r?2C{L!LL#+ zjf^+g)ehzkLmj zZb>sXq-Z}Ss%`B`^Kp$V99vW9uCu5>qWB*EX$gby&`V@&uA6Fwg}t3{QM{IK&BDLta^7|@nF3U?9JO;PbH z4&p_y{TyM%B?J|2-b|p5KVansCDt7H4TzaVuAQ{GXR8+)D4mh7ghWMg&~2wE8Fbq# z%22u;6lF49R06I?6ZmYp;0tK@e>A?*FH}>8_Xcn)gR1M3J}6qH(oalP`f+{H59-^G z#D1&+^L-7ihswhDqYdT`bw6l+Kbky$VHGARD8?U#uHa-lN{v$OQzW|7+*FNEYOa}& zG6-kjp*H>=AKS#7E_F$Q>;Y$j&<0PA7O)C}>2HgNJbakmL_9`$+km$w4_DPi$=hp? z$2ySme40GwlQ0~@D^#!e&$Rf=#RCyk9RUx4*k2-W$wTOfN7f1PL?pD79t|WPU0LT0 zY6QW72kNYsISGE&iBTSrYeADBHbKCO>yuNb)|9o}P4bHy~rTh#GLNmXu4at?LHh)~;V zrT5r|_~Eff>}}mYt^Bdgk*nj5Z=Tc?o3O1}>Coo`m+Mkje2+BO-Us#w{-;m9D}07? zYR+}xGy12w`nX;dRErob_dM89ldAOYaUjjL=K(bbX|7ivP)FGKb1diXZ+PWM9MX8I z*^;og;jVB-|NGKAHtc<<-O)jrox+y0@9W#X;o96E8`^Wk>~C9>pt-S{Yt>xa+IA=} z#?l-9J&xHGm{|j`H${Nsm=SCk)z~kntH0pkIMvO-Ks|Sw@IfYAi~yQ~>%)(Boy~8I zGNzHoJgg|n$w(&wgJ>_!rM*jl(J9vO1)6&eU3AX18opk0|Mf9I%lv9+XK;QJ%CcsL?)E+Bzv5$GCi z@mnC&g9f$G^GF>v+v0l>=>pItNO`oyT&T-MhHi@ooyZpV*Op$tZ;Kb5&=yZv90OZ? zva6dbzW?bi+nV^a`%<+P!r?vEOxIwsI#8`~U(Ie>VU342zz+XBsYz)=D;(dmU!(1A z%Nblxs1}$j6xHf-4U*(ET3yG1zOcG#Ds7I$Sr64r%{er;FYK>s`{M^%25lR;|NZy! z-9KIZBQ0=3J?;GK2fzD**4Da4wXSesT_>p4b?3kBt6j_kq+Y2#B!abOUoqVkdi(}l z+Sil_yF%SP-GtNLNnkkV%o2zTdn=S!x3=QOFxXnLe{8x%I%O=D&ua3)hy%kQZ%@O0x z^ss$cns0K>j2s7U$(fN;;Msux>pUwDqs=|fS9nAi=26UNNJ-;sdFTy^Je5Yg>QLYldyoa|guAhzQMg|gSO$^*>D(_5f_r$nXIeOM_#FI?AN~;j+lL?V zC=Tu@Ry4&Zfl>UgVx5JHQ9Xk=b@Z3x$B(z!65g8VO4yRJ<#fzVVxW_;&`G8o)7;rQ zMBB66wmsdIl$IB^rF*)^GeIrK`hAV_tPvXnFhc7pfAA3Ap$+tyf~uj{aPGIJ@9w^l#1Yc!g9NES4?y5bjvx`BQeP%Crmorr37*UaZO7`eYp7~NSvQ@ zvVT6Uo{REhk1dIvpR@bhxLz9Ox;$r4`woFYiIW0@5^e~*5r{>e)cEe7uK8Y-Z~-JB zjzI!S6fI#cB+P|`xo^zvrAv58%fYRPj~IsdubSfr5kH9d!8d}URtjR9mbgAe{2m!s z9=6%v^shKEXV#?zWl2lB4E@t)H6HrF}xEOXAp2mxOCVuZ3)zxElbgETPZD zdT3+bRnW$hTx(tJVVOtQE4|0mcWR33Fs8xgOG+QJpYz|I4OxxJVawl>!j|)(k2VY3 z!ja!KjKvOzPuR`3pux|AGvR~T-4AtZ-)vJH`G|2kdd2DN6;=83^5^Go8)@6$t`VDY z&7IpgeMEeE8$}d%s9YQ&LrlIj(k%k$w~>{C4r*g8%VK980sD zy=cYCk!gmNe8qHs`C&lQH5hJDgdF)*hkx)1W!2ntyCx z@;Yk#=jVI^-ZaP3!&x5mrd-XRig;<7KUcXa72K&wPl4P0)AFAuqc<%*Jk0YVdXwf) zMcj1FKcXwRof$u2B>5h))xLzZ^CxSgHsbZ19PXKe5j-zx&`an^?VBy_FK{V$CA#kX zLb<(7bL~ZKnvV~$tW}P*Id#%#^rd3NogCA&WaL|$lOV;L^IkX!|B*`})qZS=-JA0q zEC$m6mS39Fv;C^DGP!5ss<35p&xBQB>*Q5oTTW->O_j7)m(&@OpwR|NkZY1;7m{Lx zq*zFb%SqHF9n>YIK+^M3k|5V4DPBlwBP1n2QrjF+Upe-sC0GGo^HUP~)g>rjH~dG5 z*m|O$q2E~`o2?>x+Bo#IRpCTO7nHD_qYFyd-mxm&Aw%zRVm`%gN9%vXCS{G%_xyT0 z<~*lh9GHYYH@Uuxi@k1==yj9!Z{C6}p61RrXz=av+U_8GOpgy@nvV}_*BsZrUGwP) zA9ssG+pKgQb19Cl=#z%(@R}b$zPm+V+P>e!T0~ORZIBzmVKCe(8qWT-UwyCx+dtSMI_SMfEmZl zd1Pf%$-|CU9v}L~;*_x;6)nr`@>xOu^KTouWNKmK{NlP5kIs3z+q$K{-kcu);|$;E zz^VTjdBatuAKv_Ac;OW1o1uq3v5pH~c*E(3`aU$hB(C?7&9>*ao_+hm7q9$F{Gd0F zUe!Be^Ivv4pPFs6Ub5`uzomEk>oqq$UUPrb(YSB#`NTi*^<(dQ-+neG_VmK+hHHMk zZ}(kO`(>}2{_Xm#kFSZTI`aL!L%v?#_QAgwC3!(&U03Kd(LYwy$=)vo|m2>iKuSH)}`kmz!2zdv)C4 zi%+&&pf$Ha<_!1)_n3=-*vn48qJL3P zzx3oS>3`ZDc;KheH+M`c{%X|jRljWPa$eKA{bw$|;P0#Zr@cIJ%vH}d41Vv|^{=iP z*!Hcpe_i_BeZ4J0Cgk)4PVA+3u$5$#?8^&n|RbKJEEV*M0xxw*9Lw z`(a4G?;d+$*|W8m{&~@wdjA7wy-x#wSp{psO7_b*OKDlfY8%Y{!5JJ6=O*Xzep zE-JS?dHdHJZ=bZ>TYk&umw$fz!T7&Ellu9WC;k1sJHB}8je!+o-)i&c!|yEG)aS9y z_hx1~&+D6U^Iu-tRkD7-pl2pl-uLRVUpCdRZaiuK{r$(TwKxCp{?{YgPkw#L+27xI z%60Edm^We5-}9zTcyRAW_FF$aw6)>xt}ADc-*)<$&n-FM>#`rd?5;}(tg?hEullLq zh_*k^JZ;vNDf&f$x$9^UE5H)lNX#(-BmPyX$* zZXXY?8@1q~kM`YpUg+rbnm0P9>>TuR=V0X@j=Yubtc5-6RPoBAU%<$eLZfSRXu4Utp54R-7-}&j|`&U1AL9u7WYYPKU zWW8N^N$Kd^3%Aw(rKsqo6^j;y|1^L8H`gs(_>hP#$6J3Z%>SFXNl@#3FO z`st^?AGzqF&996ab$#2Wrk}rldk+y8YuHA3wTg%{jAs^%_5O;K0%m=bd-C^@kt6o3wZD@=LO_eaHUtm-Dkf z`>Y|px%tO6bLV~%+Q0vysW{$r-bp9*O|;wNr@Z{~EncV7b=eCq+|@fJCF7o3ZynSj zCMIF)?YE!ZCN8f1z~tl}chuH)J?E28KI(Pk$m{+y&Y1Z6=FLm~Tvc_rYnwL7cQ0Rl zTIHE%&gj>rOZHh^yAIr3UViYKnKK{%dEL5;zn?PY-U-8o&GWqT&XaF!*|KT3RW@x@O(JA3w9 zuGe0>?!9N9oi%RAkl=@7$KL$b`0;DMoH1k5*uVbuiaGW5Pxt-%-(URf!3QUu{PWL$ zdoeHX%6BJBxP5y6{v|hGaYe_!jvRSS(vLrW6>GKHmo8cIYt!V(>+**WuYT&!f9`o` zLV*}i={$G`si($0yADFZrn>UH+lU%&9N+x_hN+in}M zH$Q*nkW)_Scj4~cYmVN3|MX>-U;fLebIw`taB=aTFD||Gsq|A%&AKHN`g~nM!LGGS zm!8@%Y0}+G4j$Z4l9rZ#dEdU%9$LM6*z%Pt+rH{@t?Kj5H!r`rZQGpTojdog`S8R0 zKe_C(XHIL|_T+g74m>ifvGKqKSy{yshYqdmo{-S_z?d;NCB()imiFi|`qN7;*>>sk z&;Pmn%{O=a{o#l6?kgWeeuQHd3*L;d(oglnz1@zg@R);>0@}A9!HQwWe51q+US_2{Fc zQsU#2-hS$-ij~#XO%*+Rjt%zfcS(8h(va(wmGD`Vbx zOedeEa*36C_I&^<+)24;LOq;gxou{8J8+!7|d|iSaF|kD^UyX1})`2Gn zPc0s9K@OY(XakrHcpH!bz=5TKe*y55aytPzfDM4V0ZRej0^SAu3|Is>1Q-HX2Dk%o zHUP`^<5vN)0oMca0QrF3fDZv*0bT=411tpG48ZBJtos1Z1Dt?!0N(({18xDd1>^#L z0=xj22#5#t1Ka~B0rUpE2j~IV444PF7_c5N22cjL7SI6b2?zqhfbRjtfGYt$z*@k` zfVTkm0xkjU1zZZikD9zs0B!=50s?^1fXRS=0R9Hp4tO7M8Q>&<3veOee!z8ry8tTy zs{#7~4*}2~$|}G!fHwgNfFA�%ibW0NVgt03!jhfO^20fbM`B0Iveh19Sjb0nY&* z1MC3U062GM=>)h2Fb6Oca3i22U<64vY;4t7ez)`?o0dD}B0FMJc2ZR8(0v-lb z0@46q03HDh09*n11n?tZ5a3HdH$WBOBS1M|6rdT<1uz$I8lVs0MZnd73c!X&IODEyad<>r~x00uzPg4f_CEK2Q!ggSr&^}Oy zs3T05bxK=6{bgUH{?QJwjFiv5%6elvQ+KEv>{INI)JxU_`xpBm+lXz=KE!%r{%MP- z&+G^6f3zpGO{`Pu4Rwb7hi%1v#(H4e(SFcIP*^sy2wk_L}ZAjf@-Lg$-E2x*W zg{))tDe5G3jCx9aVO!B&vaMLZ?1$7<+CSPn_Gh*YZ43J{?ErOQaR-`Ky|$Jn2#7u0jwSK3YL1$C4CiT$4aiG7vpl(rzsf)DLwAr)=v{AG* zwB^((+7{X_+9v7&^_sStHl6*3eS^9|8zFU^Hh{L9x<>m=8%ABDeWuN%O{Ct_95@uLYvC5fVPM>fa4Lz1=?MXFSK9mv$TyI3phq_yrGSz z-Q)Pm{>t%);}2~z`!2^bj-ebQIlj`?b8O`pN}J3+&2f)5mE#A;LfTH+ACBL&)f_iD zuF=NRcF-G2I{1Z+435FMT<?gHX(Npv z;xqAUVdg0L#*g!vqG(}=Qo-Zt{irFr9~vY5LUSZS4buJ4r2kcCXgD@VK(`k_o#lC% z9D%|9L$BiD=ebq#@FAX$hy7oU1f}qg$0I??%K^bCZyMy$K2RRxN_iK-Z|>)@fhV5L*5L~D3AG)@&fQ1C@+b2rj`6{G0FSd zAdl@L<-KW=w+oqg3p5Ebe}4tRn7>~jkD~|MgQKRD_c{E`pUl$DvT{KqNO?!$kK&Vs z6kHL$U(I|-thdHPN6KppVMck+fRAIM1nE7+#J875Rt6fpPK^)dRlr^hq^+GDI}?0R z5vrkpVc5C)BWK4tJJjD|XwgL8={J^vcmVQpkO1dt7y?9A*?w#bkagIEA4dI2$5c5) zBd2t^+QV=C2H?kjg9m2}7;=KMV+dC{fB&rRHfBnR{1z}1zs8E+%Cj#ib<3-rO%~ho zKs?T|1h&SwlEk^6raHUJC(hx-x*XfpbG3>iup8n^E`4)yk25inS@u zYH`|rvL1b*+IiS&eLj%11MgTz)Q5-X_w*@8hmX!5*`+?#rJUjP75Zit6#3G8T?)=9 zm{qX;>UYAUv+wBg68@`aiqsU-U1z$^^5u>Eyn*L}2BWMwN5|(dO_a3`{#^AuP|t!R zzM=yA(cyW!!^5+8hjX$WuE9H$t;;v@-0>q}dq!-UU*y%c>C&+MXV#nD)n;5*-`&0# z-w2=ji;tYTLSL#@FNvutjdj9?h~H`h_nYCBF-#pbe*zlcDPO&5?YPr?vwfWwb=|() zmzb^`UAD~bf^S$`_!lkv=*vz`yDVd@9_zi9Etc(;+bvbfbW5z|bp4x<-rdptU> zyKYNwKk3jd%e%BOHqCzY zoe^zBp5F?u7|>~ZTj!k0q{a>3mnM(yTfTq8_p>^kJ9gR6w&Vk+yl|w@c3^Y+^p!i? zgXgVqx6YR4WZN~}jJ1)X{ib_b<6Gf+yV5)1z^=`#gPVcTT(hwHOD!``dE16Q2a zg%dL-9oy62zQzK7=M3k#*fkq`ack6YaEq8(jcZ3`G+tNtwvgLSlY8=br*DnM;q*az z)(heK9!cX;MEO%1chs%%-7%+aU6O0#Y9dW3^`|HRK+vO|yD%3MOOisQ4W!HKsmIP%Ro&waekH z$s&H&MV9n#qF=qZxg976d@~n4nA@rT(n{t>sk)v`@-7-v>ZW=#LMlmc3ZS8G9;qK-Dj=q9>RGm-;7~zYrMkThv(OU``O-B#MqV-|G9oVfBjeyV+0U|NiWs@&CQ- zVKpnl?`JQ>{{zAv7ZjxXX8NXJ4$#||=i6Vva_@o$v;9sQyZ&dJ(~233hty)T5`Xlf<-X)OF3O!bizN4S7=iD$34}S~8-@$X* z%IURn9>{q&ak|(#5c5gmjB_3y0&&JA&!u=Mmk;SuzLZT|f($!OM#8`EJJ8p~YK7Rv zyqtB(H4*n?jllg^u7x8IE7mgPpNNP1uSVc~2+@>yeLt>;;O9y>0^Dzn=1&lJ1W-OX ztwLLj{D~%hu3RI){nluCT%<*S`>oO8T$x0G`>WC6td$6Ge>FP1v-n-=nL0~n-A51i zOQZQu7W|P^xtNOp_dlcgQ%w9^!A5}lozeVV1b<|`sfOq1QPglBJCXo3ywrdfgWe-X z3pVY>+OT?#POqCtZ@6R^t7{!W`MaC=y9@sDIt#5r1aPL>#P1ON+o%u%9Ga^-HtaM; z=O<0@N3HKneeG5`~nkR1)TCI zJDG1%upuU1EXmuxv~TU`-v`1ibGUC!{}ioy+Yhgvn`0p2<}_S-7p%ayeY_sLfl{yD zbE~)d@l91_^<4Knf4?CE1`kv`_?U>7-b{MuRnXgCaH!+~f-IP`)QEy*kcc>k+!-pj z@MpJvAvNB1nQtaCUOyE5vu0U#akXEG2UopmpGt2Tz$=Wzy?Xp6Z{)+#h6~j24oH3k zgO{4>HK$f=Rk>eWq-&~H2I6bo`0k6Wcs-t4zi-^ytw|5x4Gf!M42%YxAq|c12ZU+SN&$CrY1<2*@ZMA{si9;5)9fGsr6dNebL?` zFRq=F%D^Jxe#@vYtQv2c=VwK$w2S0%c^q!xMAC+2*@(DiFH#3=Zc&-$Z!@c}{B+F` zGGw*Lk0^-msLCE9uAdYX!)g%-Uz-(lfO)J zucwQJ;^!LvwdRKD&cK=!#+Y{_OreW>mea+gv09_A)#%UAMgEV{^b@i_-){%$ATuP6`FMLfP6W4tFccQ;++V?SNQI}_s(`FVE=`E%)lf3)UaKo|U% z(ZynUwnpbYB?+_fPF}-rraMPb*3&IT`gC!)Z zrdxsgR_F$ho)%tCcdnw$qZ>qebkTkr>0yn`pOrPy<#bkdM-NATgN_UhZ=7ZJ|in5pZ zFx+E9Hy`gT=?*}<(naD2>5fG`&|RYDlXCyHvk$TZpJ1{OIe{P|$%QKLpH z!BOK!%Wz@lTau-vmS;kY^EpW~%o`T-$2uE0dH8T#GoL9L4H`_14pJV>UzEIJ z$jbnY^0@CI<-y!3FdO<_$XfV&;E^EZ!R$uKyBYFQK%+e7OUkK!Zbdj-9R_WJ7_3x zvc|{qjxfoq1>Rcz&N0dR6!IL1&+<}VW&SQU$>X`d*7W&8lRP_=Mb;nZWl|pVgFjvW zIQF$BZ;?q}XM;TIp_F&8Ngl^Y?pH`4A7PzIUJnT2_{#Ej!6W6pV3HS)c~}m@B}jQY zP4bSN8tK1mKPm4Mlf2Q8H(3hP_=tXJlJ^Vb^#+~!>yAgtvtn)sR=xh#LLT=MB*^^z zYLeFjh1>`x%5&h6@=i6$tA&!W-6TkPCxOqXe>^ipeWScIJW}4-nmjlHR-Zs#o{4WA z_*&D03ru{EpMcMA;@fkAbQhcW_Mbo=&&9D?;RyKp1oG}R@%?fFc~6@7l3?^&tLN7= zz6>p02ly&9KON?Y&s*TbbUQ#A;Rf*aSH0ptc?ccxfKP|F&~Be1t^~_B2eSA2d|+w+2>?Reob_G0WniVrLa4(#%HE4}#E2tEkI z&l83!#rCH5iSIz=97V8+k3y-RDe<-<<2QrFM~40QvPz4H_y`6+K-_AW{5r8YP#wn- zw7F3NDvvgZF6+(cO8P&w#uazA|9;&?mPjY6MH9z~YRJTp& zPEfrjt`k-C|Jd~u*GcSG^4)DV{6^~|{WDB_gMqVON|1chK%jNW(E`54;7DEL+=Et& ze8d;vF{V2jIOm-bq`Y|`808%^$fK2$^6r7(D6arG(~%(M-35YC-Y*7uq)U0P!f%x4 z1KwJBUoy$#-W=0qd1(`+yl>z)%Bu$6n!LlH8}r9I@})dlT`8|Kgc;?nL>T!bkdKfA zCZoL026?p7Qr;jaj>Ax@uJ);jX7IBTfjv2Du0+$s;J)AnR2>7(ld zkAL878&I6r1|~aS3mkQ}oojWDn`?1iJNLD@_2DzKZA~3)ZG*YKec`>1%xTT0q?#Ug223)Y9KR(4M}(A;mc^U}^56SQ_I|rT}hYjqMxvUfY^s+eg<} zdUxK5PdA{vEHfXTaZhxeg`9Uu8yiqk*L7pL(|P7DyX(rLTGe;QBIjJ;>ztHk%`7;L zJoe^p%HSPp{BschWJCNpzKa)a2&DVAgx|{EP&)_G_T!wZt>F+(Rma#41ystG=Hut7 zv^dc0%_)#~O5dELZ`S7dx-L@Em8LgpdA|MHO`UN+VT@~|I29NtP6a<(i*|M0+ni#F zMLgzblaQvK>V33vn=iiZXJ6OtDt=!hax}Yt-EHBO8T~cdosGoC)%Ee+7`~+E?YW5! zmZQVZr=-E#(|xw1!)LAuUvkS+xYw{V-8wK;78i!%@Pd-0;>f#oP^u^7TYHkDY4H~0au25;?_L)dMes4G6Z%~pZrrB z)9b)zQJmySZj6qTDzu0iq}HE|rRGDlOImIK>7iY08801C;$xh{0t;a;o(}vJ7&dp| z+^6R{af-O>;x)OJ`X5ijSF)Tx!+H!0#x*Czs8XtfvRoEdoM=&%ZgCa0+M+Tv+TE7B zYUj&Gc!R#ura;t7j4LCm?w2*%wez+83SJD`Yahe^#MQ{VFCF(HcU_!b#d?0o<76+H zdITx}dw;*A+|g!^U2L&E*k(Rcfs;V(0k z{y1Ta)9)7N5zsH5ZNVzmnjTj_Zu=Fu?=%*4y4FUkaMkkc`Zh=L6+0nyTXz5Lh()Up zYfm|f^C5)1?@L~6=^F?oa>3t#SM;a43q@!3w$oL^^f-REPS&-O}${nB{-v<W`?(@}IBGgtB3Z@IqwxqXj=<8gG-?CWn-;xpEY>QP>TN$=0nHW_^ zXf+`%p2aovU$R^(B&EKwVjomy?Fk8{@E_h2Z$M! z_@~}a!Fk+pJT1>Z$C&tIlvMDMKTGhp+!MhTn*q#wBJn19#@RFHn(FuCA_6!AjR5yd zqUA^LfN;+wI=tncNpyGzB@^PgZcw4+9!fNSN5Q{WpFErQSUQ=~?x9o(C?0{QD#Vm$-3#K>RyX_%9|rS+5&4{%IzhYZD2SH_n8Y zm~g&hA&KkSWbBcx(QuyAOvawr11A0_P57%CeCZ|d2H^mF+$YIL>W{4JTP|S z%7jqUX+<9HGZZ&~PkiGf9KP(%O+UV!=dn2{s$zb*n#q<`!q2hduphrBD7jg7ejmJ* zAf9>?$HchFrA3g38(9u<8q$!h$k|764lZ)CPNt;_#5d-nN)w6Z7Dpx{$J$sjoQ-3P zMT;oMH-W`bQ%xZludu2_J*ShVzW+*aQpd0-c!u`P83WGuV+nmVdOFTI5y!^S6wNKs z+*-P4Daw6x`{6DTjs65(#NVaid+8#bPw66^W17p(LcBfQv(azpX5bx(Mi=LPV5GQ@ zz`IEaio($zF@N}rloR0` zhsbvUUF27k6Zs1ghr5g}^0%7qK%Aq}_-I!cp1|~Eelh&aB#wNGb4$qgmBf+n>**rj zBEQJ@M&iiKX1Yk9=bGp~PZ#;P4E>zv*Y2Y`NWHU#;hTxWeTFXLb8KMwXnz=w_L^?K zmcPlGTR<1(*hm-QA8Pck=_3E4U504+N4m3_50oRQxuX72p4*9|Jffaao(GAeJkQb{ zs-3foA9zI2{ar9h}xSj0B)3bFK|8*q`Y|`82Ls6p)HXh`ECWl$j37%Ig$yk ztXsa)?6=;Wd+K|u!+yst-A>DYIQOJCSBHJd%G~&dtWNneyWO07bid+=Z&Vzycgj!o^whMV8d_T1^FQmW za9*vegKJlr*qQA;0{7s=H~Z}S!!{*W(E+gapyxWdcT{~2ft_@Of~E{pgb|KK{CD+kvCI_ml~uFgHZ z0cN5{mbU+DbB*4TQ$2Zmuf}D$J?imYq4R9mX}dFb+spU%A-Ko0LwYu(F@m#O(qD zQXBErq_eF}ORW4tm?iTYq?#1A3QGI1%@NACq<;DZzaT~)g*^TDe=)?>F>Dp3 z83Q{3>K==ob5s|uEU61`451m5q(L{H_d#MK{x{s+w}MNGQeX_m%1lG zT5sxuaZcX|BS-l-A}8j-|0Xa4e%S~B>}kvGzn7P zy&xF*N`aJ0Cb*Jsw~22*_!w7$DpJLE>$m7|1=8@ws^T%?t46+Zws~m)KTBQ)+(u<$T zmsONUmUr^b&l9cfvifId^R&6c@%wjv#;Zm_KUdxvtMYZt8Mqm)O}b@&#^AuHb(bi~+vuL`rdTL#o%bn^Nc3|^m zxM{9qfU|NX{rd)GG**59t{IhTu1{jR#~mmTKBafe0jzP72Jc`B^1rfkSsia4O~iM3 z+iNp%rFZ-Rd)U&}I^e$C?;6ByqBzea@`ZJ!Wr)(JE~~c+I~(}P94`Nf5g$3?YO=3*u6(4 zcgEABOZ(izHcuC;_1F??LSEMdepS0up7l{z*nW;3XM5B&g0nro8d~VI@~fqml6-hU%q*4Kruj)(F6e z8{j|wZaL8;KT({Otk%;=)P3~uE-vOb0_}x;AfAfHA}nnrl;`=4z_mgz!Kc0x;rV={ z&7yr7s)hH3%auc(hO<6-K7XQybNuDIT>Knf*qmB{C+c@O0TuRYIXxOszM>f>_5Kxa zbyj&*8Q)j(wVu1Oy0YJZ?7;)XtW;7sh32f<2j;>WOj+TpUj+y~n0>@0kxp<6x#lVL%Km*A~-8aq=cz*)*f%5}e2J&I}7WdK{ z<*fsad66LHoezRh-Z6tbS`sPG2ftC?Uf`{jqu3%pcQ{@@n8W%KHU)Yx%p$ z#Mc{|$4OOW!O0|7yL8Ohh$9P3r^ zKvqD;KLzt4qrIXG0^bS9G>P*bi}&f#K=RRznqvV;!qW-#-7T_Z)*qN-?d~BG)aO{ea6x!r>oquMer@@%EitZmG4H_sbL^Ks2j&Mu z#P0VIpC#>m?-5^0d4||!KkUQ3z$J%$Q%e#au{dMGJ^q%JNjB#!#gQQ98>_?F8O|+%tq&q~oTkPZPoI{$FZPqNOzf&B z3VE|#AD5|T0^{!J>r3|8QqNz}wT`}vtEaAz=YhpJO3VTK3fVlTeaEI+*EO#CaMGsn zzUzJWZn5+`^Mj)5|Ht0Dz(-YG`{U=#nRg}+NWcIACNlv-f;f<%yeva99OWUwR||+G zAtX!)B!PfNu#y1s)&{~OB%mnvR>Z4>M=RokR%+?P{w8WIRJjhdSL~#{o#A1p)?5DH zwa?izXAZH_dO!dB>+jP&pEK+1z1Lp*{WyECwf5eu7m9M<|1g@d%{)X3lKogw{|{UZ z=fIh1%1=K#7W>8u*LiM-yTOxh?<{W+^DSrdCdvc)5FaQp~A{Ir*&FRXSOT zdGv8@Y@~a8!Es89jitq$kQQ+WPs!ViVvkP5?#Vyig1haH%Od;NrcH7A#&Ny&#$2?`E<0@W6t)n8nH(P*v;89*t^+vr<}<+&gsn>QBEq%s6A^$W~K9f zbeShTY)G{SX1Jvm>7dYJJUAO%qzWx5I7?REG7s?b6cB5(hcs{-!D)LYn&xQ%hdwxN zRc+ihHLN9=ITd=+yPCwwf5v!*HKJTa6E{ zEPWK)X^%Nb<-alT=mp`XHJ#B5{S@uYYJUtLJ96k0T78vla4IJj-tbQvMOTjlgJbqn zjoEG9JmKOSjeYU|lSWgX7_g%i;cH`o(d}MfaPP1DsL~!x&NBr1=6S;}HF6(v&u%x3 z>ji!I>l#1&L<%RZg&DPWt&<=(_mkG@B2SO!jFDB6F|t;`*Frz++z)KUjp+h3DhE6! z{Garcqu#WbTVj}NVwlMcb2`otfH@;MQfy9+the-zim6Ke=DlyhyErPl{b13hGp>V( z6K7XzO_>d|8;U|gPr9kyQ1Dn0zLzvH5B@@Ww{}A>Ir6lQ-oY3>C4H2SiJt7X9tV`M zAiMpss2AF#c|8oKa||{n5gf>DS?s?L`|r*E)7gI-E0u|!FQO&no(U8j3bQ*Jj^c~! zP1LvhK)k*;puT%*>U&M&UdYP0+S_omK+gki)(6r$NC&!cgsa_~zx>S5P9f*9=r6p< zk=rcb^S9m*E8T_fTd8ylUG4i(x`u=1$Y`06b3&Q zeXEqPlr+0gra$+LlzCo%iS&=_C_Es##pq9N9VzFr7XH$4!TEfLwzN}NsV1?1EBgn3 zI%+`~d9Pk5R5Vf38EVO~LuQ=znW|3tuOj7}J)UfZuf0kB{$%B?QKtSUgkNpi9Tn4t zJ!cqkJL|oNK5P7QNoZD|m*8vYv>^t;)~aGCeLk&&LZ$?UwU;lrsolNc#`gV{!`nZq zROX075y5)j^>zeuAOGt`=iiGuSU--}b8Qp#^LW2r(3Hmdb$JuT zeLA|V*@zLN4qW1=qkfEwi}0LmJ{>)U=QN(5H$N7Aw7E*|p|l8;?pn_jxrf@4DBhD7 z3z{zZXVtdWP(7`UUdHFj!3FNx>IM62x+*`a$zQND+JA%y38QSJB{{NLOk-t1_aS)0 zeVVdy0(bevz0u9hkZEDH(^Zom&P*K^aM%2-nappWmG^wMx=+3X~sv%i?W4^N!`u3sYKj1M& zUP&@VUU8sQQtpcyQ>WpX*^z>AqC9h9rq$>4rDJR|sN+*gBrWB&h&T0jopzi|QurTX zr20|x6dvWyfc$D9|CYds74CVKNK(?vD`u&?H78b7&m&tdldpMsMgF!~veI&C{1sUK z`?Te=e&{}+lzJ=XU-to})aY#p!xG(ZLIMOWJ9^u#f-9f7y@%E=g`afYDO&8)yj zi-r8JcT$>_)zIFk4^n3XI-NDDe3IK;Szal+KdKm0=tQkg5|#NYv0k!eZLL@}DUHYM z4tnKmJOlCc$J3AX%S`s)i~aWuDyiDkx%GnbwllEy0`h*je;llqliwVUHR4N$DxiS{QE}gx!&*%O+sGA z>sY&33ELa)(X?=3&xl~lBsUvL(_LVcM4Q=VisXuld`biB+m1l>!Y6U}#p%Ye^3eXq zNRpt;QQq(m8uP1*JG3FFhrHCdBleq{fy-zX1_kFxJRUs5@eIS0hbI@$;GpQt2?}`= zE{}UL`@$v2K%I07d2d3}=@j58z|(*~1pK*E7zx=b*Q*y;+=gw3g}l?~Bd=YcmLlX& zxNLaw^2h9^j5XAk>W-PQ%X)foQcb3BCY$qw+`@oK-e3L3ypO(Jyl(%urFHuky-`j) zGY-+nFv_bBE_K)BSInyhUk=7JrS*KYxNClSO?Aa@7Q1Wu`oNU~pP8&(<$>QqnLCW? zj_J|Q)`q<{Z&4lF^`*Vh(Z>SOj5~!Zo9g!#4Gv5KHxTDI`eboF(xz|;?OPD;2*Dob z65_U@^1fPXH=Jw8fBdOhr7iG1W$Mj|?@{C(v5J!4d}Y(JeMNbJW3@w93c+;QuyPZ) zQf^zhaOJL|ex2)AJ_mS1=Z{y00Mk1y59d9+t0=Yewucu2CUr{wBHT7r>^I=13BfIu z7MD^=q+nlvro6xCR~Q8|M(i&#LLO~hG;2ik)#xz`i`VG4T-o%%p`z4)Fi1P3?^3yBsR549K zt?hV3Og#l@!VM^);39X7qYU?e<$40YflIu#@G*;MnN^;${VVFrKU#FD!o9elQo)`D zKZU2Pd~tmxZnPS4qG&~+V^Q0ptwqxNj|AHmZ7N!MuAlz|*8Vl>O*A+9#rA%FDYD(L zqiEITIWO|E7FhWsM`>p}l$_A}0rg$To&4)F(S4Y;Zo|!4f=9XgW*EM~6kKYv(}UES z47rAYvNCC<+g$SbJxc1goYR2sqV{ThC97X@G?ploduk~62IY~s6Gpt0G5M*#NFz4d zlz0SZzhFFMFN`>Rg?}cRIijzBWNCeAU;o!j2b8YAaGzgmUv+^=O@5m`>)yFzURpD1 zT+HtkAf7v=b<&qB=eJYdm9JRAy}!a;`B4Sk_$IubhBFzX4F8BMDqkia)uiR;1h%8y zSn^DmO$@1qfHHoTFYecY);M28iwm+|?_{lNJ6eE<{RFf64ZBfL+?9XBA>vzPpPw@> zinJH>Psm~=@I5Tbhf#=JW%*J6qy%5uTf-)q#z+afZRG#O=P-0`eeF4nS$FQicW{+R zi03Nt+{Mat%8rgDgLWC?hIp_N@$C#hwsENX$M^6Ov57}3(S>$3E`ljM+1UnW~-60qM>V(g{xfW0^fu zXjJX{5HH;cLOQ{@q5K}x*-z>00iFH4&QAK1xFLF^uZSD6lb#-K$nMeE?}VMwdov!o z!-I5;lYJ4h4-mHK@b~NNf_nH7W0S-U7?o8E6ge!?;#E_z?7`Zl)pH?rxr_^>)|cbL z2QW&4{cu^ipB>k#!IszIp6&`<1_9M;%5g6jwN!a6=@3^Dt%M|%ay|^H&PC7|2ANP0 zsi`PmMnTC}BUXj`45}(0np;+0J-6;bTnX`z%4(;@(F&o`u_?)fs)S<-$Mi8O3aObl zOE8X^Soe zED*clgmDrhiD>6M&bT{o!bBMA2^Bt23ccX#1@xf?BH7Z^qxv?6MbWiIng=ZozxO$JRsDTRUVI%bxTSXxcl3l;s^_E9Px2F@wuehcPwf@74m0?Eg&AUoj8s{3hzKdmpf4qeoFpD(3?OIbbd>) z823I9M7rpV+wKD|A;C%Dc>y#){(C$@S-rpEc7^ZM4)}XY?_~H>c)+y;k&f*IQ65ha zMCJpkdr);xqi_iCRN-iXi2s`eL1#NblrQlK5d0p&X}H&cAn3Dr0Y4`@;-NJWK_@}P zN2^l%bd^WEMCCo7q42lb0a>|E6@+bscN=&&2_ii&sqiy`pl?^{W)Zwo5M+YDD^=ZD zc_V!+-$)`2se zD*T}e-y?|h{0BkMyG(Ev{wQCwkslR8;}J_A(@%gz7LolvL7+7#(OsxQ)*e8IwTC%s zd$?Qehe&^r@a_k%GeI=|%LM1*{uP2KKL^!sDe^}U;|Hys3I8sFz-M{^kYD<~qW;Os z8}`1GZlr6l3Yk07l}mQe9ZRrWEtfJuK+y>H`D*{W2l@(NCwhMu&su^Eg}Pc}8bYpF zH>U|@^SLP4|8zpr$d_^k;O*p}znKP4(f5O8s z#*7}1{KyQ!W5x4jc<36}#M=NjZj9;+jVCrdUe&&~ztX2@{2?5AC_W4Y3G|+a9o;`> zIK6McMN98_&~w6#=usJPdJp5B^2E!9#$PWl3>6PRpfqdg{T%d8!j0%rx;VY3@UEqI zI|_)tzquhiGJEl8>1`RH=zJo2c08QkPw=j#R|vkk8QswPk&fQGpjQevqUXTF=|%9a zrMDIt*M9dB9+^Mj(Wb9(AnF4Fi5`u4oL(=uY3bdL#M6EnH=JG%AZh8%amLe^24qf; z)?-?FBsaf-6I1cXI$THZ6{j*6Qu=!0;q>m&(VLmvy{r(GsWyGT*U-z*&|9XX_jAze zu07Z3=w)Rq?S#@tb1_d}vyPq*g*zVp+z=j_EjoHR8hV-V=kyNg=rw}gzHaJ+=1pyW zhk+gyJf)B3I8N^a9X)3cEw8?U2XuDdR{}kkvQ{K|SsHp0(gKpP{x%cz7QxO9#Y^TY zkhJuCpf?)sL=R0nmcAh>Jpg7-fgYA&F~jNcyFO?$ulg zG$t%3Vuoe%3eG!EtuU{wg9vUdF(HEhdGCfJd#Yu za53YhKj;*InkXQn0vl#~d$qv7_6r=QBbw&BEk&;hRUc;yaxf2F_QBb$%^Dr#;1+%F9J zsQT&ZvPI(7>_x)Xp|Vnv;;6Jj#MUne*JAjUUHys zRnN=bxwcMY_8F$HhsjFGKnD2`!xx;rCH#K*v`F?2?n=ua!bxS=!A zM1QKuxasb$n4$0v#847L|4-Z@JC+sCOFSY$cX%9#ZQTD-aftE<~M6*CWNk5Zz@Bb8%PF za-7o9<>B?(BQ)|>Rg?0}#ZrqpVi&A(55>y#)N-eqiU-v*Gt?4G0XucV0TtVl(72$= zV`p;nscq4+k>oV#yN2|MP~WrT``ASgi*!bI1;)N`h`x9|`O< zN|JsOYOkc1q?I5x90Gub8>n5wo~ND}fh-A&($?*?(C zSX*GnvDf;?YnG{%Q3O$WU`?C8He_P!zSdufIYwWzFUQHp{CKOnmPle_`XAfGLec-< zw2!5o`}n6Cx!Le|@sOE`#_R+{vt{3Ugh%b{UOY5T^A$xY-Z$XkhSR$f4qAE*pob>Q z=uucsZ#mwz^r($fKjenfTLK3yy=Os>+Bv0<(#7d*!@HKA5f+M<8&2;753>nQ)_gU`r-OFM@Y1y*c>E zrn!?FPVWzJ(9&}vQM_DI;m6aL0b*Kub)Yw%6I1cXng%2-JrC&7_($pMfrr!csPvFc zW}XMVd(?LVJbj~8yjUH;qgI1QX`*K${y?IRd({F@yCx$XGL#13#mwzU-<{kOuKblC zPa7dW1A3Ha$_w>HN*|RIXeTrjYuDD9p6yp*%m4N`tmXZ}Nfg+!1c z-`tswui2k0epmgnW4c5B37J)S{h^vs=U@FhjON(5p;i<(qz^FBpO2CX4t;lN4e0=( z_DpFk#6$Y>Zqgu7`<$e+&w-urzln#wJG6a?@K@mBhV1c`R)exsjyGh+ZiGb>W5cOk zTD78rDaR)lT9eT-N_FKksLokKYH3AzMb!fpTIKr0;lZ8NwU0wxV_~4nLDKB7V_75E zVP|VF3|KT4+9~1x&{{>4B1)?+bMg6JgU6Hjl~1bv@0#POJ#oWZseX<(V%%{82}39w z4+)Q2DAfVAG2VLpcrV4n4W~!ry>_l3UIVH+qNknXm%vW+Jb1{Q#-nYwf+Gvd!tQocVnc0HjVg5KG#r-$u&df2X~hu^Iw`O~jFT6=RKd2=J88ykn56NV2t=aL8egq(~LW8_iO-O*8=^?#AhzkkCUU8JQ9a=qmV;IN2`_ zHNH+aEW&NPSjcGH^PM8wxx+>0JJaOwt8_D>6ObOV3$HJztgKYhG8$=7QZVXVvOE&n zHVoqm22NE{f_ubJ%D_Y=O{};pgw{Q4iSDE4M!+CX-jyI(H z{3O+v_4+*M_J!LhH?%{DZd-|gcH1*{BRx+PeSmxSl=N5u>75>)9_4PcV)bq_(Vgwa zW~`g;ko;}h+tE*)hU2!5Vn|)L9wwO?Iy*5zn{l%|5ciYW>e!h@ZfU?sHuXM2;iin& zJ?=EOQ+7gEx7lS@_mpuTp~dVP105jzK0a?cr|@+x~k0x`B0K!I7d%kdIq)PZ*lr2+|#w z4%yYwOQuzvu*%*^y+$Z^mpN+=DKgwnXXWTulWN?@`?7N;XJu)JyQ-Ms^o4E}CzIwD zoLJ}K5eYJx`_~U;$wIz2e5vUV=R2aAhdQEto1c!jEMmlEwFKiXS#Zg+nzoy_ZNeH* zjM&X1*h2g{wiX;dwzFv8yGdFPr; z3i;h8OS|ObVa|J=t4{YFEH7a(e}Y>p4XMU}t3ADAFZ#fS^F2{|LU?0SHjDpJ(E2Ft zTjveqEG?U}tS!G=JnOg#Eu62MMfsBo(M58M4q$e9#FqSap~c#6Y2z3M`1zY8{4Z!S z;=i)VjQ@%zl6s!s)cgD=$~g;cy_@=}wzTuxqnQ~!SW0>~p_hIpT~yMQhFe*!(0JIx z^-d5a6&8x~%ML;czxTPkM@Zv$@4TdQOeZ1J_T4+*dM>}p&9F>Cl%L^-g5PCsD*CU^ z{3?X`^^WLxoQH|2JvxiSqVy6|%ZVj!dlVUu-nH z?2)XPP6NU9XjFFE+QpW)qC%soolD~422|Gi&Cu&Es_#-T<{GhL6j3maY`VB{M3uBP zvdOTuqgvdGn_fj>EBEzunA~)ltKh~dPzzPY96RKnmD^2Coq09X4isFZx5eC^H4dpY z#9bcK-W&Z-C(4Mmd|~LS%gbJP=n>n9fk_w1|?G*B2m zlP;kR<)jy{jb;{pCz^5SJJH_F%6*_>{;JFW`ND@ECzfY=?2c1j0wmws7?^!Q$Xj)J z)e9XTlXUotFZ}F+=-P2%sH^2do@?uceAl;Oe++SNzA%BE=F)w;WlfklAVr+I;m4Px z8?4YNaBovHq`*yb(!*CVnv8Cm%3xvBW(GY?KVxuMQ_{D{KeuTrgE>vcn@pWWIK|Cn zH9X=xW)*Y%&Rgn3xlN9ngo}fkKK$f{@UvKiyhs;zYJT6t&PzymDbhdHd$DKJYBnB= z&D3L)npR^pp;75*G_!f&XB(pnHvbai-N6$y%LG}^kl3n62!|R;J~SUTF*U#QW88dc zw^KS4DdgNEcd98|Bc~maP>+rEQW>q62C$wbl?mC(y$M)}*$+m)why{7SdK{BO^Tgl zVUz7??4MRSqMgom3+%MY5$v?eNwL!^C&i9;;B1wZ7dREkf`SkXmG8e*x1W?Ko zAw7)SoC6lkevS6s)j*^7C%}h%@W1w1bo(g$i&Oa`>u#Tg_E9$Q54PBsE62C!=vmml z%a#~E4o@_O_GuFLajY6&`bM?s{B3M+mcA4J1CN1yQO1MNd3ho|`a+Hyk|9p?r!UmF zAsOOC{}i@=Lh+qO(HL~TBr}|dpT_e0aEzqlzf)t6z2b#rh7-fn$yMBRcb+~1-jp7a zL!o^v{k|3Pzj8z0+|}FyNcL4ayY_pY{CDX5pV8UBr?dZ5Xa7KFzo@h8WomCgJ}5ub zMrfan_%_Mj%{cfKfmdcIy$c9gHDs^R**EFz`t$Q6I{%-*PEMooknY?M zRr_7c?hqn6{J-ez2F*qYr6*Ho*URJ<>HKHv?8|ia$94Aa=}z%Q{jgJfN_qAaUeo!<_fbbE`>5b6U^jg# z@|qA#trOI%OBKmZzGuqBB$b2bFObF5iyHEfinO3|!KvobQoOQ#O$grUcmrt1RXO~| z7A`wCCzqJO1zA+N!^9(U9%16?CwJw15i!{~B!F{8#LoEX+C>sD&@mtUjjK|1cdn{D z8URdRGrJuT*Q|o9{su;#F4Zr7@1oY@LCxK?3eN&AKxLP*#y9bBFC&hGl}lln=pq#> zk4=Eq=M_u}i`MM^jp-yw%M?1DJA2Iufl3xn}% zXd3_A#S0dLjYWU4rIJ*f7*y0OqvTg;^Pwg|cY45+QNE}OfmCh{Mu)ewDjglglX2~I z7mcQXmo}h@)-ImAyr#Ma4J}r8yu4J_AM|%R4{*rj*$d&RGtp-a;zr%VD+vU z@N(=UJK!mTxvE^)P|U#?Jczf0@n+RgRM@D(W`dyiJ%T8Q-w+%t2p0+7qULiPj%O%epu^2EE(sz(VS;e~ zj3Da0FQp&#%IXo7wTSG%=W?DT-$40jRpB!#e3>B1@gzai%bO}><$^}{8?pmlCWv$y zsaz&2^D@QfBKt(dOK=K!ga}Sm!sOx$}Iyj(2UmG~QrXi5VX6hdR74@MuiuhU2{t2W`B5#EW5o#p?j%@%{zx+IW5N zpl>~H`0JnGprzLcdR7Fae5Byv^hh3FOV5EobT>FRoF2_zT6!BmC<-K^mxhPa8>XXo z2A{Ou5kHIOBxL0ljSW%CrK~4bJE3&DVE^f z_aDxRyT90&jWd)DjVWiO{1E}^jF>+-kbO2cXv!ZMxbYoxenB8De^g*|aAf}2fc;D! z?1nQr`SpR-!5iTYHN$~#1#f}faOURxMS%x{qw|*p&gBmaEDgR~x*oa~YlEKwZcM8V zjxAds-ITTQcDX?F)d18hz!363pa zAKjWZHTYTO`sfpBw*{pe^Um&!8ctqn=w~PnIf{2ijVA%6`bh&i>mPzXdFV6l6%g}p z=v;x2EosAp#v6N_J@ODvDb5=5je(@#XK>$<<_dl`e|>aU+JNBLiuKXfw7x;y(HeXi ze%sSBf-B&*J1s5vGGJSpBlsC$dzv*k7OB{iW(=+X+?$4zQP2E-U+*IhoABw~FJho^ zPCE__im!{#DAOvM_ho=Tg2}2Z7S;dPTEnrwRd*I_N*-5Q$>zu`}J!0*fb?HTo!++oE5E8EAkV3*b#pu2Rv zXZh{Crnkq(Zt5NLB{%gxQIvR7uhJ^JyQz1yawo6mmR{|Ry~;b?+Dng$`+Dk7=UHjP z8<(04VqSXShaHBzzMVG=?R!?b?5#g?MObAC9J%6MWx#*qsvez2@B-NJym3iNw}ZQl z>}hy-i6+2*?Ym;M!*s4Od{$7LXx}x_pU>Ld-=g!kXlT*+Y18@Bs2VpUcceA6Z%X4Z zjjhk)kM&=eRyb}?l#r_S7x`c`(g3Q4B^Kn4!o0nCG%1K$i5m6(VLBj z?yS;&`X*dIrS4E@H)+&849(K93hJ65qmiIiET$5QG#KGinKlc$y?<5R3!#k#|9t2l zW5OCVrxSl1`%X+(zXvk};T3YH%K1v`d2Md4Sz5<3)9qULnrtcI6I1`y7s&mPMXDd( z@|3v{4yxgEc{c2X$ERpL9{G_Of``_tUxw!ajy_&$licv0LhVz#e#${Yi9T2y*;<0| z$gW=>2SJP4FgKhYtx2`H9tp1W0j_A;{~H>^VqK9UQ6Mr{baX zq4~4CBC43#Zc6-a;Pe*H9~F~1r#0D^t;Y&)x&FH>!;~lu&)y9<1)@{!#|{OsCh3d{ z`_4?e$0zx-d~5ta^xy53uu8exo8@zKEt@~p|MnEIrLXTz|483D|CW$ha(nB&&{^I! z@~+4IKflAUbChqr|5o3YkWotau0hCrpP}ng!CU^DB8M5_H8%#zJ^##mVw>{-mkH|*sHgfB*A^W z@6nKCxy4)S9nW%7QvG4H`8TimmQy}bQbNX*$3s%eau!Qxbadl~(T3l=>8qz$;A?`f zIi;S()EONgY@EErmqIa#eY|Df6c*FNlf||kJ_peghk9puvk_0EtYMnDEyFhmbVOs8 zcbRvPPwcvKZ~ZhoT;0B=kjYr+o#u6e@)2L{)VqBHkWQnNeVap4(k!HP#F7!yENZz*#%Zinm*0CME7if21B4!ol-?{4o#s=^c^RWu?;bQw zM96HU-E5qLS|VyP?&4^8X5p{C{{0_+@_wB&=f8gO>JMH_efUb=o9k~&mp#vlJLLa9 z)7n_|;-7q*U;EXZjjkTe)!EjgljjW@@TvER_vD?kzd881AAj$6_ov)%X_Tp(Z0dSoa2eWnlCLsk6sx>R zsM{mu_fMXT9#f8fw8ZN`O&q;v_T-7aWZ!)Bp;_K!&{{s}>yu6BS1G=VkR(!HGj)AB z_^!!Ga7&~$eX_**pO%`f?ddCxJ)iz`&B#2WX}aG_~k zZ#qlgc=Yqc^o^Nf239YkW|*x~8#Q?h+C9=|u2j=EVv6w^S~pI%Klz$GMxkd|>dj#J z9Xe&Uj@qEfR-RwOJa2EG!JqDvN*!G$NkY!$(l!46zDG;nz+7;uG+EVYap3>_$JElP zCqAuv<@MLze55`1vmsA~9`*e3T~q%J#xa3+&;IA`_dkw4{Pe(zuRnjv=Nfu>*VVk@ z@4B}i9J8emv~28O7rMr+QeYuYTn2KaILL_SF+_ z|0-|mJL|gYj_twCS1I5h_p>j&k>KM}-m+B)zG~d4N4&gypkE^eazm}E&w>u)*NnFc zIrm15H+>yCI+So?;dgQW@kC8%;+C~dl}e?^hJs@J4=vt*WoP>SPdAsZDcujZH%f6U zMOkqf@yv+sNed9yZ5G_zw^f#^q^=(t3}r1vhOe=16;+0=SxZ-xy>aPB(uodB zB-8Xs<{J^O@u^9sx2HhE265Yv_6nE!K2%n|F`xFG>ay96M<%59J$r|I5BQie%_U*4 zm3WdA4H+X;OmCt0)(JOWJaZu8mE7S8lDDAEXA4iT5p{%OoKT8>4CQW;q$)~_h?LCC z%Jnt6LifB8nP9NwInF5DVVnzZG0^iwIQ{KEY15sPvp-E>`4lb0%1In!nLhh{2l!IbRnppF^}ojng)TfA;V%i4}{vh~^AHse^s0ZZ4~l)IzG zmENdv)m?Zd;;El>rE_Sz;W_Jp(k#Z&bfUXhdf`UxHQ_l@e%&z*?V;^9q)Td1!Yg+q zT+4y)#!YPCZ#wqqA?Qy5Kcv`Vz|DcuK}EXFbm;ylZodB4cQ{tLv}TVR<&Fee({9JQ zne=_oek{o|Dn`NoTw^DH`Uc?s{EeU_m~`~9c-9!YyE-xcL>qm_C%V{JdU9hl=u0GS z=(|1nZM%;_aQ!J{wYj9DaAK{ z*^xr}uwe?+@9>Sf2TH!{D{Ggs+ptODMETN(>Y(TvI0_$#&P`E=$y8HlFe|HS$`%PA zHyJ7%2;R#St8+ybaF7{>y3OHVTvpCvcmV3tYnKY#uIg2Q9t9jlFg*&CX3}FomcfdhQZ-#~*jZd83 zJiKe^dEiNT z`O88jG{S+#LmGo9eUwhh1LBPt(ql+v$_=LnRy3x&1j94i%lfiCtP!JbfmTH_UoLn0 zb0q-@XNsebnUpmwWX1B-8?>DA=vV8GCCiz|t<10PRWrXwT=oJ>kuFnKwNAP2Ra(zB zbkwPo9J0faO4g1EA70F#)`i4B_IJ(wv1vD+r}!kT$wq>sn)I)J zMDo8jKYLJ2ROh^P()>*AiyNv7GSs%T<2Mb0R6m#&SbY&5)sqj8mfkejDGqKpJue)z z^wL1j2{)ogY2ozv8l9)_Vfaz|z169oWVYEoD?1?Ns+!ODdB-BcSYSei?}N~9RE@z!)Oc-G~N}>VYZZ*ZISm} ze-o(P6&*CA!V8&s6>E{#7`B+c0oO-E?;49hYeUfwIt^2Lml~#|mxk#UNS&d5sy&A` z%EHj;0gIfBo#_vwhZ-{mCr7gEklEbV=yqZ5^RgL@%lc(c?dwlE*zZ|Gc%XPsG&`*r z`)1Dho6(vZX}JmY4*Eh>437g^$p*#F1zWC z&LouB?n%;F!DXA;x6~5+NtYyhP z4&UKi^U@za+5g^qhkiJ}z2P_ZegB%@zxa;J=TVNdpM%e0VNa|~`ADQV(ELy9__#4M z{Fh=5d_+?mXg!dKZ)Ny1V)#m@T zRdI9{+eV$eS;e0NNb9YC)A_5iN6MCwdMDiCss`NYhAY+}%e1U^S=l0Rq%2y5-5e-_ zn!kMU;)h6&r@$CkhU1gMBUIJYEnhZwDNu2-97Hgd)~-{BVMx zbF&Jm&lvFGOY2#}pGlCK$KSIT6k8AK3qHO8RDI$lc8!;=sD^_dXt=)Cm}9)^m{n7M z%$`t*%!St^v&5=X)qXi#Q>`(CufC0hqC`JPTr0vFGkkr6DrR_m9YbqMtsxp@hTx%5 z?aT0}o#^AGF_;^p`Z5eqy9O-95bJ~+?ST*uJrv&*JX(4|*iYl(hSQVbprzLcdbh)k z=;1X+kJ`MJ-m|b!pXP?sD}#fU9_?{a-iRKhi>I$iwexZzITKC;Ua|L`-Qdw!&J7Rq z9XJsEFQa$tI`Mvdop|qFC*E&>N9D)c59QYhJ2yNZSKy${2k})kawI^GcNOnMkII$| z;dQshC!TwhNu7cv2ra<7o;7|H^_EEysD{511c{hA4NDLY?I8`tL+MMyLv4W91zv?` zO^WOmy_4bemX;UB7x-Ab$JY2*$tx`D%Kr$D=jNNCwQR)A9+i{*%lD8zQ4)}_hZH_0 zxKBof5$ohh1BS^)oEbt(5_od|+zBO&4#Axz;~a6-FXns=Tn@iIEEcpB<-RVs2Skmf z!|PI_#=7Sx{TDPUHAami8l$EWVRklsE}D5V5EUlQ_kr@nqmjmvQD@9@bJRGoIcmCd zWrr|CEd1@^-ce(4x$h5vrbctrh{qHZZ#hvE?tBH>HozG^R_t=55N5&s{ek!3{kPm( zfA1~#kMq5M_)l|iayo9M7dpHqESdECPQ*VE@!werPUUb@ZP+Rh^c?&5E4O{Cmz*Wr zWl>$T{}lRN@?{rJn$;UboXNn!N*U{Jp(R~bv~^0#3Fp7|yL9JuytgTB_RG&F&ARY3 zg2oN4e=&t?4E6N5>25!a*2~maX5gXyuyobFh1pFp{SGuQ(0Yv4cZ5%4B(1ju>R@ai zYa-s*2^zi3t*@@CBR`1fj?NS|6j?iaLf)u3E7P~ODD|Dph7Cn20g^eIowdE_BIJRrLBVOp zV+>~EwkN?U0D2&MRHviuX0#PLrnd*gX!ZlmbGeW|%1b)?M8mm;T%$K+u9*2yZgKyI zydjfw1MaZm@sa$I5*O~`Qsb&q<(wMjRQcp0s!1VtVgTn&k(tIb(P=XV`K*yy(%zz* z&bA_NEm>~gUdTV?#w3*7Um~G&LMNf5oK1Bnp;YfLKCbv`C6hdg+|uj&I_RXV*gv&H z$jSHL9nE2UV`5>oe}B=ZXL9|Fhl=f`;p_~2Z~xi{GqUlG^;q`DM?}Si_PJ>MiyJ=D zD=svzC;BV*7@dp-Nc5$7KGB6{+_<5AsYHKA+!JrK50&UoBXr!*zEh&VexE5#=TEan z+;q3^LlkJ-q?FP+WM~|n%{hE-qwfG(hup36UjRFWKh6BD-SQRjAe5SRZUH3w(>nW$ zI{OKo{iiznTd-67ln=U>=z`Aw&#J$&PvsD7Kqh>e>*$^$r)sBkkQS<2$x}6yz9~BU zy(<22_>rC13rrfDRN)Z%X zLZB@qRm~3zK=zn$Rw7!=c`mz}n5RZLV^zh{u;UjG1T{y?7cHx2hpx&xfbPboK0uJ_ zhwjs%HIMclo$+KJsOVNA{wv0pm94CoNcYMz^2eUsCW1I{{hkU>0vg;Z{Xy^}cXWsv zf_bWr7SP>7cF<#Ix}fJIJK$gy{$qP}x_Z>Mo%oVkhlJacl-Biy7_6oIO<%Q!m6biC zuc_5;Tw|4}VJS-p7DG^LSrFy=k4UH3rp!Dr0z9HZr5f6IJ&i zfYipm49^1`3ZsozJEHU1W*Bg^>+e1ISS0$icSbmT{e3Uqwe-BOV=FplI6dMI)6#ne z^r$ZoJxU9w_aNT2^k^@c#%XRiy?Quk=^X{V8E_+dluk}>2i~>xoUl;5+;DnZ;h?2Q zd|y1j)TcPTV|drnqcwkbdyhZU;gN0+8o#;W@%|nTT6$4fp5aJ<9PbmnYt!dI;+$}! z{L(za(?{p#T6%lXkf_eN;q+30q@_0u^r+s59+eNLcautw(;$5q-F;t;1|Hg89gk-m zEZ6gWwDyX{(FS=7WJ>Y zea0mCQNR5d53TR1pL0X+S=<6hcDK%sWe*FFdw}I_+(cRhTF17!v-fF_&J?{Ja(st>=LN zDK0Vuwe#~nSiEqgwFoamYPS0MIfz8~;LZ)F$C_S@*|qkjFWdhF2gP;Y8?@*K7F%q7 z?v0a+|G(rtY5w~zc2B*o-b@nF?a!3w`BS#-_YaUi@(+}IZFg7omwUIDgbH$u;j1}- ztBadK~Xh_)0<@h8uR5ghsd# z#tm2|hOhSMQ@k!@uq3xyphKYN9@EzH%zWQKpQY=s3ra$VjG2V*6v9`XfGjR=NOG(W zp`t@6!VGWv&I0)@oFbM(-$ABN==xJ>N$9ndK9s_2P|pTjXb)fQJ27N;l*wyDDbhGd z@4pqzJvr3Z1M>W0SGcSs^nFWT3gI$@uet!MY~ibYib6Is(k)5Q)sP}3?OfW&652^t~q`DQpe%+iKf<` z+Slf@cYVIFB=nMX0ENj(;G6RJRolhNd}j9b;xdbE9wkB z*k^(72GstaQTs=YE{aF3{gJG-c+s97kk>#q+z_%EW}^&pf=l|`iMkZK{!~*EI&O1! z7sgSFy0;m|dgmd8@nkOAN*Y?rUr`3%x8!t}8?=^oxfW-{Za~d?{U@Bi}Ds&Fx9Mr+O`T zB!mEVeIFV1jvSDVlIeZS&;np)Iex`qZ{JPl0I%#|72kl>fP zE|;U`&8cljw^iJ`A!M@Jw{8fD){;=P0h&CZN5I-;G0gMXNfQc6^{COY_t8+2!_c}V zBskEX93`R8ABOG^VW!v7Wf7P7MAY*|^w=X(>fRS*VejM62_m&Vj*@>oWJ@Ut{q+&( z(lFiYP3f`-9$zl%ID!%3u)*@g=8)NJ+*{*a7qXg5LZ2>Q6EY-i4v9(T9k)U+$oA1t;T5-zp)n3+b_Hd2*kEWiwimM=YHckB-lclzf)V^)L(4edeQZ>SpvI1w zq*l|;I&ULJ*rX@wfETV?9Wt@flA1^RoW7YT*Gr{TT1J$JeGg(Yno(kEn_e~;T0}%> z^>FMwg7TV;GB9?9=cR6=db72do>+(8E>urS1q&f|g9}*Rp4tcMfoKPrl-t=X0Tk!>HjS zlCbA-jI|Q`e>`MotrsPMmPRA{gFee%YMDwMqn+)%MK(Vn2A}FWTl0w5@#G_38y>Xe z2X2v}0}xtm3?ac>Sp30s^R8QbW1v|@t#ya6=ig*2#bPu*`LNgCb`L^bUV4K(DdfN? zDi}?Gu3}-=c)1WdTBzSfFrpkYS)P1JPI__!MnMz!*;AD<2yJ^JG}~ZQF^GWa#g^?Z zc_cY&Uj3)+!!We9@7pLtSBac9B_d-ea&K4$JN- z6c5I{X&9{$!r}lNSKMRk2$aO!OiR}l%qXvjhCPlp)U(yjYSP-iAtcdg^zeL?hUh>T zhY2J2G^{bi?fd$y^L>o*mF5-ni9N>FwHTFB+qb|sjny#K?rqQ=iFxEnQlhNEh$J;|K`{`_EF*#BI5tn4xwp~8z+Y+1mUlWXbFh>}K zJ@dRG9x6eHd^^Sz38iGERYtM2J)4!stUhAb=jh!pSvQ94CJTCrqy2t_xUzg28~p{N z1JGS;-0^&$@oWx{U@UsYVcWYIW4QH6%yKpZ`$r#GI+4x$))WzNI#OoZHqvKc>z>Of zg^naU^(;dgMlVSj!RmZSrva_O7)p}*7TcTG^ijtCzhXUd)M(qA`sAa?WskkpULzii zIZM$JkdGu2+Jcl{Y}>Y@PX+YWn7Tegt-fZk%4TXkR*}_>X}3CGJTisNeMZp^n1qrZ z7F>e-&thvh8r@!&gjTG-91b>j*x5ho=aESvEaF23)T<;+3kiaX@vX&5D}wfS z%w%r0?7>KpVqi0gjrB;HOCF@Qj?fay!z4^cXfx*B@A-E6)~WMuhe-422e0(|w$R!r zTd7qgNv)glpVYP)GfYY7FDs{^#0(*m-2sSP7?Qfo_BP)(tl6lHUU69Vq*0s1`e3Fv z^~na5Q5d~!I;#m$NFi7(pf%E}MdH&;AQnB~_$!R-Ne3fjC@Mo-w z4hhd-mfsRG8Ej~O)DEe4^~GvUVCy04HoI>rYZtT{I&Ks97}_!OT9YVERtxV}k1WId zW`ouuth#~@-#x7TT}1o)p7{k?qS+g>yv33BB>huuJ&0A88S^)-hgW7wSk)`52%5Xr z$wsOltC_7NtTZxF-$j_WqBDc|iV#XM?~CYtjp%)s(EE-WrM;%sN7-5;$stmHSS)Wv z=LG2+VkY9TVx1;Bj+U0xvDLn}5qh?SR@v*oLt|{_PUKE##5&sCC8osd|0S!f z)zr2+B-kvpjx^BzhFIL^-@@4Np?P5hJrkJ|ywyA8D9H#M0 z6x&yNjaVO>pPY+w{L_Q3T~w~cUhL8Wa|$aFn#B$Y*0!y(1KP7TqQ{z^T;csXYX|O~ zSlQV&qMwmAuW2;zBY&@@h);CL(i3RAs6VR}D_xe3oE=z0K?B&mzM;@erR493BnIF6 z!MvXuXhlk`!y&Y7!51*4X3(zWz0dhx_Ra9&D*;-pJZ$~itF4!uR+`c(`tB@EZ!>fq zH9lY3bC28CRhqHK*p-627D=m*^2I)c?F8GWN`FIdlX0q4hC59)x|_o1-+qJi5S!%< z&=gfnx{)kz3+cY%vNq^&T2mz!tSLJJtxosWA1PbeXF~qKvpA5|t0afKx9HLtVTcqIvc+JwEDQFE$ZGSx@J1slg_fD}CmZbNE$$s37c}XDtre1UD`Y0wOmsofT$4jix z;)gt%;!AYoT9In!mn|zpYzz*EdW8|g3;(J4-#sXHYVT$|WT*|G%3_9(nELadFc=>) zfkpkBaOkn(nSe)2Zw~BUJlt@4y zN9~)^N9p49s_?F*Hy=opXKpyXhvA^bYX#m0js(c@Xgtxz>p{FuxKX?`y7G9p;$2J6 ziJ|Wl+_>TN9)p9H-VD&Y9d1OApA%7jp29=)_zd`4(BtoLj-~H_ibvD~f*C*eSRHQ9Km zTu}6^Ok&e4?9+ho3?Mh09*qV!*3GM)UpbPouf)#9Mty-yT0!A;%cQCIJI2n%lF+i~ zylU^EbJ12jd-lByX|P#=R@t)6yj5z(Ey_0Qi#=bkMMRslM_Idxva#6=y_S~tq!#-w zqubF^66#a_Sk&nOEPgDSO%VJZF3)38cXKkhGLpcRVLNDWDdA1HLwZbShAa8N4eWf3 zZiaTB6LT^K2|M%UOTOWN!Y#7j_i~rfz84t8>0xRywf0vyJ^m$c z#^hFb`-}HD#f8?ET0{CT(X?56j{?%#hUQ^zsErTe7C^G!th0B^hrt^=TX`rAe=eI6 zReeP=4ox({@>!4p!(#7 zkK_9JGKw+H10)*T35OmU$BXdL*hPM1=nkyz#@(s#(~i6Cz`GuPgj@t7-Ux2^I7v`D z?groxgd3VcEREC#DZScp_YeZz1~+avJv6zIqi&9K41U?TTkv=2`*~LROo@%VeXx?H zarbA3gox2P5#yr}$uVTe;5v{@9uNqWuNKzk9O35jFl1TB#>sC#h(4=n*_m41TI_f5nu%~-D(GTXorV3-XIu`o|QE_;UsIw$V{@4-{IHRI&^d{ImBR^QG^ zyTBFMIurVVpo4irlJ9g>@H9^Pv{P_S7xD<$7qbyv!rTl}W> zgJ;(ar2U&U3n%0kLxMULyEsB#Q9y_cv(h|xXkqj52{)#l9iwWg7IJTS|2@Rr^NO&v zI3h@I;@x|T_f}`{B=&WlhvfD!NIs8|y*}F8IkM30J2G#G@5X-K{8Z=&SLLU2`&>ig z&1i13aO01WTiVOPoaJGAIte!JT6b*S!U@CY|9|Yg3w%`7wLgB&yfRD@G9(cK1eweP z2n2Bg5u;cQNf?4CM4@WcN*)9zyfP$FL$$X9q}Ep}5$Zz4uNK+o`v={eQoEpFMNt5URKTem?hef1kUPGwZCq*IxVmJZrCgl)8!r zwSL6l>H@}vdD33Ev2nxbXwinmRBuIT{;|tSGmp9kr`qi}0|$9oza8V~JyriXP#M_O z=op-;%h6d2=(xFT*irjnsm))59i|yL^*#Q=(vOerY7B%M!y|@wC6;+&d)_0;bPB2k-CIq z?6HVG-7&*GC5{wF#Qk7H>s)UgQs?@0`>A#PbV}>GxM!@gO6O=F6k4Cwb3g1`hHUOl zN6*E*aFvsX9UHzU5K3tg*SGi#xm>viNV#13mOXf_BYW@xXOT&N_><7_J<`x0{*~S` z=ntRT>v>R-#q4q7}EI6}PU1{_yjJ{_swrKRmV1m~(zle#o%E??a39!f-*i`K93*hNcKRQ2?)G%sid|bWHycv8o3gh0dfbqh z&F&ty<&_WT=6y9WBKV>9)r2?rweQc0-8wel`D)@E%;EAY$Di*yIx?nB(2KJN3o;kR z9gxa(LMj(BEqgseE?3Ujd>82Dk3IeEs@T!7vEh64EiUwHp4|>e<4X67p$7my#jc-6 zoDn@I$n&H5!SU06cJ#uKHsSdDqeKhee*WE6_t#vw_Gkaux9^*8?)%*hFI{ud;E2J` zJ+|Sp&A+&*aQF77GLGsuY|Q<^m&=L|elhE_9iRU}+h2Nr75$d$t)7m-o)=$i>G{na zbARw3uRQ!>&THSuDsG+nyYrsv>bU%^C->g?#+`q<!`5lxf8TIlM{CnJJM%jace!mpy!Ye3{$SI|RdtX3 zao3-IaH@0dEC2g;JyYu;cFq5YeNT?#DdP}pN6Jo0qh=`*8%h@#*P0aub!vA&(PxNx zLekly8G*Xo)5GU1B4xM_neHwJctyc8lIiY@P0DcJG2Pv?ApkP&E2g_+F>5l^)t=5V zPt3Ok3Gzr6ni^6b>|*t8#?ML45`@DXWe@JY{oN>0$!2IYLi z0_wR|M{rcDT^`62L{h)I^Z=Rd)+Ra|%Lc=CY3rKiqyjGRH7zc1IMM8)!ZFEyvmV;8 z(OZIOc?ymD!^a!z58%uL+COGM#^0>to$Dfx&)CN-GW2T&WPRnCz&q&<`^|)~e~1wF zuNd~+OX0q!*aw|x=!6F@=m8%w=>NWTj*&yjd^H`fGv#h-d(o25XE=zgKEw8m9Zt2* zGZjY4lgTlCtCZ9%8MUK1{~xiL$*o-0MYRGh$Gh3s3j1*`t&HMV4+}HhVIX@I5ul>G5%25? zEAh}IfD`~S+zdP_+;w<2^Xo*0v7b;z@w*uoW`16DdTcA?M|;KZKD?Xx)gdG6V5f}Y zcMmMg{Cwc2;>R9Z@%t9u&HQ$PU%z5z&=K8*$IPz+{3>9___6<0{0`yW%r625AMBJN z9Zeq|GrtJ!pyG|{Cu!u{5WDLekbuxeyZNqISmT{ z8pZD;ypt|SOPD$|fL8e`e(A>NFL;n;5n+j+1EBjfYx(VnAMesuM!gcMxY6x2_(fpB zvO_m2;^zfqS`lv2EQQ6rNleC#==syjd}i}WYxxT*KK*^uN99nQT+0`s7xwPU3c1U; z&a;QSy@8hM4=e2jA@2*+gpnn_-anhT+*cGT5$_ey+m)yu9=ZmiG84kjR!!``E0ht! z8q=-C#)Y`by1u?C><^ErKa|j-hZ2tHqj&vlqNcoO%n$LqQ)tkTtF>c^Rl#SfYy(n4 zpfo%+w6E&z0VxB3{e`*3(0=`*Pz2IgWwX6YjD+yk++XTJqw)oRR&Php>LrD@EM(3*3V)cUFvUwjB2m zZpC43>Hzm+Cun$noIpdx+BNbN>9>w38dP zjV<%**NZ!~sA+$=h*|4F$rJf&r9ct=&q8y!BDn7E3G$oWhuRr0{Y47rvB|}`<5~yF*PztGz(uStnoF_Lrcegav?`a@!u4C~Fo2OuA4a;2R!W9S(=J z{hnAAb|lR`Y(;Ng_@OHI0cdB7e<$jQjR|k5 z(&D{Q;H8Oy<9E&P)o1B`8{|i}B?^lDkdV70QBXdkMV~dJVN2qYU|w9$$?fM`>xY`X z3vhb)=ZU@L`mC4mI|DTh>!FV~et5$$)Qr<`HkI40o=U+K^;R zrUpH>_yzB?-q5fv)U>?S>q5_BVE2@`jr|yo*erxaGUoXo*eRv&pU9@feoPi0kdFVgv z|Ea}=ecAS^TYoOw{fd4q&;_p2zF^Gq*p@`dr%%ZmK$-gT;*~ke2Q)t@d@E4)Uj4dW zEmqRY)--dTj0Nfa`&)Xe{Kg1=o024Bl5c#mr=372K2E#FM8VuyZNcxh z;162x?*M1?xVER9)?thL&n);+;G~~|Cl~eM0|Vz;kUFA11XPCc`4|uBhvVV?3QmOx zJSuRnb`Bj4L;498oZs=3p?kIAKLVJG@>pcx)8LkizZMJqKN;>>D09}9yDjcJE%-hQ z{v*SGjo?30d(-0nu>~KItXpyoS!%&+EcjgDWWhFmg_{pt$mE2AP5VJ(rD z3jybDS@Wva)(FzF9J=nIIE?DUj4Xg+u%)Y-4ed=vmQeW@BiP#3@&zc>Po<{?I@gyp zLo<^|2i1Rx7`8MwH&+CwFDWZqLa&X8I&wvZX(*slAyUN?S&K4S+T6lB8x?=W4?BS( zx0^*Kt?6Jkr{%Zupvq{epiJ_iwNNLvrlF|=F=|S4VQoe**+>OsT4l<+)xexC4Z;%IOJ1`VBGfULu|GEMW*z1-k~1-amZ zp5PU>@z&}hJ@HI3b4=jI4 zcp1LG8}I|dN==IsBHn*6Ap3uY<0eEPe)6d@_CKqQ{Z9yqu+Q=Uig=>~*g_ofeU1=x zuMp0W`a)=bia6q%#c+U+Bt-uC3Bi8~A^34Tu!XQsONe-g{6RjQYw%^?N)Exm!<%LV7O zKHv-=>=w?jv^4QFchwF}zEq1-PVFW)#5JuGN#kX-Hr2HL|4Zfk z&*^bSGZf>I3y=oQOUz9Lf*&wxF~+B@s4IA>;T05u}k@47{#vzxH*3Jf*->rKgLV(yBqIjeusf`E~Jd& zcLyxY{2nv$V?U+%J&JcTzokH!SIQ`U&%?q@x9Kc&>}$;7K5xorbcM-qe`4YHJoqvH zlu_YwVQ&tXeP>h=0V=xVcsGZ82;o-1j`>Hvq+>q*8IPIY6}HsAEu5145_mWB`?Dz> z?Ee(MLNGJ)yB+*EcT+~iuK+}5evTYD9x#6Scoe_s7Jhfap++$?=!j0U@Z(t#RbRLY zQ2Z8H_`L=P&PA0W9nIwyeqE;ck+0&%v4GxHd%FZL+sVK%iXZEdIlaFJzdFz`y<_kw zeo=#8(C~i*{92870*1xsF8tU{d*{^{PcQ@}4QXh&%?!Gvxdes(pmKz*dL=X!lhz4- z{9fRLJ%(-(Kh_(j!OYK{C3OP9S{TLe{PHsM)_LkqjI+oePoEL|)OyLEkeQv_JI|r6 z;eA?BU!o+RJ0aB74cxDXY+|pBI3vgXa%brJUb&a9m0a83Cw5NM4ha2bgi5ey(sxqx zc4@yz_{sw{U$3$s(*rLjipxI}`vrdW8GP4EXTJT5M4^Lz^rDv$vfP)Q)VcgbX6&5(eE|_ zXB%N3NBM|5E$$B+?)}2ut@T>mIqoRKc)zOiyRYbYA5wOS)*tZlJSv+&YBfsL_T8**K@ zx&i`lb3bIhQ6M2Al}W( zdg;NLl0rfZoIXN)2wqHx#!ycPPhrB*hJI=Ai4w;^wu2Cj@G(L#d6saT)WyrY;a&sI z@czCv&L@<;s6wfk{U=ZxH6=xF6soz<{}Ey!$v#Q-F6BtR*{E^12$pP4YJ9cQsirmw zG%UYQq6-@Q%;B=UmEoL&hVxtV+~h`l*C&59KP4UUoA59WieC-h^YJL7_@SDm@mmUh z443@qSMmEA-p%~31Ky8E8O4utd~^Idz%K$j@?*RdzbEicenC7mB|wInJF!ni-jRhc z4}k8|%$oO02vkyp(;b-oRf3Uf@6>u593E@ojW~`NPJx}fxCJ|hmblzfLT;h z?qKFw&Tf?jjZ5i2`985o00EhF4o}z4*MiU59)?bx$z%I%Z`mdn^-qquCujD1Cr=pg zPMJ86Q#$EwSD>`s9tcD<-vu$>6#u~Z(txnD3A@r*_LOr6vP#Flt@%pob=di1&M9LD z9HoUxo6$m&dZhTx6Te>Zn=O7l;x|M5I>fJ(yynd);(K=WeL@q#rjS19&FD`|Ey`TE zqbh%(KT%%xdP3WDQAqpfjtJ*su23y*llf61tLztW8&*^xDX-V-W9e%QrQvJA>VyGtoh|k zO)K%e;luZS$q}*%GbhHZKAOVxk#o87kzw(X9ZPZWuY4J4!*_TiHQK~rD2KAl8(C;l zAA>tI0=J`Oe*OB+u{go;c;}4jcRR;a2iE0PH{!_~*qXSbXlvptWn-$hChio!cZuJ- z%kp4zPf=dA`+)PKgB|DhY7cmNhV5WpRPOiSjJIlPKS-3&jhVu-%+kumLPDp4k3E(V%kVMc*LjCJQM-mT}*+6IW|Hbd<8522@ z*b~g#Uwc|>)?>A&z0Enjb52K_hxg8jyPYq8m;svWBaWVjBickq&#xkm?XI0$Q8bzw6hjJcVBB*@+;SV~tXNz}7&y~klApcCC9p zzDJLEP_8L>n-Jw!+V}~44E#iF<*6@qAw>)SW$$>@K}V=Sd=Yn|Otg4M7r%?M7O(8ImsZAWy4Kt|!&802HSs%7 z&l>+om3HbgT{lguJatJ|>AJs^+WKvvspx85w|ianiHu1Z@eH?n@UJs^o~9hbv@YBb zh5DU6@Z6vc5)!yO3YZq3(3OGGMQwmzKWMbF`MqxR9|_$#ILwe8s{F8^Yt(7m5-mQe zE911*B&qZ99{s}jkMI7NXJW7EcMUo+or6~m^Wr>%Jh^%#u{Ve%c08}ki`rQ$zx5A> z?FAth&qi#z0VO?K*rxnwC6)32+^7W(!LN?rJ;55k>p`>#5tnmfDNZR4$~B$KuQs~< zNCR}jB!pjb)5^pt*YX3$wct#lO8#*6?cvkg`Qg%qOUCTnGr>@rTzgo zkUc-*qTYfXM-qq1c2wmEX`1@|qj%Vz0j18o@NXQ`b0l$i1Z%XfSNS{!pAVJkrC9@` zdluo2v%a!f;Jvpjkfb<_al|<|0wsCGh-GE`K=j1A(l79gAL@0Hdy2E)JH<8NMf{$Q z+G0MyZ%0ut?DlRgfWD<$+g6R7ou~RuCWtKTBDU# z4Y0kqOTo9))vtr!9np%It;pxsWA-AiU)GfT1J1$OcAViEtZ`v1-RAP!HtW#WkMoLw z%Z;$KlJJ|XwX&@F<``UT)X1h~M-sP}HTCYpo=Q?SDL`2$*bA9DmYv%uySJfP>p7HZ zsDIoe<=`Vnc-y7EX%`;djx&JJU1ZDNY>zwaZz47M2M~{d12tHT_XlQ$GP|eaHZ+?) zT-4Z++jt{D7Dl)kq3HSK70#G$l)GL$`dz(v z)Vt<7b|d1JebOeE5Y7plx$})Ysl{dC>})2FKubyW8*T z9vzZ7<`~=wJOj8(%mmzi&yyMAjr9S2R-kDaxgAM7S7z%zl6ba^H!QIJsJ*U={XYLy zkSvq+Q7?Yrom76@|Bh^TUtOOnBj%2rudNTM{uwz^FMA-@M43zT2SzB z%4AK@CT{1LG;^!?-7J3Z6TkP0-@9T)Tl0_H{LGx@*ExcmGmtT@>Dc3gZjXx1B2#SG z+RN5o6T{R_tYZ|-j5L~g13&yw&&%#+&l);w&+3~t>*#dmicxR1ZNqn)d)aXBwAl9s zeL2&P4&Mzb+rkrXpr zDQ569#SDI?n2pR7Blt{n%haZ=9x%5Wt)zKi6k6A?Ky<>(C-oGssbxL1x5* z_FBk^+KWbEHZm%0rZoyPtx=e1IWg1v-MCT7c@{^uQQPG_YgCWs-1lg1 ze?yMHe;;jRuWTckrS}NCci(63>7`#f{n4J2zmfH4B*xUsPXE`pXS_dp$Kl_cFHT6} zOlxlhw|Jfd`R4w_v$~Web@iUNU-KOTekk#r9(h5=f_NQfaB{8%m#dN|ZAHu=t6XwX zf5pDZ&rn)DGu)S`ESrztnzDQFTU+)jey=FYZ0SqXmCeWR;k{DWkgZ^m~GnBecC2 z?`={JTaI3b66IH2{)ayMp~SsqA@pCH$}Yk0mNH+c;)Qsx7kR+%1;c9@6O7SDPH;?? zd4m2e`BrYc$@2Hg`zO(cBfai@c9D>H?^`AFUt9s^O;@%>b{Z;2SO)frqFtJHsxv<|N-VP=n zGT=@FK4!ot47gW7)*Y;+janq@lKKrIE-t_JQJ=^?86R`KP`RP%#q+2ahfMY2`Lud* z=!|+{=B4se@#+Jw!zNyRX}k`f!K*)6UvtCEJ*_1F&60YKK0W)1vzA7Cr-WrKEe)fd zmYq>glPAQr1J8-FXX+2i)Qg#ioC|Y&)yi}~Mp?9mJwc9oEN#{n?mWn`Dx<`KSyhQG-?4+AD+#}0TpKYpc48Op(?yW@j_ z$=LCgJl%b~2#<15yLQjEf&1Zf`8G1Z?oV}S@0Ju_|Gl)?<{yqW?iOdjOl{`<7bsKzS!cv2sl|@ zfhQO3d!>PYLEz_TUo`Mb1U^c;%fM>|K3aRgz^4j)toAR!m0|dOhC9Dm$uR=TXyjz)j)irj<*Ux3%7k&&(~6aC4hHv43+UvI;V|5dCdggYT-VR`HuE zzb6a-tL4|#B@x&e^2rvy{DcaB>30dYuNJbx{4Q(ADAQMSL!@=p5?I2~xE(lUg^!3T z$Yw}SOWv9pCJ=hUWGqH?O(1t>KK@nj;Nu?GSBl zHgqGbPD& zm+`UKz+{fJ-nwRqiHEc_Ss1kkaZ?-_Hpm)Qn=Qx&;;%+MmJz{s{WBTC(#T*V8rGyB ztClt35^2GGd2?Fq5ScZt9WBe&EfHeCDguUGQr4U!6=j?+CB)CwgqIoj3j(e%@NI;Z zn*7c`TYlds{!7{ef6st_AcQ~m=QhZWUqigw;DZT6g!mkJ9>p_P)1n6borJZT_B>&g z{BGlfB#wM| zgAn+?6Czzq2jkE4=X9S+2zTCNOLxi*(tR-@+{1)$|DFL~G2k17S0YbrfC#sc5S?W) zA@U=yg!2c&s}M=_qqO%Ma5^E}I|wh=v>0I}(nB~K z=X(t3bpS_UpG&w1DhWZ?Nr?DRCXll0j}Uq^Z4cpM$axTg{s)Ag)wG`*_^$}z z|IdVow--}$4=O<;;kA&3Cj{RO2E5mR&l1klw3iJ0r-b0kZ}8;z?}XPu#?u7|z9B-y zYXc$pQErkr=gc+?TE8Jg{Yx13`EVnh&wxP#RuZCIZX`r`93n)zju0ZfzbBjvxoS5c z{8t%p4Ph1PFCp?{Ga>x#Fz`nWd=DYg@n^z0axOqPk@XAppqvnVFE${^g#AcDq&tfc z^xbP2H1AC?)|C;9}qaA@ik|M(4SUgntXR{aKHyiRnh_472{DeJV z0r?@Gf(cIZFP4sQ4(R_&UJ|(eZp*4^E=|rjVSq#VNj_4ZABk>uILNH5= zEf%L#EX{-Q-zGAKWebsMfIh>;+RK|xHJHW$W9T}=w;06EusuWe$>4Oxz;=dDF@#}6 zGpX~DLzW!ZhNMr9a6`JL#=5gP8>8OP&ZeO}IsTnNnKtyD%{4g)DlI_&59l?YiYBPz zLANIi`-h$KRs5n`7KTmLOm_shn-+#qbc=y=ccjv=uPoHGdb|hmIPs8QT(>S!p!eE@bkfr{AjP@_g%c3 z`4wUuWFMxC;`a0gmAJa#E-1SlXevWrDzY_3^!A=>)@29XZ^NWEWdGX;ck>dA9 zyqo#C;qV&llu`UXgoT-(cO2y2U`KvAcoaYDxr`%lxRD+Wqxf0RWmJG4^)8X$2t114 zOhnEczu<7WHnsAb4kB~>_Kr`LFL$LBKgu1O`SJYF?TVQ}N0jp_Grz;&r^_)_WRE}G(Z$inXl@MAiZQT+NW{CvI?KNNM6-!Tin=fH2M@;Ggw zD@UODDo}$TQOZM_%dNzh3Ky@*aC1@DK$`5YYQgUc;L50U3Ul5IqeY7%)-z^q? zoxW5(WqVirwp;iu1wW7`jpDb(!tYKHsdmIUo#MC8;5Xgi&;@>|(n&))me(PJE@-&y z0$pp8O45+-C!j;u7*S5Sn3&E`2q0-_;GJ|yv&pAvZ^N%LijL4^t<{2G6oXwE>_3Bt z@k0@bvM}>Ij6tT;WTE(R9>h%uC4l>hvbPl%UIejRm`|3yEuV#mm}GBnz=>enMZ+iO zi?4no@`mT-g&U ztf{Y+`Ic{#;tOZkS5y_`S2EvfU#|uU8k^91!*5oM7$odwZ`m8g*IRw?QKvpwl$(vOS=0 zyA<+1j@=nhVCERC682ijbC4tb>jfX=R95Bldo*+`GalZ3TOe)fyk7^M&yXn%yy=1* zS(lz8!pk-|IQ-5hvj;C1+PGa90`@&_&%Xwi(gZ#Vw94 zwv*Mlw(0iG8YP-G>ways?Q-UC-zXP&7lU^MWi_IA<82PeNNq_t$|x<`1?ULuYHtp2 zO&knv+&?j_PKsG!Q&@-0!a-j{*yk@Qa|})tDXm927Kw7y0>?XcHQ>uT^qVePZQ=k9rIs50vw83RgeslE++2f$E z(&rCdoe0euA!VD2v?AyIj!hZgwr{g-MlRWO{JJ3l+%;n)>YI!mq&ADtLcCJK?1TOM zx?5i64;6*gSN~#Yth41f`&pYEgUdu->l3;X#dS6Qj3*s~cL9^$9Z%|Us(9gk z@V;UH{kTiT{)`OGhA?M~8Uu}0rH+Bj+k1K$V#N!3>F|M9!_|Oc(Y)k(!I@S~M}G{6&zg9A^jVy`kKq>Ek!nxk0%A-^Bl=L%(q?)5Q5 zt)#Z!5xN)kjF{Ag*Co2N?h znn=fl32jOikyI=rN{~oBZ9Mxa^|XB;xRr!UdL3c<^M$mje;P=Zu8H?i=oYN>SIGWZ zgy9Np-#;O&Qpc1eQ)qg($Fzx3)+&g2NL_^sPd^l4XpT@Ak}wY$k}zh?gA?jY!%g)f z%?G@_l(H!LRn*y~liAoEq$Yv!pklz8 z-I;GE!qIU(y9RBZ??DnS_h?~HPw(^d3n&rx3w>5oJj1TzUY@+(gM)56?&s-o&)8n$ zfHaY_TNl4SNqD2~!KMu39D$G~UcCNd^iRyGV~P4;AYuIB}S+Y*KH z6hq)Ifxy~vJ>#kiduH@XTh{=+HHaw%y!rsp8i)&+U$hZEso7o?(>#~eZ) z)EO$+FUxgz%&|k}Cw8bXSmA9D?ARGz=_`oYUM(SuMtZzsHauZJ!? z=>xLodlx}QjFRV)-W-Aq)!sv0ncn_wi5~~Y8flu1u&=_2i&+>C$35sWMgiBL+Zhb& zfehrX9N)GjUX310bk?geyr5TWVToWHz^6mzR*RB@a+Ud&7gIGG^Pr3Mz&J=mJG-BJ zFJ-HFY_PraJ#h-?KJ5VZM55Z{OZwyczOolo8ChsAkeBp3H+y$DHs|cHZyvtGwmEx; zzBy}$wt0%ktMR?NTJ%!;!0^~{z;pU@aKhb&o{{5U08-6aq963ehCxrGr*`Lm+JjLy z&(!=||9`yifSuE@`{4ZyoCZQKVVIx1_S$P4&oeCJd4@5b{~;Jf4=wMkb(GG3E4c5g znAdF*ZR$Ln-QsxrjhxbcTlnpwuO4R@J{Up+<}&K4$SLe9I}-fg>I->PlC z6O!aCo$S3qA%BPdXYSp5=Y*Lak0m`GBR#3KXdiBhIzqg2)-C2uzOZ}uQ!Q$y6M+89 z+oP{1qLE8OTM~shN0vL#vywgG=>F{qJ-DOrfvWtsA&c${PVo-d&`-g(-Ub<3+Z6Y} zFA@ctoG0gvS<^CRu*%~exDr}19c$&R#TlyHe|0PaT3UnRJ{V`n{r$^h(~a{X(`tO- zQE^|+!nmg270L|Zl-B9rv@JZ{*5A-eS$rOC1L|7yKv)iOeF@AFtal^LQ&} zFt$?HKzd#yf6+sa83?yXeY9u`kV4%J4V-oxQsUG6NQrMn3(j+$tj?>Np>;DoBSnpO z#=hO+6swtVOR#23q91b^oN2Q=y5YZj=Si1l+oW%EVlAX?b?;_wMv$BJ825c*=at`3YX`9P8@Lk0?5FQ)en0To!aC-prWf}fc@A$%j zFRn~Wt&Y@AxS1MUS^pk{C&tFx9ky))h4kf%-`QE8ssn4ixGSjE> zMApOM?`&*^hTT)2UzCci<5T!Mg`TA-xCr?f2+l$({HE0YCbp}^Q99!7PY(TO;DUL0 zsTNCFR-u~xbk zcroB=z*H;AL}*=!qB_~CFU4sBAI>Rd9q{>i9?k8JwmPszd>kjAY+LoMZ1q}qX{ZW) zrmaYxW3A|&ZM5^7VsZ_jWA^iEuqZr#^`c&XNcMcFu^}7p=MtJ^R0*@a=x8shZmGIA zCRy8j;nI*Z=}u}_5|eFq_yN&k5#}F*<34q1xRZJKcvR&a^Cd6NHh5<=a63<@eLcD* zkw9rb8vAWSS0Yi@96NrSy|huxPi?uML#|B_HG+M|jmYPZkiXaUD}D<^NgVHOy7zU| zn&RO1)5;lZQ6uNgy%GANy5BZ12mctHhIxpiH1}$X3x_t)~#PUz4xC<3w_~su1=Uo?ta-9 z>AphD+bA(#060>FLHEl}H~!0S-v2FP>nLj$n#mnyH$scEIZlh;cZuCB_r8s{WtQ48 zXXoymJ0^DPX1rdn3tY?%eTJ^0z>AoRaSiSZy2ZRi+Pg!OdujUzjL7RTC#4_v3(yCT zHJZ0~!%ACk`F_1fo`$7_zxM^1c4{au#_Yr~I3VWMI`)ZVx+Fz4PO~z_$YH}?FL5EB zfe!R7NlNzd{p;n*EF%o?SWItxuiijI968$9H_Z@Wl0Fj^94%htEN zvoLP=U=PdAGrwW_ZyEpbU4PmScP)P2`XdQ%*{eLqd&)T?b&l8j*46L02Zy=0C)}v@ zTKtOjZnR8YtaQA+2j|2Gs&IDAm9q$~WlTH+8iD3PO6pRbf4gqWnDwSO!&;{u|zmI0bqvGUdW&FJ;^0=L+IJI~z zs-ZO&2bJHwQTq$(6Zx163IzlYj{o9(tB+$P+uFo2WFzt4R0J?7hY`vX{crBd?r`>Iy5 zuI_tJ?s}Xa_{Mw6{^07IndwBT`@=ac*~6jekaJzNcbKx3s|jT<&vSWu?U?u2 zo46-c(z_|~Dn6kW9KbHBIKu?q37<|kZ!gx7cr(s70mmwWE}*oBD&SgDba#~t`>+QS zB}qC-O~14wtq<>0QS629%=Z+H)VKkDBXXi z2GKv^pC3SZZixO@CB0ChBxQJxDBXXykYi=|H{!9uo&NF3(=ZahPN71gB#9vZsVUKH za)j<-@>_<-CQ2ja&T~lV^dq!hIFtTdLC>$WDZ_I|>GXL*W`*<}cx-St8=gZ-r$0yg zH3&%0Z?-nLn+?w;rPGfRI-5y&ZCYj0g)cnEl+KS58Yx5l z{^{;xwDZ7>{3z35gFEw|bx)cx+GU3OL|E8_6t!|=c!d^zg+iW%{Gx)sFhz_vJ90Ta z{ByNs2LDS0{kbB1#zh*IO3LsYRJwm($`NmNe9laFFB1Ivl2k>>H@?`lfQ3F_ai3su zpJ0g}b1P-++9V78q?9Ax?3ik%$8WO5z0~4fYH^=xai1#U6HZc1)s&o20qktz7V@Mc z!k=dGKTY^QktC5gWxb@Crd|ZJL+U3z{z2_6(6YSm#bbjzb0msKnxJ|S(2ia)-T!oL zgHiskfWjtv&6F2AYGOKlxkmkr4DUw#*x=6aC~GB6s^${6YcnnWXKHP0|1T19&J6@tDdNtLRpw6a4=Ii3Du?E@pchv04#Qp`!upufaIe~HF(RZLE? zlk}H}@NCKSN%|}k&iKcZ!<2OASteEURWGImI`rSq#5{PL3| z(w%KZnp*WDpdF`T)BRs=algXieuc&TN{c%L@lD2#8&T8wFEF_>KNect7h2rwEbes{ z_p2=KS6SSzwzywyabIL{Uu1D#Y;j*~asRBy@06v)Q=^icKaEKe$oW!H5|{G@zX@l3 z_)`*>{n^JRJo$JSZ@1vo7p@Hb zZ?fP$7JR=2@3Y`Px8TPt_(==?u?5dD`fJWJb1`3-VBjRdIQyksNP^yK;68z01$AeVpLD-k`&OF&McPjcoMn|u zExSnp#N^>udDfNZE_h01Rr4CnIMu4|xmeOJ&lAdXD$9-IhEyqhb7ZMHqA9uXG{_QA zEWsg4rCC`7X&k5(=O2yJwFs?U!`YZ?>t-*hTReZs?9VQ)owH!^yz1GD=Pg_?eaRfE zcuwA0qv9vXJ5V8h()@~ zyan?XpBYmV&D^UOeztDO)pO?0`P|>6BcpkOgPvk&xKpgfDffiJC%j%&MMlZ zbe|{P&#ow4#5iIo)?qzNl>w8jTvE2oOVRr*Uu~kLEki^1By{p5L#Bgq3)1S9?UoW2 zA)IM_R#ovyuJlL*r_EuwO80z2p3@=Zs*#Xmkmpf(PQbHE(|o{f7-%@(_Br@sZnpOE)f z5uZXF8!5sc21wy=j6DB9KG)M7@pBVCX~g>}P2SH1_o=ksEzjY2_Q*Ma=btsLp7wAT z>HoH-eS!GD$UQdC1Vg9%UU`>1>0UDE`vDoRBZTJ}XHpR_k)PP8DyKbu5JeHLh(Fvp z_n@wNkzOPwV&EcN;7DTXo->pKSFPf{G1@pey6zKpTv3CSM!XM)8r90v+>IdQ{@(Fxr(q8dLIz&B0Is{+%6XpE8Q7;j$$Oj|8 z7!Q%pfQ{snDenwm{F#0mBwQkdD7OwmEY(E5Ae`Gtk8o;qK!kG*A?j75fp;44P6P6c z6zQHLg#VWazajOolg}>=_rDNA;?`x8?!1?XbW;i8U!1i^IF$y@Gd#4v$$$~UKAbTp zguiVD+(WnrXWt3^n)Xvd)c;=+zKH!2!k2Kr93jJF`hH;43*=W6IP;^65czR0-47bN z53VuV8PXy0^N^-JN_V95X#@5VBA%xRL7yN*INS%Jy`S)9d_XbqiwWVsige(69U=IJ z31Pp55cEd~!S7wdZ*YDJ2zpV@PZ;$afB$E0NE;W&h)Wy~sbnM&=J-oDlt|&_w|#+RKlO zc7k~DY@_GLSVI%OYUC5_6T~OLAJdEURS-S~dcxNPOVD7wN{&7~=R5{SWZz2LAWy5J)Zz zlFO#_bKmCGQ@TrP9hGixT2WaXgf^lf*H%Lj$|31pY%w{@;1W?z zEmK5vw5-9>9Xa7KP0CY_rkI>{U6xV;Am(Lhlk^lP#vxf6V%9yRo3Y|acT26(EMXZ_ z|8#0GQ9sj)HNbosBqKDW2887FKb=Q%nk@Mz)l?+yjpe{ue4P>08B2k5kES(3$|1ct z%*)cV(G0yn9g_NQE>%-Lm~W;PM9RSwxieO?DJpsgrC5Qd+sfKuTC%4*r!pkD^iL;9 zt=`k^lLcg2-=tHhl}@^?^rsdn>29gzils&ly=buzsRfEyx>(%O7BCjKv}Mc?Zp>S? zm>I&^yrdbzIkm7E!tt!j8w;&zk@JsNpuw~57`BqF7qo zFO61qcze?CKezaHddjRidezib2 zzAK~nb;H8UZ#(!^z>fSf@hEeJbDWmvdT9n4GAN;(qBfo4sir;&9H}lI! z1!0*eqxiiG3v>Kp;8y}W^2@=a_>BNFGe3T(@WEXf#V;E~W`6GTQ}Ls;gyLt_pYag* zbtq;A9Z{?Pi~{ge@uM__;SO9R*^=Z-<2+=f+HjGKwGdq@{%m2l7)!(S64fZWQ4bfQaefJW_?r zwV#>ab#REnT^Ysi=N5k3!7m6R@+-un_?@!wI|_$O6f=X4=)YU|9X7>}>ukl3Yf*Fj z^3e&XIFXNf_gUx)KzEn~hEa5dpflIM7{a|1G)zYk9u@8k3%?lnF`ddNe&<{Gg^?+$ zy#+w5_|3QQ`!o1eDrN>9QGOpZ$FCFoRDGFX(6K(&Tln!_-C8m*jEdil7Jj&wBH2FJ z$E*0=YT?%fena)o{2pnJ-%;>W^=h(--+dN-&w*c-ikv}5^q(zshd|dk1l^OCaN`KK z4s+EJ^hk}Ukvjyu6>sE$z zGz;)zj$Z`)o`601h43hTVGF+~GH(;yl~MebTKE-Qkg6}WAXfakEd0EqhaTUe7Jd~b zewUm0ZMX2d0{n*RCttA8T?aa3P13+wm}3^YRiJYxsbo5ceq^ER03G`mWfZ>=Xba~0 z(sh>j1ub-&K<87;02RN57P`G>;kU{{_xxGnx7|Yb+F9tHHt5)Q(!2$_YWXUD89%1; zMT3rg8O?Z1mRjYj_$3XWAA=6nGNPQm20C;JNkbYM&S_26SGx}N;0^dyM$r+X%TJm^ z;1@;Tdp&Hvfrs&9`LNEL`PHBiy)y(qu6M;Mefg@Jo1v`f6ZJHemj`{qe6pS<_N_3< z`{lForG)yEcvj*F?tJ$Ny(m0$TUhSJIfvlB`*Gg4CR~XVS7C$dgP_)ZB=P&8j^B8Y z_hX$7+VPtRI`I4FpcB7ZnG3&;vJCvX%iP_&T0D3=9L&P+Yr*X97Mu{4w+p_EP`$lh zUJgwHyc;Muf+vIz{BOKSMAZjo@IT@n5!SzyQLRwg*s*#^cgN(*WO%>3*-+k}eGcP@ z>WTe$hu9}jG_0RjDg=-?=NP2F1rP6YUje8L-PbAvka+6Dw5Yg3$VYAD%6&p1Sn%nt zu4W^!NF87g7>e=XU{+W!wk>5F^Puy^6hQN!?FpD;==NshrnRqI-OP2;px-#?FI0}eI#WV77d}fZ|9x>=Z+lbRhk)JYv0a2 zlrO+u8O1Nzx3{ooH{E}I;n4SAmj^$6-~NQ0d?fq!eDn~BeZk_ah*tbsL@&NKawzPb z>K@RFv)1MmU$J&i@x5yu#jmX$A9Gu5ZLrOcIV`qL*ba-?rgo%p&|&LXkstTw*a!4M z55#soF|GLCww&VE+V&J@wL6NhXx9gaJM;;z6(i&BoMHXi#EFrdiOV8;CO#gqO?)rn zDAzBbn~oF8!fn#>$eu~xibxmjyvty_pe?&?PWT+q%(dBr)sHK(gTmwY%1?T;(mz81T2%1)ADU1$MPL0>|4>Zlx$q zXi!qNIG^5zas!sW*M^g?_PB13&Wk4fxME+N?})9NZ;RbNUyF6Ze-`|^|EB-Q zyokyFYWQz~|5flGhJSDHv$3cm#JD?I_r35zVk6S-I|^-OJF1-hwW5C54EwC$(ZnUbyh`VQ zw08)5mIUt%wFO>=rWo7#$oojvfuo7*KwTLeUwQt1No^O@@_iJ_CjWpp;D#SxS)en> zTjB%M=2uwu3!HT?>4gTQvPY^ijs#AyU!0%hk~rrAB}D=e7NeC zV>3dX=LTzjohW>#X2Gu$1us@F9Nqc$Waq%fs+Ikp>(nOSGO!|ceWz{un3}CPLLHpE zcwkpO@ptPN#+;MK9P3WlHgzZL(S;}6RaGa3g|A;%d%|YxPB@}fC*1z;RSg^1u};Sw z&2Oyp?+*x{jrG%G*N@v;m3J&}@(B8mzR{WM0JX($;~d+-j&c5mu`$==(Sq(+r-yV6 z*RS(bk6u@GLie}Lae}6?;rdR;#g8_)Voz5&fWIqneW!|3o%hd;K zih_3+GNpffebf8e0Xy=OdC5Fx{`znh+oAV;sCnfX>i=7>$n(x>z9VODDU9nC)J~I8 zGfip5^-8+Cb^YS8xO;?5(Wd0&44bhgW5*0FonMxquTT@Qw4EBx8HZcwxq?U;o-Z~V zJ7%f;rbKqP|1i6#Qh3C}a!w+VNbHrwKkmYFo9ug61vRd{ldAFB+UvP!vFr$>P+ zL;9@>0VMtu9{T6^%3P%HdAut__k(!o&i7odkj#@&v05_@U)=JWq!YhBg}Wz^c#y4Eehf{(pYE*{dTwds7k)4S@?_5#^H1i!hUF|T!l;OB!K z`I*4JvC zL!fI_M1~vPe~AZ4j3}o}1o~Tc>^TN7evB{Eig1&r z4w?3gB$ndGX3L$T)-_TSe{z5KYhlSSbTA^byjzdbyT0QHNDiC zR~_xFKA{D&F7K`y-mg#ZuF4j_S^YM=XZB~*Zmh~W;P6pzjE={K$BoB}hjU9Yulz^x zGv=h;eRuBFC)@gMlQa7BVx9-J`@KSMF|^Ca>`$h!O6X~~Kj{&grG&2ga;c@=7ji=* zJ?{?Kv^VpL>@CAL=XARRo^DqlvpcKXwnyIr$=vSj-F81`*IsC?%#|9i=nE#k??J57 zhlfUm3PUdcxerR6V_He(8!pf~pP(<=XAh1M`j9*^V#>Y*29%=D{ ztdYYq_;JMv?-zQjD`vlwprCDJX5X>f$uP>A?@Ml{*!m-^qqP* zO1z*y1hoKioz)aBsIIT)&dk45eKg=cdAIw?lXnlx0C%qQG+%4np;4k z?F)rQicqqqE@vpV=fSnD|LK$W=Cr@b_v!uaL3DpbzfSxfUoZ4wzfOD*^(GuQdK30b z*yfx~&|`gLX|*?DyAfEReGW8+&*7>N2c%>3L%$7|Rv)j|%N)V`s@@%NoxIoe=*fF+ z=2*Eztn?|Dy}h<`2J#Gg?Hanz)k`mad$98vfj2ccW;!l@`lNll^9|%qLu=eE{5&pJ zk@2s{^-TOLt#Mu0p9yV-z1;$x1}-62O!2SCU#^3==EQn(p|w6BhV^cLWBsG`>9rvP zwc#LY!`mqFgQnW>V)V(*6P?sH_)m zOI^uIt1FLL>Pkjvlu=juO?73fQCGGab>-3_>dF&yJ{rh4d3VN8btQ2h>&hHaR~|^{ zvFD5$IegkJ3`PF|>c@)y-jiR;S@|a4EBc=sr}-~jh%`GM>OT8*;>f1IRbRee zp*K9brPkO zxKE#425ro`4;q<+%+-M0S-8(w${b)fbGI!_TY`YWh7gq4S0qWh6Pa~FKpx45-+Rl5%|`#yY$ z@y%J?*t~RU^HN`3TWb?edA7E-Pxf6ft$bRUZ+ft7X7Hl2i+m+*&8wOl+M6f)Zh)8b zn+-4LU2t9_j)yjdeQnLRwD3HoZ#wqmFPiyzZ8pD0H#9diwrF|@UY*sgZTOUQ-jy>n zmnY-OVYXaHzJ88A%3h$4*2n1Q>O8AtHD;@S#5cqARjWgMBV-uNH0mJZnx{4y3C>(n zhC0a7{X3IuSn1zs@$XD|#v9l8>Hbj*O@?dyba%Jl$MHxSOsq`Cj#+0qJ!hdQ!!>@o zdzM_&B}t^a--I*$l_os-iWlwyr1MM1xaLo{@rv+p8;fDQ>P0|1X3pvK;`VQo-Vx5M zQ^t-jwCVI5xl@KZ#TfsT;p{VI>|(`h#z!S}x`klPZ@8S#E5q0}C#eJDPS=M{668oHw>~ENf|AwNxpZ7}AYQq&fz1 zyc#F{!>b#bmaJZSfqbD_0de>rdI(Hs)JY>W4%{MwliL=l+q=1wV%A9@qAIR=U($gu z&_>L${7&w@&?;>+ggYdrt&cP}5NqM)&gn~AmbY{uJH+>Xp4s!`-7{IPn>?sM0>?y< zb3x*4f0&OT|A0@^G@oIw+*$Udt1;jLLeSl0;9&#aV!)^Yw-cT(+Z*}QKVhu_uP|Vp z0T&yPb(C~0Z-yfv7H~_6W6k#+11kF{aje~bZovQ6K8rZ;bk<6e+Wi=^o}_P_q}P~~ zTdIbn>_o|?sz$m)Q>s!}9MbQiPj^hK46G=B!q-ws%q`P~S9Y-^042an?SUA&w5od&}GS{cRf z8CaP4JqvzJ2l?SO$*&*pW`1?(yMwS(M)7+M7G{3G2NR}`{D$LE@%uNtoB2h-kK=_h zir?>GVdi%l{0d-4eqKC^A9d)N`SBijjxWk6er^z%`DJCN__6O){3coW?LkE>QOpcF zqLfWxxYLQi@Lu;?`6_5(E_qqpkpJv~y80lETfDEHv2~F9c z^?_eK0vH4PNq87P<|Fe2)=A?-Up!1PfvtLFpC`T)7#C1}(!N(&`Sc2(FrO^fxKu_U zx$iX`(m7er2KQ~m&JOMb+`TB_DAl{UvryPOwFbB4@OCL^vJY0;_EIM@GCGO(iKgSDJxQU2txiX5{R5w;qQHNg<~)%_U=!vINtd5(?!OeI8H+jtOQbE z+`XbKn3A8DcK~>Chl!M@cf@sDzL0vWirZ}36x>~Ahm4!wCBANWai1mcGZ-oM+-HQQ zgbsa}Ddc}5;OX9{g*jUBu9V2W4Y*rwxS(@}ruOoyL|9-Be2GwU`O_iwCchbIK8$IG zKk@BC(iTHjacimRo<6J3zDm&VJF9~tKZ@2!xN`+^bmnqrmBcKzJ7J zjnRahow|iN&@!vHvE}04u|gutQDiH^9f3uV+$_@l>69^XuOYVTLS7NB;@}hE(cYHE zxoL%?)H84#cMlF52&~W{dD+mSl|lNnNZzWfLh+>eiiIo1Ig6M`Q!6k3NQ4B;Z6;$54BkT4;Tgd`{` z)j?>>Dz?<1rJ`LbY>St+M5UFg6_vJbrTZpZ+M;cDr?suFxVu5oovq#a|9;P9p7Wd} zYPb7--uL}~dXh8e`JLbIe9!ec&$*wvW@m>R%hEX0i#g2uFgR=Dn;^?dR@_%C?*uB1 z^NtG6)ohO68Q&Gp{R&enC!T%sv@Dq~`FIbqal0h#vr87n_Z0G}t^cM#q_eGmeW0O} zcZp$LsCWk+#C<5tMRhNfOc{(XyK@T+6W=%9ns(m-=H8C@1R<9bzdBP!1Jkd>`knr8BEgg&6l=J7bnFAu#bZPn<-LE4qB(eD zisRD8Cv2SakXuvkPA^`L)wv?X6^Cv~IK?ldAIO(?K7a7~SaIhO-$S-0Cu&iWw=D;TrTW9A?;gc-&;%)uw*tIqG>5g5q zf5fqC&SA2jDpgR(voE4){X}!Hv1~6t$MVl^qYkt9cAGTM;-k}JDBf?uKC7$vm30-@ zlX>~}`m#|LUw#lZ0`H^nx@Qa7RU9k!^6jzWGb}z&G;kH~kMO!@uh~@rv;w_+whCPp za3)xMETl3N?|<;RpD6P8tc;B0v%N3XMExL4-v8ir9~SPh+)63QcgsguGlYrd?iL*p zeG0a1RzeqGE`;q!t=KM_s|lE-t1NnvMPFmlY!4EjZVS*yx!L01Y0-L}O{tI{CeFmu zBE^cO(WN``N=^S2iNn~E>f`*hg}H^RenE9j&YKLxlS<+mi1VtCUQ%`@M$B7ZVy zim>GHeDgULpY2+$D9^L=D9`qI0m?VeRnUt3?TXL-M*+(BR~7wBK~HpgEcq7|pJQhQ zPT1367M)Hx&O4K*;Hs6EJOsD1pd-RForkH!OvJN?p$8!U(Eg5ug zYKn7~k-l2ONOIY)<~$GI^H5Z+tDPawLI}ZJLVJ9~IlmJ^2bTTJ(0R}_Q5k8vgfuE2 z_RYy3qiDgOYxMo9%=asn zioTB~=sk-5nKFM*bCJA@CWJwbKamE`SRYLIJDfBEBhuj#MI*fmX^(J=cp%)8$f@sgg@5Q-lxcyI;|VXFO;MMVr8{)OfsGxd6mEe)%d$1}a<&FoUs_~33 z9!1k0uL*`%yyx6q4chegC}`FlT4{gzUYg^Te*_-oh{s+D<#M4rp-nutJ4&Io(s*sK zF!6@tMb~(=*Le5f*~ANi7_Y&U9Ldi^n|R|5Job7t-jjGX@xmZjS81igcK{Y9-ZbDb zevB`BNgD5e;n~ES27-06RvPbRSeSUTfft4y@kT;xyc2jf@m39i_d4WEycNJ>Iumac zw8qPUdlT;g5QC*(0QM%{CL{eu8+gSQ-cz7gfv=U0?*t2PJMf|qV)#yn*5R9L;c-rr zZE3AEUWJAC4DgZ=BA$89-PIOeH3p}{kkLxxasJti?_6YpE`RJr>hRrc;c*Uctp-za zBsW-iOMu66!0@rxsqyZy@OpvAa;23HUyminbD#JwX{F`9Vfnk&@RxlG?eEJL-jiTl zufdcY$sbyH&lu?vR&oq4`#Jc}%#r734L(2cuMk4e+aW5{?U2J~J)?+0#WD-Ij5-Q` z|4TDrs~>5ih-B20z>7hj1)J&6j4$(%d28bF95kK>ua(9-_x$t1rpnCs4mCe;UIjQ* zA3Z-XmyI{$9GfUsDthJzI5#j{%nsz|pT_wC?qJY!1bTM^$JqmktHmfRM%p1^(&lsf z!Qt^h;!}x2HI7@`n-wp_xF$zUBh4r3_BKCS7fF=YH6+UG_a<8EUQ4vp*R_zlSM2}K z7W=cE@-ff(i+va+3>F8~$g>>D4q{~4C$Ljuq?xoFX^ymTgn2K;D&VYee8Kh`uO9A--(URKjqv${JFw-RlnaU=ucu_e+;YV3r;25 z_>}9kFX%t`xdS6HQaS;tc71{=CC6-?FMT5&touefd--tqHDY(L``L6w^c&chK2q$Q z$l4LasB9?OtmavAI3oamIg|fvx-jNU@!jimkH##?>FK z=}+l-3yy``d8_GQaVGaUkCWhdx^r@vuTGQ+F_$yIj&AcY7L3Ev@R#*~Z)flUXD3t2 zFZQ8u?k1+n6wA=KM4>KWe?8X-wFvtyt_q6vIIJ^fLUWvVGEDX{q>}RNqi9+`(d0O< z7Y`Mdp|}>o%V&$qRa}eU<(pp&;)g808#1DB4YJq0+2SGpG>eaqtD!g!>~)XI*-*Tv z*vn^a?kcV^_VQ)Ls3kZ~?B%n?;VO<3d--fZxQc6vy?iefK(Etl!(OPXc+aqx@8%?> zIF9V)4;)AK@+Y|9!u>?c{lIZzulum&zQl50V(}+ie3njE1<(rd;!kmzc;Y?4UjEq@ zf2zfwYVpf0dKwVrH=8Am6v?Y*|z7~7B<>Cl-K7C zv*Z-ueW$+RgK4wPN`6 zJ)dLn%yHRf!n!gf3Wda?`bPu-Z12Seq>_k z*i2Goqn*nH?b7#kuEQ3r4IMZ!&*a;;0=l8gjB#sm5iLFCaZ__sbFBz+mv;CO&+^jHxl{N;L*vrS5mDR`=k|m;G^$wKvf{ z=(RLK->B$)%KR?PxVndL0nUmgU8Cj_7OOc4;LoN##@^wD6I!P1FH_-J!Fmz)SE#Y| zE0sMagxR_bVQ_$TU+7cHtW)w0O0H4Kt%lJ5%-FgoxslYWu2fq<+K~F~t3;iUd7X{j zBOb?@D$yM-9>7;s&`=VhLiBpA-vaUe@^;W6y;oz^Vj4kv%#x+5B`<6yHu|#BA%cVy zVe$7m*0q6k_stL?vn#2AK8d)oZt{A=Fn(0Dy)}yE9+5Zdm4W{1w3QQtL-SP?@*x7c zGVumL)g1%fF;;dhGEizjjWM_g!I)H;O5!h{kzh}Q?H<-4RD7pRrJGU3u%sNCEkfCH zn+c>L{BJQzZ0 zyxTyT;oAs$Fn@1>y*bVsHvFZ3+TX8Ocw2|SyWheqHSpN$)_8~UY=&X8t^7-FeOLwkcIb@5f8>k;~lf`xb8=Xi8%WC zlqI(Ua)Xt79HZfhN3RhNb3TIiGC?#`ztj!k?|GIS*CC015CaFL!#4x=X8IjB{GF)e z_}*P%;dOx?Lm;Iz-lY~^%D@XlmU6_q(Zbs{1YU=Q_lAL2V&L6t;jzD~!vwL+^EWNI zJ)j3GSC7EnOqYJc-zkQ_$1FVVlNc<2do8@QfyX*tr^|m>crSq-EZ^R-@Hi>Sbe#@8 z)xaylhlAxM3Qct!?%L#|_$7}z0y+G*Yt{k~voxV(NZRv0x$t)uWVIp|6-{`RQJWx> z#7FU5*jxn7_%dEhW5c=gM_7zEGEU>Mf2NMN7PC1YHSaTX_W2+C{(`kR2!t{3!+n0a z2lL`HdVCwBQv-eS1f$$%wHbY~H~qj2o_*Y#-ZQT^y>CWudcW`Vve?^yOh!vWIBWSw zIQjX`_Iz<>a^Cupk;UsDi`=z77uz+WKL8x;kIkwatQU2RjCEi`>=eL|*RnmFD#2bs>yh2xAxUyWnqWtp9K6Z^j;>?1G;g@-~bOkBKa9e=udn z{S3tY&g8Ec_bGSd6y_h{6y^|4VP3TU$LsH0&r_yH;5_NYIA!`SoHG4#=9KBYj4kjp zr%b!HNCk1q^rvvjv}=nnb|4K9`o*0h4RF5n%Q#;;59doS?i#JrAY+R(=*mSJd=e;k zbs}_~2wm4=#Pu%3>E$kw0viy%4G7-`pl=wB6yPb?c^O-TZ$oY>zjTtcEm^eT?UFk; zyu6_>JQk@jR_r$!Eq?RG?+Ec562Cd(*W8m5eMawV$iW_*)8p4;E=%sAc;?Jo$3IXR zd;4H=PP~0$1y0W0`RkR-@caDAr9~TmRhEO3bDjQ&lRP^&*8fEEYpL$EFPV#zfzGVn zUU|6xp=7iy*!QS73HQyq&!x@>)gn%+ePm@abpfaof+}mcIaN`X*Z0)P;K;65?%(Ju z^Yyhfw59xI>-rvTXh}7~r>9Pi8rk&9i3Xl?Tehk`bvvF-haOMmDunh@Kb>h{2L;>{wPAKBg1nV7BXGz+>NDD~)$<1r8`RRHj{b;o$py zXP!4>wp`Hj(d&8M!dNO)X20(pn5E1;_(hzXk99A&J7aTtcJ%r5o@gI#nab<&hj%T= zeYPr5)O=6+c-V*UUw3+YcvxAWZ+BZ>&!RQ=uafIKUTStq0%DC0_tEAaBrSJXlGgil z8xlVes6n9O?7}YYbk)4bra3)0(Xva+WJBh|_*of#wOI2J>)EoAHwgrLosz6#xx19^ zNzumc>1EziSEiX zdcT^uKXGS~Q+*CmP)C3OvTMTr%4>NKcCu>;Vh>@d4pPq*^Q$MaoSpf#?9=^kKp_f2k z4ZRAQb^liA9ne(VmpdY!6zjOA#^w4hu0hH@cv#r6>`56(PmQO9y_892WZDnzNN=y> zPV7_B&ZkGjgYm<712(q}pTQj3iFe}{hqmIqm=XVGWyNz-7!0|3HQ(U3Yqk?FSG{BruFuwcjWgRD!|@%U+=K;QN0oFCXZrWTJPiU zI=7Epd}bo&xp?H7D&L9CzJ-nq%dzy1sBhL0{O)bJ?vO;?DNyIcXX5^qR2zLvBkh>R zTz?geV?Xx8Q^T}>5I3XvuujfF?DA*$O17p)6$jtlwqA1Ox-7=%uFBlrWO{pkZ+bXt zmH^}r<4LYB3nB$8QD%teOhL*nmmxcn-n0C*M0dI(yr^etd`r3l)C~=(M0UwEu?9^_ zMX=86C-9+g8{NZypBcKW@Xqw88TVKE`xsK}P)7*!jS%WIVC{y}Iv4B&(shR-iK0!F zhlsmxI8h^NB^g(}=`ysqZ8TQn1$vjAaynmZ%1ZHOm@&`gMuL$So1BtSD%}Qq@AV_4VihBIw$(MSxvcJzM$t{+- z@tf9c^|Z1zsGc5bMLj3-iscz~P1R=(Y>WF&K9lV`nasj{JnTRY;l*t!CGiX6HDWzqP24v-|GeO|H~O-;1AB|y(|!+f{`B~lJa9&O{zI8J}@=6M@KkNCw~E=H~LY;rADzZ#HK0 ziPk*vG;(uG+Xy_>^bPwp-T6k2?OiWi$(55E4@oz9z0yTauauAV|2pYQIcI+|{uEAl zjrD&s`Dhz&@8L>Mp6`m7hC&a^^tf7KoYJ32-EC_-tv|?PaH=*=9FL6x4+dXUC|qk_`3Z#vwPX8 z67&YvhpFdyxR*K(?#qV74d!hI}U;NHWO^nY-9Z9$Exhy_-%DuMEDZlts z?YBB#Pqp3fx^Ku7W(fbaFDKvaEz-W9J$;{`04pX0f=7->Qr z=JvH8+D%e-j9|TLv0BmU+WBx2fqd^>JO3bdfsEbKo^afiEx{?_ zEHggbHuOL=h!Y{&7x246JBhnzoxC5*neumyx(1aT`5Q@0=wdxrAmOTatiTVEo|mHfvTL|;K^84D03SY%H5QyDh~g180HtwS3a`Or%tutcY|My zzfoTd8Hb8zWmyUoOWHT(cdJl)sdNZ?^sNHak5LYPY(A5PsRn6B~Dqg{TgbNNLz0A@Gsqq z?=Vg&d2U2^*2!lAqxNT=O!~4;9?fBmAL@yvhmVipDSyV(I3xY1sGG-jKiYC;_lcIG zZvVqFeRC$}y*dRsK_q`U^CR~;{SE(GV@3P|zJbx%;&(>s-`n`+bbM?3>scyBU(Gyy z{dFD7*II++@AQqz#8k`P^*ZxG`b(K#_WkSYIyJP+*4LNujC`Is4HbxNS?$M{uP_o= zJuybo*ztSi_!=g=fQLi-25@>oc($O+GV1IqgB_A^Nd!O zttmn)B`j_+e6;Pg)vUL7ioEipwG(L=mTGB&-2n+E?ECL{Dq$o>m!0oR8F$X!VI!&z>fyG~SDrnMlQlTAfJu6_KX= z)QNSSJ*GVC`wQWZe6!HMYI>~|<)$}SUMNb~5tK5tI&y+1pUHL3nvD3HqpGj@SCdSuN~e-GOsxoGJgD{^J*? z#@ofYK2GU>_7N}p}2v?kkJ?9+vr9hC3s&RKD^#+gB2%E4B^Gd+tNq^}%!&ZmP@adp4aNB|Ul`+a&%z zS;LsWGbZ$lGV6sUcuS#PZs;$51f?tIVsrW%bHZ${0#jH zU-`9tAysc(ff_G}H!oY|T0`@!ro|~KyC6l5d=59u@5`QMr*w|!5mcm#ZQ`CEbS8A zmtv~PeBl%G^qI=rPo4QKQi0cHo$u}s`DuDv)a;p3Dj`Y+zYpy=D=eOfikx_(Cm4_7 z3n3)L`<1FIJtTc%{nq~Mn#5FL-;#*b7ew~f6|4RcMjq7O8WZm+18)?hvr8T>44%w) zh+lr|6Mxw9&UM3H8x-TT_`?v!= z7`ce-1HCW(rzrauf%36^x(0T_70=3;XT=jeI_Cb!qjkEbqXac$UxBmB<|oQU`5O0h z5Ovrod<%y4-HY$842K&UiJFabz%*(w@&tPA7_o`Y>eTm~xZ#$mYo?u3%Fzk!dcR2Z zaJ@G-N<}X2sgJ2V3JY3dJ(6fnNV=@~UlMx+=g~w%eR=b1^(UGmE6WqFF+ZAvC(A^7 z1_sSdZ~3Ue`B~l&)mb5;ugu*?6MO5?n@#B4D!(;|{aGU;IfMFAfpnEAR%hfk+UQ0O zyLs@@W#e>@OIH84^bgi63Mnh8Yr1 z8xjXI5@QXCM+}L58HqCti7%t>k>icQ%=jbgA(^+!jl3Ohq%zykTE@)(5r&NW4h}AR z+MgjC`UqJwocQ)<${^~BgWq`fhEHe3)1tO?N^ip(npXAYTXjxJ!NYv_m=hE450?dx zm#iGC+K&&WZ$mrhx6udFn_<2I^E1(gl@F%3fa(Xe73NzolTqY(U@KZj52imY-peED z<7oMO8sj3d_tuUE74?=ip9F`|He3dV(g8TCc> zu6{7RJlY`IKO;_FhIYv`f&F0m_I$LURPFU(`tPIodIRT2FoQ5(3dhQ_kKdin4wpQP z_L*?wdsx?U>hwaS2*+h^!YHYO)c{A6+yxnoFFUoPohgAHAzJ*$lb}1~mj(S>!O4yn z9+FrurC_JH)9W4d?Dj-gEuNUjKJnSW_tOWWS?}K2i5p{Q;EvcCH!S-^9dS$RDiQ(s z==9&4ymrz3b=c8b=bYRcIE=JBj4$4mi^tVnR~OkBSl_U*YW?1gTh_m}@x}F#n*$vU zH&=BK!706^uOcy~c)Z9lr}Rcp^bw=w_c-@l=lJ9Cg<-{O0S}`z-A=bZUb(PT*)|xq zpI9iz#hlXXU^{2=-uJU3MDmq5_w3H~$1hkIRj!uORq+@}O$ppPBH~A%-?`k^eePnX zdrsoqMN`##Jtq;k_p}oKJ>Yiu?kQX3Kx>{KBareoE?-HO8V`&env-}iJtn*bqiheR zPlq{5^*@~OIJ6?$7qEPOytM$BLs(o?M|rUuH8VynIvm!|kB=TbYFP==Uop0n=LI_b z!zUF^nl$M&XsXfDm{jEW5GLQ$;m-xay!Dq)!EiR7xObUUPMG(wG2}kZ8t}A5sN91b z<xevT=5(toL{UU+%cvZYJsSFM;|O{|4W=T)s(cqLZV^LfSm z%i{B@=GQ`MUgZ)>TwXPA>C%NuMR@03v7%=F(iID<=B*Gm^De)9S=B;G=x4f_ebu~c zYHQ{#t-icw-eMpxxNO-K%MFXFWtS{pGG7Kj-~Z#|jMiVzQ51U*m={tZO$^2Tvfdq{ zTp{c#t^x4!vt1^h0+`|S@;T$_D$enH`Q8-;+%N0pbGFn~+%N0pbEeW&yo1Nfckkg1 zL(DH`G);N#_%l_3*k{Z+8>zfp-ofLw86)H$(BhtzluVDYmi$;D&-o##xEjD!+%N0J zFBI}_DoVa9Bl)om1Xs4Yiu-F_S=tx5OgsfJYvtv07RyyUhtbRDN>W$xoJB7`>@xAh za}>S&66ZYF`=C3Z{o<4^JSY{wN?x!0WD);;TEMf?m-uH{@@Hiv`4GS-)r(hZ@yjfJ znZ=)G@uyk*=@x&w2rt(JNHtwQ2s3~gLT`9DW9X^?TzmO5#XKU@s{-0D&`tT-mONKM zxGI3t9KCqv3L2(Qg~hKBK3t!Hps%h9V8y2w?;;_8oBPi+_d1 z$EGzy1)Qra{#DLK@L3#K68zwsD&SmW$zS8_gn%oLO>l+^IM-S7*E!!(@*L6igKw&U zbAu&+gUD~UR=B|wNBY-V^0gMf-s0C={058PVDTF*ext=-ZShxI{AP>aZ1Gzxev8Fl zXYtpG^hhzmg<7W{gc-ofBX49X+MFSV4v(UQN>lE2Zy-zfO5B}=cX0$AxZ(e3)l>__^ZF2#T;JQGoW|)s}pxk`D|1aOZA||8)=l zH0ODX|1-tkB-|G`CoKLSEL!hNVoP_F^Y#Y?sOu@hHU;kloSLco<$AXZEjnh=+#jSB z9orB{IVSC8fX`-;zZtN^KoOQ8jxl%ME?0+N>euyYP* zt?2*7ioYMv1!x~#W$_y<7XNz|eN@T+gOD$E zerNIj)1t>1T@d;=%c2)r^fjOvo`(c}nRBbf=lxt-5%2FT`dN$q2hhY@3SEHHV*lCV z|GVOIA5sB!QzSzmm!Glr^-x!OISoAE=OfT-!EI|A4Qj6bi(VtcFtS1U^YWw{b z{~H$lyhXoa(f?-AV$rV={zCWTD0-vQEV|mFn=E>>MgOfuKVi`?S@f%*nLaYT=Q}u% zEu)ye0WA0>e>rpk+PC8sEz8S7=R8HTeOG|?>y?UTpP>Nl)z2vWIzcaU)+qWOL0{_J zsN`k-UheErG{0#D&K1tXivF3PZ*X1&trf#}T=C`isMh%pMNbB=z^Qj8prT~>C0>IQ z^U#e>t%qLiY*Dmy-|Rf3=ps>mTAUw%){6fB%A)&~Joj@JIP08Xo|cy1lhZAFszrao zqL*3pN{eo@=)VCSq?cj&eS1*T2oJw^-vQ0^SOQ&u_T3K^JzUW3&Oa%-P|zLDyB>a* zlMO+-m-06{llTBt4Lu62{n?6UwLS{%A+g88-3lT1TDWvA?mxo@33&)a?W(rcMxJgz zfUnOWscooVyLM&0I{VxZlP9?2sP|Ufri+WW2SV^X035%91A+$fIyTm}ceb~!T{{HJ zXePRI=Tjigicnq-PAgiBSg)Jmdp{lX{v8t>ohkuHdaJu z&X^&NYJor4$D-ncy)6wmjGlfoTGlB-JOc)?)FIR9r`&clu5Rw^LYPh0$lSIDbqJB# zDWgz$s0Af;+G$kAS7c5tPR3ugwlM=|?Qmf(fT?ayHe!o2Hr(VkGDmvA!+%yc+S#Mx ztxDTaUVao?CZ0HoT&Dp8&07oBZCKmYT#J*$ZJ$w4#l^w2U%8W#Too;mQZ3 z9dCXRX|S#~N3JBRy_yYprD% z@g_GnWTH<_eY;?w#&+X&N8ZB$OCEkD1l)~Z zwC>!{+=ZQAb<;K4@D-4%mU#xlZj$!z(;U$izAD${wYssUC&LjAr|~ z7N>n>%c`cC;=XaglGP;wJfMWFtM6#3)zz&DgEG?8EYBM=ULWhAc}9|FvvhY?eKu)m1UIyi1_#dYhXnCS+|MDqx{fy5@iw)$0!8PdPg zsE_r@WO#Kl?~(N?>ia{Oa7M7Y>Je1h@!n7#HrKFzLvu%_a%HJ)=xFA3B5So6LV@Gf zP*rGq{Q;7n3t9VW-#Z#`XjErc8z8NmWc*oHbq3KZ7HgcS)M3lAY8Lf{g&}<;hrXb+ z4=BV^Lgb|lDp|U)*<9hBTgo7YWM`8?$Bf^&4w;Kaddk}r*pku7#Z*;EHt^eu3VGe? zb%T7U*EKh=_8injQ%Bpn+6}EOt^AN1ejBOLfE(XgpAXW2%6tO`xLj9{>TZ?1zQJxd~o7ln5IojW~boqY>0A zJJ1(TSnMJoN~n6vGcO%WpnA*kwd%OJ9WO|etPGVk>e7sr5qa^xvp}%{gEI!*E0r=0 zz=n=Sed1uIx#YEDVe;BAU$m8e5i?bb;k|fCL{^%qAY6BLwRr4A12V&5gw@3>#J3ZP zu|dJHt@$?7Iul`j4(hwq+wN9rbdCxTcRCqimF1+Zz0Io^CFn$+xyh8~goWNETr z6Z(cSG4L;FZT6`1{2&*n+!KzohUSxU&z=7PN6ba|530S_-;raIp{GPw$q5~DoEYW5 z>o^c~D8Gj$EZkz7NeBMGHP?_L*tFLYY@9^Ll00DRY%F6Ur3v zM!1E)-&64fJ(PVB+=X6{XLg02m-`GuSk1vP!_X0Vr&;Lx>TV;%gMIbT57c>v&&s`s zq#5tf59L^>|0UVS3;jsBds&_%75cF{&kzH8j2F}ITAJ{W;Sc>Ug$s9xx)XxCuh8su z97ILr2fT8y`wp|QFn=P)5<|!2Udhl;)!j$v@NXj@CdUeBitxb{;Xkh8_o~XjUnuv# zl=&TkiJ&>5U&;F#7|uT`oDe#ZhO{?mm2JPTO(qs*@<^8n3%!ugIg|5@H87fPW# zpifPCp5aULQ_5Vc%o6&CaGtNsMauk?GP{&1%JHuuOZqqLm%~Ikn@ICDbw|_lDj$%} zGim=jb!XN*MZfOSx#Yi~-iLot>3mq8AryL3h3ABl|Gg~Fq3J3=A%8vGGyg^TL%NB0 zz>E?XnfFPWplfI%zv^frAJ))BK8SpK#c>`Z4HKeH=pW&Z>G!WFFEml^Zlie=^2!wH z|7(oplK#2lJfiHsO%wJ%Q1<_z?0*T9;VdG4%E4>|%#$kIZ>#e0j(X2gUPU~fSLuOt z6!C%oqCDX}5%EE~UrijCWi)@J(hH`D_q*zx(Dzh)ksdoK2lKni9Uh$!>K7C#VgIJG z7v&F3QQlyRd`_$I|EJ?DqTC-8{`(GQgFwU4{WRhK*Od9VGGC?n_l}dI+3z^Nr}C4 zzKAB$BTO^LC+k0?>lo4~RDutBGHKw=q>1=c(!@knE=@yT$bm23$3ggE<$juSUrH0@ zL*N2OmwS;Ct5NEhJ_auu{k_?9Rf9geZeUbutYS)_HiLC;mR2tVvEBaQH0 zugs0g+)8th@PBA}0Ov~OE}JI&E1-#RP87nDH)BmIR9~vHnU-&=DC(brw zctrdWo*EUNAvuOTEl~1I5J89H6_t3<5^e9c(ze@8=ygYn=K$#6#d9 zy-+ko`c=?Gd8(!f`?WOj@e}ET%HbK(6MW8bniKJUDig1+peHL+)LWB$&T`UWpTjdP zY2HW^xJjA_|NS(JeYi3m@AE-F&>GrUL##-B9v)ii^oM=1H>J|XYV0}kzn$#nCd z<`d^y`jMF9X+P5EglV2G>lZ%?T`6fSd6-5Mi5R6B@`@#9_r&k5_d<#Ykg^Iudw5%h?=nc2D||gQI!?#kIG5$P7aQ9^E5(e9!;xWZQaGh+rrN(>x69#T|{go>l}KUBmT!nI^k{4yi2 z7R&8i3ZLYdt5?Pt^z<@}M3OS4dkw`}LWbozahDTHLv+0Qt(X8aDJ4K z5hpuCGebm%j1lC)#sNRfH*;KDj|hH<{J^on4-xi`5`KuFfvU#~KSbJ7_Kj4qYR*B& z5M9_oq}4~A9CO%hNH=-_Zb$(=6z2*I7?g8aR)E~$IU&Lkyy-?hy90I8JK7|(t7FFW z*+>sb>hZhj7~`8RzS++Iv*!x3t%v5R=##<_ZM`rblw#YGY8kXCw*qt+S}QGgBP>k0 zF3@a;YNh4a#~B74Q;gh;WV<#AIvbjDZ0CL++Qi!mdN8~@VQ=E)8+gRgc#m6nJ3+HO ztd$NQ`wk{vp@GMKjmA5MXES_g9uJx>y%t{Bz#DGhr7b+V3qxzA!*>c6X81}Cywkwb z;X^mb6TW`%!q8f2JdUB5cv0ZxLnsMNQ{z=xc<82xaU6&UrSUGd@M6FVLm~-X0Il&> zT6o(*?*m^ejrS=FuMT)o2qmG%Kx@1$7G4y6spa5nrSWdJ@OA*N5JE}lvCtatAqy`F zJammRO5=US!rKqLt09$yCa%W&zJ>P|@W|In<8d9ZnIFy^H$RFXtnvQc!V6+y63dfT z8jtISO}u>I@x4ewv;1qkNeB-_Gxb3}@CFOtILMiJg+}-|-lFkpEWFYo!pAXY6R*?= zU)aFoeQ{>^W&^Jae64hR*IRf|;4vOa=n`lhzK1Nl{lH_FRx6G76$`Hhcrgeip(jIY zydxIgaxi9tuNCE}4qJHJjP&F9hQ|AM3vVkJy#hi0Xgsc?Hq&nx@L2AX(528CuK@8g z({BaV%kBeTD~)#=Qo@7qNdUU;rs{`d{jc-L5XHTW2C zEwWZRd{F{JmMswXF_Yd!xr8i;8laKmBxFy_@3-1*0stKT!#`~m&R}*ubFl3U@3!yb$qlH&7N{)L%L@13{ zZ{a0kF5Y5DYP=m5UKjALmrx$O?H1lP1Md<8@4FUWFYxLFlKj!(`?iI*KjzkJY}4!T z{gZ_kMI&wJAb77@c)h^88gfbKOQAL12NvED;H?5*E6P#5Z{d~BbH6i}gRk)-_zsxm zrF66$rxqYej?a@IXO@>b;1$9?34H~$#*15cD}aY8I-_*>DlNQ?zzahv34Il`##?LQ z?HB?tVd3pE@UAiNzG&ec1>Ruq$K4j*Q^1P?CkcI>f%mwDHy)MbaqzX$@%^TS_XhBi z5Td>TnsTfUerDmF0v=wujM8}jXyGL)-TJf^(i-mr3ooeVB{ZPMd*8xq2cFK~dIRs= z9ILzlkL?VtG+sI6%<{6$z-xf84&U__-ijgcuD0;@7867HFM*Aru-qNA?Nz7CKqn zL!y<&%Z8kp9~Jlr^+PBLy$)LAO}6lyGv$0VM1<0KpS0wbKn|bkjM8!!S^kz*yXCYU z(%Roz3-4;+vHWSJ@vgJ*qQK+(k%aDm)_6$^uYHK{ZL;uUz=M^*>oV|mTX>s>!26Pg zS7YFD3`d9WdkQZAJF0EKgJ?!kj_SJ>-f{>fVNbmgnsN|vUQu{6@l16Tc)R4I_@x}n z$s0;e7m-!y^y)BBkaARM$Prib5|AUUm6p@{yrPOhIZSnC6#b+gj=~2lX1P#5hukbp zz@!WnKDQY^N%vOc{b0dWO3TsIaUg5>IQ0GSE!>RfuR}9@EEjaD}x?~?wuHm8xVQ-!*FpXRUq#41nUap`8e+o z=kF($9LkR8iu2{7bqD^C?2iqLRklA{pO{)5Nqo6-)GK=u%f(r+**HV7E>RYDilgHE z(y!sX!wJ<>>Uhebd(vS|4}D4|M zhU00*Z9)3k+Y%$=KLNDS-gG+qY2(AiXl#>X2Z_zXY6%^~av;^Pz$IC*TwW2(#7$MK^O#y1T<(dhTe72{Ehw247B-Rs=o-OD+X9G;FD8C7se1?Oe{k)>D z6*S_fDjRWslzf)EupBJpzD4ZEQX<@_fScTDkJaAX<)&|fQqHh}qtV_#r&#Tn{a25x zFfZ%w?Xos~DVbXuJKEaoS0RX^EfGRHg4JXQ?UOQ^NSupk;=9893ZcGbTxqg;quh9! z_+h+56Xo}U_RJ^R3qI_HJ6j&O6-q9q+{bC+JMm|&gUPHyQSp+g=9#l%0;q%?U@sCh zGgVGWrsDuHrsx1mV<+7Jo=)DsctQ)wfg~5@OMX_o<^f2&0a8wPg%-PlU@LA;?;m&0j-tBtAd4zcN`49tHfhi zG@gD>b@(0tk8)aRyz5|L;`M{UGD$p!OXID_vl+f8l?)7_G~Q=HnQ||Io-IW@a(93- z{lzhPnRu|))c)?qvx)ad(37FH(*Ax47A9VyfyX*h<6&u^2QLip>ou5?Bl!fhi5CSP z^PK6zT3O>A#WTZ49@QoggSGj73ZB{K<6OzdY4g>=-`@#j`9r^`Xqs)lFbJ;cLscR) z!^eDN9CVq-qZ0+=K|Uy@@lZoxgU#9vjjGxAQEk3iGiRMITZbRr=G)1FWVHE;oHIn5 zPoB+QgBD-4I9D~bTDS4?#i`f%JR=-!F`nVSEq+FPLOkayg%h%O7YgZKoayU}1lp6h zzTa0iT9J;aR8D&Y|xnO+ZlYo*(s5ElR@<0XK?ClZhTa{C_a2Q zZ%4i8P`I3TL(QHyIpIt>l741+pgV83zdN+s*F9{v)1AAUzGdN7p^xi-t_WwLXp!>F zQQWQMi1S8yM(JPMeWD$tiZl$Cepj*G=WS`}T2adIo=q(hx@^I- z+mRU3&(uk5XVxoPw)1j4^QWwrwpw_1S@b>1J^d{}`tP^+KTv$OqY9Az$1VN|#h)wq zBb+~2d@MN@;g$F!9W2?-D2DGM#pgPQ0<^oYu=pDlpCzdPeb_H5nkA2CV6nNWDpcLH zH5c}Y)lCrbwsLE+O?|K}h}>e&#Y1A5qFjeae2U9)kVIT&|cZDUu0Cu8VSHn`DRu0O2DF##HwtEKezeJSD62&i7~H`w0vfjn&1 z9Ar(d71)mq*=L2{;s|PSV899JE zSbhx}J2%$`WgB_-7k454Z|Xm=F49VW!R-FSVN^IQr)cu(Qk z#QQC1yqX!M!}la?OuT*rk6&MncMQ)a9*MabG&fy(Ej(QQlnEcdof_|7ExftF8!W#6 zV&O5-bbQVJ1Gdn3!nX=E^H(b!Uu@m;#5dQ#V{1T%Z<>YI1)Bb9rSTS9a_qNg9%c1& zi;{~ffF$U_`Zx5)?B8(B*vIMLsq-pkVCWGjQ#?Xl9^D}%*Czb)Te=`RC zrAIXgc>0kmcW+wD9rLn-^HKeq^JmVCe%$`eF@7+N{!PRQi~fzg+iah{HR##ggMI1! zLALyJ@$6qH$Gp*+hf?>e7CUc05Tiu!9_gkp@99}#T7+n${wlMhi zJ|S0n=vaE6z;wd$Rt$N!8ZoJO$Jz+=gK$@(kU9+cUAU8}tgZ#O1z%A&q3+S!gD18G zev*D5{7Bu4=?9h{OYfP@dtvv?KBnBryPC2vwmmjp(&J)%_4X{>M%M?Icl7M6%5J+MThn#p2z)&g{i%X?z+=cMe%Ojm2nbxj{$cM?$?<8`yMnaQ@*o)s_?&tTNWAT z_Gitzz;Jh3T|=Gg%9(zm_qVTOJRf)F;%1LbjHbu0Oz)gDJke8$d#lgBNZnr%!Cf_z z6HipW2Y+SE)6WJH*Wnf<-f&&o%lpc<#Wx!`%i>q1ceb6E_+I7cUfk*&a#9oGvs(^irpKSf?aHqxY~DzTk@$lg(|1GGa&wMM+1RF@?6Wv}A3S-c z!D!6?seK7&T7Tcq6%t4FUu>!B*^$0CoKqI;yY^;ZnXhlx&BIcGwU@*8zHnh# zuCUF4t<#@mFOYper4g@XTMj&!p1tGolbFBk&%R>wB|H@@MFqLt_0WH_w60@x+dF1#Z=>>%A8@nedL2pKpFR;Y@Yy>wOB4C(O0xFv(Jqs=&BbL~n#|3cj5y9Ol(clqW^n`;_q9?#tAI|uh0 z65(pm!$tb%ot-*_`^qMXaE$ADG(o!)hj71ZUQvo+d$h;DMuy~J;l{yDvtQUusSxf$ zbn`4%J!`zB{leb5A%^!BWGM;^MvadJ2P2|pz^!DV~ z)ZZ;GtkX6z5!)l+SM*3(#*=b>+;jsoI(&DeeoouCo`yB2-G6_j?-!@-Zdmzv^E=HC zt$c0etnCe3JGV!+K8^UCEz;{3iJ~A4pipLQ3!F+;?S9-Tza96<@~&s3M(Ebmt}w&TG;sRgP0HB$ zb`+|+yiTNc;btNEPG*-}nklu6<6!af?MBK(Wx5oql-i#BKNs84Ju|H zDtyj|dy$Cx>1e4)rLF1P1X8$x{szQNiAT~~or=Vk#JIJ2m_6LJfOqy~_ZF*CJ2AyN zUFJ-oct3F0CvM4g@^<`wYKt@VJAFTVomjV@Y7w@d`Mso83F@}r2Y6TG7yMDLSL5a9I_Odx>tW2+DBEr!OFkszmuE29yLJ`t zv+?2&cbRzNeKuacm*RakUK_KuP4@*BUpDTvCmc)jx@Ri6iucoa`ItO3Q~+b&Uj7*( zzNt(jR`R)aTBuqt&(TIljqrYD0(tFF$E|O+>fLc<-0BVev5v@qQ7g=M?kZGM9uSxe*~H$v!M%6 ze*RI>2LxT@ys79?K_mZ-&XO3saOL8YtH3ZDJ$sV0IYp!-2Lqr`3yz(-F;s1@&=_ULkD8 zx17lXGQCp5u6E_kwe>jHR)vs5R*bsk9M)nOc3r!SE9vIW+70#Xoj89F=9p)o?-;KiCtZ%skwC(k4Sa#8P0X}T@4ArcQaa?rdTWX zn~O1|&{TEC8%);lxPw`m=g9ZmkA6Gf_Yhuwma9-yjg!uhYcu^b<@~k3Ovyngrf^us zL!kM-6St1$G-zcXrg@$ke+8d480F8WiIOjtZg6B{I6pc!+V_h^DY8Sg{rLb#_1xyxuG9j>N1PwttZJ>N5$Z_xx!2;&o^ zOKCz*oNWd(MjGyy(1hI8O70G2KCaAH6+f+L?u!dmI!+T!;BHeU_m728neg5SrU&=! z(0r9923`0q@mJw2E7C|;oqnHH@!LTY>GL(3NI!lLn7$KfULfb!nScMiacr})aEE~1 z5@=QuL-D=qOuTO0%3&zieAS2I@eLn}C*SoC-Tmr z7v4A6l^FD0{{PkZ_!tyPt@v%DLh<&Di}s^1vwkUt*55$3I9X@lH5GLj46$#F^@dg& z53idCkBdF%o_N{Nlq1~)ZQ`u}O@Fo0cq?Hs3_7M5xxmxmBaX(q1J5SjD$uMSwbFRE z!NSBV1YRlZ7(UjY8gCz-O}uA7*FbBf@%F&N#ETi>WBO>k!+19Ff*`iTPAiQ^e@wjP zz$=6u!^e6?v$6{3A`}u zilN!k*LaiQ9{-v0!a9Ah^cx2u6K{`!$MUN2F0k<0frlzQqjdOISaOd+j_pLP^y6~N z-^1{i0WOBY9)tFmqQZ+R0PY=W(_k>c7Wyb2@Y!qEti`z>Zyq8I ze}733rSv0BWI;wHf!B^emB3~yG{Xm{B2D0LM)l+6`9OofRzIp*x&h~Ui-`z54{-rz z8{~w8{GAbvo_{`8Sk0ITr(uchOfbF!cWpp z@;F9jeBD_s5TeEmlA~*-`cLYnO%_*vLCUdkc^oQVkpR8=`r5{C`?tEIh^^Kg? ztMSeTZ_`|-e3>w(#>=)naiU;SP2Y)4qx*d$bNXI==w2Evq>+xhfM>GsFUQ{@{2{UZap<$0Nq&g^x{W{DV$TeE9HQR;Yl>)fY% zbUX@r@5FO*uJk#tk1?m*>5G@0da0wR4s$N>bL-r*-^|Wg_-6Jn-|R8xg{OV7Z}{R# zb-_6=BF4Fk@p~Xk;GTG$=x@9(N7&>&(9Db-W1QGw=%diN2Yn^kxY;@zIWVV(zoW(O zh1_R38k_s<(TPrsZm4jkA0*os&@T624rI?vWG@<>@XZ=s*O!hB7vs0$o@_aPL^qT* z@1Z^ou8I=;9o_Hu`FrygpIJ8|g*?P_E}nDn>>C;E&0d(7h@48!%ByoCcckm)igjhc z8Z^#E4U^tBj;|KP$S z=|4VzcO@62z;_=>AGm&Nx;A+%z2m}?tyQODofxaXun>O9yV_rCS$66x9rxeyTFaL7 z*yL+1zuNFxOLj@ojik@q!QTn6-I9(h-;$oZoKI8KuOH*hcYFe)+&5sn8+yy#Thm)_ z*xOun%IVx&zcu|C(3@{4i)TH2TA1;|9q!Sp`B?i{KKwa}UOc)OHWQo@y>CUPqx=oNcPwxu zeXQb0`gn!3af+pxbK-M3w>!=h-@VSgbK)iOISYgDE|NK-Y%W+Bd{54L2JX!gH`mv6 z`nqS~=K4r{X6APKz`en<{P!(8bzg(8d%EDAWAK*s=j8r4otwlv4t}ZNpKb6%XZyc| zJMV!T5xkNNFDmj~u33$V=WLN;ay}_fJWCnBcxH;pm}T+)_*4+TqU3~gI10Z7Z`prC zJR#>tnN3zD&szdn^~9|u9oxT(TaNc7|@1XB4uKH;PM ztq}ELp!9~mNWxbvX9Tr0-gPy8k2&DdCB07Zv2;|_hR4!JEBxiY-f;O;th^n6;nPnz zC0o+l!5@F74D+#ce?=$ezfV=n!Ed@^7JmO&5hy7?RGjja_*4E8l=c!IW+}Ii^1nN> zvn;+M9m$t&^HUG6S>NN7NUALU@EZMeEM1tvmm!g}JB(MLG=MnB7+@A1>dYtd4~F8s z@>}W~|D5ygX{)xb@|WlK`hO9e<0}uoQgAAH`^dhMQ%*<0Dd%?V{z7i2=g9e^WcsS; zC{gyfZ_SA$)61jD^rg{k&i;zE(lohN_?e2ls>As8P4)K%r;hABBgOCx#}_2h9|+*> zZx~&-r(_tuF|^MW+~A^7iLAwWFAkZ(-?lhYPWLkjtE#c4Ur z@|tP*E=^lJF|q8_%Z*v@XP+utJiabmJr%Z7WjuU;@02bYRhL_R_M-f{z=K)vTWk>e52~MvEi(^}oBKqXfK?#b+k6-uJyzTob4(sxGeaL;A^^!ZkT{ z7gbLJHK`^GIvd(oS5iG`VW@6&^+bGAsRM7{9+?2M{~5&Za3IWd5b6cvS=;?@F!A-C z$DNO-i;}ry@mDHiZ*NYbYu>*(=|6|h!4uWF%ieq`H~Zb!S5{Ud^%?^2XO#rr4@QC~ zw)z7n{G(T%dGW$w3E%tvS)W|gUAgACEIUqVqEEg}Ie6O}`zny$6^kc8)0|kBjhwjv zsh9n(bL07-fn4XjAADyvQ>J>>qOoO$4imd<2e+*ZEy2WcnOQC$Xl%+hGx)S4i47ILc; zA-Jos6n;`OH_nEckCg`>TrfiXaz(-i7v!Yc`h7V;{Q7fz_zmP#h#d(Z-0lyHY)J;C z`M)8LY)EvU#5z8jUuGxfuX_j_cA;IxL~#}8J-z&FFa6xaYttpJ(Ccxx#n~Ukx>c~mc-qZO_SpaJaVa~9lHoLBYoIr{D@&a-;?lSFuzWJD%qp7;>J2#i<0 zSjg|mNEK(E_z-X+mV89WyQMy2$xpH5rwDnsd`_|C&$i^xw&c&Y!(*%9NPk$Yr z8Fp&U6W3bwM$nX>2F-cpt-rn#hRg!_>gzLGR+0`Ae7r|4=y7dV$Hn&&rip8f{VS~0xqESh7bT9MB&QLRXS z$)b54r&i=YX3^iX=pS43FD&{Ui}w39x=i0u7CptHD=d0}Mb}w$mql;2=x&RC(4zN% zX4wfNeOZ3KY4M*^`~wgo|Hl^ppA?1*re8viR#Q`ZE@NuSI{;qF=J;-&pkf7R_@GwPN@tS@blEzSyEKwde+m z=D4(0#J|&`9|q0zS^`~w@7s?o{_hl@^E3tczWqtj%$5Rt&qifQ_mZCIoTcci1%HzB z3D8>6|4S{pS;_AQuK?exTP^AgA$W9Vj&b3Kdt6n z(9NA4wGHZ~QtT>eZ@Y>6BkON$tgXMPzC|tyf;}!;CdvC=)VvQbBY@CwUt-I|T@VIVU$kX}ScHGV^?&8H0?8U8+3U@8;M{aLx z#x?M`C%FrOT-$~FHTk@*ZB?Tq9e~}~aihAA7Qy2Ms-_39l5rCuuoiDAkGT3k#J+8V z5r1r3MWiUww4t?uJ1$#St-RS$re{qUJP`|&?4?U;mnt!o&&J{|26pYb#*9ne%uPx6 zmTg>@%b<0LE6VFvt~K*O_=m8-ITlt4YN#b8Dwa%j-ZHF6rivyJIIiDuIyPz_Ggow5YwcDdVQXhDsJVp;+~m!q^q3css<37( z9g$+0+ej5VW9cALHd9$ST%scjhXw9o7^}(=`<1*~Rtwm0IY7nB;7{tSovR)^*c4JPi~;Ra&8uUWum zN)#9U&<|B^_0mE&o_fEmYlCG-VzYDxgDalgaEgn0MGPT;ySxY79O1p2!&{m~Dd)x3 zs~c5?A(2IW#2hpP%%z7o1_94wxfT|FS4@xC|2kitSN%&zgD(={TK)k{7*SfnD%fd_zPs)+7FZCezX^BMeEg?Uzi+a z3$2nkp+>d#7IY=;L9?$BYEo-;Vb&>njWWBG$#rd%+osHWl=*-%AELQh-GiIJT3^JA z_*^#@YF78B0`#|}TO6^bJ+u~khDonO*wJYTwL;TuL;PsAt2MeXFC)F)aaxu6d1XGL z%mXw#9Ov80d`6isDDx#{{(~})(Zt3Ev8ER$`??`S_O~=Q;NDi6HzHE#BQjlir-uI~ zq&H0rybPzg68&77mmz;>-i)2$p^5d}db z%Y+G0VTyc5dLk+!os!B#&_p_ka_~8IZ|7#mxsUd+{0hy#kz-GxJ8{1;X$(v~MRU93 z9Ht5RAJM#9-Shc*b#L_-u*Q((0cM=$4uo6L*DG@sO}qzdXnqmpf#y93C(Uk0jGZyR zs)>KED*yLmsWj=`>b(Fh(g*Zuw0}^gFU&I1h=fQ_l!FRIFQxgn@-Cv#9>>AICro+%_kh^6wN1bHZILYGJSbpC_8NaZ=*cZ z{0_oP6M-8}^AO52&F`Yz(R>=^nI-}~p60VCe>72uCe!>L;s(gj*HyVXtm=>Fk^bZZ zZwXDrkNL=Ote}Z_ajibngZawzsH2JWVE!^45;TzxqMk(j+est-90O$hH_}A>H_=4= zlQa?ktuzt;Z8Q=8?KBbZ9W)W|oiq{eT{IEz2WTR`duSrQ`)DG*`)MM+kI_VY570z> z|9flh)i=g%K&gJ{fYsr;|2cqRb~fc2blto$I*9{Zv15de-vE2jeqtY4^b!ZK+hu>z z^~cj|bj8(2F4KQ>IVy1CYwFs%O*Xgx;c|6^+@6Tbb6r{uWZi<6VRgl<#m&ciVZ!Q(P&x?c>^^T`C%s!mYqFTS9;FR)_zq&JrDtsz57#1E{7!4}*?D zQ(O_1aUJM_^b1m?FQ{{v4z(QJaaBZ<4v&eb^?#zF{TT{sqyT+ z4p{3u=w63e7T(;+ZaR-O@Obtf{@wBnJbs6@(&6KLpBdk3;MKs6;bVEw;ad+oI%gTD zI+7#%DUx#2kMn~j-u5YO_=+H`@%XJb@$ykvn9f@1@O{UU3qwxxD65})Ejivpp?Rj9 zeJ@`rX)>B3b6#D-IGek?>Y>5_!ye$ZO!Fz}8i6Yojj#UMny zVrY#w4RR)4DIC!stu!9@)|hywjC77bSmQ0R@S<=u*!x>!;r-FT!(#?-wS_l#2)qUh zFL;()4$e04?y~S|fLE#`tK>+2&cYiGJpEoyRdP(HeHLCj@tB}W>Ga!U;f)6#>n7$$ zIkZl{=PkTlXk0%z2;Q?6-YMYqE4*_IyyFTFT}+{tATxBBXrZ6qf*j+c<(3a2_a_T) zC^>zRAlp57yuamf<3-YVs+E|y$vVS%$h}1~ zqvy-rMjySm=rNQ5sLbA?^AYpj^e;;vKl#DMqk8k=C4cap93^&>_+~B4<SlT3gl12{>LCIUJnVdO(4itS6) zmPtZzA^~E8iXYWUKpPR;XwdSZy=k>AR@)MgR<7j+E4JFJy^|JN)OtJB-iw9az7q}J zqxOA$e`|ltIdceC?{nYh|NP(QxjQ+t&)RG4wLj0^`|PvUUXXwcc3d8OR;!s_w&K(u z-c^#`QTO5SoP;{!bzHAbymDH@wT5GH zQBK_%JyN7;qxWc9{S=QrdQW{p{Zdfx#0gy!1hwZgQ3pwacIa`ME&G(hXyp-K@>s+-dZ00s98hW#YPoAT6*Ah_5lm<-q_EL9 zdMpweEmmeH^l~?7L`br)BQzQdPEGidE}x^^F&rAb7rZp3AmL=t{S3-ZxClyWjiJ$O zuQQQR?idOs9UOuVhcD^&g>V%{zGOH)Mh>sh&b`;%r)bgTM@o{el9J?QT76RKB2AT| zdN1F}p>8URfUG&XR|ek4yDG5h|!xA?ZtPehj)-8cElm z!7~fJtzD3rC%CYlAZ3W)48ZkCRWE>)@vLT&#CPCcEzux9Of*r@B*BO1{1!UY1?ZIX z=xPr4390&daY(e|M7%4QRJ<~CJY=NDv(&ayY9Fk-My8w#ZKk?WrG)g`#<6r_T?G+G z74V}kWVqW+%2P=k|RDCKBC1?@S zF4N<=33Z`9oRJ8a>5laIyv6)HGp21ZL<3+>*`!l?`Giw7WS>aHj-D4AaT{ga z8q(BYah<4>Hx5QBoN<5h6O=NdQ}2N;9r9x!FI;{;BYe$D=rk;Lqa=4N&cN@M#jCEW zMqSL-t0P=9ryBjpgqE#TikGdZHyl&?>po_uh+TqH@VNwiMoB$tww~2b2x&SB@D!~s zh&W3wLwHHsR_JVgdxxVM+6j1e8{iL=c=4$1GoMPQ+cC6`8X+iS632qdNI7V6MZWdHmL;CZY@~#X<%DuJ6ZO=%h+6RTNBpQ;I)_g|ef0&sLbnJO zlHjtb-3y_IiD&Bo>YDn%PF!1Y1r?{4`ZCd-0G|AW@c#loF7B~_VK|5IiOy}g?w zJA@M$UyCIVZ#$43=BHnBoMSY0t7G(7*P(OA?wJTJSsWhJ?<#q!J<9s#^(&RVMG;4} zir)j(=Dk+3n{;*Og_8Hj#zV{K%Q)fgf8eyoP_j3+I&l?y4&8Xh4)z{Ar`$91)XG&g z(aNZgo$gp0&5RuCbyQ<)U%e7{mC;f`ESd>%``yNhWm(JYg6Aun9Fj&hs9 za_c`*n#$*R=p}1$A4Y_t$oQl?H$PsC{_u!#4Nm&QdZ6y$Iq&9Tj6wWYvwf!{DwF??2?+^dn0=*c&`LrLai~3P-%OAwu!0I=k^P6PPcqz5LX#WWO zaf+rl!pB--Yu*aQpKqu$zEwAAc*0#vwmw)_F#NFLYwX5d4(@`DFW@c{ccDi0_6O@6 zxNB@&f;%m)R{H{p;leP@GoMUSLb>IR*JqSxpYqnaRwxPA3bjV@=QVf@^`c*1>#dFi zJ{<12Jc9mCeO?RCD9b*Tu{>jCZrO>ULRhI#{N9G5@Xr|K$DmY(By9)u#^N>F!s&r> z%%OTRzidKTS(z8tSPti;U8N^-pbe!Ya;~CtPw7c?OwW0Jx|W>ez*A%BT%IVrVsxXxu$vl=kS$c=g5AA za_RcuFD|8YHcqc~4EGtkii^VskR!sX^Br?WhLzbR2}fWCR?@upXG}l#URL)- zEzed)iiCN2qO_C+n+?g@<}e~n@7REc#{v3l;uH&bf6KJ3ra%k$pa zWvr{5GWc_2{PLe0-sK3bbOv4>^7~6Lsy{_)K+O@tr^ zlxG=&+%@ss?K1kW*5*22r~EtSmJMk$-3bl8GL!Npa~ohBtUP@egU8jJ2j!D;=nX`N zCmc~KZiz<|O8HIkqC@WgALWvIGugBGzwImKKN>bpcaV-G+Uk{g>9k$EWBT+8<-K3yO$Tf4`22XBUGUCxUd1&MYu3)a+nKMfo}OE! z6zdod_hQAO&-1+1hn`2L4gV-c`sU*^j~m|1j-xX4yDW4nLJL{w&f;Gm(dIed0#&&K z=dx;kCp33~E!O}Qgz25_-RkR*&J0UBILq|tjJegPwA$(AL5y%}(tFY8F1mH>8h!bfAB&V)8EdkAY%4OBQm!pYto zWiIj(>b!HmPvdxmV(AEX5AhiPa$432pMz-hbDlfFMn5O?<}rSt3=_j24Hj%L-y4;g z(<0st^c+`hpcvYFJch<%kkv`*BN%msHB9nF$Pl(AV(po72SHJ8&eYh_g??_$G&e2o z%#^!1h>+<_(cGKqO5N|s#73%cU(Yl*D0XGi4hDC7GO_6-+&436cb>Z^GO_U?-2P13 zW#{hM%p7@}=2NH{BMrp>kJwomFLt3m$G!yMdp?s`q5Wtc`#Saoo$SZb4l$hxSIYdP z*bR!R%NU$cBg~(wwKHE)vs5)pQ^)C^EQe>DGbhKTj>}NT{kI;a!)9y9!S=AIht0=z z{&})a8@-dso)G!#CTS!%rfNJF_nmJeIAe5Wi0`)L-_2}DQ~X`HG~g+|4;ObF?vo(f8BGD;cfPHu^zxg}62&6D zQS)h`5Ra|C%;$~MpKq%wJ%8pi$?3Cg{IhN4Ft0hpsNS*9aB)O7xZjNYt8GbM^e=l!PBWAoSIbbPf=$4_DWNX~@2De{K-q?ejb zKaJ5}k)oQGx}zJ?Q%$Fz&gl6lNfguRC*??QHJu)MLBN{y^u@_^I-ZPldLN^weQ)kK zZ1x|YL@-~6)?<475*xnMhBtFUggQo>^!R+ETQbq{1t%R}#_~ts|G49L8Z#s;@5t97 z@s%FGg2gxcD~JCz6^X;|rrNI}h2i|>u=r-+9HmD9G+wG{U&tx_xi7$@3Iuk$J3AnCcK#o-d}<# zJntW?Q!uaZYb`LvcUtO`+QVgmQ>3^dyOkOB-pN!(ITET<-4t2`l7!dZJus#cquNlnP2B1bB10C}O=tyQJrJUx>C zNVUU4%v2=L*X$TeNrO~5K25oUXowrr2(c*QFty9E)H)b~mx*aI@lIx`GA;+YrE9$< z>^xOCn@lM?x1CfN(HO)Psv%5ok#zf*2roO=ee4tVTbsZ=r2s|G6FVy!ob1mSiTdIs z?9S8^)6(9_I(jNlB){>N_APBKO*nF&%y&{3x*#G=j+7oxP?9R!m@LLd@W+OVEmPwm zL`@;4h9f=5tv9x9-b7;$jcpzbcr>qjZs+GOd+sp9FQst6dxh^N|0_b?{%-N^`DOkd zN8z;o@F1M#Pz~XD+Xa^bx~ z9vqv~O#XX^ko$f>$b0V;^4<^PdlAL^8szrL|2jWYUh9LrKEV$`UY~sAFPrj#@=quK zHlb%=DL;+vq>q5CG5%pUIyi-4M~^)%FVVQHeCf zlb!-g_c3mRk@OqV!uN2wI?`J})v5u*Fwf(~*QEPfsNpudWI&^MD0`Yq&xo@A$pFVojSdUS3v<^Q1Y zksU?5o|i{!0`Q-)*}A25>y~-c{poq2naP~xxV*P@=CaIn=OeAt-T7Ez%_hC`QK_kq zqs+O)HrLtF_8HHPH|IS&#+?0ZV%}-<;8-|5y`6`#%u;EG^8W{um_1MSsqDNoXk4Ue z$sW-hw#SMh2Jo>^=MCj1XZv8xG1jh-8j{mdX#=yFs2E>qs7xPUO_B=^)+UMIa{gd! zk_axJMv^{qr$7pRmFNxqwx>np<>AV2wT|_!bhv_Pf?h4SYqlm&uIt=S+bO$h2?vh0C zCEXDlU4zcwi6p6{J7uH0_5yTtyVu6A3|rr0^@DC3zjrPCXtz`H`>u`OI`EqcymYd>(tea| zj~M7^uRuCU_j6miBn&F_l*|MjfvatF z&w!4`2D1GJ!qqIV12VyGJ`Mq-48lzIp}~ms%I*PPtZ}nr{~|A?p@$F z9^ukSe$UwWc`f{Ko2r-J*!cBZ9;*B zImxfm#xDVWvOOw5DfwL~_`zfDZx~eei+l1UzZM(alVCPhzglZcH)%7dRABx7>))HM=gu$;#W*&ISPb^5=)_+#H`in2FUEm}4 z>2me!=ta~bSN|pDI@s(`qI81y~Fii_w3CS?oEX&{y^uoar#}e;)Jo&djgh` zN&cWa92An5Jz=sud*?8$ED!6TyDXvagB6P0oVW_#4Y}ZoJL-~Nt)jC4QjmH)a7TU8 zrw2MI){Hntg5oHX<=SJY-kZW&Pk=SUVSQ_D?HcUHshMs=h!`ozY9hVj*1k@**w zMjMviGy3Hp$vR%c@~8MpNh&5-oQNHH?6g^my`mJ2oT}`y(XBsv=0CvSGkW)r)}PK~ z)_So$|6Zfwz(xInYMGtO4oOfYigxNR2z$cEnCu-6gE&C)`brvZAPuLNw=>BXZQ6p> zFTS6n&tDEz^)t$ntorKZS&`~xI^?MDS)%TnqU}4FRj)@bVzxoG=-Q zHz*6fKuvW3Z7e{#>0kh|=_wGP(+uKl>4 z!Bum3f{-$79OTl1Pi&Z0|D6`9S1vJKbF}8Llhv~W@=iOImvCj5Q12m{U63xm7gsi( znfd4=+!izst$}nQ$sl^e)58Afn;Z7+$cp)+uW#^{Os=H3{^%PU^6vi4SL)An>wyl) zg@bmY$j#P4XZQ<){;2X^KI~?kV$wY2VIRu}e=;Gz6U~G50LVmExQ1ON$_HgEMVjOlwvtYv}t4#?kia!ty4E`9BzWsHD zL*IpslkD(s4dYIC+sd}WhC(Je>A%w+`?pTSBaf2r+G8b;Qd zL8bI1!&pf?SB)w+^E&i}r4*>a=(Yz}Kc5}08eFk;_4GFn&ImY8WmY;8O0g%5F$2Fm zmMa_|OxzS596u7w@WhE@vUyI~Ni3-4uNjg3>EKz2EJ zlqR6pUVZgdIlj<*<9*fdtf8`-$SLzj5x}h+VP&@`TamJJg% zEYBe3Ul8_UA|Eq@BkmLM^MD^B=Qvr-VuV6cPzs_fSbxpR`4?m3{B7qi~ra%&F9Y)8U98x}Zb zD+%B1Y2oo0EtQyiPl)w3NYD2L!fzj}4RhELzI2d3vA$H_(`bI{R))A%!=SM&s0Y3T z{(5qpW1{AsJu#8h?hJV2;{t_4)aTX@XD1#qRs?_9-Z&o{7HnR-A>lwCG?s^5!1WLA zG8%omU=yV%w9B}L{jO%e*RtQw2Ma=<1m?zFQO8K5%5&rlKZ~?s$!fUq5Is4w6MFcP zu#^>cgri4Z8gyTz#vK=Bvh~>08|ss8{b@F5D2diVIkrq>wN=3D4E33l@P&E3Dt;?J zrED~=%zT^a4e)x+h#oxm^5)6E-whq09OxA(#lJHen2yNf7%kNP*HE*nzalIxDoHGk z%VDYUn)Dv-Ie-_ZT~r3t@&Ov#Hdz^ z4j1Mh$B2imeOx7<6#J$To`-r_=*(Lk{+3&3`k%N}_rG(i>i6Ha9rmZbbK8uP;xHeh zonpi(WA-hm?Wm=h+%$~nFlnX7EDJ&PL-$PSGE>L_-Cwek~aaZP`M-BT#RZULV(79Fc@TY2c;qrZm{!Arw48c>QHvg|d{ zP^YNLdsOvox2lv)ckhw5A;pYL~nq`!aF89fKjQ}wpG zk>Q`z{Q){E#0j>+5+{v!g>9q2uu^;jq{_1O6|6Vtpy&%P_15&a$03Q{p!sQ#kxWYLe5(wSl9 zkpQfLl?y!s(u+7Z{0$=()B+yN6<@_2>D4*e%y-Pqa)@vHz}P#xpm>jRB#JnXz(OM+WTss2{sVTq~dWSgpK2^Mt^bg$-5@ zlvlD{gf$hc;MsV!OI~V8#>yhW%XuCzS_=^$a>NVvk0Mg{&}s__b_l~%XL{gkLyi^B z71tPkpJN%7#(%9KLw=3Gz*}Be$uUHb_wh3n`ceb`{QLfm0o3E?w#!XG_K020oQ&-!T z+Zkj0(7Smlt_BMDZ@V-$`9aGD1*&H(d1tCN>Bf?8rusEyy08gqabw9JThK`FeEH#Y z{Q2^~>G)h*d2(5MyHi9QPv4!nY;j~~CLKTC zf~ERBU%uB$FqS+o39;;^FvRj>Bf?uC4Ocr%rc2_rz^8<{BRCG!m(v49LJljG+QE=uR|#* zJwN3vzDdvV^u2+*a(TmivLllo-;`V>0+N?a$IrFJzsQEa$cC@9;VW(UY8&3V(@E)H zY{S#8rRj8>>XM#6I_1T5I=(Qa<7-SL?sQnoNXK7d!!NSo7qR*^_m!<*V2IzPHu_5$ z{S7Ium!|IMMpr^M`j8D@XT#Uo@bxx)y$#=B!?Rf=MWZWAZTO`r9Np+JahqO0m$UMO zQdF0x?&wBWmfPr;+vu0u=vUh4S2B8Y{99?GztTp3rH%ed8~rL9{VGOpjv}jU^jF*H zuV(b-^Lw?8el^3JfvX`=%pJ{4sq6&w794gSCeTh#YhT}(s1D8IBeBYE{o0)(UV zLjq5Al#ltG&R3I;@LSUGRWP>v_p5H}? zOIG3Rc8x9*cp8Jqj?)(e9AxlBBQ#w~M{{-BNC zZina(HayuSB7U@{BKhxgHoO}TF5yYGj^x3o2>3ciKU)a`mX6Y|6ZpU3J|FY(wKn{E zfxm*`1IjHne2>7>oSl#P_8S7`{q-W{X&e2Jz{ePUrSg`5?_+Sa@;4j3*U8Jn`CZKH z6oHgE%KsvPr)c?@Pa6eXgi6TA)87nOI->C~ zWAv9QXKnPFi|4sGJ#Vke zm3=n+4{Y!YY4ppLVFB~}tyKOc4Zc$OP{4Coc~&vGO>2I};}wMZA0L0ORw~3R35Dg+ zd|`foW%>nB8GvkAYiH-C%?f_Gh-8X}iBvNK9i}9^5H&O{Qy@N$x+SX(kcm#&2w{OF z)5(meKt_*I1uAg$b<+?)^xS5r8|U+ zObg>R8=Hh6c#F)DO=G}JlX$OhzojcGS)epHEm?%o9W(MV7RkESrdzf|E&1$1-XXBcUy1H|)#>M*PHGZ+R* zMNFAT;qAu73OP@v#n8mq^4e}~tqE3DR#F3##G$1qgpY3FxwQn#%%)IgJtL^437QVA zYqxA>1>ymg+GCRY5E$LkxtTOAOpORp3uvjfb+*J<9MNUh=~H|blupIsnn9hd8#lxL7O4=J=}>7Y#3Y0A zgN;Upm^52ArnTahlvO6yD0o;}n>sdg)f_&KuqTM@z*v&zVK_Y9k~j+!GO=sBZfo1p zlJ@+v04XyIgEZ^X9#e=irz@7o+=%8hOU;rwuaT+qb4gpXq|`hpzxjYtWgr_-b5KOf zQp}W%9h6w8hoo05vEprDaw8QMOYzy`xv8lI{ifAA!eDek$!x}isMRkp?8tG_#(Iok zV$!rOnm(Q(tONF~2kp>$TIE>T{qh89)4Fuy`oDf&JPz-`&gwYX9teBQC- zGfmS=JmY*KGaH4f$sX2$fXy%)w~-$%pVBr03qj8G1Q>f(GMbA?&&5Z6Oz3rV!zLub zJzU}yOXzn||plO78T?-qJG@Fh&|$z->K-d#w4 zrIh?VLT?B0X-!G_W#Q6)jLXH5K1-v(-ynQilau~RjQjzSPr$S*M=-77Jx_2OqZB_( zF!<8i*z+WpYo+*%4)_IDJj#6&!QX|x4f)>_`Z@dgejC}Pa8UTuB7Z2?B7%P?(m{D@ z2*wK|iw`_lvU=J}{sHLkkdJzNj(pVP&jg;eC-74VjtP4s2Sqv1j;tOBMf!(@o)7ZR z>Uq0a?<{@54a5iKUQhnBbdCyql$+Hz;!P(Qbc`N!vYx&m;wMB&FpJE1)q2}Zl#An>d{9}|4ge?5f%rRY!pDC!&G zjL&PLo_@t;(zTm;{ZcxN&u665&}8d@#jFhg_o0HFNFgpM=+I} ze8vYpm7n1Mls*(RT1_<=IVD1Cg@#x=!m{Q*PlZH`W~tTpSOhfB@MZ0VuwsFGL9|@U z+H*#eg?&XkCKJ%{T+rHfEDAZrvo*L_pN>UGLw9P?ITitBiIVzMBEHJJ5DRm?c_9k1 zS{Ms$Suu`^e!eFgQ*#&uM7ABZp@fsDR&{9SeuQ7PG6UeMPXT?jfJLe znp%C?Ta>t85NR&Z?9m7#muX|Ai3QtO!E#|XCXOxQ#>5~ozT_GUD^^(8Z5RtHmS$t2 zL`1H_iAQuE#zv70LFQ_0Ugu_}9M`p_vvS^io{R_rJ$uhp{@>My3t~FKbez)TnUDMN zxTK>wh#bwenYco@$Zaj<^Agl_**rybG*2zZW#zXFFwN`INq$iTSm`2wY0q9dNw*FG z)^uZ~T%U&0&A>%;l4HpM;|OCzbxP+zn|mY%5TdB`2Ebr&u!s1&cg2<8^1pAV+p_`C-awuM6K!e>3rS6 zsZ2VuTVt(fFVgjai0XybC9)iIZTv{DZxQg)$#lzY{DR;|{ebwWMB+mA_tAN4iq zB)_X|{FYhjh5D=HN9XI{Kh>Y70zrLTI?1oo#;?&*URtk7e*0|vt^gt;nF%@qzhUFo z0e)0(R9-5#&j&o^ zk9^6m%Em7nFA)R4ODFlwv++xSUkF6RuLPImx5~!v8Soox+*oPj=bL3d=cN{YQ5(PK zz>mJ&NGJ1muZ`|y3>r5`BKVT7+m>z_(v{EAY(Yok4V|fAt>;oy=HJHPH(=uz0>2RG zs9wr($$B|v<)!b7*7EKLzZwux{Z-f%@Y`nNHvoRJj37(h zf6GSqBc$i{K^DB+GmjbesANK zya2ybHh%lT&nNiN{3ZGQZyP@!Cf#+wOGk9%KCIZZVm^IyJFF<#*jqcb5=pM4sy$U)Shol3Vxd(A$ zEk_dNAUf&?OK}k$m21C^-=Dzm3WQ51`8{dl=SC-x{oryCOMb^~{DL`S^E+na7qswO zZsGUOHhy=3-&pnhhK=7E@NCA3RS5*~`=TU*PyC3sThL+HXD*n_*I@!orTZ1oS?gst%HabMwHxh= z%6fTN@RMUy2>dn!FCC>x7s(l*tQ|7^N1&teTRNg4mxoK%3Bk`H-KP;(I!Q;qECb>G z1bz`bBeX`i3m4_ji;K!&<<~F`I`A0?6i)IJTNU^*A0FJ4ds7yMkM4=Qiu_-|z} z2%}6s-~8$YH7xiu=t-aA4~BC-OS25}UWH1>v-)GooZ7hZ!3pQA=rQ92u2aiqMQ@4B zj4T=S4DM|9CCGMwt7?f+5-Lh4frW9h8qklhhw2s$&qxHG4@JM++MC zNRdZNev&^$)03b03+g@5)uF72#xR~}&zY_+D|ASgkg2cmPPDKg5usG*I&Q!Y9<(== zA+Bq%YV_3(3dfL_+#ZAOk$+oc!1&tL@3+T{{>p0n?y7v$SQ(7rg#JgNVKrq?^~Yf8 zVl%W>$ls7~z{bNvm9V>0d3A!U7Clr+A$^ry?Mh%-{83}3?@^;P2y0-_(S6h?2^Q2p z3XKCmC2vO_HHy8DLdU88j9OgW;}1K|4RjPW@7X@SdUxGNOph&6IZJTmJUv$B^)*17R! z|KIW{U9KbMfL_SNFl z^YGL09%Tv8b0|UP@X}>*I#yii^wt#^rI%yFr&E*uNqPv)n5HBBlXN`oOqq`KPtx($ z6%nORwPZS7nZ)&?L=dG$YYFKHZpB5#U51O~lW8qMFzuP-qyBo}OGoq%O8`E>{Wf?v zV2V#?Gv=dypRwV8YJ*=C^d0c?QNO>n;s0!dKeoX+NQcs+cO0s{Vgb|KPx8jq0&Zk* zzOq!nYZ#3Bvn)iIz~EX4a7n2!tWF@O($U`3(yG|Q*FnswbMx(VCb3`vVGt=pM3(GH zphb#cwY98mhD6o|i_Fh$t(HS^t%5UMojBXoLP(il@>XL>cBM-f5@~zerVT7d;Ms+2 zG_~A(OIv43w8kcnBZ%ZN!|6q5A~|M|{A6Z^$l{tItkB2?eHCWwZ4WvU^;yphzDD$v z2-r{UgYZ&ZHIs27ZrZH@H>Q0vhIZ6D*U^I9}_;wy%4@v_&bH)C;VN)-!1&dgg+qs zCxuVXo~N8YlXPw=^<6p#^*Zt^`1`*nz@I;&LlrVPS$f|hI_mp`XZ1Rhzwdgmu4i;0 zqEX8OpQQu8M!>9{z>md8y0i~L{H_xG{!`!i*#}AcLn9w1^8=WjLOxdf6W_+vgJZ@p zJ6q}z8VhMYHW$LOC*?v&*7wIk%BNr~oO~X}!kJITfBECw6toArN@`D>+RN(Xds;C9 ziB7(c+35&J4v!prRzHmnUFE!Vk0DGtIh#{|vF2|SFzvZWC+SuoAQM+eAoc^GIb|{~ zn(2v-X6$xcR({z4Xugt8@{1wBN=MJTPZGhGbo9PzP4_tfls`(B(v#^vj(aOVdj4r% zlTPy6g8(bPIF$o*#E+gq$?tjGTlq~zh10wzo#giu1X%eczyyyk6El$zT4d~@Y9?4^`q{{}9#mogJytGtH`8di;VG-OAnPP-z4ck}wk z4YyCJ7(b+x;9cpOg8By{WZ$*VsKLn#+OT_NR+BTK2eNT8i(a7b%W7JcB&)BEz5ETD z_7`;2h=DV%a&W3if5g4lwXgn6PkqK-aIZca$=nxcVzjeyp443@jCG6D>f`vOFr-)? zU^^CvHXSz{i_{Va?8iD*&WM~a-0Y1PZ|FmR61I8i{0{!E%5iFm7t|*V$78P>?nreM zXW6|$?|WIRCWtpzq`1>eadCv7OhJ@?eRCPkEWybaaKs<)k*gj&;0X7I9}dspy93xK zQM$Xs_eF1s*5KR{9yjE_7rPh=ev5<88qvjP@1s-oyf`7Of}K!8Vcu$gGFN{Rcl5TN z9e0&1ft|}zhUfP|cAtflJ3fJ(P>G>?j&Y9EWKkM5LFdRN?t6{ej=u*dNBolT)#98T z_t^~nj~hH~?uz|G?vh8tYKhy(@crG?ZHAio2hdJ@s|Gtxe=wH4urgY0RK@)5zX|C{ z)wAtY}4FJAA>01tOn#@U?gthSb=@BLm$%m~l*FYG8P zxufHq4SRO{z!>i>y!-X9EIAWXA2cRtj48@%#tiRkh95Qh zpfNl6pizda99IRd0IoT><_4WlJvWv5%hb>P+;AV5w9h|S6rQ+;pFuNe-;9QTJe!dP zJH%aDzwTH2=}Z=S*S|hoSR(f>R*oL~+`mVZnHM+l-4NOPs#))Q-G~OM|7EOv5%FBG zsxJHAIB_=LjqBI1H~V37)mINV!#l$L;XPqTKkMD-Eq{!~&|5mA-mvpw>M<1F71c+4 zj{Lnlo>yjXPOj3n89v{Q9f#33-sy3bIMH+D89Wrmd(L-o=PJqDBTwYX+au4vaf}oy zPa1^>o-~REzPD{Do@EJ{Ldf6pUr8h5q%n05ozQqw{G_o4&+AM0 zos#Xpm7pn}7Un;3t7BxUvIl#I`_y-C{X&B0!@Jk9$Hh*8^X}6}T%P=WD-SJ5?7<$X zKdEUd&aPB(Zk$^3=0P>EYp8Bi>8u-dbne04qZ6k;)s4ok0ax8otR)s=PsbD1AJ!f! zEKwgudk$2EmxvR80tw#Q9<;WKovPJnY3CmPguZ>+j`O8(RHo;Khfmjp2XK<+fT0aw z7c}PBrv$z|Of~q&(;1?@y@zz1bJeRThrG}J#@HEhG}KRhtZvHd^;`E&)f#ry75+xs zx}(lLJSibV@>oc2T&pf*wC|n5`DzJvnhMU8QG6Qi6gxwG!pGSOfUZ8; zl`ZJuC-3~i$j*K`IxXz(zdoATPd%xSjYv2Fs4sKG=T`jpV+`sw)ZkChQ}!DZgRYVc z^rTEN22m*QN#Da=#s_5+TzC#LQ{xQX^Km9~q(=Aj|HSxm`SWdbTBn;@pt&xoYR-q* zd7Z<~eRN)@v%g%N)Z;)-m&J8)O6Zh9!h3;t?0LxeN>Hgr+xj#5A2RM1_gVcbH?BT( z!tf^4k?QQ~YW1w{QD$9?KC5O;#cr-VH;Klqg0~`3y;q)^6ttY0l!a51rq!zp=+vaH zr6a>9mL_m&67g~Drl-uAnAL{Ur!Iz#=>Cj;vwn^mosuBSI}MNiO0igwOU!ywzux&@15UtUKA#j^G-1m%H6)%bmOPW_p>0`zh}db$6t5-Lpw zepR1>U(MH8+-vCG_weiRYWQ_}dkvRQ1unzaYq-%CneO`&i;H=SDrKwV^;;(d8ebRb zty+oGpn4Xofy-ZiV#nCBQi9DRAml^jb>u`Fl6Ob==>JW!= zemKj}7B9uji_o6Ske`iE8-E8l=A^eYL-O4YFC_9<)7YO&EW}@X^WcBEIeBG4v zvB{5dD^tv4ieK;7`U^3lDZ2t;tfEACMuKWU9}RUKIv2X<<>8H|b1icc)-9KB$GRnl zrJ$6B;<4~oamtosfX0nR(SB^)a8_+}ja)0v|D|=xQz$|DdZhp-ie4Xm#i;SUVk`a`dVT}Q@6}>$Wy$X&G>%+?(V5S!4$9wGm7(1@Yn0Yi_u|~*pDr7p%*fpo-yMDk zdF5xW^@NqO);P=U0m|(fmfJgN#&k~K&Q@1FI196;AnDAVgH_aWe8=G@pY|T1byS%+ zv2Hn5SMnS?y7#hHD@fqv%!zcKoeYsHHA#z>XB^V&;3!sIGmXN_(o3%S3roIuUdc7~ zm4sGh;u{NV8QCT~Sb2_PH0M~Q?521IRtUevI8No|BMqRK8zrU{o^yB^#wDBt=-<%c zNbvhS-1E9f&wqNZQPv_C+D6vN)u~eR8dP(~#djHB4sI9>^wuY1`hK>G4cxln)>TQ( zO{YLAZng?^^K#QVie}b&wD|J#%1x`QvxQZ#PH%1TJ|nWIAo)q|s$tYX{k`awG&Xmm zmFwei!yP+iI68tTH($2_YE))zPF}CmiW=+b$g{?EwVTgutKWR4zkb7ZlosaznyX{$ zDmy)o)nPAA5acs0&va90V7!B@>IxVY+uS9(p z2U(tD$g?Nw8hkV4BOvc(>HVs@S5G$Tje+c8kz=aU{_F9jQRX?@widJElv&gcG^U}) z9B`g>W0a6B5yXr}b;`@Y+rb&WAMamIwxXt$#%kGbLiD`wIzqpR_!i)NMfQ|uh%zh} z^-ink_j}UU(ph*{!aHwWNu+36Gv0YK>Ys=x{&ymeh%ED741-(j_g;br$;;CrXzdR>G)|jeta`a#vuFC z)IO%eM<YURopIW#}_>UelFCEFAIsk`3N$gS%}o zee05r;tv3(paER@=zm89Jj&n#<&=$H+WW!K4EhPmzYF?j7(7WChjLSt6AYfL_*2go zumAZ1|5JvaqAU||oWawSbvF9D1x$}j9{Pj)n9DX(*#}{Za-jeYC}sS$Q` zZf`5TXC%Y*!t!Kd`S#0Tk@3BK0|zBdW~R^k6x_`eeV z8^SLU>E2B~zOC#fAL+CFz^B=j_!pADn9K1~Io62yl7EkYpC$iN)HCt9OxP7gx%N}O z;nVjy&qADMK|c6=jeNoj|JUS)6eU6a5|m$r=U_0Qe9a;s`IPzcQF`EmEHXYUKcMR) z7<7f?136F7vHC$H(pXLD+Uu!Tgg+|$0pZ8V2j4T~%k)tn;|T`;3i6SUX7a(8wKx17 z0zN@L;{TO=#Lp++5^s&5TQB?=`Jf*lAM`98^aIw;=m&Kaj`Xe|e;J?aDBXt%M!e_9 z2OZ7(seX*~N+=xh=8%tgi^xa3PVy1&Yr_9N`A9d1e8i(UJyp+$$DRYk`zD326zPHf zKPAs#Ker%lJ}^J{iy?W8Y2iE?+pulo#~$M_HeZm0Yr!*-3p281biQyzHcnp)7bHxLwHKriqwWRqQrA7z6lPi3n9l~@3&v(+s~m#op>pP>jkf<~sSJLuD29^% z&*d^O^s+UP3qCnK0%Lv`r{~D}o&7N^$K43?4=ux3S3rx zPl8_=!iXQ`h3JSsJ+D@NNx;^1v*fo4;Z}aHf}amz#Eu~Ah?HL4E(|r}`?nW4;OYb`}U6P})@^he&1Hels`DKF0%5U9dv)%GQ zEcpd({GJ5AQOQiu5je-jZzuSzK{(})-WVl6l3l=m>iO7>G1vesoy;HUU|aL|C-4h_ zi1^VPrsQ|0jb9%c;Thnill*S8@hgR(1wAvw4_zX~&#qg&2>LB2Br`!r;5Tgit_8mk z!igWUn&S7oEq`R|k={$Blll9Jjo&Wts{s-5qc>bx-q&sXUIjmz>!p+YPTKgrYAG+h z0ZV=#gN~jnT`+hIok{YbAbBs}xv0*_5#45f%YMOzwd+JYgW%O6QCZ&4CMWYZw5zUYCzXMGr9e8gE_Ft$D+#Z^PI<&wi}6&3R~-@jyhKH+7i4o8*RF;j*o4M_TN5 z#s+($^qntz_!PdjX%9bc%=f7eD}Da(iquXt-|?pM#EtE>YV|Sv>VeF+NULW}Epa(cP8OlZ1hwLsz{0&EJX3^go=og*>mFC4C(ORBcxbO%^W-Bx z?qsgJ?997cA2f1e`G&r8)tSP{%14DCy`RMX@UADRRb7X&=eWPLLOYYW zT-m$)jHWHar9Aw_Gj826TsLYI)I&~5>Hqu+WnV$$pV5O#M(1o2Za~@~l+xG|E^Sg(A;R=dv?SC(rZJ(ZtN9N!>XC(my=U_tb%N4Dp&} zo>TSUqV|OB3abZ4WfN?KzWhV>qpnodfpONX~2$ ztVhDO+EN{x{Uoas<2E=!e*$D;OcWY{uV(80x`L_}N zE3JpAg3_NjfiK8@$Y~sh&uKzQ)kX2IfZS8>m)vvT9RA(uPow(zX|5Y3%iv(61 zRlTK?M=PA6sA5<$oKYY^YE6+zCY#grG ztW;bHc-H1jzU@BSA`q=gU zloj9pSoiPCe935Mva2r{Te43Y9g7MEPa0bnZG)a7a&rZJd7J{8yB0kHYhpYvFBw~} zc*$6Ig_%<&@S|bl=-#a~clW36zBc^SX=)EtJ+tfOK1@9^b43BH?jBoR5c&4G%bIaN z`t>e9+O9t7(F-C(!P<+BEhHbC3+>Uku_(6NnAlgpVOp=7tr+gDD`)c^tUb?EW|lJR zo9&BTqp|Rh4^DsnImk)ruza`gpnmAitp%_|IU|lfrlXGIT=rLUGfQ`ggRjrQPnarKQ^YurZ1SjIOqG);@oY6 z*73-}#uwPlPM+*m{Vm&v|M>Fz<1iOF`}3F_)GxcAG_EVh{1AN7xB(Vh*9i$A)WsKh zt3$>a(myAR_HsVLtKu8KGrZ=AT4F*Z`S(OOwd1?Y?jTv6)P&VZK6=J-GlmIowMUuQ z@VN1U@7ss+_GPr~4(n$%4_YASHOSeGGTFL9L*17tY!YrR-;WkkbCioFzZH&F3kf@K zVgZw|%Vu^7Ny6@mGkW&qWDM3_9ea!F9S@VV-Sc&4hvQ3sH_TQsg8$>jE8d3=746Mv z>kH>W3ht9y?W~%G6jN3ZCmZUI%v6gkspg4P$FWrDiRbASotN&J;ZsXb48LR?IOf>* z#Cnnsbi`XA-Qmz%B6^|)&jj7g*s}z6%p}rQ>BCM~Ri>IcY~(kK(91H|yo$OTjkR!F zp40aEk4#4^hLZ2ZTnQDk;Fp8b_XUn9v&$07Y(O1+4xsuDiI#ZW_*HQFUfF8CJvR?d z-`B-b)<^HFC&{eL;VLmBWMIvwz*;Gkb8txsWRgXE(zq%p`b?0|M0`$u3lc$6E&{sD z=n*?&^*hnSg7H6l{Kx-X= z*Hz+zz4v8oeK9L~!idxs96?X5Jv->bOyk;5HASHr{U?lQtsB4VYBTZMUhBc{&ft{Z zasBGP?Eb9&Ly!t97<4@q4DY`Dgz>7dQF+m@tPfYX_mBb|Ypy|k!bsM1A*Ioph4?jU zs`2}`8lBmzM2jczE|gF#_A0Bw*TP;UFOTHGOG9mQMG)&sm18N#$Bmtt6H>NVDMS}- zSbDma$zMtNT3BHYs}DUK-X~UAO0p+L@-Sbq$n)PFB|D_|F=?p#giQbY_od6gaJeg& zkgkP&(mk$}EG@|>xr8*W?!S*DO7L47Ty^Fj{re8qpYdGsUE`90qed`z=&Lmg{viT6 z6v*_H_%}p+E1!;Fe1w*TV|enE>Nq zwN(u`hi5_pO!Xg&64wfRu<~#*pJv5&?%uX_tDMx9f!^fg@Qo;U8;>xUC50Rb-o}yBWd62X%b$CK>yTU{c_cp01B#)>v#1}D>|U=?e1Q7T{_~_Oc|USn`jI=;kJLTxR4?j9uTUWe zSQ~sC^GmOBNpL2hg}7UEK;QF#q9+$&_V0lV-(pBNU)JG<^yMp{KzPkk!TR*yM0(6p&(tlssCksW-+@ZzitD!ZvcW9?zDz``wI-w^U? zYKdzlY2$F+9QiI(j6Rkr`WHQy?!)fz>IWug6^tsI9dU>>u<_b`l=lK!n`&`>QVRM8 z_AHjM)r%>^$VPQY`nuyKo2!n13r2lajJvcyQFHi#=oG9Ul6owV7k8Zs26Ez%UWE1N$*Q|OPWYyKObEyQhP+ljcdQi+nQUk@3_1JHm0!y272O%9F*seCf2$jkN0Y! zTJC&3&*#J}r{Y(|Z{Ey&v}g7ZjfP5b{%CA-#?YGwt8m5)-roC^M>uaMX12@XdfVh) z*{|r`5VnAazrWEra)TJ-HTG7^pDii@>b?57F;MG|;!K`fPr$l4?-$BSf$Ba zHXdTV45gTgoxT{J=~NFB^A67=<<8#5)JwAQW=_26y^vt5jdkrn$59%~9N}!dS7fty z3jP**eWn)1KFtSPS-UyTO=`i@R(l-1vMkKUH3e?Qy=Si`pT?^}=pZSx4uzeMR)$kO zwagd}Y*qs2*eL5fHz|sDl;s0U_2Ff2H!O7zDW(2sbM(4MOSB|f6qyxS&-(cZqp~0L?;|U5Sr}kX-#{&rYbxg-oq$D5e&VTzD{QB-V zexL2mdYkJs)iAkgT5sz;M@Ia)q4z%N9ye0xxhDM7NU543Mm^^~CKo#37@4EfekE`7 zEVjNr-Tk=ngioyqCExBY6nmZ=zhT3+x=`|WY{mQ%)&dzL9wF17!B$ScWEi7`OBv(c zkL^Dw$$#jkU51wcKlewTr^DSR#nY@Le}sFY^wN86x06XzlTJ;eSUZH0-|TjM-Hn@>e=6Y2ksx6yaV^sx{o|CS-d4p{G%Gkjr#|oBN~q(ja*iLv=dA< z%yZ=UqoX2|-oDumu^Db8s3xbQA52&18S=6g|7+|p(o~DJhh8%r1CJZu^JU80o2ep9n)C1Tx z!Mi`(8($c{F06{2QXIdii5`)syW^CjJ72fwr*l}Q;Y~}!fz?-+jsF)#uUL~ndGdH2 z_n^-=LQ9YLUB0$jmRNZA`-6I*Wcbdz3(!J2@oCtl_T?76{&X8=%zGxrS7Hq$`$;Zv z({i(4w}v?rS~z25s(7w=&zP2QR7@G>Bf})@B^2b2f8Bicv~YBA;f^U;FBCpx`0|Pe zXI0<6BQw)yRC~RwCrc@6YHu2m8n9#flOGVoLwc8&Vvc%A%#`D?1L2MH5-Mf)jDDR_ zo&=WHE1yrtCCs?g?|A5VwAN*#xBh@yN)IfGI|7%+wZJ{28-H-ph~fLkOZau+`^UY; znl_$-96PW77p(-L<$A#R7WzNlmQ?h)-L=#&U&HP?Y90FtB*}h#s;xEFD>E^cXVO@X zuO+fa%>Nh8Jt+J+|3c$7|8_#(V%B8_2PywFSFFQ0-(IWuFk@t574zR`PuXOFP1$jh z4;t(2sa&eulyfqU`PqD?mr|*D`u3jR62|ELrkqgcE%uE=_SOBoDR> z7kAiewz#om%PnZ|KVO#ob`TK%@%T8Z0Z;r%c8)tv-(!IZPv;m*yu2g-(`C`Uw)*xm z`7TyI{Al=6OZ-VzgFE_iYdSjnD!u;mO(gExoqi&t-0MAr!TV6<0H1kk!|+$^rtG{1D=wfh!59V+Ufn2-|05`=`8)y zRDP#hxD&n4M(?xXOKkWO8@|+rr?2X!BU|n1`KPDGbYz=79Y5Pd;*M;ur{l|Q_<#)` zVC7wtDsR9>Ki5V-*M^^G!_TwfgEoB7hOe^Wt8DoBHvD`Wet`|Yz=r>v4bKGzB$2Mv z+VHg!#e7{^Xu~gL^+j?b+(A&);&f%Pjeaqse-<)E+(97J;&kOQ8$ASKElyXKSRiWO zI_0<}gr4SiZ19IRn9jMBj_7CE;Dt7Ll?}cIFa=RhCf$eiHvAnn_+cBo2QURuzDf7t zfDM1t2LI9q|JerrFB?2PHFk2jl({xIY=g-*wRBV;H`(BAHh3RkN|NMt$@cY80n^At zwyx={dcw!>laKMw9Lsrraxf1_N9p+h6MYaD*}4u0c$C3K%4IhCunqo_4Q{u=ciCV% z<6SyRf3FSxu?@D{s(!B@MFv(iy|6*jou27ewf#iubpU-2m$ z1b$*edN7r&8k<|4e$+J2Tlu z=WVXvEXzmrYc*7xW?#Z#=dkYtHCzULi za7GsGi3%}!@i263zO5DJWSeenRjA^d(bYSxeNbp3M44ulMFnattX^~b;;rpy#ZS|_ z68d1NN=`TaYZjlG4@@(0ZQ`ims4Jw0NORqy8#i6>yizlo(C}(+P8sZ`)?|+wSAi3! zoI)#)wKXD=eP!@qqd}Lop6vLvfkYy4`XnmEOjw4~ zVSg5qMole}cc_W#+P7kC9i`i{V59W_7x7clBSQ|CLjx~+9Rz_e# zBcP>m_#?ZeYyE%D(l|->;vp9nxy|vevy}#N%j~gsOH-HiFkU`$kg0n?#7^t%OsP}C z79ZnH21z&F(t-Ji-o`voVwgq#mxQj~mxT^jhNAc=d@Y~LJrP9-5!}Sz@3m$wyX;vf zbfsFk%`2j(x!ki}ghxeqo6s4%N#V9~;a^8|8x$o%KGFlJ;%OH;d>f&&M(`#@`M&VS zlMkXx$=@vUi-L3t_%ZTthK*(czaiiR`N-$Ll8=1S+R%eYeqm+E)gVtbF z|Id(*dMG3R4pBdMiZg3bzMT|)m!dpM{#O*`yX0d5{X_CmA3r7k9z}VXeB}FAu3}gq-gN>N)k{<(q^1D%g?OM$VWe+wJ_EH8S>HYeWm_|%sPK0*HfVeehQqpZ$-@%7D2CX@T*0)!A?auFs8m&rwqTy&U( zFXFM)ur&oA@s77xfq6Uq=2A{u&V;ii(E|Ck}Zy2%m?8@bf7= zgwN{}!l(K~;inKi_)Z{*aLoi!4<-@BcgVwmzqy3N-$H`$mq`%y)J_oYrNZ4w5bnG_ zAyOrTBc4|hM7~rI+@-D2(Hr8`gzsT|E*<+I%879N@pN=C_8H;bh$qGOAyKb4BL78t zXl_FBvJu@QT0H1`Z~`~fiSK^1$WL85_7Xkf*GCZb`}YJB#2rnfpNEU|^L#`-uu?c4 zq?;gc9zN>JO2Wa1-=PJc2M7nBrwM}35rW|J5<&R;kl;vh$9Dt1X9IB5Tbk=qy>k%+ zzr_M>B#272m!K8jw}7t`M0o#6a4+IX^>ixYDPR#n@F^Ftg&=*u1Z!~Li6F}FW#LZm zXOa7-!d(^aCKK$D?sS4kubbeH&|VVUfp(JMPSiu;ejh>5(_D?{j}Uwm@g}&OvDXNq zynZWidS8j~j|u*m@i`pPu_(>nOb|G|b3}9%1fx-Z2%?_RoQ%@{2tlOlX9ST?&k!7k z@ByhlAd7js^Bgb{XIziV6@=Jg!|;KwjeaD|hs(n|@j>$NPI4gp-@Y|H2JIZWn9!Y2>VNeL-33=!_$N9v z^IS#JksGOr&=%v5_T#EmL(E4yqKq_G;m5d*{vLXEJ;792oe#A%eI|8$wAC0ebVJDU3NAZq< zb_$0|UHqs|lKkF*SIbfgBL z_57N^kMgA)IuV-ai2q11)AOVLw~{P`lKg0#rsvlOeyyM(epYD7@6rfnM!n0Nq#hL)W^@Oiyx&&#_!h={LX>jpkyZK2>n$A zzwJ7H8G??=_e=!8_Ynyi^GPL*Bu`2ozrEl`bBuBjWB!?ugsrE)}JN)UP7wUIiUKxsZ}{w6~?tXPYiu zYU5?Td=kMg2!8s$LGt@Jf}b7ytq*6HI{3VxNKDTk(= zYZ<@DcbB&$Yi@!arz3NHM7Z~XZn%7@i3oR#E*+?fp>Xes;CB@KhRYY)lhdbTw~imx zFPV;KBKUm@e${Z7O6JRx5&RB-AJr?$mm+A%?{^XW=A&_}kjw-fp}&pb*9U$!*b_fW zkL1@M!S4a^n-6!Xh>lb&4Epj7fNrto#Q!Co83uj61a#rj&OOo5cUlm^uLJ@wXn##A z$!}r=Kl*rOeW9Ix$!|dfzwK#SS|j<*i{MuYelnk@>-epV;CBN2X#Y(r3YXN12!0;$ zv*4R4huchOqNDWQ4TC=awxdzD!Coq&Bh?OrKL46f_~gGF`YLFmBm5^Y5I+RR)j{xE zE6fDQjWoT@f+B7rJ5u8@aD-^FkRlpVsFtCzAK|SCm*(lRH z2fwb(4o2htaM>Wky?=wz5@6lRbfPYY&F*7lX973FK`H0K6vv#5bft62jqBFcmB%ng zvF)Idy{?pux$7rIW4^k|hD4>?zfPjzo84LIw7Sy1wtCZ&6tsV_&Z2p2`CjHdV4Swx zl2KVXYu3`GCLc=fpXVF-co%sll&+G)X1KRB>D#zZlGB2fPSIPM-ay#CX;F$G^tmcVy&Jc<S^bo1Ip&sE(fTs1fD+waOlph9~*i2d9 zwz{PTDOq-~SFJ6n;H)hG9I8JF^OFpNBBk}*#oSFI@ zsbmc-g9WvVvYDiQnCgU7l8*WTeIL=S*_ z_AFnFeY|h;cw>R#WHL_DC-|Cs6?@_-<9MaOaLQ7u*keyZN)bb!!(XOpx>^C0TG}P! zW|r#v!&!oV@LxQu$@77%eAVfUdSJUc%fSk+_wB@M4v_Fa;EGdlE26CUm2?x{Uomzn zou;6{bmowu^U}Z`b$rtevnRb{?vF|xv$1S;mY+;Z+7x@{sWEMIgUjGEbnSfJgxg%E zq!gc_dvlM{6}5*>$1mx*ro1kbUI-_S$hDBtSW45IIOP zQ4(559L|JXmqfR<9MU&tnEH*W>9dn`SdR-lu?{!EW}I_OhJfCKKLQ zFK$u-@%U$ua}aEI9C=ucXAi5^O56i=KG0-sQu>+Ibh~Mi0r$Alnhd#%n|6NAoJzkb zd23tpx=z()9o^$^ofR}G{-#cpkg<1c&+%50^l`kYtW7CMKXsxN_j5Tmdi)!Qlsw}p z3(5`k;$(TyZaM)fGjL;p@#OK=y5L=)bny3ztVdYCl02`?P>|_g)Y?AXT#$J3kLQfR z*k}_e7M+vY6df?*-xP?#zk<}HQtTWIx$sb1+PX2fFKU|CW-3S&{vS9Noui|%Zf;9m zX9SHV0}Df?;IF+#H;&!~U-1T?Rm!i<>~)o$a<*>D%qZXC>I=T=&1hBf(|j3jOQ5o~ zBQvf1QP{rVO>0#q;g+YvP+pnY?ix7BvPZg)1Y&!80~zk?TX9q`_;c^#Oy$xMN;l$MzW86t^z^BNKHA69Kk6`rO!6qe_UK#tN+VKkX znTcN*F3|1L(Y`WzHP3MqyYEZm=5A}%7n{|nekEqB`mFcpz{sTNlT*I1TrlFhVg+=k z66wxW3Y8*bL#ONg_wQDZSa+#uMRZK`bUOjfEjukGlu_r^x`e`wgLj>=Aj zTm$2~$`v)iXhkWEokb;S^yQ~-Qa^Jjj&W#Xl;Ud&&Opk|MYc;#DKg>(wRa8~A(P_t zA@HYTskpbu^5c(LvSPC+w5QwY+duFpW6o=k$pT3W9L~vp95=HQ0wq0+<4X2^;86iZ zxc&s#Pv*U*HqDG0IBtv@xS%k+0H{pe)>KV81p{ixgO9uq*xW^}2Rz4y+J z_ve=z@oLs4^~7Fqw<$&NZF>m4LMC3#I(;a^{k-e!>BIpg@#EQ$qc!#O4<1pmr4Csr zWw<%`-dV)XK{xtJd{F~O*n_G)Z+BuZwm_RY!H#`k#NF%f6avRhRTLcRLr{J87X>0RmxhcZ2`;8om(zFW;N3a9H<#7(BF31*qDl`zY6Er(gA z>t>i`x|aAMr{jr48>Q=Eyo;rvj_vT$ozlC#cLZV}>4ME%5upFN07<`KGpjhZERdQF zZ1OIS?F>AqZnr7X}eWUcoVw%Ot4AV$%Q{u^t0^`a79lU+G zYjEJ_1ELiF@-e>YYzo8kixf zO$8WD0JlMM$vu#O^5f5KHVfL(&bE7qQt%}XO3sd+%z(~jnaXM1nh5=hV?!YIaeVmB5F9Vy^9Oq^=yJ@qU1(@r!6yx1c zKec4t?LPIZ8LXi1{EuL29IND6{IAZzdx6Pw+LXMGV{wo~_SK9{i05{7qIHv+ZGC~e z#@xP1&9a*EqWmPE1gXd({8@5K?QYwIG_3xUPi=aU-W`i_r{bP2)n>X0T-*L2T8?+o zQm%S_ePilCX;Reb!;n&f)(K;v#eviTcT)6eC4c@YvVDi!&Vg-ZQtW9%z8S9r-gm51 z^*f}G=>cQN))%sM1c>+LLwHXBml>S5O~-o-Y+c|z9G8TEImG+z5N|^u6@6~1`+jw* zW0%Ut?m;U9v$d#vCsOe+OiRb@a9Mm-_f2l|zzIXbz*MF0koGlCb!50>FjhBoDm#^l z^bS?bfXyK97{?6QV=X^^8*h!ya365Lt!8dlred@TZJcU3X39n1f?m&@EF zO49TXPO6Rnf^du-kU_=b-d58ND%thDH?}HQw7W9=ohs{#7p*&Fv&1C_G?xUwVvjqe z+P^)6}2%KR{YOKst6qx(X|B`Op%ghrZGm(;bJJlCkloHbcI_Z^BDg zMwDZW-+aoNN)Z^ z+sLK~+;sEC2~FnKN7jvO{lmHmtv9zQmqPk%wNj!uti?*vGp>JoKdv>lEw%^mKj#>+ zqDY$NGZc4<7eO=Dj1q8bEdjIaF*uQ%3hB^x49+vKvv(O@3z&%E%yl>^TY|eTjo8(l z#%8fLb`Sn5@c&@Q-?!0Ui_?<(*b$tqUC&P9A?FP^?RSACDx2_PL!vTCk>Sa3B!B5o z(m%w0&dL>!@(cC~YgBeAuYlVwNE8{2q$?&8vd{A_Is;= zvz8WGr)V05^$Kzao&wySgEc*Y)0#{Gn@i~e&jnuT#`yt((|SuE&I~#PUIBbB7S<{R zUJcy)JY&@YZv-BEhp|TB2184W7dXivX9A}}ejO7BMc|u&Pnr)}fo}%B9gBHJfp-93 zvK#3Y_}##5Z(@-HI6Srd0HEU|)E9x@1N_`y8H)yPGHKzzPy$PVQ}~ToOrr3~ox(49 zL<^sA3ZLRn;S)~bFS}CV;S)~b2Od+j@PSkK551>o;RC1emn0c@_{`GMvKKhzzYYBU z%j%!&;P{U9%Eb3=6j-f<9`2t1Q2MCQy7lileUzRV)(NSN(W_{7DNy2H4vkXOJA&R4 zL2p4Dq|qnx&ir%0b>Yl{;Q5&EstU%}#y+JpWKAF>7Lo`^F z*C~2~ZYX^!r#FRYu;Hpx^d{X<`VpLdH`+%{Y3@(!aLTXn9w8b^oigHufT8^Gq3V>8 zjgD|1&BJRA@z>nLcVVfa3oCllZYcjT+<&+furaAq^v2y#`mvmTdx(a5g|MPG?kIg> zHJ<%QAe4s+y?LfnMrMnkx3O;Njyj^iU9ad(yrKNF*b{=lgpNrO?%`0h`WxmFSwFM& z?D0M2uz!UORZonQu+J3FjtiXnDZ0n~p}^;JJc)fN@HCDmvk^Sr+Fn8m>eEDlzkm#& zH|ENSxlmLOuM_SMz?_2ezeV8NQKl&5>281$@4Z)m%s0PYt+( z=4s;(fWZPMH#Iv?3t6pav}eUHtF2zUyuRwz1{@rPJq~~_uV>AG;;tjMqN*B}w=%Hc z?)(I3eRBgiz-kS*Yp!37)6Tb$nQpJ%QibbFM32+1WQX0Y#`==NLTrrPvW9cvHsM(F z2isQHh|6Z`5jw+VQ`2$ zKlj|Y_O^%{WIVN@jHk=CEo+-Ym%b1>Kj^BxafT+F&XUo+he-lb`)8P@ok20o)b{=f z(lm%@|b?uA*;&x&^AKG;bm`Srp=Pxlb$ znj{gRq@z60)5W8`q5f4WNp}Jk`uLUV@`c(a8NV-J*7FO%f%-?OB)?P;>FEaHFkcb@ zN;-PCRv)fY7cPw`iH`Dz?g#4mIZ;@{gd`O2+Sd5&SB3 z{Afff`P~}9kKO{KafejWNd1dGy-UE)0isKwX+$ddJruz&9evRQl9`|*baw>5ExP#8 z2v+i=dWm1?J9--YXq+RJjNj1+e%ry%2_lLgj3IufBKXa~bYZyihbyAeQ{-oHv!HOyZ~DCwUN0X&wVQu*7^yQ$)ut1TN_c z1zm_ADkqJ5rIK{B1s#AhJO zF7YCzPXpm}E|%oZOn|u@n&LN%e?ezh3_9JTt2iJuUVuHdZ&Kr*KFzS3QL;e zWY8vP%d>rEXV6<61Ano?Uo_S=qeK2)=e8!i4#VOtFY5iq!@n|DTLq0l?h(ID2eE71 z$vTti6%V|2WGFB~A|zg4-}X+ivM|1 zoY9PCh-0vKTm`K=YyV@p`3R5K7;e`7Gj#Up+^qduUL3`-k-C4)cN(`z)%|Ps$=t@u z|1B>%Cs^4n4QXr^pmU;?OS!7Z z-ZK(vtaNy>%-5H;%z9rJoaU1o^>mU-Pjv>b4%rkUh}B=xVI z`;N-?<_E{4wqzQkJ5#Gy(`kc)(b*Z*Nj$E~cIV(Am*X4HaRcx^t25|i#3PN{8MoWe zL^=m;oKCYN%n4hfx^dppxf#`l0dI3gb!qV!55gJrj%UFid1C_81!nS#-J=;7{nyUD z2DQp~(O&loVrMGG|G^hk3L(ftz-t~b_X%@+DCAjaZ@|0PsnsT)FWQS6QQe1;JDGkn zhv|L;kN4%E&k5dDmNVF7%o+T;E(hZ~Q8wZ+c!XG`K1EC|M0JZEG0D-y%iWgqJG{8;A1D?_XU zkk@RSBj@MIMLm~oP}<47VcH-nsoaRx=W?_@m!kC<9`g$m+`QDhIW1IYH+lnCnujEz@``(0V1zi9>w8UQd$! z%kYiHRX=*Mpe7D4+4L=ngP$^-zE7<-@zP{A-X9{bsBO5f(JtC*N|)5nUVQzo8-2>m zn8{0U0xv3<7=W$QkUu-m6B&oM*VBnW?d_;+ zt)&HKDdK5C{GEuu)~AgnSN`^t!~I%M#{l+t7|%HAKef!Qnx-p-?XLguTY3IaelQ!_ZD^8*FW;=~ue*%vm6XkFC>{Y}m2P2L$yt_wlcTt?RyyT}l z?_tL%os>T2Wj5BQIBBe%OSzGsUyxrwKd#uq#kSlD)wRo;np)yD6a zPFvww*|;WcWO_#CxbH1ly6om`d;X*;j-rywX3%kYu8df)DlRrL6X2FDCKd(w!3Q|I zDeNu6K1SHb3Hx|qpCIg$ggptyAwCKq@i77te>30;x|AdojZI0D4QU#WVk*VQ=Vm~1 z!}1(=qwzht(U_LpFs0#cw9-s&)Kiiht)r70X>!xAM8Ayy+@ryzB$_>!5ygrPj6L@W zDBrR$_kWNN@DOP^7llyrEUaToEV~>1aJ(^*^{c0me);QyJ7`ajBG1v8XD`D(;B9u z^%6&M>m&_;E5+_NSql5*6$aKl+sHP|H1V_~y&J{+jb`@4lV(RK}!_$WRRtRBKX=T0-QE3y@y_A5S2WOIL;!0KO$XWL$j<9RVKC6-P7aSUrN zh+#+1B4?2|6(3pH5A89`|3C~&njFjXYQwMNSoe$Z?3ZsOu;2VCkx|}~rtm3W$sgeq zUy3Wmlj2D36gP?&#fjoW^b`+@1BFjvlYa`6!lSS#oPXXAGDYkYY5SsfWVfcm-xlFU zd@1xjgq4}k3*~$@yh5N+47blo^GN!>G!-@xPWOd|@{i&CD+RUoMTQX2e{)}y_#j2~GIJ%81wwjlY%0{~*<0uE=*-kv#LE^eGyB zh$JOsA_K`cA4;Fb>6e74^s6XDKQe-TB&W{~(QEFLbU4K@y!J__K*Gw19pjI@`<_GH z!#j{fNc)^a-ECwYQnar*)IBTYMg}9=?xF7CoY&lQBHVK#{O3mCmtbd7>(@#E={}uJ zM?qmthDI~VoX{!wUat^1xuxLCn=SCg9FJkw3cQcwv8-C)CpaF@W&gp*74i6iFcTmg zM_zb;N-ae)JOA~w5%_DsDL$`5(?0BRfuF-SLACu4z@;L7pGpLf@V`djal}k0a;JS; zsR+-Dz-L6@b0hGZBk*;=$?8>TlAqln@Z}s&!DECd_=h ziMv*9SkG=-T?PAElC3JFi)uEsn?<|TYw8<$fLyNYKOjrkQ@@-T)@^8Ru3r;oA+5o& zs->-|UWD6R&tszn$&Hja+OmTL*T%IJ<;Jy;*14(%5`yb$w5U+jG-vp0YHnE5vVuoZ zc>8;INa3wv-Rg$*t4V+<_avAN@f5b>7f-*DA%)y0c-;nhy^utDh6LF<&Oiu1tomJ2vnK>D7@|Mv)j-!g&M z3g{7Vjc{)f`0WB(1*9I0_}B=#MSLnSEG2x7kTDAPC4%l|LC3=Z-Mzy80Rg*&JGHB^ za6d#4eEI+lQ?+&!?^Ut42uEUk1aWqN#|P=49+J{y7m&vTII@(-bH0EJh5J&0R|~j8 zz(xU^3Bo_M7nIKL3)mqbk3ZbE3;aO=e?|}+t49G1I1JZE5b=GLAmYpOO&3pIUch-d z0rGM|c)VPI4~ZA>A>{$w3%(S;9|(A#fIMH}&hr)SdxbrZAMj@ghx-v>|D1q*1kpL3 z5co#~7ch37U?u9I0$7IG8A0%|3!L^E38#LC+^N4Iyi(xH1ipnJ!sRkG;ZA#JME9zI z?-0CByC)or!ukW@iy#k);9`Vt0K8tv*hFScB77G1It83T5d13yPJ2e=zF5Fz!o61D z9s$<~NC$?=|3-mt5^yI$lxvr8e*uun_XI(lK?x8%MkBRvE$n3@4dvQIk*qI~WZu!|t_>l`5E!>0oN zm(Jic*EfojEBY=se5wNtFZIN{za2uT9rEC!JwttO*qheH=zHNvH+_E`=_Y#7NNX*~ zP>hG^#=<_R2O83$eVY??ZA0C@(Q#>KR79U6kFk8Cf6=HSIx0y$q%+efMW+|?CNtz4 zJ(=VIxetX?N;HWNI3k0t^BO&Zel*M&-ROV&6b;Rc6lki?q)Idp`p4(SRTln<&Zwc^ zpd&X@6QM28-$myDO=P(GuuX6$H3>Qf+6hhS+z72tqHu_YG=+zzV+cPFa3{1>k{_DR zA^cPwKgt8i??#yQ@jC$A3N4l7w+a?|I%+r2jE0n?qjpD67Z0Q_M5TpGD76>*^jb!0 zZ4;%J+8P-@Y8UkU-U4z0c2dc3AB2UT-vRL34m;u(4=wrq5@tQWy(swCVJDU3cL)}G zex=x}r#6%LB|=MnZ^NwT=Rt)yCo7Yn17L4J>-n7nzjN?N{3yRAzYk&7^Q%N*y$U<2 zWc>aB3q3zN+tCU;;+Fy~`F#zuo?lut?t#EgD#`ClSm^mxWN7t`_@zNhei>k<=hq5; zG?$Y~@*4>|J-*D47X5LL<)>?FByv?1^7Gw2a?%f}i{@ z8xaYbyGbSENAp$+R~FSq&^aX$prpGkg5L|EqkNW1(#iA5l3xib@o;pHM)0GvKsLz? zQ1W{wg6^$v;YVj`@#Aveh)sE%cJ74Cq=+B6lZ-}!p?m?|Uf@#6a6gTpi^s=9^;0TI zNAqqyzf90k`AQ|}qLCp)M`cB-5p-)cBmXBi(ljqe+?u4*Y0zQ#5K=@#Dj8a)fp8z{ zflX#Qa7jl{XU(iwcx%G9WQYAMXo?@Di|VMJpEnivn{^%~KQS$AZv6+(ucg>ACvI}SKAY|?#d#lkIbAyyL2itn3o`Y;iuRmv zBToJt{K6Y_#yDM&Lvj-6goo*r!?>X;<9XUuHD-Z^oPGX*0|N@kaB-=e}>g zc;r@=o9!-GkVW@ToqtrH_Tt`n{7lc2uIP8l)cAPZQ>?tCyiBz$GX{WX!j$<4UP~Vb zj&G?JM>K3rGnAtEV$;Ppmp?N5kN)Sd8<^;0Id9;k!W(KPbP4qQnJMt`hMGGA_Zshs z`7FKc4K;lR+-K0M-mKtbykT#3$1aGuc82HC^i!}O>v^nlUUjCYrh0U>;;1Mp_Uv{A zkH2%4(x+E+*V_as6_gY0?*473d!h!g-7^8{;XA}95GK7`o;!mmP9XI+sHUk)tGhg} zs%evOI>D=4*1zqr>)iV%5Nd`ezj_tkv%kK2Y_-_E%%<~E{I2-1;5fdwy5MfSBfiD$ zS1sFn)sYUIl8WoaeNOYBcf~IcP3WbYrp{n=`(Gi)#{|g<3a~G{WNXLE^vkcQ%QO~j z!42|0b@@zFKi&Z7f5muQHI0qouiM_jt{ffJ|C;$V)pS3__%+p%a8$k0@fyxFoK#By ztuvYJH8m~aH8nlqy#Tqv-lpt)7radqyWEBcl^JZ((PPG3;vS`OUyb3YFO0{}Tr_*7 zKa5*vDqWqf8l6qrOr^Mc_7eXg57K5$-7?RKx6YF)al_YSO>Nubng>&qbrDQ%n9SBr zm}bEgZ9NXt445p|)Z6#CE{7?`x(KEsm}0G+FinOj&UzdsJ52G`)I0V-n#3M8!MX^h zaWExXJ7F3PQSW~neeILv`V*!3n?k z>}(W23qti6-|DO%pgzUB&7N(m_E1|>yTdge`M$6Li$TG|-aTUtI}(eHo%?^3INj8# zO|%4*iDqaMv;i72Iwsx|RHD~IipPD9m;#r7VL*G$*+a1`G8Gv48}7LV!+(Y~UM>l<=X9@E0}emzr6r$SNr+?b|v`d-jz7}=d5^; zpNl)Krq7Q*1-O_=IUz(W#oUt5wrgzlQ7? z;p4J1yp2-|W(B?NM|qn&D>$e9wAzHb{m$UiUM-Ez;N#xYs4GfAmj6*t+!kZ-E29_X z)6L%opLM<#KD3w9mEf{=bGEfGPQDcHpfjRnuFU~w0rIjq9WC6Q0g?`|-!n$=QG%}a z%8=`tKvaRpzt58@TrUlUGF2B!UVFD{ADy~!4{B<+nm2k8Orv1RAKeL48cYSFkHeG< z)1=X&~b8d1u0+j`Yp!+}18B&&}ryrvn zKn)50*84LLO1sX%TbI3?PpVelBg_iE=v{(RFdnXH(7w@q9&&r-f^+>MFM3^2IUtv6 zpJ&k)k~6x<`zYL*qbp|K7LpP$&szcC?EsY`(cP4|X8QeVrNarg zv?nz>9`>Mjy`^(j@cTRrqB>Kng}bTvr;tBVyu}EL?n9|P<^ijM6FpMpc*533C@C&N4j=9Ms~z&r}(RWPT*oCfO$2fJdChurJRUT zjfakf)=FGIMo@A#C9UvTpYK)! z_AZw*cnpVOPU`M2$Kw(2U> zQ>(4Zl@6F}B~Ga@rC5m%)%8>>afyK`%}P8>FpaPhhispW#be<5C;R#~iqTJC{?LAy zoAk*x)` zK0BML1hdc{rC-5T2tCt@o>q*5tPP)m7H(@W0Z!1OE+HKmI*d;_5=|oy@zj9WwlW=}kJBaR7FR)hs2o zFkxVI4CE>P8>FoC4{8Rt$lP*&q4) z&t|B~oAF;He)R$Dou9+BS(E6UbRVl?c;Oyl2 zdpljxr;USml^wo#*MckkDT8+{nC>Un^Y>n@=Gcn2V1#ObG@3zHyYo9u1`Cr`~JrEl+N64=+yU&GMON{mIo(}1rG+%h;?$el29QKS^Mc>=wcbl{4 zxHEV=Om-Q3j0Xu&FLtXnGg5K7G7n$+Lo?yVmgCxVDht9hLCThPX)Ad^nHMShqz8~v)^m7OS){~Qk4Jq zYG$EQ{2KlrJ@e(gV`9$SliRDc_AL0}3*GAXXHwZLKwHQhah)eri+5tf;{z6BOebQE zo4=dDu+IOJWn?>y>Oe6j6)PBO0?Ee+DpD@My z6>2|WiuTW<_7f%pjYspM(6^@N83NSb9a>3YnA;DjKTI~|IfF_&LS9F*D=p~FV|N$= zmH`tWdcAc$==BVKj6Gv50n`=V=YuL1U&%mhY{KQEg2trGV-thMXw&8Is2zoAo$shy z3P*LG@t8Z0S(K|-aI2T1p0JvFl}eg$oB zhWj11G)wuya6YvxDbVbW*)wN$%qjC8YBkL}62D-7EBhqT7&CxxApZmZ&{!y!zUbO~ z-1M+HV0sv5#~(I8GiaISoQC}a7A31@UxQ`ZzM2m`l}pn0Y{809YWX77G-*_Km#fI9 z)eLP+#Y;jNQW6R;3B{b9FtCQtIjL4XDr%KETM_L6YLSCsv`zEE7DwFf&7Lus(Xe2} zri>?cUtRuT&MMs3sA9pn9cMg=osT!{7c<@I8ROk?ow1!GvlI7d-^q(=i+8~%mYnWG zWuK(y#CIA85@X&NU`AumXh_((0R2dAN5WL+`K*rg?3kU@51MzG9(-H1K}uNZ!I6Q# z%I$IeF~;tU zn9fm+FTwBwyNr*nt?ugnoC_&wt=nvTw*deDU?QNvCj+rxy2OrrxZY*2Y54 z<#np5U^eh+?(Q+4`jZ1jj8!-yRha0{;ci8IY-30SAzhz3=$q&?O zuVugz_46_2?35=Cq*Jekx!M!jOj^0>ZFQMNZ~dZEeO=qs!BGlkb?oaGc`1&Bgogd8{rmkTAt`>M1qe`zlH^Dv;itSp3MC32OY- zpVj?Tov}URNz)z+=fmt*4fa2i<(8N;cy?1lSE_qnIclz}{GB)YSjCHCPqW!g^EpA(xi&i`W5uaW+BD>I&2GMZ9LBT38@F)x06 zQ|HIgd?s{7XeLBGk^E|-QMVFA`#HUbD6ux7-F-oOjGi9ful98XJe%5HX`{KDq4kM5 zF|9jXHviSFkI$LY`XjXc_q6_W&Q_R4`hU~9Z_XPqCHte>eljPf?MG<)uWoyI&K#Jc z{X=U^rhhlDF_UNwhu4>h7<+f}+LDd2_jX=a#$xRKLtazTG4|fZ>q)AAe}lRB^?suT z<>RH=Pw5VLFxz{=TuNrN^V9d&jR@>-0F`n7J&C1z>&TVd_O9aJOF5YzUA6B=sj-x< zZSDJ36^e0$3#ELcd#?WcD2$@FrP~ zMGxe%c;Az#cX7zo(ydkmE_X=_w-%YLFLzrD&;N$-; zZ+a`)Dii|!)5hfi5J_l&=x83;#pvf=u zVMSnCTPNCrwvBh7O-O>j=KVXER1Wh9nlerJd3ntgHRgZp^6{_0TG2cSCW{K2OHZeGOxF zW;{VFV3ZykMsr^~#`6CR{y&QUr}6(}{-3C;$27JeA2b&H+CgK%uN}-@TZAMLEq461yC&p(KJIkN`6{Xa*F$H7V0^hS3zq<}& zJ;P=QCMhv_+5Wi0FJHL3XzBXhs?pZ9o?KqOa8Js6>oYt`Q3lq~wAGbp-_M8(?7FI1 zcE);hU?1?Mz?W7lz-5_GeraoY&I>*-t>t;o`@Fvmkp18O!d}u~>Pv>{;|i|&%=o0T zZ=sqKWXB$l!kawUAN^MW$xONd`&!cc{{^IQ4b#TuU-env6Uw94so5efmmusH;YLtu zTyY7RrQQEYNO3VgY6(VB#+hU-aJf z{lS=D{OvDCU()g{=Lu#1qEMbu`8*#{eyjg~C_nm^NPP|U&wD$=-`i1;KbxSxDbMhm z_jc@{n*^;X6rfHGcW;O2=}eraPziKuxO+H6-x#8y6ER^m+&vtkpKp*f*gV&%;qKw+ zoU#A?&3iY*@72)8xaRKD;pBcK6fMpDs17IhQk*f+l;%D|hm*ULHsHC^+$(fAxm&Oo zuPMzvO^1`ajSi-8rMW+(!^!<=9ggjPX2%&QOE77%T*!&be={k!haKi|1JXm zFarON2>kO1JT>&~Y57SqYowy^=0xCC5%_HpczXoCJpz9$0;hAtQc-xPBk+L;JTCMt zYw^vBz)K@=Tvg?Y>T@!3m|uV(nTM^~Gh*S36f2r*R@Yb6G(vn}{c@6xxcZjWE$i#o zm9;cCPAPO0mXylN6a0`UZpAdnyCXOTiUYCrcw(%oPH)e{sajpXS`#oxh84~A^|*LZ zyWti_C)|CDywqMS~R}VJL!?u+^+@O~t8uSGCWqf}X;@vaqw_3pZmeHJF|2E9V--tvPB>@WaBF=t z5`yev^bUE$niXrgD8>-dw4p`kgZM)ZUBhyj_qejsy1GFNU&|u?nm9!qIk}#)rEcAZ z1}UMjVMPPKywljQzNLOm6-gW9F4B_A z?gLWrh>+igAv(JF(^y+mw~Az7hFnO5=1|vg1>wRa*KBBPT(MzIoi2UczG;2a+Qvq` zEpi6gy=HAo!-_Vxx}mPB7Dc@Z*JbJo9VJCo9$cQPZ)Ua44a;#0sbvG*iISEg%&Njd z5_E}Lh03*hy^guKLsiwXmfy_TP`{?GP3KHGF0WaI_^v$Au~Q)jlb{2dV39ccU#yKc4CrY1+4@Vx+4{@G*?QQY z13jIcw+KF&0_GD0zh!z@s*NvVr!n?Jv}FVzAqe}I1bmMm&hCFApaE@2>pAZiF&jidRez4Cc2>aU!y0msEwp`r7K|Wkb^nm;> z0^+}daKJ6X{y~91E%4_APJI>KC2$a5x45%`bTm=CuZD~Pf)xl4Z5pMA#yN&LI0sB{ zF75^qoQJy>XzVDSQwYw&-HK?9K1dMp`hp`w^1wn*UNf7zIM8F3KqP!0h zT!``{xCm!N2`9ue@UfG-Hx zC*Z3BzAoTf0{R3zA>c^?-xn|-;707VAd{HBH)t*QIBZ;LGk!Vz;gnADqv8+K>_Iw z2ho2eARVOV=@5|aP!Jw3pjE&$0n-J{6woGMu7Gv{Ckf~futdO80cQy46tG;t3IXQ} zSSjFQ0hb6!^DM&>^b-W<;QRTnybBw;F%|BRhmJUh-hYZ1Iu7R;aTDvC-n7n{wcInlHV#===oKGpAB}zkK!fyZGlg4uLBl(eoZ=l=<-AS zehRamUpdAvG!K_b^4kjwJ-_YXR|z|cAG-Pwzt>>a^Lqdc9v};$B)^w|>-h!1uL5?& z4?}_wKgusXKf4)Y0est1Nq!%}LeI}W5pU#REtL3CITIb_<6i~DFpR7FGBr1Wayl|6 zAW#bTyXYmVGrRP0-6RAEek%` zV$sg7M7t3I?5VljEQ@%&P)lK@K2Kpy#fhl*`MHGitNISHey6dU#ZOQ1q?SKl*M*bN zilM9eS@my@f}ZDAbwZAt!Pr?{6(DmCH{TdwlWy?E418VuqMGQvk&Q0j%AyBK3~vl1 z#sAFBa_j*uE&cxUt9Gf$g>--AyF-|w3&9?UFRYtw@|W^6(#%#G2(B`D;>%4%Y1I{9 z82QVudp+^Ta88EVp!ZUI@#d=4)8Q${Tg)BRrU039xmhV*Kl}H7*1f7n>AtBa(~Y-D z_h_8&QR$_>J8Dba(KuBC46g+e#sbWyo%8>^r}SCF+An$&+$Q&Sabip1W%R1rQK{tQ z+`@7?RIg+IEt*Wq6KaWe=A56YcDhQr6u>6FcSrYuns6T|3g?QAxA@c!+UZ+q;b+)R zs2#jSSPuB|^LEv@ShF;qRDLJaEnGV5NbJQKeJ9jOJd~&K_GVm<9lLK$;O$3T6WqqN zZ}ytp;9|u+0-Tdml45v#!Ql#$h4?BUam;gT+=s?ctFI=yQ)HQqw_+FfKtG!24y6lc z=^sFPm4aMfYLB6y%2ylMzZS8#?O3_ZWj?O)H1Iq*a~9`Nl!a=RZHI?m){gd{P+zcE zvE#YiM{jl4;LX?lw@h`HV=t09p8V2i>$4y)D-l!MPEQKa!AFPfx|J-i%E$7iegK+O zHo>@k!6&JBPt@i}UZw2tuVg!9T5>!$c@C>z*_8ZfUnl>b%)SLki()Yaj6XTxRd|hkUeGzh1RteyQ7`c%UIo1xWh*ZynIG%UB0p`iq)L1D zucb7dZBTd){QFrApXz>x?|~g~e~7omsdae!t+(F#=N=Md=uu{bli#uDY`UHqdkqMBivt#z~70${|ub`FNLN(j=x2? zCz7L3)X$Qv5vd5LyT?)yel2jaDu<>$k9vVO0@9wxt>h>a(Ql2w_eJ1GB5;yTA{F_U z>$to_m(rrNm29%Y0_r-AWVn@E8VrPA?}=Ok7mK&y`3jb?*YV{|TI-Z+n&fNfLOLx| zhdp6Ev$>^;;v%D}6N?lkqXp3wjT_c$GMmKGvslR2bE7oXtzF&JSWoX8Q360a+^YF) zAX~Gd;TB2r4{a2LerUvbhJM@fc-X792Hm{2wmy{mh+_*@^@WrtF$SX-a2o6D zn`lj+-@T^yg`6nTSgbL$5X8sTK@j8ME`k{3I#K?y82oh+9If4hj~%0}>&9ZNNn@{A zj1j?t)4_^E47*wEcx{doYZG)?LY4}~igU>w{xS%{9!rHR7WPF1VafSiB4oCV!}=%L zgXtha;0B5h#^U1$+OcjW@JR&0cN#$(zB6G@ZHyrgp8vPz?GY6`JTcalLb%=y>pZj~ z4M#Gh3WN!CBj_q^_Ko_;_i(1nT6oQrnwFO4hT0AERMBx-~ih<98o&K12K{E|Q-IW<5Wevn~^U2uOa*Vb=3|8inH_sU*Jxu+a1S3dj=JQF`@r>Q{i1cPbZB zm0++|GxC3OBmEXM%C?DH;_n#f{*gK6VnqIZ=?PFql^~)XQuNjjG*FMQT(!F3Xu8C$8%wF!p%waID zS(|hIx!N>PZZr*aC^&Uk1Ze|Px4FLbo3pP7h1#JO)FNE7c1}$DXIbxxkl$60l;l)L zoj+1mS54++)xTDcl)pf0a{5*E zXbH_k50*eG#j$_RQ%n)3k4&^}z}pXk!<}`*P)4U@52IPpyk)4nh4)$17HW!G?XaSG z%usr2g~N*GF+<(MoQR6ft_^jMoX1!r+^Mw-D>}QSS9Dg3#z#^S^%c<6{>gcWTRH-g z`;8HJbp+lJfo}v({KzlOOSVS1?-cI)xO;5qEmtBVKfPoSits*(022P22;2{x!g~*z z<|%)SaK8|NN6G-6DjR>$5q}!*Nk#bl2)qh7S-GI;?A98AXLH=jHb&6P$%8!CqMaC$ z7zWz>$PYg%G;~4|BEcjfJQoNbYr*`b4yRXEHP^4;2WP~Tf=<3jrXdz|mWC6OA!J*# zd^t|7w0J_NVTPO}60;}@2J<5Bn{O6{ywl_<9I<~u0Pm17E)LbyG*>NOd)pcuOu~5` zob6e=W<6Wpu%2Fzt!fS(^ucTcyGd26>h+T|V0`ixPK-7O3q_7cRhK1okb8*ZDq0!S ze1yIvnupj4VxCY$5Cd8&SDKH|mrwHp`XUV&FA$`80G#+d<4S@k^!WsHw0#-!zl?D3 zeUKoCsYQsz{3DIv1TjyUDCQ{$yn^fzev5$IUmoLU7Gv?T9wmFgmk44$!s7w{lL*fh z^B{ynElVuc;{W&NC82ziRVO@c2su$b9LAce)G*dVE7vdty!s4h&nMHvIA~jy|1Zs1 z(otMe(O8ufl`9PuozSEXpzcu}qV+1GAx-!^XliPxo|2+^SjkP=pX7HnP(8nc;Aev! z@gu*IUoFgfeh&es_D?Fw?`Bx&`T4+)=!hT1OY&=jSGn|@AZ z1X9d>Xn(|m6wTT6bD%88MOUluY0z zjbhcI=Tx{L(OK!vK{I) z2CUfHU@S|m+3|S}ntjuIWkE|>*`UP;8%R1x4O+23liE-==*^KfN>aRU5%$qo-JpqUEFPaLC_Z(aaGeC# zl8|dIcb#FyP1U;NH8gh{P7Yn8jeThf(zt(EIdls)A5^wqIWN<5C2nA`g4w?G6518I z+WjlH8#_b6gMU;5ds$Ywnx087y3H#uhlI9r)tXrjX>;X}HiuoE%9-Vj+1fm2Drm~e z`>THnNi9!RzlR+kW%^;`wH)&3q65(bjYinnQp=xO0h{^CW$7g`1C=q2 zms>8r=^=lQxL!G)33_Eutov6gn*nKVGje*BEW>9OK8qQNcd?i)y?VslWY3M&yQ@!D zr`M#;?ZiuXjZwQ+;7)PWztrrmQ7*o|?o>^B?ULHvwb$SBvUoe|6i8TbP3@*Hn z>1xWjcN6i;nlwL~_)V+Dez8)t@X$_$++S{b&tq8ko+l;NweG#Go7SDa_HMX1$zKAx)i?+mz^?<9R z+*{pg?j4Ojwdv^Ln&k-`~9{AjbTe9)*(LS-wVZwj5 zgZ4+W99^o}(V^x#9)S67RhhXheRT1_XhO^&Ew?C1bGK_*74?no?v8+KkXfm_|x_fu2i){Hlnw^2$S&DbN{>@L4DcRKj>61PeV)s}{3iv-htC>#w zKcRisJQjT08|Us3GB$ogwE?Fy?rzN6q6~UletIYk5-3TIM$~{QRvC1*9O05Sq6X3} zkW4kmAde%NC@e$~Yw@Xzx5o^OFr{&M8@WNF$yCC2n%&3MjakN>$5n5kq0^^U9{j0A znQ^<1T{6|JY&T9W4$0e~w#*258^%t<6GkqzK$%o^3bG|^&q4Ym$=ev}YM8X})Je$O z7;0;rH0KnKR}A4}nlww3v{7c9zMHkrHojvVzh`!;KQ?d^WNVl%w*@LQErsTOmN+om zN)k1)Q{7>S8p#2r`?$Kek7X&@ac=i`blmP2W}8lpQI)tYPGgyJxhek-{-Lo@E`7|k z{kZZ7PNh5od2Ns2RLUc`iT{X9S@Y`R)y6&kN`k57hOVX6>l-b_>zA)zjsJS>tOfN` zR3dm2HutTXBuHdQH2*L5-UL3XBHJIYyL6}1SxFGW-bu40BtS#LqNsGXW+x!1xF#Vi zjcgH6gB#7};5tZ<0U{%UGo#~1SVmD97;)h_>U7IEvW|_okvQ*lLcmtk{J*De-R|3$ zFzU?j_kRE1=l2Ru&OKG3k z)bi*R)Y_m!Kx={a0j>UN0j>6_0j=)I0j=hV0lL?Y650S(fC|t{9WBQiAK4bULk~sD zvm&AcmwPs1tdlKDnV-C=D)4zko+Z2a(W2a+SuyONPI7N%SK{gu?#t{_T%F>c%nESz zX*Tu909<{RO+C^V-jDR}z4Ub$--!z!EMlQYR&JV;hOuDvvrx@z+O<2hXk{1ggLtQ~ z(7mrw31t6N-r`hd7W!c2L{33Jyr#vxuyPYGp*u8pq90p)(3ARG0v;=2uXXuK8U@FYpTzy6N_BV_z+X>6 zB;MJ=;P+^l_Ki7|{TjV3d?`Ks;Dk1H1hnzeQH=9+(Z?yGj}t^6$B8~xL?5f7HQ>wb zwQaC>k>Nqc5&uSb)Eeqv72X-TEwkV>aS8q(E?@aQ^aH+{!%JdOv(0XUr$C#1XE#f2 zC-KHp*Rbpfe$992z<^Q*tG2+H7T=v1N9ooq1$^-B!`B+DS~Z~LFYt8!l^hayjig0fB$Wi3D{WAn6}O|DXFtyxrJt&G~U zlVN&xQboHaigvZ>?TXb7b)&Yffx5<&*oWGoMcNtahjhIqsNPyjD7?gXil;V5d47BfQ%647@}W zPQ}^oDauw{B?fngy;o2?VJ-eQxWYWg{zf=s3;eu5Q?pR@a`*;Hr>l9(Up~8kfiE;? z`NB`A6ls2*cjzHyHGHf-OY^;@HviF*P2ueW&YOb1pYI+s^R!xV_$e!dG#Wkw_ewYyPT0A=9wYVhWwRl9tYw=|fuf>Dm@kV=l zx&j}{v<4NXAcT~7=D-Mkf-I4+XcrF6SNg5hpw~V;@-kpXqS-xqDlVKT>d?fpD|kw zvflVr(3?S@X;y^BH^(4PrsxeC*Bj;AZmS!fqxXE-ZN) ze4=cd-32rB^oojkC+A8f>cB1YY$d1WWt8kclfWicoSIuXuRP<)c_}3)=UrOzr!xt0 zT~wvz?sg*^a5gu*qt#YW-?Q8@*g4X73tP$<(bqZtZF6^i!;q#Pb%%V_?iMz2BIlNK z;{sd89;(?>^XO*%Dq+C2PvPb+Fr@;e2IBVH;DDUue?czjYM-XfuVI1~Moxl~Hb z!!r{+pUh6f@4t+F#hB-9^&YC&=IvBTvx042^m|d~3VuBw{-H0IvI^nfeo>HRFKs#T zdz#%1dsUwi#m-a6ch|~iTfg2BNpJo7xmRfnE^T=h@h`p#?@6_F9`-5C%OrSl`3|uG z#M-jtjmyFghm>`HSaRrEmGtdhpJk1fgPEflz&ga5rM>0KLRQ;DpCu0|3nklDe-<6@ z_kT{Me2ACwvsbj7=Od5X{%IbgKcgqY2d^;d^9^6bYhPvd;fo=D%4cQa3uQk2u0IUl z{`w~x?H8T$=%b!ZT>q#Zf0#W4KSdS!_=eTvDB3W=RVMXXC-i5EU$gHe{TUz7p3T2< z{rLv|z7ei|?TQLbQ_}sXj=_^WJB(k}V?4^n4DLmXsIesn@t1rU(N(I8=8!pl=YrgN z=A;yg%M8N$d6*KiC=zm72d~^}uEifsZx^JVeFG;quwLx3&Ed=8b8eWUQ0NOtnFuKp zb-Q$`GwO=7BIqua|J?(RYnL9kc<6WbFw~1>9txLmJMj<0?Cw9(__2Qs|8i2#PC@JsY^Sgw`e>A;;f% z$#azTsMK4NK-4$K@Q&kiP^)KuqdnnzPkbYjf?ln6QnzozE)K`w-%KAAnMu5aw}0bu z`m4=ZswPG0o^sO=ss;Oq+LVZhDEDzZJiRKbhCLg#5?f z0-Zf=-o}ir^V%WKPH|FaRc^UEt*}9c&UP<)7FxBjLdxok^Fp6)YOfsI5trNH@LhAn zi3dAu*~;Nu{IX#Axs~=|`*$dUc7vY7e>u*ne7Q+UQ4Vuz@|?=e;cQnC?0_^25lwGy z^`7K8qA}+KYv6#-cwy;d_(R!X1Ecyk<3X3!G$8ml& zQi(Vd|Apt98yU#fB*_>7n(}u<;xszC*Ns-!qCsH^ru!43iMUdb8Y@4E%YP`srKPVi z(2+}ie~Rh;6uv%2)pcWGPR7r9DsNA$OKCx)?=#WfNU;S!E$Zg;?)EJC1u=_dYk&c7kT5^IS=ev4w|U&8rUNBC()FB%lAp2hNa)OIlrv1_dV} zQF}xK7X1x@&PR_;*XR)u9&J3h4K(o=8`_h~Q(~Yg{Wk`h(tm59Dg76Q_NVmK1{!1} z(E04fpulosAR;7y3y{VvG3%|Vh?@-11JP9gT5lwv^TFo>)BR+lordS<9$f2z_*d!# zFwwLYtOuf>(+OarUop{#O!OBf`Uex8fpSpU4g;us*`TR>8GtmbkkY$gSh3kRJv&uwoHqqNbQxxsBNW*A)&y@a=iT>0?H<@S^>$-ZN{1Z%cHxq64 z>qK_VB=Rdj8d_kIiGPlXzQ;sAEbb>;gETyUX1`8<5a|!`^h?j4JAy0;350TxN*mK6IuC`ddK{Bfd%6es}V* z!_VgieKW;_J}A=l^S1{vgx?kTu)r;ZAs3Sp?mNj&81>2! zbhe=HB#is_69#_+VH7$nu!@%x(H#WtDX>f6B7teY2Jzh}@HE1ZJC`uzJ|N;B7x-@i zegi)Tq5{BGii5QmyV4F#}pRn1y4&|e9gz}+C{9JK9PH`U} zpEuxsitnn~<KP9>nd^Db?p&baL{OYMz(2mhJYShkC zBtg|c1#7Rwwh}cfP%bR0yNs_P?QHXC8U&hNMp3abT+!FkMHtL)n6}vr#;`H(f+#nS zHYjXevtOJ;SLsoXXvw3>T|a<4#y6lA*`|lUf7`1L&G0c4=yBwM#*;BXjq#9)zr+W% z#DkfS(kS!+;8FZhzC7@l^YtT64^T%ujKp6fAH8Q>B8`yFherdmb?y12`2CsM9KJiQ zp>jAMm1{b{D6aqn{T9)KF0TR+MtO%JZzAGI9@I)i-V$6JUQ|d+Bz4ygY}-px^#_(Ca}TZjAD(A&=UIIy=4x zVuk~#9;pCo6QjIpbl5~ggf36$?b{a5o*V0H=pr`DgNF3U{MP~!j&{?HH&03FklxnL6 z`;^rwIsJ~L?t+!2YRhXM=$Mmz1S|9W#L8ZNg`XW}uUB2!L4}`ol~8YYWd&1l7Dm@S z9dkMyNp*Dzs<=-(A5IG3=?eM8YDe)qYZz_#OC&do}f2* zd#VR#mV6N2JN(8MyWUbGUrO|ToCTYTmEDxXO77);<*<}H(4TcAKWNFl!awYgHTOz? z9Bg`4`W^|oa=m{0;a<5*{PJN(?!Eq1!M?dw{(iZ${l4I^+`AAv2tI!93|^5t-G3st zum3izU9QH;@U4NY`e$oc?mWMv@0`OgRon+JIJ1IZ1K;nc3J$HjFZ_U`Ja`B2gN_@5 zF9AQ~xHkAT@WYPD!J*Uc3qRs03%&&WOUKxt(s%R`lI96C)=bPT4&*Lr&UFXoERkRh zaYxmC;abO~LA7tv5ji&@@a&Qm4~bueMKM=#2Zq8ywEy zOGsbm=n?!H^4B=J1c!ot)X_26sqZC6?wFn)yrgftBQH%)3BEM*zVKQ{LhugoZE?f} z-wOMx?+ZVHez*fEn;r3;ugH*cMx2}UBJGuB*@gb_Zus)bx!f=Hz3fP$r+rwyVN{KL znc`VqH0ThWZv5Ge4|UaJat@v7*C7yxbK_Untl4w0CNVoD5Wl222PeV}wFfh^ZHRaA zcm?ra?=?Q(`|FW6w|4D7 z-jy=iVK;&7C;PW{fj4lTGuaQXaXA;v*^?@~Nu_d+RIz{Y9H4%Rhv5`PTi?_G6Hj0J zeHo7K4(-HM+dkO^OO>(&zv`+UsrI$PmM*C5hkb(Gk&4ltK~MC)VY?kU+4~%sNuGpp zc8?@hFR^lWwXi_7+I^M2y3L^|oYt>DCt0bjNUvK$&DjP2|# z8I#uAHCd)LOvD3`0;b(~jk*nRhfOnEBDS zj%et?_tz1f36S{AFq*31;WY*YTuwwt02;aEAI$ihlh&?=h;;l!K)MyizR^Hayvb3L z&pqdWCVBWN1AZId?@RPTPTTqK0-}AKPGDPriFN>JUppx|qA%xk68pQLFXMDFYY{Z< z!AnEynD@D-A)fAkfb+Mz!2j1ok$x3VPiIxcECSW%4v}7j>om0HYC&t|>2SfBBeX_E zA|`VF4E7QcB9OedMLNxc(=c9+o6^4$>D2CNwCfrZd}6to)97#GBwhC(Mjz$zv3ha; zVXi?#wLRGelEjB&RJ;l zyLmBAJ9mGz3aSx^9unHU)fhB`E<<)mAET6Wd{Pr31r!QEjHyS?cQ`)S` z+4IXUJjek#Qla$hAP@Y)DOt2&7c3y?jjx~h5oMh=qY}4sF9q~%&v;%*l!+>eg%7Ni zu%gn!;<5?lw7A93D~V;AjuR-12T#zATJ%)|vA-V=DgB-$n#P^282M?(fr?@RQ2(KkUo=zL{}F!H%U(>qoVI^S4C7{77(Fy*3WoZj1%2l0{o zX#k_VDWH1-^q|Y5_li;88iPENqszM$*G73Ypce!5pv(IuB8+@rf%fT4z&hVExHj^U zJ&>afzV}Uh4}p)~y?W5)ok4_A9(`+Ti!UAVMn19ySwj&b=<-q!XKa@(XcuY^YG;~* z5g*a?E!-$?oznKVPM1mE(*}7otJUSv8k13;0}0f3deF;9Yp=%gZ8!8A&4P7#cbViZ zMnZ`$Oz;u8-o*DZ_}XeeI=ju7?|wtR9fUk;mz^egjV zJhCA&%6kg(Ds*9jkH{SGQGGB>cu?`sW{PVVafYAZ&Yz~b$K*h zT{yFR=FG+Xld>b?TX1mOz6A^NhyG;Wf?c(rlaX)Ay8rrLY%e@hh7+Dy%8U~$J@~?0 zYpJ!=-N0CFmmQW`WfSW=@x*~MWr|c=)-Xy@YnfB7{k5m>XnE5$H|LDL_GY@X-~@B{ zcCb3-JUe;+4yjIRR1#9Yeeir@N`4sml4dZ{+jVjb#lvm);m2 zIPpIkEtYZN;S-hf)-&??j`>~Er+Gi<{7zVPAk{IZWE}oZ!?&NR3dzNGxJ&OZdVq7k%qLyrX&~zMU6ZJRRXR zL$jGL*op6jJwFal^re;Qdw+2Du}f*(uUH!2kk@{xuoVEQV7PZ%(LW9^s%KfYvbYI{ z*jE{sdJ{@&k+zc32jzQfi_RP-Z-829>3f+fJR9o%&*?kbAgnL0X9wAN)=w&z9+eJA zXQe*!b@Bu9Yw|zj9?C>znX*GUrKGDR>SFab>W8XoDX>(bOJBEyEIq8_t#?_Uv3_J# z;|9jv9QQ!nOL3pXCEG5wO}G8hw#RnLmJ~lEzC8XH@!R7+if@hYZ69x6WZ!82qy3cK zmXMb)F=0`{`h=GgjwXDU&@J)G#G4cEN!*L8DMwRIrhJj|RZ1wOB_*74KIQwAA5vOVSZXWq_n^aw4v@EryJ zpK;$Sxc_&M^C;xiK<-VD??yhEDXA%~$oClX--U8Ki1JjRT=^)Un%s!8?m?MXqU_^Q zhxSQdqdu>qUiYAWS0;8#{0{Yf8TDR-`sXFs5>BB#Hlkg|+k4wv(N5dZUgc=Fr1(>4 z!(XB;FSRAxK0(_)fHoc&r^bDRHopsP-@_V0Kh&Z>3M{JSL-fyL^i#Te3VpT=eb+l_+-qJd()zW(ChbMnn@WT~9B>ZsX``>+k z`}ZZ^r+?BnE5A+s_T)D^zghLo zwcq4?^IiBa;jQ7@!=uC9!_8m6{q^Ht-}?2)ue*JH=G;5yo;%3^)p+~tTYW_>}&gNe= z-`!l3hZ zLaRe}g%*UShpr2ig*>4vLW4v7LcK#hLY+eGaTY{k$QH7Ml#m>fLJT0|+Jd-5q_qc6 z5AgQGJy+n~GRT+?S$9F^V~|~sJpO{bLLmwH_Co&SP>$PBo-HWXdnn&`O_!joBT?qN zQTCmv!&lAms81>CwF32f{miK|D(X85^?n%j|7%OQr5oB~7TRU!*`sGAw9`bi*OO?s zGv~UY4R1wTzWsIc*WJ;!x1){!68lB(C_cJ{?GXTpYi|y^!WdO{rxwxxcD+h z_Q(lWJEkmHFn5t-=7NO|_+@vL&YL!4`t%vo9TOKWsDhi!1q&DDI0pAG=s(ag$Te_? z>(YUjIxTW9Ve$y6K&ad)17S6bRHaXaI3@UIHTsq_?R!DE0${Cf@ zW-}=pmzLrM3-Ljz-}roHwa1N*m($et(j`(yB}3{Yb(XqIS^7j)pTqzgqyAT{8O5%O zsC=&eX(T_IaYqBK5ykRjf^G=3zbZC8&X7uTcAF{P#`BvV;i6wB(LnpFV&#$bWi-&5 zQS7QrV)VNOiY_7D7-$VCmOqKUa%+L}E64(3(+CL{J~lw*^|GpgtwKH>a)q z3|XQ{F720@1Wfc?PTMZ)*NU*3-J+udjRcUp&wVqbpAP6{HTe1g2yN2L4${| zD%i3U#t)u_gUYARUR*wZ#vSac894TN(Tt+mix;s8w_%trESpKhgc&n1#Mv~kVW_f2 zm2^->`TX1FvBfjyRpTy_!#tiTr`%ew{W$xc=e zx;*hc1=iiOZ>29PKlVN4(m^iGt>{lUtKm5Z&n@yjo0RkN*=^?)r_xxeq-BgPeB(58TDFJw zlCOhb!AB<+gPk3opK)4tt{t^@w5>wDG~S!pvNR{Bh0X5Z>ES_6&Ki7!aBuxMT(I2U z@P@OvAzs10eivTH8#V6+OY_v@Cr-C_{vo5hFh1zZi#tKoiPN&=%ebx(5+u3cfxKV< zF$Ft|7B|Kz`A2KU)0ZBq%Lb1R@`E@l?ypZK2>I^_`R#eVu4#x<-$jOVG z-sCm;2{}DRIem$oSe|-fs>nw5|AZHXdO3I0p zt2z}oG3AETvW7wOd%UNt;FO75+IdpW%Oh?ldBIqdQQW9V3dYEZ)7ESHWlSi{#witK zGllyk^ye(Z^Lxj5CR2%>wI^XF5Nwsy1m+I6wzgtRx0{dTa0*8xxgAbXeDAa^4fk&9 z)F{P|X;kbqT2dS1Sz)6cPFu?g2YH5jSpPekm?R%%hfyP{!$FMr!>3i0GTC_$aff+{Q*o{0+9n-Dtq()q zLA2??)Am{FX0>0(6Kgx&RY=dvD97ma;-JSFXdK9#|#61Vh7P|oc{_m(<3l{Q*Lf6BZbF3+Wg z{#4PjG!Ce>ONx9nM7<>WZ0?3-3m>Fj z!kDp16B{Je=3id4s8Nz^N9`MvA4P4QN>IN-FG=s6PDC#-^nyAIJv0kF^cs5SHAC-| zqIUpx=bk1trSpljPIGwgoWPyFPD=_?8xyRBjfrtuuh@^;>(ud4y$;FTuHzO<$A#lFGxZ0GSEjqVibShv1|j;C(EuG5pv zo}Rt(5|6AY+Osdm^XJZ~o9a3%jfWQ9kgA+rzbLh~M=-fAZG%#BeX7(LxOHsmrlP0! zb@rsLO%JBlwhL0fCkIpOwxJ}G*-q$yvnR@0)>kv)mLn1VFcQb4 zdz~#yolcx9lfE%Cw6rj@rCQ2tX|2o*#YqXp=@rb;G2&mCo-zITB7Oew_HARfL5e!O z3rWe&Zed>SZNX+$L&y5IZXS=nJ2WkBQ*tXD4tkbZwmbCVa`L5oD6Iae!tp8YEgd(YaB!kHyN09c{B~>AQi+o*SX1SwW#^46vN`dg)z_@rtkcB_A}^ zmZ&DPrrEg$>yFvN$Kjr90NdXS=dHo$7w-WYqx@YU-U0kYfIpCWzZGw=dF&|OUmH-{ zXq^@;OQ`GQ#Tek@5M!Y5WJip^kTwF{ih5r9;i>Z~M!*Cqt&!P^@g9lZebqA7pM|C^ zlkoSdWitLwSw@;ItE0gj5p*PFc zqGt>DHy`v#L!}}1CL7*>OxLx*tYCfrr7iPcd?~oT51tttARa!#sip7lj&{EMNkBc# z1T-CX7qi!1A0F!R#I&N{N8Zps2&)5+bo?L;pQjy_^HK}ugJ`!BNj`6B!AucPREe?@ za|6W!U0)h)#ht#7@YcB%o;z>w%ek{K1Cyn!j8d9Ih^IXREk7D9?}k4knujgHds;b~ z(WJ1^ZM30Pw4voFwV)cLzfv$^qy5p&qou~Tw07*@<;2I~slgv_%jCwll5aM&Wfq=m zlLW>g=`I0`f z-F(iG9_%@$r{Hvg6EoP0aCYbKMW0d(e|PB>wm~|d4-!dU^4RVi+tC8rnbWuL(w8NLF_ZS|Q5@3t57Q$0fHELon;Y~uZD+bww{UT%5p)H#%&c7|O}d8^bH zkSib$xSxRjzykm-0Rw>t3CIU701PHr+A0kJ3>7d8_)@@S0)_)$F5n8_D*+<}xPc1= z6ag0lN(7VwdjyOG9t9XJU<~lsCo~ubJRUGXKpF5v0h55Q0!$V#1^8+K*8pD&m@42p z;Ohn40DL3hCIL4CmkX!>t^`aIP_?gBnl4}l@Jzrg0bbzQ0&W4G1DGpd9`Jkt3xKNu zw+dJYyhy-e;M)MV3%CQgM!*u_I{|kIxEuH$0rvtg1^7PJU>Wdo0V{x40#*sQ5BL`X zRs-J;ctF5|zz+#{82AyuF9oatepJ9(;99^s0gnN%7w{|K4Sy`QNTXn{eV9ScpdnFfH#2O1iU5SZQz3f{sZ_Oz@G)Y z3;dpd_ksTc@Cyh4e<0ux@LvHR3iuoFM* zv=?v*a0dY$finP|1at=OBA_d9H$Zm*J%AkoGJ&%IP60iEdkN?b+y{^?AP2awfL!1_ zKtBQffd>e10S^QW5|9sEAYd@?5Wr9Y!+tP$OUo@SOtg0=^q?kAQoDmkRK0X_b}%mJ3(`yi&j_;QIi-5U?8fegO{v zKL~h8z{9|g2>2!N8o;9h)&kcGSO@$VV7-7}0dEkn5qJ}zPQc^9n|&Jm8u$so76DHJ zZxyf&_$k0|1pF5GcLJUUeg^P+0nY+IC*XPD7XU8`*bZDTUbHg|3|Yi;ez{%3!r z%GWydX>ZIz4I*i6J2je!D=T*R!9S0zM{4Oc2Ab0M)0z(t`f8g-(OP$`JnbA?I#W&y zTKX;nP4aq1QRo1bEH2m8{0x@BGCOW=>#y*`%Ux_&{UoZ{Gqk?uT1IRnP~E?tp}2qYN9)t=o}M$ zg^9k}MAN#c9!UNI6HWUA^+4&yvjBao>#x1kB=M|UDp6t*=EDwtJ1$OR(}T7}g+ zf|`Q%W)TCn4bGc~X6M~OoEM}-?;}YIXR^xqvu4cI*6QsJaXxMjoP&ySVF#vttb}h7 z_yK`w|0u=NegS(X>_32IC)V-_V}W_Gpr6CyGVzgbB;q5TN$Kwr2LCY1rx#;y2t1AC zf^HFbi;(lQkoz3r-i&P*@dts4-%9xaCsRJa;{?vOX!+z3M!qh>xc_!wdmrpe6!;;5 zHwa9+miW2-N{+UVn9c>pe`W`LIbjg={Md7~eNXm0$QATTfgd1@_!kKGV{8}UEX{{4 z(SIkpCu2Vl?vHvCMAUP(SL*sH2V*G zwkSW!N4i}efCaR+9McFh?!?HM0g?vRo{Y*CXMV|p8p zblZ#)~UcgJ^$zS|6wR_y>11jfUf56P)^_vhCL4$ zaxwNUPr){e-j_6wARYpuCju~J@J7{w4~X8edeG&KLj+z8%qHeIFd)@(&bT{QQo*j?H!G19&~w+gEGo{4f0+_9Lci- zba{JmZIt(1lJ<^7G!MGGJ)n&87DI<+Adcjr2oZS!T$4PgA0FO-JbH)fL6>&|5k|gj z_~0Vlp$DDM0w&_4ex~5Nh)=I%Fd`gr2DE8e~9v z1Gn({rUFyd^t$4b?Sk(4FI@;|{Spu9+y$g1wCz_vYA=Lyh=;fw6n@;NS9Nd7IqK7^ zHx}qZyAIc9jAvOH<2sdfnvm9@N;q@%(#lYZBn=!IZs$61CY}w^u%a~joTWJ5Q5xjv zdpeL7OdkJC_vCR`4z`UN7?wwXQ!dajIF~!!;C!a2DG<_p&A25uX16|G0`UjmoLaYI zPgL3{mX$UxeRLX54$@ALO;M!$f^a(JuGGmSiSqobAyStY zoePNFQ^<$J>v^T_w&S#3XK2f0XGMGdecp+al6|12N2w}j1$ii{iu^3gIliMObAsIDE0oR#2PSx_bUV+orBCnMcGg#w<6Yq{45z{2mC{(G zlvk+OQMIFSMat)gLVHGg!$IqK4) z5-;;+m8@`2u22X59qaD(ko9+xwU6qjhhgyHMXL={_oyC)*Ql>Y#R(|1o(<^Q)hH!&BfNzkB>=p`FpNL&dkB!RcxDYQt&FGaQ(QC^}$LYa=H8@Y|oZ;Y=}>^e3;nO zdimT?N@8M@l!%stNwPqoma5)FCl*mEaEzq ze28^eT6=vHSa5Hd2-|{Tg4@oyrGdZ4s%S#V->}L_+;4$f8cI9wo+x_t5QFbAD%pOl zbZ>!u*#@7nWIcYgWZi#i$#^}|+vG;ccsGu+y+tYNgn-QrJmhkjtx->}!21$(qvXq8R&XA-%M z9@&uWUXi%LzA34W+I-7-hBfA5DWOh2ul%5`GEd-kBMn%Gf3oCY>7frk4m)aLEwGJj zRK_%{`lqt*|4Fh>Lw2u{($bHPyg1&{lx@YR%Jkv%p0i)deEt1HSP8n`NFI_R=)EiAB5{`K2tSa}J@)a1v%Ws1BLuP*ur~FZU{$L&M#P1skMr+9$>W&y zsP!>wcPcmG3(Ni7auuas+Hcp1-+qE!wYA1}pSavwSJqJ6cT9`F^O%rq&l^vc6z_$* z`nXk8ICYC!SKM&_KOJ|Ie_9O&=Xgk`knQRFo+G^VWu#&an*{=pkK6{WHMxOldsXvWTa{dNl4=)F*N<4$nqgee*Ed`uonT z-%H1BPjvPC;w1Wk*M^;0zii4y%U<@QWzYR@DEm;9{Y0mw{a!jjWgmEwIpgRVi7(?P zy}8f4;d{r^`b8X`lNAYLTl`(dhUA3I@vOIXJ@p76{=KkmyNu^#B%gucNxvWcaKE`9 z-uC^Ru_O)oqvzVvRMJteU5}?EktH9sq8C)B9dj2jRt_Dm7Oahbv#>1LOg&nRek!@k?xn0jOXMF7ypCiVp*~+$_vpFFHNQuX}7&$O#+pMQs6?e(@RBfeL$lH9yMxz zJN6Llu9c>V>R-7f87tU;cf87HH%!8_kU*!vN(q~Z4_4^bv?#BwU}Moj@a}K!yQ1Kf zy6JVy1<9_@eP+@M89L%X{F?D49-94@Y_*~Fv&qua*Wse=|Ml{tXn&F=_cRy*>Yq93ywOB7|VKdai_Ooj7B(H@nrWQ8T!pds_D@ao<)-JcA z7EAMXpU`UY&?jo$0rZ83TUol#T(SIhXl;dNH`$|F{28M{vQ4i0nDrgUu*2o)HKa91 zT`>L%$a2|r)bKRK{dg@3GCj0sNE-YH&o$aOjMPFKe^d(#`T{)tV*8?D8A{!lY-Rml zK7NY!g@{1UI{}e=UQ?%_hbG{SM6=DmEW5B?^}Js1ZoRNh?=GVe_OI7TJX@kp=4Z=X zqh+X(f*LLDzvslACuw$neSkkDVlMN+GHraV65}glE1nUItmT*stTbtZTY1S9SR_ZE zjeliSv1r-3mLj`%-W#SdwRoBKWW0Rb{_FWCjZeiQQR-_&si!}wm3m4WrOx6XSo+@ha22hQ>a|Qe@muK7L_cuH$u?{$yKqxi;|MSrZjC(Z>BN zCoGtaC^55<>w}VGD&QKx6u?!0iGT?Jd>3;jaP)^qP;K4PoA z9`g}fG+u5!*9&tJYXSWRANM&uWp4!T{Y=w@uk+v3BU+X{e@etq`qGxqo}c#_UA^f4 z=rjATwYf{^h4Ei#*GpR_z8L(RVxI^c=eW*4;tOF-&F7m80dtU%^tE8E!IFBUu%s?& z!;-qN4NK~d-aereVM#rlTT-|0t=m#-e)ylm_sk*q#--U(7es8S2O=(NOFdwWOISt^ zfMxW6HY}s*y=?VQ@{Ho&2>XnkT4f8;`N)4db4@!fE(>v)uvqQ{Oy6yK2rFu5NV%vb zb<)@#6~r6=XuHM>umUEMuKKw*eKsE5dtS zcINiO9^2P0Ny2J9%aZPtHUYAv&A7R$_3uC2_rpxU-+!pWyW_{wCxYWs$haC3)#8?4>cvgTC^lQDh5+B`ibGY@cWpX+d8p)F`rbisjdAmuUS_3pV|NW3p9><=1SN zo{DhMYF#vttx_z%X1kOfVWHKyXdqjqSpH;g?-S*x75!)++oV|jRL&p7Q>g`7^@;`s z)-F5%1AS%={It$U(3F06BtKf=iw5#dADdqXo?q0Kj4ZdJfqc`)@@H`VsP6|XBpL$ws*i0^ z@{Sn|bdGUsIu;lWfqd7;rblxkHidOFrFS#jP3hfD=~yT>1oCAcD<3NdhM?d_d2G7F zkV^71O>`DtGp6C>1CXto6PV}-gsHq<}LyMTp!H93J#sf>fTgj#`N{(ibAV^*@BYE5WDjZ134FNA=mPdw( z|DwU&PqVK3XZCbRo4Y_SJFFWR90(1}A4Kk#bTov=4Y|0mg(!Xo zESEP@^Ic76!fv5>&?~`5^ajG9uf#J%^fiP*tBOYRGkQVO`&S-|^8*QwgB(FWCFme9 z-QPkO{7>QGB%1t96J24^=*5IV=iz-x>2AWH>HW>+6NdbABAw2yrF7DD6ptq0_e~&- z%8~9QImZQkP}Gb3MN|4CqF!VhPV_aRoHr5%P49Wi7p`r&{I3KKiFm3HwP#;d%ZHye zO!o=8fiUPzqJCQlgPtnNe=}jwG+&^6z9Nizga|`!PpTiv-Xx>2SKEg;(rFxZt zk1z&Bo}jN3G>uofe;VOJoXITc)dK&XFqmH^j2GX#gvX2a!hNKu=}crBVY~>rJ?dnP zW1@j+UP9xqfH2B4j4(REO&G>;C4{F4yU45Y{1AN&>3!T!o%Ao+Lz>jH!BWzc!u4C! zbn!wJ9!-nU;fCl~rK}q|{}=<*`9)a_TH9pDc@fnfm1I=a+>M&<2G~`7LIt`js;~@1LkrH)g7fmFwWE%~mYSte<8~ax-Uc7Mj zwA&WXScEa~69dgeF?_haBi(}G6%+6X^N@+Z#HVQF1$>l7p$`C$(U0+2^?h-Qr*j|M{~?JOq@Vc&sAz*oC+XfF5*t9z+=Bwa?U^_nwG{dWgvL;@Vig z_8_RtdeG%fM}$#cPlG%v7x58Z2{6i|XLvi}^q|Z0A;KuH0P->r*Arj|=<@1tZInmv zqPEKSEAScRjf1>w#F4y2fG+PfTpQ)}M8~%!k93+*-eQA1dVlNkJ~qjl0(mqy*8}lU zI0i7vdmQpcA&$zI3ee?|t~8c!DiUZeuLoV83MQkxPa*Fxm`EP!M_nH6T`|fFqA{Ys zS2aG8-OD8JwJf|x5Kr>j19W*sCVA5@(S93%g$LrJK)(Zw<$KPly(dUs2Y@cG$|Ua~ z5~<(xpvx;a$(s&A)GQ>A#*HrTP9aZ!enXJwM!Ft!dH0$4T(H4)>rB8pUoH43A6*`u z`>UrByMFy^lRV;^N(>_CeEUs&2QQK@{jS0PNWL~yQkOv@_3INRzC`f3i9rONuR-wX zV|^U>7U)dClo!Dn{6Sr-^`uw9m#-6ohsbsSy-kRo+Y_`*S6U!h6bKulSqSn%n8)-& zJk2wxd^G-PtQh45@zXxtaHB5oj(O#G(0TBc)w5&I8y?d3*${&U4jcAUZIj(aD1^Z_ z`7X@lQ+F>XUxr!oGqCaPXXooQQk}C%-Pg5PT8GcJ&uDS-_SC%+zO(CRY#Og+d@(-| z4l_M16=xc7d(`@pN;;D?N~_a|*7%vE^M`k*LjwP|;LsiyFN4LizdmU~zE{fH7XHp* z>2o0bR5k0lE&PMqm6ge*zgUsuadG=whbO#u0VbVceky91+u<4|R`1qis-BnYlg8)j zGJ8X21tdCLIGdNs``e04Pqt>!i*uAoV)e+fP^+t2*aO>kTTxE(^C;|oT^ai%hr0+~ zNa(W!b0T#^QITJ-uLJa_^~Ei|V)#v|NcYNT^QNW4GTL2C9s)D4uFz>rC;nH>@65ly zXYh8=q^It6;?5Mqolbry%ZkT6$zyObYDK~r{Z1;sE#_{tE(LtYzi!;PaVoy)m&9wY zg6nR*wF>=Lqqo0a-W%(aaoTE8h!u6N=;BSpDn)UKp{(|x4e?nb-b$+%JYF5!m6w|O zG>Ck-pB7{pBYW41?L|wJC0*p=2D$k4n)Kq8HAOXPa#4d?BG+UU%b^kM;V@&bpIMdq zFn)l&e#YW@I4rZZi4TVroLW0Vei)~O95}No?cuNm*fQwhu+>qA)HqjCQ)_-wQ=H1O z(!)|pk$YRww}DQh6nF|^inC=)u~NaDH7=z>1@!2fa+t=1l%oWsZ{_CB#cEK>q$_8G zEGMBYQ-hAnKG>0PQ639;$f8#}KZM$K6kr^{wQKqI5>ZFB_>CH8@v54Vnw~OFh%I3? zj$#%Xp`woN{byFSM;(DJ1*oIjM|EVVBU(UZs3Tgy0!%GnWifTMFbC>bR^fgYbsR+2 z_PmavVvH`QvtkuyX^vHG`&PM-9IY|M@seH>qaxr=P$)dF^^llbv zoKZY5sHN>Mmfc;E+L=<_=@p$R)twb|4BNj0BQ{1#d+@avQmAb+irX2aP@5(RDT!PP zEc>8AB#fqv3VFLr+p{3ru?E-&xEnxix)KmSrkyu_EP30*Q zH-8FnM*6z#%=vJ*%;o5N_2Kq7U&ZRl+Vi?MeHx%z4esa9_!)24X=&@SJUJdax17e3 z)H8j!?up-&R=26h60ko5w=8(BRGeBYW!ax$Je96AJ}K+1ur0IwGg4UUm&?;w&mKXN zqj73-)QB8MjFhCNGD*8@GfE0c1IZq!wZP$Nzr7lDSSxk}u;$ArQYlu2Z!GNzO_kIr zGoq!e_-FEXuI=g`M`^R*5j38cH&}1v8&aQF-wyTEQ=vb^eVLHv)iib1Y)?O}297l3 zjx*$;B@cvy4u-#G<0wfix&cY6=?N-DMPmwPrt!>kG)FIch1jdoIa%hITtfTwdjV4N$yqs*v}1VpBQ@^+thw1&R-+DJ%}u^UkHwRKbJE?R<-XhNm4T;fQgT`I zmp*5=fhB4XPkC-iU)Frex3MT3=-fZs@9sawZ}0C8o$x(|(nz@Pn2!x$_?wCMc2$iu z+|sC|rgn<5iaCyRVUY9;``NQrv*a4NeAsGwdss2kt0p349oa$AD}WddNqu z%N%cleltAG65dNSTH&+wm;FioJNt(P*YH~29bg0Q@w3LbR7;jJkTt_^1@4Qjk0nd5 z5#1x_UV?jO@_UjldQVd19-Kpen0i0FSG#-Mk>57ilw&)F=8ic`rPchGZK;u5lrxnV zmW=+bH7C0gy{!g3y_my41$DG+t3h1`>+=sy>d*a4J@eD=Av?{w?JMAIZmp+S8+X}D z{1#8Kn9FQ{HCAr*hOp$CgTJ!t+3K&l+M1MjTsuaK6Ww!bcJi_9;5y*hn!D<4O|tdH zuB>MVyia*QWnXnH4%bq_vo+g_&K%G2XuFG4kLGcj<&_4m^2Cd$U~_n;gYw-})Gn|& zJOkGft`l%Q9oMp}7Fuew&{2wK;!dqBn$Dv89uCiR(LE1`X8_B<(_K;NZE7oL9O=w_ zmH*|>U4DkAi?C<4?WVb!Huqx=+umXIT8y?wjbf<1t4IE+-nDzh_C(KquQe-=_qP1J zb&K zZ<{MvR$r|odS+7EN5>t@N|w(-7iC#47>FV8YdNob`v2Rq3n$4R34oyvZ`p!U9OMhh2(jN#d-3MBF6xY)4 zLQA)}NTsYw)eO?lX9L;uS@u#tf94(8Grcpbp_i8gG_9QBF9>S2S`=W}i~Vsf(#e)a zoKqFi$y&a;M%K%p4*qiRlRj>XU)RRfdv$HB-#Id(jY|(_&yUr`*)v)86$VY*cRuOi zFK2A4nS*lkk@7G8#Yp=ZcOLN8hX0Dbp5iBMT)Ta(C&jZ&Xk&|qWsmji+IVn{8) zb-jweCD5SKGur594!h7>)J9w{xU^>=ooh_gqA=03hHEir&E!|LU9W1^lNp+}SF_Zn zRLRnm$f)1@cO%*+Y1VX>CR-fQiPA8B|7DnOYIhZRGR3Z0&F?4Wt@;0?{Fd`s?i%bV z)8@>(cRKnHJ?tJZ>~N29Ql^7HTYerIdY(0FU_?U`pQfQH4=SU> zD>?Uvx45XMhB@r@*H=kFmW99k_dauQ4H?rCGPT4q?e}~b`+iff-%u-=Wv9D;=Haw) zdRd~SyHu3PkZUG#O+DbfuIhl7_3S#0mjz!bh(rIY<$*RR*7yEy@N5oeS8KVkyg7c> zZ?1n)_hUPH|yBfXME!ZI9bJ(*Bob`2cj}@;B_jTn2yE_T@ z3Z{?G!Kjj&|5m7tmmKe-;Ts2&Y~Wr}^W~E?`ML0zzS5%Sq2lgr?C!6{e12s($E8W* zvn!G6P4g}fS0>WeLYB26JS`9Xf}kLc{?#H)?|b^T8OP(hda)~_mep#fv}h%w zFPd84>fgWEO{sJq%gE*1mt!yfqtHp%x#ClrGAz&U&~(}AovNp=NBD8}IKt|)q<^UN z)~xyrwaLz(FKd<+dJwvjQfYF%dP4!}?>tJj9LOas^7V$R7_GlWJ z>!Dw0<y+k&rvzj@ps;Nv4-h2P7RE3dJ(QemvIHP5B z{q?WfgIeokdrJ8eTEzd3;D0X_BDI8g)vRYQifnwWP%Pf3hHGs+Y4PoOywtmQHOtgW$ujW{ z=CQio#hX#+1Hr-lZc{3~;}(QereTR=Jzi@wdZu(;YsUX$x2%%y6GKq>nk22^&^osq zS-sL&$Zk27-^$nZqRXDNVjT@+w;aol$sm%W{Lw4Ai|m$T`R$ya)`hh|E6&kCcFVE+ zQBL9_JLTAPS`m&0vP+IlPc`L7D_+q+cFD2)X`FvzgoRdgqk-&-WBJ>g_-Vy98ptj< zmcIk%?;2sD71?MYyWm*<49>qM!jciWq6@MMj^*#n`J=sHwiE7Z;>RZy$SH|Pjh0N4 z-`&LD-6S82g@!`JJZpUZ!+f9gPMB)+Vv{_lYLrN?|#s^c-HE{t>R6$Q4~E@PrpDudgXR z&y-G2Z8Ru2v?TWaXimhXU>Pko-4(r=rw=lv4>F|}n9>VO=|fEELrm$zOzFc+>6e+( zFXQbMead0%Xb1`$ZsH$qNTvE-VM@Qkls>|gKEjk2>5-0HHK6w-&9ljRGwc&gllT#iY^p(y@~&N&VMk%b$#SY_E_5w~9?CKVtd-53;DvPb)lQWkSxuXLsHFQ&^#P}prI zevKX);n3bsQ7x>!pI|yBf~Jq7vk*-mchZxwONQDnI*Ty($L$eLx=`31%wve5aXZCC z(}<()BArh@sh7pjHg1zo>2xpc+j$#U4;1$vfYNE^M?RVxf%QP?-vOfO$t(jzR9-5( zRM1o|+NU#;5=EfY=(=A$ic4lJ)J(1_vmHQZurgvxeiu6x;x`WjUdMcRGFdwTIw2P-_ zu|tBkaoWimKnkJ1EvN53b zKxMqyM9($ROU3vEa?0v}>a31~hAXucK_ZNUC{Y8CU%%+0iXdd)Xn$mvQ+U+3!s9{U$zS;?JXumWK5I5_r;I zihZE^*mR8+R-)i7+COqf(QdhoX zqPf2I=BnDoRn7G`5LmOx8>fH6Yao%9)|%GkE&P;u+W2QcA(dM=7WVDcH!s6k^x}1G zw7aBnaZT+_+!yLmOt@#Xc!Lky;i84P)-AO-(!ROAWqBixw>P|zq8t`r1MCxQS?=>S zHUOa&2ZArFtE#`5Hh+YJ`f6HQ8g8zyqIX1GNk{S4mPRSuSOuLU?$|IA#-L;O8+pEn z%p#A^x;2r5G*)?Usk)`+rh4SLfNjL?$EApK!?K3(`ytcPSYIzCXyr1gV}7wn5Yk^1 zjEG9yhsVI|v!tOB<)CV5Z4ItfSXxsX9ks>Exk|`Ie32)uO}GSMV2N7XMnL5R1lJ45 z$VNLFiaN5Qj`FeAw}O+UK4$VAYjmhzR@=P7hcdC`hFI=VZa3Ah5OS2RhRYWW_#{$_ z)!<{9x4x~awW+GE;Rd9>K>`J|VcC+VDB!wfEz4?_)*HoeXg=a$2nS>%YtTD}v(ZUv zZFV9pjc!sep1!DevY?_GHein_3S*tO)>nnB5=BQyP@%1TAQr4Jag2 z-`u>ciKB?t1?jS+v8Kh#3>AylDU{a6ChR{3%#wy1L|#)Nm9SNdS5%3F;ibUjLn&tV zZfRQ5TI0hd8Mv6BX~o&^t7~Xc*g`b&PAcU!%h3$gA=))O(-@$log?D%O}uWPeXA$g zh9xz%^=FCHHa5_O94f-*22=u3y3u==EypDt;YLbDr;+^VS`>N>iA5q3k#0a*Rn=lM zAhZcXAWAq)U1SsGf+3X+7-wZn#IFi@e@j!dXo$n@fx2ddE_t5%S`@+F>PYb zSLEF>zm)oea{ZAWB=7tc!^j;J=~5>teTg7`HBrdX_?hIkN%zO4dlFILjU7#^@~x6jBpQ%Mo&cj2V@3}sVbkYRQ?UFu26z~jaJt0F_>Y(IULxr> z>Ar&~(&0zao#v6mb3G6b{(k5?m)ybg{71U*_y1nzze*R#Juda*@qUZuR0#J6na=R1 zUtlQyJpVzbOa3aNe-vj&P&y?O|0h`=U&j`B;_)$xhr=e*S;RvhUTz@&RpS3F^*Vtw z7Qjl5&>LPzoo*wT-_$-pg zdy0rc51uaX%km4#)8%iG`0)3ADE6e1yvm2k6dv*N5dBEj^N(e|!kyBQ{C^<*Ujik4 zGKfNN(u*ih-!me=i0AE&rU`dLdE)5}JU7A-?iG?Ql(dzoRTJgwux!_WNAII@nMd@; zjE$G_TyNmamHhRR`iLU@HllWo{X|lpKbTPmC4Z7Ac=}Ir^evtA9}Rb+yc}b*GB3xd zOuQZYK&Ja3*-pl5q8;lI=O8(ME$hi|Wcz_|Yw0~sjeUnG;=$7sdha0~lj>HO``Ah!Do7i10DLabi6HOw_b|4BfRhhN5!T6g=V; zp3$E*%{IvlI316nO7t8&B4C)c@>%SFJ~&B(D{ly3nAgG=!_|Q#2Jj8f(PM^Irl**e#`NMJ-(vdEoPMDA(U*q5n*gyr)og#D_wlb2v4g5F0Fj{K`cW&V z^#glFrsjWN#xO~bAt+}*YPudnf@+a@Rvtr40`YpwCs+f^#GZ~1EN`5c4=jaCKEqRV zQx!8#%VPkXJwcBl6q$}2@-m!V6m8>#JO+SaY8}Iu0OTY)hA*Gp8mHSaWC+1Hzm6di zog z?gl6Mv77hJVVr-fvxaEArVNce$&gz-9ycB`r6pp#x&n5Rp@+tIv+!VK#K+_yd=8LE z7HJe7%~gze<-nuxgh%6Rg|{4bBVHr;Sa@H9yAf}e0grGM-lHbGHt;m=Rz~Uf5FCtn z^9^`T_$j;h$AmWvjcA_&lXAp5k*>!4os4-m>8Ff(Zye-|>2etFbwP;U zOW!-xdkai>8wd}&D5LQ5On9e(NBKs0qwy#_wT6cJ05b1FiM$x)z+=M88^`#$$b>fr zkHV`r>6e@kyM3-P;jIN8UX(r^fu)3OrSRk(6P)117w-0pk0V32&c4zwrjV-OMd_!=LHb)SD6y8)5-elmZ`b#Ui6<)0g&z)>IP)*7a za+L{hHSpYUr}$3AqwxGDybj=PgTFE)N9HyY-tz|iroms~Z871^O%dPaIEa)Z`*%%v ziCEA{`A_;);8A$pCcJ&XbH%9V2TXY7z^j&cmmBcjG2tD?AOlej8+tF9w@rAD0gvQJ zzZrN)j_SP=?QvB310K06qwwM&XDomFfT!AxN(d{wViVrGz@z%BjKaIbg!haAZ#IM# z-qj{NTUzY+UTMO6-hem9fOnS(FCBP!N;xS<>`D{fD+atPr5we7w+XLo0KES&;T;B^ z%8z*lyjM+l*8@+b8N|Z&cTIBDkef>a(x@=+nBM!g;k{phtV-t;bVSB-&;h(U1t#T) z9Sk{RIXDeG7u=~_QM;w^E;Zq$r;FdcAi|B(uh4{-j!H!FAUyiMukfxi;RS(5<2hv% z-W4XiI^d~tO5gbvp3j8$3GnbgY!qIj39r$B_f-Sl117xWq2hN-1*q`8X~Js-o+<~` z2E08cyfWa$8b3d2!dq?7Z?OUIHzvGt;B8RKNjYKzCcL%4n+|u%k2*Xm{Z5(ijsTC= zJ1V2%`xg`5Vc_{7M0iW^C_Gw&X)J&53={p487~2H#`0G_F_OPt1Kwy8UdDN7pJV7p zYdwv4vw-(Ba7e!!4S2Incylvix05qWc(%(T`ZXHxXw9fGz6%DxTVldXG~g{W;H@^{ zRS$sYH{qol@F-tYemo30sy7(MaN`A@%2RSs_Gdq%+>y^H*ZUdeoKTL^uZU}hF){RO zl6E5GM^75$5C@7HH@|~iiDE!Wh77vpa9oMcLb~Kq0!X9eh#H3dEO9u-B?$a=xIc-9 z(j^%Wl_`~Wu(Da0OuQ2V@6zJ2r7nXh9yh)Wrf9+dXSSA&EgmbxtDSdL%+fVYX-07= z2*-!sxf;5`*h@Jnzl*e41w2PeOULMLT z`br4LlSy6=U09@*zP*ReAK31B=LpLiS()XdXE+`@$s=nzg(TYuZp(yw<}BO$&)qUL zpIyjLVBtI9r&`(Ue{bK2jWt*7#k!k*0S$(85(Bj>5`DEuRT zF8*$c}vx`&ipmyDC0Zx;J1dlhKSswCzz}6Ae|;a=R(BcG?>ITxtAViJGJ|9rp9Q7)9X5Q)^AN>xtY3k zblK5ip5ve*ju+YMV0gW!Nl5cw5TU zldEkc^W1x@taM@@B#JD+9}%>S9xm`~_Ffy|as3OfCbVlMhF0|4ds^DGlYVRA)fz&v zAj`SThckJ3IT7b&@DqRTktYk>gEX8&XWiV3GlnkE&*VH*ndy57<=E4<*^?F6I`hi5 zJKG-goEO+M^TD=fVM`5scV?i?vf@F{;J}8NDJwj%IRfivUb*5<*sKAT|3KwPUtaKj zgiM(BAf5o8lobinu3C{cEj6$P*i=>q2jX%XvR3mFxoT!w+q6E-n&xBqw5p5~o-RVcSea}nwR ze|*2jPir8b#WOv?qw~VP?=QGroH$7Ci^A?({5#LrzSF`=%CDVi>78a}?U^{yD={er zr!0=>TpwbGYRl7n+SspEydM~KdqV@&W*}q~-aZR&EpXw!VW%Zz-8OS(%A&&C6Ux)B zo}m|TsW(H3oip`|8?Fl#aD70FtfBaAlV_$a(n{XglXmsPo<)Hnd$oX7Y%gtoNKxx5T4klb|69rXM)@MfsPKj);L z@YQnq%Pt(FzZvqcdo%RdHcGj|sgzsi^{_1q7kzinC0)+mIPC(SYY9QJi!(Uvy$M=I z~ET;-(pM0MRPLsa&z`BrST|Aex_pqF6q{I(`2$c}uHzoe%3Oi7* zl2i0FtzUyPN8{-1*!~ebk6CV(K2pE@ zBWr&OQart9=Wyo=OWz^QeKBE`$&d{}oX%{q?;KwKi?8LBZ`h-a{{U@IT;U%ACFd8G z$G1DbeLrrU-hTeba=l_^U`M~jIW*)f(%Scow5(5&r+-=3SvHBbz#ek49pxDzZSB`a z%GLoVA5SULHo+&^x*6}XPC+YtLBL;eexMLFcZxNxXV}ctHSHctk2VP>4f_*u=2zA? z5R*-V`-h6ss?Wq3GOD$4R%ZB;&<^3eYM#0d*l}(%x2rQ75sXy&i4cgV2lf$uSvU$eao=bR;q8oRe}4SH6TsjR?b{TWuRFyrW` z+wJ|Ctl{>@`ZKNfc?SJ@NH95MDNIIhWSMlI$MM>zuHsI6ztcimYC)~=uSbyLUVG(c zk1Zhbg?!57SxZ!xsHWW0fzMYPo(QG8sny#?J?T1YyJfxhpvy(_bTSU>`>vnt0%etM z+de)H_p-3k2{#Lqd|Fq}>=8gZOG#4d7O_+y;TgtL!$Tx`2=QX9AV;d-UC(`~J} z0d0b7d{@OgI9UsIuncWueA(;~C+&9YZ?rGf_g!Gu#@*kEQ7L75?uBi++{X%^k<#aU?z6dV zB={@#owbKQ@Nxroq_wnnoI__u-*4erQa-FD#*EQ^JAH z{D}^aNwmRO*&vfYCQSxo#q1+Gd^~^O+VI4MW+xG2WeFzv1TG($uHa+1!B}xKW;Fg_ zF8?$|kQ;n-HW({*s7K2W;qvc8~`SY;rRT#=y&7>WjArvj2 z!Ri$rUZ6#NF{OFraFhISoGKuUA4G${(a@Pg(fFC7@~}BY7&>8vKoh?lO(meNc#;+t(0M|^Wk@;Q7hHwpRi zXz({0Is+*hKbK)SH8%nu<9ma#;)^w{A0Q+A^GxCMgs1##<-f>AoUB->PwOR=q3;!= zO?(M>a$1dt&VX=Bp4L00qWzggp3+cwTDPDK@wHO^B_wDn+M|0-{_9NqMk!CD?|FYz?1)0E}zDxn*67m`0yTPxwMR5 zs>Am(hZnZh;=au0s$$ybB{t;Ip62DX)0Q{S#*S0i=F(2DdG%kL-_%;uI3IgI;Z}|X z&sWvAG&SBl=O(&7l*?RIa|^k%8(Lb!RszBf$ECt!c`Yz!*R81cx`iSSwzgY z81PkmiLX#I3@vcfSRI}Ka!(w|QCMt37Q0hX2B~#)&K2tc zh|=#cbQff`q-~OJmh=ab?gO=Ci|^Bx3h{fg1>Y49k^6M9PQ!wan=cVRUi{u-nJ9j* zwRo_PiQF#{`%ElF;x0-Hn#Lfxqse%i=wy7SBzlq9KVX?6es^%r7j1+S9otycA=29q zIpTj!6#Pk|7>e+HRgfP=a-fYwA-A6BRoDk3`FDw4!`MfXjz=AIF2KGLqL80UbRPE6 zfRdi;h$23;wubcINEGqjN;Dh4`w~U`A0vwN*+CTP^EgqYM+Z?4#$7~_4$lxpyblpY zyq_nEcsmdeiuWFh5Ar38^t%rGn}o@v81^|IU`-MthJ@o|wq zMt{T@L$ZA1~z4x(`Xv2=e?x|6=-&iAK*^7H`Z=>S>>{pdY6 z5=A+nbRj)f6Gc2JeJEaQi6TDK_7mM8DYgAPp8wE3lW}5xwBfP0cCg_6~lL+MoC;88tJ6Oa^Dy|mGYoIAlzLEM^m3A z93vf<3Tf!Ql#`(gQ%=02K^G}5M^zo0>db^xq(@Uc52d;_6(`lXMM0xGr5;YWMR#*i za^b#?yPnnI{d?W9O6`p@Hff=@f@Xzo$v>SY+Jge{Btwq_52`mGM^RfuCJ%LYwXpKP z3hxGR@ZxP2dF%jFP7cy2xmxhX_j=RCdJ%drnw0Q+zX7`u?_IRjG`3Vm;jMs!5zi02 zBXA=;awj>8#}jyrc>C|cFD*Lt=ay+3VzLMJ?L(VT{h$y)iV#v{)TNx#HB!-+pEUZHBr^qS1Ph!ZaI!fctPGO_$wC>r6w{d`cTNp#mA4ZGBV_6J2WBRH1 zuKpA`6=pN!$X+DhMf$~BPp}G-A!pbqJgO@yO~|tc<(}3|E2HFy8l2f= z;JJ_?7sH+EF~uVp57i+f-t~CFwT6HSud$)E-q+Nytd-vY>vDzH6HJJG2W)ZC*s-7W z4%oHgvrBkA!DKcO=e!TW`S;Vh#JyOFxScUw-XcaP?JIY>yHUB*O$=$cg8{jcAV+wG{_{xK`^$7?R!vQdrS&T?!BseVJFfUnJ7Oi4s{ zhkBJe{We42vHkoj{+;}9Pl#QAVV9fR>35Am`jg&PZyEFzU%NAMd+VxZBzU)YZ>pbj z+BYq?`efrxOVi42CAHP>G~T$>Qtmm)JdakhoIg}+IXN{>Z{W6+zB5D8yXW-zuVmRZ zHCNvH^F0X{mmN*v?pfWqVRK$>^=r4LK`N_zMrCeIJGWbVavP?dX1TdF^KQkjq=nX_ zL`O)PRd`;Y{kAvu#1~qRaJdqMmO@u{jPW{WXV|_a=BLH)`8Wb96Y&2aXDwQZT)%tcd)peD{))ED1*I-+i~-( z_m6icm*n;hv5v5fNKC8lZLBQcTRpsTxHr3#Qe-;6d4N(0H?)?#hdWbCYUbd6*OF>V z`{XW>1G~L&!@aP=O{D+sWj_nC$-=JX#34udFHPM%AH%H``tFgq8+QMdo$&>0yK$3Z z_O^Z}?%A^zZtTX45BG}ET`n72>2F4>Rz`1#lqtG#x3V+y)?e?r)05Gi(XtS>Z^CwM z3rY!$V}B}sjm_wEpYGtdQ4K-rU549G+gp=xA1Xa`59(l`jKEzD3tP|RENr2&IjIZ3 z6?*0Rn4Cdpm1DY(l<<{t?2m_hS*NiEoXL5;#Rk`muxmtbx{oBGlb7Z2Jrtq+>7>VA zmj6)oSF1->C(oJWeSS|?>#KVfwTk;&5?mB})FFQY|J@tEPL()i9MrE_lW=p-=2m;< z$ZFhl%+s3dc>6G>bZ+ZR-dEGPu#?hmS|{pZHEHSgI&iD+7H?WLzrhxPzRu+qNVzSy zBCU{mskd4ncipWCVL7@}$J%*>Qk=ifu|F9uFLw^X{j3RItt0_=8!x;q74;*bXQ4N< zGZQ!P;3m7*DK45yTlyWA+KL!$2HjCZ_p_wnSnOOqM{kg~Fl+1B7Ol|IV<}ASIduZ5 z;$6_mTO_{j!hsS@clOX*{&0f+zRCX{JE8K7sAE6wu+BE$A%vT7&T=CJ$3eQsHtj(2 z{?#3>>=d2{7#?q68{DUk&CAh~W+ZVp>xTGTOZ%MDtVL^2sATQAx^rf>{Do1=u^^^4 z+%BWi%czgCQ``3NtUNERADXzby84l7%@u5QpVC~bJgo0QKkmRfr6sTOwDjzKf8`x1 zzqS^#zRiB?5v{PfXX(+_puN!BbEq$2Wn7`7+nUe%94q^R?o)PzXheurcf|kNeaZ^1 z7Tle8jP7>i$K?khF$iu|Tw>wTz3;O-9Nmcseb^s>Up(G@T~7wyY=`Zto_N@-uvJ1{ z14eRwQvOc}18x;?v%oDqKQ(_J+|uAS4Q}>367c2}aAiFckL-QlhBpku8@BtmL24-6 zOS#mgN8P8!f^&3d!FPlI!Js{VUC@%hJ;-wZ*sA3}%&8@R3#Zon?*<3u`-AcMw+Eg1 zw*?dOzaAW%-xeH_e{(P;zcrYa-y9s8?+c!nzbu%M-xwU8e`7E+-y0l}za+@==k#g0 z71OQNKj=@iCgax6smSB+gx``&x*y?84_N$}@RJ87M^=7g4i<^;uh)d_0lSi#pJ)QvyIOsDJg`p@iC-Zv4;MYoapyvntMh}9G5Q&I~ZpOB=0cE zJGeZJ?}drjh^ISdqVb(v{%JL6@~K?DAS{&{wvmPIpox}G=kgI$e3UR4`n@??ei)aZAC|%7 z!C>h3<7oK|E}s{c$q3uXLca$`%V(P8GfnbYCiyHbA4&B{$_o6ECi#(EzC4U-{=L>^ zl6P_WxnX%%*hUumJvCbY94;S8&m5C{o=HB>Bp*pBfuC=Z&o|)}nEVS&`bJ8Ez#Ck=h88)RysY$*RDI^Wj zSTp?^Ze`<4^5c{=r&gQ{7X5zwSYa^M@M;m^XvI#yiLg@hMQuhZQgOEM;(rMX-CNiY zUx$a{LQe`ib*96z5oA_(MlT#TUy5oU<<5Pz4?veg3*i&(WA-ZZV@8x_ld)6fX zD--{F@W%A|FB!gwhsO#5p3h>>Rw~-tF_IVf!}uKw5aovSxd#5q5ML|hspit15B?}V zlmDhjDv@j|ez8^i;^%UHB%}T6=yJoN;rXx~aq{uNRF zd8|(I4lbY1Xx}x37x68?0!T2@5dLE({%I5cvJ6kZmC_v!Crthyn)rCi3~318W#Y?B z{2UX%$iy#`d@Hb0(0;T@o=6Jn>wQuDc(yr;AHyDt;zzUHQT$j&`)SEgHl$GN5cx31 zS8-siMq?|E+HS>~3Sq-igJoliizbX4$7ao+JAXbr=Cz80g~MU#6s6`?zW%|qpn(=Z z+)%|Ko7=>S1HrWlj(%9AV~%hPQ3MU=`!qDwvFQu>Is^I%F_t$4g*!ilC>)lDFrUdz zN0%86OP1>n!r*5g+)K~^!O3RzH7z*Ryt$zjC(Ado=32gJfzA;|3N3D|H?G-00In)6 z4GBjJXIh7kP$oy5*K0VD4F@ZWB{DKD;b_Pc{>9Az9IdqtN9ITTTM3w-^xcf(yL}B+ zw?Id(j`7fQF3(dhd_J=YNBlUF=j)WP)(DB$v=jwX zUP%z`ym$q0V~C4{l&5vy(ACEsjgL{@O>J(b*S7&5i>Gd`X{@3f zBOoo3Sh<32ZR1(QnwQmKsg!C)C|3NmzS>)gi)B!(uRwd|#NdKv1hf_;NAfh{Bi;>a zsS@`^TB^m~UCR~XcMZ!|a25plFBWHzS{8`kWXPSGODE>s9Dj-E_nbG#wMyt@ipU@M zr-))6UVzSrcse`Oc_ZE{saH}z(VN72Q^KXS9>mjcgihem@4-ZA4rr;y_W+`E#hJ?% zw>)chsni?(TyMmSW*#I@;~=L`#Mk*XF%GkM#BU}P|M?_`cvJsO^7zjz7SN=EPJ_;^nq@o-m^^d+9& zAbj~9M#gx#Nn~@s_;;mix|ht0wYGZCp=7@-~V}G z)d*C5G%c7h3q$>c(V#iuCL4cAPEBjfa^y$mB0LT}pC(rX8S{Io8B)e3Eu%r>@wg@b z7`{`N!G0MYlA(v*hpr$B&kx>>M;V1T6AnhaW5COR8{ttJD7;qKjrtt|pNK~pg@>*z z3hy-VNFTx@s_-_$Zp3>9JbimmM&W%24o3Yx0p2>e5gzqL3h&3T8}Vo@FQtnz3hzg7 zFygI6WL11AJ__$B>_)sE;JM(YjKcd39E^C60Zhd=34RKXj#V(?^#RYTz@!|p|Bc6p z_c-v}aHIGp<574tUNYh>KmnsNq>R!p4?;$~cY)`E5aFfbQFwDrcx!-1V+Umv-YgRy zomZjaOY#cu8zwwDhb>loSD5h9fma3`(r=gnZ@UT4h8GpUUm2y}!zR302K^BAuztTV z;f)>ukH#a$^qX(M%QWEinegTVkMc_yrQf?Iyz32kG!`W}O3$Gv9}o?Stzw zQ5b}&>#p+)zBI?@>nSaJ32V&;aqh@iWTN zxv9oFWe1j4y)}nNL4=Xrp*Zc%5K8wThQdn|7J2;PNZ3cFk{UYHV>WX=-*& zy=u0rV(H@gy1M#07Z&x`*0;1YHMd;s8aJkNOtEWhQStbqiNzCL1@Z&s#jfiR zVfoTP>+NU)&<93HjW|y!y z=b+i~mQ-81HdH&$nxPHXGPRLfj!GB^D`RxBvHmm4ov1aOlET#*l{@5b%mv~NnZU!N z0g;(d`@fsL{y%#LJB@`YO;z0~g*{f?prSv)p@Jd)uU_o({Q zGUiQCh30s59ttv};SKXV^MYXxA=^+77pL1Xqos6StH$Mf%6 zZ{~as^@hGJC}XHMRZDU2Y*U?>{e{ zw+&GR0@PT4ZfU6v_dTClf6jfk{^x(UrusvfaIWs~5=v#0o`3Ya!Mx9*?j&%{#2-W5 zX~IRon6y^4&`&>~T6gFY3U1DQH=vaKXU%yGneYYIAog$j?fTl!p$^e+v&yJCgwHFlgmx#ZgT5rzvJ)cXxsj6vhZLV6p zyrHq~i>^ZdwtdeR_09Uy;)_j`RSS$fGm+?&K!dz{Cer|+2guY&yWjdTZrgD8-E}7$?e4qtPF9d~+__?L*H?90a9BvYgI((GyX8)8 z>^izp%ysac(6?BDyYGrsWYZ9$Yvl_e*0Jd=vOfj8qoe(;WZ3t^p4f3ex9^2Lx#J#g z?}R<0V!k{t4K#I+k<$ZrEKNO&oJ4?0Fpx-2FdbFX*V{_V2?!x?|B> z4ustfdr`+VT>cT*OFORQG7rOE)=|mr--CT}M+LWUf!*CP<*o4uy9xI4j)`yCVE-=c z(>qEy&bMKo)o}^OZ-;$u$3<_sAoC#X^ES!`27eY4-}) zLa=?}ZiS6?z7VoaZQ^=e2)(v-H1M>^UHO~P2H-9}B2XJU32{U5!k~XNJC(6Ay>Ado zat1;q10j9t6-c>RN1ZDwSB$XW+_Y)m#EoN>6~%!;#leyOJ&y8%V1k~8dvdO5+cbQQ zXToc?Q5PI}^Bqf{)iF42IMc?sQBQ8F9tw>d#VE}+$w6{^1^7FPoZS8*?2i}Oxcw09 zj}_gQwbAq1k(A)R_x(-m)Q_O?J3J*iR|=gsA|(#-lz68&q1UEerLV1+@(_41${)iUjq6u zo{5rIVOIe2E3lMyq5H3BZxE_1b6~+coxhWB5TJGNjMnGyxej*9w$x*;lh?{{=m$A-m#U>L{3a4Q zxC4g5IPr(#V8KKCJBU6mX@{hAPB^(gE$K6o(s|$H{=B3wN&1SUJ(3=l^oXR#Bn?XX zwxo1#JK@ni??n3~JuPWS(oZC%BqBMRqz*|FB~6wzUD6Cmvm|v%nkQ+2q@yJ*lC)IP zGD#;(>Xx)z(&>`Ul60=5^Cev%>GhIUOIjzXS5mrvo#I9JwG(ZXv`y06B=t+WTGBO= zu9bA1q#GpNDCt&7w@Lb#q&p;iT+$9n_euJ+q|ZotNYdveeM!<+B<+#(u%t&MJtk>T z(zhjjN78pC?UVGhq#;Q^k(3s)^Zb|8A!(wd$&#i^njvYHq%KMGB&B=N36JhkCt4(F zsib9+PL|XyX}P3S*D0J=(nd*rlD10PCh2XG`Xya0=^9DbO1e(c4U%q@bgQJ>Bz;WM z9g;pSX@{iyBz;=aXCyr&>GP7lBARBlNqSn+kffhT zN}CdRK1%A4G*QxINz*0GkTgqDm!x@;7Dzf;(jrMqB`uS5vZQWF%O#yIDedbbz2-_f zU(yAVUN332q;-;dC2f?{CuyssZIa$5sbA97lCF_-t)%ND-5}{kNw-S6P146C-684Y zl6FYCPtvC)eMZtll0Gl#OOn1KX^*6bB|RePF-e1xzAfoHlD;cxpQNWH4N3Zmq_lyH z=f9-?*tr(bz1;tKlRf&zf7SQzBD|aWc{Fp}(4Hac^OaG%s%UQnIlHBY3qOET8&7T| zLl2UepSwbMgiB^J1Y+$$nhie;M|@lee|AIev+O~78b2E07?ZG}cab5AsY2KUftL(N z+FwNL97sP5Ke)c(VG#H}2wVcHjKaGVo8$gB-vY-@C@K^3pZ+bd6>Skr_*-B)b}6n} zYwgRi&g*<`+4lZ;%lJ|DBif{AGuDSLc3HZfTSB+ov+lP#efyL2J3Hr(Oc)-&Xlj=& zSU)l?q+OEJH5zx_hXS@>y6zgCc!Yr#<5uY$eiHxa#Rz?n=19PRjdjo1TeSaM9fS3r zP|?)Vz1zJB^u^I?Yp?ZM+S|cdz>U`J?en~eldSD=PiWt^>}Kt@-Ig3zcCd)fJa*)S z?{zN9=+<0rkBx5Y#T)4FkdC5*f9NdQe_uzs{!Op5OY2XxR08Wa8QB-ykDKEyPq1%) zHlzd~vF@IbzlarnxyQGEux^9a@49+I?H$XMLheleq%{bkAF z(3<3~&bS?WZrge%yH=!X1XThr`;gIk3H{J**pP+7jjl z*7SnEqv<09X&OU2p*zlrKetyj>MRFp9dL{WZ$olG>Hc7&u`;_!-p)^SrM5!n^60n? zhUQP{243ufj5yH!!O{NIibf3G7aZ+xTx~+_=3tY*aTgf*8&`G_pUf&E@Vp(I1)lV; z!$bFtE;0G9F!6ryBtH`m-8Z@!R2gble}ISlcj2M?MW2%Vqnsbieg&!w$s6X6VqdbD zK~l$hOH=brH8}TeSsk57S66jY{R%Oy6oSIBrKO=x9h%5{W9eD5ywzL3j80i>ZE9v- zx)!pGz}4caP-7Up0LLC;%@qYUhKZ6QU7{Qi0F&F+hGjRj5UQBy z(!OZtPQQOJK?Qa@4j4~az2$2q1%AxHT`a&400 z=rS(%zZ$<_hj#0jfs#0K5Rx%%cnJyZPuPfIdPuE?TYAuU3L1aW&y3U>ksR^M@KC)}@7)G_ z9Uf&A-i>fD;@J>@>M7yTa7E#*huw%rccxKWqKv|O01igHbORo_D?B=X$cRVh?mVu* zq#Ut(@EGy(faii6=|`=j!lUo$Mm)N=>oK?~qx5?f4o19k;1$4)@TfIacz=bR@QUz| z*@i)8ld$r?u+m7c>K0Kgu79gK<2Y zhs^&i+>}vxRHtL@#~SFj?Q?sAKZiZRhD@kEcP0fdqEtTV8HWeu*vB35XFHzHQh&av z{aBQBb@ey%Pu{dZQLV2k!z{6gPyEE+$e#LhYkO5*68)#F?TyNWFS-W(>%P&bv4iS^ zXzUQH5Bf*z&;nFI+8?e zp?}etNKu(^t_~T?D3#Oy7wS-R{S6H*t<5#fhCL?d`XQB*VmzU^&t-oj)uBixeBqVo zUp8_P>x z_}l(sR>dkuvci<(zJ28*wQ`rva?(RuRZ2)}#G&m?T1Z=@jnyV?_PqCo?-}3a$@qTk za?zLQZPnGdD|NX3s5dazR^lrBD8O7UzazKSzs;X~u&GaD$@n&&ynmH(3->++5-plUsev)yo_oa-X zIvX|o_%)$?*Oj4sNDVzQ>@}7f7n~omoj*5ZyW~Mn%8}I9w36MP_Ys#NK|&SpU@ix^ zIQ|=g6*i6A`j8%u{iJE7x0-P&E+`g)V0F6ZcR_tQ~2*3xW6=amEY>W z)7$Z(1*zdYp1hCcX6c@gt&&p1lDx?iI>zr;z4+qeKH##VO&-e|l-9n3cX}Tv0&!FS%Lik=Me76F%F4N}s@jFF}_O0%) z>j}E{_@I|o!qRWc+ukv&KK?$P_aj+EK=gED2Xk~)i`WtOhZQmfPR$|ki z>rb}6nz56KoZr#8t$H6MGxbaM)cbdPl3(A#nClVmOLMicwi5UxKZtU|TxAF8p^_zR z7~}crF4Q~qd-d~liaX{0R*&WQ`Ffmg30|mY=}CG`)>B*y+v{aKA6UT^fu}xX{@Scb z{@3yI>(P)S_`8r}9lIp`_$xZgcIqe#yT2N;O`2DpR-QQ|x6e0i7C5c=eoucOzW1*9 zi**e*4?FemdxrL`^`sqtK>s=NX1c!DYc0R-S{264!>skZ7x-nb%@*IgIzGPNru{s` zTzGrY#tQp!mNPYEE7VFhR(uqYx?!Dd-#zpoUUOD;ti(;C=jjwHtv!okSUpbPgV;Re zu^%r+$)d8s3Mwc*9bfh%CWnrM9Dc+msBK||!;YWQ-_n^&)31dt3-GQjnb&0=%GUOO?a+1-ukd-NB0FfyOi3! zt593f&RqMakK;70JsH@ign1elUkZ{NCw)8q<$@_ui|h(+q?&P{b-2!zwLGO zYc@7I`M9=w)CF@+u5nm)YP}9NFJ!v_b^L-=o(}_#ew(AB9Q8l%2PhXTw}SGv^aq}g z0@_AiLR@D~3`V>uU)FfW^4}@rs5B+-=QYFnx=5k21N*$Yyhs7wIxzXYvB@S$TAuF(G@%f ziTWCkrHAxO?qV+LGpOA!vr#jeZGqGtGA=&XC?R*H#gT`c5ui(y74(~H{ySt z)_%ORKxcU*?du$b-+;@(^B8Z*JfE#U->XgjAVBQ}>O-jpH*G!LX#3{?(#l2Uw8(Ai z@LfJ+9|NY1FcY-)GJZpJMEY+5YsaFHt(PS6+vLS#&^9OH*K10@>~hhKY-)9``lG0;RkB=#LT$H0vlsfvm9`z0lX_Ig>__Va@+%;NB zuK!8TUjotSqHkK&xp3sOz0X=6WKJDD)HH39-#Zug+Lv@{W3O|35=hpOTPT4JP{)2= z%Fy>vDYEFS;OXvX&$ssefjJKP_NVKEN7|0bwiN3RkgrA0^*(3a-@9sv^$n5gzJr3U zIWXLuiqbqb#%{mS6QFSr%TM~dP>mIe`XxZyGU2KZwInvIvVkz^(xklcU=BJ z2YTzdShCZ%+rJTgRuWc(P~K2qmlB}9#NIPimUo*@Z8m za_mfyx$5rwreDjpb57*>O4-ldsVD3dE+`QNHv}9zMgK=!b>J7h@$rsbGi2{ZZeI!5 zqxdTVIl+XT4vwr9B=#iWt!h*;F<{wcm)wp6$kSa~w(!H9-MRKK7vFzo;`9Suh}qHC z_kCzvcS|Sw0JmkQh!Kqyayz?vZM^P{*mJ;7X^`5Juv3)Lr+JH$(Os$U2-%9_f_Cih zvt#TAZRs(kMFD)9a21CZM66FFQE9n{*0ikd5MvFIC&>r2-7H7-Hkhx-{B-Fu7t0+L9x+jGO8ur?Pl+03(-`}ejBPx`j%pv~YK8tr|8D3iR2cMN_VTDYinY{$4JO+Qm;J65T) z%~ZOt3eM22vb4?$JP}GG48mI<(@zvXZKz;d|E%V=3pd%QRsE4Lp&#~sao*^o_7O@ zy>};K%$lP;baQ!U#Y^3r}rv;9+0|h5>#>+1r6e3)6o8*;j!e~ndlSp5G#yP zS<$3&t%Tx3BS2R8N_Xxamb=>@Li$pBB+A$YGKV_4v|X{vm@`OY&a-oqS|Q#>pgp&t zG@K`8790>I$*es1VvG?TQjl%EP3Grr> zG1;23ZpZ!^EW6J=HM?(+X06=*u~uv;CLGL2yJa79`FD5;<#P=;u|BtigvWJ~)=R ziAJs5e`2nVaajU-t08>6%Qj-XE8;F_Q)57w-^u5eH!xX&~$(-oh?YMA%*W zFy125JnLSx9o7d?lH4^+D;9khD_9YDM6YF9$rGXUA~t%-@yD$FVk zO$bx8HdD(n?~$#|gszIW07s(9p>h^2Lt zpfb*`q~PEnD|j>T*AsW5Ht*GM)-79J?zLJ+ma|cZ19zWbxz^3?{&?Op@%CZPUpusQ z7}&12wmW#~7*ybX@0OK$_Hgs7^S5~pcMr;Q z!>E4xAM)Gw4e6y z4?muOc{01GP@k=DLa!y-DSFEreh08z`i{$q`<-8#@Z*ZV1S<8N`uTcO*5wr=UN6-} zD3_jCnxm)eq*hB{KNk)w_=5<2dBw>$L})Q`5bN z#BHz!EqQTUu48fSgSKds2DK*yevCGCL62q27Vo^!sG_)b3Xui*xGm4Ucc?bKixvL4 zCk4B}hXh)>FnZ#7q#@lJ0~DWx9WPx@Tz%lR6WWBI^(I)_-S*9y)9mXHpl6~!G3_|Z zn}d;CB;*_!@@XD&dO$1Pj+E%q6L#6RBxWb=&_1?mV(dl8Wl`@b4rqDoBKvo@p0JF* zFu*R&1^V$&`Zfdpfrwr`U2{&(bY`A(4586piX7i*>wH}i8F!af`X8R(2dsz-Pw_2U zyRd0nZ0(MqZQX3$&i`71voTJat&6fF^1hxeaA<(5Xw*-m7%-iCg`K}@BJBh613*og0?)21@l-iE>EDZ#3=Sy z&(RZt`^AY!shx6q%aiY&Xp;4af91xXWQ@I7o^`_>j7hc8@jdu{1nTG^H@1Pt4(%b0 zDCw`QBboR0?YuXn){A<;A>sb&SANk0CWc)OahDk^KJ3ydUE;znkNR(56T{;pci#qy zGJ|<#zu@j$jc*1io7w1ut#UCR41 ze0%=j*b|}2?$i-agwQndH(Pfqy(a`3bfs}=Ab!`7oq}6)AYPWiwgYjy8l;BR2jX|d z$#O9r{gQ}T*@20=(6#{nDN;81z*xOOO0WXeFa2XZl0X+di5Rg7yGTc27rF?$h=Z_; zmkiMUGXkjhgeJ}XYA{7fP>UmHno8phghi68Zm!Um+!e4{g zLSAwgMhL_4eMRUY{2unh$3;GY46XQyP)71d{I)RV_?(mXIjL>m!mRD+*%E{3+0r}O z^@J_xb$kwtb)8AtmX~_1+AFzr5RQyfo*AlLkv46SuWzDkDqTkDB1r(Qwf$Q}6UA9f4b+PGd z4p_c7N{4o7a;E@|G=~PXY>{pXaa$MR#q*Zg-(?RHXWK;eYC)H6lZE#l6!H{y zf7mduqdBilYD06J!K%KVTl3G7RHMbIS_vu~X^nrhKdqvT7+TXG?T=ZU!O)uiXn$In z88KGuM2hxzMm%AoHUH86XRrB>_BXHjPcns%mZm)b(JsanI`sY&lfUtoQ}RzW`KR)` z)vCj>pp~-`W5o`I==Y`bJqxtPg4PjPOgk8?xZN^Zei*~@cMJg;JH+Mb_REN|VuyOP zd`83*HY--nNBh%C`iQaOrt@h3EWXE`-bZViE&L{Qqx?vdJl!T4F;?a>`MV;XuvuA- z$v?;BpJ(#VGx_J6{PRuz1tx!5%^WdSHp=8bD&h&76*sd-r^jfXzfXsyXcbz-Sh15K zT7FE#6E-U*_|g7q)qoJQvSO2eu_=6s$-ji_voS1XTpdd98)uRq$K_9lgTHThp_^1sgHf1SyHk%|8*Qc&neHJ8rtSRi?-sj2Ai>OhsD z-k=o^#b-Po+7tE-P-Vz}Egtfx{G>f#--P4eP>3G6>4FZNXoWG?je`gLJ`{bNMN(OY)OBKb8Gb@*+Q`vENHxysv`wN?zykmF#`VH*j^yWprlS6pNd7W}OJ(!eWs-lA^VcyC!oe!b*EOtM`ajS8Q6CHox72wv zEmcdgZ?CS7aZAhc#cWAUOKTP9mM(9ss%xO#dAN>Xd7}`iZ}YV-ZNlC`AC~alT#vJB zu&J+xP#Tssw9-bts+z{erdq7_U%VVA*x(wY#`+q*Z?I}fbA7$R9ex}K`w6R-)(BZ_ zEd=-V`sOD7h88cEsBf$F3aA<(fcq_PVNJA(kiU%kHThN$B{i^BM=&*ux%RZTPQk-HGjqaM~` zd1E-AR9GI0OW}H3O1!hYaCq_4h=%JM>E@dHClLZIg1KTpxr_BzMSjeZW)w zcix9Hn27qRE`q|H?==M;wcpOQVy`dxqZ%^j19*tSpT7rq)D99ZL?bxhX=F@t)b5jB zWklD>ctWo$h=<$)qTpMJ-p<%zqJGAHPxKDP-X;ov{@%Ogdmohg!JWqgl*bSL`6Lg2 zYF{ZFf6sEp=1V?~XiYm4|UBOVk`%y|!Ecm=1Mi6YXw zB>geb+ZgY!oU6tDJLf8?KlC|8{-DVkDB@Kl=~szj#{f6dvKI z9j1825ry8Wyzulvc%F~Ym*ad#rhj%MJ(4LL+(#0{d$=6R{V3v*S*1ibF?NWkU&aUT z{Uz~uFAuj+)(fTYqKLj@Ne+X8%cOh~QOFG=3cd1(A|1FK!nuhD=q#ehm+Of_-#Vgr zUpi6f<%y!cte5m5qVV4(=?6riuLTu_Xp*FvlIBY~R?;s^s^a$%=?gtAgoE^^{h)-y z^9$*wlRwgNK2f+YmJ~&fIX6NtqTiME8R`El@hIPK6MYEzMtCZ|z@hyYk3OVlshU+DK+;z4;lMmBkhlX%d4qN|{v zq-{i@?`lccNlJG`llu-yd3u58`G&@T>wUXy-;i!Rf1rOz>PdqcN;j?t!le=qc^A=j z^8Kh!G%loe;|ii*M?D~l{Nd#h`ud26zER~7nZJ$Pf&0Hd1AuqPV(??!a~~N7MO+~& z20&x@=>f#zsbf(rjeeqoV3-q@;G>}^?`9gSg#G#GD9qJ1(ika9oR5|wF5xks$%#gP z5hrL#Fg0?EN?NEjPi+$DkIL(^Mi;x3Jnb#%?@Yj7g5F}{C5VYQJ|4Q=vnWhQE5b$YpNzK z5oahG(=mb@4Fh>O0{%ZAq46&}tBl5NX!7{@2ZWDT$W0lWv`hsx%2k6el72*#+?Qad zuk>y_WDZ{>#@5xa+wqW`1J5;hjCg3e`Ir`j8-+(-jEs2440wa!r|_1*Zp3>Dd?_Af z6ds1IQF5PvZ&gB|N)AKFC^_mcVx6j!oe61 zny1qkP8lWleK;7?--I{WfR_n>g-7Ep{14aPEL1WZIY^`M#+dM~H^es!vI>vx)-mF_;xJZH zU{a3QN)uiq@EmZj#)DyE7_Y{JM`zkndMKmxTV%p(HR$Jpu+oplmG~cyFP(WC3$N9L zx7wgzjsfpp6W$@<(fgE9`q4S?#{6Atz{`WM(r>c~F9^K(3QWoo`yCVB219)FrJMtF zj|s1IQ0#W-NfX{<2K`_S>-Vw=uNru<^7m&ZyvGfAG`CgpJ#NCIvu2-xzcMQQj+*fH z0nY`YYCP1gD!hIZ9-X1O0RGA-y#F%c9X8;Nfw00$M0*0!aCxCKPKy+nlp{9JB$tlN zrh1`_dM~w~#&U2P?*$}}M=_{+?=%x$5dv*fU{a3QDJHy64Dp~};}qUCCcGZtg$O_z zrQbXgUe?7T{i-214v)gS$%OZkQ+x}Na9ob;H<<8Rf#-sIHJ%B06yAL%ybN@Hb?{e4 z>35F_Zv*fUWsWxyRN?I~;oSy2s{6_)yvIy<&j4>DgsSmO!lUrIOn9dgWB03czeGI3 zx$%&hkHSvnr5X>7Z4}-wOn7s#bf6sm$|(I_G~ulVUNVHL@l3&^@Q$1CHUMuj{FPC7 zM@@JefTz-LDufl@Cnmfe;E|j%3NK{Bd&Z#OGzcraVb{ayi{ z1Mby$D)1=0OH6pvF=#mke`S<@7n|@78{&I8gcV+e2`_I*?D5}J6W%8V{bm^OzGA{# z0KDl+IVnf%RVF-JVI)5)r5wfoMibsP;KdquE-~Sa2A&(?tMSanqvE^Dg!lhV1emB4 zfbuW^3%`0a^KyXZ!Q#Uos2FD6YBcjqfaZbbm|y^A-f}eaN`U6w04hoV;y@sVnRg7$ zycS@i&jw5WJ%nbS53n*&0Gg8k#Gtl6%)IMp<{c0Qx(_G@12FepMl&x5XdcXcp-ASv zLo-i69D90sg=U_ME+YKGk<4QTIsoK7V08}$E + Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** \ingroup Cortex_M3 + @{ + */ + +/* CMSIS CM3 definitions */ +#define __CM3_CMSIS_VERSION_MAIN (0x03) /*!< [31:16] CMSIS HAL main version */ +#define __CM3_CMSIS_VERSION_SUB (0x20) /*!< [15:0] CMSIS HAL sub version */ +#define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16) | \ + __CM3_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x03) /*!< Cortex-M Core */ + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __TMS470__ ) + #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#endif + +/** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all +*/ +#define __FPU_USED 0 + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TMS470__ ) + #if defined __TI__VFP_SUPPORT____ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif +#endif + +#include /* standard types definitions */ +//#include /* Core Instruction Access */ +//#include /* Core Function Access */ + +#endif /* __CORE_CM3_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM3_H_DEPENDANT +#define __CORE_CM3_H_DEPENDANT + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM3_REV + #define __CM3_REV 0x0200 + #warning "__CM3_REV not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0 + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 4 + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0 + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/*@} end of group Cortex_M3 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + ******************************************************************************/ +/** \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ +#else + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ +#endif + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + + +/** \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + + +/** \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ +#else + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ +#endif + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + + +/** \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/*@} end of group CMSIS_CORE */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24]; + __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[24]; + __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24]; + __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24]; + __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56]; + __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644]; + __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0 /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL << NVIC_STIR_INTID_Pos) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5]; + __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24 /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20 /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16 /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4 /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0 /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL << SCB_CPUID_REVISION_Pos) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31 /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28 /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27 /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26 /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25 /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23 /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22 /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12 /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11 /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0 /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL << SCB_ICSR_VECTACTIVE_Pos) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#if (__CM3_REV < 0x0201) /* core r2p1 */ +#define SCB_VTOR_TBLBASE_Pos 29 /*!< SCB VTOR: TBLBASE Position */ +#define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ + +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x3FFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#else +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16 /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15 /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8 /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2 /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1 /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0 /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL << SCB_AIRCR_VECTRESET_Pos) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4 /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2 /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1 /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9 /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8 /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4 /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3 /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1 /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0 /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL << SCB_CCR_NONBASETHRDENA_Pos) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18 /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17 /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16 /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15 /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14 /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13 /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12 /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11 /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10 /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8 /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7 /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3 /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1 /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0 /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL << SCB_SHCSR_MEMFAULTACT_Pos) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Registers Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16 /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8 /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0 /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL << SCB_CFSR_MEMFAULTSR_Pos) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* SCB Hard Fault Status Registers Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31 /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30 /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1 /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4 /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3 /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2 /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1 /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0 /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL << SCB_DFSR_HALTED_Pos) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1]; + __I uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ +#if ((defined __CM3_REV) && (__CM3_REV >= 0x200)) + __IO uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +#else + uint32_t RESERVED1[1]; +#endif +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0 /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL << SCnSCB_ICTR_INTLINESNUM_Pos) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2 /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1 /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0 /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL << SCnSCB_ACTLR_DISMCYCINT_Pos) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __O union + { + __O uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __O uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __O uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864]; + __IO uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15]; + __IO uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15]; + __IO uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29]; + __O uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __I uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IO uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43]; + __O uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __I uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6]; + __I uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __I uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __I uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __I uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __I uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __I uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __I uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __I uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __I uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __I uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __I uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __I uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0 /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL << ITM_TPR_PRIVMASK_Pos) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23 /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16 /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10 /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8 /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4 /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3 /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2 /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1 /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0 /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL << ITM_TCR_ITMENA_Pos) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0 /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL << ITM_IWR_ATVALIDM_Pos) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0 /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL << ITM_IRR_ATREADYM_Pos) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0 /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL << ITM_IMCR_INTEGRATION_Pos) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2 /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1 /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0 /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL << ITM_LSR_Present_Pos) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1]; + __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1]; + __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1]; + __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28 /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27 /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26 /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25 /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24 /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22 /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21 /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20 /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19 /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18 /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17 /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16 /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12 /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10 /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9 /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5 /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1 /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0 /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL << DWT_CPICNT_CPICNT_Pos) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0 /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL << DWT_EXCCNT_EXCCNT_Pos) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0 /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL << DWT_SLEEPCNT_SLEEPCNT_Pos) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0 /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL << DWT_LSUCNT_LSUCNT_Pos) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0 /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL << DWT_FOLDCNT_FOLDCNT_Pos) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0 /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL << DWT_MASK_MASK_Pos) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24 /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16 /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12 /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10 /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9 /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8 /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7 /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5 /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0 /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL << DWT_FUNCTION_FUNCTION_Pos) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IO uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IO uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2]; + __IO uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55]; + __IO uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131]; + __I uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IO uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __I uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759]; + __I uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __I uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __I uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1]; + __I uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __I uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IO uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39]; + __IO uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IO uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8]; + __I uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __I uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0 /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL << TPI_ACPR_PRESCALER_Pos) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0 /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL << TPI_SPPR_TXMODE_Pos) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3 /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2 /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1 /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0 /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL << TPI_FFSR_FlInProg_Pos) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8 /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1 /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0 /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL << TPI_TRIGGER_TRIGGER_Pos) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29 /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27 /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26 /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24 /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16 /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8 /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0 /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL << TPI_FIFO0_ETM0_Pos) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY_Pos 0 /*!< TPI ITATBCTR2: ATREADY Position */ +#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL << TPI_ITATBCTR2_ATREADY_Pos) /*!< TPI ITATBCTR2: ATREADY Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29 /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27 /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26 /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24 /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16 /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8 /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0 /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL << TPI_FIFO1_ITM0_Pos) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY_Pos 0 /*!< TPI ITATBCTR0: ATREADY Position */ +#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL << TPI_ITATBCTR0_ATREADY_Pos) /*!< TPI ITATBCTR0: ATREADY Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0 /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x1UL << TPI_ITCTRL_Mode_Pos) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11 /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10 /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9 /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6 /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5 /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0 /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL << TPI_DEVID_NrTraceInput_Pos) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 0 /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL << TPI_DEVTYPE_SubType_Pos) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 4 /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if (__MPU_PRESENT == 1) +/** \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __I uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IO uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IO uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IO uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IO uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IO uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IO uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IO uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IO uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IO uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IO uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +/* MPU Type Register */ +#define MPU_TYPE_IREGION_Pos 16 /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8 /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0 /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL << MPU_TYPE_SEPARATE_Pos) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register */ +#define MPU_CTRL_PRIVDEFENA_Pos 2 /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1 /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0 /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL << MPU_CTRL_ENABLE_Pos) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register */ +#define MPU_RNR_REGION_Pos 0 /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL << MPU_RNR_REGION_Pos) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register */ +#define MPU_RBAR_ADDR_Pos 5 /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4 /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0 /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL << MPU_RBAR_REGION_Pos) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register */ +#define MPU_RASR_ATTRS_Pos 16 /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28 /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24 /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19 /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18 /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17 /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16 /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8 /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1 /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0 /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL << MPU_RASR_ENABLE_Pos) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16 /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25 /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24 /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19 /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18 /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17 /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16 /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5 /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3 /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2 /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1 /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0 /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL << CoreDebug_DHCSR_C_DEBUGEN_Pos) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register */ +#define CoreDebug_DCRSR_REGWnR_Pos 16 /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0 /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL << CoreDebug_DCRSR_REGSEL_Pos) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19 /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18 /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17 /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16 /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10 /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9 /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8 /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7 /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6 /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5 /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4 /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0 /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL << CoreDebug_DEMCR_VC_CORERESET_Pos) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M3 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if (__MPU_PRESENT == 1) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/** \brief Set Priority Grouping + + The function sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8)); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** \brief Get Priority Grouping + + The function reads the priority grouping field from the NVIC Interrupt Controller. + + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +{ + return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos); /* read priority grouping field */ +} + + +/** \brief Enable External Interrupt + + The function enables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* enable interrupt */ +} + + +/** \brief Disable External Interrupt + + The function disables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */ +} + + +/** \brief Get Pending Interrupt + + The function reads the pending register in the NVIC and returns the pending bit + for the specified interrupt. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */ +} + + +/** \brief Set Pending Interrupt + + The function sets the pending bit of an external interrupt. + + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */ +} + + +/** \brief Clear Pending Interrupt + + The function clears the pending bit of an external interrupt. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ +} + + +/** \brief Get Active Interrupt + + The function reads the active register in NVIC and returns the active bit. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + */ +__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */ +} + + +/** \brief Set Interrupt Priority + + The function sets the priority of an interrupt. + + \note The priority cannot be set for every core interrupt. + + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if(IRQn < 0) { + SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M System Interrupts */ + else { + NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */ +} + + +/** \brief Get Interrupt Priority + + The function reads the priority of an interrupt. The interrupt + number can be positive to specify an external (device specific) + interrupt, or negative to specify an internal (core) interrupt. + + + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented + priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if(IRQn < 0) { + return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M system interrupts */ + else { + return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ +} + + +/** \brief Encode Priority + + The function encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the samllest possible priority group is set. + + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + return ( + ((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) | + ((SubPriority & ((1 << (SubPriorityBits )) - 1))) + ); +} + + +/** \brief Decode Priority + + The function decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the samllest possible priority group is set. + + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + *pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1); + *pSubPriority = (Priority ) & ((1 << (SubPriorityBits )) - 1); +} + + +/** \brief System Reset + + The function initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __asm volatile ("dsb"); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */ + __asm volatile ("dsb"); /* Ensure completion of memory access */ + while(1); /* wait until reset */ +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0) + +/** \brief System Tick Configuration + + The function initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + + \param [in] ticks Number of ticks between two interrupts. + + \return 0 Function succeeded. + \return 1 Function failed. + + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ + + SysTick->LOAD = ticks - 1; /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY 0x5AA55AA5 /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** \brief ITM Send Character + + The function transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + + \param [in] ch Character to transmit. + + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && /* ITM enabled */ + (ITM->TER & (1UL << 0) ) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0].u32 == 0); + ITM->PORT[0].u8 = (uint8_t) ch; + } + return (ch); +} + + +/** \brief ITM Receive Character + + The function inputs a character via the external variable \ref ITM_RxBuffer. + + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) { + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** \brief ITM Check Character + + The function checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) { + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) { + return (0); /* no character available */ + } else { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + +#endif /* __CORE_CM3_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ + +#ifdef __cplusplus +} +#endif diff --git a/sdk/Platform/Boot/gcc/misc.c b/sdk/Platform/Boot/gcc/misc.c new file mode 100644 index 0000000..5cc9d93 --- /dev/null +++ b/sdk/Platform/Boot/gcc/misc.c @@ -0,0 +1,225 @@ +/** + ****************************************************************************** + * @file misc.c + * @author MCD Application Team + * @version V3.6.1 + * @date 05-March-2012 + * @brief This file provides all the miscellaneous firmware functions (add-on + * to CMSIS functions). + */ + +/* Includes ------------------------------------------------------------------*/ +#include "misc.h" + +/** @addtogroup W600_Driver + * @{ + */ + +/** @defgroup MISC + * @brief MISC driver modules + * @{ + */ + +/** @defgroup MISC_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Private_Defines + * @{ + */ + +#define AIRCR_VECTKEY_MASK ((uint32_t)0x05FA0000) +/** + * @} + */ + +/** @defgroup MISC_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Private_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Private_Functions + * @{ + */ + +/** + * @brief Configures the priority grouping: pre-emption priority and subpriority. + * @param NVIC_PriorityGroup: specifies the priority grouping bits length. + * This parameter can be one of the following values: + * @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority + * 4 bits for subpriority + * @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority + * 3 bits for subpriority + * @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority + * 2 bits for subpriority + * @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority + * 1 bits for subpriority + * @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority + * 0 bits for subpriority + * @retval None + */ +void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) +{ + /* Check the parameters */ +// assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); + + /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */ + SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; +} + +/** + * @brief Initializes the NVIC peripheral according to the specified + * parameters in the NVIC_InitStruct. + * @param NVIC_InitStruct: pointer to a NVIC_InitTypeDef structure that contains + * the configuration information for the specified NVIC peripheral. + * @retval None + */ +void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) +{ + uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F; + + /* Check the parameters */ +// assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd)); +// assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority)); +// assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority)); + + if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE) + { + /* Compute the Corresponding IRQ Priority --------------------------------*/ + tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08; + tmppre = (0x4 - tmppriority); + tmpsub = tmpsub >> tmppriority; + + tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre; + tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub; + tmppriority = tmppriority << 0x04; + + NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority; + + /* Enable the Selected IRQ Channels --------------------------------------*/ + NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = + (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F); + } + else + { + /* Disable the Selected IRQ Channels -------------------------------------*/ + NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = + (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F); + } +} + +void NVIC_Configration(int irqno, FunctionalState irqstatus) +{ + NVIC_InitTypeDef NVIC_InitStruct; + + NVIC_InitStruct.NVIC_IRQChannel = irqno; + NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStruct.NVIC_IRQChannelCmd = irqstatus; + NVIC_Init(&NVIC_InitStruct); +} + + +/** + * @brief Sets the vector table location and Offset. + * @param NVIC_VectTab: specifies if the vector table is in RAM or FLASH memory. + * This parameter can be one of the following values: + * @arg NVIC_VectTab_RAM + * @arg NVIC_VectTab_FLASH + * @param Offset: Vector Table base offset field. This value must be a multiple + * of 0x200. + * @retval None + */ +void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset) +{ + /* Check the parameters */ +// assert_param(IS_NVIC_VECTTAB(NVIC_VectTab)); +// assert_param(IS_NVIC_OFFSET(Offset)); + + SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80); +} + +/** + * @brief Selects the condition for the system to enter low power mode. + * @param LowPowerMode: Specifies the new mode for the system to enter low power mode. + * This parameter can be one of the following values: + * @arg NVIC_LP_SEVONPEND + * @arg NVIC_LP_SLEEPDEEP + * @arg NVIC_LP_SLEEPONEXIT + * @param NewState: new state of LP condition. This parameter can be: ENABLE or DISABLE. + * @retval None + */ +void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState) +{ + /* Check the parameters */ +// assert_param(IS_NVIC_LP(LowPowerMode)); +// assert_param(IS_FUNCTIONAL_STATE(NewState)); + + if (NewState != DISABLE) + { + SCB->SCR |= LowPowerMode; + } + else + { + SCB->SCR &= (uint32_t)(~(uint32_t)LowPowerMode); + } +} + +/** + * @brief Configures the SysTick clock source. + * @param SysTick_CLKSource: specifies the SysTick clock source. + * This parameter can be one of the following values: + * @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source. + * @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source. + * @retval None + */ +void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource) +{ + /* Check the parameters */ +// assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource)); + if (SysTick_CLKSource == SysTick_CLKSource_HCLK) + { + SysTick->CTRL |= SysTick_CLKSource_HCLK; + } + else + { + SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8; + } +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. *****END OF FILE****/ diff --git a/sdk/Platform/Boot/gcc/retarget.c b/sdk/Platform/Boot/gcc/retarget.c new file mode 100644 index 0000000..b62cf3b --- /dev/null +++ b/sdk/Platform/Boot/gcc/retarget.c @@ -0,0 +1,93 @@ +#if GCC_COMPILE +#include + +extern int sendchar(int ch); + +_ssize_t _write_r (struct _reent *r, int file, const void *ptr, size_t len) +{ + size_t i; + char *p; + + p = (char*) ptr; + + for (i = 0; i < len; i++) + { + sendchar(*p++); + } + return len; +} + +_ssize_t _read_r(struct _reent *r, int file, void *ptr, size_t len) +{ + return 0; +} + +int _close_r(struct _reent *r, int file) +{ + return 0; +} + +_off_t _lseek_r(struct _reent *r, int file, _off_t ptr, int dir) +{ + return (_off_t)0; /* Always indicate we are at file beginning. */ +} + +int _fstat_r(struct _reent *r, int file, struct stat *st) +{ + return 0; +} + +int _isatty(int file) +{ + return 1; +} + +void abort(void) +{ + while(1); +} + +extern char end[]; +extern char __HeapLimit[]; +static char *heap_ptr = end; +static char *heap_end = __HeapLimit; +/** + * @brief This function is used to extend current heap size, Limit is __HeapLimit + * + * @param[in] None + * + * @return None + * + * @note Normally, it is just used in gcc mode. + */ +void * _sbrk_r(struct _reent *_s_r, ptrdiff_t nbytes) +{ + char *base; + + base = heap_ptr; + + if(base + nbytes > heap_end) + { + return (void *)-1; + } + + heap_ptr += nbytes; + return base; +} + +void * tls_reserve_mem_lock(int nbytes) +{ + if(heap_end - (nbytes + 4) <= heap_ptr) + { + return NULL; + } + heap_end -= (nbytes + 4); + return (void *)((((int)heap_end + 3) >> 2) << 2); +} + +void tls_reserve_mem_unlock(void) +{ + heap_end = __HeapLimit; +} + +#endif diff --git a/sdk/Platform/Boot/gcc/startup.S b/sdk/Platform/Boot/gcc/startup.S new file mode 100644 index 0000000..37aab42 --- /dev/null +++ b/sdk/Platform/Boot/gcc/startup.S @@ -0,0 +1,256 @@ +/* File: startup_ARMCM3.S + * Purpose: startup file for Cortex-M3 devices. Should use with + * GCC for ARM Embedded Processors + * Version: V2.0 + * Date: 16 August 2013 + * + * Copyright (c) 2011 - 2013 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM 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 COPYRIGHT HOLDERS AND 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. + ---------------------------------------------------------------------------*/ + .syntax unified + .arch armv7-m + + .section .stack + .align 3 +#ifdef __STACK_SIZE + .equ Stack_Size, __STACK_SIZE +#else + .equ Stack_Size, 0xc00 +#endif + .globl __StackTop + .globl __StackLimit +__StackLimit: + .space Stack_Size + .size __StackLimit, . - __StackLimit +__StackTop: + .size __StackTop, . - __StackTop + + .section .heap + .align 3 +#ifdef __HEAP_SIZE + .equ Heap_Size, __HEAP_SIZE +#else + //.equ Heap_Size, 0x1A000 + .equ Heap_Size, 0x21000 +#endif + .globl __heap_base + .globl __HeapLimit +__heap_base: + .if Heap_Size + .space Heap_Size + .endif + .size __heap_base, . - __heap_base +__HeapLimit: + .size __HeapLimit, . - __HeapLimit + + .section .isr_vector + .align 2 + .globl __isr_vector +__isr_vector: + .long __StackTop /* Top of Stack */ + .long Reset_Handler /* Reset Handler */ + .long NMI_Handler /* NMI Handler */ + .long HardFault_Handler /* Hard Fault Handler */ + .long MemManage_Handler /* MPU Fault Handler */ + .long BusFault_Handler /* Bus Fault Handler */ + .long UsageFault_Handler /* Usage Fault Handler */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long SVC_Handler /* SVCall Handler */ + .long DebugMon_Handler /* Debug Monitor Handler */ + .long 0 /* Reserved */ + .long PendSV_Handler /* PendSV Handler */ + .long OS_CPU_SysTickHandler /* SysTick Handler */ + + /* External interrupts */ + .long SDIO_RX_IRQHandler /*HSPI&SDIO SLAVE Trasmit*/ + .long SDIO_TX_IRQHandler /*HSPI & SDIO SLAVE Receive Data*/ + .long 0 /*Not Used*/ + .long SDIO_TX_CMD_IRQHandler /*HSPI & SDIO SLAVE Receive Command*/ + .long tls_wl_mac_isr /*MAC isr*/ + .long 0 /*Not Used*/ + .long tls_wl_rx_isr /*Wi-Fi Rx isr*/ + .long tls_wl_mgmt_tx_isr /*Wi-Fi Managment tx isr*/ + .long tls_wl_data_tx_isr /*Wi-Fi Data Tx isr*/ + .long PMU_TIMER1_IRQHandler /*PMU Timer1*/ + .long PMU_TIMER0_IRQHandler /*PMU Timer0*/ + .long PMU_GPIO_WAKE_IRQHandler /*PMU Wakeup pin isr*/ + .long PMU_SDIO_WAKE_IRQHandler /*SDIO Wakeup isr*/ + .long DMA_Channel0_IRQHandler /*DMA Channel 0*/ + .long DMA_Channel1_IRQHandler /*DMA Channel 1*/ + .long DMA_Channel2_IRQHandler /*DMA Channel 2*/ + .long DMA_Channel3_IRQHandler /*DMA Channel 3*/ + .long DMA_Channel4_7_IRQHandler /*DMA Channel 4-7*/ + .long DMA_BURST_IRQHandler /*DMA Burst isr*/ + .long I2C_IRQHandler /*I2C isr*/ + .long ADC_IRQHandler /*ADC isr*/ + .long SPI_LS_IRQHandler /*Master SPI isr*/ + .long SPI_HS_IRQHandler /*HSPI isr, not used*/ + .long UART0_IRQHandler /*UART0 isr*/ + .long UART1_IRQHandler /*UART1 isr*/ + .long GPIOA_IRQHandler /*GPIOA isr*/ + .long TIM0_IRQHandler /*TIM0 isr*/ + .long TIM1_IRQHandler /*TIM1 isr*/ + .long TIM2_IRQHandler /*TIM2 isr*/ + .long TIM3_IRQHandler /*TIM3 isr*/ + .long TIM4_IRQHandler /*TIM4 isr*/ + .long TIM5_IRQHandler /*TIM5 isr*/ + .long WDG_IRQHandler /*Watchdog isr*/ + .long 0 /*Not Used*/ + .long 0 /*Not Used*/ + .long PWM_IRQHandler /*PWM isr*/ + .long I2S_IRQHandler /*I2S isr*/ + .long PMU_RTC_IRQHandler /*PMU RTC isr*/ + .long RSA_IRQHandler /*RSA isr*/ + .long CRYPTION_IRQHandler /*CRYPTION isr*/ + .long GPIOB_IRQHandler /*GPIOB isr*/ + .long UART2_IRQHandler /*UART2&7816 isr*/ + + .size __isr_vector, . - __isr_vector + + .text + .thumb + .thumb_func + .align 2 + .globl Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Firstly it copies data from read only memory to RAM. + * + * The ranges of copy from/to are specified by following symbols + * __etext: LMA of start of the section to copy from. Usually end of text + * __data_start__: VMA of start of the section to copy to + * __data_end__: VMA of end of the section to copy to + * + * All addresses must be aligned to 4 bytes boundary. + */ + ldr r1, =__etext + ldr r2, =__data_start__ + ldr r3, =__data_end__ + +.L_loop1: + cmp r2, r3 + ittt lt + ldrlt r0, [r1], #4 + strlt r0, [r2], #4 + blt .L_loop1 + +/* Single BSS section scheme. + * + * The BSS section is specified by following symbols + * __bss_start__: start of the BSS section. + * __bss_end__: end of the BSS section. + * + * Both addresses must be aligned to 4 bytes boundary. + */ + ldr r1, =__bss_start__ + ldr r2, =__bss_end__ + + movs r0, 0 +.L_loop3: + cmp r1, r2 + itt lt + strlt r0, [r1], #4 + blt .L_loop3 +/* +#ifndef __NO_SYSTEM_INIT + bl SystemInit +#endif +*/ + bl main + + .pool + .size Reset_Handler, . - Reset_Handler + + .align 1 + .thumb_func + .weak Default_Handler + .type Default_Handler, %function +Default_Handler: + b . + .size Default_Handler, . - Default_Handler + +/* Macro to define default handlers. Default handler + * will be weak symbol and just dead loops. They can be + * overwritten by other handlers */ + .macro def_irq_handler handler_name + .weak \handler_name + .set \handler_name, Default_Handler + .endm + + def_irq_handler NMI_Handler + def_irq_handler HardFault_Handler + def_irq_handler MemManage_Handler + def_irq_handler BusFault_Handler + def_irq_handler UsageFault_Handler + def_irq_handler SVC_Handler + def_irq_handler DebugMon_Handler + def_irq_handler PendSV_Handler + def_irq_handler OS_CPU_SysTickHandler + + def_irq_handler SDIO_RX_IRQHandler + def_irq_handler SDIO_TX_IRQHandler + def_irq_handler SDIO_TX_CMD_IRQHandler + def_irq_handler tls_wl_mac_isr + def_irq_handler tls_wl_rx_isr + def_irq_handler tls_wl_mgmt_tx_isr + def_irq_handler tls_wl_data_tx_isr + /*def_irq_handler PMU_TIMER1_IRQHandler*/ + /*def_irq_handler PMU_TIMER0_IRQHandler*/ + /*def_irq_handler PMU_GPIO_WAKE_IRQHandler*/ + def_irq_handler PMU_SDIO_WAKE_IRQHandler + def_irq_handler DMA_Channel0_IRQHandler + def_irq_handler DMA_Channel1_IRQHandler + def_irq_handler DMA_Channel2_IRQHandler + def_irq_handler DMA_Channel3_IRQHandler + def_irq_handler DMA_Channel4_7_IRQHandler + def_irq_handler DMA_BURST_IRQHandler + /*def_irq_handler I2C_IRQHandler*/ + def_irq_handler ADC_IRQHandler + def_irq_handler SPI_LS_IRQHandler + def_irq_handler SPI_HS_IRQHandler + def_irq_handler UART0_IRQHandler + def_irq_handler UART1_IRQHandler + def_irq_handler GPIOA_IRQHandler + def_irq_handler TIM0_IRQHandler + def_irq_handler TIM1_IRQHandler + def_irq_handler TIM2_IRQHandler + def_irq_handler TIM3_IRQHandler + def_irq_handler TIM4_IRQHandler + def_irq_handler TIM5_IRQHandler + def_irq_handler WDG_IRQHandler + /*def_irq_handler PWM_IRQHandler*/ + /*def_irq_handler I2S_IRQHandler*/ + /*def_irq_handler PMU_RTC_IRQHandler*/ + def_irq_handler RSA_IRQHandler + def_irq_handler CRYPTION_IRQHandler + def_irq_handler GPIOB_IRQHandler + def_irq_handler UART2_IRQHandler + + .end diff --git a/sdk/Platform/Common/CMakeLists.txt b/sdk/Platform/Common/CMakeLists.txt new file mode 100644 index 0000000..42f451a --- /dev/null +++ b/sdk/Platform/Common/CMakeLists.txt @@ -0,0 +1,72 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WCOMMON) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin -Wall") +STRING(APPEND CMAKE_C_FLAGS " -DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES +"${CMAKE_CURRENT_SOURCE_DIR}/fwup/wm_fwup.c" +"${CMAKE_CURRENT_SOURCE_DIR}/mem/wm_mem.c" +"${CMAKE_CURRENT_SOURCE_DIR}/Params/wm_param.c" +"${CMAKE_CURRENT_SOURCE_DIR}/task/wm_wl_mbox.c" +"${CMAKE_CURRENT_SOURCE_DIR}/task/wm_wl_task.c" +"${CMAKE_CURRENT_SOURCE_DIR}/task/wm_wl_timers.c" +"${CMAKE_CURRENT_SOURCE_DIR}/utils/utils.c" +"${CMAKE_CURRENT_SOURCE_DIR}/crypto/wm_crypto_hard.c" + + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/digest/hmac.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/digest/md2.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/digest/md4.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/digest/sha224.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/digest/sha384.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/digest/sha512.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/keyformat/asn1.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/keyformat/base64.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/keyformat/x509.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/math/pstm_mul_comba.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/prng/prng.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/prng/yarrow.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/pubkey/dh.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/pubkey/ecc.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/pubkey/pkcs.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/pubkey/pubkey.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/pubkey/rsa.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/symmetric/aesGCM.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/symmetric/des3.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/symmetric/idea.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/symmetric/rc2.c" + # "${CMAKE_CURRENT_SOURCE_DIR}/crypto/symmetric/seed.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmcommon.a) diff --git a/sdk/Platform/Common/Params/wm_param.c b/sdk/Platform/Common/Params/wm_param.c new file mode 100644 index 0000000..628e1f3 --- /dev/null +++ b/sdk/Platform/Common/Params/wm_param.c @@ -0,0 +1,1351 @@ +/***************************************************************************** +* +* File Name : wm_param.c +* +* Description: param manager Module +* +* Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-6-12 +*****************************************************************************/ +#include +#include "wm_debug.h" +#include "wm_efuse.h" +#include "wm_flash.h" +#include "wm_internal_flash.h" +#include "wm_params.h" +#include "wm_param.h" +#include "wm_mem.h" +#include "utils.h" +#include "wm_flash_map.h" + +static struct tls_param_flash flash_param; +static struct tls_sys_param sram_param; +struct tls_sys_param user_default_param; +static tls_os_sem_t *sys_param_lock = NULL; +static const u8 factory_default_hardware[8] = {'H', 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +u8 updp_mode;//upadate default parameters mode, 0:not updating or up finish; 1:updating + +static int param_flash_verify(u32 data_addr, u8 *data_buffer, u32 len) +{ + int err; + u8 *buffer; + + buffer = tls_mem_alloc(len); + if (buffer == NULL) {return 0;} + + do { + err = tls_fls_read(data_addr, buffer, len); + if (err != TLS_FLS_STATUS_OK) { + err = 0; + break; + } + + if (memcmp(buffer, data_buffer, len) == 0) { + err = 1; + } else { + err = 0; + } + } while (0); + + tls_mem_free(buffer); + + return err; +} + +static int param_to_flash(int id, int modify_count, int partition_num) +{ + int err; + struct tls_sys_param *src; + struct tls_sys_param *dest; + + if ((id < TLS_PARAM_ID_ALL) || (id >= TLS_PARAM_ID_MAX)) {return TLS_PARAM_STATUS_EINVALID;} + + err = TLS_PARAM_STATUS_OK; + src = &sram_param; + dest = &flash_param.parameters; + + switch (id) { + case TLS_PARAM_ID_ALL: + MEMCPY(dest, src, sizeof(struct tls_sys_param)); + break; + + case TLS_PARAM_ID_SSID: + MEMCPY(&dest->ssid, &src->ssid, sizeof(struct tls_param_ssid)); + break; + + case TLS_PARAM_ID_ENCRY: + dest->encry = src->encry; + break; + + case TLS_PARAM_ID_KEY: + MEMCPY(&dest->key, &src->key, sizeof(struct tls_param_key)); + break; + + case TLS_PARAM_ID_IP: + MEMCPY(&dest->ipcfg, &src->ipcfg, sizeof(struct tls_param_ip)); + break; + + case TLS_PARAM_ID_AUTOMODE: + dest->auto_mode = src->auto_mode; + break; + + case TLS_PARAM_ID_DEFSOCKET: + MEMCPY(&dest->remote_socket_cfg, &src->remote_socket_cfg, sizeof(struct tls_param_socket)); + break; + + case TLS_PARAM_ID_BSSID: + MEMCPY(&dest->bssid, &src->bssid, sizeof(struct tls_param_bssid)); + break; + + case TLS_PARAM_ID_CHANNEL: + dest->channel = src->channel; + break; + + case TLS_PARAM_ID_CHANNEL_EN: + dest->channel_enable = src->channel_enable; + break; + + case TLS_PARAM_ID_COUNTRY_REGION: + dest->wireless_region = src->wireless_region; + break; + + case TLS_PARAM_ID_WPROTOCOL: + dest->wireless_protocol = src->wireless_protocol; + break; + + case TLS_PARAM_ID_ADHOC_AUTOCREATE: + dest->auto_create_adhoc = src->auto_create_adhoc; + break; + + case TLS_PARAM_ID_ROAMING: + dest->auto_roam = src->auto_roam; + break; + + case TLS_PARAM_ID_AUTO_RETRY_CNT: + dest->auto_retrycnt = src->auto_retrycnt; + break; + + case TLS_PARAM_ID_WBGR: + MEMCPY(&dest->wbgr, &src->wbgr, sizeof(struct tls_param_bgr)); + break; + + case TLS_PARAM_ID_USRINTF: + dest->user_port_mode = src->user_port_mode; + break; + case TLS_PARAM_ID_ESCAPE_CHAR: + dest->EscapeChar = src->EscapeChar; + break; + + case TLS_PARAM_ID_ESCAPE_PERIOD: + dest->EscapePeriod = src->EscapePeriod; + break; + + case TLS_PARAM_ID_AUTO_TRIGGER_LENGTH: + dest->transparent_trigger_length = src->transparent_trigger_length; + break; + + case TLS_PARAM_ID_AUTO_TRIGGER_PERIOD: + dest->transparent_trigger_period = src->transparent_trigger_period; + + case TLS_PARAM_ID_DEBUG_MODE: + dest->debug_mode = src->debug_mode; + break; + + case TLS_PARAM_ID_HARDVERSION: + MEMCPY(&dest->hardware_version, &src->hardware_version, sizeof(struct tls_param_hardware_version)); + break; + + case TLS_PARAM_ID_BRDSSID: + dest->ssid_broadcast_enable = src->ssid_broadcast_enable; + break; + + case TLS_PARAM_ID_DNSNAME: + MEMCPY(dest->local_dnsname, src->local_dnsname, 32); + break; + + case TLS_PARAM_ID_DEVNAME: + MEMCPY(dest->local_device_name, src->local_device_name, 32); + break; + + case TLS_PARAM_ID_PSM: + dest->auto_powersave = src->auto_powersave; + break; + + case TLS_PARAM_ID_ORAY_CLIENT: + MEMCPY(&dest->oray_client_setting, &src->oray_client_setting, sizeof(struct tls_param_oray_client)); + break; + + case TLS_PARAM_ID_UPNP: + dest->upnp_enable = src->upnp_enable; + break; + + case TLS_PARAM_ID_UART: + MEMCPY(&dest->uart_cfg, &src->uart_cfg, sizeof(struct tls_param_uart)); + break; +#if TLS_CONFIG_WPS + case TLS_PARAM_ID_WPS: + MEMCPY(&dest->wps, &src->wps, sizeof(struct tls_param_wps)); + break; +#endif + case TLS_PARAM_ID_CHANNEL_LIST: + dest->channellist = src->channellist; + break; + case TLS_PARAM_ID_ONESHOT_CFG: + dest->oneshotflag = src->oneshotflag; + break; + case TLS_PARAM_ID_SHA1: + MEMCPY(&dest->psk, &src->psk, sizeof(struct tls_param_sha1)); + break; + case TLS_PARAM_ID_ORIGIN_KEY: + MEMCPY(&dest->original_key, &src->original_key, sizeof(struct tls_param_original_key)); + break; + case TLS_PARAM_ID_ORIGIN_SSID: + MEMCPY(&dest->original_ssid, &src->original_ssid, sizeof(struct tls_param_ssid)); + break; + case TLS_PARAM_ID_AUTO_RECONNECT: + dest->auto_reconnect = src->auto_reconnect; + break; + + case TLS_PARAM_ID_IO_MODE: + dest->IoMode = src->IoMode; + break; + + case TLS_PARAM_ID_CMD_MODE: + dest->CmdMode = src->CmdMode; + break; + + case TLS_PARAM_ID_PASSWORD: + MEMCPY(dest->PassWord, src->PassWord, sizeof(src->PassWord)); + break; + + case TLS_PARAM_ID_WEBS_CONFIG: + dest->WebsCfg = src->WebsCfg; + break; + case TLS_PARAM_ID_QUICK_CONNECT: + MEMCPY(&dest->quick_connect, &src->quick_connect, sizeof(struct tls_param_quick_connect)); + break; + case TLS_PARAM_ID_KEY_CHANGE: + dest->key_changed = src->key_changed; + break; + case TLS_PARAM_ID_SSID_CHANGE: + dest->ssid_changed = src->ssid_changed; + break; +#if TLS_CONFIG_AP + case TLS_PARAM_ID_SOFTAP_SSID: + MEMCPY(&dest->apsta_ssid, &src->apsta_ssid, sizeof(struct tls_param_ssid)); + break; + case TLS_PARAM_ID_SOFTAP_PSK: + MEMCPY(&dest->apsta_psk, &src->apsta_psk, sizeof(struct tls_param_sha1)); + break; + case TLS_PARAM_ID_SOFTAP_ENCRY: + dest->encry4softap = src->encry4softap; + break; + case TLS_PARAM_ID_SOFTAP_KEY: + MEMCPY(&dest->key4softap, &src->key4softap, sizeof(struct tls_param_key)); + break; + case TLS_PARAM_ID_SOFTAP_IP: + MEMCPY(&dest->ipcfg4softap, &src->ipcfg4softap, sizeof(struct tls_param_ip)); + break; + case TLS_PARAM_ID_SOFTAP_CHANNEL: + dest->channel4softap = src->channel4softap; + break; + case TLS_PARAM_ID_SOFTAP_WBGR: + MEMCPY(&dest->wbgr4softap, &src->wbgr4softap, sizeof(struct tls_param_bgr)); + break; +#endif + case TLS_PARAM_ID_SNTP_SERVER1: + strncpy(dest->sntp_service1, src->sntp_service1, strlen(src->sntp_service1)+1); + break; + case TLS_PARAM_ID_SNTP_SERVER2: + strncpy(dest->sntp_service2, src->sntp_service2, strlen(src->sntp_service2)+1); + break; + case TLS_PARAM_ID_SNTP_SERVER3: + strncpy(dest->sntp_service3, src->sntp_service3, strlen(src->sntp_service3)+1); + break; + case TLS_PARAM_ID_TEM_OFFSET: + MEMCPY(&dest->params_tem, &src->params_tem, sizeof(struct tls_param_tem_offset)); + break; + + + default: + err = TLS_PARAM_STATUS_EINVALIDID; + goto exit; + } + + flash_param.magic = TLS_PARAM_MAGIC; + flash_param.length = sizeof(flash_param); + + if (modify_count < 0){ + flash_param.modify_count ++; + TLS_DBGPRT_INFO("update the \"modify count(%d)\".\n", flash_param.modify_count); + } else { + flash_param.modify_count = modify_count; + TLS_DBGPRT_INFO("initialize the \"modify count(%d)\".\n", flash_param.modify_count); + } + + if (partition_num < 0) { + flash_param.partition_num = (flash_param.partition_num + 1) & 0x01; + TLS_DBGPRT_INFO("switch the parameter patition number(%d).\n", flash_param.partition_num); + } else { + flash_param.partition_num = partition_num; + TLS_DBGPRT_INFO("initialize the parameter patition number(%d).\n", flash_param.partition_num); + } + flash_param.resv_1 = flash_param.resv_2 = 0; + flash_param.crc32 = get_crc32((u8 *)&flash_param, sizeof(flash_param) - 4); + + TLS_DBGPRT_INFO("update the parameters to parameter patition(%d) in spi flash.\n", flash_param.partition_num); + + err = tls_fls_write((flash_param.partition_num == 0) ? TLS_FLASH_PARAM1_ADDR : TLS_FLASH_PARAM2_ADDR, + (u8 *)&flash_param, sizeof(flash_param)); + if (err != TLS_FLS_STATUS_OK) { + TLS_DBGPRT_ERR("write to spi flash fail(%d)!\n", err); + err = TLS_PARAM_STATUS_EIO; + goto exit; + } + if (param_flash_verify((flash_param.partition_num == 0) ? TLS_FLASH_PARAM1_ADDR : TLS_FLASH_PARAM2_ADDR, + (u8 *)&flash_param, sizeof(flash_param)) == 1) {err = TLS_PARAM_STATUS_OK;} + else { + TLS_DBGPRT_ERR("verify the parameters in spi flash fail(%d)!\n", err); + err = TLS_PARAM_STATUS_EIO; + } +exit: + + return err; +} + +/********************************************************************************************************** +* Description: This function is used to initial system param. +* +* Arguments : sys_param is the system param addr +* + +* Returns : TLS_PARAM_STATUS_OK init success +* TLS_PARAM_STATUS_EMEM memory error +* TLS_PARAM_STATUS_EIO io error +* TLS_PARAM_STATUS_EPERM +**********************************************************************************************************/ +int tls_param_init(void) +{ + bool is_damage[TLS_PARAM_PARTITION_NUM]; + u8 damaged; + int err; + signed short i; + u16 tryrestore = 0; + struct tls_param_flash *flash; + + if(flash_param.magic == TLS_PARAM_MAGIC) + { + TLS_DBGPRT_ERR("parameter management module has been initialized!\n"); + return TLS_PARAM_STATUS_EPERM; + } + + err = tls_os_sem_create(&sys_param_lock, 1); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create semaphore @sys_param_lock fail!\n"); + return TLS_PARAM_STATUS_EMEM; + } + + tls_os_sem_acquire(sys_param_lock, 0); + err = TLS_PARAM_STATUS_OK; + i = 0; + damaged= 0; + is_damage[0] = is_damage[1] = FALSE; + flash = NULL; + memset(&flash_param, 0, sizeof(flash_param)); + memset(&sram_param, 0, sizeof(sram_param)); + tryrestore = 0; + do + { + flash = tls_mem_alloc(sizeof(*flash)); + if (flash == NULL) + { + TLS_DBGPRT_ERR("allocate \"struct tls_param_flash\" fail!\n"); + err = TLS_PARAM_STATUS_EMEM; + break; + } + memset(flash, 0, sizeof(*flash)); + + for (i = 0; i < TLS_PARAM_PARTITION_NUM; i++) + { + TLS_DBGPRT_INFO("read parameter partition - %d.\n", i); + tls_fls_read((i == 0) ? TLS_FLASH_PARAM1_ADDR : TLS_FLASH_PARAM2_ADDR, (u8 *)flash, sizeof(*flash)); + TLS_DBGPRT_INFO("patition %d magic - 0x%x, crc -0x%x .\n", i, flash->magic, flash->crc32); + if (flash->magic != TLS_PARAM_MAGIC) + { + TLS_DBGPRT_WARNING("parameter partition - %d has been damaged.\n", i); + is_damage[i] = TRUE; + damaged++; + //continue; + } + else if (get_crc32((u8 *)flash, flash->length - 4) != *(u32*)((u8*)flash + flash->length - 4)) + { + is_damage[i] = TRUE; + damaged++; + //continue; + } + else + { + /* Load the latest parameters */ + TLS_DBGPRT_INFO("read parameter partition modify count - %d.\n", flash->modify_count); + TLS_DBGPRT_INFO("current parameter partition modify count - %d.\n", flash_param.modify_count); + if ((flash_param.magic == 0) || (flash_param.modify_count < flash->modify_count)) + { + TLS_DBGPRT_INFO("update the parameter in sram using partition - %d,%d,%d.\n", i, flash->length,sizeof(*flash)); + if (flash->length != sizeof(*flash)){ + MEMCPY(&flash_param, flash, (flash->length-4)); + MEMCPY(&sram_param, &flash_param.parameters, sizeof(sram_param)); + }else{ + MEMCPY(&flash_param, flash, sizeof(*flash)); + MEMCPY(&sram_param, &flash_param.parameters, sizeof(sram_param)); + } + } + memset(flash, 0, sizeof(*flash)); + } + + /* try to erase one sector at the same block to restore parameter area*/ + if ((tryrestore == 0)&&(damaged >= TLS_PARAM_PARTITION_NUM)) + { + damaged= 0; + is_damage[0] = is_damage[1] = FALSE; + memset(&flash_param, 0, sizeof(flash_param)); + memset(&sram_param, 0, sizeof(sram_param)); + tls_fls_erase(TLS_FLASH_PARAM_RESTORE_ADDR / INSIDE_FLS_SECTOR_SIZE); + tryrestore = 1; + i = -1; + } + } + + if (damaged >= TLS_PARAM_PARTITION_NUM) + { + TLS_DBGPRT_INFO("all the parameter partitions has been demaged and load the default parameters.\n"); + tls_param_load_factory_default(); + + TLS_DBGPRT_INFO("write the default parameters to all the partitions.\n"); + err = param_to_flash(TLS_PARAM_ID_ALL, 1, 0); + if (err != TLS_PARAM_STATUS_OK) + { + TLS_DBGPRT_ERR("write the default parameters to the partitions - 0 fail!\n"); + err = TLS_PARAM_STATUS_EIO; + break; + } + + err = param_to_flash(TLS_PARAM_ID_ALL, 1, 1); + if (err != TLS_PARAM_STATUS_OK) + { + TLS_DBGPRT_ERR("write the default parameters to the partitions - 1 fail!\n"); + err = TLS_PARAM_STATUS_EIO; + break; + } + + //MEMCPY(&flash_param, flash, sizeof(*flash)); + } + else + { + /* restore damaged partitions */ + for (i = 0; i < TLS_PARAM_PARTITION_NUM; i++) + { + if (is_damage[i]) + { + TLS_DBGPRT_INFO(" restore damaged partitions - %d.\n", i); + err = param_to_flash(TLS_PARAM_ID_ALL, -1, i); + if (err != TLS_PARAM_STATUS_OK) + { + TLS_DBGPRT_ERR("write the default parameters to the partitions - %d fail!\n", i); + err = TLS_PARAM_STATUS_EIO; + break; + } + } + } + + if (err == TLS_PARAM_STATUS_OK) + { + break; + } + } + }while (0); + + tls_os_sem_release(sys_param_lock); + + if (flash) + { + tls_mem_free(flash); + } + +/* + printf("[sparam01] channel = %d | channel_enable = %d | wireless_region = %d\r\n", sram_param.channel, sram_param.channel_enable, sram_param.wireless_region); + printf("[sparam02] encry = %d | wprotocol = %d | auto_create_adhoc = %d\r\n", sram_param.encry, sram_param.wireless_protocol, sram_param.auto_create_adhoc); + printf("[sparam03] auto_retrycnt = %d | auto_roam = %d | auto_powersave = %d\r\n", sram_param.auto_retrycnt, sram_param.auto_roam, sram_param.auto_powersave); + printf("[sparam04] ssid_bcst = %d | channellist = %d | upnp_enable = %d\r\n", sram_param.ssid_broadcast_enable, sram_param.channellist, sram_param.upnp_enable); + printf("[sparam05] always_online = %d | user_port_mode = %d | res_2 = %d\r\n", sram_param.always_online, sram_param.user_port_mode, sram_param.res_2); + printf("[sparam06] local_dnsname = '%s' | local_device_name = '%s'\r\n", sram_param.local_dnsname, sram_param.local_device_name); + printf("[sparam07] auto_mode = %d | res_3 = %d | debug_mode = %d\r\n", sram_param.auto_mode, sram_param.res_3, sram_param.debug_mode); + printf("[sparam08] escape_char = %d | esc_rsvd = %d | escape_period = %d\r\n", sram_param.EscapeChar, sram_param.EscReserved, sram_param.EscapePeriod); + printf("[sparam09] io_mode = %d | cmd_mode = %d | password = '%s'\r\n", sram_param.IoMode, sram_param.CmdMode, sram_param.PassWord); + printf("[sparam10] oneshotflag = %d | oneshotres = %02x/%02x | auto_reconnect = %d\r\n", sram_param.oneshotflag, sram_param.oneshotres[0], sram_param.oneshotres[1], sram_param.auto_reconnect); + //printf("[sparam11] res_4 = %s | res_5 = %s | res_6 = %02x\r\n", sram_param.res_4, sram_param.res_5, sram_param.res_6); + printf("[sparam12] bssid = '%s' | ssid = '%s' | psk = '%s'\r\n", sram_param.bssid.bssid, sram_param.ssid.ssid, sram_param.key.psk); + printf("[sparam13] wgbr.bg = %d | wgbr.maxrate = %d | wgbr.res = %02x-%02x\r\n", sram_param.wbgr.bg, sram_param.wbgr.max_rate, sram_param.wbgr.res[0], sram_param.wbgr.res[1]); + printf("[sparam14] ipcfg.dhcp_enable = %d | ipcfg.ip = %d.%d.%d.%d | ipcfg.netmask = %d.%d.%d.%d\r\n", sram_param.ipcfg.dhcp_enable, sram_param.ipcfg.ip[0], sram_param.ipcfg.ip[1], sram_param.ipcfg.ip[2], sram_param.ipcfg.ip[3], sram_param.ipcfg.netmask[0], sram_param.ipcfg.netmask[1], sram_param.ipcfg.netmask[2], sram_param.ipcfg.netmask[3]); + printf("[sparam15] ipcfg.dns1 = %d.%d.%d.%d | ipcfg.dns2 = %d.%d.%d.%d\r\n", sram_param.ipcfg.dns1[0], sram_param.ipcfg.dns1[1], sram_param.ipcfg.dns1[2], sram_param.ipcfg.dns1[3], sram_param.ipcfg.dns2[0], sram_param.ipcfg.dns2[1], sram_param.ipcfg.dns2[2], sram_param.ipcfg.dns2[3]); + printf("[sparam16] ipcfg.gateway = %d.%d.%d.%d\r\n\n", sram_param.ipcfg.gateway[0], sram_param.ipcfg.gateway[1], sram_param.ipcfg.gateway[2], sram_param.ipcfg.gateway[3]); + + printf("[sparam17] original_ssid = '%s' | original_key = '%s'\r\n", sram_param.original_ssid.ssid, sram_param.original_key.psk); + printf("[sparam18] apsta_psk = '%s' | apsta_ssid = '%s'\r\n\n", sram_param.apsta_psk.psk, sram_param.apsta_ssid.ssid); + + printf("[sparam19] channel4softap = %d | encry4softap = %d | psk = '%s'\r\n", sram_param.channel4softap, sram_param.encry4softap, sram_param.key4softap.psk); + printf("[sparam20] wgbr4ap.bg = %d | wgbr4ap.maxrate = %d | wgbr4ap.res = %02x-%02x\r\n", sram_param.wbgr4softap.bg, sram_param.wbgr4softap.max_rate, sram_param.wbgr4softap.res[0], sram_param.wbgr4softap.res[1]); + printf("[sparam21] ipcfg4softap.dhcp_enable = %d | ipcfg4softap.ip = %d.%d.%d.%d | ipcfg4softap.netmask = %d.%d.%d.%d\r\n", sram_param.ipcfg4softap.dhcp_enable, sram_param.ipcfg4softap.ip[0], sram_param.ipcfg4softap.ip[1], sram_param.ipcfg4softap.ip[2], sram_param.ipcfg4softap.ip[3], sram_param.ipcfg4softap.netmask[0], sram_param.ipcfg4softap.netmask[1], sram_param.ipcfg4softap.netmask[2], sram_param.ipcfg4softap.netmask[3]); + printf("[sparam22] ipcfg4softap.dns1 = %d.%d.%d.%d | ipcfg4softap.dns2 = %d.%d.%d.%d\r\n", sram_param.ipcfg4softap.dns1[0], sram_param.ipcfg4softap.dns1[1], sram_param.ipcfg4softap.dns1[2], sram_param.ipcfg4softap.dns1[3], sram_param.ipcfg4softap.dns2[0], sram_param.ipcfg4softap.dns2[1], sram_param.ipcfg4softap.dns2[2], sram_param.ipcfg4softap.dns2[3]); + printf("[sparam23] ipcfg4softap.gateway = %d.%d.%d.%d\r\n\n", sram_param.ipcfg4softap.gateway[0], sram_param.ipcfg4softap.gateway[1], sram_param.ipcfg4softap.gateway[2], sram_param.ipcfg4softap.gateway[3]); + + + printf("[sparam24] sntp_service1 = '%s' | sntp_service2 = '%s' | sntp_service3 = '%s'\r\n", sram_param.sntp_service1, sram_param.sntp_service2, sram_param.sntp_service3); + printf("[sparam25] key_changed = %d | ssid_changed = %d\r\n", sram_param.key_changed, sram_param.ssid_changed); +*/ + +/* + struct tls_param_wps wps; + struct tls_param_oray_client oray_client_setting; + struct tls_param_uart uart_cfg; + u16 transparent_trigger_length; + u16 transparent_trigger_period; + struct tls_param_socket remote_socket_cfg; + struct tls_webs_cfg WebsCfg; + struct tls_param_quick_connect quick_connect; + struct tls_param_tem_offset params_tem; +*/ + + +#if 0 + TLS_DBGPRT_INFO("sys_param = 0x%x, *sys_param = 0x%x\n", sys_param, *sys_param); + if (sys_param) + { + *sys_param = &sram_param; + } + TLS_DBGPRT_INFO("sys_param = 0x%x, *sys_param = 0x%x\n", sys_param, *sys_param); +#endif + return err; +} + +#define TLS_USER_MAGIC 0x574D3031 // WM01 +int tls_param_load_user(struct tls_sys_param *param) +{ + u32 magic, crc32, offset; + + offset = TLS_FLASH_PARAM_DEFAULT; + tls_fls_read(offset, (u8 *)&magic, 4); + if(TLS_USER_MAGIC != magic) + { + TLS_DBGPRT_INFO("no user default param = %x!!!\r\n", magic); + return TLS_PARAM_STATUS_EINVALID; + } + offset += 4; + memset(param, 0, sizeof(*param)); + tls_fls_read(offset, (u8 *)param, sizeof(struct tls_sys_param)); + offset += sizeof(struct tls_sys_param); + tls_fls_read(offset, (u8 *)&crc32, 4); + if(crc32 != get_crc32((u8 *)param, sizeof(struct tls_sys_param))) + { + TLS_DBGPRT_INFO("user default param crc err =%x!!!\r\n", crc32); + return TLS_PARAM_STATUS_EINVALID; + } + return TLS_PARAM_STATUS_OK; +} + +/********************************************************************************************************** +* Description: This function is used to load the system default parameters. +* +* Arguments : param is the param point +* + +* Returns : +* +* Notes : This function read user defined parameters first, if wrong, all the parameters restore factory settings. +**********************************************************************************************************/ +void tls_param_load_factory_default(void) +{ + struct tls_sys_param *param = &sram_param; + + if (param == NULL) {return;} + + TLS_DBGPRT_INFO("load the default parameters.\n"); + + memset(param, 0, sizeof(*param)); + MEMCPY(¶m->hardware_version, factory_default_hardware, 8); + + param->wireless_region = TLS_PARAM_REGION_1_BG_BAND; + param->channel = 1; + param->channellist = 0x3FFF; +#if TLS_CONFIG_11N + param->wbgr.bg = TLS_PARAM_PHY_11BGN_MIXED; + param->wbgr.max_rate = TLS_PARAM_TX_RATEIDX_MCS5; +#else + param->wbgr.bg = TLS_PARAM_PHY_11BG_MIXED; + param->wbgr.max_rate = TLS_PARAM_TX_RATEIDX_36M; +#endif + param->ssid_broadcast_enable = TLS_PARAM_SSIDBRD_ENABLE; + param->encry = TLS_PARAM_ENCRY_OPEN; +#if 0 //def CONFIG_AP + param->wireless_protocol = TLS_PARAM_IEEE80211_SOFTAP; + tls_efuse_read(TLS_EFUSE_MACADDR_OFFSET, mac_addr, 6); + ssid_len = sprintf((char *)¶m->ssid.ssid, "cuckoo_softap_%02x%02x%02x", mac_addr[3], mac_addr[4], mac_addr[5]); + param->ssid.ssid_len = ssid_len; +#else + param->wireless_protocol = TLS_PARAM_IEEE80211_INFRA; + +#endif + + param->auto_retrycnt = 255; + param->auto_roam = TLS_PARAM_ROAM_DISABLE; + param->auto_powersave = TLS_PARAM_PSM_DISABLE; + param->auto_reconnect = 0; + //param->wps.wps_enable = TLS_PARAM_WPS_DISABLE; + + param->auto_mode = TLS_PARAM_MANUAL_MODE; + param->transparent_trigger_length = 512; + + param->uart_cfg.baudrate = TLS_PARAM_UART_BAUDRATE_B115200; + param->uart_cfg.stop_bits = TLS_PARAM_UART_STOPBITS_1BITS; + param->uart_cfg.parity = TLS_PARAM_UART_PARITY_NONE; + + param->user_port_mode = TLS_PARAM_USR_INTF_LUART; + + param->ipcfg.dhcp_enable = TLS_PARAM_DHCP_ENABLE; + param->ipcfg.ip[0] = 192; + param->ipcfg.ip[1] = 168; + param->ipcfg.ip[2] = 1; + param->ipcfg.ip[3] = 1; + param->ipcfg.netmask[0] = 255; + param->ipcfg.netmask[1] = 255; + param->ipcfg.netmask[2] = 255; + param->ipcfg.netmask[3] = 0; + param->ipcfg.gateway[0] = 192; + param->ipcfg.gateway[1] = 168; + param->ipcfg.gateway[2] = 1; + param->ipcfg.gateway[3] = 1; + param->ipcfg.dns1[0] = 192; + param->ipcfg.dns1[1] = 168; + param->ipcfg.dns1[2] = 1; + param->ipcfg.dns1[3] = 1; + param->ipcfg.dns2[0] = 192; + param->ipcfg.dns2[1] = 168; + param->ipcfg.dns2[2] = 1; + param->ipcfg.dns2[3] = 1; + + strcpy((char *)param->local_dnsname, "local.winnermicro"); + strcpy((char *)param->local_device_name, "tln13sp01"); + + param->remote_socket_cfg.protocol = TLS_PARAM_SOCKET_TCP; + param->remote_socket_cfg.client_or_server = TLS_PARAM_SOCKET_SERVER; + param->remote_socket_cfg.port_num = TLS_PARAM_SOCKET_DEFAULT_PORT; + memset(param->remote_socket_cfg.host, 0, 32); + + param->EscapeChar = 0x2b; + param->EscapePeriod = 200; + + param->WebsCfg.AutoRun = 1; + param->WebsCfg.PortNum = 80; + + param->debug_mode = 0; + memset(param->PassWord, '0', 6); + + param->channel4softap = 11; + param->encry4softap = TLS_PARAM_ENCRY_OPEN; + param->ipcfg4softap.dhcp_enable = TLS_PARAM_DHCP_ENABLE; + param->ipcfg4softap.ip[0] = 192; + param->ipcfg4softap.ip[1] = 168; + param->ipcfg4softap.ip[2] = 0; + param->ipcfg4softap.ip[3] = 1; + param->ipcfg4softap.netmask[0] = 255; + param->ipcfg4softap.netmask[1] = 255; + param->ipcfg4softap.netmask[2] = 255; + param->ipcfg4softap.netmask[3] = 0; + param->ipcfg4softap.gateway[0] = 192; + param->ipcfg4softap.gateway[1] = 168; + param->ipcfg4softap.gateway[2] = 0; + param->ipcfg4softap.gateway[3] = 1; + param->ipcfg4softap.dns1[0] = 192; + param->ipcfg4softap.dns1[1] = 168; + param->ipcfg4softap.dns1[2] = 0; + param->ipcfg4softap.dns1[3] = 1; + param->ipcfg4softap.dns2[0] = 0; + param->ipcfg4softap.dns2[1] = 0; + param->ipcfg4softap.dns2[2] = 0; + param->ipcfg4softap.dns2[3] = 0; +#if TLS_CONFIG_SOFTAP_11N + param->wbgr4softap.bg = TLS_PARAM_PHY_11BGN_MIXED; + param->wbgr4softap.max_rate = TLS_PARAM_TX_RATEIDX_36M; +#else + param->wbgr4softap.bg = TLS_PARAM_PHY_11BG_MIXED; + param->wbgr4softap.max_rate = TLS_PARAM_TX_RATEIDX_36M; +#endif + + strcpy(param->sntp_service1, "cn.ntp.org.cn"); + strcpy(param->sntp_service2, "ntp.sjtu.edu.cn"); + strcpy(param->sntp_service3, "us.pool.ntp.org"); +} + +/********************************************************************************************************** +* Description: This function is used to set system parameter. +* +* Arguments : id param id,from TLS_PARAM_ID_SSID to (TLS_PARAM_ID_MAX - 1) +* argc store parameters +* to_flash whether the parameter is written to flash,1 write + +* Returns : TLS_PARAM_STATUS_OK set success +* TLS_PARAM_STATUS_EINVALID invalid param +**********************************************************************************************************/ +int tls_param_set(int id, void *argv, bool to_flash) +{ + int err = 0; + struct tls_sys_param *param = &sram_param; + if ((id < TLS_PARAM_ID_ALL) || (id >= TLS_PARAM_ID_MAX) || (argv == NULL)) {return TLS_PARAM_STATUS_EINVALID;} + + if(updp_mode) + param = &user_default_param; + tls_os_sem_acquire(sys_param_lock, 0); + + err = TLS_PARAM_STATUS_OK; + switch (id) { + case TLS_PARAM_ID_ALL: + MEMCPY(param, argv, sizeof(struct tls_sys_param)); + break; + + case TLS_PARAM_ID_SSID: + MEMCPY(¶m->ssid, argv, sizeof(struct tls_param_ssid)); + break; + + case TLS_PARAM_ID_ENCRY: + param->encry = *((u8 *)argv); + break; + + case TLS_PARAM_ID_KEY: + MEMCPY(¶m->key, argv, sizeof(struct tls_param_key)); + break; + + case TLS_PARAM_ID_IP: + MEMCPY(¶m->ipcfg, argv, sizeof(struct tls_param_ip)); + break; + + case TLS_PARAM_ID_AUTOMODE: + param->auto_mode = *((u8 *)argv); + break; + + case TLS_PARAM_ID_DEFSOCKET: + MEMCPY(¶m->remote_socket_cfg, argv, sizeof(struct tls_param_socket)); + break; + + case TLS_PARAM_ID_BSSID: + MEMCPY(¶m->bssid, argv, sizeof(struct tls_param_bssid)); + break; + + case TLS_PARAM_ID_CHANNEL: + param->channel = *((u8 *)argv); + break; + + case TLS_PARAM_ID_CHANNEL_LIST: + param->channellist = *((u16*)argv); + break; + + case TLS_PARAM_ID_CHANNEL_EN: + param->channel_enable = *((u8 *)argv); + break; + + case TLS_PARAM_ID_COUNTRY_REGION: + param->wireless_region = *((u8 *)argv); + break; + + case TLS_PARAM_ID_WPROTOCOL: + param->wireless_protocol = *((u8 *)argv);; + break; + + case TLS_PARAM_ID_ADHOC_AUTOCREATE: + param->auto_create_adhoc = *((u8 *)argv); + break; + + case TLS_PARAM_ID_ROAMING: + param->auto_roam = *((u8 *)argv); + break; + + case TLS_PARAM_ID_AUTO_RETRY_CNT: + param->auto_retrycnt = *((u8 *)argv); + break; + + case TLS_PARAM_ID_WBGR: + MEMCPY(¶m->wbgr, argv, sizeof(struct tls_param_bgr)); + break; + + case TLS_PARAM_ID_USRINTF: + param->user_port_mode = *((u8 *)argv); + break; + + case TLS_PARAM_ID_AUTO_TRIGGER_LENGTH: + param->transparent_trigger_length = *((u16 *)argv); + break; + + case TLS_PARAM_ID_AUTO_TRIGGER_PERIOD: + param->transparent_trigger_period = *((u16 *)argv); + break; + + case TLS_PARAM_ID_ESCAPE_CHAR: + param->EscapeChar = *((u8 *)argv); + break; + + case TLS_PARAM_ID_ESCAPE_PERIOD: + param->EscapePeriod = *((u16 *)argv); + break; + + case TLS_PARAM_ID_IO_MODE: + param->IoMode = *((u8 *)argv); + break; + + case TLS_PARAM_ID_CMD_MODE: + param->CmdMode = *((u8 *)argv); + break; + + case TLS_PARAM_ID_PASSWORD: + MEMCPY(param->PassWord, (u8 *)argv, sizeof(param->PassWord)); + break; + case TLS_PARAM_ID_WEBS_CONFIG: + param->WebsCfg = *((struct tls_webs_cfg *)argv); + break; + case TLS_PARAM_ID_DEBUG_MODE: + param->debug_mode = *((u32 *)argv);; + break; + + case TLS_PARAM_ID_HARDVERSION: + MEMCPY(¶m->hardware_version, argv, sizeof(struct tls_param_hardware_version)); + break; + + case TLS_PARAM_ID_BRDSSID: + param->ssid_broadcast_enable = *((u8 *)argv); + break; + + case TLS_PARAM_ID_DNSNAME: + strcpy((char *)param->local_dnsname, (char *)argv); + break; + + case TLS_PARAM_ID_DEVNAME: + strcpy((char *)param->local_device_name, argv); + break; + + case TLS_PARAM_ID_PSM: + param->auto_powersave = *((u8 *)argv); + break; + + case TLS_PARAM_ID_ORAY_CLIENT: + MEMCPY(¶m->oray_client_setting, argv, sizeof(struct tls_param_oray_client)); + break; + + case TLS_PARAM_ID_UPNP: + param->upnp_enable = *((u8 *)argv); + break; + + case TLS_PARAM_ID_UART: + MEMCPY(¶m->uart_cfg, argv, sizeof(struct tls_param_uart)); + break; +#if TLS_CONFIG_WPS + case TLS_PARAM_ID_WPS: + MEMCPY(¶m->wps, argv, sizeof(struct tls_param_wps)); + break; +#endif + case TLS_PARAM_ID_ONESHOT_CFG: + param->oneshotflag = *((u8 *)argv); + break; + case TLS_PARAM_ID_SHA1: + MEMCPY(¶m->psk, (u8 *)argv, sizeof(struct tls_param_sha1)); + + break; + case TLS_PARAM_ID_ORIGIN_KEY: + MEMCPY( ¶m->original_key, (u8*)argv, sizeof(struct tls_param_original_key)); + break; + case TLS_PARAM_ID_ORIGIN_SSID: + MEMCPY( ¶m->original_ssid, (u8*)argv, sizeof(struct tls_param_ssid)); + break; + case TLS_PARAM_ID_AUTO_RECONNECT: + param->auto_reconnect = *((u8 *)argv); + break; + case TLS_PARAM_ID_QUICK_CONNECT: + MEMCPY(¶m->quick_connect, argv, sizeof(struct tls_param_quick_connect)); + break; + case TLS_PARAM_ID_KEY_CHANGE: + param->key_changed = *((u8 *)argv); + break; + case TLS_PARAM_ID_SSID_CHANGE: + param->ssid_changed = *((u8 *)argv); + break; +#if TLS_CONFIG_AP + case TLS_PARAM_ID_SOFTAP_SSID: + MEMCPY(¶m->apsta_ssid, argv, sizeof(struct tls_param_ssid)); + break; + case TLS_PARAM_ID_SOFTAP_PSK: + MEMCPY(¶m->apsta_psk, argv, sizeof(struct tls_param_sha1)); + break; + case TLS_PARAM_ID_SOFTAP_ENCRY: + param->encry4softap = *((u8 *)argv); + break; + case TLS_PARAM_ID_SOFTAP_KEY: + MEMCPY(¶m->key4softap, argv, sizeof(struct tls_param_key)); + break; + case TLS_PARAM_ID_SOFTAP_IP: + MEMCPY(¶m->ipcfg4softap, argv, sizeof(struct tls_param_ip)); + break; + case TLS_PARAM_ID_SOFTAP_CHANNEL: + param->channel4softap = *((u8 *)argv); + break; + case TLS_PARAM_ID_SOFTAP_WBGR: + MEMCPY(¶m->wbgr4softap, argv, sizeof(struct tls_param_bgr)); + break; +#endif + case TLS_PARAM_ID_SNTP_SERVER1: + strncpy(param->sntp_service1, (const char *)argv, strlen(argv)+1); + break; + case TLS_PARAM_ID_SNTP_SERVER2: + strncpy(param->sntp_service2, (const char *)argv, strlen(argv)+1); + break; + case TLS_PARAM_ID_SNTP_SERVER3: + strncpy(param->sntp_service3, (const char *)argv, strlen(argv)+1); + break; + case TLS_PARAM_ID_TEM_OFFSET: + MEMCPY(¶m->params_tem, argv, sizeof(struct tls_param_tem_offset)); + break; + + default: + TLS_DBGPRT_WARNING("invalid parameter id - %d!\n", id); + err = TLS_PARAM_STATUS_EINVALIDID; + goto exit; + } + + if (to_flash && !updp_mode) { + err = param_to_flash(id, -1, -1); + TLS_DBGPRT_INFO("write the parameter to spi flash - %d.\n", err); + } +exit: + tls_os_sem_release(sys_param_lock); + + return err; +} + +/********************************************************************************************************** +* Description: This function is used to get system parameter. +* +* Arguments : id param id,from TLS_PARAM_ID_SSID to (TLS_PARAM_ID_MAX - 1) +* argc store parameters +* from_flash whether the parameter is readed from flash,1 read from flash + +* Returns : TLS_PARAM_STATUS_OK success +* TLS_PARAM_STATUS_EINVALID invalid param +**********************************************************************************************************/ +int tls_param_get(int id, void *argv, bool from_flash) +{ + int err; + struct tls_sys_param *src; + + if ((id < TLS_PARAM_ID_ALL) || (id >= TLS_PARAM_ID_MAX)) {return TLS_PARAM_STATUS_EINVALID;} + + err = TLS_PARAM_STATUS_OK; + if (from_flash) {src = &flash_param.parameters;} + else {src = &sram_param;} + + tls_os_sem_acquire(sys_param_lock, 0); + + switch (id) { + case TLS_PARAM_ID_ALL: + MEMCPY(argv, src, sizeof(struct tls_sys_param)); + break; + + case TLS_PARAM_ID_SSID: + MEMCPY(argv, &src->ssid, sizeof(struct tls_param_ssid)); + break; + + case TLS_PARAM_ID_ENCRY: + *((u8 *)argv) = src->encry; + break; + + case TLS_PARAM_ID_KEY: + MEMCPY(argv, &src->key, sizeof(struct tls_param_key)); + break; + + case TLS_PARAM_ID_IP: + MEMCPY(argv, &src->ipcfg, sizeof(struct tls_param_ip)); + break; + + case TLS_PARAM_ID_AUTOMODE: + *((u8 *)argv) = src->auto_mode; + break; + + case TLS_PARAM_ID_DEFSOCKET: + MEMCPY(argv, &src->remote_socket_cfg, sizeof(struct tls_param_socket)); + break; + + case TLS_PARAM_ID_BSSID: + MEMCPY(argv, &src->bssid, sizeof(struct tls_param_bssid)); + break; + + case TLS_PARAM_ID_CHANNEL: + *((u8 *)argv) = src->channel; + break; + case TLS_PARAM_ID_CHANNEL_LIST: + *((u16*)argv) = src->channellist; + break; + + case TLS_PARAM_ID_CHANNEL_EN: + *((u8 *)argv) = src->channel_enable; + break; + + case TLS_PARAM_ID_COUNTRY_REGION: + *((u8 *)argv) = src->wireless_region; + break; + + case TLS_PARAM_ID_WPROTOCOL: + *((u8 *)argv) = src->wireless_protocol; + break; + + case TLS_PARAM_ID_ADHOC_AUTOCREATE: + *((u8 *)argv) = src->auto_create_adhoc; + break; + + case TLS_PARAM_ID_ROAMING: + *((u8 *)argv) = src->auto_roam; + break; + + case TLS_PARAM_ID_AUTO_RETRY_CNT: + *((u8 *)argv) = src->auto_retrycnt; + break; + + case TLS_PARAM_ID_WBGR: + MEMCPY(argv, &src->wbgr, sizeof(struct tls_param_bgr)); + break; + + case TLS_PARAM_ID_USRINTF: + *((u8 *)argv) = src->user_port_mode; + break; + + case TLS_PARAM_ID_AUTO_TRIGGER_LENGTH: + *((u16 *)argv) = src->transparent_trigger_length; + break; + case TLS_PARAM_ID_AUTO_TRIGGER_PERIOD: + *((u16 *)argv) = src->transparent_trigger_period; + break; + + case TLS_PARAM_ID_ESCAPE_CHAR: + *((u8 *)argv) = src->EscapeChar; + break; + + case TLS_PARAM_ID_ESCAPE_PERIOD: + *((u16 *)argv) = src->EscapePeriod; + break; + case TLS_PARAM_ID_IO_MODE: + *((u8 *)argv) = src->IoMode; + break; + case TLS_PARAM_ID_CMD_MODE: + *((u8 *)argv) = src->CmdMode; + break; + + case TLS_PARAM_ID_PASSWORD: + MEMCPY((u8 *)argv, src->PassWord, sizeof(src->PassWord)); + break; + case TLS_PARAM_ID_WEBS_CONFIG: + *((struct tls_webs_cfg *)argv) = src->WebsCfg; + break; + + + case TLS_PARAM_ID_DEBUG_MODE: + *((u32 *)argv) = src->debug_mode; + break; + + case TLS_PARAM_ID_HARDVERSION: + MEMCPY(argv, &src->hardware_version, sizeof(struct tls_param_hardware_version)); + break; + + case TLS_PARAM_ID_BRDSSID: + *((u8 *)argv) = src->ssid_broadcast_enable; + break; + + case TLS_PARAM_ID_DNSNAME: + strcpy((char *)argv, (char *)src->local_dnsname); + break; + + case TLS_PARAM_ID_DEVNAME: + strcpy((char *)argv, (char *)src->local_device_name); + break; + + case TLS_PARAM_ID_PSM: + *((u8 *)argv) = src->auto_powersave; + break; + + case TLS_PARAM_ID_ORAY_CLIENT: + MEMCPY(argv, &src->oray_client_setting, sizeof(struct tls_param_oray_client)); + break; + + case TLS_PARAM_ID_UPNP: + *((u8 *)argv) = src->upnp_enable; + break; + + case TLS_PARAM_ID_UART: + MEMCPY(argv, &src->uart_cfg, sizeof(struct tls_param_uart)); + break; +#if TLS_CONFIG_WPS + case TLS_PARAM_ID_WPS: + MEMCPY(argv, &src->wps, sizeof(struct tls_param_wps)); + break; +#endif + case TLS_PARAM_ID_ONESHOT_CFG: + *((u8 *)argv) = src->oneshotflag; + break; + case TLS_PARAM_ID_SHA1: + MEMCPY(argv, &src->psk, sizeof(struct tls_param_sha1)); + + break; + case TLS_PARAM_ID_ORIGIN_KEY: + MEMCPY(argv, &src->original_key, sizeof(struct tls_param_original_key)); + break; + case TLS_PARAM_ID_ORIGIN_SSID: + MEMCPY(argv, &src->original_ssid, sizeof(struct tls_param_ssid)); + break; + case TLS_PARAM_ID_AUTO_RECONNECT: + *((u8 *)argv) = src->auto_reconnect; + break; + case TLS_PARAM_ID_QUICK_CONNECT: + MEMCPY(argv, &src->quick_connect, sizeof(struct tls_param_quick_connect)); + break; + case TLS_PARAM_ID_KEY_CHANGE: + *((u8 *)argv) = src->key_changed; + break; + case TLS_PARAM_ID_SSID_CHANGE: + *((u8 *)argv) = src->ssid_changed; + break; +#if TLS_CONFIG_AP + case TLS_PARAM_ID_SOFTAP_SSID: + MEMCPY(argv, &src->apsta_ssid, sizeof(struct tls_param_ssid)); + break; + case TLS_PARAM_ID_SOFTAP_PSK: + MEMCPY(argv, &src->apsta_psk, sizeof(struct tls_param_sha1)); + break; + case TLS_PARAM_ID_SOFTAP_ENCRY: + *((u8 *)argv) = src->encry4softap; + break; + case TLS_PARAM_ID_SOFTAP_KEY: + MEMCPY(argv, &src->key4softap, sizeof(struct tls_param_key)); + break; + case TLS_PARAM_ID_SOFTAP_IP: + MEMCPY(argv, &src->ipcfg4softap, sizeof(struct tls_param_ip)); + break; + case TLS_PARAM_ID_SOFTAP_CHANNEL: + *((u8 *)argv) = src->channel4softap; + break; + case TLS_PARAM_ID_SOFTAP_WBGR: + MEMCPY(argv, &src->wbgr4softap, sizeof(struct tls_param_bgr)); + break; +#endif + + case TLS_PARAM_ID_SNTP_SERVER1: + strncpy(argv, src->sntp_service1, strlen(src->sntp_service1)+1); + break; + case TLS_PARAM_ID_SNTP_SERVER2: + strncpy(argv, src->sntp_service2, strlen(src->sntp_service2)+1); + break; + case TLS_PARAM_ID_SNTP_SERVER3: + strncpy(argv, src->sntp_service3, strlen(src->sntp_service3)+1); + break; + case TLS_PARAM_ID_TEM_OFFSET: + MEMCPY(argv, &src->params_tem, sizeof(struct tls_param_tem_offset)); + break; + + default: + TLS_DBGPRT_WARNING("invalid parameter id - %d!\n", id); + err = TLS_PARAM_STATUS_EINVALIDID; + break; + } + + tls_os_sem_release(sys_param_lock); + + return err; +} + +/********************************************************************************************************** +* Description: This function is used to write parameter to flash. +* +* Arguments : id param id,from TLS_PARAM_ID_ALL to (TLS_PARAM_ID_MAX - 1) +* +* Returns : * Returns : TLS_PARAM_STATUS_OK success +* TLS_PARAM_STATUS_EINVALID invalid param +* TLS_PARAM_STATUS_EIO error +**********************************************************************************************************/ +int tls_param_to_flash(int id) +{ + int err = 0; + + + tls_os_sem_acquire(sys_param_lock, 0); + if (TLS_PARAM_ID_ALL == id) + { + if (0 == memcmp(&sram_param, &flash_param.parameters,sizeof(sram_param))) + { + tls_os_sem_release(sys_param_lock); + return TLS_FLS_STATUS_OK; + } + } + + err = param_to_flash(id, -1, -1); + + tls_os_sem_release(sys_param_lock); + + return err; +} + + +/********************************************************************************************************** +* Description: This function is used to load default parametes to memory. +* +* Arguments : +* +* Returns : +* +* Notes : This function read user defined parameters first, if wrong, all the parameters restore factory settings. +**********************************************************************************************************/ +int tls_param_to_default(void) +{ + int err = 0; + tls_param_load_factory_default(); + err = param_to_flash(TLS_PARAM_ID_ALL, 1, 0); + if(err) + return err; + err = param_to_flash(TLS_PARAM_ID_ALL, 1, 1); + flash_param.magic = 0; + + return err; +} +#if 0 +#define WM_BACKUP_PARM_ADDR 0xF0000 + +/********************************************************************************************************** +* Description: This function is used to recovery the parameters from the backup area to the parameter area, +* and read to memory. +* +* Arguments : +* +* Returns : +**********************************************************************************************************/ +void tls_restore_param_from_backup(void) +{ + u8 buff[32]; + + tls_fls_read(WM_BACKUP_PARM_ADDR, (u8 *)&flash_param, 408); + if(TLS_PARAM_MAGIC == flash_param.magic) + { + u8 tmp[5]; + + TLS_DBGPRT_INFO("tls_restore_param_from_backup...\n"); + MEMCPY(&sram_param, &flash_param.parameters, 408); + param_to_flash(TLS_PARAM_ID_ALL, 1, 0); + param_to_flash(TLS_PARAM_ID_ALL, 1, 1); + flash_param.magic = 0; + memset(tmp, 0xff, 5); + tls_fls_write(WM_BACKUP_PARM_ADDR, tmp, 4); + + tls_fls_read(WM_BACKUP_PARM_ADDR + 408, buff, 22);// 4 + 6 + 12 +// tls_efuse_write(TLS_EFUSE_LOLEAKAGE_OFFSET, &buff[0], 4); +// tls_efuse_write(TLS_EFUSE_MACADDR_OFFSET, &buff[4], 6); +// tls_efuse_write(TLS_EFUSE_TXGAIN_BG_OFFSET, &buff[10], 12); + } +} +#endif +/********************************************************************************************************** +* Description: This function is used to modify user default parameters,then write to flash. +* +* Arguments : +* +* Returns : +**********************************************************************************************************/ +int tls_param_save_user(struct tls_user_param *user_param) +{ + struct tls_sys_param *param = &user_default_param; + + + param->wireless_protocol = user_param->wireless_protocol; + param->auto_mode = user_param->auto_mode; + param->uart_cfg.baudrate = user_param->baudrate; + param->user_port_mode = user_param->user_port_mode; + param->ipcfg.dhcp_enable = user_param->dhcp_enable; + param->auto_powersave = user_param->auto_powersave; + + MEMCPY(param->ipcfg.ip, user_param->ip, 4); + MEMCPY(param->ipcfg.netmask, user_param->netmask, 4); + MEMCPY(param->ipcfg.gateway, user_param->gateway, 4); + MEMCPY(param->ipcfg.dns1, user_param->dns, 4); + MEMCPY(param->ipcfg.dns2, user_param->dns, 4); + param->remote_socket_cfg.protocol = user_param->socket_protocol; + param->remote_socket_cfg.client_or_server = user_param->socket_client_or_server; + param->remote_socket_cfg.port_num = user_param->socket_port_num; + MEMCPY(param->remote_socket_cfg.host, user_param->socket_host, 32); + MEMCPY(param->PassWord, user_param->PassWord, 6); + + MEMCPY(¶m->hardware_version, factory_default_hardware, 8); + param->wireless_region = TLS_PARAM_REGION_1_BG_BAND; + param->channel = 1; + param->channellist = 0x3FFF; + param->wbgr.bg = TLS_PARAM_PHY_11BG_MIXED; + param->wbgr.max_rate = TLS_PARAM_TX_RATEIDX_36M; + param->ssid_broadcast_enable = TLS_PARAM_SSIDBRD_ENABLE; + param->encry = TLS_PARAM_ENCRY_OPEN; + + param->auto_retrycnt = 255; + param->auto_roam = TLS_PARAM_ROAM_DISABLE; + //param->wps.wps_enable = TLS_PARAM_WPS_DISABLE; + + param->transparent_trigger_length = 512; + + param->uart_cfg.stop_bits = TLS_PARAM_UART_STOPBITS_1BITS; + param->uart_cfg.parity = TLS_PARAM_UART_PARITY_NONE; + + strcpy((char *)param->local_dnsname, "local.winnermicro"); + strcpy((char *)param->local_device_name, "tln13sp01"); + + param->EscapeChar = 0x2b; + param->EscapePeriod = 200; + + param->debug_mode = 0; + + tls_param_save_user_default(); + + return TLS_PARAM_STATUS_OK; +} + +/********************************************************************************************************** +* Description: This function is used to save user parameters to the flash. +* +* Arguments : +* +* Returns : +**********************************************************************************************************/ +int tls_param_save_user_default(void) +{ + u32 magic, crc32, offset; + offset = TLS_FLASH_PARAM_DEFAULT; + magic = TLS_USER_MAGIC; + TLS_DBGPRT_INFO("=====>\n"); + tls_fls_write(offset, (u8 *)&magic, 4); + offset += 4; + tls_fls_write(offset, (u8 *)&user_default_param, sizeof(struct tls_sys_param)); + offset += sizeof(struct tls_sys_param); + crc32 = get_crc32((u8 *)&user_default_param, sizeof(struct tls_sys_param)); + tls_fls_write(offset, (u8 *)&crc32, 4); + return TLS_PARAM_STATUS_OK; +} + +u8 tls_param_get_updp_mode(void) +{ + return updp_mode; +} + +void tls_param_set_updp_mode(u8 mode) +{ + updp_mode = mode; +} + diff --git a/sdk/Platform/Common/Params/wm_param.h b/sdk/Platform/Common/Params/wm_param.h new file mode 100644 index 0000000..1cddaf8 --- /dev/null +++ b/sdk/Platform/Common/Params/wm_param.h @@ -0,0 +1,124 @@ +/***************************************************************************** +* +* File Name : tls_param.h +* +* Description: param manager Module +* +* Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-6-12 +*****************************************************************************/ +#ifndef TLS_PARAM_H +#define TLS_PARAM_H +#include "wm_type_def.h" + + + +struct tls_param_quick_connect{ + bool quick_connect_en; + u8 chanId; +}; + +struct tls_sys_param { + struct tls_param_hardware_version hardware_version; + + struct tls_param_bssid bssid; + struct tls_param_ssid ssid; + u8 channel_enable; + u8 channel; + u8 wireless_region; + u8 encry; + u8 wireless_protocol; + u8 auto_create_adhoc; + u8 auto_retrycnt; + u8 auto_roam; + u8 auto_powersave; + u8 ssid_broadcast_enable; + u16 channellist; + struct tls_param_key key; + struct tls_param_bgr wbgr; + struct tls_param_wps wps; + + struct tls_param_ip ipcfg; + + u8 local_dnsname[32]; + u8 local_device_name[32]; + + struct tls_param_oray_client oray_client_setting; + + u8 upnp_enable; + + u8 always_online; + + u8 user_port_mode; + + u8 res_2; + + struct tls_param_uart uart_cfg; + + u8 auto_mode; + u8 res_3; + u16 transparent_trigger_length; + u16 transparent_trigger_period; + + struct tls_param_socket remote_socket_cfg; + + u32 debug_mode; + u32 res_4[2]; /* reserv and mark the end */ + + /*escape char*/ + u8 EscapeChar; + u8 EscReserved; + u16 EscapePeriod; /* unit: ms */ + + u8 IoMode; + u8 CmdMode; + u8 PassWord[6]; + + struct tls_webs_cfg WebsCfg; + u8 oneshotflag; + u8 oneshotres[2]; + struct tls_param_sha1 psk; + struct tls_param_original_key original_key; + struct tls_param_ssid original_ssid; + u8 auto_reconnect; + u8 res_5[3]; + + struct tls_param_quick_connect quick_connect; + + u8 key_changed; + u8 ssid_changed; + + struct tls_param_ssid apsta_ssid; + struct tls_param_sha1 apsta_psk; + u8 res_6[1]; + u8 channel4softap; + u8 encry4softap; + struct tls_param_key key4softap; + struct tls_param_ip ipcfg4softap; + struct tls_param_bgr wbgr4softap; + + char sntp_service1[32]; + char sntp_service2[32]; + char sntp_service3[32]; + struct tls_param_tem_offset params_tem; +}; + +struct tls_param_flash { + u32 magic; + u16 partition_num; + u16 modify_count; + u32 resv_1; + u16 resv_2; + u16 length; + struct tls_sys_param parameters; + u32 crc32; +}; + + +#endif /* WM_PARAM_H */ + + diff --git a/sdk/Platform/Common/crypto/wm_crypto_hard.c b/sdk/Platform/Common/crypto/wm_crypto_hard.c new file mode 100644 index 0000000..bd46ca9 --- /dev/null +++ b/sdk/Platform/Common/crypto/wm_crypto_hard.c @@ -0,0 +1,2696 @@ +#include +#include +#include +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_crypto_hard.h" +#include "wm_internal_flash.h" +#include "wm_pmu.h" +#include "sha1.h" +#include "md5.h" + +//#define TEST_ALL_CRYPTO +#undef DIGIT_BIT +#define DIGIT_BIT 28 + +#define SOFT_RESET_RC4 25 +#define SOFT_RESET_AES 26 +#define SOFT_RESET_DES 27 + +#define RNG_SWITCH 28 +#define RNG_LOAD_SEED 29 +#define RNG_START 30 + +#define STORE32L(x, y) { \ +unsigned long __t = (x); memcpy(y, &__t, 4); \ +} + +volatile u8 crypto_complete = 0; +#if 1 +typedef s32 psPool_t; +#include "libtommath.h" +#define pstm_set(a, b) mp_set((mp_int *)a, b) +#define pstm_init(pool, a) wpa_mp_init((mp_int *)a) +#define pstm_count_bits(a) mp_count_bits((mp_int *)a) +#define pstm_init_for_read_unsigned_bin(pool, a, len) mp_init_for_read_unsigned_bin((mp_int *)a, len) +#define pstm_read_unsigned_bin(a, b, c) mp_read_unsigned_bin((mp_int *)a, b, c) +#define pstm_copy(a, b) mp_copy((mp_int *)a, (mp_int *)b) +#define pstm_clear(a) mp_clear((mp_int *)a) +#define pstm_clamp(a) mp_clamp((mp_int *)a) +#define pstm_mulmod(pool, a, b, c, d) mp_mulmod((mp_int *)a, (mp_int *)b, (mp_int *)c, (mp_int *)d) +#define pstm_exptmod(pool, G, X, P, Y) mp_exptmod((mp_int *)G, (mp_int *)X, (mp_int *)P, (mp_int *)Y) +#define pstm_reverse mp_reverse +#define pstm_cmp mp_cmp +#define pstm_to_unsigned_bin_nr(pool, a, b) mp_to_unsigned_bin_nr((mp_int *)a, (unsigned char *)b) +#endif + + +void RSA_IRQHandler(void) +{ + RSACON = 0x00; + crypto_complete = 1; +} +void CRYPTION_IRQHandler(void) +{ + tls_reg_write32(HR_CRYPTO_SEC_STS, 0x10000); + crypto_complete = 1; +} + +static void tls_crypto_clear_32reg(u32 base, u32 len) +{ + u32 i; + + for (i = 0; i < len; i += 4) + { + tls_reg_write32(base + i, 0); + } + + return; +} + +static void tls_crypto_write_32reg(u32 base, void *data, u32 len) +{ + u32 i; + u32 *value; + + value = (u32 *)data; + + for (i = 0; i < len; i += 4) + { + tls_reg_write32(base + i, *value++); + } + + return; +} + +static void tls_crypto_read_32reg(void *data, u32 base, u32 len) +{ + u32 i; + u32 *value; + + value = (u32 *)data; + + for (i = 0; i < len; i += 4) + { + *value = tls_reg_read32(base + i); + value++; + } + + return; +} + +static int16 pstm_get_bit (hstm_int * a, int16 idx) +{ + int16 r; + int16 n = idx / DIGIT_BIT; + int16 m = idx % DIGIT_BIT; + + if (a->used <= 0) { + return 0; + } + + r = (a->dp[n] >> m) & 0x01; + return r; +} + +u32 Reflect(u32 ref,u8 ch) +{ + int i; + u32 value = 0; + for( i = 1; i < ( ch + 1 ); i++ ) + { + if( ref & 1 ) + value |= 1 << ( ch - i ); + ref >>= 1; + } + return value; +} + +/** + * @brief This function is used to stop random produce. + * + * @param[in] None + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_random_stop(void) +{ + unsigned int sec_cfg, val; + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + sec_cfg = val & ~(1 << RNG_START); + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return ERR_CRY_OK; +} + +/** + * @brief This function initializes random digit seed and BIT number. + * + * @param[in] seed The random digit seed. + * @param[in] rng_switch The random digit bit number. (0: 16bit 1:32bit) + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_random_init(u32 seed, CRYPTO_RNG_SWITCH rng_switch) +{ + unsigned int sec_cfg; + tls_crypto_random_stop(); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + tls_reg_write32(HR_CRYPTO_KEY0, seed); + sec_cfg = (rng_switch << RNG_SWITCH) | (1 << RNG_LOAD_SEED) | (1 << RNG_START); + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + return ERR_CRY_OK; +} + +/** + * @brief This function is used to get random digit content. + * + * @param[in] out Pointer to the output of random digit. + * @param[in] len The random digit bit number will output. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_random_bytes(unsigned char *out, u32 len) +{ + unsigned int val; + uint32 inLen = len; + int randomBytes = 2; + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + randomBytes = val & (1 << RNG_SWITCH) ? 4 : 2; + while(inLen > 0) + { + val = tls_reg_read32(HR_CRYPTO_RNG_RESULT); + if(inLen >= randomBytes) + { + memcpy(out, (char*)&val, randomBytes); + out += randomBytes; + inLen -= randomBytes; + } + else + { + memcpy(out, (char*)&val, inLen); + inLen = 0; + } + } + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return ERR_CRY_OK; +} + +/** + * @brief This function initializes a RC4 encryption algorithm, + * i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] key Pointer to the key. + * @param[in] keylen the length of key. + * + * @retval 0 success + * @retval other failed + + * + * @note The first parameter ctx must be a structure which is allocated externally. + * And all of Context parameters in the initializing methods should be allocated externally too. + */ +int tls_crypto_rc4_init(psCipherContext_t * ctx, const unsigned char *key, u32 keylen) +{ + if(keylen != 16) + return ERR_FAILURE; + memcpy(ctx->arc4.state, key, keylen); + ctx->arc4.byteCount = keylen; + + return ERR_CRY_OK; +} + + +/** + * @brief This function encrypts a variable length data stream according to RC4. + * The RC4 algorithm it generates a "keystream" which is simply XORed with the plaintext to produce the ciphertext stream. + * Decryption is exactly the same as encryption. This function also decrypts a variable length data stream according to RC4. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] in Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length. + * @param[in] out Pointer to the resulting ciphertext data stream. + * @param[in] len Length of the plaintext data stream in octets. + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned, and it needs to operate in words, not in bytes. + */ +int tls_crypto_rc4(psCipherContext_t * ctx, unsigned char *in, unsigned char *out, u32 len) +{ + unsigned int sec_cfg, val; + unsigned char *key = ctx->arc4.state; + u32 keylen = ctx->arc4.byteCount; + //memset((void *)HR_CRYPTO_KEY0, 0, 16); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + tls_crypto_clear_32reg(HR_CRYPTO_KEY0, 16); + //memcpy((void *)HR_CRYPTO_KEY0, key, keylen); + tls_crypto_write_32reg(HR_CRYPTO_KEY0, key, keylen); + tls_reg_write32(HR_CRYPTO_SRC_ADDR, (unsigned int)in); + tls_reg_write32(HR_CRYPTO_DEST_ADDR, (unsigned int)out); + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + sec_cfg = (val & 0xF0000000) | (CRYPTO_METHOD_RC4 << 16) | (1 << SOFT_RESET_RC4) | (len & 0xFFFF); + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + tls_reg_write32(HR_CRYPTO_SEC_CTRL, 0x1);//start crypto + while (!crypto_complete) + { + + } + crypto_complete = 0; + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return ERR_CRY_OK; +} + + +/** + * @brief This function initializes a AES encryption algorithm, i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] IV Pointer to the Initialization Vector + * @param[in] key Pointer to the key. + * @param[in] keylen the length of key. + * @param[in] cbc the encryption mode, AES supports ECB/CBC/CTR modes. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_aes_init(psCipherContext_t * ctx, const unsigned char *IV, const unsigned char *key, u32 keylen, CRYPTO_MODE cbc) +{ + int x = 0; + if (keylen != 16) + return ERR_FAILURE; + + memcpy(ctx->aes.key.eK, key, keylen); + ctx->aes.key.Nr = cbc; + ctx->aes.blocklen = 16; + for (x = 0; x < ctx->aes.blocklen; x++) { + ctx->aes.IV[x] = IV[x]; + } + return ERR_CRY_OK; +} + +/** + * @brief This function encrypts or decrypts a variable length data stream according to AES. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] in Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length. + * @param[in] out Pointer to the resulting ciphertext data stream. + * @param[in] len Length of the plaintext data stream in octets. + * @param[in] dec The cryption way which indicates encryption or decryption. + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned, and it needs to operate in words, not in bytes. + */ +int tls_crypto_aes_encrypt_decrypt(psCipherContext_t * ctx, unsigned char *in, unsigned char *out, u32 len, CRYPTO_WAY dec) +{ + unsigned int sec_cfg, val; + u32 keylen = 16; + unsigned char *key = (unsigned char *)ctx->aes.key.eK; + unsigned char *IV = ctx->aes.IV; + CRYPTO_MODE cbc = (CRYPTO_MODE)(ctx->aes.key.Nr & 0xFF); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + //memset((void *)HR_CRYPTO_KEY0, 0, 16); + tls_crypto_clear_32reg(HR_CRYPTO_KEY0, 16); + + //memcpy((void *)HR_CRYPTO_KEY0, key, keylen); + tls_crypto_write_32reg(HR_CRYPTO_KEY0, key, keylen); + + //memset((void *)HR_CRYPTO_IV0, 0, 8); + tls_crypto_clear_32reg(HR_CRYPTO_IV0, 8); + + //memset((void *)HR_CRYPTO_IV1, 0, 8); + tls_crypto_clear_32reg(HR_CRYPTO_IV1, 8); + + //memcpy((void *)HR_CRYPTO_IV0, IV, 8); + tls_crypto_write_32reg(HR_CRYPTO_IV0, IV, 8); + + //memcpy((void *)HR_CRYPTO_IV1, IV+8, 8); + tls_crypto_write_32reg(HR_CRYPTO_IV1, IV+8, 8); + + tls_reg_write32(HR_CRYPTO_SRC_ADDR, (unsigned int)in); + tls_reg_write32(HR_CRYPTO_DEST_ADDR, (unsigned int)out); + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + sec_cfg = (val & 0xF0000000) | (CRYPTO_METHOD_AES << 16) | (1 << SOFT_RESET_AES) |(dec << 20) | (cbc << 21) | (len & 0xFFFF); + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + tls_reg_write32(HR_CRYPTO_SEC_CTRL, 0x1);//start crypto + while (!crypto_complete) + { + + } + crypto_complete = 0; + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return ERR_CRY_OK; +} + +/** + * @brief This function initializes a 3DES encryption algorithm, i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] IV Pointer to the Initialization Vector + * @param[in] key Pointer to the key. + * @param[in] keylen the length of key. + * @param[in] cbc the encryption mode, 3DES supports ECB/CBC modes. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_3des_init(psCipherContext_t * ctx, const unsigned char *IV, const unsigned char *key, u32 keylen, CRYPTO_MODE cbc) +{ + unsigned int x; + if (keylen != DES3_KEY_LEN) + return ERR_FAILURE; + + memcpy(ctx->des3.key.ek[0], key, keylen); + ctx->des3.key.ek[1][0] = cbc; + ctx->des3.blocklen = DES3_IV_LEN; + for (x = 0; x < ctx->des3.blocklen; x++) { + ctx->des3.IV[x] = IV[x]; + } + + return ERR_CRY_OK; +} + +/** + * @brief This function encrypts or decrypts a variable length data stream according to 3DES. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] in Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length. + * @param[in] out Pointer to the resulting ciphertext data stream. + * @param[in] len Length of the plaintext data stream in octets. + * @param[in] dec The cryption way which indicates encryption or decryption. + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned, and it needs to operate in words, not in bytes. + */ +int tls_crypto_3des_encrypt_decrypt(psCipherContext_t * ctx, unsigned char *in, unsigned char *out, u32 len, CRYPTO_WAY dec) +{ + unsigned int sec_cfg, val; + u32 keylen = DES3_KEY_LEN; + unsigned char *key = (unsigned char *)(unsigned char *)ctx->des3.key.ek[0]; + unsigned char *IV = ctx->des3.IV; + CRYPTO_MODE cbc = (CRYPTO_MODE)(ctx->des3.key.ek[1][0] & 0xFF); + //memset((void *)HR_CRYPTO_KEY0, 0, DES3_KEY_LEN); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + tls_crypto_clear_32reg(HR_CRYPTO_KEY0, DES3_KEY_LEN); + //memcpy((void *)HR_CRYPTO_KEY0, key, keylen); + tls_crypto_write_32reg(HR_CRYPTO_KEY0, key, keylen); + //memset((void *)HR_CRYPTO_IV0, 0, DES3_IV_LEN); + tls_crypto_clear_32reg(HR_CRYPTO_IV0, DES3_IV_LEN); + //memcpy((void *)HR_CRYPTO_IV0, IV, DES3_IV_LEN); + tls_crypto_write_32reg(HR_CRYPTO_IV0, IV, DES3_IV_LEN); + tls_reg_write32(HR_CRYPTO_SRC_ADDR, (unsigned int)in); + tls_reg_write32(HR_CRYPTO_DEST_ADDR, (unsigned int)out); + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + sec_cfg = (val & 0xF0000000) |(CRYPTO_METHOD_3DES << 16) | (1 << SOFT_RESET_DES) | (dec << 20) | (cbc << 21) | (len & 0xFFFF); + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + tls_reg_write32(HR_CRYPTO_SEC_CTRL, 0x1);//start crypto + while (!crypto_complete) + { + + } + crypto_complete = 0; + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return ERR_CRY_OK; +} + + +/** + * @brief This function initializes a DES encryption algorithm, i.e. fills the psCipherContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] IV Pointer to the Initialization Vector + * @param[in] key Pointer to the key. + * @param[in] keylen the length of key. + * @param[in] cbc the encryption mode, DES supports ECB/CBC modes. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_des_init(psCipherContext_t * ctx, const unsigned char *IV, const unsigned char *key, u32 keylen, CRYPTO_MODE cbc) +{ + unsigned int x; + if (keylen != DES_KEY_LEN) + return ERR_FAILURE; + memcpy(ctx->des3.key.ek[0], key, keylen); + ctx->des3.key.ek[1][0] = cbc; + ctx->des3.blocklen = DES3_IV_LEN; + for (x = 0; x < ctx->des3.blocklen; x++) { + ctx->des3.IV[x] = IV[x]; + } + return ERR_CRY_OK; +} + + +/** + * @brief This function encrypts or decrypts a variable length data stream according to DES. + * + * @param[in] ctx Pointer to the Cipher Context. + * @param[in] in Pointer to the input plaintext data stream(or the encrypted text data stream) of variable length. + * @param[in] out Pointer to the resulting ciphertext data stream. + * @param[in] len Length of the plaintext data stream in octets. + * @param[in] dec The cryption way which indicates encryption or decryption. + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned, and it needs to operate in words, not in bytes. + */ +int tls_crypto_des_encrypt_decrypt(psCipherContext_t * ctx, unsigned char *in, unsigned char *out, u32 len, CRYPTO_WAY dec) +{ + unsigned int sec_cfg, val; + u32 keylen = DES_KEY_LEN; + unsigned char *key = (unsigned char *)ctx->des3.key.ek[0]; + unsigned char *IV = ctx->des3.IV; + CRYPTO_MODE cbc = (CRYPTO_MODE)(ctx->des3.key.ek[1][0] & 0xFF); + //memset((void *)HR_CRYPTO_KEY0, 0, DES_KEY_LEN); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + tls_crypto_clear_32reg(HR_CRYPTO_KEY0, DES_KEY_LEN); + //memcpy((void *)HR_CRYPTO_KEY0, key, keylen); + tls_crypto_write_32reg(HR_CRYPTO_KEY0, key, keylen); + //memset((void *)HR_CRYPTO_IV0, 0, DES3_IV_LEN); + tls_crypto_clear_32reg(HR_CRYPTO_IV0, DES3_IV_LEN); + //memcpy((void *)HR_CRYPTO_IV0, IV, DES3_IV_LEN); + tls_crypto_write_32reg(HR_CRYPTO_IV0, IV, DES3_IV_LEN); + tls_reg_write32(HR_CRYPTO_SRC_ADDR, (unsigned int)in); + tls_reg_write32(HR_CRYPTO_DEST_ADDR, (unsigned int)out); + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + sec_cfg = (val & 0xF0000000) | (CRYPTO_METHOD_DES << 16) | (1 << SOFT_RESET_DES) | (dec << 20) | (cbc << 21) | (len & 0xFFFF); + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + tls_reg_write32(HR_CRYPTO_SEC_CTRL, 0x1);//start crypto + while (!crypto_complete) + { + + } + crypto_complete = 0; + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return ERR_CRY_OK; +} + + +/** + * @brief This function initializes a CRC algorithm, i.e. fills the psCrcContext_t structure pointed to by ctx with necessary data. + * + * @param[in] ctx Pointer to the CRC Context. + * @param[in] key The initialization key. + * @param[in] crc_type The CRC type, supports CRC8/CRC16 MODBUS/CRC16 CCITT/CRC32 + * @param[in] mode Set input or outpu reflect. + * @param[in] dec The cryption way which indicates encryption or decryption. + * see OUTPUT_REFLECT + * see INPUT_REFLECT + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_crc_init(psCrcContext_t * ctx, u32 key, CRYPTO_CRC_TYPE crc_type, u8 mode) +{ + ctx->state = key; + ctx->type = crc_type; + ctx->mode = mode; + return ERR_CRY_OK; +} + +/** + * @brief This function updates the CRC value with a variable length bytes. + * This function may be called as many times as necessary, so the message may be processed in blocks. + * + * @param[in] ctx Pointer to the CRC Context. + * @param[in] in Pointer to a variable length bytes + * @param[in] len The bytes 's length + * + * @retval 0 success + * @retval other failed + * + * @note The address needs to be four-byte aligned. + */ +int tls_crypto_crc_update(psCrcContext_t * ctx, unsigned char *in, u32 len) +{ + unsigned int sec_cfg, val; + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + sec_cfg = (val & 0xF0000000) | (CRYPTO_METHOD_CRC << 16) | (ctx->type << 21) | (ctx->mode << 23) | (len & 0xFFFF); + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + if(ctx->mode & OUTPUT_REFLECT) + { + u8 ch_crc = 16; + u32 state = 0; + switch(ctx->type) + { + case CRYPTO_CRC_TYPE_8: + ch_crc = 8; + break; + case CRYPTO_CRC_TYPE_16_MODBUS: + ch_crc = 16; + break; + case CRYPTO_CRC_TYPE_16_CCITT: + ch_crc = 16; + break; + case CRYPTO_CRC_TYPE_32: + ch_crc = 32; + break; + default: + break; + } + state = Reflect(ctx->state, ch_crc); + tls_reg_write32(HR_CRYPTO_CRC_KEY, state); + } + else + tls_reg_write32(HR_CRYPTO_CRC_KEY, ctx->state); + + tls_reg_write32(HR_CRYPTO_SRC_ADDR, (unsigned int)in); + tls_reg_write32(HR_CRYPTO_SEC_CTRL, 0x1);//start crypto + while (!crypto_complete) + { + + } + crypto_complete = 0; + ctx->state = tls_reg_read32(HR_CRYPTO_CRC_RESULT); + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return ERR_CRY_OK; +} + + +/** + * @brief This function ends a CRC operation and produces a CRC value. + * + * @param[in] ctx Pointer to the CRC Context. + * @param[in] crc_val Pointer to the CRC value. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_crc_final(psCrcContext_t * ctx, u32 *crc_val) +{ + *crc_val = ctx->state; + return ERR_CRY_OK; +} + +static void hd_sha1_compress(psDigestContext_t *md) +{ + unsigned int sec_cfg, val; + int i = 0; + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + tls_reg_write32(HR_CRYPTO_SRC_ADDR, (unsigned int)md->sha1.buf); + + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + sec_cfg = (val & 0xF0000000) | (CRYPTO_METHOD_SHA1 << 16) | (64 & 0xFFFF); // TODO + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST0, md->sha1.state[0]); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST1, md->sha1.state[1]); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST2, md->sha1.state[2]); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST3, md->sha1.state[3]); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST4, md->sha1.state[4]); + tls_reg_write32(HR_CRYPTO_SEC_CTRL, 0x1);//start crypto + while (!crypto_complete) + { + + } + crypto_complete = 0; + for (i = 0; i < 5; i++) { + val = tls_reg_read32(HR_CRYPTO_SHA1_DIGEST0 + (4*i)); + md->sha1.state[i] = val; + } + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); +} + + +/** + * @brief This function initializes Message-Diggest context for usage in SHA1 algorithm, starts a new SHA1 operation and writes a new Digest Context. + * + * @param[in] md Pointer to the SHA1 Digest Context. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +void tls_crypto_sha1_init(psDigestContext_t * md) +{ + md->sha1.state[0] = 0x67452301UL; + md->sha1.state[1] = 0xefcdab89UL; + md->sha1.state[2] = 0x98badcfeUL; + md->sha1.state[3] = 0x10325476UL; + md->sha1.state[4] = 0xc3d2e1f0UL; + md->sha1.curlen = 0; +#ifdef HAVE_NATIVE_INT64 + md->sha1.length = 0; +#else + md->sha1.lengthHi = 0; + md->sha1.lengthLo = 0; +#endif /* HAVE_NATIVE_INT64 */ +} + + +/** + * @brief Process a message block using SHA1 algorithm. + * This function performs a SHA1 block update operation. It continues an SHA1 message-digest operation, + * by processing InputLen-byte length message block pointed to by buf, and by updating the SHA1 context pointed to by md. + * This function may be called as many times as necessary, so the message may be processed in blocks. + * + * @param[in] md Pointer to the SHA1 Digest Context. + * @param[in] buf InputLen-byte length message block + * @param[in] len The buf 's length + * + * @returnl None + * + * @note None + */ +void tls_crypto_sha1_update(psDigestContext_t * md, const unsigned char *buf, u32 len) +{ + u32 n; + while (len > 0) { + n = min(len, (64 - md->sha1.curlen)); + memcpy(md->sha1.buf + md->sha1.curlen, buf, (size_t)n); + md->sha1.curlen += n; + buf += n; + len -= n; + + /* is 64 bytes full? */ + if (md->sha1.curlen == 64) { + hd_sha1_compress(md); +#ifdef HAVE_NATIVE_INT64 + md->sha1.length += 512; +#else + n = (md->sha1.lengthLo + 512) & 0xFFFFFFFFL; + if (n < md->sha1.lengthLo) { + md->sha1.lengthHi++; + } + md->sha1.lengthLo = n; +#endif /* HAVE_NATIVE_INT64 */ + md->sha1.curlen = 0; + } + } +} + + +/** + * @brief This function ends a SHA1 operation and produces a Message-Digest. + * This function finalizes SHA1 algorithm, i.e. ends an SHA1 Message-Digest operation, + * writing the Message-Digest in the 20-byte buffer pointed to by hash in according to the information stored in context. + * + * @param[in] md Pointer to the SHA1 Digest Context. + * @param[in] hash Pointer to the Message-Digest + * + * @retval 20 success, return the hash size. + * @retval <0 failed + + * + * @note None + */ +int tls_crypto_sha1_final(psDigestContext_t * md, unsigned char *hash) +{ + s32 i; + u32 val; +#ifndef HAVE_NATIVE_INT64 + u32 n; +#endif + if (md->sha1.curlen >= sizeof(md->sha1.buf) || hash == NULL) { + return ERR_ARG_FAIL; + } + +/* + increase the length of the message + */ +#ifdef HAVE_NATIVE_INT64 + md->sha1.length += md->sha1.curlen << 3; +#else + n = (md->sha1.lengthLo + (md->sha1.curlen << 3)) & 0xFFFFFFFFL; + if (n < md->sha1.lengthLo) { + md->sha1.lengthHi++; + } + md->sha1.lengthHi += (md->sha1.curlen >> 29); + md->sha1.lengthLo = n; +#endif /* HAVE_NATIVE_INT64 */ + +/* + append the '1' bit + */ + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80; + +/* + if the length is currently above 56 bytes we append zeros then compress. + Then we can fall back to padding zeros and length encoding like normal. + */ + if (md->sha1.curlen > 56) { + while (md->sha1.curlen < 64) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + hd_sha1_compress(md); + md->sha1.curlen = 0; + } + +/* + pad upto 56 bytes of zeroes + */ + while (md->sha1.curlen < 56) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + +/* + store length + */ +#ifdef HAVE_NATIVE_INT64 + STORE64H(md->sha1.length, md->sha1.buf+56); +#else + STORE32H(md->sha1.lengthHi, md->sha1.buf+56); + STORE32H(md->sha1.lengthLo, md->sha1.buf+60); +#endif /* HAVE_NATIVE_INT64 */ + hd_sha1_compress(md); + +/* + copy output + */ + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + for (i = 0; i < 5; i++) { + val = tls_reg_read32(HR_CRYPTO_SHA1_DIGEST0 + (4*i)); + STORE32H(val, hash+(4*i)); + } + memset(md, 0x0, sizeof(psDigestContext_t)); + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return SHA1_MAC_LEN; +} + +static void hd_md5_compress(psDigestContext_t *md) +{ + unsigned int sec_cfg, val, i; + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + tls_reg_write32(HR_CRYPTO_SRC_ADDR, (unsigned int)md->md5.buf); + val = tls_reg_read32(HR_CRYPTO_SEC_CFG); + sec_cfg = (val & 0xF0000000) | (CRYPTO_METHOD_MD5 << 16) | (64 & 0xFFFF); + tls_reg_write32(HR_CRYPTO_SEC_CFG, sec_cfg); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST0, md->md5.state[0]); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST1, md->md5.state[1]); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST2, md->md5.state[2]); + tls_reg_write32(HR_CRYPTO_SHA1_DIGEST3, md->md5.state[3]); + tls_reg_write32(HR_CRYPTO_SEC_CTRL, 0x1);//start crypto + while (!crypto_complete) + { + + } + crypto_complete = 0; + for (i = 0; i < 4; i++) { + val = tls_reg_read32(HR_CRYPTO_SHA1_DIGEST0 + (4*i)); + md->md5.state[i] = val; + } + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); +} + + +/** + * @brief This function initializes Message-Diggest context for usage in MD5 algorithm, starts a new MD5 operation and writes a new Digest Context. + * This function begins a MD5 Message-Diggest Algorithm, i.e. fills the psDigestContext_t structure pointed to by md with necessary data. + * MD5 is the algorithm which takes as input a message of arbitrary length and produces as output a 128-bit "fingerprint" or "message digest" of the input. + * It is conjectured that it is computationally infeasible to produce two messages having the same message digest, + * or to produce any message having a given prespecified target message digest. + * + * @param[in] md MD5 Digest Context. + * + * @return None + * + * @note None + */ +void tls_crypto_md5_init(psDigestContext_t * md) { + md->md5.state[0] = 0x67452301UL; + md->md5.state[1] = 0xefcdab89UL; + md->md5.state[2] = 0x98badcfeUL; + md->md5.state[3] = 0x10325476UL; + md->md5.curlen = 0; +#ifdef HAVE_NATIVE_INT64 + md->md5.length = 0; +#else + md->md5.lengthHi = 0; + md->md5.lengthLo = 0; +#endif /* HAVE_NATIVE_INT64 */ +} + + +/** + * @brief Process a message block using MD5 algorithm. + * This function performs a MD5 block update operation. It continues an MD5 message-digest operation, + * by processing InputLen-byte length message block pointed to by buf, and by updating the MD5 context pointed to by md. + * This function may be called as many times as necessary, so the message may be processed in blocks. + * + * @param[in] md MD5 Digest Context. + * @param[in] buf InputLen-byte length message block + * @param[in] len The buf 's length + * + * @return None + * + * @note None + */ +void tls_crypto_md5_update(psDigestContext_t *md, const unsigned char *buf, u32 len) +{ + u32 n; + + while (len > 0) { + n = min(len, (64 - md->md5.curlen)); + memcpy(md->md5.buf + md->md5.curlen, buf, (size_t)n); + md->md5.curlen += n; + buf += n; + len -= n; + +/* + is 64 bytes full? + */ + if (md->md5.curlen == 64) { + hd_md5_compress(md); +#ifdef HAVE_NATIVE_INT64 + md->md5.length += 512; +#else + n = (md->md5.lengthLo + 512) & 0xFFFFFFFFL; + if (n < md->md5.lengthLo) { + md->md5.lengthHi++; + } + md->md5.lengthLo = n; +#endif /* HAVE_NATIVE_INT64 */ + md->md5.curlen = 0; + } + } +} + +/** + * @brief This function ends a MD5 operation and produces a Message-Digest. + * This function finalizes MD5 algorithm, i.e. ends an MD5 Message-Digest operation, + * writing the Message-Digest in the 16-byte buffer pointed to by hash in according to the information stored in context. + * + * @param[in] md MD5 Digest Context. + * @param[in] hash the Message-Digest + * + * @retval 16 success, return the hash size. + * @retval <0 failed + * + * @note None + */ +s32 tls_crypto_md5_final(psDigestContext_t * md, unsigned char *hash) +{ + s32 i; + u32 val; +#ifndef HAVE_NATIVE_INT64 + u32 n; +#endif + +// psAssert(md != NULL); + if (hash == NULL) { + psTraceCrypto("NULL hash storage passed to psMd5Final\n"); + return PS_ARG_FAIL; + } + +/* + increase the length of the message + */ +#ifdef HAVE_NATIVE_INT64 + md->md5.length += md->md5.curlen << 3; +#else + n = (md->md5.lengthLo + (md->md5.curlen << 3)) & 0xFFFFFFFFL; + if (n < md->md5.lengthLo) { + md->md5.lengthHi++; + } + md->md5.lengthHi += (md->md5.curlen >> 29); + md->md5.lengthLo = n; +#endif /* HAVE_NATIVE_INT64 */ + +/* + append the '1' bit + */ + md->md5.buf[md->md5.curlen++] = (unsigned char)0x80; + +/* + if the length is currently above 56 bytes we append zeros then compress. + Then we can fall back to padding zeros and length encoding like normal. + */ + if (md->md5.curlen > 56) { + while (md->md5.curlen < 64) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + hd_md5_compress(md); + md->md5.curlen = 0; + } + +/* + pad upto 56 bytes of zeroes + */ + while (md->md5.curlen < 56) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } +/* + store length + */ +#ifdef HAVE_NATIVE_INT64 + STORE64L(md->md5.length, md->md5.buf+56); +#else + STORE32L(md->md5.lengthLo, md->md5.buf+56); + STORE32L(md->md5.lengthHi, md->md5.buf+60); +#endif /* HAVE_NATIVE_INT64 */ + hd_md5_compress(md); + +/* + copy output + */ + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + for (i = 0; i < 4; i++) { + val = tls_reg_read32(HR_CRYPTO_SHA1_DIGEST0 + (4*i)); + STORE32L(val, hash+(4*i)); + } + memset(md, 0x0, sizeof(psDigestContext_t)); + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_GPSEC); + return MD5_MAC_LEN; +} + +static void rsaMonMulSetLen(const u32 len) +{ + RSAN = len; +} +static void rsaMonMulWriteMc(const u32 mc) +{ + u32 val = 0; + RSAMC = mc; + val = RSAMC; + if(val == mc) + { + val = 1; + return; + } +} +static void rsaMonMulWriteA(const u32 *const in) +{ + //memcpy((u32*)&RSAXBUF, in, RSAN * sizeof(u32)); + tls_crypto_write_32reg(RSA_BASE_ADDRESS + 0x0, (void *)in, RSAN * sizeof(u32)); +} +static void rsaMonMulWriteB(const u32 *const in) +{ + //memcpy((u32*)&RSAYBUF, in, RSAN * sizeof(u32)); + tls_crypto_write_32reg(RSA_BASE_ADDRESS + 0x100, (void *)in, RSAN * sizeof(u32)); +} +static void rsaMonMulWriteM(const u32 *const in) +{ + //memcpy((u32*)&RSAMBUF, in, RSAN * sizeof(u32)); + tls_crypto_write_32reg(RSA_BASE_ADDRESS + 0x200, (void *)in, RSAN * sizeof(u32)); +} +static void rsaMonMulReadA(u32 *const in) +{ + //memcpy(in, (u32*)&RSAXBUF, RSAN * sizeof(u32)); + tls_crypto_read_32reg((void *)in, RSA_BASE_ADDRESS + 0x0, RSAN * sizeof(u32)); +} +static void rsaMonMulReadB(u32 *const in) +{ + //memcpy(in, (u32*)&RSAYBUF, RSAN * sizeof(u32)); + tls_crypto_read_32reg((void *)in, RSA_BASE_ADDRESS + 0x100, RSAN * sizeof(u32)); +} +static void rsaMonMulReadD(u32 *const in) +{ + //memcpy(in, (u32*)&RSADBUF, RSAN * sizeof(u32)); + tls_crypto_read_32reg((void *)in, RSA_BASE_ADDRESS + 0x300, RSAN * sizeof(u32)); +} +static int rsaMulModRead(unsigned char w, hstm_int * a) +{ + u32 in[64]; + int err = 0; + memset(in, 0, 64 * sizeof(u32)); + switch(w) + { + case 'A': + rsaMonMulReadA(in); + break; + case 'B': + rsaMonMulReadB(in); + break; + case 'D': + rsaMonMulReadD(in); + break; + } + pstm_reverse((unsigned char *)in, RSAN * sizeof(u32)); + /* this a should be initialized outside. */ + //if ((err = pstm_init_for_read_unsigned_bin(NULL, a, RSAN * sizeof(u32) + sizeof(hstm_int))) != ERR_CRY_OK){ + // return err; + //} + if ((err = pstm_read_unsigned_bin(a, (unsigned char *)in, RSAN * sizeof(u32))) != ERR_CRY_OK) { + pstm_clear(a); + return err; + } + return 0; +} +static void rsaMulModWrite(unsigned char w, hstm_int * a) +{ + u32 in[64]; + memset(in, 0, 64 * sizeof(u32)); + pstm_to_unsigned_bin_nr(NULL, a, (unsigned char*)in); + switch(w) + { + case 'A': + rsaMonMulWriteA(in); + break; + case 'B': + rsaMonMulWriteB(in); + break; + case 'M': + rsaMonMulWriteM(in); + break; + } +} +static void rsaMonMulAA(void) +{ + RSACON = 0x2c; + + while (!crypto_complete) + { + + } + crypto_complete = 0; +} +static void rsaMonMulDD(void) +{ + RSACON = 0x20; + + while (!crypto_complete) + { + + } + crypto_complete = 0; +} +static void rsaMonMulAB(void) +{ + RSACON = 0x24; + + while (!crypto_complete) + { + + } + crypto_complete = 0; +} +static void rsaMonMulBD(void) +{ + RSACON = 0x28; + + while (!crypto_complete) + { + + } + crypto_complete = 0; +} +/****************************************************************************** +compute mc, s.t. mc * in = 0xffffffff +******************************************************************************/ +static void rsaCalMc(u32 *mc, const u32 in) +{ + u32 y = 1; + u32 i = 31; + u32 left = 1; + u32 right = 0; + for(i = 31; i != 0; i--) + { + left <<= 1; /* 2^(i-1) */ + right = (in * y) & left; /* (n*y) mod 2^i */ + if( right ) + { + y += left; + } + } + *mc = ~y + 1; +} + + +/** + * @brief This function implements the large module power multiplication algorithm. + * res = a**e (mod n) + * + * @param[in] a Pointer to a bignumber. + * @param[in] e Pointer to a bignumber. + * @param[in] n Pointer to a bignumber. + * @param[out] res Pointer to the result bignumber. + * + * @retval 0 success + * @retval other failed + * + * @note None + */ +int tls_crypto_exptmod(hstm_int *a, hstm_int *e, hstm_int *n, hstm_int *res) +{ + int i = 0; + u32 k = 0, mc = 0, dp0; + u8 monmulFlag = 0; + hstm_int R, X, Y; + + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_RSA); + tls_fls_sem_lock(); + pstm_init(NULL, &X); + pstm_init(NULL, &Y); + pstm_init(NULL, &R); + k = pstm_count_bits(n);//n->used * DIGIT_BIT;//pstm_count_bits(n); + k = ((k / 32) + (k % 32 > 0 ? 1 : 0)) * 32; + pstm_set(&Y, k); + pstm_set(&X, 2); + pstm_exptmod(NULL, &X, &Y, n, &R); //R = 2^k % n + //pstm_set(&Y, 1); + pstm_mulmod(NULL, a, &R, n, &X); //X = A * R + pstm_copy(&R, &Y); + if(n->used > 1) + dp0 = 0xFFFFFFFF & ((n->dp[0]) | (u32)(n->dp[1] << DIGIT_BIT)); + else + dp0 = n->dp[0]; + rsaCalMc(&mc, dp0); + k = pstm_count_bits(n); + rsaMonMulSetLen(k/32 + (k%32 == 0 ? 0 : 1)); + rsaMonMulWriteMc(mc); + rsaMulModWrite('M', n); + rsaMulModWrite('B', &X); + rsaMulModWrite('A', &Y); + k = pstm_count_bits(e); + for(i=k-1;i>=0;i--){ + //montMulMod(&Y, &Y, n, &Y); + //if(pstm_get_bit(e, i)) + // montMulMod(&Y, &X, n, &Y); + if(monmulFlag == 0) + { + rsaMonMulAA(); + monmulFlag = 1; + } + else + { + rsaMonMulDD(); + monmulFlag = 0; + } + + if(pstm_get_bit(e, i)) + { + if(monmulFlag == 0) + { + rsaMonMulAB(); + monmulFlag = 1; + } + else + { + rsaMonMulBD(); + monmulFlag = 0; + } + } + } + pstm_set(&R, 1); + rsaMulModWrite('B', &R); + //montMulMod(&Y, &R, n, res); + if(monmulFlag == 0) + { + rsaMonMulAB(); + rsaMulModRead('D', res); + } + else + { + rsaMonMulBD(); + rsaMulModRead('A', res); + } + pstm_clamp(res); + pstm_clear(&X); + pstm_clear(&Y); + pstm_clear(&R); + tls_fls_sem_unlock(); + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_RSA); + + return 0; +} + + +/** + * @brief This function initializes the encryption module. + * + * @param None + * + * @return None + * + * @note None + */ +void tls_crypto_init(void) +{ + NVIC_ClearPendingIRQ(RSA_IRQn); + NVIC_ClearPendingIRQ(CRYPTION_IRQn); + tls_irq_enable(RSA_IRQn); + tls_irq_enable(CRYPTION_IRQn); +} + +//#define TEST_ALL_CRYPTO + +#ifdef TEST_ALL_CRYPTO +#define KEY_LEN 16 +#define CHAR_LEN 16 +#define MAX_CHAR_LEN 2048 +int test_rc4(char* key, int keylen, char* pt, int len, u8 mode){ + int ret = -1; + int i = 0, j = 0; + psCipherContext_t ctx; + unsigned char *ct = NULL; + ct = tls_mem_alloc(len); + if(ct == NULL){ + goto out; + } + + if(mode == 0)//software encrypt + { + psArc4Init(&ctx, key, keylen); + if(psArc4(&ctx, pt, ct, len) < 0) + { + goto out; + } + if(tls_crypto_rc4_init(&ctx, key, keylen) < 0) + { + goto out; + } + if(tls_crypto_rc4(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + else//hardware encrypt + { + if(tls_crypto_rc4_init(&ctx, key, keylen) < 0) + { + goto out; + } + if(tls_crypto_rc4(&ctx, pt, ct, len) < 0) + { + goto out; + } + + psArc4Init(&ctx, key, keylen); + if(psArc4(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + + if(memcmp(pt, ct, len)) + { + goto out; + } + ret = 0; + +out: + if(ct) + tls_mem_free(ct); + return ret; +} + +int test_aes_ctr(char* key, int keylen, char * IV, char* pt, int len, u8 mode){ + int ret = -1; + psCipherContext_t ctx; + unsigned char *ct = NULL; + + ct = tls_mem_alloc(MAX_CHAR_LEN); + if(ct == NULL){ + goto out; + } + + if(mode == 0)//software encrypt + { + if(psAesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psAesEncrypt_CTR(&ctx, pt, ct, len) < 0) + { + goto out; + } + + if(tls_crypto_aes_init(&ctx, IV, key, keylen, CRYPTO_MODE_CTR) != 0) + { + goto out; + } + if(tls_crypto_aes_encrypt_decrypt(&ctx, ct, ct, len, CRYPTO_WAY_DECRYPT) < 0) + { + goto out; + } + } + else//hardware encrypt + { + if(tls_crypto_aes_init(&ctx, IV, key, keylen, CRYPTO_MODE_CTR) != 0) + { + goto out; + } + if(tls_crypto_aes_encrypt_decrypt(&ctx, pt, ct, len, CRYPTO_WAY_ENCRYPT) < 0) + { + goto out; + } + + if(psAesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psAesEncrypt_CTR(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + if(memcmp(pt, ct, len)) + { + goto out; + } + ret = 0; + +out: + if(ct) + tls_mem_free(ct); + return ret; +} +int test_aes_cbc(char* key, int keylen, char * IV, char* pt, int len, u8 mode){ + int ret = -1; + psCipherContext_t ctx; + unsigned char *ct = NULL; + + ct = tls_mem_alloc(MAX_CHAR_LEN); + if(ct == NULL){ + goto out; + } + + if(mode == 0)//software encrypt + { + if(psAesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psAesEncrypt(&ctx, pt, ct, len) < 0) + { + goto out; + } + + if(tls_crypto_aes_init(&ctx, IV, key, keylen, CRYPTO_MODE_CBC) != 0) + { + goto out; + } + if(tls_crypto_aes_encrypt_decrypt(&ctx, ct, ct, len, CRYPTO_WAY_DECRYPT) < 0) + { + goto out; + } + } + else//hardware encrypt + { + if(tls_crypto_aes_init(&ctx, IV, key, keylen, CRYPTO_MODE_CBC) != 0) + { + goto out; + } + if(tls_crypto_aes_encrypt_decrypt(&ctx, pt, ct, len, CRYPTO_WAY_ENCRYPT) < 0) + { + goto out; + } + + if(psAesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psAesDecrypt(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + if(memcmp(pt, ct, len)) + { + goto out; + } + ret = 0; + +out: + if(ct) + tls_mem_free(ct); + return ret; +} +int test_aes_ecb(char* key, int keylen, char * IV, char* pt, int len, u8 mode){ + int ret = -1; + psCipherContext_t ctx; + unsigned char *ct = NULL; + + ct = tls_mem_alloc(MAX_CHAR_LEN); + if(ct == NULL){ + goto out; + } + + if(mode == 0)//software encrypt + { + if(psAesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psAesEncrypt_ECB(&ctx, pt, ct, len) < 0) + { + goto out; + } + + if(tls_crypto_aes_init(&ctx, IV, key, keylen, CRYPTO_MODE_ECB) != 0) + { + goto out; + } + if(tls_crypto_aes_encrypt_decrypt(&ctx, ct, ct, len, CRYPTO_WAY_DECRYPT) < 0) + { + goto out; + } + } + else//hardware encrypt + { + if(tls_crypto_aes_init(&ctx, IV, key, keylen, CRYPTO_MODE_ECB) != 0) + { + goto out; + } + if(tls_crypto_aes_encrypt_decrypt(&ctx, pt, ct, len, CRYPTO_WAY_ENCRYPT) < 0) + { + goto out; + } + + if(psAesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psAesDecrypt_ECB(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + if(memcmp(pt, ct, len)) + { + goto out; + } + ret = 0; + +out: + if(ct) + tls_mem_free(ct); + return ret; +} +int test_3des_cbc(char* key, int keylen, char * IV, char* pt, int len, u8 mode){ + int ret=-1; + psCipherContext_t ctx; + unsigned char *ct = NULL; + + ct = tls_mem_alloc(MAX_CHAR_LEN); + if(ct == NULL){ + goto out; + } + + + if(mode == 0)//software encrypt + { + if(psDes3Init(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psDes3Encrypt(&ctx, pt, ct, len) < 0) + { + goto out; + } + if(tls_crypto_3des_init(&ctx, IV, key, keylen, CRYPTO_MODE_CBC) != 0) + { + goto out; + } + + if(tls_crypto_3des_encrypt_decrypt(&ctx, ct, ct, len, CRYPTO_WAY_DECRYPT) < 0) + { + goto out; + } + } + else//hardware encrypt + { + if(tls_crypto_3des_init(&ctx, IV, key, keylen, CRYPTO_MODE_CBC) != 0) + { + goto out; + } + + if(tls_crypto_3des_encrypt_decrypt(&ctx, pt, ct, len, CRYPTO_WAY_ENCRYPT) < 0) + { + goto out; + } + + if(psDes3Init(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psDes3Decrypt(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + if(memcmp(pt, ct, len)) + { + goto out; + } + ret = 0; +out: + if(ct) + tls_mem_free(ct); +} +int test_3des_ecb(char* key, int keylen, char * IV, char* pt, int len, u8 mode){ + int ret=-1; + psCipherContext_t ctx; + unsigned char *ct = NULL; + + ct = tls_mem_alloc(MAX_CHAR_LEN); + if(ct == NULL){ + goto out; + } + + + if(mode == 0)//software encrypt + { + if(psDes3Init(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psDes3Encrypt_ECB(&ctx, pt, ct, len) < 0) + { + goto out; + } + if(tls_crypto_3des_init(&ctx, IV, key, keylen, CRYPTO_MODE_ECB) != 0) + { + goto out; + } + + if(tls_crypto_3des_encrypt_decrypt(&ctx, ct, ct, len, CRYPTO_WAY_DECRYPT) < 0) + { + goto out; + } + } + else//hardware encrypt + { + if(tls_crypto_3des_init(&ctx, IV, key, keylen, CRYPTO_MODE_ECB) != 0) + { + goto out; + } + + if(tls_crypto_3des_encrypt_decrypt(&ctx, pt, ct, len, CRYPTO_WAY_ENCRYPT) < 0) + { + goto out; + } + + if(psDes3Init(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psDes3Decrypt_ECB(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + if(memcmp(pt, ct, len)) + { + goto out; + } + ret = 0; +out: + if(ct) + tls_mem_free(ct); +} +int test_des_ecb(char* key, int keylen, char * IV, char* pt, int len, u8 mode){ + int ret=-1; + psCipherContext_t ctx; + unsigned char *ct = NULL; + + ct = tls_mem_alloc(MAX_CHAR_LEN); + if(ct == NULL){ + goto out; + } + + + if(mode == 0)//software encrypt + { + if(psDesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psDesEncrypt_ECB(&ctx, pt, ct, len) < 0) + { + goto out; + } + if(tls_crypto_des_init(&ctx, IV, key, keylen, CRYPTO_MODE_ECB) != 0) + { + goto out; + } + + if(tls_crypto_des_encrypt_decrypt(&ctx, ct, ct, len, CRYPTO_WAY_DECRYPT) < 0) + { + goto out; + } + } + else//hardware encrypt + { + if(tls_crypto_des_init(&ctx, IV, key, keylen, CRYPTO_MODE_ECB) != 0) + { + goto out; + } + + if(tls_crypto_des_encrypt_decrypt(&ctx, pt, ct, len, CRYPTO_WAY_ENCRYPT) < 0) + { + goto out; + } + + if(psDesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psDesDecrypt_ECB(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + if(memcmp(pt, ct, len)) + { + goto out; + } + ret = 0; +out: + if(ct) + tls_mem_free(ct); +} +int test_des_cbc(char* key, int keylen, char * IV, char* pt, int len, u8 mode){ + int ret=-1; + psCipherContext_t ctx; + unsigned char *ct = NULL; + + ct = tls_mem_alloc(MAX_CHAR_LEN); + if(ct == NULL){ + goto out; + } + + + if(mode == 0)//software encrypt + { + if(psDesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psDesEncrypt(&ctx, pt, ct, len) < 0) + { + goto out; + } + if(tls_crypto_des_init(&ctx, IV, key, keylen, CRYPTO_MODE_CBC) != 0) + { + goto out; + } + + if(tls_crypto_des_encrypt_decrypt(&ctx, ct, ct, len, CRYPTO_WAY_DECRYPT) < 0) + { + goto out; + } + } + else//hardware encrypt + { + if(tls_crypto_des_init(&ctx, IV, key, keylen, CRYPTO_MODE_CBC) != 0) + { + goto out; + } + + if(tls_crypto_des_encrypt_decrypt(&ctx, pt, ct, len, CRYPTO_WAY_ENCRYPT) < 0) + { + goto out; + } + + if(psDesInit(&ctx, IV, key, keylen) < 0) + { + goto out; + } + if(psDesDecrypt(&ctx, ct, ct, len) < 0) + { + goto out; + } + } + if(memcmp(pt, ct, len)) + { + goto out; + } + ret = 0; +out: + if(ct) + tls_mem_free(ct); +} +int test_sha1(unsigned char *pt, int len){ + int i = 0, ret = -1; + psDigestContext_t ctx; + unsigned char *ct = NULL, *cth = NULL; + ct = tls_mem_alloc(20); + if(ct == NULL){ + goto out; + } + cth = tls_mem_alloc(20); + if(cth == NULL){ + goto out; + } + psSha1Init(&ctx); + psSha1Update(&ctx, pt, len); + psSha1Final(&ctx, ct); + + tls_crypto_sha1_init(&ctx); + tls_crypto_sha1_update(&ctx, pt, len); + tls_crypto_sha1_final(&ctx, cth); + if(memcmp(ct, cth, 20) != 0) + goto out; + ret = 0; +out: + if(ct) + tls_mem_free(ct); + if(cth) + tls_mem_free(cth); + return ret; +} +int test_md5(unsigned char *pt, int len){ + int i = 0, ret = -1; + psDigestContext_t ctx; + unsigned char *ct = NULL, *cth = NULL; + ct = tls_mem_alloc(MD5_HASH_SIZE); + if(ct == NULL){ + goto out; + } + cth = tls_mem_alloc(MD5_HASH_SIZE); + if(cth == NULL){ + goto out; + } + psMd5Init(&ctx); + psMd5Update(&ctx, pt, len); + psMd5Final(&ctx, ct); + + tls_crypto_md5_init(&ctx); + tls_crypto_md5_update(&ctx, pt, len); + tls_crypto_md5_final(&ctx, cth); + if(memcmp(ct, cth, MD5_HASH_SIZE) != 0) + goto out; + ret = 0; +out: + if(ct) + tls_mem_free(ct); + if(cth) + tls_mem_free(cth); + return ret; +} + +int test_crc(u32 key, unsigned char *pt, int len, CRYPTO_CRC_TYPE crc_type, u8 mode){ + int i = 0, ret = -1; + u32 crc_result = 0, crc_result1 = 0; + psCrcContext_t ctx; + + tls_crypto_crc_init(&ctx, key, crc_type, mode); + tls_crypto_crc_update(&ctx, pt, len); + tls_crypto_crc_update(&ctx, pt, len); + tls_crypto_crc_update(&ctx, pt, len); + tls_crypto_crc_final(&ctx, &crc_result); + + tls_crc_init(&ctx, key, crc_type, mode); + tls_crc_update(&ctx, pt, len); + tls_crc_update(&ctx, pt, len); + tls_crc_update(&ctx, pt, len); + tls_crc_final(&ctx, &crc_result1); + if(crc_result != crc_result1) + goto out; + ret = 0; +out: + return ret; +} + +/****************************************************************************** +Generate random number +******************************************************************************/ +static void rsaRnd(u32 *res, const u32 len) +{ + u32 i = len; + u32 *pRes = res; + + //RNGCTR = 1; + + while(i--) + { + //while(RNGSTR==0) + //{} + *pRes++ = rand();//RNGOUT; + } +} + +int test_crc_md5_sha1(void) +{ + int ret = -1; + u32 key = 0xFFFFFFFF; + int len = 32; + CRYPTO_CRC_TYPE crc_type=0; + u8 mode=3; + u32 crc_result = 0, crc_result1 = 0; + psCrcContext_t h_crc8_ctx; + psCrcContext_t crc8_ctx; + psDigestContext_t h_sha1_ctx; + psDigestContext_t sha1_ctx; + psDigestContext_t h_md5_ctx; + psDigestContext_t md5_ctx; + unsigned char *pt = NULL; + unsigned char *ct = NULL, *cth = NULL; + unsigned char *md5_ct = NULL, *md5_cth = NULL; + ct = tls_mem_alloc(20); + if(ct == NULL){ + goto out; + } + cth = tls_mem_alloc(20); + if(cth == NULL){ + goto out; + } + md5_ct = tls_mem_alloc(MD5_HASH_SIZE); + if(md5_ct == NULL){ + goto out; + } + md5_cth = tls_mem_alloc(MD5_HASH_SIZE); + if(md5_cth == NULL){ + goto out; + } + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(crc_type = 0; crc_type < 4; crc_type++) + { + for(mode = 0; mode < 4; mode++) + { + psMd5Init(&md5_ctx); + tls_crypto_md5_init(&h_md5_ctx); + tls_crypto_crc_init(&h_crc8_ctx, key, crc_type, mode); + tls_crc_init(&crc8_ctx, key, crc_type, mode); + psSha1Init(&sha1_ctx); + tls_crypto_sha1_init(&h_sha1_ctx); + + for(len = 0x100; len < MAX_CHAR_LEN; len+=103) + { + rsaRnd(pt, len/sizeof(u32)); + psMd5Update(&md5_ctx, pt, len); + tls_crypto_crc_update(&h_crc8_ctx, pt, len); + tls_crypto_md5_update(&h_md5_ctx, pt, len); + tls_crypto_sha1_update(&h_sha1_ctx, pt, len); + tls_crc_update(&crc8_ctx, pt, len); + psSha1Update(&sha1_ctx, pt, len); + } + tls_crypto_crc_final(&h_crc8_ctx, &crc_result); + tls_crc_final(&crc8_ctx, &crc_result1); + tls_crypto_md5_final(&h_md5_ctx, md5_cth); + tls_crypto_sha1_final(&h_sha1_ctx, cth); + psSha1Final(&sha1_ctx, ct); + psMd5Final(&md5_ctx, md5_ct); + + if(crc_result != crc_result1) + goto out; + if(memcmp(ct, cth, 20) != 0) + goto out; + if(memcmp(md5_ct, md5_cth, MD5_HASH_SIZE) != 0) + goto out; + } + } + ret = 0; +out: + if(ct) + tls_mem_free(ct); + if(cth) + tls_mem_free(cth); + if(md5_ct) + tls_mem_free(md5_ct); + if(md5_cth) + tls_mem_free(md5_cth); + if(pt) + tls_mem_free(pt); + return ret; +} + +int test_crc_all(void) +{ + int ret = -1; + u32 key = 0xFFFFFFFF; + int len = 32; + CRYPTO_CRC_TYPE crc_type=0; + u8 mode; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(len = 0x100; len < MAX_CHAR_LEN; len+=8) + { + for(crc_type = 0; crc_type < 4; crc_type++) + { + for(mode = 0; mode < 4; mode++) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_crc(key, pt, len, crc_type, mode)) + { + goto out; + } + } + } + } + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_rc4_all(void) +{ + int ret = -1; + int keylen = 16, len = 16; + unsigned char key[16] = "abcdefghijklmnop"; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(len = 16; len < MAX_CHAR_LEN; len+=8) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_rc4(key, keylen, pt, len, 0)) + { + goto out; + } + rsaRnd(pt, len/sizeof(u32)); + if(test_rc4(key, keylen, pt, len, 1)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_sha1_all(void) +{ + int ret = -1; + int len = 16; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(len = 16; len < MAX_CHAR_LEN; len+=8) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_sha1( pt, len)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_md5_all(void) +{ + int ret = -1; + int len = 16; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(len = 16; len < MAX_CHAR_LEN; len+=8) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_md5( pt, len)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_aes_ctr_all(void) +{ + int ret = -1; + int keylen = 16, len = 16; + unsigned char key[16] = "abcdefghijklmnop"; + unsigned char IV[16] = "bcdefghijklmnopq"; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(len = 16; len < MAX_CHAR_LEN; len+=16) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_aes_ctr(key, keylen, IV, pt, len, 0)) + { + goto out; + } + rsaRnd(pt, len/sizeof(u32)); + if(test_aes_ctr(key, keylen, IV, pt, len, 1)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_aes_cbc_all(void) +{ + int ret = -1; + int keylen = 16, len = 16; + unsigned char key[16] = "abcdefghijklmnop"; + unsigned char IV[16] = "bcdefghijklmnopq"; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(len = 16; len < MAX_CHAR_LEN; len+=16) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_aes_cbc(key, keylen, IV, pt, len, 0)) + { + goto out; + } + rsaRnd(pt, len/sizeof(u32)); + if(test_aes_cbc(key, keylen, IV, pt, len, 1)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_aes_ecb_all(void) +{ + int ret = -1; + int keylen = 16, len = 16; + unsigned char key[16] = "abcdefghijklmnop"; + unsigned char IV[16] = "bcdefghijklmnopq"; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(len = 16; len < MAX_CHAR_LEN; len+=16) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_aes_ecb(key, keylen, IV, pt, len, 0)) + { + goto out; + } + rsaRnd(pt, len/sizeof(u32)); + if(test_aes_ecb(key, keylen, IV, pt, len, 1)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_3des_cbc_all(void) +{ + int ret = -1; + int keylen = DES3_KEY_LEN, len = 8; + unsigned char key[DES3_KEY_LEN] = "abcdefghijklmnopqrstuvwx"; + unsigned char IV[DES3_IV_LEN] = "bcdefghi"; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(; len < MAX_CHAR_LEN; len+=8) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_3des_cbc(key, keylen, IV, pt, len, 0)) + { + goto out; + } + rsaRnd(pt, len/sizeof(u32)); + if(test_3des_cbc(key, keylen, IV, pt, len, 1)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_3des_ecb_all(void) +{ + int ret = -1; + int keylen = DES3_KEY_LEN, len = 8; + unsigned char key[DES3_KEY_LEN] = "abcdefghijklmnopqrstuvwx"; + unsigned char IV[DES3_IV_LEN] = "bcdefghi"; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(; len < MAX_CHAR_LEN; len+=8) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_3des_ecb(key, keylen, IV, pt, len, 0)) + { + goto out; + } + rsaRnd(pt, len/sizeof(u32)); + if(test_3des_ecb(key, keylen, IV, pt, len, 1)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_des_ecb_all(void) +{ + int ret = -1; + int keylen = DES_KEY_LEN, len = 8; + unsigned char key[DES_KEY_LEN] = "abcdefgh"; + unsigned char IV[DES3_IV_LEN] = "bcdefghi"; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(; len < MAX_CHAR_LEN; len+=8) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_des_ecb(key, keylen, IV, pt, len, 0)) + { + goto out; + } + rsaRnd(pt, len/sizeof(u32)); + if(test_des_ecb(key, keylen, IV, pt, len, 1)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} +int test_des_cbc_all(void) +{ + int ret = -1; + int keylen = DES_KEY_LEN, len = 8; + unsigned char key[DES_KEY_LEN] = "abcdefgh"; + unsigned char IV[DES3_IV_LEN] = "bcdefghi"; + unsigned char *pt = NULL; + pt = tls_mem_alloc(MAX_CHAR_LEN); + if(pt == NULL){ + goto out; + } + for(; len < MAX_CHAR_LEN; len+=8) + { + rsaRnd(pt, len/sizeof(u32)); + if(test_des_cbc(key, keylen, IV, pt, len, 0)) + { + goto out; + } + rsaRnd(pt, len/sizeof(u32)); + if(test_des_cbc(key, keylen, IV, pt, len, 1)) + { + goto out; + } + } + + ret = 0; +out: + if(pt) + tls_mem_free(pt); + return ret; +} + +/* res = a^e % n DIGIT_BIT must be 32, or you need to re calc the RSAN value and the mc value */ +int rsaMontExptMod(hstm_int *a, hstm_int *e, hstm_int *n, hstm_int *res) +{ + int i = 0; + u32 k = 0, mc = 0; + u8 monmulFlag = 0; + hstm_int R, X, Y; + pstm_init(NULL, &X); + pstm_init(NULL, &Y); + pstm_init(NULL, &R); + k = n->used * DIGIT_BIT;//pstm_count_bits(n); + pstm_set(&Y, k); + pstm_set(&X, 2); + pstm_exptmod(NULL, &X, &Y, n, &R); //R = 2^k % n + //pstm_set(&Y, 1); + pstm_mulmod(NULL, a, &R, n, &X); //X = A * R + pstm_copy(&R, &Y); + + rsaCalMc(&mc, n->dp[0]); + rsaMonMulSetLen(n->used); + rsaMonMulWriteMc(mc); + rsaMulModWrite('M', n); + rsaMulModWrite('B', &X); + rsaMulModWrite('A', &Y); + k = pstm_count_bits(e); + for(i=k-1;i>=0;i--){ + //montMulMod(&Y, &Y, n, &Y); + //if(pstm_get_bit(e, i)) + // montMulMod(&Y, &X, n, &Y); + if(monmulFlag == 0) + { + rsaMonMulAA(); + monmulFlag = 1; + } + else + { + rsaMonMulDD(); + monmulFlag = 0; + } + + if(pstm_get_bit(e, i)) + { + if(monmulFlag == 0) + { + rsaMonMulAB(); + monmulFlag = 1; + } + else + { + rsaMonMulBD(); + monmulFlag = 0; + } + } + } + pstm_set(&R, 1); + rsaMulModWrite('B', &R); + //montMulMod(&Y, &R, n, res); + if(monmulFlag == 0) + { + rsaMonMulAB(); + rsaMulModRead('D', res); + } + else + { + rsaMonMulBD(); + rsaMulModRead('A', res); + } + pstm_clamp(res); + pstm_clear(&X); + pstm_clear(&Y); + pstm_clear(&R); + return 0; +} + +int exptModTest(u32 len){ + u32 * a = NULL; + u32 * b = NULL; + u32 * m = NULL; + + hstm_int pa; + hstm_int pb; + hstm_int pm; + hstm_int pres; + hstm_int mres; + int err = -1; + + u32 mc = 0; + a = tls_mem_alloc(len * sizeof(u32)); + if(a == NULL) + goto out; + b = tls_mem_alloc(len * sizeof(u32)); + if(b== NULL) + goto out; + m = tls_mem_alloc(len * sizeof(u32)); + if(m == NULL) + goto out; +#if 1 + rsaRnd(a, len); + rsaRnd(b, len); + rsaRnd(m, len); + m[0] |= 0x01; + a[len-1] = 0; + b[len-1] = 0; + b[len-2] = 0; +#else +a[0] = 0x1DD31133; +a[1] = 0xB8A4D048; +a[2] = 0x47713BC4 ; +a[3] = 0x42EB5E0E ; +a[4] = 0x44D6F5FC; +a[5] = 0xF9C80DC7 ; +a[6] = 0x75F2D4DD; +a[7] = 0x2CF39311; +a[8] = 0x25994486; +a[9] = 0xD4B262D1; +a[10] = 0x429992AC; +a[11] = 0xCFE41022; +a[12] = 0x77BFE5F3; +a[13] = 0x71361EBB ; +a[14] = 0x9D5374B1; +a[15] = 0xF49234E7 ; +a[16] = 0x0; +a[17] = 0x0; +a[18] = 0x0; +a[19] = 0x0; +a[20] = 0x0; +a[21] = 0x0; +a[22] = 0x0; +a[23] = 0x0; +a[24] = 0x0; +a[25] = 0x0; +a[26] = 0x0; +a[27] = 0x0; +a[28] = 0x0; +a[29] = 0x0; +a[30] = 0x0; +a[31] = 0x0; +a[32] = 0x0; +a[33] = 0x0; +a[34] = 0x0; +a[35] = 0x0; +a[36] = 0x0; +a[37] = 0x0; +a[38] = 0x0; +a[39] = 0x0; +a[40] = 0x0; +a[41] = 0x0; +a[42] = 0x0; +a[43] = 0x0; +a[44] = 0x0; +a[45] = 0x0; +a[46] = 0x0; +a[47] = 0x0; +a[48] = 0x0; +a[49] = 0x0; +a[50] = 0x0; +a[51] = 0x0; +a[52] = 0x0; +a[53] = 0x0; +a[54] = 0x0; +a[55] = 0x0; +a[56] = 0x0; +a[57] = 0x0; +a[58] = 0x0; +a[59] = 0x0; +a[60] = 0x0; +a[61] = 0x0; +a[62] = 0x0; +a[63] = 0x0; + +b[0] = 0x26315342; +b[1] = 0x5E7A4F8D; +b[2] = 0xCAEC5A27; +b[3] = 0xB53E1326; +b[4] = 0x523A7348; +b[5] = 0xC4E2FC4F; +b[6] = 0x9DEE4D75; +b[7] = 0xCD9CE8D5; +b[8] = 0x0; +b[9] = 0x0; +b[10] = 0x0; +b[11] = 0x0; +b[12] = 0x0; +b[13] = 0x0; +b[14] = 0x0; +b[15] = 0x0; +b[16] = 0x0; +b[17] = 0x0; +b[18] = 0x0; +b[19] = 0x0; +b[20] = 0x0; +b[21] = 0x0; +b[22] = 0x0; +b[23] = 0x0; +b[24] = 0x0; +b[25] = 0x0; +b[26] = 0x0; +b[27] = 0x0; +b[28] = 0x0; +b[29] = 0x0; +b[30] = 0x0; +b[31] = 0x0; +b[32] = 0x0; +b[33] = 0x0; +b[34] = 0x0; +b[35] = 0x0; +b[36] = 0x0; +b[37] = 0x0; +b[38] = 0x0; +b[39] = 0x0; +b[40] = 0x0; +b[41] = 0x0; +b[42] = 0x0; +b[43] = 0x0; +b[44] = 0x0; +b[45] = 0x0; +b[46] = 0x0; +b[47] = 0x0; +b[48] = 0x0; +b[49] = 0x0; +b[50] = 0x0; +b[51] = 0x0; +b[52] = 0x0; +b[53] = 0x0; +b[54] = 0x0; +b[55] = 0x0; +b[56] = 0x0; +b[57] = 0x0; +b[58] = 0x0; +b[59] = 0x0; +b[60] = 0x0; +b[61] = 0x0; +b[62] = 0x0; +b[63] = 0x0; + +m[0] = 0xfda31a0b; +m[1] = 0x6bc894eb; +m[2] = 0xc79ac381; +m[3] = 0xc2d6bfb0; +m[4] = 0x7af55f47; +m[5] = 0xbfef6a12; +m[6] = 0xb07aa16e; +m[7] = 0xe8517da4; +m[8] = 0xfb313dd3; +m[9] = 0x4023c0e8; +m[10] = 0x14892c0d; +m[11] = 0x3b220767; +m[12] = 0xeeae653e; +m[13] = 0x48a3585b; +m[14] = 0xf54dcfeb; +m[15] = 0x259f881b; +m[16] = 0xab888a5b; +m[17] = 0x385cf671; +m[18] = 0xc0efe32f; +m[19] = 0xb94fe6dc; +m[20] = 0x1ef81224; +m[21] = 0x203faed4; +m[22] = 0x37dcd3f2; +m[23] = 0x59adb9dd; +m[24] = 0x9a696663; +m[25] = 0x9c4234ad; +m[26] = 0x2fe0a674; +m[27] = 0xd61401e2; +m[28] = 0x98a81f0c; +m[29] = 0xb82b62f7; +m[30] = 0xc052b828; +m[31] = 0x47966b41; +m[32] = 0x63238814; +m[33] = 0x87b722aa; +m[34] = 0xcaecdb0c; +m[35] = 0x17faa31c; +m[36] = 0xb116dc97; +m[37] = 0xd96aa8e1; +m[38] = 0x86481396; +m[39] = 0x8adbaeb5; +m[40] = 0x48c7f345; +m[41] = 0xf8a660ed; +m[42] = 0x8f6318f9; +m[43] = 0xc50475f6; +m[44] = 0xb95aeb64; +m[45] = 0x3f5ddf0f; +m[46] = 0x38c2f4e4; +m[47] = 0x8950b200; +m[48] = 0xfc9c54fb; +m[49] = 0xa152967e; +m[50] = 0x41f8aad6; +m[51] = 0xad7ba552; +m[52] = 0x43561858; +m[53] = 0xe77fef0f; +m[54] = 0x3d56023b; +m[55] = 0xcb591817; +m[56] = 0x9d171380; +m[57] = 0x24a0f121; +m[58] = 0x57b0bb2c; +m[59] = 0x705251ee; +m[60] = 0x8324e642; +m[61] = 0x2fb245a0; +m[62] = 0x7c7438b3; +m[63] = 0xb10abd76; + +#endif + + pstm_reverse(a, len * sizeof(u32)); + pstm_reverse(b, len * sizeof(u32)); + pstm_reverse(m, len * sizeof(u32)); + if ((err = pstm_init_for_read_unsigned_bin(NULL, &pa, len * sizeof(u32))) != PS_SUCCESS){ + goto out; + } + if ((err = pstm_read_unsigned_bin(&pa, a, len * sizeof(u32))) != PS_SUCCESS) { + goto out; + } + if ((err = pstm_init_for_read_unsigned_bin(NULL, &pb, len * sizeof(u32))) != PS_SUCCESS){ + goto out; + } + if ((err = pstm_read_unsigned_bin(&pb, b, len * sizeof(u32))) != PS_SUCCESS) { + goto out; + } + if ((err = pstm_init_for_read_unsigned_bin(NULL, &pm, len * sizeof(u32))) != PS_SUCCESS){ + goto out; + } + if ((err = pstm_read_unsigned_bin(&pm, m, len * sizeof(u32))) != PS_SUCCESS) { + goto out; + } + pstm_init(NULL, &pres); + pstm_init(NULL, &mres); + + tls_crypto_exptmod(&pa, &pb, &pm, &pres); + //rsaMontExptMod(&pa, &pb, &pm, &mres); + pstm_exptmod(NULL, &pa, &pb, &pm, &mres); + if(pstm_cmp(&mres, &pres) != PSTM_EQ) + { + err = -1; + goto out; + } + err = 0; +out: + if(a) + tls_mem_free(a); + if(b) + tls_mem_free(b); + if(m) + tls_mem_free(m); + pstm_clear(&pa); + pstm_clear(&pb); + pstm_clear(&pm); + pstm_clear(&pres); + pstm_clear(&mres); + return err; +} + +int test_rsa_all(void) +{ + int i=0,err = -1; + for(i=64;i>=16;i = i/2) + { + if(exptModTest(i)) + goto out; + } + err = 0; +out: + return err; +} + +int test_crypto_all(void) +{ + int ret = -1; + tls_crypto_init(); + + if(test_crc_md5_sha1()) + { + printf("crc md5 err\n"); + goto out; + } + if(test_crc_all()) + { + printf("crc err\n"); + goto out; + } + if(test_rsa_all()) + { + printf("rsa err\n"); + goto out; + } + if(test_md5_all()) + { + printf("md5 err\n"); + goto out; + } + if(test_sha1_all()) + { + printf("sha1 err\n"); + goto out; + } + if(test_aes_ctr_all()) + { + printf("aes ctr err\n"); + goto out; + } + if(test_aes_cbc_all()) + { + printf("aes cbc err\n"); + goto out; + } + if(test_aes_ecb_all()) + { + printf("aes ecb err\n"); + goto out; + } + if(test_rc4_all()) + { + printf("rc4 err\n"); + goto out; + } + if(test_3des_ecb_all()) + { + printf("3des ecb err\n"); + goto out; + } + if(test_3des_cbc_all()) + { + printf("3des cbc err\n"); + goto out; + } + if(test_des_ecb_all()) + { + printf("des ecb err\n"); + goto out; + } + if(test_des_cbc_all()) + { + printf("des cbc err\n"); + goto out; + } + ret = 0; +out: + return ret; +} +#endif + diff --git a/sdk/Platform/Common/fwup/wm_fwup.c b/sdk/Platform/Common/fwup/wm_fwup.c new file mode 100644 index 0000000..3894ccc --- /dev/null +++ b/sdk/Platform/Common/fwup/wm_fwup.c @@ -0,0 +1,1005 @@ +/***************************************************************************** +* +* File Name : wm_fwup.c +* +* Description: firmware update Module +* +* Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-6-16 +*****************************************************************************/ +#include +#include +#include + +#include "wm_mem.h" +#include "list.h" +#include "wm_debug.h" +#include "wm_internal_flash.h" +#include "wm_flash.h" +#include "wm_crypto_hard.h" + +#include "utils.h" +#include "wm_fwup.h" +#include "wm_watchdog.h" +#include "wm_wifi.h" +#include "wm_flash_map.h" +#include "wm_wl_task.h" +#include "wm_params.h" +#include "wm_param.h" + +#define FWUP_MSG_QUEUE_SIZE (4) + +#define FWUP_TASK_STK_SIZE (512) + +#define FWUP_MSG_START_ENGINEER (1) + +static struct tls_fwup *fwup = NULL; +static tls_os_queue_t *fwup_msg_queue = NULL; + +static u32 fwup_task_stk[FWUP_TASK_STK_SIZE]; + +//static u8 oneshotback = 0; +static u8 *fwupwritebuffer = NULL; + +T_BOOTER imgheader[2]; +extern u32 flashtotalsize; +static void fwup_update_autoflag(void) +{ + u8 auto_reconnect = 0xff; + + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_reconnect); + if(auto_reconnect == WIFI_AUTO_CNT_TMP_OFF) + { + auto_reconnect = WIFI_AUTO_CNT_ON; + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_reconnect); + } + return; +} + +int tls_fwup_img_header_check(T_BOOTER *img_param) +{ + psCrcContext_t crcContext; + u32 value = 0; + int i = 0; + u32 runaddr = 0; + u32 updaddr = 0; + + if (img_param->magic_no != SIGNATURE_WORD) + { + return FALSE; + } + + if ((IMG_TYPE_OLD_PLAIN != img_param->img_type) && (IMG_TYPE_NEW_PLAIN != img_param->img_type)) + { + return FALSE; + } + + tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); + for (i = 0; i < (sizeof(T_BOOTER)-4)/4; i++) + { + value = *(((int *)img_param)+i); + tls_crypto_crc_update(&crcContext, (unsigned char *)&value, 4); + } + value = 0; + tls_crypto_crc_final(&crcContext, &value); + + runaddr = img_param->run_img_addr|FLASH_BASE_ADDR; + if ((img_param->hd_checksum == value) && (runaddr < FLASH_1M_END_ADDR)) + { + /*forbid 1M_Plain and 2M_PLAIN update*/ + tls_fls_read(CODE_RUN_HEADER_ADDR, (unsigned char *)&imgheader[0], sizeof(T_BOOTER)); + if (imgheader[0].img_type != img_param->img_type) + { + return FALSE; + } + + /*run addr must be page-aligned in first 1M flash */ + if ((runaddr % INSIDE_FLS_PAGE_SIZE) + || (0 == img_param->run_img_len) + || (runaddr + img_param->run_img_len >= FLASH_1M_END_ADDR) + || (runaddr < CODE_RUN_START_ADDR)) + { + return FALSE; + } + + /*can not upd over flash size minus system parameter area*/ + value = runaddr + img_param->run_img_len; + if (value < imgheader[0].run_img_addr + imgheader[0].run_img_len) + { + value = (imgheader[0].run_img_addr|FLASH_BASE_ADDR) + imgheader[0].run_img_len; + } + + value = value%INSIDE_FLS_BLOCK_SIZE ? (value/INSIDE_FLS_BLOCK_SIZE)*INSIDE_FLS_BLOCK_SIZE + INSIDE_FLS_BLOCK_SIZE : value; + updaddr = img_param->upd_img_addr|FLASH_BASE_ADDR; + /*upd address can not overlap run addr & must be 64K aligned*/ + if ((updaddr < value) + || (updaddr%INSIDE_FLS_BLOCK_SIZE) + || (0 == img_param->upd_img_len)) + { + return FALSE; + } + + if(updaddr != 0x08100000 || img_param->upd_img_len > 0xbfc00) + { + printf("tampered with OTA header!\n"); + return FALSE; + } + + /*over flash capacity decrease one block size used as sys param area*/ + if ((updaddr + img_param->upd_img_len) > ((flashtotalsize - INSIDE_FLS_BLOCK_SIZE)|FLASH_BASE_ADDR)) + { + return FALSE; + } + + return TRUE; + } + else + { + return FALSE; + } +} + +static void tls_fwup_img_update_header(T_BOOTER* img_param) +{ + unsigned char current_img; + psCrcContext_t crcContext; + + tls_fls_read(CODE_RUN_HEADER_ADDR, (unsigned char *)&imgheader[0], sizeof(T_BOOTER)); + tls_fls_read(CODE_UPD_HEADER_ADDR, (unsigned char *)&imgheader[1], sizeof(T_BOOTER)); + + //将两个upd_no中较大的那个值å�–出æ�¥ï¼Œå†�将其加1å�Žèµ‹å€¼ç»™ CODE_UPD_HEADER_ADDR 处的headerï¼› + if (tls_fwup_img_header_check(&imgheader[1])) + { + current_img = (imgheader[1].upd_no > imgheader[0].upd_no); + } + else + { + current_img = 0; + } + img_param->upd_no = imgheader[current_img].upd_no + 1; + + tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); + tls_crypto_crc_update(&crcContext, (unsigned char *)img_param, sizeof(T_BOOTER)-4); + tls_crypto_crc_final(&crcContext, &img_param->hd_checksum); + tls_fls_write(CODE_UPD_HEADER_ADDR, (unsigned char *)img_param, sizeof(T_BOOTER)); +} + + +/* ninlight: SHA256 + tail verification hook instead of the QConnex RSA check (see sdk/PATCHES.md) */ +#include "util/nl_sha256.h" +#include "sys/nl_fwup_hook.h" + + +static void fwup_scheduler(void *data) +{ + u8 *buffer = NULL; + int err; + u32 msg; + u32 len; + u32 image_checksum = 0; + u32 org_checksum = 0; + struct tls_fwup_request *request; + struct tls_fwup_request *temp; + T_BOOTER booter; + u32 currentlen = 0; + u32 tmplen = 0; + bool isacrossflash = FALSE; + + nl_sha256_ctx sha; + nl_sha256_init(&sha); + u8 image_sha256[32] = {0}; + u8 *RSA_SHA256 = NULL; + + while (1) + { + err = tls_os_queue_receive(fwup_msg_queue, (void **)&msg, 0, 0); + tls_watchdog_clr(); + if(err != TLS_OS_SUCCESS) + { + continue; + } + switch(msg) + { + case FWUP_MSG_START_ENGINEER: + if(dl_list_empty(&fwup->wait_list) == 0) + { + fwup->current_state |= TLS_FWUP_STATE_BUSY; + } + dl_list_for_each_safe(request, temp, &fwup->wait_list, struct tls_fwup_request, list) + { + request->status = TLS_FWUP_REQ_STATUS_BUSY; + if(fwup->current_state & TLS_FWUP_STATE_ERROR) + { + TLS_DBGPRT_WARNING("some error happened during firmware update, so discard all the request in the waiting queue!\n"); + if(fwup->current_state & TLS_FWUP_STATE_ERROR_IO) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + } + else if(fwup->current_state & TLS_FWUP_STATE_ERROR_SIGNATURE) + { + request->status = TLS_FWUP_REQ_STATUS_FSIGNATURE; + } + else if(fwup->current_state & TLS_FWUP_STATE_ERROR_MEM) + { + request->status = TLS_FWUP_REQ_STATUS_FMEM; + } + else if(fwup->current_state & TLS_FWUP_STATE_ERROR_CRC) + { + request->status = TLS_FWUP_REQ_STATUS_FCRC; + } + goto request_finish; + } + else if(fwup->current_state & TLS_FWUP_STATE_COMPLETE) + { + TLS_DBGPRT_WARNING("the firmware updating conpletes, so discard the request in the waiting queue!\n"); + request->status = TLS_FWUP_REQ_STATUS_FCOMPLETE; + goto request_finish; + } + + if(fwup->current_image_src <= TLS_FWUP_IMAGE_SRC_WEB) + { + buffer = request->data; + if(fwup->received_len < sizeof(T_BOOTER)) + { + len = sizeof(T_BOOTER) - fwup->received_len; + if(request->data_len < len) + { + len = request->data_len; + } + MEMCPY(&booter, buffer, sizeof(T_BOOTER)); + request->data_len -= len; + buffer += len; + fwup->received_len += len; + if(fwup->received_len == sizeof(T_BOOTER)) + { + + if (!tls_fwup_img_header_check(&booter)) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + nl_sha256_update(&sha, (u8*)&booter, sizeof(T_BOOTER)); + + if ((IMG_TYPE_OLD_PLAIN == booter.img_type ) ||(IMG_TYPE_NEW_PLAIN == booter.img_type)) + { + fwup->program_base = booter.upd_img_addr | FLASH_BASE_ADDR; + fwup->total_len = booter.upd_img_len + 512; + org_checksum = booter.upd_checksum; + isacrossflash = FALSE; + currentlen = 0; + } + else + { + request->status = TLS_FWUP_REQ_STATUS_FCRC; + goto request_finish; + } + + fwup->updated_len = 0; + } + } + fwup->received_len += request->data_len; + } + if ((request->data_len > 0) && (fwupwritebuffer)) + { + if((currentlen + request->data_len) < INSIDE_FLS_SECTOR_SIZE) + { + memcpy(fwupwritebuffer + currentlen, buffer, request->data_len); + currentlen += request->data_len; + fwup->updated_len += request->data_len; + } + else + { + if ((IMG_TYPE_OLD_PLAIN == booter.img_type) \ + && (0x200000 == flashtotalsize)\ + && (fwup->program_base < FLASH_1M_END_ADDR)\ + && ((fwup->program_base + fwup->updated_len) >= (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE))) + { + isacrossflash = TRUE; + fwup->program_base = FLASH_1M_END_ADDR; + fwup->program_offset = 0; + } + + //TLS_DBGPRT_INFO("write the firmware image to the flash. %x\n\r", fwup->program_base + fwup->program_offset); + memcpy(fwupwritebuffer + currentlen, buffer, (INSIDE_FLS_SECTOR_SIZE - currentlen)); + tmplen = fwup->program_offset/INSIDE_FLS_SECTOR_SIZE * INSIDE_FLS_SECTOR_SIZE; + err = tls_fls_write(fwup->program_base + tmplen, fwupwritebuffer, INSIDE_FLS_SECTOR_SIZE); + if(err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("failed to program flash!\n"); + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + + memcpy(fwupwritebuffer, buffer + (INSIDE_FLS_SECTOR_SIZE - currentlen), (currentlen + request->data_len) - INSIDE_FLS_SECTOR_SIZE); + fwup->program_offset += INSIDE_FLS_SECTOR_SIZE; + fwup->updated_len += request->data_len; + currentlen = (currentlen + request->data_len) - INSIDE_FLS_SECTOR_SIZE; + } + + //TLS_DBGPRT_INFO("updated: %d bytes\n" , fwup->updated_len); + if(fwup->updated_len >= fwup->total_len) + { + u32 left = 0, offset = 0; + psCrcContext_t crcContext; + + if (fwup->program_offset <= fwup->updated_len) + { + if ((IMG_TYPE_OLD_PLAIN == booter.img_type) \ + && (0x200000 == flashtotalsize)\ + && (fwup->program_base < FLASH_1M_END_ADDR)\ + && ((fwup->program_base + fwup->updated_len) >= (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE))) + { + isacrossflash = TRUE; + fwup->program_base = FLASH_1M_END_ADDR; + fwup->program_offset = 0; + } + + err = tls_fls_write(fwup->program_base + fwup->program_offset, fwupwritebuffer, currentlen); + if(err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("failed to program flash!\n"); + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + } + + + offset = 0; + fwup->program_base = booter.upd_img_addr | FLASH_BASE_ADDR; + if (TRUE == isacrossflash) + { + left = (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE) - fwup->program_base; + } + else + { + left = fwup->total_len - 512; + } + + tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); + while (left > 0) + { + len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; + + err = tls_fls_read(fwup->program_base + offset, fwupwritebuffer, len); + if (err != TLS_FLS_STATUS_OK) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + tls_crypto_crc_update(&crcContext, fwupwritebuffer, len); + nl_sha256_update(&sha, fwupwritebuffer, len); + offset += len; + left -= len; + } + + if (TRUE == isacrossflash) + { + left = fwup->total_len - offset; + fwup->program_base = FLASH_1M_END_ADDR; + offset = 0; + while (left > 0) + { + len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; + + err = tls_fls_read(fwup->program_base + offset, fwupwritebuffer, len); + if (err != TLS_FLS_STATUS_OK) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + tls_crypto_crc_update(&crcContext, fwupwritebuffer, len); + nl_sha256_update(&sha, fwupwritebuffer, len); + offset += len; + left -= len; + } + } + tls_crypto_crc_final(&crcContext, &image_checksum); + + nl_sha256_final(&sha, image_sha256); +#if 0 + printf("last sector - len:%d isacrossflash: %d\n", len, isacrossflash); + u8 *bptr = (u8*)fwupwritebuffer; + for (int i = 0; i < 32; ++i) + printf("%02x ", bptr[i]); + printf("\n"); +#endif + // now read last 256 byte - with RSA encoded SHA256 + left = 512; + RSA_SHA256 = tls_mem_alloc(left); + if(!RSA_SHA256) + { + goto request_finish; + } + while (left > 0) + { + len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; + + err = tls_fls_read(fwup->program_base + offset, RSA_SHA256, len); + if (err != TLS_FLS_STATUS_OK) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + offset += len; + left -= len; + } +#if 0 + bptr = (u8*)RSA_SHA256; + printf("start enc hash - len:%d\n", len); + for (int i = 0; i < 32; ++i) + printf("%02x ", bptr[i]); + printf("\n"); +#endif + if (org_checksum != image_checksum || !nl_fwup_verify_tail(image_sha256, RSA_SHA256, 512)) // QC_CHANGE_MK + { + TLS_DBGPRT_ERR("varify incorrect[0x%02x, but 0x%02x]\n", org_checksum, image_checksum); + request->status = TLS_FWUP_REQ_STATUS_FCRC; + fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; + goto request_finish; + } + else /*CRC MATCH and Update IMAGE HEADER PARAM*/ + { + tls_fwup_img_update_header(&booter); + } + + TLS_DBGPRT_INFO("update the firmware successfully!\n"); + fwup->current_state |= TLS_FWUP_STATE_COMPLETE; + //if (oneshotback == 1){ + // tls_wifi_set_oneshot_flag(oneshotback); // æ�¢å¤�一键é…�ç½® + //} + + } + } + request->status = TLS_FWUP_REQ_STATUS_SUCCESS; + +request_finish: + if(RSA_SHA256) + { + tls_mem_free(RSA_SHA256); + } + tls_os_sem_acquire(fwup->list_lock, 0); + dl_list_del(&request->list); + tls_os_sem_release(fwup->list_lock); + if(dl_list_empty(&fwup->wait_list) == 1) + { + fwup->current_state &= ~TLS_FWUP_STATE_BUSY; + } + if(request->complete) + { + request->complete(request, request->arg); + } + if(fwup->updated_len >= (fwup->total_len)) + { + fwup_update_autoflag(); + tls_sys_reset(); + } + } + break; + + default: + break; + } + } +} + +void fwup_request_complete(struct tls_fwup_request *request, void *arg) +{ + tls_os_sem_t *sem; + + if((request == NULL) || (arg == NULL)) + { + return; + } + sem = (tls_os_sem_t *)arg; + tls_os_sem_release(sem); +} + +u32 tls_fwup_enter(enum tls_fwup_image_src image_src) +{ + u32 session_id = 0; + u32 cpu_sr; + + tls_fwup_init(); + + if (fwup == NULL) + { + TLS_DBGPRT_INFO("fwup is null!\n"); + return 0; + } + if (fwup->busy == TRUE) + { + TLS_DBGPRT_INFO("fwup is busy!\n"); + return 0; + } + + cpu_sr = tls_os_set_critical(); + + do + { + session_id = rand(); + }while(session_id == 0); + + if (NULL == fwupwritebuffer) + { + fwupwritebuffer = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); + if (NULL == fwupwritebuffer) + { + tls_os_release_critical(cpu_sr); + return 0; + } + } + + fwup->current_state = 0; + fwup->current_image_src = image_src; + + fwup->received_len = 0; + fwup->total_len = 0; + fwup->updated_len = 0; + fwup->program_base = 0; + fwup->program_offset = 0; + fwup->received_number = -1; + + fwup->current_session_id = session_id; + fwup->busy = TRUE; + tls_os_release_critical(cpu_sr); + //oneshotback = tls_wifi_get_oneshot_flag(); + //if (oneshotback == 1){ + // tls_wifi_set_oneshot_flag(0); // 退出一键é…�ç½® + //} + + tls_wifi_set_psflag(FALSE, 0); + + return session_id; +} + +int tls_fwup_exit(u32 session_id) +{ + u32 cpu_sr; + bool enable = FALSE; + //tls_os_task_t fwtask; + //tls_os_status_t osstatus = 0; + + if ((fwup == NULL) || (fwup->busy == FALSE)) + { + return TLS_FWUP_STATUS_EPERM; + } + if (session_id != fwup->current_session_id) + { + return TLS_FWUP_STATUS_ESESSIONID; + } + if (fwup->current_state & TLS_FWUP_STATE_BUSY) + { + return TLS_FWUP_STATUS_EBUSY; + } + + cpu_sr = tls_os_set_critical(); + if (fwupwritebuffer) + { + tls_mem_free(fwupwritebuffer); + fwupwritebuffer = NULL; + } + fwup->current_state = 0; + + fwup->received_len = 0; + fwup->total_len = 0; + fwup->updated_len = 0; + fwup->program_base = 0; + fwup->program_offset = 0; + fwup->received_number = -1; + + fwup->current_session_id = 0; + fwup->busy = FALSE; + tls_os_release_critical(cpu_sr); + + //if (oneshotback == 1){ + // tls_wifi_set_oneshot_flag(oneshotback); // æ�¢å¤�一键é…�ç½® + //} + tls_param_get(TLS_PARAM_ID_PSM, &enable, TRUE); + tls_wifi_set_psflag(enable, 0); + + return TLS_FWUP_STATUS_OK; +} + +int tls_fwup_get_current_session_id(void) +{ + if (fwup){ + return fwup->current_session_id; + } + return 0; +} + +int tls_fwup_set_update_numer(int number) +{ + if(1 == number - fwup->received_number) + { + fwup->received_number = number; + return TLS_FWUP_STATUS_OK; + } + return TLS_FWUP_STATE_UNDEF; +} + +int tls_fwup_get_current_update_numer(void) +{ + return fwup->received_number; +} + +int tls_fwup_get_status(void) +{ + return fwup->busy; +} + +int tls_fwup_set_crc_error(u32 session_id) +{ + if(fwup == NULL) + { + return TLS_FWUP_STATUS_EPERM; + } + if(session_id != fwup->current_session_id) + { + return TLS_FWUP_STATUS_ESESSIONID; + } + fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; + + return TLS_FWUP_STATUS_OK; +} + +static int tls_fwup_request_async(u32 session_id, struct tls_fwup_request *request) +{ + u8 need_sched; + + if(fwup == NULL) + { + return TLS_FWUP_STATUS_EPERM; + } + if(session_id != fwup->current_session_id) + { + return TLS_FWUP_STATUS_ESESSIONID; + } + if((request == NULL) || (request->data == NULL) || (request->data_len == 0)) + { + return TLS_FWUP_STATUS_EINVALID; + } + tls_os_sem_acquire(fwup->list_lock, 0); + if(dl_list_empty(&fwup->wait_list)) + { + need_sched = 1; + } + else + { + need_sched = 0; + } + request->status = TLS_FWUP_REQ_STATUS_IDLE; + dl_list_add_tail(&fwup->wait_list, &request->list); + tls_os_sem_release(fwup->list_lock); + if(need_sched == 1) + { + tls_os_queue_send(fwup_msg_queue, (void *)FWUP_MSG_START_ENGINEER, 0); + } + return TLS_FWUP_STATUS_OK; +} + + +int tls_fwup_request_sync(u32 session_id, u8 *data, u32 data_len) +{ + int err; + tls_os_sem_t *sem; + struct tls_fwup_request request; + + if(fwup == NULL) + { + return TLS_FWUP_STATUS_EPERM; + } + if(session_id != fwup->current_session_id) + { + return TLS_FWUP_STATUS_ESESSIONID; + } + if((data == NULL) || (data_len == 0)) + { + return TLS_FWUP_STATUS_EINVALID; + } + + err = tls_os_sem_create(&sem, 0); + if(err != TLS_OS_SUCCESS) + { + return TLS_FWUP_STATUS_EMEM; + } + request.data = data; + request.data_len = data_len; + request.complete = fwup_request_complete; + request.arg = (void *)sem; + + tls_wifi_set_psflag(FALSE, 0); + + err = tls_fwup_request_async(session_id, &request); + if(err == TLS_FWUP_STATUS_OK) + { + tls_os_sem_acquire(sem, 0); + } + tls_os_sem_delete(sem); + + switch(request.status) + { + case TLS_FWUP_REQ_STATUS_SUCCESS: + err = TLS_FWUP_STATUS_OK; + break; + + case TLS_FWUP_REQ_STATUS_FIO: + err = TLS_FWUP_STATUS_EIO; + break; + + case TLS_FWUP_REQ_STATUS_FSIGNATURE: + err = TLS_FWUP_STATUS_ESIGNATURE; + break; + + case TLS_FWUP_REQ_STATUS_FMEM: + err = TLS_FWUP_STATUS_EMEM; + break; + + case TLS_FWUP_REQ_STATUS_FCRC: + err = TLS_FWUP_STATUS_ECRC; + break; + + case TLS_FWUP_REQ_STATUS_FCOMPLETE: + err = TLS_FWUP_STATUS_EIO; + break; + + default: + err = TLS_FWUP_STATUS_EUNDEF; + break; + } + return err; +} + +u16 tls_fwup_current_state(u32 session_id) +{ + if(fwup == NULL) + { + return TLS_FWUP_STATE_UNDEF; + } + if(session_id != fwup->current_session_id) + { + return TLS_FWUP_STATE_UNDEF; + } + return fwup->current_state; +} + +int tls_fwup_reset(u32 session_id) +{ + u32 cpu_sr; + + if ((fwup == NULL) || (fwup->busy == FALSE)) {return TLS_FWUP_STATUS_EPERM;} + if (session_id != fwup->current_session_id) {return TLS_FWUP_STATUS_ESESSIONID;} + if (fwup->current_state & TLS_FWUP_STATE_BUSY) {return TLS_FWUP_STATUS_EBUSY;} + + cpu_sr = tls_os_set_critical(); + + fwup->current_state = 0; + + fwup->received_len = 0; + fwup->total_len = 0; + fwup->updated_len = 0; + fwup->program_base = 0; + fwup->program_offset = 0; + + tls_os_release_critical(cpu_sr); + + return TLS_FWUP_STATUS_OK; +} + +int tls_fwup_clear_error(u32 session_id) +{ + u32 cpu_sr; + + if ((fwup == NULL) || (fwup->busy == FALSE)) {return TLS_FWUP_STATUS_EPERM;} + if (session_id != fwup->current_session_id) {return TLS_FWUP_STATUS_ESESSIONID;} + if (fwup->current_state & TLS_FWUP_STATE_BUSY) {return TLS_FWUP_STATUS_EBUSY;} + + cpu_sr = tls_os_set_critical(); + + fwup->current_state &= ~TLS_FWUP_STATE_ERROR; + + tls_os_release_critical(cpu_sr); + + return TLS_FWUP_STATUS_OK; +} + +int tls_fwup_init(void) +{ + int err; + + if(fwup != NULL) + { + TLS_DBGPRT_ERR("firmware update module has been installed!\n"); + return TLS_FWUP_STATUS_EBUSY; + } + + fwup = tls_mem_alloc(sizeof(*fwup)); + if(fwup == NULL) + { + TLS_DBGPRT_ERR("allocate @fwup fail!\n"); + return TLS_FWUP_STATUS_EMEM; + } + memset(fwup, 0, sizeof(*fwup)); + + err = tls_os_sem_create(&fwup->list_lock, 1); + if(err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create semaphore @fwup->list_lock fail!\n"); + tls_mem_free(fwup); + return TLS_FWUP_STATUS_EMEM; + } + + dl_list_init(&fwup->wait_list); + fwup->busy = FALSE; + + err = tls_os_queue_create(&fwup_msg_queue, FWUP_MSG_QUEUE_SIZE); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create message queue @fwup_msg_queue fail!\n"); + tls_os_sem_delete(fwup->list_lock); + tls_mem_free(fwup); + return TLS_FWUP_STATUS_EMEM; + } + + err = tls_os_task_create(NULL, "fwup", + fwup_scheduler, + (void *)fwup, + (void *)&fwup_task_stk[0], + FWUP_TASK_STK_SIZE * sizeof(u32), + TLS_FWUP_TASK_PRIO, + 0); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create firmware update process task fail!\n"); + + tls_os_queue_delete(fwup_msg_queue); + tls_os_sem_delete(fwup->list_lock); + tls_mem_free(fwup); + return TLS_FWUP_STATUS_EMEM; + } + + return TLS_FWUP_STATUS_OK; +} + + +/**Run-time image area size*/ +unsigned int CODE_RUN_AREA_LEN = 0; + +/**Area can be used by User in 1M position*/ +unsigned int USER_ADDR_START = 0; +unsigned int TLS_FLASH_PARAM_DEFAULT = 0; +unsigned int USER_AREA_LEN = 0; +unsigned int USER_ADDR_END = 0; + + +/**Upgrade image header area & System parameter area */ +unsigned int CODE_UPD_HEADER_ADDR = 0; +unsigned int TLS_FLASH_PARAM1_ADDR = 0; +unsigned int TLS_FLASH_PARAM2_ADDR = 0; +unsigned int TLS_FLASH_PARAM_RESTORE_ADDR = 0; + +/**Upgrade image area*/ +unsigned int CODE_UPD_START_ADDR = 0; +unsigned int CODE_UPD_AREA_LEN = 0; + +/**Area can be used by User in 2M position*/ +unsigned int EX_USER_ADDR_START = 0; +unsigned int EX_USER_AREA_LEN = 0; +unsigned int EX_USER_ADDR_END = 0; + +unsigned int TLS_FLASH_END_ADDR = 0; + +void tls_fls_layout_init(void) +{ + T_BOOTER tbooter; + + tls_fls_read(CODE_RUN_HEADER_ADDR, (u8 *)&tbooter, sizeof(tbooter)); + switch (flashtotalsize) + { + case 0x200000: /*2M*/ + { + if (IMG_TYPE_OLD_PLAIN == tbooter.img_type) + { + //printf("2M use old layout\r\n"); + /**Run-time image area size*/ + CODE_RUN_AREA_LEN = (896*1024 - 256); + + /**Area can be used by User in 1M position*/ + USER_ADDR_START = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); + TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); + USER_AREA_LEN = (48*1024); + USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); + + + /**Upgrade image header area & System parameter area */ + CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN); + TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); + TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); + TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); + + /**Upgrade image area*/ + CODE_UPD_START_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000); + CODE_UPD_AREA_LEN = (704*1024); + + /**Area can be used by User in 2M position*/ + EX_USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); + EX_USER_AREA_LEN = (320*1024); + EX_USER_ADDR_END = (EX_USER_ADDR_START + EX_USER_AREA_LEN - 1); + + TLS_FLASH_END_ADDR = (EX_USER_ADDR_END); + + } + else + { + //printf("2M use new layout\r\n"); + /**Run-time image area size*/ + CODE_RUN_AREA_LEN = (960*1024 - 256); + + /**Upgrade image area*/ + CODE_UPD_START_ADDR = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); + CODE_UPD_AREA_LEN = (768*1024); + + /**Area can be used by User*/ + USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); + TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); + USER_AREA_LEN = (240*1024); + USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); + + /**Area can be used by User in 2M position*/ + EX_USER_ADDR_START = 0; + EX_USER_AREA_LEN = 0; + EX_USER_ADDR_END = 0; + + + /**Upgrade image header area & System parameter area */ + CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN) ; + TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); + TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); + TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); + TLS_FLASH_END_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000 -1); + } + } + break; + default: /*1M*/ + { + //printf("1M layout\r\n"); + /**Run-time image area size*/ + CODE_RUN_AREA_LEN = (512*1024 - 256); + + /**Upgrade image area*/ + CODE_UPD_START_ADDR = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); + CODE_UPD_AREA_LEN = (384*1024); + + /**Area can be used by User*/ + USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); + TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); + USER_AREA_LEN = (48*1024); + USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); + + /**Area can be used by User in 2M position*/ + EX_USER_ADDR_START = 0; + EX_USER_AREA_LEN = 0; + EX_USER_ADDR_END = 0; + + /**Upgrade image header area & System parameter area */ + CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN); + TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); + TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); + TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); + TLS_FLASH_END_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000 - 1); + + } + break; + } +} + diff --git a/sdk/Platform/Common/mem/wm_mem.c b/sdk/Platform/Common/mem/wm_mem.c new file mode 100644 index 0000000..beea305 --- /dev/null +++ b/sdk/Platform/Common/mem/wm_mem.c @@ -0,0 +1,765 @@ +/***************************************************************************** +* +* File Name : wm_mem.c +* +* Description: memory manager Module +* +* Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-6-12 +*****************************************************************************/ + +#include "wm_osal.h" +#include "wm_mem.h" +#include "list.h" +#include + +extern u8 tls_get_isr_count(void); +/** + * This variable is set if the memory mananger has been initialized. + * This is available only for debug version of the driver + */ +bool memory_manager_initialized = false; +/** + * This mutex is used to synchronize the list of allocated + * memory blocks. This is a debug version only feature + */ +tls_os_sem_t *mem_sem; +#if WM_MEM_DEBUG + + +struct dl_list memory_used_list; +struct dl_list memory_free_list; +#define MEM_BLOCK_SIZE 600 +MEMORY_BLOCK mem_blocks[MEM_BLOCK_SIZE]; + + +u32 alloc_heap_mem_bytes = 0; +u32 alloc_heap_mem_blk_cnt = 0; +u32 alloc_heap_mem_max_size = 0; + +#define PRE_OVERSIZE 0 +#define OVERSIZE 0 + +/** + * This is a debug only function that performs memory management operations for us. + * Memory allocated using this function is tracked, flagged when leaked, and caught for + * overflows and underflows. + * + * \param size The size in bytes of memory to + * allocate + * + * \param file The full path of file where this + * function is invoked from + * \param line The line number in the file where this + * method was called from + * \return Pointer to the allocated memory or NULL in case of a failure + */ +void * mem_alloc_debug(u32 size, char* file, int line) +{ + void *buf = NULL; + u32 pad_len; + int i = 0; + u32 cpu_sr; + // + // If the memory manager has not been initialized, do so now + // + cpu_sr = tls_os_set_critical(); + if (!memory_manager_initialized) { + tls_os_status_t os_status; + memory_manager_initialized = true; + // + // NOTE: If two thread allocate the very first allocation simultaneously + // it could cause double initialization of the memory manager. This is a + // highly unlikely scenario and will occur in debug versions only. + // + os_status = tls_os_sem_create(&mem_sem, 1); + if(os_status != TLS_OS_SUCCESS) + printf("mem_alloc_debug: tls_os_sem_create mem_sem error\n"); + dl_list_init(&memory_used_list); + dl_list_init(&memory_free_list); + for(i = 0; i < MEM_BLOCK_SIZE; i++) + { + dl_list_add_tail(&memory_free_list, &mem_blocks[i].list); + } + } + tls_os_release_critical(cpu_sr); + + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + // + // Allocate the required memory chunk plus header and trailer bytes + // + pad_len = sizeof(u32) - (size & 0x3); + buf = malloc(sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + size + pad_len + OVERSIZE + sizeof(MEMORY_PATTERN)); + + if (buf) { + // + // Memory allocation succeeded. Add information about the allocated + // block in the list that tracks all allocations. + // + PMEMORY_PATTERN mem_ptn_hd; + PMEMORY_PATTERN mem_ptn_tl; + PMEMORY_BLOCK mem_blk_hd1; + + if(dl_list_empty(&memory_free_list)) + { + printf("Memory blocks empty!\n"); + free(buf); + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + tls_mem_alloc_info(); + return NULL; + } + mem_blk_hd1 = dl_list_first(&memory_free_list, MEMORY_BLOCK, list); + dl_list_del(&mem_blk_hd1->list); + dl_list_add_tail(&memory_used_list, &mem_blk_hd1->list); + alloc_heap_mem_bytes += size+sizeof(MEMORY_PATTERN)+sizeof(MEMORY_PATTERN)+pad_len + PRE_OVERSIZE + OVERSIZE; + alloc_heap_mem_blk_cnt++; + if (alloc_heap_mem_bytes > alloc_heap_mem_max_size) + { + alloc_heap_mem_max_size = alloc_heap_mem_bytes; + //printf("alloc_heap_mem_max_size=%d\n", alloc_heap_mem_max_size); + } + + mem_blk_hd1->pad = pad_len; + mem_blk_hd1->file = file; + mem_blk_hd1->line = line; + mem_blk_hd1->length = size; + mem_blk_hd1->header_pattern = (u32)buf; + + // Fill in the memory header and trailer + mem_ptn_hd = (PMEMORY_PATTERN)buf; + mem_ptn_hd->pattern0= MEM_HEADER_PATTERN; + /*mem_ptn_hd->pattern1= MEM_HEADER_PATTERN; + mem_ptn_hd->pattern2= MEM_HEADER_PATTERN; + mem_ptn_hd->pattern3= MEM_HEADER_PATTERN;*/ + + mem_ptn_tl = (PMEMORY_PATTERN)(((u8 *)(buf))+size + sizeof(MEMORY_PATTERN)+pad_len + PRE_OVERSIZE + OVERSIZE); + mem_ptn_tl->pattern0= MEM_TAILER_PATTERN; + /*mem_ptn_tl->pattern1= MEM_TAILER_PATTERN; + mem_ptn_tl->pattern2= MEM_TAILER_PATTERN; + mem_ptn_tl->pattern3= MEM_TAILER_PATTERN;*/ + + // Jump ahead by memory header so pointer returned to caller points at the right place + buf = ((u8 *)buf) + sizeof (MEMORY_PATTERN) + PRE_OVERSIZE; + +#if 0 + + printf("==>Memory was allocated from %s at line %d with length %d\n", + mem_blk_hd->file, + mem_blk_hd->line, + mem_blk_hd->length); + printf("==>mem alloc ptr = 0x%x\n", buf); + +#endif + } + else + { + printf("==>Memory was allocated from %s at line %d with length %d, allocated size %d, count %d\n", + file, + line, + size, alloc_heap_mem_bytes, alloc_heap_mem_blk_cnt); + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + tls_mem_alloc_info(); + return buf; + } + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + return buf; +} + +void * mem_calloc_debug(u32 n, u32 size, char* file, int line) +{ + void *buf = NULL; + u32 pad_len; + int i = 0; + u32 cpu_sr; + // + // If the memory manager has not been initialized, do so now + // + cpu_sr = tls_os_set_critical(); + if (!memory_manager_initialized) { + tls_os_status_t os_status; + memory_manager_initialized = true; + // + // NOTE: If two thread allocate the very first allocation simultaneously + // it could cause double initialization of the memory manager. This is a + // highly unlikely scenario and will occur in debug versions only. + // + os_status = tls_os_sem_create(&mem_sem, 1); + if(os_status != TLS_OS_SUCCESS) + printf("mem_alloc_debug: tls_os_sem_create mem_sem error\n"); + dl_list_init(&memory_used_list); + dl_list_init(&memory_free_list); + for(i = 0; i < MEM_BLOCK_SIZE; i++) + { + dl_list_add_tail(&memory_free_list, &mem_blocks[i].list); + } + } + tls_os_release_critical(cpu_sr); + + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + // + // Allocate the required memory chunk plus header and trailer bytes + // + pad_len = sizeof(u32) - ((n*size) & 0x3); + buf = malloc(sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + n*size + pad_len + OVERSIZE + sizeof(MEMORY_PATTERN)); + + if (buf) { + // + // Memory allocation succeeded. Add information about the allocated + // block in the list that tracks all allocations. + // + PMEMORY_PATTERN mem_ptn_hd; + PMEMORY_PATTERN mem_ptn_tl; + PMEMORY_BLOCK mem_blk_hd1; + + if(dl_list_empty(&memory_free_list)) + { + printf("Memory blocks empty!\n"); + free(buf); + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + tls_mem_alloc_info(); + return NULL; + } + mem_blk_hd1 = dl_list_first(&memory_free_list, MEMORY_BLOCK, list); + dl_list_del(&mem_blk_hd1->list); + dl_list_add_tail(&memory_used_list, &mem_blk_hd1->list); + alloc_heap_mem_bytes += n*size+sizeof(MEMORY_PATTERN)+sizeof(MEMORY_PATTERN)+pad_len + PRE_OVERSIZE + OVERSIZE; + alloc_heap_mem_blk_cnt++; + if (alloc_heap_mem_bytes > alloc_heap_mem_max_size) + { + alloc_heap_mem_max_size = alloc_heap_mem_bytes; + //printf("alloc_heap_mem_max_size=%d\n", alloc_heap_mem_max_size); + } + + mem_blk_hd1->pad = pad_len; + mem_blk_hd1->file = file; + mem_blk_hd1->line = line; + mem_blk_hd1->length = n*size; + mem_blk_hd1->header_pattern = (u32)buf; + + // Fill in the memory header and trailer + mem_ptn_hd = (PMEMORY_PATTERN)buf; + mem_ptn_hd->pattern0= MEM_HEADER_PATTERN; + /*mem_ptn_hd->pattern1= MEM_HEADER_PATTERN; + mem_ptn_hd->pattern2= MEM_HEADER_PATTERN; + mem_ptn_hd->pattern3= MEM_HEADER_PATTERN;*/ + + mem_ptn_tl = (PMEMORY_PATTERN)(((u8 *)(buf))+n*size + sizeof(MEMORY_PATTERN)+pad_len + PRE_OVERSIZE + OVERSIZE); + mem_ptn_tl->pattern0= MEM_TAILER_PATTERN; + /*mem_ptn_tl->pattern1= MEM_TAILER_PATTERN; + mem_ptn_tl->pattern2= MEM_TAILER_PATTERN; + mem_ptn_tl->pattern3= MEM_TAILER_PATTERN;*/ + + // Jump ahead by memory header so pointer returned to caller points at the right place + buf = ((u8 *)buf) + sizeof (MEMORY_PATTERN) + PRE_OVERSIZE; + +#if 0 + + printf("==>Memory was allocated from %s at line %d with length %d\n", + mem_blk_hd->file, + mem_blk_hd->line, + mem_blk_hd->length); + printf("==>mem alloc ptr = 0x%x\n", buf); + +#endif + } + else + { + printf("==>Memory was allocated from %s at line %d with length %d, allocated size %d, count %d\n", + file, + line, + n*size, alloc_heap_mem_bytes, alloc_heap_mem_blk_cnt); + + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + tls_mem_alloc_info(); + return buf; + } + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + return buf; +} +/** + * This routine is called to free memory which was previously allocated using MpAllocateMemory function. + * Before freeing the memory, this function checks and makes sure that no overflow or underflows have + * happened and will also try to detect multiple frees of the same memory chunk. + * + * \param p Pointer to allocated memory + */ +void mem_free_debug(void *p, char* file, int line) +{ + PMEMORY_PATTERN mem_ptn_hd; + PMEMORY_PATTERN mem_ptn_tl; + PMEMORY_BLOCK mem_blk_hd1; + u8 needfree = 0; + u8 haserr = 0; + u32 cpu_sr; + + // Jump back by memory header size so we can get to the header + mem_ptn_hd = (PMEMORY_PATTERN) (((u8 *)p) - sizeof(MEMORY_PATTERN) - PRE_OVERSIZE); + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + dl_list_for_each(mem_blk_hd1, &memory_used_list, MEMORY_BLOCK, list){ + if(mem_blk_hd1->header_pattern == (u32)mem_ptn_hd) + { + needfree = 1; + break; + } + } + if(needfree) + { + dl_list_del(&mem_blk_hd1->list); + dl_list_add_tail(&memory_free_list, &mem_blk_hd1->list); + alloc_heap_mem_bytes -= mem_blk_hd1->length + sizeof(MEMORY_PATTERN) + sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + OVERSIZE + + mem_blk_hd1->pad; + alloc_heap_mem_blk_cnt--; + } + if(needfree == 0) + { + printf("Memory Block %p was deallocated from %s at line %d \n", mem_ptn_hd, file, line); + printf("Memory %p has been deallocated!\n", p); + dl_list_for_each_reverse(mem_blk_hd1, &memory_free_list, MEMORY_BLOCK, list){ + if(mem_blk_hd1->header_pattern == (u32)mem_ptn_hd) + { + printf("Memory Block %p has been put free list!\n", mem_ptn_hd); + break; + } + } + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + tls_mem_alloc_info(); + return; + } +#if 0 + if(mem_blk_hd1->line == 976 || mem_blk_hd1->line == 983) + { + printf("Memory Block %p can not deallocated from %s at line %d \n", mem_ptn_hd, file, line); + printf("Memory %p has been deallocated!\n", p); + tls_mem_alloc_info(); + } +#endif + mem_ptn_tl = (PMEMORY_PATTERN) ((u8 *)p + mem_blk_hd1->length + mem_blk_hd1->pad + OVERSIZE); + // + // Check that header was not corrupted + // + if (mem_ptn_hd->pattern0 != MEM_HEADER_PATTERN /*|| mem_ptn_hd->pattern1 != MEM_HEADER_PATTERN + || mem_ptn_hd->pattern2 != MEM_HEADER_PATTERN || mem_ptn_hd->pattern3 != MEM_HEADER_PATTERN*/) + { + printf("Memory %p was deallocated from %s at line %d \n", p, file, line); + printf("Memory header corruption due to underflow detected at memory block %p\n", + mem_ptn_hd); + printf("Header pattern 0(0x%x)\n",//, 1(0x%x), 2(0x%x), 3(0x%x) + mem_ptn_hd->pattern0/*, + mem_ptn_hd->pattern1, + mem_ptn_hd->pattern2, + mem_ptn_hd->pattern3*/); + //printf("Dumping information about memory block. " + // "This information may itself have been " + // "corrupted and could cause machine to bugcheck.\n"); + printf("Memory was allocated from %s at line %d with length %d\n", + mem_blk_hd1->file, + mem_blk_hd1->line, + mem_blk_hd1->length); + haserr = 1; + } + +#if 0 + printf("<==free memory allocated from %s at line %d with length %d\n", + mem_blk_hd->file, + mem_blk_hd->line, + mem_blk_hd->length); + printf("<==free memory 0x%x\n", (u8 *)mem_blk_hd+sizeof(*mem_blk_hd)); +#endif + + // + // Check that trailer was not corrupted + // + if(mem_ptn_tl->pattern0 != MEM_TAILER_PATTERN /*|| mem_ptn_tl->pattern1 != MEM_TAILER_PATTERN + || mem_ptn_tl->pattern2 != MEM_TAILER_PATTERN || mem_ptn_tl->pattern3 != MEM_TAILER_PATTERN*/) { + printf("Memory %p was deallocated from %s at line %d \n", p, file, line); + printf("Memory tailer corruption due to overflow detected at %p\n", mem_ptn_hd); + printf("Tailer pattern 0(0x%x)\n",//, 1(0x%x), 2(0x%x), 3(0x%x) + mem_ptn_tl->pattern0/*, + mem_ptn_tl->pattern1, + mem_ptn_tl->pattern2, + mem_ptn_tl->pattern3*/); + //printf("Dumping information about memory block. " + // "This information may itself have been " + // "corrupted and could cause machine to bugcheck.\n"); + printf("Memory was allocated from %s at line %d with length %d\n", + mem_blk_hd1->file, mem_blk_hd1->line, mem_blk_hd1->length); + haserr = 1; + } + if(needfree){ + free(mem_ptn_hd); + } + + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + + if(haserr) + tls_mem_alloc_info(); +} + +void * mem_realloc_debug(void *mem_address, u32 size, char* file, int line) +{ + void * mem_re_addr; + u32 cpu_sr; + + if ((mem_re_addr = mem_alloc_debug(size, file, line)) == NULL){ + printf("mem_realloc_debug failed(size=%d).\n", size); + return NULL; + } + if(mem_address != NULL) + { + cpu_sr = tls_os_set_critical(); + memcpy(mem_re_addr, mem_address, size); + tls_os_release_critical(cpu_sr); + mem_free_debug(mem_address, file, line); + } + //printf("mem_realloc_debug mem_address=%p, mem_re_addr=%p, size=%d, file=%s, line=%d\n", mem_address, mem_re_addr, size, file, line); + return mem_re_addr; +} + +void tls_mem_alloc_info(void) +{ + int i; + MEMORY_BLOCK * pos; + u32 cpu_sr; + + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + printf("==>Memory was allocated size %d, count %d\n", + alloc_heap_mem_bytes, alloc_heap_mem_blk_cnt); + i = 1; + dl_list_for_each(pos, &memory_used_list, MEMORY_BLOCK, list){ + printf("Block(%2d): addr<%p>, file<%s>, line<%d>, length<%d>\n", + i, pos->header_pattern, pos->file, pos->line, pos->length); + i++; + } + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + +} + +int is_safe_addr_debug(void* p, u32 len, char* file, int line) +{ + int i; + MEMORY_BLOCK * pos; + u32 cpu_sr; + + if(((u32)p) >= (u32)0x64ae8 || ((u32)p) < (u32)0x54ae8) + { + return 1; + } + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + i = 1; + dl_list_for_each(pos, &memory_used_list, MEMORY_BLOCK, list){ + if((pos->header_pattern + sizeof (MEMORY_PATTERN) + PRE_OVERSIZE) <= ((u32)p) && ((u32)p) <= ((u32)(pos->header_pattern + sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + pos->length))) + { + if(((u32)p) + len > ((u32)(pos->header_pattern + sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + pos->length))) + { + printf("==>Memory oversize. Block(%2d): addr<%p>, file<%s>, line<%d>, length<%d>\n", + i, pos->header_pattern, pos->file, pos->line, pos->length); + break; + } + else + { + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + return 1; + } + } + //else if(((u32)p) < pos->header_pattern) + //{ + // //tls_os_release_critical(cpu_sr); + // tls_os_sem_release(mem_sem); + // return 1; + // } + i++; + } + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + printf("==>Memory is not safe addr<%p>, file<%s>, line<%d>.\n",p, file, line); + return 0; +} + +#else /* WM_MEM_DEBUG */ +#if TLS_OS_FREERTOS +u32 alloc_heap_mem_bytes = 0; +u32 alloc_heap_mem_blk_cnt = 0; +u32 alloc_heap_mem_max_size = 0; +#define OS_MEM_FLAG (0x5AA5A55A) +#define MEM_HEAD_FLAG (0xBB55B55B) +#endif + +void * mem_alloc_debug(u32 size) +{ + u32 cpu_sr = 0; + u32 *buffer = NULL; + +#if TLS_OS_FREERTOS + u32 flag = 0; +#endif + + if (!memory_manager_initialized) { + tls_os_status_t os_status; + memory_manager_initialized = true; + // + // NOTE: If two thread allocate the very first allocation simultaneously + // it could cause double initialization of the memory manager. This is a + // highly unlikely scenario and will occur in debug versions only. + // + os_status = tls_os_sem_create(&mem_sem, 1); + if(os_status != TLS_OS_SUCCESS) + printf("mem_alloc_debug: tls_os_sem_create mem_sem error\n"); + } + +#if TLS_OS_FREERTOS + size += 8; + if(tls_get_isr_count() > 0) + { + extern void *pvPortMalloc( size_t xWantedSize ); + buffer = pvPortMalloc(size); + flag = OS_MEM_FLAG; + } + else + { + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + buffer = (u32*)malloc(size); + } + + if(buffer) + { + *buffer = MEM_HEAD_FLAG; + buffer++; + *buffer = flag; + buffer++; + } + + if(tls_get_isr_count() == 0) + { + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + } +#else //UCOSII + cpu_sr = tls_os_set_critical(); + buffer = (u32*)malloc(size); + tls_os_release_critical(cpu_sr); +#endif +// if(buffer == NULL) +// { +// printf("malloc error \n"); +// } + return buffer; + +} + +void mem_free_debug(void *p) +{ + u32 cpu_sr = 0; +#if TLS_OS_FREERTOS + u32* intMemPtr = (void*)p; + + if(tls_get_isr_count() == 0) + { + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + } + + if(p) + { + intMemPtr = intMemPtr - 2; + if(*intMemPtr != MEM_HEAD_FLAG) + { + //printf("mem_free_debug ptr error!!!!!\n"); + } + else + { + intMemPtr++; + if(*intMemPtr == OS_MEM_FLAG) + { + extern void vPortFree( void *pv ); + intMemPtr -= 1; + vPortFree(intMemPtr); + } + else + { + intMemPtr -= 1; + free(intMemPtr); + } + } + } + + if(tls_get_isr_count() == 0) + { + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + } +#else //UCOSII + cpu_sr = tls_os_set_critical(); + free(p); + tls_os_release_critical(cpu_sr); +#endif +} + + +void * mem_realloc_debug(void *mem_address, u32 size) +{ + u32 * mem_re_addr = NULL; + u32 cpu_sr = 0; +#if TLS_OS_FREERTOS + u32 flag = 0; + u32 length = size + 8; + if(tls_get_isr_count() > 0) + { + extern void *pvPortMalloc( size_t xWantedSize ); + mem_re_addr = pvPortMalloc(length); + flag = OS_MEM_FLAG; + if (mem_re_addr) + { + return NULL; + } + if(mem_address != NULL) + { + memcpy((u8 *)(mem_re_addr + 2), (u8 *)mem_address, size); + mem_free_debug(mem_address); + } + } + else + { + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + if (!mem_address) + { + mem_re_addr = (u32*)malloc(length); + }else{ + mem_re_addr = (u32 *)realloc(((u32 *)mem_address-2), length); + } + } + if(mem_re_addr) + { + *mem_re_addr = MEM_HEAD_FLAG; + mem_re_addr++; + *mem_re_addr = flag; + mem_re_addr++; + } + if(tls_get_isr_count() == 0) + { + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + } +#else + cpu_sr = tls_os_set_critical(); + mem_re_addr = realloc(mem_address, size); + tls_os_release_critical(cpu_sr); +#endif + if(mem_re_addr == NULL) + { + printf("realloc error \n"); + } + return mem_re_addr; +} + +void *mem_calloc_debug(u32 n, u32 size) +{ + u32 cpu_sr = 0; + u32 *buffer = NULL; + u32 length = 0; +#if TLS_OS_FREERTOS + u32 flag = 0; + length = n*size; + length += 8; + if(tls_get_isr_count() > 0) + { + extern void *pvPortMalloc( size_t xWantedSize ); + buffer = pvPortMalloc(length); + flag = OS_MEM_FLAG; + } + else + { + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + buffer = (u32*)malloc(length); + } + if(buffer) + { + memset(buffer, 0, length); + *buffer = MEM_HEAD_FLAG; + buffer++; + *buffer = flag; + buffer++; + } + if(tls_get_isr_count() == 0) + { + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + } +#else //UCOSII + cpu_sr = tls_os_set_critical(); + buffer = (u32*)calloc(n,size); + tls_os_release_critical(cpu_sr); +#endif +// if(buffer == NULL) +// { + // printf("calloc error \n"); +// } + return buffer; +} + +/** + * @brief This function is used to get available memory + * + * @param[in] None + * + * @return None + * + * @note None. + */ +#if !defined(__CC_ARM) +extern unsigned int __HeapLimit; +#else +extern unsigned int __heap_limit; +#endif +unsigned int tls_mem_get_avail_heapsize(void) +{ + unsigned int heap_size; + int *p = NULL; + unsigned int cpu_sr = 0; + + tls_os_sem_acquire(mem_sem, 0); + cpu_sr = tls_os_set_critical(); + + p = malloc(4); + if (p) + { +#if !defined(__CC_ARM) + heap_size = (unsigned int)&__HeapLimit - (unsigned int)p; +#else + heap_size = (unsigned int)&__heap_limit - (unsigned int)p; +#endif + free(p); + p = NULL; + }else{ + heap_size = 0; + } + tls_os_release_critical(cpu_sr); + tls_os_sem_release(mem_sem); + + return heap_size; +} + +#endif /* WM_MEM_DEBUG */ + + diff --git a/sdk/Platform/Common/task/wm_wl_mbox.c b/sdk/Platform/Common/task/wm_wl_mbox.c new file mode 100644 index 0000000..24afff0 --- /dev/null +++ b/sdk/Platform/Common/task/wm_wl_mbox.c @@ -0,0 +1,160 @@ +/** + * @file wm_wl_mbox.c + * + * @brief mailbox (mbox) APIs + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#include "wm_mem.h" +#include "wm_wl_mbox.h" +#include "wm_wl_task.h" +const void * const tls_null_pointer = (void *)0; + +/** + * @brief create a malibox + * + * @param[out] *mbox pointer to the mailbox + * @param[in] size size of mailbox + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note None + */ +s8 tls_mbox_new(tls_mbox_t *mbox, int size) +{ + s8 err; + tls_os_status_t status; + + if (size == 0) + size = 10; + + status = tls_os_queue_create(mbox, size); + if (status == TLS_OS_SUCCESS) { + err = TLS_OS_SUCCESS; + } + else + err = TLS_OS_ERROR; + return err; +} + +#ifndef tls_mbox_valid +/** + * @brief check if an mbox is valid/allocated + * + * @param[in] mbox pointer to the mailbox + * + * @retval 0 invalid + * @retval 1 valid + * + * @note None + */ +int tls_mbox_valid(tls_mbox_t mbox) +{ + if (mbox == NULL) + return 0; + else + return 1; +} +#endif + +/** + * @brief sends a message to a mailbox + * + * @param[in] mbox pointer to the mailbox + * @param[in] *msg pointer to the message to be post + * + * @return None + * + * @note None + */ +void tls_mbox_post(tls_mbox_t mbox, void *msg) +{ + u8 err; + u8 i=0; + + if(msg == NULL ) + msg = (void*)tls_null_pointer; + + /* try 10 times */ + while (i < 10) + { + err = tls_os_queue_send(mbox, msg, 0); + if(err == TLS_OS_SUCCESS) + break; + i++; + tls_os_time_delay(5); + } +} + +/** + * @brief posts the "msg" to the mailbox. + * + * @param[in] mbox pointer to the mailbox + * @param[in] *msg pointer to the message to be post + * + * @retval TLS_OS_SUCCESS success + * @retval TLS_OS_ERROR failed + * + * @note this function have to block until the "msg" is really posted. + */ +s8 tls_mbox_trypost(tls_mbox_t mbox, void *msg) +{ + u8 err; + + if( msg == NULL ) + { + msg = (void*)tls_null_pointer; + } + + err = tls_os_queue_send(mbox, msg, 0); + if(err == TLS_OS_SUCCESS) + return TLS_OS_SUCCESS; + + return TLS_OS_ERROR; +} + +/** + * @brief waits for a message within the specified time + * + * @param[in] mbox pointer to the mailbox + * @param[out] **msg pointer to the message to be received + * @param[in] timeout the specified time + * + * @retval SYS_ARCH_TIMEOUT time out + * @retval other time of elapsed + * + * @note None + */ +u32 tls_arch_mbox_fetch(tls_mbox_t mbox, void **msg, u32 timeout) +{ + u8 err; + u32 ucos_timeout = 0; + u32 in_timeout = timeout * HZ / 1000; + u32 tick_start, tick_stop, tick_elapsed; + + if(timeout && in_timeout == 0) + in_timeout = 1; + + tick_start = tls_os_get_time(); +// *msg = OSQPend(mbox, in_timeout, &err); + err = tls_os_queue_receive(mbox,msg,0,in_timeout); +// if (err == OS_ERR_TIMEOUT ) { + if (err != TLS_OS_SUCCESS ) { + ucos_timeout = SYS_ARCH_TIMEOUT; + return ucos_timeout; + } + tick_stop = tls_os_get_time(); + + // Take care of wrap-around. + if( tick_stop >= tick_start ) + tick_elapsed = tick_stop - tick_start; + else + tick_elapsed = 0xFFFFFFFF - tick_start + tick_stop ; + + + return tick_elapsed * 1000/HZ; +} + diff --git a/sdk/Platform/Common/task/wm_wl_task.c b/sdk/Platform/Common/task/wm_wl_task.c new file mode 100644 index 0000000..b5b76de --- /dev/null +++ b/sdk/Platform/Common/task/wm_wl_task.c @@ -0,0 +1,164 @@ +#include "wm_mem.h" +#include "wm_wl_timers.h" +#include "wm_wl_task.h" +#include + +static tls_mbox_t task_mbox[TLS_MBOX_ALL_COUNT]; +struct task_msg wl_msg[TLS_MSG_ALL_COUONT]; +//#define TLS_MSG_ID_TX_MGMT_CMPLT 0 +//#define TLS_MSG_ID_MLME_TASK 1 + +static void +task_thread(void *arg) +{ + struct task_msg *msg; + start_routine fun; + void * argu; + struct task_parameter *task_param = (struct task_parameter *)arg; + + while (1) { /* MAIN Loop */ + /* wait for a message, timeouts are processed while waiting */ + tls_timeouts_mbox_fetch_p(task_param->timeo_id, task_mbox[task_param->mbox_id], (void **)&msg); + switch (msg->type) { + case TASK_MSG_CALLBACK: + msg->msg.cb.function(msg->msg.cb.ctx); + tls_mem_free(msg); + break; + case TASK_MSG_CALLBACK_STATIC: + fun = msg->msg.cbs.function; + argu = msg->msg.cbs.ctx; + msg->msg.cbs.cnt--; + fun(argu); + break; + case TASK_MSG_TIMEOUT: + tls_timeout_p(task_param->timeo_id, msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + tls_mem_free(msg); + break; + case TASK_MSG_UNTIMEOUT: + tls_untimeout_p(task_param->timeo_id, msg->msg.tmo.h, msg->msg.tmo.arg); + tls_mem_free(msg); + break; + default: + break; + } + } +} + +s8 tls_wl_task_run(struct task_parameter *task_param) +{ + if(tls_mbox_new(&task_mbox[task_param->mbox_id], task_param->mbox_size) != TLS_OS_SUCCESS) { + return -1; + } + + tls_os_task_create(NULL, "WL_TSK", + //tls_os_task_create(NULL, task_param->name, + task_thread, + (void *)task_param, + (void *)(task_param->stk_start), + task_param->stk_size * sizeof(u32), + TLS_TASK_START_PRIO+task_param->task_id, + 0); + return 0; +} + +s8 +tls_wl_task_callback_static(struct task_parameter *task_param, start_routine function, void *ctx, u8 block, u8 msg_id) +{ + struct task_msg *msg = &wl_msg[msg_id]; + + if (msg->msg.cbs.cnt > 0) + return TLS_OS_ERROR; + + if (tls_mbox_valid(task_mbox[task_param->mbox_id])) { + msg->type = TASK_MSG_CALLBACK_STATIC; + msg->msg.cbs.function = function; + msg->msg.cbs.ctx = ctx; + if (block) { + tls_mbox_post(task_mbox[task_param->mbox_id], (void *)msg); + } + else { + if (tls_mbox_trypost(task_mbox[task_param->mbox_id], (void *)msg) != TLS_OS_SUCCESS) + return TLS_OS_ERROR; + } + msg->msg.cbs.cnt++; + return TLS_OS_SUCCESS; + } + return TLS_OS_ERROR; +} + +s8 +tls_wl_task_callback(struct task_parameter *task_param, start_routine function, void *ctx, u8 block) +{ + struct task_msg *msg; + + if (tls_mbox_valid(task_mbox[task_param->mbox_id])) { + msg = (struct task_msg *)tls_mem_alloc(sizeof(struct task_msg)); + if (msg == NULL) { + return TLS_OS_ERROR; + } + + msg->type = TASK_MSG_CALLBACK; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + if (block) { + tls_mbox_post(task_mbox[task_param->mbox_id], msg); + } else { + if (tls_mbox_trypost(task_mbox[task_param->mbox_id], msg) != TLS_OS_SUCCESS) { + //printf("tls_mbox_trypost error.\n"); + tls_mem_free(msg); + return TLS_OS_ERROR; + } + } + return TLS_OS_SUCCESS; + } + return TLS_OS_ERROR; +} + +s8 +tls_wl_task_add_timeout(struct task_parameter *task_param, u32 msecs, tls_timeout_handler h, void *arg) +{ + struct task_msg *msg; + + if (tls_mbox_valid(task_mbox[task_param->mbox_id])) { + msg = (struct task_msg *)tls_mem_alloc(sizeof(struct task_msg)); + if (msg == NULL) { + return TLS_OS_ERROR; + } + + msg->type = TASK_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + tls_mbox_post(task_mbox[task_param->mbox_id], msg); + return TLS_OS_SUCCESS; + } + return TLS_OS_ERROR; +} + +s8 +tls_wl_task_untimeout(struct task_parameter *task_param, tls_timeout_handler h, void *arg) +{ + struct task_msg *msg; + + if (tls_mbox_valid(task_mbox[task_param->mbox_id])) { + msg = (struct task_msg *)tls_mem_alloc(sizeof(struct task_msg)); + if (msg == NULL) { + return TLS_OS_ERROR; + } + + msg->type = TASK_MSG_UNTIMEOUT; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + tls_mbox_post(task_mbox[task_param->mbox_id], msg); + return TLS_OS_SUCCESS; + } + return TLS_OS_ERROR; +} + +s8 +tls_wl_task_init(void) +{ + memset(wl_msg, 0, sizeof(struct task_msg)); + return 0; +} + diff --git a/sdk/Platform/Common/task/wm_wl_timers.c b/sdk/Platform/Common/task/wm_wl_timers.c new file mode 100644 index 0000000..806bddc --- /dev/null +++ b/sdk/Platform/Common/task/wm_wl_timers.c @@ -0,0 +1,180 @@ +#include +#include "wm_mem.h" +#include "wm_wl_timers.h" +#include "wm_wl_task.h" + +struct tls_timeo { + struct tls_timeo *next; + u32 time; + tls_timeout_handler h; + void *arg; +}; + +/** The one and only timeout list */ +struct tls_timeo *next_timeout[TLS_TIMEO_ALL_COUONT]; + +/** + * @brief Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed + * + * @param[in] timeo_assigned timer NO. by assigned + * @param[in] mbox the mbox to fetch the message from + * @param[out] **msg the place to store the message + * + * @return None + * + * @note None + */ +void tls_timeouts_mbox_fetch_p(u8 timeo_assigned, tls_mbox_t mbox, void **msg) +{ + u32 time_needed; + struct tls_timeo *tmptimeout; + tls_timeout_handler handler; + void *arg; + + struct tls_timeo **timeo = &next_timeout[timeo_assigned]; + + again: + if (!(*timeo)) { + time_needed = tls_arch_mbox_fetch(mbox, msg, 0); + } else { + if ((*timeo)->time > 0) { + time_needed = tls_arch_mbox_fetch(mbox, msg, (*timeo)->time); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = *timeo; + *timeo = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; + tls_mem_free(tmptimeout); + if (handler != NULL) { + handler(arg); + } + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < (*timeo)->time) { + (*timeo)->time -= time_needed; + } else { + (*timeo)->time = 0; + } + } + } +} + +/** + * @brief create a one-shot timer (aka timeout) + * + * @param[in] timeo_assigned timer NO. by assigned + * @param[in] msecs time in milliseconds after that the timer should expire + * @param[in] handler callback function that would be called by the timeout + * @param[in] *arg callback argument that would be passed to handler + * + * @return None + * + * @note while waiting for a message using sys_timeouts_mbox_fetch() + */ +void tls_timeout_p(u8 timeo_assigned, u32 msecs, tls_timeout_handler handler, void *arg) +{ + struct tls_timeo *timeout, *t; + struct tls_timeo **timeo = &next_timeout[timeo_assigned]; + + timeout = (struct tls_timeo *)tls_mem_alloc(sizeof(struct tls_timeo)); + if (timeout == NULL) { + return; + } + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; + timeout->time = msecs; + + if (*timeo == NULL) { + *timeo = timeout; + return; + } + + if ((*timeo)->time > msecs) { + (*timeo)->time -= msecs; + timeout->next = *timeo; + *timeo = timeout; + } else { + for(t = *timeo; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * @brief Go through timeout list (for this task only) and remove the first + * matching entry, even though the timeout has not triggered yet + * + * @param[in] timeo_assigned timer NO. by assigned + * @param[in] handler callback function that would be called by the timeout + * @param[in] *arg callback argument that would be passed to handler + * + * @return None + * + * @note None + */ +void tls_untimeout_p(u8 timeo_assigned, tls_timeout_handler handler, void *arg) +{ + struct tls_timeo *prev_t, *t; + struct tls_timeo **timeo = &next_timeout[timeo_assigned]; + + if (*timeo == NULL) { + return; + } + + for (t = *timeo, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == handler) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + *timeo = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + tls_mem_free(t); + return; + } + } + return; +} + +/** + * @brief timer initialized + * + * @param None + * + * @return None + * + * @note None + */ +s8 tls_wl_timer_init(void) +{ + memset(next_timeout, 0, sizeof(struct tls_timeo *) * TLS_TIMEO_ALL_COUONT); + + return 0; +} diff --git a/sdk/Platform/Common/utils/utils.c b/sdk/Platform/Common/utils/utils.c new file mode 100644 index 0000000..66bff51 --- /dev/null +++ b/sdk/Platform/Common/utils/utils.c @@ -0,0 +1,1093 @@ +/************************************************************************** + * File Name : utils.c + * Author : + * Version : 1.0 + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ +#include +#include +#include +#include + +#include "wm_include.h" +#include "tls_common.h" +#include "wm_debug.h" +#include "wm_sockets.h" +#include "utils.h" + +static const u8 crc8_tbl[256] = { + 0x00,0x91,0xe3,0x72,0x07,0x96,0xe4,0x75, + 0x0e,0x9f,0xed,0x7c,0x09,0x98,0xea,0x7b, + 0x1c,0x8d,0xff,0x6e,0x1b,0x8a,0xf8,0x69, + 0x12,0x83,0xf1,0x60,0x15,0x84,0xf6,0x67, + 0x38,0xa9,0xdb,0x4a,0x3f,0xae,0xdc,0x4d, + 0x36,0xa7,0xd5,0x44,0x31,0xa0,0xd2,0x43, + 0x24,0xb5,0xc7,0x56,0x23,0xb2,0xc0,0x51, + 0x2a,0xbb,0xc9,0x58,0x2d,0xbc,0xce,0x5f, + 0x70,0xe1,0x93,0x02,0x77,0xe6,0x94,0x05, + 0x7e,0xef,0x9d,0x0c,0x79,0xe8,0x9a,0x0b, + 0x6c,0xfd,0x8f,0x1e,0x6b,0xfa,0x88,0x19, + 0x62,0xf3,0x81,0x10,0x65,0xf4,0x86,0x17, + 0x48,0xd9,0xab,0x3a,0x4f,0xde,0xac,0x3d, + 0x46,0xd7,0xa5,0x34,0x41,0xd0,0xa2,0x33, + 0x54,0xc5,0xb7,0x26,0x53,0xc2,0xb0,0x21, + 0x5a,0xcb,0xb9,0x28,0x5d,0xcc,0xbe,0x2f, + 0xe0,0x71,0x03,0x92,0xe7,0x76,0x04,0x95, + 0xee,0x7f,0x0d,0x9c,0xe9,0x78,0x0a,0x9b, + 0xfc,0x6d,0x1f,0x8e,0xfb,0x6a,0x18,0x89, + 0xf2,0x63,0x11,0x80,0xf5,0x64,0x16,0x87, + 0xd8,0x49,0x3b,0xaa,0xdf,0x4e,0x3c,0xad, + 0xd6,0x47,0x35,0xa4,0xd1,0x40,0x32,0xa3, + 0xc4,0x55,0x27,0xb6,0xc3,0x52,0x20,0xb1, + 0xca,0x5b,0x29,0xb8,0xcd,0x5c,0x2e,0xbf, + 0x90,0x01,0x73,0xe2,0x97,0x06,0x74,0xe5, + 0x9e,0x0f,0x7d,0xec,0x99,0x08,0x7a,0xeb, + 0x8c,0x1d,0x6f,0xfe,0x8b,0x1a,0x68,0xf9, + 0x82,0x13,0x61,0xf0,0x85,0x14,0x66,0xf7, + 0xa8,0x39,0x4b,0xda,0xaf,0x3e,0x4c,0xdd, + 0xa6,0x37,0x45,0xd4,0xa1,0x30,0x42,0xd3, + 0xb4,0x25,0x57,0xc6,0xb3,0x22,0x50,0xc1, + 0xba,0x2b,0x59,0xc8,0xbd,0x2c,0x5e,0xcf +}; + +#ifndef isdigit +#define in_range(c, lo, up) ((u8)c >= lo && (u8)c <= up) +#define isdigit(c) in_range(c, '0', '9') +#endif +int chk_crc8(u8 *ptr, u32 len) +{ + u8 crc8; + u8 data; + + crc8=0; + while (len--!=0) { + data = *ptr++; + crc8 = crc8_tbl[crc8^data]; + } + + if(crc8==0x00) {return 0;} + else {return -1;} +} + +u8 get_crc8(u8 *ptr, u32 len) +{ + u8 crc8; + u8 data; + + crc8=0; + while (len--!=0) { + data = *ptr++; + crc8 = crc8_tbl[crc8^data]; + } + + return crc8; +} + +u8 calculate_crc8(u8 crc8, u8 *ptr, u32 len) +{ + u8 data; + + while (len--!=0) { + data = *ptr++; + crc8 = crc8_tbl[crc8^data]; + } + + return crc8; +} + +static u32 _cal_crc32(u32 crc_result, u8 data_8) +{ + u8 crc_out[32]; + u8 crc_buf[32]; + u8 in_data_buf[8]; + u32 i; + u32 flag; + + flag = 0x01; + + for (i = 0; i < 32; i++) { + crc_out[i] = 0; + } + + + for (i = 0; i < 8; i++) { + in_data_buf[i] = (data_8 >> i) & flag; + } + + for (i = 0; i < 32; i++) { + crc_buf[i] = (unsigned char)(crc_result >> i) & flag; + } + + crc_out[0] = in_data_buf[1]^in_data_buf[7]^crc_buf[30]^crc_buf[24]; + crc_out[1] = in_data_buf[0]^in_data_buf[1]^in_data_buf[6]^in_data_buf[7]^crc_buf[31]^crc_buf[30]^crc_buf[25]^crc_buf[24]; + crc_out[2] = in_data_buf[0]^in_data_buf[1]^in_data_buf[5]^in_data_buf[6]^in_data_buf[7]^crc_buf[31]^crc_buf[30]^crc_buf[26]^crc_buf[25]^crc_buf[24]; + crc_out[3] = in_data_buf[0]^in_data_buf[4]^in_data_buf[5]^in_data_buf[6]^crc_buf[31]^crc_buf[27]^crc_buf[26]^crc_buf[25]; + crc_out[4] = in_data_buf[1]^in_data_buf[3]^in_data_buf[4]^in_data_buf[5]^in_data_buf[7]^crc_buf[30]^crc_buf[28]^crc_buf[27]^crc_buf[26]^crc_buf[24]; + crc_out[5] = in_data_buf[0]^in_data_buf[1]^in_data_buf[2]^in_data_buf[3]^in_data_buf[4]^in_data_buf[6]^in_data_buf[7]^ + crc_buf[31]^crc_buf[30]^crc_buf[29]^crc_buf[28]^crc_buf[27]^crc_buf[25]^crc_buf[24]; + crc_out[6] = in_data_buf[0]^in_data_buf[1]^in_data_buf[2]^in_data_buf[3]^in_data_buf[5]^in_data_buf[6]^ + crc_buf[31]^crc_buf[30]^crc_buf[29]^crc_buf[28]^crc_buf[26]^crc_buf[25]; + crc_out[7] = in_data_buf[0]^in_data_buf[2]^in_data_buf[4]^in_data_buf[5]^in_data_buf[7]^crc_buf[31]^crc_buf[29]^crc_buf[27]^crc_buf[26]^crc_buf[24]; + crc_out[8] = in_data_buf[3]^in_data_buf[4]^in_data_buf[6]^in_data_buf[7]^crc_buf[28]^crc_buf[27]^crc_buf[25]^crc_buf[24]^crc_buf[0]; + crc_out[9] = in_data_buf[2]^in_data_buf[3]^in_data_buf[5]^in_data_buf[6]^crc_buf[29]^crc_buf[28]^crc_buf[26]^crc_buf[25]^crc_buf[1]; + crc_out[10] = in_data_buf[2]^in_data_buf[4]^in_data_buf[5]^in_data_buf[7]^crc_buf[29]^crc_buf[27]^crc_buf[26]^crc_buf[24]^crc_buf[2]; + crc_out[11] = in_data_buf[3]^in_data_buf[4]^in_data_buf[6]^in_data_buf[7]^crc_buf[28]^crc_buf[27]^crc_buf[25]^crc_buf[24]^crc_buf[3]; + + crc_out[12] = in_data_buf[1]^in_data_buf[2]^in_data_buf[3]^in_data_buf[5]^in_data_buf[6]^in_data_buf[7]^ + crc_buf[30]^crc_buf[29]^crc_buf[28]^crc_buf[26]^crc_buf[25]^crc_buf[24]^crc_buf[4]; + crc_out[13] = in_data_buf[0]^in_data_buf[1]^in_data_buf[2]^in_data_buf[4]^in_data_buf[5]^in_data_buf[6]^ + crc_buf[31]^crc_buf[30]^crc_buf[29]^crc_buf[27]^crc_buf[26]^crc_buf[25]^crc_buf[5]; + crc_out[14] = in_data_buf[0]^in_data_buf[1]^in_data_buf[3]^in_data_buf[4]^in_data_buf[5]^crc_buf[31]^crc_buf[30]^crc_buf[28]^crc_buf[27]^crc_buf[26]^crc_buf[6]; + crc_out[15] = in_data_buf[0]^in_data_buf[2]^in_data_buf[3]^in_data_buf[4]^crc_buf[31]^crc_buf[29]^crc_buf[28]^crc_buf[27]^crc_buf[7]; + crc_out[16] = in_data_buf[2]^in_data_buf[3]^in_data_buf[7]^crc_buf[29]^crc_buf[28]^crc_buf[24]^crc_buf[8]; + crc_out[17] = in_data_buf[1]^in_data_buf[2]^in_data_buf[6]^crc_buf[30]^crc_buf[29]^crc_buf[25]^crc_buf[9]; + crc_out[18] = in_data_buf[0]^in_data_buf[1]^in_data_buf[5]^crc_buf[31]^crc_buf[30]^crc_buf[26]^crc_buf[10]; + crc_out[19] = in_data_buf[0]^in_data_buf[4]^crc_buf[31]^crc_buf[27]^crc_buf[11]; + crc_out[20] = in_data_buf[3]^crc_buf[28]^crc_buf[12]; + crc_out[21] = in_data_buf[2]^crc_buf[29]^crc_buf[13]; + crc_out[22] = in_data_buf[7]^crc_buf[24]^crc_buf[14]; + crc_out[23] = in_data_buf[1]^in_data_buf[6]^in_data_buf[7]^crc_buf[30]^crc_buf[25]^crc_buf[24]^crc_buf[15]; + crc_out[24] = in_data_buf[0]^in_data_buf[5]^in_data_buf[6]^crc_buf[31]^crc_buf[26]^crc_buf[25]^crc_buf[16]; + crc_out[25] = in_data_buf[4]^in_data_buf[5]^crc_buf[27]^crc_buf[26]^crc_buf[17]; + crc_out[26] = in_data_buf[1]^in_data_buf[3]^in_data_buf[4]^in_data_buf[7]^crc_buf[30]^crc_buf[28]^crc_buf[27]^crc_buf[24]^crc_buf[18]; + crc_out[27] = in_data_buf[0]^in_data_buf[2]^in_data_buf[3]^in_data_buf[6]^crc_buf[31]^crc_buf[29]^crc_buf[28]^crc_buf[25]^crc_buf[19]; + crc_out[28] = in_data_buf[1]^in_data_buf[2]^in_data_buf[5]^crc_buf[30]^crc_buf[29]^crc_buf[26]^crc_buf[20]; + crc_out[29] = in_data_buf[0]^in_data_buf[1]^in_data_buf[4]^crc_buf[31]^crc_buf[30]^crc_buf[27]^crc_buf[21]; + crc_out[30] = in_data_buf[0]^in_data_buf[3]^crc_buf[31]^crc_buf[28]^crc_buf[22]; + crc_out[31] = in_data_buf[2]^crc_buf[23]^crc_buf[29]; + + crc_result = 0; + for (i = 0; i < 32; i++) { + if (crc_out[i]) {crc_result |= (1<>i) & 0x1) {val |= (1<<(31-i));} + } + +// TLS_DBGPRT_INFO("calculate crc -0x%x .\n", ~val); + return ~val; +} + +u32 checksum(u32 *data, u32 length, u32 init) +{ + static long long sum = 0; + u32 checksum; + u32 i; + + /* + Calculate the checksum. + */ + if (!init) {sum = 0;} + + for (i = 0; i < length; i++) {sum+=*(data + i);} + checksum = ~((u32)(sum>>32)+(u32)sum); + + return checksum; +} + +int atodec(char ch) +{ + int dec = -1; + + if ((ch >= '0') && (ch <= '9')) {dec = ch - '0';} + + return dec; +} + +int strtodec(int *dec, char *str) +{ + int i; + int dd; + int sign; + + i = -1; + dd = 0; + sign = 1; + + if (*str == '-') { + str++; + sign = -1; + } + + while (*str) { + i = atodec(*str++); + if (i < 0) {return -1;} + dd = dd*10 + i; + } + + *dec = dd*sign; + + return ((i < 0) ? -1 : 0); +} + +int atohex(char ch) +{ + int hex; + + hex = -1; + + if ((ch >= '0') && (ch <= '9')) {hex = ch - '0';} + else if ((ch >= 'a') && (ch <= 'f')) {hex = ch - 'a' + 0xa;} + else if ((ch >= 'A') && (ch <= 'F')) {hex = ch - 'A' + 0xa;} + + return hex; +} + +int strtohex(u32 *hex, char *str) +{ + int n; + int i; + u32 dd; + + n = -1; + i = 0; + dd = 0; + + while(*str){ + n = atohex(*str++); + if (n < 0) {return -1;} + dd = (dd<<4) + n; + if (++i > 8){return -1;} + } + + *hex = dd; + + return (n<0?-1:0); +} + +int strtohexarray(u8 array[], int cnt, char *str) +{ + int hex; + u8 tmp; + u8 *des; + + des = array; + + while (cnt-- > 0) { + hex = atohex(*str++); + if (hex < 0) {return -1;} + else {tmp = (hex << 4) & 0xf0;} + + hex = atohex(*str++); + if (hex < 0) {return -1;} + else {tmp = tmp | (hex & 0x0f);} + + *des++ = (u8) tmp; + } + + return ((*str==0) ? 0 : -1); +} + +int strtoip(u32 *ipadr, char * str) +{ + int n; + u32 i; + u32 ip; + char *head; + char *tail; + + ip = 0; + head = str; + tail = str; + + for (i = 0; i < 3; ) { + if (*tail == '.') { + i++; + *tail = 0; + ip <<= 8; + if (strtodec(&n, head) < 0) {return -1;} + if ((n < 0) || (n > 255)) {return -1;} + ip += n; + *tail = '.'; + head = tail + 1; + } + tail++; + } + + if (i < 3) {return -1;} + + ip <<= 8; + if (strtodec(&n, head) < 0) {return -1;} + if ((n < 0) || (n > 255)) {return -1;} + ip += n; + + *ipadr = ip; + + return ((ip == 0) ? -1 : 0); +} + +void iptostr(u32 ip, char *str) +{ + sprintf(str, "%d.%d.%d.%d", + ((ip >> 24) & 0xff),((ip >> 16) & 0xff),\ + ((ip >> 8) & 0xff), ((ip >> 0) & 0xff)); +} + + +void mactostr(u8 mac[], char *str) +{ + sprintf(str, "%02x%02x%02x%02x%02x%02x", + mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); +} + + +int hex_to_digit(int c) +{ + if( '0' <= c && c <= '9' ) + return c - '0'; + if( 'A' <= c && c <= 'F' ) + return c - ('A' - 10); + if( 'a' <= c && c <= 'f' ) + return c - ('a' - 10); + return -1; +} + +int digit_to_hex(int c) +{ + if( 0 <= c && c <= 9 ) + return c + '0'; + if( 0xA <= c && c <= 0xF ) + return c - 0xA + 'A' ; + return -1; +} + +int hexstr_to_unit(char *buf, u32 *d) +{ + int i; + int len = strlen(buf); + int c; + *d = 0; + + if (len > 8) + return -1; + for (i=0; i 11 || len == 0) + return -1; + for(i=0; i 255) + return -1; + addr[count] = in[count]; + } + return 0; +} + + +char * strdup(const char *s) +{ + char * ret; + int len; + if(s == NULL) + return NULL; + len = strlen(s) + 1; + ret = tls_mem_alloc(len); + if(ret == NULL) + return NULL; + memset(ret, 0, len); + memcpy(ret, s, len-1); + return ret; +} + +char * strndup(const char *s, size_t len) +{ + char * ret; + if(s == NULL) + return NULL; + ret = tls_mem_alloc(len + 1); + if(ret == NULL) + return NULL; + memset(ret, 0, len + 1); + memcpy(ret, s, len); + return ret; +} + +int gettimeofday(struct timeval *tv, void *tz) +{ + int ret = 0; + u32 current_tick; + + current_tick = tls_os_get_time();//OSTimeGet(); + tv->tv_sec = (current_tick) / 100; + tv->tv_usec = 10000 * (current_tick % 100); + return ret; +} + +int sendchar(int ch) +{ +#if TLS_CONFIG_IOUART + return tls_iouart_output_char(ch); +#else + return tls_uart_output_char(ch); +#endif +} + +//function: ½«Êý¾Ýת»»³É×Ö·û´® +//paramter: +//str : ת»»Ö®ºó´æÔÚ´Ëbuffer +//num : Ҫת»»µÄÊý¾Ý +//base : Ê®½øÖÆ£¬Ê®Áù½øÖÆ +//width : ¶ÔÆë¿í¶È +//opflag : ²Ù×÷·û£¬bit¶¨ÒåÈçÏ + +#define P_ALIGN_BIT (0x01<<0) // bit=1 ×ó¶ÔÆë bit=0ÓÒ¶ÔÆë +#define P_FILL_BIT (0x01<<1) //bit = 1Ìî³ä'0'£¬·ñÔòÌî³ä' ' +#define P_BIG_BIT (0x01<<2) //bit=1£¬´óд£¬·ñÔòСд + +int Int2Str(char *str,int num,char base,char width,int opflag) +{ + char temp; + int len = 0; + signed char k = 0; + char *str_bk; + signed char k_bk; + + if(num <0) + { + num = -num; + *str='-'; + str++; + len++; + } + if(0 == num) + { + *str = '0'; + str ++; + k ++; + } + while(num) + { + temp= num%base; + if(temp > 9) // insert hexdecimal--ABCDEF-- + { + temp-=10; + if(opflag & P_BIG_BIT) + *str = temp + 'A'; + else + *str = temp + 'a'; + } + else + { + *str = temp + '0'; + } + num=num/base; + str++; + k++; + } + + if(opflag&P_ALIGN_BIT) //×ó¶ÔÆë + { + str_bk = str; + k_bk = k; //Ïȱ¸·ÝÖ¸ÕëºÍ³¤¶È£¬µ¹ÐòÖ®ºó¼ÌÐø + str --; + k --; + while(k>0) + { + temp = *str; + *str = *(str-k); + *(str-k) = temp; + str--; + k-=2; + } + k = k_bk; + str = str_bk; + } + + //²»×ã¿í¶ÈµÄÓÃ' '²¹Æë + while(width>k) + { + if(opflag&P_FILL_BIT) + { + *str++ ='0'; + } + else + { + *str++ =' '; + } + k++; + } + + len=len+k; + *str-- = '\0'; + k--; + if(0 == (opflag&P_ALIGN_BIT)) //ÓÒ¶ÔÆë + { + //µ¹Ðò + while(k>0) + { + temp = *str; + *str = *(str-k); + *(str-k) = temp; + str--; + k-=2; + } + } + return len; +} + +static int IP2Str(unsigned char v4v6, unsigned int *inuint, char *outtxt) +{ + unsigned char i; + unsigned char j = 0; + unsigned char k; + unsigned char h; + unsigned char m; + unsigned char l; + unsigned char bit; + + if (4 == v4v6) + { + for(i = 0; i < 4; i++) + { + bit = (*inuint >> (8 * i)) & 0xff; + h = bit / 100; + if (h) + outtxt[j++] = '0' + h; + m = (bit % 100) / 10; + if (m) + { + outtxt[j++] = '0' + m; + } + else + { + if (h) + outtxt[j++] = '0'; + } + l = (bit % 100) % 10; + outtxt[j++] = '0' + l; + outtxt[j++] = '.'; + } + } + else + { + for (k = 0; k < 4; k++) + { + for(i = 0; i < 4; i++) + { + m = (*inuint >> (8 * i)) & 0xff; + h = m >> 4; + l = m & 0xf; + if (h > 9) + outtxt[j++] = 'A' + h - 10; + else + outtxt[j++]= '0' + h; + if (l > 9) + outtxt[j++] = 'A' + l - 10; + else + outtxt[j++] = '0' + l; + if (0 != (i % 2)) + outtxt[j++] = ':'; + } + inuint++; + } + } + + outtxt[j - 1] = 0; + return j - 1; +} + +static int Mac2Str(unsigned char *inchar, char *outtxt) +{ + unsigned char hbit,lbit; + unsigned int i; + + for(i = 0; i < 6; i++)/* mac length */ + { + hbit = (*(inchar + i) & 0xf0) >> 4; + lbit = *(inchar + i ) & 0x0f; + if (hbit > 9) + outtxt[3 * i] = 'A' + hbit - 10; + else + outtxt[3 * i]= '0' + hbit; + if (lbit > 9) + outtxt[3 * i + 1] = 'A' + lbit - 10; + else + outtxt[3 * i + 1] = '0' + lbit; + outtxt[3 * i + 2] = '-'; + } + + outtxt[3 * (i - 1) + 2] = 0; + + return 3 * (i - 1) + 2; +} + +int wm_vprintf(const char *fmt, va_list arg_ptr) +{ + unsigned char width=0; //±£Áô¿í¶È + unsigned int len; //Êý¾Ý¿í¶È + char *fp = (char *)fmt; + //va_list arg_ptr; + char *pval; + int opflag = 0; + char store[40]; + char c; + int i; + char* str; + + //va_start(arg_ptr, fmt); //arg_ptr Ö¸ÏòµÚÒ»¸ö²ÎÊý + while (*fp !='\0') + { + c = *fp++; + if (c != '%') + { + sendchar(c); + } + else + { + width = 0; //»ñÈ¡Êý¾Ý¿í¶È + opflag = 0; + if('-' == *fp) + { + opflag |= P_ALIGN_BIT;//×ó¶ÔÆë + fp ++; + } + if('0' == *fp) //Ç°Ãæ²¹Áã + { + opflag |= P_FILL_BIT; //²¹Áã + fp ++; + } + + while(*fp>='0'&&*fp<='9') + { + width = width * 10 + (*fp) - '0'; + fp++; + } + if('.' == *fp) //¸¡µãÔËËãÔÝʱûÓ㬲»´¦Àí + { + fp ++; + while(*fp>='0'&&*fp<='9') + { + fp++; + } + } + + while('l' == *fp || 'h' == *fp) + { + fp ++; + } + + switch (*fp) + { + case 'c': + case 'C': + c = (char)va_arg(arg_ptr, int); + sendchar(c); + break; + case 'd': + case 'i': + case 'u': + i = va_arg(arg_ptr, int); + str = store; + Int2Str(store,i,10,width,opflag); + while( *str != '\0') sendchar(*str++); + break; + case 'x': + case 'X': + i = va_arg(arg_ptr, int); + str = store; + if('X' == *fp) + { + opflag |= P_BIG_BIT; + } + Int2Str(store,i,16,width,opflag); + while( *str != '\0') sendchar(*str++); + break; + case 'o': + i = va_arg(arg_ptr, int); + str = store; + Int2Str(store,i,8,width,opflag); + while( *str != '\0') sendchar(*str++); + break; + case 's': + case 'S': + pval=va_arg(arg_ptr,char*); + len = strlen(pval); + if((width > len) && (0 == (opflag&P_ALIGN_BIT))) //ÓÒ¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //×ó±ß²¹¿Õ¸ñ + { + sendchar(' '); + } + } + for(i=0;i < len;i++) + { + sendchar(pval[i]); + } + if((width > len) && (opflag&P_ALIGN_BIT)) //×ó¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //Óұ߲¹¿Õ¸ñ + { + sendchar(' '); + } + } + break; + case 'v':/* ip v4 address */ + i = va_arg(arg_ptr, int); + len = IP2Str(4, (unsigned int *)&i, store); + if((width > len) && (0 == (opflag&P_ALIGN_BIT))) //ÓÒ¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //×ó±ß²¹¿Õ¸ñ + { + sendchar(' '); + } + } + str = store; + while( *str != '\0') sendchar(*str++); + if((width > len) && (opflag&P_ALIGN_BIT)) //×ó¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //Óұ߲¹¿Õ¸ñ + { + sendchar(' '); + } + } + break; + case 'V':/* ip v6 address */ + pval=va_arg(arg_ptr,char*); + len = IP2Str(6, (unsigned int *)pval, store); + if((width > len) && (0 == (opflag&P_ALIGN_BIT))) //ÓÒ¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //×ó±ß²¹¿Õ¸ñ + { + sendchar(' '); + } + } + str = store; + while( *str != '\0') sendchar(*str++); + if((width > len) && (opflag&P_ALIGN_BIT)) //×ó¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //Óұ߲¹¿Õ¸ñ + { + sendchar(' '); + } + } + break; + case 'M':/* mac address */ + pval = va_arg(arg_ptr, char*); + len = Mac2Str((unsigned char *)pval, store); + if((width > len) && (0 == (opflag&P_ALIGN_BIT))) //ÓÒ¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //×ó±ß²¹¿Õ¸ñ + { + sendchar(' '); + } + } + str = store; + while( *str != '\0') sendchar(*str++); + if((width > len) && (opflag&P_ALIGN_BIT)) //×ó¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //Óұ߲¹¿Õ¸ñ + { + sendchar(' '); + } + } + break; + case '%': + sendchar('%'); + break; + default: + break; + } + fp++; + } + } + //va_end(arg_ptr); + return 0; + +} + +int wm_printf(const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + wm_vprintf(fmt,ap); + va_end(ap); + return 0; +} + +int wm_vsnprintf(char *outstr, size_t size, const char *fmt, va_list arg_ptr) +{ + unsigned char width=0; //±£Áô¿í¶È + unsigned int len; //Êý¾Ý¿í¶È + char *fp = (char *)fmt; + char *pval; + int opflag = 0; + char store[40]; + char c; + int i; + char* str; + int ret = 0; + + while (*fp !='\0') + { + c = *fp++; + if (c != '%') + { + if ((ret + 1) >= size) + break; + outstr[ret++] = c; + } + else + { + width = 0; //»ñÈ¡Êý¾Ý¿í¶È + opflag = 0; + if('-' == *fp) + { + opflag |= P_ALIGN_BIT;//×ó¶ÔÆë + fp ++; + } + if('0' == *fp) //Ç°Ãæ²¹Áã + { + opflag |= P_FILL_BIT; //²¹Áã + fp ++; + } + + while(*fp>='0'&&*fp<='9') + { + width = width * 10 + (*fp) - '0'; + fp++; + } + if('.' == *fp) //¸¡µãÔËËãÔÝʱûÓ㬲»´¦Àí + { + fp ++; + while(*fp>='0'&&*fp<='9') + { + fp++; + } + } + + while('l' == *fp || 'h' == *fp) + { + fp ++; + } + + switch (*fp) + { + case 'c': + case 'C': + c = (char)va_arg(arg_ptr, int); + if ((ret + 1) >= size) + break; + outstr[ret++] = c; + break; + case 'd': + case 'i': + case 'u': + i = va_arg(arg_ptr, int); + str = store; + Int2Str(store,i,10,width,opflag); + while( *str != '\0') + { + if ((ret + 1) >= size) + break; + outstr[ret++] = *str++; + } + break; + case 'x': + case 'X': + i = va_arg(arg_ptr, int); + str = store; + if('X' == *fp) + { + opflag |= P_BIG_BIT; + } + Int2Str(store,i,16,width,opflag); + while( *str != '\0') + { + if ((ret + 1) >= size) + break; + outstr[ret++] = *str++; + } + break; + case 'o': + i = va_arg(arg_ptr, int); + str = store; + Int2Str(store,i,8,width,opflag); + while( *str != '\0') + { + if ((ret + 1) >= size) + break; + outstr[ret++] = *str++; + } + break; + case 's': + case 'S': + pval=va_arg(arg_ptr,char*); + len = strlen(pval); + if((width > len) && (0 == (opflag&P_ALIGN_BIT))) //ÓÒ¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //×ó±ß²¹¿Õ¸ñ + { + if ((ret + 1) >= size) + break; + outstr[ret++] = ' '; + } + } + for(i=0;i < len;i++) + { + if ((ret + 1) >= size) + break; + outstr[ret++] = pval[i]; + } + if((width > len) && (opflag&P_ALIGN_BIT)) //×ó¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //Óұ߲¹¿Õ¸ñ + { + if ((ret + 1) >= size) + break; + outstr[ret++] = ' '; + } + } + break; + case 'v':/* ip v4 address */ + i = va_arg(arg_ptr, int); + len = IP2Str(4, (unsigned int *)&i, store); + if((width > len) && (0 == (opflag&P_ALIGN_BIT))) //ÓÒ¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //×ó±ß²¹¿Õ¸ñ + { + if ((ret + 1) >= size) + break; + outstr[ret++] = ' '; + } + } + str = store; + while( *str != '\0') + { + if ((ret + 1) >= size) + break; + outstr[ret++] = *str++; + } + if((width > len) && (opflag&P_ALIGN_BIT)) //×ó¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //Óұ߲¹¿Õ¸ñ + { + if ((ret + 1) >= size) + break; + outstr[ret++] = ' '; + } + } + break; + case 'V':/* ip v6 address */ + pval=va_arg(arg_ptr,char*); + len = IP2Str(6, (unsigned int *)pval, store); + if((width > len) && (0 == (opflag&P_ALIGN_BIT))) //ÓÒ¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //×ó±ß²¹¿Õ¸ñ + { + if ((ret + 1) >= size) + break; + outstr[ret++] = ' '; + } + } + str = store; + while( *str != '\0') + { + if ((ret + 1) >= size) + break; + outstr[ret++] = *str++; + } + if((width > len) && (opflag&P_ALIGN_BIT)) //×ó¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //Óұ߲¹¿Õ¸ñ + { + if ((ret + 1) >= size) + break; + outstr[ret++] = ' '; + } + } + break; + case 'M':/* mac address */ + pval = va_arg(arg_ptr, char*); + len = Mac2Str((unsigned char *)pval, store); + if((width > len) && (0 == (opflag&P_ALIGN_BIT))) //ÓÒ¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //×ó±ß²¹¿Õ¸ñ + { + if ((ret + 1) >= size) + break; + outstr[ret++] = ' '; + } + } + str = store; + while( *str != '\0') + { + if ((ret + 1) >= size) + break; + outstr[ret++] = *str++; + } + if((width > len) && (opflag&P_ALIGN_BIT)) //×ó¶ÔÆë + { + for(i = 0;i < (width - len);i ++) //Óұ߲¹¿Õ¸ñ + { + if ((ret + 1) >= size) + break; + outstr[ret++] = ' '; + } + } + break; + case '%': + if ((ret + 1) >= size) + break; + outstr[ret++] = '%'; + break; + default: + break; + } + fp++; + } + } + + outstr[ret] = '\0'; + + return ret; +} + diff --git a/sdk/Platform/Common/wm_common.c b/sdk/Platform/Common/wm_common.c new file mode 100644 index 0000000..a7ebfeb --- /dev/null +++ b/sdk/Platform/Common/wm_common.c @@ -0,0 +1,11 @@ +#ifndef WM_COMMON +#define WM_COMMON +#include "../Platform/Common/fwup/wm_fwup.c" +#include "../Platform/Common/task/wm_wl_mbox.c" +#include "../Platform/Common/task/wm_wl_task.c" +#include "../Platform/Common/task/wm_wl_timers.c" +#include "../Platform/Common/utils/utils.c" +#include "../Platform/Common/crypto/wm_crypto_hard.c" +#include "../Platform/Common/mem/wm_mem.c" +#include "../Platform/Common/Params/wm_param.c" +#endif //WM_COMMON diff --git a/sdk/Platform/Drivers/7816/wm_7816.c b/sdk/Platform/Drivers/7816/wm_7816.c new file mode 100644 index 0000000..e655227 --- /dev/null +++ b/sdk/Platform/Drivers/7816/wm_7816.c @@ -0,0 +1,291 @@ +/**************************************************************************//** + * @file wm_i2s.c + * @author + * @version + * @date + * @brief + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. All rights reserved. + *****************************************************************************/ + +#include +#include +#include +#include "wm_regs.h" +#include "wm_7816.h" +#include "wm_gpio.h" +#include "wm_uart.h" +#include "wm_cpu.h" + +#include "wm_osal.h" + +#define DEBUG_7816 1 + +#if DEBUG_7816 +#define PRINT_DEBUG(fmt, args...) do{(printf("[DEBUG] "), printf(fmt, ##args));}while(0) +#else +#define PRINT_DEBUG(fmt, args...) +#endif + +#define SYS_CLK (40000000) + +sc_io_map sc_io; + +/** + * @brief + * This function is used to config the pin in gpio or 7816 mode for the 7816 power on timing + * @param[in] mode : 1--gpio mode ; 0--7816 mode + * @retval + */ +void wm_sc_io_clk_config(uint8_t mode) +{ + if (sc_io.initialed == 0) + { + printf("error : 7816 io map must init....\r\n"); + return ; + } + if (mode) //gpio mode + { + tls_io_cfg_set(sc_io.clk_pin_num, WM_IO_OPTION5); + tls_io_cfg_set(sc_io.io_pin_num, WM_IO_OPTION5); + tls_gpio_cfg(sc_io.clk_pin_num, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_gpio_cfg(sc_io.io_pin_num, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + } + else //7816 mode + { + tls_io_cfg_set(sc_io.clk_pin_num, sc_io.clk_opt); + tls_io_cfg_set(sc_io.io_pin_num, sc_io.io_opt); + } +} + +/** + * @brief + * This function is used to config the block guard time param in 7816 mode + * @param[in] bgt : the value of blcok guard time will be set + * @retval + */ +void wm_sc_set_bgt(uint8_t bgt) +{ + uint32_t reg; + bgt = (bgt > 0x1f) ? 0x1F : bgt; + reg = tls_reg_read32(HR_UART2_LINE_CTRL); + reg &= ~(0x1f << 11); + reg |= (bgt << 11); + tls_reg_write32(HR_UART2_LINE_CTRL, reg); +} + +/** + * @brief + * This function is used to config the tx retry count when detect err signal + * @param[in] count : the value of retry time will be set 7 for max + * @retval + */ +void wm_sc_tx_retry_times(uint8_t count) +{ + uint32_t reg; + reg = tls_reg_read32(HR_UART2_LINE_CTRL); + reg &= ~(0x7 << 16); + tls_reg_write32(HR_UART2_LINE_CTRL, reg|(count<<16)); + tls_bitband_write(HR_UART2_LINE_CTRL, 19, 1); +} + +/** + * @brief + * This function is used to config the rx retry count when detect parity error + * @param[in] count : the value of retry time will be set 7 for max + * @retval + */ +void wm_sc_rx_retry_times(uint8_t count) +{ + uint32_t reg; + reg = tls_reg_read32(HR_UART2_LINE_CTRL); + reg &= ~(0x7 << 20); + tls_reg_write32(HR_UART2_LINE_CTRL, reg|(count<<20)); + tls_bitband_write(HR_UART2_LINE_CTRL, 23, 1); +} + +/** + * @brief + * This function is used to config the etu param + * @param[in] etu : the value of etu will be set + * @retval + */ +void wm_sc_set_etu(uint16_t etu) +{ + uint32_t reg; + + reg = tls_reg_read32(HR_UART2_BAUD_RATE_CTRL); + reg &= ~ 0xFFFF; + reg |= etu; + tls_reg_write32(HR_UART2_BAUD_RATE_CTRL, reg); +} + +/** + * @brief + * This function config the module clock freq + * @param[in] freq : the value of clock freq + * @retval + */ +void wm_sc_set_frequency(uint32_t freq) +{ + uint32_t reg; + uint8_t div; + tls_sys_clk clk; + + tls_sys_clk_get(&clk); + + div = (clk.apbclk * 1000000 + freq)/(2 * freq) - 1; + + reg = tls_reg_read32(HR_UART2_BAUD_RATE_CTRL); + reg &= ~ 0x3F0000; + reg |= (div<<16); + tls_reg_write32(HR_UART2_BAUD_RATE_CTRL, reg); +} + +/** + * @brief + * close af to use PB29 and PB30(uart2_rx as default if af is on) as gpio + * @retval + */ +void wm_sc_powerInit(void) +{ +#ifdef WM_SC_PWR_PIN + u32 regValue; + + // PB29 AF close, used as GPIO + regValue = tls_reg_read32(HR_GPIOB_AFSEL); + regValue &= ~0x20000000; + tls_reg_write32(HR_GPIOB_AFSEL, regValue); + + // PB30 AF close, used as GPIO + regValue = tls_reg_read32(HR_GPIOB_AFSEL); + regValue &= ~0x40000000; + tls_reg_write32(HR_GPIOB_AFSEL, regValue); +#endif +} + +/** + * @brief + * power on the 7816 device if power is controled by GPIO + * @retval + */ +void wm_sc_poweron(void) +{ +#ifdef WM_SC_PWR_PIN + tls_gpio_cfg(WM_SC_PWR_PIN, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_gpio_write(WM_SC_PWR_PIN, 1); +#endif +} + +/** + * @brief + * power off the 7816 device if power is controled by GPIO + * @retval + */ +void wm_sc_poweroff(void) +{ +#ifdef WM_SC_PWR_PIN + tls_gpio_cfg(WM_SC_PWR_PIN, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_gpio_write(WM_SC_PWR_PIN, 0); +#endif +} + +/** + * @brief + * driver the reset gpio in low level + * @retval + */ +void wm_sc_rst_low(void) +{ +#ifdef WM_SC_RST_PIN + tls_gpio_cfg(WM_SC_RST_PIN, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_gpio_write(WM_SC_RST_PIN, 0); +#endif +} + +/** + * @brief + * driver the reset gpio in high level + * @retval + */ +void wm_sc_rst_high(void) +{ +#ifdef WM_SC_RST_PIN + tls_gpio_cfg(WM_SC_RST_PIN, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_gpio_write(WM_SC_RST_PIN, 1); +#endif +} + +/** + * @brief + * hotrest the 7816 device obey the 7816-3 timing + * @retval + */ +void wm_sc_hotreset(void) +{ + uint32_t delay = 0xffff; + + /* set the rst pin to low */ + wm_sc_rst_low(); + /* delay */ + while(delay--); + /* set f/d to default 372 */ + wm_sc_set_etu(WM_SC_DEFAULT_FD); + /* set the rst pin to high */ + wm_sc_rst_high(); +} + +/** + * @brief + * colreset the 7816 device obey the 7816-3 timing + * @retval + */ +void wm_sc_colreset(void) +{ + /* power down */ + wm_sc_poweroff(); + /* select the clk io in gpio mode */ + wm_sc_io_clk_config(1); + /* reset the clk pin */ + tls_gpio_write(sc_io.clk_pin_num, 0); + /* reset the io pin */ + tls_gpio_write(sc_io.io_pin_num, 0); + /* set the ret pin to low */ + wm_sc_rst_low(); + /* power on the card */ + wm_sc_poweron(); + /* config the model in 7816 mode */ + wm_sc_7816_mode(1); + /* select the clk io pin in 7816 mode */ + wm_sc_io_clk_config(0); + /* config the output clock freq*/ + wm_sc_set_frequency(5000000); + /* set the F/D to default (372)*/ + wm_sc_set_etu(WM_SC_DEFAULT_FD); + /* set the rst pin to high */ + wm_sc_rst_high(); +} + +/** + * @brief + * deactive the 7816 device obey the 7816-3 timing + * @retval + */ +void wm_sc_deactive(void) +{ + /* set the rst pin in low level*/ + wm_sc_rst_low(); + /* select the clk and io pin to 7816 mode */ + wm_sc_io_clk_config(0); + /* disable the output clock */ + wm_sc_clk_enable(0); + /* select the clk and io pin to gpio mode */ + wm_sc_io_clk_config(1); + /* set the clk pin to low */ + tls_gpio_write(sc_io.clk_pin_num, 0); + /* set the io pin to low */ + tls_gpio_write(sc_io.io_pin_num, 0); + /* set the power pin to low */ + wm_sc_poweroff(); +} + diff --git a/sdk/Platform/Drivers/CMakeLists.txt b/sdk/Platform/Drivers/CMakeLists.txt new file mode 100644 index 0000000..03cb9dd --- /dev/null +++ b/sdk/Platform/Drivers/CMakeLists.txt @@ -0,0 +1,64 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WMDRIVER) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin ") +STRING(APPEND CMAKE_C_FLAGS " -DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/adc/wm_adc.c" + "${CMAKE_CURRENT_SOURCE_DIR}/cpu/wm_cpu.c" + "${CMAKE_CURRENT_SOURCE_DIR}/dma/wm_dma.c" + "${CMAKE_CURRENT_SOURCE_DIR}/efuse/wm_efuse.c" + "${CMAKE_CURRENT_SOURCE_DIR}/flash/wm_fls.c" + "${CMAKE_CURRENT_SOURCE_DIR}/flash/wm_fls_gd25qxx.c" + "${CMAKE_CURRENT_SOURCE_DIR}/gpio/wm_gpio.c" + "${CMAKE_CURRENT_SOURCE_DIR}/gpio/wm_gpio_afsel.c" + "${CMAKE_CURRENT_SOURCE_DIR}/hspi/wm_hspi.c" + "${CMAKE_CURRENT_SOURCE_DIR}/i2c/wm_i2c.c" + "${CMAKE_CURRENT_SOURCE_DIR}/irq/wm_irq.c" + "${CMAKE_CURRENT_SOURCE_DIR}/pwm/wm_pwm.c" + "${CMAKE_CURRENT_SOURCE_DIR}/pmu/wm_pmu.c" + "${CMAKE_CURRENT_SOURCE_DIR}/rtc/wm_rtc.c" + "${CMAKE_CURRENT_SOURCE_DIR}/spi/wm_hostspi.c" + "${CMAKE_CURRENT_SOURCE_DIR}/timer/wm_timer.c" + "${CMAKE_CURRENT_SOURCE_DIR}/uart/wm_uart.c" + "${CMAKE_CURRENT_SOURCE_DIR}/watchdog/wm_watchdog.c" + "${CMAKE_CURRENT_SOURCE_DIR}/internalflash/wm_internal_fls.c" + "${CMAKE_CURRENT_SOURCE_DIR}/i2s/wm_i2s.c" + "${CMAKE_CURRENT_SOURCE_DIR}/io/wm_io.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lcd/wm_lcd.c" + "${CMAKE_CURRENT_SOURCE_DIR}/7816/wm_7816.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmdriver.a) \ No newline at end of file diff --git a/sdk/Platform/Drivers/adc/wm_adc.c b/sdk/Platform/Drivers/adc/wm_adc.c new file mode 100644 index 0000000..477cba8 --- /dev/null +++ b/sdk/Platform/Drivers/adc/wm_adc.c @@ -0,0 +1,517 @@ + +/***************************************************************************** +* +* File Name : wm_adc.c +* +* Description: adc Driver Module +* +* Copyright (c) 2014 Winner Microelectronics Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-8-15 +*****************************************************************************/ +#include +#include +#include +#include "wm_regs.h" +#include "wm_adc.h" +#include "wm_dma.h" +#include "misc.h" +#include "wm_io.h" +#include "wm_irq.h" + + +static u16 adc_offset = 0; + +volatile ST_ADC gst_adc; +volatile u8 adc_irq_flag = 0; +void ADC_IRQHandler(void) +{ + u16 adcvalue; + int reg; + + reg = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + if(reg & CONFIG_ADC_INT) //ADC�ж� + { + adc_irq_flag = 1; + tls_adc_clear_irq(ADC_INT_TYPE_ADC); + adcvalue = tls_read_adc_result(); + if(gst_adc.adc_cb) + gst_adc.adc_cb(&adcvalue,1); + } + if(reg & CONFIG_ADC_INT_CMP) + { + tls_adc_clear_irq(ADC_INT_TYPE_ADC_COMP); + if(gst_adc.adc_bigger_cb) + gst_adc.adc_bigger_cb(NULL, 0); + } + +} + +static void adc_dma_isr_callbk(void) +{ + if(gst_adc.adc_dma_cb) + gst_adc.adc_dma_cb((u16 *)(ADC_DEST_BUFFER_DMA), gst_adc.valuelen); +} + + +void tls_adc_init(u8 ifusedma,u8 dmachannel) +{ + tls_reg_write32(HR_SD_ADC_CONFIG_REG, 0x0); + tls_irq_enable(ADC_IRQn); + +//ע���жϺ�channel�йأ�������Ҫ������ + if(ifusedma) + { + gst_adc.dmachannel = tls_dma_request(dmachannel, 0); //����dma����Ҫֱ��ָ������Ϊ�����dma���ܻᱻ�������ʹ�� + tls_dma_irq_register(gst_adc.dmachannel, (void(*)(void*))adc_dma_isr_callbk, NULL, TLS_DMA_IRQ_TRANSFER_DONE); + } + + //printf("\ndma channel = %d\n",gst_adc.dmachannel); +} + +void tls_adc_clear_irq(int inttype) +{ + int reg; + reg = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + if(ADC_INT_TYPE_ADC == inttype) + { + reg |= CONFIG_ADC_INT; + tls_reg_write32(HR_SD_ADC_CONFIG_REG, reg); + } + else if(ADC_INT_TYPE_ADC_COMP== inttype) + { + reg |= CONFIG_ADC_INT_CMP; + tls_reg_write32(HR_SD_ADC_CONFIG_REG, reg); + } + else if(ADC_INT_TYPE_DMA == inttype) + { + tls_dma_irq_clr(gst_adc.dmachannel, TLS_DMA_IRQ_TRANSFER_DONE); + } +} + +void tls_adc_irq_register(int inttype, void (*callback)(u16 *buf, u16 len)) +{ + if(ADC_INT_TYPE_ADC == inttype) + { + gst_adc.adc_cb = callback; + } + else if(ADC_INT_TYPE_DMA == inttype) + { + gst_adc.adc_dma_cb = callback; + } + else if(ADC_INT_TYPE_ADC_COMP == inttype) + { + gst_adc.adc_bigger_cb = callback; + } +} + +u16 tls_read_adc_result(void) +{ + u32 value; + u16 ret; + + value = tls_reg_read32(HR_SD_ADC_RESULT_REG); + ret = value&0x3fff; + + return ret; +} + +void tls_adc_start_with_cpu(int Channel) +{ + u32 value; + + Channel &= CONFIG_ADC_CHL_MASK; + + /* Stop adc first */ + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value &= ~CONFIG_ADC_DMA_MASK; + value &= ~CONFIG_ADC_START; + value |= Channel; + + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value |= CONFIG_ADC_START; + value |= CONFIG_ADC_INT_MASK; + if(Channel < 8) + { + value |= CONFIG_ADC_G_CTRL12; + value &= ~ CONFIG_ADC_VCM(0x3F); + value |= CONFIG_ADC_VCM(0x1F); + } + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); +} + + +void tls_adc_enable_calibration_buffer_offset(void) +{ + u32 value; + + /* Stop adc first */ + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value &= ~CONFIG_ADC_DMA_MASK; + value &= ~CONFIG_ADC_START; + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value |= CONFIG_ADC_CHL_OFFSET; + value |= CONFIG_ADC_START; + value |= CONFIG_ADC_INT_MASK; + value |= CONFIG_ADC_EN_CAL; + value |= CONFIG_ADC_G_CTRL12; + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); +} + +void tls_adc_temp_offset_with_cpu(u8 calTemp12) +{ + u32 value; + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value &= ~CONFIG_ADC_DMA_MASK; + value &= ~CONFIG_ADC_START; + + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value |= CONFIG_ADC_CHL_TEMP; + value |= CONFIG_ADC_START; + value |= CONFIG_ADC_INT_MASK; + if(calTemp12) + { + value |= CONFIG_ADC_CAL_OFFSET_TEMP12; + } + else + { + value &= ~CONFIG_ADC_CAL_OFFSET_TEMP12; + } + value &= ~CONFIG_ADC_G_CTRL12; + value |= CONFIG_ADC_TEMP_ON; + value |= CONFIG_ADC_G_TEMP12(3); + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); /*start adc*/ +} + +void tls_adc_voltage_start_with_cpu(void) +{ + u32 value; + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value &= ~CONFIG_ADC_DMA_MASK; + value &= ~CONFIG_ADC_START; + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value |= CONFIG_ADC_CHL_VOLT; + value |= CONFIG_ADC_START; + value |= CONFIG_ADC_INT_MASK; + value |= CONFIG_ADC_G_CTRL12; + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); /*start adc*/ +} + +void tls_adc_start_with_dma(int Channel, int Length) +{ + u32 value; + int len; + + if(Channel < 0 || Channel > 11) + return; + + if(Length > ADC_DEST_BUFFER_SIZE) + len = ADC_DEST_BUFFER_SIZE; + else + len = Length; + + gst_adc.valuelen = len; + + Channel &= CONFIG_ADC_CHL_MASK; + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value &= ~CONFIG_ADC_START; //stop + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); + + /* Stop dma if necessary */ + while(DMA_CHNLCTRL_REG(gst_adc.dmachannel) & 1) + { + DMA_CHNLCTRL_REG(gst_adc.dmachannel) = 2; + } + + DMA_SRCADDR_REG(gst_adc.dmachannel) = HR_SD_ADC_RESULT_REG; + DMA_DESTADDR_REG(gst_adc.dmachannel) = ADC_DEST_BUFFER_DMA; + /* Hard, Normal, adc_req */ + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + if (Channel == 8){ + DMA_MODE_REG(gst_adc.dmachannel) = (0x01 | (0+6)<<2); + value |= (0x1 << 11); + } + else if (Channel == 9){ + DMA_MODE_REG(gst_adc.dmachannel) = (0x01 | (2+6)<<2); + value |= (0x1 << 13); + } + else if (Channel == 10){ + DMA_MODE_REG(gst_adc.dmachannel) = (0x01 | (4+6)<<2); + value |= (0x1 << 15); + } + else if (Channel == 11){ + DMA_MODE_REG(gst_adc.dmachannel) = (0x01 | (6+6)<<2); + value |= (0x1 << 17); + } + else{ + DMA_MODE_REG(gst_adc.dmachannel) = (0x01 | (Channel+6)<<2); + value |= (0x1 << (11 + Channel)); + } + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); + /* Dest_add_inc, halfword, */ + DMA_CTRL_REG(gst_adc.dmachannel) = (1<<3)|(1<<5)|((len*2)<<8); + DMA_INTMASK_REG &= ~(0x01 << (gst_adc.dmachannel *2 + 1)); + DMA_CHNLCTRL_REG(gst_adc.dmachannel) = 1; /* Enable dma */ + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value |= CONFIG_ADC_START; + value |= Channel; + if(Channel < 8) + { + value |= CONFIG_ADC_G_CTRL12; + value &= ~ CONFIG_ADC_VCM(0x3F); + value |= CONFIG_ADC_VCM(0x1F); + } +// printf("config value==%x\n", value); + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); /*start adc*/ +} + +void tls_adc_stop(int ifusedma) +{ + u32 value; + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value &= ~CONFIG_ADC_START; + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); + + if(ifusedma) + tls_dma_free(gst_adc.dmachannel); +} + +void tls_adc_config_cmp_reg(int cmp_data, int cmp_pol) +{ + u32 value; + + cmp_data &= 0x3FFF; + value = tls_reg_read32(HR_SD_ADC_RESULT_REG); + value &= ~(0x3FFF<<14); + value |= (cmp_data<<14); + tls_reg_write32(HR_SD_ADC_RESULT_REG, value); + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value |= CONFIG_ADC_CMP_INT_MASK; + if(cmp_pol) + { + value |= CONFIG_ADC_CMP_POL; + } + else + { + value &= ~CONFIG_ADC_CMP_POL; + } + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); +} + +void tls_adc_cmp_start(int Channel, int cmp_data, int cmp_pol) +{ + u32 value; + + Channel &= CONFIG_ADC_CHL_MASK; + + /* Stop adc first */ + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value &= ~CONFIG_ADC_DMA_MASK; + value &= ~CONFIG_ADC_START; + value |= Channel; + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); + + tls_adc_config_cmp_reg(cmp_data, cmp_pol); + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + value |= CONFIG_ADC_START; + if(Channel < 8) + { + value |= CONFIG_ADC_G_CTRL12; + value &= ~ CONFIG_ADC_VCM(0x3F); + value |= CONFIG_ADC_VCM(0x1F); + } + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); /*start adc*/ +} + + +void tls_adc_reference_sel(int ref) +{ + u32 value; + + value = tls_reg_read32(HR_SD_ADC_CONFIG_REG); + if(ADC_REFERENCE_EXTERNAL == ref) + { + value &= ~CONFIG_ADC_REF_SEL; + value |= CONFIG_ADC_BUF_BYPASS; + } + else if(ADC_REFERENCE_INTERNAL == ref) + { + value |= CONFIG_ADC_REF_SEL; + value &= ~CONFIG_ADC_BUF_BYPASS; + } + tls_reg_write32(HR_SD_ADC_CONFIG_REG, value); +} + +void tls_adc_set_clk(int div) +{ + u32 value; + + value = tls_reg_read32(HR_CLK_DIV_CTL); + value &= ~(0xffff<<12); + value |= (div << 12); + value |= ((u32)0x1<<31); + tls_reg_write32(HR_CLK_DIV_CTL, value); +} + +void signedToUnsignedData(u16 *adcValue, u16 *offset) +{ + // s16 temp = 0; + if(*adcValue >= 8192) + { + *adcValue -= 8192; + } + else + { + *adcValue += 8192; + } + + /* if (*offset >=8192) + { + temp =((*offset-1)&0x1FFF) - 8192; + }else{ + temp = *offset; + } + + *adcValue -= temp; + */ +} + +static void waitForAdcDone(void) +{ + while(1) + { + if(adc_irq_flag) //ADC�ж� + { + adc_irq_flag = 0; + break; + } + } +} + +static void waitForStable(void) +{ + volatile u32 count; + + count = 2000; + while( count-- ); +} + +u16 adc_get_offset(void) +{ + tls_adc_init(0, 0); + tls_adc_reference_sel(ADC_REFERENCE_INTERNAL); + adc_irq_flag = 0; + waitForStable(); + tls_adc_enable_calibration_buffer_offset(); //ʹ��У׼���� + waitForAdcDone(); + adc_offset = tls_read_adc_result(); //��ȡadcת����� + tls_adc_stop(0); + + //printf("\r\noffset:%d", adc_offset); + return adc_offset; +} + +u32 adc_get_interTemp(void) +{ + u16 code2, code1, realCode; + u32 tem; + + tls_adc_init(0, 0); + tls_adc_reference_sel(ADC_REFERENCE_INTERNAL); + adc_irq_flag = 0; + waitForStable(); + tls_adc_temp_offset_with_cpu(1); //code2 + waitForAdcDone(); + code2 = tls_read_adc_result(); + tls_adc_stop(0); + signedToUnsignedData(&code2, &adc_offset); + adc_irq_flag = 0; + waitForStable(); + tls_adc_temp_offset_with_cpu(0); //code1 + waitForAdcDone(); + code1 = tls_read_adc_result(); + tls_adc_stop(0); + signedToUnsignedData(&code1, &adc_offset); + + realCode = ( (code1-code2)/2+8192 ); + //printf("code1:%d, code2:%d, realCode:%d\r\n", code1, code2, realCode); + //printf("\r\nTEMP:%.1f", realCode*0.12376-1294.58); + //return (realCode*0.12376-1294.58); + tem = realCode*124-1294580; + //printf("tem:%d\r\n", tem); + return tem; +} + +u16 adc_get_inputVolt(u8 channel) +{ + u16 average = 0; + + /*calibration*/ + adc_get_offset(); + /*get value*/ + tls_adc_init(0, 0); + tls_adc_reference_sel(ADC_REFERENCE_INTERNAL); + adc_irq_flag = 0; + waitForStable(); + tls_adc_start_with_cpu(channel); + waitForAdcDone(); + average = tls_read_adc_result(); + tls_adc_stop(0); + + signedToUnsignedData(&average, &adc_offset); + + //printf("ch[%d]inputVolt:%.3f\r\n", channel+1, ((average-8192.0)/8192*2.25/1.2 + 1.584)); + return average; +} + +u16 adc_get_interVolt(void) +{ + u16 voltValue; + + /*calibration*/ + adc_get_offset(); + /*get value*/ + tls_adc_init(0, 0); + tls_adc_reference_sel(ADC_REFERENCE_INTERNAL); + + adc_irq_flag = 0; + waitForStable(); + tls_adc_voltage_start_with_cpu(); + waitForAdcDone(); + voltValue = tls_read_adc_result(); + tls_adc_stop(0); + + signedToUnsignedData(&voltValue, &adc_offset); + float voltage = ( 1.214 - ((float)voltValue-8192)/8192*2.25/1.2 )*2; + printf("interVolt:%.2f\r\n", voltage); + return voltValue; +} + +u32 adc_temp(void) +{ + u32 tem; + adc_get_offset(); + tem = adc_get_interTemp(); + return tem; +} + + + diff --git a/sdk/Platform/Drivers/cpu/wm_cpu.c b/sdk/Platform/Drivers/cpu/wm_cpu.c new file mode 100644 index 0000000..cf7dcf9 --- /dev/null +++ b/sdk/Platform/Drivers/cpu/wm_cpu.c @@ -0,0 +1,86 @@ +/** + * @file wm_cpu.c + * + * @brief cpu driver module + * + * @author kevin + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#include "wm_debug.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_cpu.h" +#include "wm_pwm.h" +#include "wm_uart.h" +#include "wm_wifi.h" + +extern void Debug_UartInit(void); + +void tls_sys_clock_switch_hook(void) +{ + tls_sys_clk sysclk; + + tls_sys_clk_get(&sysclk); + tls_reg_write32(HR_TIMER_CFG, sysclk.apbclk-1); + + /*to do, like uart baudrate switch, other device clock switch*/ + tls_uart_set_baud_rate(0, 115200); +} + +/** + * @brief This function is used to set cpu clock + * + * @param[in] clk select cpu clock + * clk == CPU_CLK_80M 80M + * clk == CPU_CLK_40M 40M + * + * @return None + * + * @note None + */ +void tls_sys_clk_set(u32 clk) +{ + u32 RegValue; + u8 wlanDiv, cpuDiv; + + RegValue = tls_reg_read32(HR_CLK_DIV_CTL); + wlanDiv = (RegValue>>4)&0x0F; + RegValue &= 0xFFFFF000; + RegValue |= 0x80000000; + switch(clk) + { + case CPU_CLK_40M: + cpuDiv = 4; + break; + + default: + cpuDiv = 2; + break; + } + RegValue |= ((wlanDiv*4/cpuDiv)<<8) | (wlanDiv<<4) | cpuDiv; + tls_reg_write32(HR_CLK_DIV_CTL, RegValue); + + return; +} + +/** + * @brief This function is used to get cpu clock + * + * @param[out] *sysclk point to the addr for system clk output + * + * @return None + * + * @note None + */ +void tls_sys_clk_get(tls_sys_clk *sysclk) +{ + u32 value; + value = tls_reg_read32(HR_CLK_DIV_CTL); + sysclk->apbclk = W600_PLL_CLK_MHZ/4/((value>>4)&0x0F); + sysclk->cpuclk = W600_PLL_CLK_MHZ/(value&0x0F); + sysclk->wlanclk = W600_PLL_CLK_MHZ/((value>>4)&0x0F); +} + + + diff --git a/sdk/Platform/Drivers/dma/wm_dma.c b/sdk/Platform/Drivers/dma/wm_dma.c new file mode 100644 index 0000000..56e8cba --- /dev/null +++ b/sdk/Platform/Drivers/dma/wm_dma.c @@ -0,0 +1,405 @@ +/** + * @file wm_dma.c + * + * @brief DMA Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#include +#include +#include + + +#include "misc.h" +#include "wm_dma.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_osal.h" +#include "wm_pmu.h" + + +static u16 dma_used_bit = 0; +struct tls_dma_channels { + unsigned char channels[8]; /* list of channels */ +}; + +typedef void (*dma_irq_callback)(void *p); + +struct dma_irq_context { + u8 flags; + dma_irq_callback burst_done_pf; + void *burst_done_priv; + dma_irq_callback transfer_done_pf; + void *transfer_done_priv; +}; + +static struct dma_irq_context dma_context[8]; +static struct tls_dma_channels channels; + +extern void wm_delay_ticks(uint32_t ticks); + +static void dma_irq_proc(void *p) +{ + unsigned char ch; + unsigned int int_src; + + ch = (unsigned char)(unsigned long)p; + int_src = tls_reg_read32(HR_DMA_INT_SRC); + + if (ch > 3) + { + for (ch = 4; ch < 8; ch++) + { + if (int_src & (TLS_DMA_IRQ_BOTH_DONE << ch * 2)) + break; + } + + if (8 == ch) + return; + } + + if ((int_src & (TLS_DMA_IRQ_BOTH_DONE << ch * 2)) && + (TLS_DMA_IRQ_BOTH_DONE == dma_context[ch].flags)) + { + tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE); + if (dma_context[ch].burst_done_pf) + dma_context[ch].burst_done_pf(dma_context[ch].burst_done_priv); + } + else if ((int_src & (TLS_DMA_IRQ_BURST_DONE << ch * 2)) && + (TLS_DMA_IRQ_BURST_DONE == dma_context[ch].flags)) + { + tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE); + if (dma_context[ch].burst_done_pf) + dma_context[ch].burst_done_pf(dma_context[ch].burst_done_priv); + } + else if ((int_src & (TLS_DMA_IRQ_TRANSFER_DONE << ch * 2)) && + (TLS_DMA_IRQ_TRANSFER_DONE == dma_context[ch].flags)) + { + tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE); + if (dma_context[ch].transfer_done_pf) + dma_context[ch].transfer_done_pf(dma_context[ch].transfer_done_priv); + } + return; +} + +void DMA_Channel0_IRQHandler(void) +{ + dma_irq_proc((void *)0); +} +void DMA_Channel1_IRQHandler(void) +{ + dma_irq_proc((void *)1); +} +void DMA_Channel2_IRQHandler(void) +{ + dma_irq_proc((void *)2); +} +void DMA_Channel3_IRQHandler(void) +{ + dma_irq_proc((void *)3); +} +void DMA_Channel4_7_IRQHandler(void) +{ + dma_irq_proc((void *)4); +} + +/** + * @brief This function is used to clear dma interrupt flag. + * + * @param[in] ch Channel no.[0~7] + * @param[in] flags Flags setted to TLS_DMA_IRQ_BURST_DONE, TLS_DMA_IRQ_TRANSFER_DONE, TLS_DMA_IRQ_BOTH_DONE. + * + * @return None + * + * @note None + */ +void tls_dma_irq_clr(unsigned char ch, unsigned char flags) +{ + unsigned int int_src = 0; + + int_src |= flags << 2 * ch; + + tls_reg_write32(HR_DMA_INT_SRC, int_src); + + return; +} + +/** + * @brief This function is used to register dma interrupt callback function. + * + * @param[in] ch Channel no.[0~7] + * @param[in] callback is the dma interrupt call back function. + * @param[in] arg the param of the callback function. + * @param[in] flags Flags setted to TLS_DMA_IRQ_BURST_DONE, TLS_DMA_IRQ_TRANSFER_DONE, TLS_DMA_IRQ_BOTH_DONE. + * + * @return None + * + * @note None + */void tls_dma_irq_register(unsigned char ch, void (*callback)(void *p), void *arg, unsigned char flags) +{ + unsigned int mask; + + mask = tls_reg_read32(HR_DMA_INT_MASK); + mask |= TLS_DMA_IRQ_BOTH_DONE << 2 * ch; + mask &= ~(flags << 2 * ch); + tls_reg_write32(HR_DMA_INT_MASK, mask); + + dma_context[ch].flags = flags; + if (flags & TLS_DMA_IRQ_BURST_DONE) + { + dma_context[ch].burst_done_pf = callback; + dma_context[ch].burst_done_priv = arg; + } + if (flags & TLS_DMA_IRQ_TRANSFER_DONE) + { + dma_context[ch].transfer_done_pf = callback; + dma_context[ch].transfer_done_priv = arg; + } + + if (ch > 3) + ch = 4; + + tls_irq_enable(DMA0_INT + ch); + + return; +} + +/** + * @brief This function is used to Wait until DMA operation completes + * + * @param[in] ch channel no + * + * @retval 0 completed + * @retval -1 failed + * + * @note None + */ +int tls_dma_wait_complt(unsigned char ch) +{ + unsigned long timeout = 0; + + while(DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON) + { + tls_os_time_delay(1); + timeout ++; + if(timeout > 500) + return -1; + } + return 0; +} + +/** + * @brief This function is used to Start the DMA controller by Wrap + * + * @param[in] ch channel no + * @param[in] dma_desc pointer to DMA channel descriptor structure + * @param[in] auto_reload does restart when current transfer complete + * @param[in] src_zize dource address size + * @param[in] dest_zize destination address size + * + * @retval 1 success + * @retval 0 failed + * + * @note + * DMA Descriptor: + * +--------------------------------------------------------------+ + * |Vld[31] | RSV | + * +--------------------------------------------------------------+ + * | RSV | Dma_Ctrl[16:0] | + * +--------------------------------------------------------------+ + * | Src_Addr[31:0] | + * +--------------------------------------------------------------+ + * | Dest_Addr[31:0] | + * +--------------------------------------------------------------+ + * | Next_Desc_Add[31:0] | + * +--------------------------------------------------------------+ + */ +unsigned char tls_dma_start_by_wrap(unsigned char ch, struct tls_dma_descriptor *dma_desc, + unsigned char auto_reload, + unsigned short src_zize, + unsigned short dest_zize) +{ + if((ch > 7) && !dma_desc) return 1; + + DMA_SRCWRAPADDR_REG(ch) = dma_desc->src_addr; + DMA_DESTWRAPADDR_REG(ch) = dma_desc->dest_addr; + DMA_WRAPSIZE_REG(ch) = (dest_zize << 16) | src_zize; + DMA_CTRL_REG(ch) = ((dma_desc->dma_ctrl & 0x1ffff) << 1) | (auto_reload ? 0x1: 0x0); + DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_ON; + + return 0; +} + +/** + * @brief This function is used to Start the DMA controller + * + * @param[in] ch channel no + * @param[in] dma_desc pointer to DMA channel descriptor structure + * @param[in] auto_reload does restart when current transfer complete + * + * @retval 0 success + * @retval 1 failed + * + * @note + * DMA Descriptor: + * +--------------------------------------------------------------+ + * |Vld[31] | RSV | + * +--------------------------------------------------------------+ + * | RSV | Dma_Ctrl[16:0] | + * +--------------------------------------------------------------+ + * | Src_Addr[31:0] | + * +--------------------------------------------------------------+ + * | Dest_Addr[31:0] | + * +--------------------------------------------------------------+ + * | Next_Desc_Add[31:0] | + * +--------------------------------------------------------------+ + */ +unsigned char tls_dma_start(unsigned char ch, struct tls_dma_descriptor *dma_desc, unsigned char auto_reload) +{ + if((ch > 7) && !dma_desc) return 1; + + if ((dma_used_bit &(1<src_addr; + DMA_DESTADDR_REG(ch) = dma_desc->dest_addr; + DMA_CTRL_REG(ch) = ((dma_desc->dma_ctrl & 0x7fffff) << 1) | (auto_reload ? 0x1: 0x0); + DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_ON; + + return 0; +} + +/** + * @brief This function is used to To stop current DMA channel transfer + * + * @param[in] ch channel no. to be stopped + * + * @retval 0 success + * @retval 1 failed + * + * @note + * If channel stop, DMA_CHNL_CTRL_CHNL_ON bit in DMA_CHNLCTRL_REG is cleared. + */ +unsigned char tls_dma_stop(unsigned char ch) +{ + if(ch > 7) return 1; + if(DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON) + { + DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_OFF; + + while(DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON); + } + + return 0; +} + +/** + * @brief This function is used to Request a free dma channel + * + * @param[in] ch channel no. + * @param[in] flags flags setted to selected channel + * + * @return Channel no. that is free now + * + * @note + * If ch is 0, the function will select a random free channel, + * else return the selected channel no. + */ +unsigned char tls_dma_request(unsigned char ch, unsigned char flags) +{ + + unsigned char freeCh = 0; + int i = 0; + + if (ch == 0) + { + for (i = 0; i < 8; i++) + { + if (!(channels.channels[i] & TLS_DMA_FLAGS_CHANNEL_VALID)) + { + freeCh = i; + break; + } + } + + if (8 == i) + { + printf("!!!there is no free DMA channel.!!!\n"); + freeCh = 0; + } + } + else if ((ch >0) && (ch < 8)) + { + if (!(channels.channels[ch] & TLS_DMA_FLAGS_CHANNEL_VALID)) + { + freeCh = ch; + } + else + { + printf("!!!there is no free DMA channel.!!!\n"); + freeCh = 0; + } + } + + //if(freeCh != 0) + { + channels.channels[freeCh] = flags | TLS_DMA_FLAGS_CHANNEL_VALID; + DMA_MODE_REG(freeCh) = flags; + } + if (dma_used_bit == 0) + { + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_DMA); + } + dma_used_bit |= (1< +#include +#include +#include "wm_debug.h" +#include "wm_regs.h" +#include "wm_efuse.h" +#include "wm_config.h" +#include "list.h" +#include "wm_internal_flash.h" +#include "wm_crypto_hard.h" +#include "wm_timer.h" +#include "wm_cpu.h" +#include "wm_irq.h" +#include "wm_flash_map.h" +#include "wm_mem.h" + +extern void flashSRRW(unsigned long offset,unsigned char *buf,unsigned long sz, unsigned char *backbuf, unsigned int backlen, unsigned int rd); + + +#define FT_MAGICNUM_ADDR (FLASH_BASE_ADDR) +#define MAGICNUM_LEN (4) +#define FT_CHECKSUM_ADDR (FT_MAGICNUM_ADDR + MAGICNUM_LEN) +#define CHECKSUM_LEN (4) +#define FT_DATALEN_ADDR (FT_CHECKSUM_ADDR + CHECKSUM_LEN) +#define FT_DATALEN_LEN (4) +#define FT_MAC_ADDR (FT_DATALEN_ADDR + FT_DATALEN_LEN) +#define MAC_ADDR_LEN (8) +#define FT_TX_DC_OFFSET_ADDR (FT_MAC_ADDR + MAC_ADDR_LEN) +#define TX_DC_OFFSET_LEN (4) +#define FT_RX_DC_OFFSET_ADDR (FT_TX_DC_OFFSET_ADDR + TX_DC_OFFSET_LEN) +#define RX_DC_OFFSET_LEN (4) +#define FT_TX_IQ_GAIN_ADDR (FT_RX_DC_OFFSET_ADDR + RX_DC_OFFSET_LEN) +#define TX_IQ_GAIN_LEN (4) +#define FT_RX_IQ_GAIN_ADDR (FT_TX_IQ_GAIN_ADDR + TX_IQ_GAIN_LEN) +#define RX_IQ_GAIN_LEN (4) +#define FT_TX_IQ_PHASE_ADDR (FT_RX_IQ_GAIN_ADDR + RX_IQ_GAIN_LEN) +#define TX_IQ_PHASE_LEN (4) +#define FT_RX_IQ_PHASE_ADDR (FT_TX_IQ_PHASE_ADDR + TX_IQ_PHASE_LEN) +#define RX_IQ_PHASE_LEN (4) +#define FT_GAIN_ADDR (FT_RX_IQ_PHASE_ADDR + RX_IQ_PHASE_LEN) +#define FT_GAIN_LEN (84) +#define FT_FIX_DATA_LEN (MAC_ADDR_LEN + TX_DC_OFFSET_LEN + RX_DC_OFFSET_LEN + TX_IQ_GAIN_LEN + RX_IQ_GAIN_LEN \ + + TX_IQ_PHASE_LEN \ + + RX_IQ_PHASE_LEN) + + +/* key paramater area -- begin -- */ +/** PHY parameter area **/ +#define PHY_BASE_ADDR (FLASH_BASE_ADDR) +#define PHY_AREA_LEN (0x1000) + +/** QFlash parameter area **/ +#define QFLASH_BASE_ADDR (PHY_BASE_ADDR + PHY_AREA_LEN) +#define QFLASH_AREA_LEN (0x1000) +#define QFLASH_HDR_BASE_ADDR (QFLASH_BASE_ADDR) +#define QFLASH_HDR_LEN (12) +#define QFLASH_Sec_Level_BASE_ADDR (QFLASH_HDR_BASE_ADDR + QFLASH_HDR_LEN) +#define QFLASH_Sec_Level_LEN (4) +#define QFLASH_CHIP_ID_BASE_ADDR (QFLASH_Sec_Level_BASE_ADDR + QFLASH_Sec_Level_LEN) +#define QFLASH_CHIP_ID_LEN (16) + +/* key parameter area -- end -- */ + +typedef struct FT_PARAM +{ + unsigned int magic_no; + unsigned int checksum; + unsigned int data_len; + unsigned char mac_addr[MAC_ADDR_LEN]; + unsigned int tx_dcoffset; + unsigned int rx_dcoffset; + unsigned int tx_iq_gain; + unsigned int rx_iq_gain; + unsigned int tx_iq_phase; + unsigned int rx_iq_phase; + unsigned char tx_gain[FT_GAIN_LEN]; +}FT_PARAM_ST; + +static u8 default_mac[6] = {0x00,0x25,0x08,0x09,0x01,0x0F}; + +FT_PARAM_ST gftParam; +int tls_ft_param_init(void) +{ + int i = 0; + int usedcnt[2] = {0, 0}; + u32 crcvalue = 0; + psCrcContext_t ctx; + u8 *pmem = NULL; + FT_PARAM_ST *pft = NULL; + + if (gftParam.magic_no == SIGNATURE_WORD) + { + return TRUE; + } + + pmem = tls_mem_alloc(512); + if (pmem == NULL) + { + return FALSE; + } + + pft = tls_mem_alloc(sizeof(FT_PARAM_ST)); + if (pft == NULL) + { + return FALSE; + } + + + for (i = 0; i < 2; i++) + { + if (i == 0) + { + tls_fls_read(FT_MAGICNUM_ADDR, (unsigned char *)pft, sizeof(FT_PARAM_ST)); + }else{ + flashSRRW(0, (unsigned char *)pft, sizeof(FT_PARAM_ST), pmem, 256, 1); + } + if (pft->magic_no == SIGNATURE_WORD) + { + tls_crypto_init(); + tls_crypto_crc_init(&ctx, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, INPUT_REFLECT | OUTPUT_REFLECT); + tls_crypto_crc_update(&ctx, (unsigned char *)pft + 12, sizeof(FT_PARAM_ST) - 12); + tls_crypto_crc_final(&ctx, &crcvalue); + if ((pft->checksum != crcvalue)) + { + usedcnt[i] = -1; + continue; + } + + if (gftParam.magic_no != SIGNATURE_WORD) + { + memcpy(&gftParam, pft, sizeof(FT_PARAM_ST)); + } + }else{ + usedcnt[i] = -1; + } + } + + if ((usedcnt[0] + usedcnt[1]) == -2) + { + /*Use default ft param*/ + } + else if (usedcnt[0] < 0) /*flash param destroyed*/ + { + tls_flash_unlock(); + tls_fls_write(FT_MAGICNUM_ADDR, (unsigned char *)&gftParam, sizeof(gftParam)); + tls_flash_lock(); + } + else if (usedcnt[1] < 0) /*SR param destroyed*/ + { + flashSRRW(0, (unsigned char *)&gftParam, sizeof(FT_PARAM_ST), pmem, 256, 0); + } + else + { + if (gftParam.checksum != pft->checksum) + { + flashSRRW(0, (unsigned char *)&gftParam, sizeof(FT_PARAM_ST), pmem, 256, 0); + } + } + + tls_mem_free(pmem); + tls_mem_free(pft); + + return TRUE; +} + +int tls_ft_param_get(unsigned int opnum, void *data, unsigned int rdlen) +{ + //FT_PARAM_ST ft; + + //tls_fls_read(FT_MAGICNUM_ADDR, (unsigned char *)&ft, sizeof(ft)); + switch (opnum) + { + case CMD_MAC: /*MAC*/ + if ((gftParam.mac_addr[0]&0x1) + ||(0 == (gftParam.mac_addr[0]|gftParam.mac_addr[1]|gftParam.mac_addr[2]|gftParam.mac_addr[3]|gftParam.mac_addr[4]|gftParam.mac_addr[5]))) + { + memcpy(data, default_mac, rdlen); + } + else + { + memcpy(data, gftParam.mac_addr, rdlen); + } + break; + + case CMD_TX_DC: /*tx_dcoffset*/ + *(unsigned int *)data = gftParam.tx_dcoffset; + break; + + case CMD_RX_DC: /*rx_dcoffset*/ + *(unsigned int *)data = gftParam.rx_dcoffset; + break; + + case CMD_TX_IQ_GAIN: + *(unsigned int *)data = gftParam.tx_iq_gain; + break; + + case CMD_RX_IQ_GAIN: + *(unsigned int *)data = gftParam.rx_iq_gain; + break; + + case CMD_TX_IQ_PHASE: + *(unsigned int *)data = gftParam.tx_iq_phase; + break; + + case CMD_RX_IQ_PHASE: + *(unsigned int *)data = gftParam.rx_iq_phase; + break; + + case CMD_TX_GAIN: /*gain*/ + if (rdlen < FT_GAIN_LEN) + { + memcpy(data, gftParam.tx_gain, rdlen); + } + else + { + memcpy(data, gftParam.tx_gain, FT_GAIN_LEN); + } + break; + + default: + return -1; + } + return 0; +} + +int tls_ft_param_set(unsigned int opnum, void *data, unsigned int len) +{ + psCrcContext_t ctx; + unsigned int writelen = 0; + u8 *pmem = NULL; + + + + if (!data || !len) + { + return -1; + } + //tls_fls_read(FT_MAGICNUM_ADDR, (unsigned char *)&gftParam, sizeof(gftParam)); + switch (opnum) + { + case CMD_MAC: /*MAC*/ + memcpy(gftParam.mac_addr, (unsigned char *)data, len); + break; + + case CMD_TX_DC: /*tx_dcoffset*/ + gftParam.tx_dcoffset = *(unsigned int *)data; + break; + + case CMD_RX_DC: /*rx_dcoffset*/ + gftParam.rx_dcoffset = *(unsigned int *)data; + break; + + case CMD_TX_IQ_GAIN: + gftParam.tx_iq_gain = *(unsigned int *)data; + break; + + case CMD_RX_IQ_GAIN: + gftParam.rx_iq_gain = *(unsigned int *) data; + break; + + case CMD_TX_IQ_PHASE: + gftParam.tx_iq_phase = *(unsigned int *)data; + break; + + case CMD_RX_IQ_PHASE: + gftParam.rx_iq_phase = *(unsigned int *) data; + break; + + case CMD_TX_GAIN: /*gain*/ + if (len >= FT_GAIN_LEN) + { + writelen = FT_GAIN_LEN; + } + else + { + writelen = len; + } + memcpy(gftParam.tx_gain, data, writelen); + break; + + default: + return -1; + } + + tls_crypto_init(); + tls_crypto_crc_init(&ctx, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, INPUT_REFLECT | OUTPUT_REFLECT); + gftParam.magic_no = SIGNATURE_WORD; + tls_crypto_crc_update(&ctx, (unsigned char *)&gftParam + 12, sizeof(gftParam) -12); + tls_crypto_crc_final(&ctx, &gftParam.checksum); + tls_flash_unlock(); + tls_fls_write(FT_MAGICNUM_ADDR, (unsigned char *)&gftParam, sizeof(gftParam)); + tls_flash_lock(); + pmem = tls_mem_alloc(512); + if (pmem) + { + flashSRRW(0, (unsigned char *)&gftParam, sizeof(gftParam), pmem, 512, 0); + tls_mem_free(pmem); + } + return 0; +} + + +/********************************************************************************************************** +* Description: This function is used to get mac addr. +* +* Arguments : mac mac addr,6 byte +* +* Returns : TLS_EFUSE_STATUS_OK get success +* TLS_EFUSE_STATUS_EIO get failed +**********************************************************************************************************/ +int tls_get_mac_addr(u8 *mac) +{ + return tls_ft_param_get(CMD_MAC, mac, 6); +} + +/********************************************************************************************************** +* Description: This function is used to set mac addr. +* +* Arguments : mac mac addr,6 byte +* +* Returns : TLS_EFUSE_STATUS_OK get success +* TLS_EFUSE_STATUS_EIO get failed +**********************************************************************************************************/ +int tls_set_mac_addr(u8 *mac) +{ + return tls_ft_param_set(CMD_MAC, mac, 6); +} + + +/********************************************************************************************************** +* Description: This function is used to get tx lod. +* +* Arguments : *txlo +* +* Returns : 0 get success +* -1 get failed +**********************************************************************************************************/ +int tls_get_tx_lo(u8 *txlo) +{ + return tls_ft_param_get(CMD_TX_DC, txlo, 4); +} + +/********************************************************************************************************** +* Description: This function is used to set tx lo. +* +* Arguments : txlo +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_set_tx_lo(u8 *txlo) +{ + return tls_ft_param_set(CMD_TX_DC, txlo, 4); +} + +/********************************************************************************************************** +* Description: This function is used to get tx iq gain. +* +* Arguments : txGain +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_get_tx_iq_gain(u8 *txGain) +{ + return tls_ft_param_get(CMD_TX_IQ_GAIN, txGain, 4); +} + +/********************************************************************************************************** +* Description: This function is used to set tx iq gain. +* +* Arguments : txGain +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_set_tx_iq_gain(u8 *txGain) +{ + return tls_ft_param_set(CMD_TX_IQ_GAIN, txGain, 4); +} + +/********************************************************************************************************** +* Description: This function is used to get rx iq gain. +* +* Arguments : rxGain +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_get_rx_iq_gain(u8 *rxGain) +{ + return tls_ft_param_get(CMD_RX_IQ_GAIN, rxGain, 4); +} + +/********************************************************************************************************** +* Description: This function is used to set rx iq gain. +* +* Arguments : rxGain +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_set_rx_iq_gain(u8 *rxGain) +{ + return tls_ft_param_set(CMD_RX_IQ_GAIN, rxGain, 4); +} + +/********************************************************************************************************** +* Description: This function is used to get tx iq phase. +* +* Arguments : txPhase +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_get_tx_iq_phase(u8 *txPhase) +{ + return tls_ft_param_get(CMD_TX_IQ_PHASE, txPhase, 4); +} + +/********************************************************************************************************** +* Description: This function is used to set tx iq phase. +* +* Arguments : txPhase +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_set_tx_iq_phase(u8 *txPhase) +{ + return tls_ft_param_set(CMD_TX_IQ_PHASE, txPhase, 4); +} + +/********************************************************************************************************** +* Description: This function is used to get rx iq phase. +* +* Arguments : rxPhase +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_get_rx_iq_phase(u8 *rxPhase) +{ + return tls_ft_param_get(CMD_RX_IQ_PHASE, rxPhase, 4); +} + +/********************************************************************************************************** +* Description: This function is used to set tx iq phase. +* +* Arguments : rxPhase +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_set_rx_iq_phase(u8 *rxPhase) +{ + return tls_ft_param_set(CMD_RX_IQ_PHASE, rxPhase, 4); +} + + +int tls_freq_err_op(u8 *freqerr, u8 flag) +{ + tls_flash_unlock(); + if (flag){ + tls_fls_write(FT_MAGICNUM_ADDR + sizeof(FT_PARAM_ST), freqerr, 4); + } + else + { + tls_fls_read(FT_MAGICNUM_ADDR + sizeof(FT_PARAM_ST), freqerr, 4); + } + tls_flash_lock(); + return TLS_EFUSE_STATUS_OK; +} + +int tls_rf_vcg_ctrl_op(u8 *vcg, u8 flag) +{ + tls_flash_unlock(); + if (flag){ + tls_fls_write(VCG_ADDR, vcg, VCG_LEN); + } + else + { + tls_fls_read(VCG_ADDR, vcg, VCG_LEN); + } + tls_flash_lock(); + return TLS_EFUSE_STATUS_OK; +} + +/********************************************************************************************************** +* Description: This function is used to get tx gain. +* +* Arguments : txgain tx gain +* +* Returns : 0 get success +* -1 get failed +**********************************************************************************************************/ +int tls_get_tx_gain(u8 *txgain) +{ + return tls_ft_param_get(CMD_TX_GAIN, txgain, TX_GAIN_LEN); +} + +/********************************************************************************************************** +* Description: This function is used to set tx gain. +* +* Arguments : txgain tx gain +* +* Returns : 0 set success +* -1 set failed +**********************************************************************************************************/ +int tls_set_tx_gain(u8 *txgain) +{ + return tls_ft_param_set(CMD_TX_GAIN, txgain, TX_GAIN_LEN); +} + + +/********************************************************************************************************** +* Description: This function is used to get uuid +* +* Arguments : *uuid +* +* Returns : 0 get success +* -1 get failed +**********************************************************************************************************/ +int tls_get_uuid(u8 *uuid, u8 len) +{ + u8 *pmem = NULL; + + if (uuid) + { + pmem = tls_mem_alloc(512); + if (pmem == NULL) + { + return TLS_EFUSE_STATUS_ERR; + } + + flashSRRW(256, (unsigned char *)pmem, len, pmem, 512, 1); + memcpy(uuid, &pmem[0], len); + + tls_mem_free(pmem); + pmem = NULL; + + return TLS_EFUSE_STATUS_OK; + } + + return TLS_EFUSE_STATUS_ERR; +} + + +/********************************************************************************************************** +* Description: This function is used to get uuid +* +* Arguments : *uuid :data to be written to otp area +* len : data len to be written +* iflock : if lock the otp area,1-lock, once lock,can not unlock!!! +* +* Returns : 0 get success +* -1 get failed +**********************************************************************************************************/ +int tls_set_uuid(u8 *uuid, u8 len, u8 iflock) +{ + u8 *pmem = NULL; + + if (uuid) + { + pmem = tls_mem_alloc(512); + if (pmem == NULL) + { + return TLS_EFUSE_STATUS_ERR; + } + + flashSRRW(256, (unsigned char *)uuid, len, pmem, 512, 0); + + if (iflock) + { + writeSRLockBit(2); + } + + tls_mem_free(pmem); + pmem = NULL; + + return TLS_EFUSE_STATUS_OK; + } + + return TLS_EFUSE_STATUS_ERR; +} + + + +#define FF_n16 0xff, 0xff, 0xff, 0xff, \ + 0xff, 0xff, 0xff, 0xff, \ + 0xff, 0xff, 0xff, 0xff, \ + 0xff, 0xff, 0xff, 0xff + +int tls_get_chipid(u8 chip_id[QFLASH_CHIP_ID_LEN]) +{ + int ret = 0; + static u8 _chip_id[QFLASH_CHIP_ID_LEN] = { FF_n16 }; + u8 zero[QFLASH_CHIP_ID_LEN] = { FF_n16 }; + + if (!memcmp(_chip_id, zero, QFLASH_CHIP_ID_LEN)) + { + ret = tls_fls_read(QFLASH_CHIP_ID_BASE_ADDR, _chip_id, QFLASH_CHIP_ID_LEN); + } + if (0 == ret) + { + memcpy(chip_id, _chip_id, QFLASH_CHIP_ID_LEN); + } + return ret; +} + +#undef FF_n16 + +unsigned int tls_sleep(unsigned int seconds) +{ + int ret = 0; + ret = tls_msleep(seconds * 1000); + return ret; +} + + +int tls_msleep(unsigned int msec) +{ + int ret = 0; + ret = tls_delay_via_timer(msec, TIMER_MS_UNIT_FLAG); + return ret; +} + +int tls_usleep(unsigned int /*useconds_t*/ usec) +{ + int ret = 0; + ret = tls_delay_via_timer(usec, TIMER_US_UNIT_FLAG); + return ret; +} diff --git a/sdk/Platform/Drivers/flash/wm_fls.c b/sdk/Platform/Drivers/flash/wm_fls.c new file mode 100644 index 0000000..cc4b833 --- /dev/null +++ b/sdk/Platform/Drivers/flash/wm_fls.c @@ -0,0 +1,577 @@ +/** + * @file wm_fls.c + * + * @brief flash Driver Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#include +#include +#include + +#include "list.h" +#include "wm_hostspi.h" +#include "wm_flash.h" +#include "wm_dbg.h" +#include "wm_mem.h" +#include "wm_fls_gd25qxx.h" + + +static struct tls_fls *spi_fls = NULL; + +int tls_spifls_read_id(u32 * id) +{ + u32 cmd; + int err; + + cmd = FLS_CMD_READ_DEV_ID; + *id = 0; + + err = tls_spi_read_with_cmd((const u8 *) &cmd, 4, (u8 *) id, 3); + + if (err != TLS_SPI_STATUS_OK) + { + TLS_DBGPRT_ERR("flash read ID fail(%d)!\n", err); + return TLS_FLS_STATUS_EIO; + } + + TLS_DBGPRT_FLASH_INFO("flash ID - 0x%x.\n", *id); + + return TLS_FLS_STATUS_OK; +} + + +/** + * @brief This function is used to read data from the flash. + * + * @param[in] addr is byte offset addr for read from the flash. + * @param[in] buf is user for data buffer of flash read + * @param[in] len is byte length for read. + * + * @retval TLS_FLS_STATUS_OK if read sucsess + * @retval TLS_FLS_STATUS_EIO if read fail + * @retval TLS_FLS_STATUS_EPERM if flash driver module not beed installed + * @retval TLS_FLS_STATUS_ENODRV if the current spi flash driver not installed + * @retval TLS_FLS_STATUS_EINVAL if parameter error + * + * @note None + */ +int tls_spifls_read(u32 addr, u8 * buf, u32 len) +{ + int err; + u32 read_bytes; + struct tls_fls_drv *drv; + + if (spi_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (spi_fls->current_drv == NULL) + { + TLS_DBGPRT_ERR("the current spi flash driver not installed!\n"); + return TLS_FLS_STATUS_ENODRV; + } + + if ((addr >= spi_fls->current_drv->total_size) || (len == 0) + || (buf == NULL)) + { + return TLS_FLS_STATUS_EINVAL; + } + tls_os_sem_acquire(spi_fls->fls_lock, 0); + drv = spi_fls->current_drv; + read_bytes = + ((addr + len) > drv->total_size) ? (drv->total_size - addr) : len; + + err = TLS_FLS_STATUS_OK; + + err = drv->read(addr, buf, read_bytes); + tls_os_sem_release(spi_fls->fls_lock); + return err; +} + +int tls_spifls_fast_read(u32 addr, u8 * buf, u32 len) +{ + int err; + u32 read_bytes; + struct tls_fls_drv *drv; + + if (spi_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (spi_fls->current_drv == NULL) + { + TLS_DBGPRT_ERR("the current spi flash driver not installed!\n"); + return TLS_FLS_STATUS_ENODRV; + } + + if ((addr >= spi_fls->current_drv->total_size) || (len == 0) + || (buf == NULL)) + { + return TLS_FLS_STATUS_EINVAL; + } + if ((spi_fls->current_drv->flags & TLS_FLS_FLAG_FAST_READ) != + TLS_FLS_FLAG_FAST_READ) + { + return TLS_FLS_STATUS_ENOSUPPORT; + } + + drv = spi_fls->current_drv; + read_bytes = + ((addr + len) > drv->total_size) ? (drv->total_size - addr) : len; + + err = TLS_FLS_STATUS_OK; + + err = drv->fast_read(addr, buf, read_bytes); + + return err; +} + +int tls_spifls_page_write(u32 page, u8 * buf, u32 page_cnt) +{ + int err; + u32 write_pages; + u32 i; + struct tls_fls_drv *drv; + + if (spi_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (spi_fls->current_drv == NULL) + { + TLS_DBGPRT_ERR("the current spi flash driver not installed!\n"); + return TLS_FLS_STATUS_ENODRV; + } + + if ((page >= (spi_fls->current_drv->total_size / spi_fls->current_drv->page_size)) + || (page_cnt == 0) || (buf == NULL)) + { + return TLS_FLS_STATUS_EINVAL; + } + + drv = spi_fls->current_drv; + write_pages = ((page + page_cnt) > (drv->total_size / drv->page_size)) ? + ((drv->total_size / drv->page_size) -page) : page_cnt; + + err = TLS_FLS_STATUS_OK; + + for (i = 0; i < write_pages; i++) + { + err = drv->page_write(page + i, buf + i * drv->page_size); + if (err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("flash page write fail(page %d)!\n", (page + i)); + break; + } + } + + return err; +} + +/** + * @brief This function is used to write data to the flash. + * + * @param[in] addr is byte offset addr for write to the flash + * @param[in] buf is the data buffer want to write to flash + * @param[in] len is the byte length want to write + * + * @retval TLS_FLS_STATUS_OK if write flash success + * @retval TLS_FLS_STATUS_EPERM if flash struct point is null + * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed + * @retval TLS_FLS_STATUS_EINVAL if argument is invalid + * @retval TLS_FLS_STATUS_EIO if io error + * @retval TLS_FLS_STATUS_ENOMEM if memory allocation fail + * + * @note None + */ +int tls_spifls_write(u32 addr, u8 * buf, u32 len) +{ + u8 *cache; + int err; + u32 sector_addr; + u32 sector_off; + u32 sector_remain; + u32 cntforFF; + u32 write_bytes; + u32 write_page = 0; + u32 i; + struct tls_fls_drv *drv; + + if (spi_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (spi_fls->current_drv == NULL) + { + TLS_DBGPRT_ERR("the current spi flash driver not installed!\n"); + return TLS_FLS_STATUS_ENODRV; + } + + if ((addr >= spi_fls->current_drv->total_size) || (len == 0) + || (buf == NULL)) + { + return TLS_FLS_STATUS_EINVAL; + } + tls_os_sem_acquire(spi_fls->fls_lock, 0); + drv = spi_fls->current_drv; + + err = TLS_FLS_STATUS_OK; + + cache = tls_mem_alloc(drv->sector_size); + if (cache == NULL) + { + tls_os_sem_release(spi_fls->fls_lock); + TLS_DBGPRT_ERR("allocate sector cache memory(%dB) fail!\n", + drv->sector_size); + return TLS_FLS_STATUS_ENOMEM; + } + + sector_addr = addr / drv->sector_size; //Section addr + sector_off = (addr % drv->sector_size); //Offset in section + sector_remain = drv->sector_size - sector_off; + write_bytes = len; + TLS_DBGPRT_FLASH_INFO("write to flash: sector address - %d, sector off - %x.\n", sector_addr, + sector_off); + + if(write_bytes <= sector_remain) + { + sector_remain = write_bytes; //Not bigger with remain size in section + } + while (1) + { + TLS_DBGPRT_FLASH_INFO("firstly, read the sector - %d to cache.\n", sector_addr); + err = drv->read(sector_addr * drv->sector_size, cache, drv->sector_size); + if (err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("flash read fail(sector %d)!\n", sector_addr); + break; + } + + cntforFF = 0; + for(i = 0; i < sector_remain; i++) + { + if(cache[i + sector_off] == 0xFF) + { + cntforFF++; + } + cache[i + sector_off] = buf[i]; + } + + //If the flash address to be written is all 0xFF, then erase sector is not necessary; + if( cntforFF != sector_remain) + { + TLS_DBGPRT_FLASH_INFO("second, erase the sector - %d.\n", sector_addr); + err = drv->erase(sector_addr); + if (err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("flash erase fail(sector %d)!\n", sector_addr); + break; + } + } + + if( (cntforFF != sector_remain ) || ((sector_off == 0) && (sector_remain == drv->sector_size))) + { + for(i = 0; i < (drv->sector_size / drv->page_size); i++) + { + err = drv->page_write(sector_addr * drv->sector_size / drv->page_size + i, &cache[i * drv->page_size]); + if (err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("flash page write fail(page %d)!\n", i); + tls_mem_free(cache); + tls_os_sem_release(spi_fls->fls_lock); + return err; + } + } + } + else + { + write_page = (sector_off / drv->page_size) == 15 ? 16 : (sector_off + sector_remain) / drv->page_size + 1; + //As long as this sector is not erased in this round, we only program the changed pages instead of all of them. + for(i = (sector_off / drv->page_size); i < write_page; i++) + { + err = drv->page_write(sector_addr * drv->sector_size / drv->page_size + i, &cache[i * drv->page_size]); + if (err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("flash page write fail(page %d)!\n", i); + tls_mem_free(cache); + tls_os_sem_release(spi_fls->fls_lock); + return err; + } + } + } + + if(write_bytes == sector_remain) + { + break; + } + else + { + sector_addr++; + sector_off = 0; + buf += sector_remain; + write_bytes -= sector_remain; + if(write_bytes > (drv->sector_size)) + sector_remain = drv->sector_size; + else + sector_remain = write_bytes; //Next section will finish + } + } + + tls_mem_free(cache); + tls_os_sem_release(spi_fls->fls_lock); + return err; +} + + +int tls_spifls_erase(u32 sector) +{ + int err; + struct tls_fls_drv *drv; + + if (spi_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (spi_fls->current_drv == NULL) + { + TLS_DBGPRT_ERR("the current spi flash driver not installed!\n"); + return TLS_FLS_STATUS_ENODRV; + } + + if (sector >= (spi_fls->current_drv->total_size / spi_fls->current_drv->sector_size)) + { + TLS_DBGPRT_ERR("the sector to be erase overflow!\n"); + return TLS_FLS_STATUS_EINVAL; + } + tls_os_sem_acquire(spi_fls->fls_lock, 0); + drv = spi_fls->current_drv; + + err = TLS_FLS_STATUS_OK; + + err = drv->erase(sector); + tls_os_sem_release(spi_fls->fls_lock); + return err; +} + +int tls_spifls_chip_erase(void) +{ + int err; + struct tls_fls_drv *drv; + + if (spi_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (spi_fls->current_drv == NULL) + { + TLS_DBGPRT_ERR("the current spi flash driver not installed!\n"); + return TLS_FLS_STATUS_ENODRV; + } + + drv = spi_fls->current_drv; + + err = TLS_FLS_STATUS_OK; + + err = drv->chip_erase(); + + return err; +} + +int tls_spifls_get_param(u8 type, void *param) +{ + int err; + struct tls_fls_drv *drv; + + if (spi_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (spi_fls->current_drv == NULL) + { + TLS_DBGPRT_ERR("the current spi flash driver not installed!\n"); + return TLS_FLS_STATUS_ENODRV; + } + + if (param == NULL) + { + return TLS_FLS_STATUS_EINVAL; + } + tls_os_sem_acquire(spi_fls->fls_lock, 0); + drv = spi_fls->current_drv; + err = TLS_FLS_STATUS_OK; + switch (type) + { + case TLS_FLS_PARAM_TYPE_ID: + *((u32 *) param) = drv->id; + break; + + case TLS_FLS_PARAM_TYPE_SIZE: + *((u32 *) param) = drv->total_size; + break; + + case TLS_FLS_PARAM_TYPE_PAGE_SIZE: + *((u32 *) param) = drv->page_size; + break; + + case TLS_FLS_PARAM_TYPE_PROG_SIZE: + *((u32 *) param) = drv->program_size; + break; + + case TLS_FLS_PARAM_TYPE_SECTOR_SIZE: + *((u32 *) param) = drv->sector_size; + break; + + default: + TLS_DBGPRT_WARNING("invalid parameter ID!\n"); + err = TLS_FLS_STATUS_EINVAL; + break; + } + tls_os_sem_release(spi_fls->fls_lock); + return err; +} + +int tls_spifls_drv_register(struct tls_fls_drv *fls_drv) +{ + u32 cpu_sr; + struct tls_fls_drv *drv; + + if (fls_drv == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EINVAL; + } + + dl_list_for_each(drv, &spi_fls->fls_drvs, struct tls_fls_drv, drv_list) + { + if (drv->id == fls_drv->id) + { + TLS_DBGPRT_WARNING + ("corresponding spi flash driver has registered!\n"); + return TLS_FLS_STATUS_EEXIST; + } + } + + cpu_sr = tls_os_set_critical(); + dl_list_add_tail((struct dl_list *) &spi_fls->fls_drvs, + (struct dl_list *) &fls_drv->drv_list); + tls_os_release_critical(cpu_sr); + + TLS_DBGPRT_FLASH_INFO("the spi flash driver is registered successfully!\n"); + + return TLS_FLS_STATUS_OK; +} + +int tls_spifls_drv_unregister(struct tls_fls_drv *fls_drv) +{ + TLS_DBGPRT_WARNING + ("unregister spi flash driver operation is not supported!\n"); + return TLS_FLS_STATUS_EPERM; +} + +int tls_spifls_probe(void) +{ + int err; + u32 id; + struct tls_fls_drv *drv; + + if (spi_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + if (spi_fls->current_drv != NULL) + { + TLS_DBGPRT_ERR("the current spi flash has fount the matched driver!\n"); + return TLS_FLS_STATUS_EBUSY; + } + + TLS_DBGPRT_FLASH_INFO("try to read the current spi flash ID.\n"); + err = tls_spifls_read_id(&id); + if (err != TLS_FLS_STATUS_OK) + { + return err; + } + + TLS_DBGPRT_FLASH_INFO("current spi flash ID - 0x%x.\n", id); + + dl_list_for_each(drv, &spi_fls->fls_drvs, struct tls_fls_drv, drv_list) + { + err = drv->probe(id); + if (err != TLS_FLS_STATUS_OK) + { + return err; + } + + tls_spi_setup(drv->mode, drv->cs_active, drv->clock); + + TLS_DBGPRT_FLASH_INFO("matched the spi flash driver.\n"); + spi_fls->current_drv = drv; + break; + } + + if (spi_fls->current_drv == NULL) + { + TLS_DBGPRT_WARNING("not found the matched spi flash driver!\n"); + return TLS_FLS_STATUS_ENODRV; + } + + return TLS_FLS_STATUS_OK; +} + +int tls_spifls_init(void) +{ + struct tls_fls *fls; + int err; + + if (spi_fls != NULL) + { + TLS_DBGPRT_ERR("flash driver module has been installed!\n"); + return TLS_FLS_STATUS_EBUSY; + } + + fls = (struct tls_fls *) tls_mem_alloc(sizeof(struct tls_fls)); + if (fls == NULL) + { + TLS_DBGPRT_ERR("allocate @spi_fls fail!\n"); + return TLS_FLS_STATUS_ENOMEM; + } + + memset(fls, 0, sizeof(*fls)); + dl_list_init((struct dl_list *) &fls->fls_drvs); + err = tls_os_sem_create(&fls->fls_lock, 1); + if (err != TLS_OS_SUCCESS) + { + tls_mem_free(fls); + TLS_DBGPRT_ERR("create semaphore @fls_lock fail!\n"); + return TLS_FLS_STATUS_ENOMEM; + } + spi_fls = fls; + + tls_spifls_drv_install(); + + return TLS_FLS_STATUS_OK; +} + +int tls_spifls_exit(void) +{ + TLS_DBGPRT_FLASH_INFO("Not support flash driver module uninstalled!\n"); + return TLS_FLS_STATUS_EPERM; +} + diff --git a/sdk/Platform/Drivers/flash/wm_fls_gd25qxx.c b/sdk/Platform/Drivers/flash/wm_fls_gd25qxx.c new file mode 100644 index 0000000..625023c --- /dev/null +++ b/sdk/Platform/Drivers/flash/wm_fls_gd25qxx.c @@ -0,0 +1,307 @@ +/** + * @file wm_fls_gd25qxx.c + * + * @brief gd25qxx flash Driver Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#include +#include +#include "wm_type_def.h" +#include "wm_flash.h" +#include "wm_hostspi.h" +#include "wm_fls_gd25qxx.h" +#include "wm_debug.h" +#include "wm_gpio.h" + +static int tls_spifls_drv_read(u32 addr, u8 * buf, u32 len); +static int tls_spifls_drv_fast_read(u32 addr, u8 * buf, u32 len); +static int tls_spifls_drv_page_write(u32 page, u8 * buf); +static int tls_spifls_drv_erase(u32 sector); +static int tls_spifls_drv_chip_erase(void); +static int tls_spifls_drv_probe(u32 id); +static void tls_spifls_drv_remove(void); + +static struct tls_fls_drv exspi_fls = { + .drv_list = {NULL, NULL}, + .clock = SPI_SCLK, + .mode = TLS_SPI_MODE_0, + .cs_active = TLS_SPI_CS_LOW, + .flags = 0, + .read = tls_spifls_drv_read, + .fast_read = tls_spifls_drv_fast_read, + .page_write = tls_spifls_drv_page_write, + .erase = tls_spifls_drv_erase, + .chip_erase = tls_spifls_drv_chip_erase, + .probe = tls_spifls_drv_probe, + .remove = tls_spifls_drv_remove +}; + +static struct tls_fls_drv *exspifls_drv = NULL; + + +static unsigned int swap32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} + +static int tls_spifls_drv_write_enable(void) +{ + u8 cmd; + int err; + + cmd = EXSPIFLASH_WRITE_ENABLE; + + err = tls_spi_write((const u8 *) &cmd, 1); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + return TLS_FLS_STATUS_OK; +} + +static int tls_spifls_drv_wait_write_enable(void) +{ + u8 cmd; + u8 sr; + int err; + + cmd = EXSPIFLASH_READ_SR1; + sr = 0; + do + { + err = tls_spi_read_with_cmd((const u8 *) &cmd, 1, &sr, 1); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + if (sr & FLASH_STATUS_WEL) + { + break; + } + } + while (1); + + return TLS_FLS_STATUS_OK; +} + +static int tls_spifls_drv_wait_flash_ready(void) +{ + u8 cmd; + u8 sr; + int err; + + cmd = EXSPIFLASH_READ_SR1; + sr = 0; + do + { + err = tls_spi_read_with_cmd((const u8 *) &cmd, 1, &sr, 1); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + if ((sr & FLASH_STATUS_BUSY) == 0x00) + { + break; + } + } + while (1); + + return TLS_FLS_STATUS_OK; +} + +static int tls_spifls_drv_read(u32 addr, u8 * buf, u32 len) +{ + u32 cmd; + int err; + + cmd = 0; + cmd |= EXSPIFLASH_DATA_READ; + cmd |= swap32(addr) & 0xffffff00; + err = tls_spi_read_with_cmd((const u8 *) &cmd, 4, buf, len); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + return TLS_FLS_STATUS_OK; +} + +static int tls_spifls_drv_fast_read(u32 addr, u8 * buf, u32 len) +{ + return TLS_FLS_STATUS_ENOSUPPORT; +} + +static int tls_spifls_drv_page_write(u32 page, u8 * buf) +{ + u32 cmd; + int err; + + err = tls_spifls_drv_write_enable(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + err = tls_spifls_drv_wait_write_enable(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + cmd = 0; + cmd |= EXSPIFLASH_PAGE_PROGRAM; + cmd |= swap32(page * exspifls_drv->page_size) & 0xffffff00; + err = tls_spi_write_with_cmd((const u8 *) &cmd, 4, (const u8 *) buf, + exspifls_drv->page_size); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + err = tls_spifls_drv_wait_flash_ready(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + return TLS_FLS_STATUS_OK; +} + +static int tls_spifls_drv_erase(u32 sector) +{ + u32 cmd; + int err; + + err = tls_spifls_drv_write_enable(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + err = tls_spifls_drv_wait_write_enable(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + cmd = 0; + cmd |= EXSPIFLASH_SECTOR_ERASE; + cmd |= swap32(sector * exspifls_drv->sector_size) & 0xffffff00; + err = tls_spi_write((const u8 *) &cmd, 4); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + tls_os_time_delay(6); + err = tls_spifls_drv_wait_flash_ready(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + return TLS_FLS_STATUS_OK; +} + +static int tls_spifls_drv_chip_erase(void) +{ + u8 cmd; + int err; + + err = tls_spifls_drv_write_enable(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + err = tls_spifls_drv_wait_write_enable(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + cmd = EXSPIFLASH_CHIP_ERASE; + err = tls_spi_write((const u8 *) &cmd, 1); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + err = tls_spifls_drv_wait_flash_ready(); + if (err != TLS_SPI_STATUS_OK) + { + return TLS_FLS_STATUS_EIO; + } + + return TLS_FLS_STATUS_OK; +} + +static int tls_spifls_drv_probe(u32 id) +{ +// int i = 0; + + + if (!id) + { + return TLS_FLS_STATUS_EINVAL; + } + + exspi_fls.id = id; + if ((id>>16)&0xFF) + { + exspi_fls.total_size = 1 << (id>>16); + }else{ + exspi_fls.total_size = FLASH_TOTAL_SIZE; /*1MByte*/ + } + + exspi_fls.page_size = PAGE_SIZE; + exspi_fls.program_size = PROGRAM_SIZE; + exspi_fls.sector_size = SECTOR_SIZE; + + + exspifls_drv = &exspi_fls; + return TLS_FLS_STATUS_OK; +} + +static void tls_spifls_drv_remove(void) +{ + exspifls_drv = NULL; +} + + +/** + * @brief This function is used to install gd25qxx driver. + * + * @param[in] None + * + * @retval TLS_FLS_STATUS_OK if write flash success + * @retval TLS_FLS_STATUS_EPERM if flash struct point is null + * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed + * @retval TLS_FLS_STATUS_EINVAL if argument is invalid + * @retval TLS_FLS_STATUS_EIO if io error + * @retval TLS_FLS_STATUS_EEXIST if driver is already existed + * + * @note None + */ +int tls_spifls_drv_install(void) +{ + int err; + extern int tls_spifls_probe(void); + extern int tls_spifls_drv_register(struct tls_fls_drv *fls_drv); + + err = tls_spifls_drv_register((struct tls_fls_drv *) &exspi_fls); + if (err == TLS_FLS_STATUS_EEXIST) + { + return err; + } + TLS_DBGPRT_INFO("register the spi flash driver - %d.\n", err); + + err = tls_spifls_probe(); + TLS_DBGPRT_INFO("probe spi flash - %d.\n", err); + + return err; +} diff --git a/sdk/Platform/Drivers/gpio/wm_gpio.c b/sdk/Platform/Drivers/gpio/wm_gpio.c new file mode 100644 index 0000000..40c980c --- /dev/null +++ b/sdk/Platform/Drivers/gpio/wm_gpio.c @@ -0,0 +1,411 @@ +/** + * @file wm_gpio.c + * + * @brief GPIO Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#include "wm_gpio.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_osal.h" +#include "tls_common.h" + +struct gpio_irq_context{ + tls_gpio_irq_callback callback; + void *arg; +}; + + +static struct gpio_irq_context gpio_context[WM_IO_PB_30 - WM_IO_PA_00 + 1] = {{0,0}}; + + +void GPIOA_IRQHandler(void) +{ + u8 i = 0; + u8 found = 0; + u32 reg = 0; + + reg = tls_reg_read32(HR_GPIO_MIS); + + for (i = 0; i <= WM_IO_PA_15; i++) + { + if (reg & BIT(i)) + { + found = 1; + break; + } + } + + if (found) + { + if (NULL != gpio_context[i].callback) + gpio_context[i].callback(gpio_context[i].arg); + } + return; + +} + +void GPIOB_IRQHandler(void) + { + u8 i = 0; + u8 found = 0; + u32 reg = 0; + + reg = tls_reg_read32(HR_GPIO_MIS + TLS_IO_AB_OFFSET); + + + for (i = WM_IO_PB_00; i <= WM_IO_PB_31; i++) + { + if (reg & BIT(i - WM_IO_PB_00)) + { + found = 1; + break; + } + } + + if (found) + { + if (NULL != gpio_context[i].callback) + gpio_context[i].callback(gpio_context[i].arg); + } + + return; +} + +/** + * @brief This function is used to config gpio function + * + * @param[in] gpio_pin gpio pin num + * @param[in] dir gpio direction + * @param[in] attr gpio attribute + * + * @return None + * + * @note + * All gpio's attribute can only be set as WM_GPIO_ATTR_FLOATING or WM_GPIO_ATTR_PULLHIGH on W600/W60X. + */ +void tls_gpio_cfg(enum tls_io_name gpio_pin, enum tls_gpio_dir dir, enum tls_gpio_attr attr) +{ + u8 pin; + u16 offset; + + if (gpio_pin >= WM_IO_PB_00) + { + pin = gpio_pin - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = gpio_pin; + offset = 0; + } + + /* enable gpio function */ + tls_io_cfg_set(gpio_pin, WM_IO_OPT5_GPIO); + + /* gpio direction */ + if (WM_GPIO_DIR_OUTPUT == dir) + { + tls_reg_write32(HR_GPIO_DIR + offset, tls_reg_read32(HR_GPIO_DIR + offset) | BIT(pin)); /* 1 set output */ + } + else if (WM_GPIO_DIR_INPUT == dir) + { + tls_reg_write32(HR_GPIO_DIR + offset, tls_reg_read32(HR_GPIO_DIR + offset) & (~BIT(pin))); /* 0 set input */ + } + + /* gpio pull attribute */ + if (WM_GPIO_ATTR_FLOATING == attr) + { + tls_reg_write32(HR_GPIO_PULL_EN + offset, tls_reg_read32(HR_GPIO_PULL_EN + offset) | BIT(pin)); /* 1 disable pull */ + } + else if (WM_GPIO_ATTR_PULLHIGH == attr) + { + tls_reg_write32(HR_GPIO_PULL_EN + offset, tls_reg_read32(HR_GPIO_PULL_EN + offset) & (~BIT(pin))); /* 0 enable pull */ + } +} + +/** + * @brief This function is used to read gpio status + * + * @param[in] gpio_pin gpio pin num + * + * @retval 0 low level + * @retval 1 high level + * + * @note None + */ +u8 tls_gpio_read(enum tls_io_name gpio_pin) +{ + u32 reg_en; + u32 reg; + u8 pin; + u16 offset; + + if (gpio_pin >= WM_IO_PB_00) + { + pin = gpio_pin - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = gpio_pin; + offset = 0; + } + + reg_en = tls_reg_read32(HR_GPIO_DATA_EN + offset); + tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en | (1 << pin)); + reg = tls_reg_read32(HR_GPIO_DATA + offset); + tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en); + if(reg & (0x1 << pin)) + return 1; + else + return 0; +} + +/** + * @brief This function is used to modify gpio status + * + * @param[in] gpio_pin gpio pin num + * @param[in] value power level + * 0: low level + * 1: high level + * + * @return None + * + * @note None + */ +void tls_gpio_write(enum tls_io_name gpio_pin, u8 value) +{ + u32 cpu_sr = 0; + u32 reg; + u32 reg_en; + u8 pin; + u16 offset; + + if (gpio_pin >= WM_IO_PB_00) + { + pin = gpio_pin - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = gpio_pin; + offset = 0; + } + + + cpu_sr = tls_os_set_critical(); + + reg_en = tls_reg_read32(HR_GPIO_DATA_EN + offset); + tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en | (1 << pin)); + + reg = tls_reg_read32(HR_GPIO_DATA + offset); + if(value) + tls_reg_write32(HR_GPIO_DATA + offset, reg | (1 << pin)); /* write high */ + else + tls_reg_write32(HR_GPIO_DATA + offset, reg & (~(1 << pin)));/* write low */ + + tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en); + + tls_os_release_critical(cpu_sr); +} + +/** + * @brief This function is used to config gpio interrupt + * + * @param[in] gpio_pin gpio pin num + * @param[in] mode interrupt trigger type + * + * @return None + * + * @note None + */ +void tls_gpio_irq_enable(enum tls_io_name gpio_pin, enum tls_gpio_irq_trig mode) +{ + u32 reg; + u8 pin; + u16 offset; + u8 vec_no; + + if (gpio_pin >= WM_IO_PB_00) + { + pin = gpio_pin - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + vec_no = GPIOB_INT; + } + else + { + pin = gpio_pin; + offset = 0; + vec_no = GPIO_INT; + } + switch(mode) + { + case WM_GPIO_IRQ_TRIG_RISING_EDGE: + reg = tls_reg_read32(HR_GPIO_IS + offset); + reg &= (~(0x1 << pin)); + tls_reg_write32(HR_GPIO_IS + offset, reg); /* 0 edge trigger */ + reg = tls_reg_read32(HR_GPIO_IBE + offset); + reg &= (~(0x1 << pin)); + tls_reg_write32(HR_GPIO_IBE + offset, reg); /* 0 edge trigger */ + reg = tls_reg_read32(HR_GPIO_IEV + offset); + reg |= (0x1 << pin); + tls_reg_write32(HR_GPIO_IEV + offset, reg); /* 1 rising edge trigger */ + break; + case WM_GPIO_IRQ_TRIG_FALLING_EDGE: + reg = tls_reg_read32(HR_GPIO_IS + offset); + reg &= (~(0x1 << pin)); + tls_reg_write32(HR_GPIO_IS + offset, reg); /* 0 edge trigger */ + reg = tls_reg_read32(HR_GPIO_IBE + offset); + reg &= (~(0x1 << pin)); + tls_reg_write32(HR_GPIO_IBE + offset, reg); /* 0 edge trigger */ + reg = tls_reg_read32(HR_GPIO_IEV + offset); + reg &= (~(0x1 << pin)); + tls_reg_write32(HR_GPIO_IEV + offset, reg); /* 0 falling edge trigger */ + break; + case WM_GPIO_IRQ_TRIG_DOUBLE_EDGE: + reg = tls_reg_read32(HR_GPIO_IS + offset); + tls_reg_write32(HR_GPIO_IS + offset, reg & (~(0x1 << pin))); /* 0 edge trigger */ + reg = tls_reg_read32(HR_GPIO_IBE + offset); + tls_reg_write32(HR_GPIO_IBE + offset, reg | (0x1 << pin)); /* 1 double edge trigger */ + break; + case WM_GPIO_IRQ_TRIG_HIGH_LEVEL: + reg = tls_reg_read32(HR_GPIO_IS + offset); + tls_reg_write32(HR_GPIO_IS + offset, reg | (0x1 << pin)); /* 1 level trigger */ + reg = tls_reg_read32(HR_GPIO_IEV + offset); + tls_reg_write32(HR_GPIO_IEV + offset, reg | (0x1 << pin)); /* 1 high level trigger */ + break; + case WM_GPIO_IRQ_TRIG_LOW_LEVEL: + reg = tls_reg_read32(HR_GPIO_IS + offset); + tls_reg_write32(HR_GPIO_IS + offset, reg | (0x1 << pin)); /* 1 level trigger */ + reg = tls_reg_read32(HR_GPIO_IEV + offset); + tls_reg_write32(HR_GPIO_IEV + offset, reg & (~(0x1 << pin))); /* 0 low level trigger */ + break; + } + + reg = tls_reg_read32(HR_GPIO_IE + offset); + reg |= (0x1 << pin); + tls_reg_write32(HR_GPIO_IE + offset, reg); /* enable interrupt */ + tls_irq_enable(vec_no); +} + +/** + * @brief This function is used to disable gpio interrupt + * + * @param[in] gpio_pin gpio pin num + * + * @return None + * + * @note None + */ +void tls_gpio_irq_disable(enum tls_io_name gpio_pin) +{ + u32 reg; + u8 pin; + u16 offset; + + if (gpio_pin >= WM_IO_PB_00) + { + pin = gpio_pin - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = gpio_pin; + offset = 0; + } + + reg = tls_reg_read32(HR_GPIO_IE + offset); + tls_reg_write32(HR_GPIO_IE + offset, reg & (~(0x1 << pin))); /* disable interrupt */ + tls_irq_disable(GPIO_INT); +} + +/** + * @brief This function is used to get gpio interrupt status + * + * @param[in] gpio_pin gpio pin num + * + * @retval 0 no interrupt happened + * @retval 1 interrupt happened + * + * @note None + */ +u8 tls_get_gpio_irq_status(enum tls_io_name gpio_pin) +{ + u32 reg; + u8 pin; + u16 offset; + + if (gpio_pin >= WM_IO_PB_00) + { + pin = gpio_pin - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = gpio_pin; + offset = 0; + } + + reg = tls_reg_read32(HR_GPIO_RIS + offset); + if(reg & (0x1 << pin)) + return 1; + else + return 0; +} + +/** + * @brief This function is used to clear gpio interrupt flag + * + * @param[in] gpio_pin gpio pin num + * + * @return None + * + * @note None + */ +void tls_clr_gpio_irq_status(enum tls_io_name gpio_pin) +{ + u32 reg; + u8 pin; + u16 offset; + + if (gpio_pin >= WM_IO_PB_00) + { + pin = gpio_pin - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = gpio_pin; + offset = 0; + } + + reg = tls_reg_read32(HR_GPIO_IC + offset); + tls_reg_write32(HR_GPIO_IC + offset, reg | (0x1 << pin)); /* 1 clear interrupt status */ +} + +/** + * @brief This function is used to register gpio interrupt + * + * @param[in] gpio_pin gpio pin num + * @param[in] callback the gpio interrupt call back function + * @param[in] arg parammeter for the callback + * + * @return None + * + * @note + * gpio callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_gpio_isr_register(enum tls_io_name gpio_pin, + tls_gpio_irq_callback callback, + void *arg) +{ + gpio_context[gpio_pin].callback = callback; + gpio_context[gpio_pin].arg = arg; +} + diff --git a/sdk/Platform/Drivers/gpio/wm_gpio_afsel.c b/sdk/Platform/Drivers/gpio/wm_gpio_afsel.c new file mode 100644 index 0000000..20863db --- /dev/null +++ b/sdk/Platform/Drivers/gpio/wm_gpio_afsel.c @@ -0,0 +1,1012 @@ +/** + * @file wm_gpio.c + * + * @brief GPIO Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#include "wm_gpio.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_osal.h" +#include "tls_common.h" +#include "wm_gpio_afsel.h" +#include "wm_debug.h" +#include "wm_pmu.h" + +void wm_hspi_gpio_config(uint8_t numsel) +{ + switch(numsel) + { + case 0: + tls_io_cfg_set(WM_IO_PB_14, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_15, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_16, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_17, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_18, WM_IO_OPTION1); + break; + + case 1: + tls_io_cfg_set(WM_IO_PB_08, WM_IO_OPTION2); + tls_io_cfg_set(WM_IO_PB_09, WM_IO_OPTION2); + tls_io_cfg_set(WM_IO_PB_10, WM_IO_OPTION2); + tls_io_cfg_set(WM_IO_PB_11, WM_IO_OPTION2); + tls_io_cfg_set(WM_IO_PB_12, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("highspeed spi gpio config error!"); + break; + } +} + +void wm_spi_ck_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_01: + tls_io_cfg_set(WM_IO_PA_01, WM_IO_OPTION3); + break; + + case WM_IO_PB_16: + tls_io_cfg_set(WM_IO_PB_16, WM_IO_OPTION3); + break; + + case WM_IO_PB_27: + tls_io_cfg_set(WM_IO_PB_27, WM_IO_OPTION1); + break; + + case WM_IO_PA_11: + tls_io_cfg_set(WM_IO_PA_11, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("spi ck afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); +} + +void wm_spi_cs_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_02: + tls_io_cfg_set(WM_IO_PA_02, WM_IO_OPTION3); + break; + + case WM_IO_PB_15: + tls_io_cfg_set(WM_IO_PB_15, WM_IO_OPTION3); + break; + + case WM_IO_PB_00: + tls_io_cfg_set(WM_IO_PB_00, WM_IO_OPTION1); + break; + + case WM_IO_PB_07: + tls_io_cfg_set(WM_IO_PB_07, WM_IO_OPTION4); + break; + + case WM_IO_PA_12: + tls_io_cfg_set(WM_IO_PA_12, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("spi cs afsel config error!"); + break; + } +} + + +void wm_spi_di_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_03: + tls_io_cfg_set(WM_IO_PA_03, WM_IO_OPTION3); + break; + + case WM_IO_PB_17: + tls_io_cfg_set(WM_IO_PB_17, WM_IO_OPTION3); + break; + + case WM_IO_PB_01: + tls_io_cfg_set(WM_IO_PB_01, WM_IO_OPTION1); + break; + + case WM_IO_PA_05: + tls_io_cfg_set(WM_IO_PA_05, WM_IO_OPTION3); + break; + + case WM_IO_PA_10: + tls_io_cfg_set(WM_IO_PA_10, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("spi di afsel config error!"); + break; + } +} + +void wm_spi_do_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PB_18: + tls_io_cfg_set(WM_IO_PB_18, WM_IO_OPTION3); + break; + + case WM_IO_PB_02: + tls_io_cfg_set(WM_IO_PB_02, WM_IO_OPTION1); + break; + + case WM_IO_PA_04: + tls_io_cfg_set(WM_IO_PA_04, WM_IO_OPTION3); + break; + + case WM_IO_PA_09: + tls_io_cfg_set(WM_IO_PA_09, WM_IO_OPTION4); + break; + + case WM_IO_PA_10: + tls_io_cfg_set(WM_IO_PA_10, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("spi do afsel config error!"); + break; + } +} + +void wm_sdio_config(uint8_t numsel) +{ + switch(numsel) + { + case 0: + tls_io_cfg_set(WM_IO_PB_08, WM_IO_OPTION3); + tls_io_cfg_set(WM_IO_PB_09, WM_IO_OPTION3); + tls_io_cfg_set(WM_IO_PB_10, WM_IO_OPTION3); + tls_io_cfg_set(WM_IO_PB_11, WM_IO_OPTION3); + tls_io_cfg_set(WM_IO_PB_12, WM_IO_OPTION3); + tls_bitband_write(HR_GPIOB_DATA_PULLEN, 8, 0); + tls_bitband_write(HR_GPIOB_DATA_PULLEN, 9, 0); + tls_bitband_write(HR_GPIOB_DATA_PULLEN, 10, 0); + tls_bitband_write(HR_GPIOB_DATA_PULLEN, 11, 0); + tls_bitband_write(HR_GPIOB_DATA_PULLEN, 12, 0); + break; + + default: + TLS_DBGPRT_ERR("sdio afsel config error!"); + break; + } +} + +void wm_sdio_cmd_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_06: + tls_io_cfg_set(WM_IO_PA_06, WM_IO_OPTION3); + tls_bitband_write(HR_GPIOA_DATA_PULLEN, 6, 0); + break; + + case WM_IO_PB_07: + tls_io_cfg_set(WM_IO_PB_07, WM_IO_OPTION3); + tls_bitband_write(HR_GPIOB_DATA_PULLEN, 7, 0); + break; + + case WM_IO_PB_13: + tls_io_cfg_set(WM_IO_PB_13, WM_IO_OPTION4); + tls_bitband_write(HR_GPIOB_DATA_PULLEN, 13, 0); + break; + + default: + TLS_DBGPRT_ERR("sdio cmd afsel config error!"); + break; + } +} + +void wm_uart0_tx_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_04: + tls_io_cfg_set(WM_IO_PA_04, WM_IO_OPTION1); + break; + + case WM_IO_PA_08: + tls_io_cfg_set(WM_IO_PA_08, WM_IO_OPTION3); + break; + + case WM_IO_PB_07: + tls_io_cfg_set(WM_IO_PB_07, WM_IO_OPTION2); + break; + + case WM_IO_PB_26: + tls_io_cfg_set(WM_IO_PB_26, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("uart0 tx afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_UART0); +} + +void wm_uart0_rx_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_05: + tls_gpio_cfg(WM_IO_PA_05, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PA_05, WM_IO_OPTION1); + break; + + case WM_IO_PA_09: + tls_gpio_cfg(WM_IO_PA_09, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PA_09, WM_IO_OPTION3); + break; + + case WM_IO_PB_06: + tls_gpio_cfg(WM_IO_PB_06, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_06, WM_IO_OPTION2); + break; + + case WM_IO_PB_25: + tls_gpio_cfg(WM_IO_PB_25, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_25, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("uart0 rx afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_UART0); +} + +void wm_uart0_rts_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_13: + tls_io_cfg_set(WM_IO_PA_13, WM_IO_OPTION1); + break; + + case WM_IO_PB_04: + tls_io_cfg_set(WM_IO_PB_04, WM_IO_OPTION2); + break; + + case WM_IO_PB_24: + tls_io_cfg_set(WM_IO_PB_24, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("uart0 rts afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_UART0); +} + +void wm_uart0_cts_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_14: + tls_io_cfg_set(WM_IO_PA_14, WM_IO_OPTION1); + break; + + case WM_IO_PB_05: + tls_io_cfg_set(WM_IO_PB_05, WM_IO_OPTION2); + break; + + case WM_IO_PB_23: + tls_io_cfg_set(WM_IO_PB_23, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("uart0 cts afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_UART0); +} + +void wm_uart1_tx_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PB_12: + tls_io_cfg_set(WM_IO_PB_12, WM_IO_OPTION1); + break; + + case WM_IO_PB_18: + tls_io_cfg_set(WM_IO_PB_18, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("uart1 tx afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_UART1); +} + +void wm_uart1_rx_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PB_11: + tls_gpio_cfg(WM_IO_PB_11, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_11, WM_IO_OPTION1); + break; + + case WM_IO_PB_17: + tls_gpio_cfg(WM_IO_PB_17, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_17, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("uart1 rx afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_UART1); +} + +void wm_uart1_rts_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PB_10: + tls_io_cfg_set(WM_IO_PB_10, WM_IO_OPTION1); + break; + + default: + TLS_DBGPRT_ERR("uart1 rts afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_UART1); +} + +void wm_uart1_cts_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PB_09: + tls_io_cfg_set(WM_IO_PB_09, WM_IO_OPTION1); + break; + + default: + TLS_DBGPRT_ERR("uart1 cts afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_UART1); +} + +void wm_uart2_tx_scio_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_01: + tls_io_cfg_set(WM_IO_PA_01, WM_IO_OPTION1); + break; + + case WM_IO_PA_11: + tls_io_cfg_set(WM_IO_PA_11, WM_IO_OPTION3); + break; + + case WM_IO_PB_02: + tls_io_cfg_set(WM_IO_PB_02, WM_IO_OPTION2); + break; + + case WM_IO_PB_20: + tls_io_cfg_set(WM_IO_PB_20, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("uart2 tx afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_7816); +} + +void wm_uart2_rx_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_00: + tls_gpio_cfg(WM_IO_PA_00, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PA_00, WM_IO_OPTION1); + break; + + case WM_IO_PA_10: + tls_gpio_cfg(WM_IO_PA_10, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PA_10, WM_IO_OPTION3); + break; + + case WM_IO_PB_03: + tls_gpio_cfg(WM_IO_PB_03, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_03, WM_IO_OPTION2); + break; + + case WM_IO_PB_19: + tls_gpio_cfg(WM_IO_PB_19, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_19, WM_IO_OPTION2); + break; + + case WM_IO_PB_30: + tls_gpio_cfg(WM_IO_PB_30, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_30, WM_IO_OPTION1); + break; + + default: + TLS_DBGPRT_ERR("uart2 rx afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_7816); +} + +void wm_uart2_rts_scclk_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_02: + tls_io_cfg_set(WM_IO_PA_02, WM_IO_OPTION1); + break; + + case WM_IO_PA_12: + tls_io_cfg_set(WM_IO_PA_12, WM_IO_OPTION3); + break; + + case WM_IO_PB_01: + tls_io_cfg_set(WM_IO_PB_01, WM_IO_OPTION2); + break; + + case WM_IO_PB_21: + tls_io_cfg_set(WM_IO_PB_21, WM_IO_OPTION2); + break; + + case WM_IO_PB_06: + tls_io_cfg_set(WM_IO_PB_06, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("uart2 rts afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_7816); +} + +void wm_uart2_cts_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_03: + tls_io_cfg_set(WM_IO_PA_03, WM_IO_OPTION1); + break; + + case WM_IO_PB_28: + tls_io_cfg_set(WM_IO_PB_28, WM_IO_OPTION2); + break; + + case WM_IO_PB_00: + tls_io_cfg_set(WM_IO_PB_00, WM_IO_OPTION2); + break; + + case WM_IO_PB_22: + tls_io_cfg_set(WM_IO_PB_22, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("uart2 cts afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_7816); +} + +void wm_i2s_m_ck_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_04: + tls_io_cfg_set(WM_IO_PA_04, WM_IO_OPTION4); + break; + + case WM_IO_PA_08: + tls_io_cfg_set(WM_IO_PA_08, WM_IO_OPTION1); + break; + + case WM_IO_PB_08: + tls_io_cfg_set(WM_IO_PB_08, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("i2s master ck afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2S); +} + +void wm_i2s_m_ws_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_13: + tls_io_cfg_set(WM_IO_PA_13, WM_IO_OPTION2); + break; + + case WM_IO_PA_09: + tls_io_cfg_set(WM_IO_PA_09, WM_IO_OPTION1); + break; + + case WM_IO_PB_10: + tls_io_cfg_set(WM_IO_PB_10, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("i2s master ws afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2S); +} + +void wm_i2s_m_do_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_03: + tls_io_cfg_set(WM_IO_PA_03, WM_IO_OPTION4); + break; + + case WM_IO_PA_07: + tls_io_cfg_set(WM_IO_PA_07, WM_IO_OPTION1); + break; + + case WM_IO_PB_09: + tls_io_cfg_set(WM_IO_PB_09, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("i2s master do afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2S); +} + +void wm_i2s_s_ck_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_15: + tls_io_cfg_set(WM_IO_PA_15, WM_IO_OPTION2); + break; + + case WM_IO_PA_11: + tls_io_cfg_set(WM_IO_PA_11, WM_IO_OPTION1); + break; + + case WM_IO_PB_15: + tls_io_cfg_set(WM_IO_PB_15, WM_IO_OPTION4); + break; + + case WM_IO_PB_04: + tls_io_cfg_set(WM_IO_PB_04, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("i2s slave ck afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2S); +} + +void wm_i2s_s_ws_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_06: + tls_io_cfg_set(WM_IO_PA_06, WM_IO_OPTION2); + break; + + case WM_IO_PA_12: + tls_io_cfg_set(WM_IO_PA_12, WM_IO_OPTION1); + break; + + case WM_IO_PB_16: + tls_io_cfg_set(WM_IO_PB_16, WM_IO_OPTION4); + break; + + case WM_IO_PB_05: + tls_io_cfg_set(WM_IO_PB_05, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("i2s slave ws afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2S); +} + +void wm_i2s_s_di_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_14: + tls_io_cfg_set(WM_IO_PA_14, WM_IO_OPTION2); + break; + + case WM_IO_PA_10: + tls_io_cfg_set(WM_IO_PA_10, WM_IO_OPTION1); + break; + + case WM_IO_PB_14: + tls_io_cfg_set(WM_IO_PB_14, WM_IO_OPTION4); + break; + + case WM_IO_PB_03: + tls_io_cfg_set(WM_IO_PB_03, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("i2s slave di afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2S); +} + +void wm_i2s_mclk_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_02: + tls_io_cfg_set(WM_IO_PA_02, WM_IO_OPTION4); + break; + + case WM_IO_PB_28: + tls_io_cfg_set(WM_IO_PB_28, WM_IO_OPTION1); + break; + + case WM_IO_PB_31: + tls_io_cfg_set(WM_IO_PB_31, WM_IO_OPTION1); + break; + + default: + TLS_DBGPRT_ERR("i2s mclk afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2S); +} + +void wm_i2s_extclk_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_05: + tls_io_cfg_set(WM_IO_PA_05, WM_IO_OPTION4); + break; + + default: + TLS_DBGPRT_ERR("i2s extclk afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2S); +} + +void wm_i2c_scl_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_06: + tls_gpio_cfg(WM_IO_PA_06, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PA_06, WM_IO_OPTION1); + break; + + case WM_IO_PA_08: + tls_gpio_cfg(WM_IO_PA_08, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PA_08, WM_IO_OPTION4); + break; + + case WM_IO_PB_13: + tls_gpio_cfg(WM_IO_PB_13, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_13, WM_IO_OPTION3); + break; + + case WM_IO_PB_11: + tls_gpio_cfg(WM_IO_PB_11, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_11, WM_IO_OPTION4); + break; + + case WM_IO_PB_21: + tls_gpio_cfg(WM_IO_PB_21, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_21, WM_IO_OPTION3); + break; + + default: + TLS_DBGPRT_ERR("i2c scl afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2C); +} + +void wm_i2c_sda_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_15: + tls_gpio_cfg(WM_IO_PA_15, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PA_15, WM_IO_OPTION1); + break; + + case WM_IO_PA_07: + tls_gpio_cfg(WM_IO_PA_07, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PA_07, WM_IO_OPTION4); + break; + + case WM_IO_PB_14: + tls_gpio_cfg(WM_IO_PB_14, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_14, WM_IO_OPTION3); + break; + + case WM_IO_PB_12: + tls_gpio_cfg(WM_IO_PB_12, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_12, WM_IO_OPTION4); + break; + + case WM_IO_PB_22: + tls_gpio_cfg(WM_IO_PB_22, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_io_cfg_set(WM_IO_PB_22, WM_IO_OPTION3); + break; + + default: + TLS_DBGPRT_ERR("i2c sda afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_I2C); +} + +void wm_pwm1_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_00: + tls_io_cfg_set(WM_IO_PA_00, WM_IO_OPTION2); + break; + + case WM_IO_PB_18: + tls_io_cfg_set(WM_IO_PB_18, WM_IO_OPTION2); + break; + + case WM_IO_PB_05: + tls_io_cfg_set(WM_IO_PB_05, WM_IO_OPTION3); + break; + + case WM_IO_PA_05: + tls_io_cfg_set(WM_IO_PA_05, WM_IO_OPTION2); + break; + + case WM_IO_PB_19: + tls_io_cfg_set(WM_IO_PB_19, WM_IO_OPTION1); + break; + + case WM_IO_PB_30: + tls_io_cfg_set(WM_IO_PB_30, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("pwm1 afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_PWM); +} + + +void wm_pwm2_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_01: + tls_io_cfg_set(WM_IO_PA_01, WM_IO_OPTION2); + break; + + case WM_IO_PB_17: + tls_io_cfg_set(WM_IO_PB_17, WM_IO_OPTION2); + break; + + case WM_IO_PB_04: + tls_io_cfg_set(WM_IO_PB_04, WM_IO_OPTION3); + break; + + case WM_IO_PA_07: + tls_io_cfg_set(WM_IO_PA_07, WM_IO_OPTION2); + break; + + case WM_IO_PB_13: + tls_io_cfg_set(WM_IO_PB_13, WM_IO_OPTION1); + break; + + case WM_IO_PB_20: + tls_io_cfg_set(WM_IO_PB_20, WM_IO_OPTION1); + break; + + default: + TLS_DBGPRT_ERR("pwm2 afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_PWM); +} + +void wm_pwm3_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_02: + tls_io_cfg_set(WM_IO_PA_02, WM_IO_OPTION2); + break; + + case WM_IO_PB_16: + tls_io_cfg_set(WM_IO_PB_16, WM_IO_OPTION2); + break; + + case WM_IO_PB_03: + tls_io_cfg_set(WM_IO_PB_03, WM_IO_OPTION3); + break; + + case WM_IO_PA_08: + tls_io_cfg_set(WM_IO_PA_08, WM_IO_OPTION2); + break; + + case WM_IO_PB_21: + tls_io_cfg_set(WM_IO_PB_21, WM_IO_OPTION1); + break; + + default: + TLS_DBGPRT_ERR("pwm3 afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_PWM); +} + +void wm_pwm4_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_03: + tls_io_cfg_set(WM_IO_PA_03, WM_IO_OPTION2); + break; + + case WM_IO_PB_15: + tls_io_cfg_set(WM_IO_PB_15, WM_IO_OPTION2); + break; + + case WM_IO_PB_02: + tls_io_cfg_set(WM_IO_PB_02, WM_IO_OPTION3); + break; + + case WM_IO_PA_09: + tls_io_cfg_set(WM_IO_PA_09, WM_IO_OPTION2); + break; + + case WM_IO_PB_22: + tls_io_cfg_set(WM_IO_PB_22, WM_IO_OPTION1); + break; + + case WM_IO_PB_06: + tls_io_cfg_set(WM_IO_PB_06, WM_IO_OPTION3); + break; + + default: + TLS_DBGPRT_ERR("pwm4 afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_PWM); +} + +void wm_pwm5_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PA_04: + tls_io_cfg_set(WM_IO_PA_04, WM_IO_OPTION2); + break; + + case WM_IO_PB_14: + tls_io_cfg_set(WM_IO_PB_14, WM_IO_OPTION2); + break; + + case WM_IO_PB_01: + tls_io_cfg_set(WM_IO_PB_01, WM_IO_OPTION3); + break; + + case WM_IO_PA_10: + tls_io_cfg_set(WM_IO_PA_10, WM_IO_OPTION2); + break; + + case WM_IO_PB_23: + tls_io_cfg_set(WM_IO_PB_23, WM_IO_OPTION1); + break; + + case WM_IO_PB_08: + tls_io_cfg_set(WM_IO_PB_08, WM_IO_OPTION1); + break; + + default: + TLS_DBGPRT_ERR("pwm5 afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_PWM); +} + +void wm_pwmbrk_config(enum tls_io_name io_name) +{ + switch(io_name) + { + case WM_IO_PB_24: + tls_io_cfg_set(WM_IO_PB_24, WM_IO_OPTION1); + break; + + case WM_IO_PB_29: + tls_io_cfg_set(WM_IO_PB_29, WM_IO_OPTION2); + break; + + case WM_IO_PB_00: + tls_io_cfg_set(WM_IO_PB_00, WM_IO_OPTION3); + break; + + case WM_IO_PA_11: + tls_io_cfg_set(WM_IO_PA_11, WM_IO_OPTION2); + break; + + default: + TLS_DBGPRT_ERR("pwmbrk afsel config error!"); + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_PWM); +} + +void wm_swd_config(bool enable) +{ + if (enable) + { + tls_io_cfg_set(WM_IO_PB_06, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_07, WM_IO_OPTION1); + } + else + { + tls_io_cfg_set(WM_IO_PB_06, WM_IO_OPTION5); + tls_io_cfg_set(WM_IO_PB_07, WM_IO_OPTION5); + } +} + +void wm_adc_config(u8 Channel) +{ + switch(Channel) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + tls_io_cfg_set((enum tls_io_name)(WM_IO_PB_19+Channel), WM_IO_OPTION6); + break; + case 8: + case 9: + case 10: + case 11: + tls_io_cfg_set((enum tls_io_name)(WM_IO_PB_19+(Channel-8)*2), WM_IO_OPTION6); + tls_io_cfg_set((enum tls_io_name)(WM_IO_PB_19+(Channel-8)*2+1), WM_IO_OPTION6); + break; + + default: + break; + } + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_SDADC); +} + +void wm_gpio_af_disable(void) +{ + tls_reg_write32(HR_GPIOA_AFSEL, 0x0); + tls_reg_write32(HR_GPIOB_AFSEL, BIT(6) | BIT(7));//only enable swd +} + diff --git a/sdk/Platform/Drivers/hspi/wm_hspi.c b/sdk/Platform/Drivers/hspi/wm_hspi.c new file mode 100644 index 0000000..de78a2d --- /dev/null +++ b/sdk/Platform/Drivers/hspi/wm_hspi.c @@ -0,0 +1,493 @@ +/** + * @file wm_hspi.c + * + * @brief High speed spi slave Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#include +#include +#include +#include "wm_hspi.h" +#include "wm_regs.h" +#include "wm_config.h" +#include "wm_mem.h" +#include "wm_osal.h" +#include "wm_irq.h" +#include "lwip/mem.h" +#include "wm_io.h" + +#if TLS_CONFIG_HS_SPI + +struct tls_slave_hspi g_slave_hspi; +#define SET_BIT(x) (1UL << (x)) +void hspi_free_rxdesc(struct tls_hspi_rx_desc *rx_desc); + + +void hspi_rx_init(struct tls_slave_hspi *hspi) +{ + struct tls_hspi_rx_desc *hspi_rx_desc; + int i; + +/* set current availble rx desc pointer */ + hspi_rx_desc = (struct tls_hspi_rx_desc *) HSPI_RX_DESC_BASE_ADDR; + hspi->curr_rx_desc = hspi_rx_desc; + +/* initialize rx descriptor content */ + for (i = 0; i < HSPI_RX_DESC_NUM; i++) + { + /* initialize tx descriptors */ + if (i < HSPI_RXBUF_NUM) + { + hspi_rx_desc->valid_ctrl = SET_BIT(31); + hspi_rx_desc->buf_addr = HSPI_RXBUF_BASE_ADDR + i * HSPI_RXBUF_SIZE; + } + else + { + /* indicate this descriptor is can't use by hspi */ + hspi_rx_desc->valid_ctrl = 0; + /* point to null */ + hspi_rx_desc->buf_addr = 0x0; + } + + if (i == (HSPI_RX_DESC_NUM - 1)) + { + hspi_rx_desc->next_desc_addr = (u32) HSPI_RX_DESC_BASE_ADDR; + } + else + { + hspi_rx_desc->next_desc_addr = (u32) (hspi_rx_desc + 1); + } + hspi_rx_desc++; + } +} + +void hspi_tx_init(struct tls_slave_hspi *hspi) +{ + struct tls_hspi_tx_desc *hspi_tx_desc; + int i; + + hspi_tx_desc = (struct tls_hspi_tx_desc *) HSPI_TX_DESC_BASE_ADDR; + + hspi->curr_tx_desc = hspi_tx_desc; + + for (i = 0; i < HSPI_TX_DESC_NUM; i++) + { + hspi_tx_desc->valid_ctrl = 0; + hspi_tx_desc->buf_info = 0; +#if HSPI_TX_MEM_MALLOC + hspi_tx_desc->txbuf_addr = NULL; + hspi_tx_desc->buf_addr[0] = 0; +#else + hspi_tx_desc->buf_addr[0] = HSPI_TXBUF_BASE_ADDR + i * HSPI_TXBUF_SIZE; +#endif + hspi_tx_desc->buf_addr[1] = 0; + hspi_tx_desc->buf_addr[2] = 0; + if (i == (HSPI_TX_DESC_NUM - 1)) + { + hspi_tx_desc->next_desc_addr = (u32) HSPI_TX_DESC_BASE_ADDR; + } + else + { + hspi_tx_desc->next_desc_addr = (u32) (hspi_tx_desc + 1); + } + hspi_tx_desc++; + } + +} + +static int slave_spi_rx_data(struct tls_slave_hspi *hspi) +{ + struct tls_hspi_rx_desc *rx_desc; + +/* get rx descriptor */ + rx_desc = hspi->curr_rx_desc; + + while (!(rx_desc->valid_ctrl & SET_BIT(31))) + { + if (hspi->rx_data_callback) + hspi->rx_data_callback((char *) rx_desc->buf_addr); + hspi_free_rxdesc(rx_desc); + + rx_desc = (struct tls_hspi_rx_desc *) rx_desc->next_desc_addr; + hspi->curr_rx_desc = rx_desc; + } + + return 0; + +} + + +void SDIO_RX_IRQHandler(void) +{ + struct tls_slave_hspi *hspi = (struct tls_slave_hspi *) &g_slave_hspi; + +#if HSPI_TX_MEM_MALLOC + hspi->txdoneflag = 1; +#endif + if (hspi->tx_data_callback) + hspi->tx_data_callback((char *) hspi->curr_tx_desc->buf_addr); +/* clear interrupt */ + tls_reg_write32(HR_SDIO_INT_SRC, SDIO_WP_INT_SRC_DATA_UP); +} + +void SDIO_TX_IRQHandler(void) +{ + struct tls_slave_hspi *hspi = (struct tls_slave_hspi *) &g_slave_hspi; + +//Óû§Ä£Ê½Ï£¬Ö±½Ó¸ø³öÊý¾Ý£¬Á´±íµÄ²Ù×÷²»¶ÔÍ⿪·Å£¬±ÜÃâÔì³ÉÁ´±í²Ù×÷´íÎó + if (hspi->ifusermode) + { + slave_spi_rx_data(hspi); + } + else + { + if (hspi->rx_data_callback) + hspi->rx_data_callback((char *) hspi->curr_rx_desc->buf_addr); + } + +/* clear interrupt */ + tls_reg_write32(HR_SDIO_INT_SRC, SDIO_WP_INT_SRC_DATA_DOWN); +} + + +void SDIO_TX_CMD_IRQHandler(void) +{ + struct tls_slave_hspi *hspi = (struct tls_slave_hspi *) &g_slave_hspi; + + if (hspi->rx_cmd_callback) + hspi->rx_cmd_callback((char *) SDIO_CMD_RXBUF_ADDR); + + if (hspi->ifusermode) // Óû§Ä£Ê½Ï£¬Êý¾Ý¸ø³öÈ¥Ö®ºó£¬¼Ä´æÆ÷ÓÉÇý¶¯×Ô¼º²Ù×÷ + { + tls_reg_write32(HR_SDIO_DOWNCMDVALID, 0x1); + } + +/* clear interrupt */ + tls_reg_write32(HR_SDIO_INT_SRC, SDIO_WP_INT_SRC_CMD_DOWN); +} + + +void SPI_HS_IRQHandler(void) +{ + printf("spi HS irqhandle\n"); +} + +void hspi_free_rxdesc(struct tls_hspi_rx_desc *rx_desc) +{ + rx_desc->valid_ctrl = SET_BIT(31); +/* ÉèÖÃhspi/sdio tx enable¼Ä´æÆ÷£¬ÈÃsdioÓ²¼þÖªµÀÓпÉÓõÄtx descriptor */ + tls_reg_write32(HR_SDIO_TXEN, SET_BIT(0)); +} + + +void hspi_regs_cfg(void) +{ + tls_reg_write32(HR_HSPI_CLEAR_FIFO, 0x1); /* Clear data up&down interrput + */ + tls_reg_write32(HR_HSPI_SPI_CFG, 0); /* CPOL=0, CPHA=0, Small-Endian */ + tls_reg_write32(HR_HSPI_MODE_CFG, 0x0); + tls_reg_write32(HR_HSPI_INT_MASK, 0x03); + tls_reg_write32(HR_HSPI_INT_STTS, 0x03); +} + +void sdio_init_cis(void) +{ + tls_reg_write32(FN0_TPL_FUNCID, 0x000C0221); + tls_reg_write32(FN0_TPL_FUNCE, 0x00000422); + tls_reg_write32(FN0_TPL_FUNCE_MAXBLK, 0x04203208); + tls_reg_write32(FN0_TPL_MANFID_MID, 0x53470296); + tls_reg_write32(FN0_TPL_END, 0x000000ff); + + tls_reg_write32(FN1_TPL_FUNCID, 0x000C0221); + tls_reg_write32(FN1_TPL_FUNCE, 0x01012a22); + tls_reg_write32(FN1_TPL_FUNCE_VER, 0x00000011); + tls_reg_write32(FN1_TPL_FUNCE_NSN, 0x02000000); + tls_reg_write32(FN1_TPL_FUNCE_CSASIZE, 0x08000300); + tls_reg_write32(FN1_TPL_FUNCE_OCR, 0x00FF8000); + tls_reg_write32(FN1_TPL_FUNCE_MINPWR, 0x010f0a08); + tls_reg_write32(FN1_TPL_FUNCE_STANDBY, 0x00000101); + tls_reg_write32(FN1_TPL_FUNCE_OPTBW, 0x00000000); + tls_reg_write32(FN1_TPL_FUNCE_NTIMEOUT, 0x00000000); + tls_reg_write32(FN1_TPL_FUNCE_AVGPWR, 0x00000000); + tls_reg_write32(FN1_TPL_FUNCE_AVGPWR + 4, 0x00000000); + tls_reg_write32(FN1_TPL_END, 0x000000ff); +} + +void hsdio_regs_cfg(void) +{ + u32 v; + + sdio_init_cis(); + tls_reg_write32(HR_SDIO_CIS0, SDIO_CIS0_ADDR - 0x1000); + tls_reg_write32(HR_SDIO_CIS1, SDIO_CIS1_ADDR - 0x2000); + + v = tls_reg_read32(HR_SDIO_CIA); + tls_reg_write32(HR_SDIO_CIA, (v & 0xFFFFF000) | 0x232); + +/* set sdio ready */ + tls_reg_write32(HR_SDIO_PROG, 0x02FD); +} + + +/** + * @brief This function is used to initial HSPI register. + * + * @param[in] None + * + * @retval 0 success + * @retval other failed + * + * @note When the system is initialized, the function has been called, so users can not call the function. + */ +int tls_slave_spi_init(void) +{ + struct tls_slave_hspi *hspi; + + hspi = &g_slave_hspi; + memset(hspi, 0, sizeof(struct tls_slave_hspi)); + + hspi_rx_init(hspi); + hspi_tx_init(hspi); +// tls_set_high_speed_interface_type(HSPI_INTERFACE_SPI); +/* regiseter hspi tx rx cmd interrupt handler */ + +/* setting hw interrupt module isr enable regiset */ + tls_irq_enable(SDIO_TX_DATA_INT); + tls_irq_enable(SDIO_RX_DATA_INT); + tls_irq_enable(SDIO_TX_CMD_INT); + tls_irq_enable(SPI1_INT); + + /******************************************** + * setting hspi wrapper registers + *********************************************/ +/* hspi data down(rx) */ + tls_reg_write32(HR_SDIO_TXBD_ADDR, HSPI_RX_DESC_BASE_ADDR); + tls_reg_write32(HR_SDIO_TXBD_LINKEN, 1); + tls_reg_write32(HR_SDIO_TXEN, 1); +/* hspi data up (tx) */ + tls_reg_write32(HR_SDIO_RXBD_ADDR, HSPI_TX_DESC_BASE_ADDR); + tls_reg_write32(HR_SDIO_RXBD_LINKEN, 1); + +/* hspi cmd down */ + tls_reg_write32(HR_SDIO_CMD_ADDR, SDIO_CMD_RXBUF_ADDR); + tls_reg_write32(HR_SDIO_CMD_SIZE, SDIO_CMD_RXBUF_SIZE); + tls_reg_write32(HR_SDIO_DOWNCMDVALID, 0x1); + +/* enable sdio module register */ + tls_reg_write32(HR_SDIO_INT_MASK, 0x00); + + return 0; +} + + +/** + * @brief This function is used to set high speed interface type. + * + * @param[in] type is the interface type. HSPI_INTERFACE_SPI or HSPI_INTERFACE_SDIO + * + * @return None + * + * @note None + */ +void tls_set_high_speed_interface_type(int type) +{ + + + if (HSPI_INTERFACE_SPI == type) + { +#if 0 +#if (HSPI_IO_REUSE_NUM == 0) + tls_io_cfg_set(WM_IO_PB_14, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_15, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_16, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_17, WM_IO_OPTION1); + tls_io_cfg_set(WM_IO_PB_18, WM_IO_OPTION1); +#else + if(WM_IO_OPTION1 == tls_io_cfg_get(WM_IO_PB_14)) + tls_io_cfg_set(WM_IO_PB_14, WM_IO_OPTION5); + if(WM_IO_OPTION1 == tls_io_cfg_get(WM_IO_PB_15)) + tls_io_cfg_set(WM_IO_PB_15, WM_IO_OPTION5); + if(WM_IO_OPTION1 == tls_io_cfg_get(WM_IO_PB_16)) + tls_io_cfg_set(WM_IO_PB_16, WM_IO_OPTION5); + if(WM_IO_OPTION1 == tls_io_cfg_get(WM_IO_PB_17)) + tls_io_cfg_set(WM_IO_PB_17, WM_IO_OPTION5); + if(WM_IO_OPTION1 == tls_io_cfg_get(WM_IO_PB_18)) + tls_io_cfg_set(WM_IO_PB_18, WM_IO_OPTION5); + tls_io_cfg_set(WM_IO_PB_08, WM_IO_OPTION2); + tls_io_cfg_set(WM_IO_PB_09, WM_IO_OPTION2); + tls_io_cfg_set(WM_IO_PB_10, WM_IO_OPTION2); + tls_io_cfg_set(WM_IO_PB_11, WM_IO_OPTION2); + tls_io_cfg_set(WM_IO_PB_12, WM_IO_OPTION2); +#endif +#endif + hspi_regs_cfg(); + } + else if (HSPI_INTERFACE_SDIO == type) + { +#if 0 + if(WM_IO_OPTION1 == tls_io_cfg_get(WM_IO_PB_16)) //HSPI_CK,as sdio need close + tls_io_cfg_set(WM_IO_PB_16, WM_IO_OPTION5); + if(WM_IO_OPTION2 == tls_io_cfg_get(WM_IO_PB_08)) //HSPI_CK,as sdio need close + tls_io_cfg_set(WM_IO_PB_08, WM_IO_OPTION5); + tls_io_cfg_set(WM_IO_PB_08, WM_IO_OPTION3); + tls_io_cfg_set(WM_IO_PB_09, WM_IO_OPTION3); + tls_io_cfg_set(WM_IO_PB_10, WM_IO_OPTION3); + tls_io_cfg_set(WM_IO_PB_11, WM_IO_OPTION3); + tls_io_cfg_set(WM_IO_PB_12, WM_IO_OPTION3); +#if (SDIO_IO_REUSE_NUM == 0) + tls_io_cfg_set(WM_IO_PA_06, WM_IO_OPTION3); +#elif (SDIO_IO_REUSE_NUM == 1) + tls_io_cfg_set(WM_IO_PB_07, WM_IO_OPTION3); +#else + tls_io_cfg_set(WM_IO_PB_13, WM_IO_OPTION4); +#endif +#endif + hsdio_regs_cfg(); + } +} + +/** + * @brief This function is used to enable or disable user mode. + * + * @param[in] ifenable TRUE or FALSE + * + * @return None + * + * @note If the user enables the user mode, RICM instruction in the system will not be used by SPI. + * If the user wants to use the SPI interface as other use, need to enable the user mode. + * This function must be called before the register function. + */ +void tls_set_hspi_user_mode(u8 ifenable) +{ + struct tls_slave_hspi *hspi = &g_slave_hspi; + + hspi->ifusermode = ifenable; + + if (ifenable) + { + hspi->rx_cmd_callback = NULL; + hspi->rx_data_callback = NULL; + hspi->tx_data_callback = NULL; + } +} + +/** + * @brief This function is used to register hspi rx command interrupt. + * + * @param[in] rx_cmd_callback is the hspi rx interrupt call back function. + * + * @return None + * + * @note None + */ + void tls_hspi_rx_cmd_callback_register(s16(*rx_cmd_callback) (char *buf)) +{ + g_slave_hspi.rx_cmd_callback = rx_cmd_callback; +} + +/** + * @brief This function is used to register hspi rx data interrupt. + * + * @param[in] rx_data_callback is the hspi rx interrupt call back function. + * + * @return None + * + * @note None + */ +void tls_hspi_rx_data_callback_register(s16(*rx_data_callback) (char *buf)) +{ + g_slave_hspi.rx_data_callback = rx_data_callback; +} + +/** + * @brief This function is used to register hspi tx data interrupt. + * + * @param[in] tx_data_callback is the hspi tx interrupt call back function. + * + * @return None + * + * @note None + */ +void tls_hspi_tx_data_callback_register(s16(*tx_data_callback) (char *buf)) +{ + g_slave_hspi.tx_data_callback = tx_data_callback; +} + +/** + * @brief This function is used to transfer data. + * + * @param[in] txbuf is a buf for saving user data. + * @param[in] len is the data length. + * + * @retval transfer data len success + * @retval 0 failed + * + * @note None + */ +int tls_hspi_tx_data(char *txbuf, int len) +{ + struct tls_hspi_tx_desc *tx_desc; + int totallen = len; + int txlen; + + if (NULL == txbuf || len <= 0 || len > (HSPI_TXBUF_SIZE * HSPI_TX_DESC_NUM)) + { + printf("\nhspi tx param error\n"); + return 0; + } + tx_desc = g_slave_hspi.curr_tx_desc; + while (1) + { + // printf("---\n"); + if ((tx_desc->valid_ctrl & SET_BIT(31)) == 0) + break; + tls_os_time_delay(1); + } + while (!(tx_desc->valid_ctrl & SET_BIT(31))) + { + txlen = (totallen > HSPI_TXBUF_SIZE) ? HSPI_TXBUF_SIZE : totallen; +#if HSPI_TX_MEM_MALLOC + if (tx_desc->txbuf_addr != NULL) + { + printf("\nhspi txbuf not null,error %x\n", tx_desc->txbuf_addr); + if (tx_desc->txbuf_addr == tx_desc->buf_addr[0]) + { + mem_free((void *) tx_desc->txbuf_addr); + tx_desc->txbuf_addr = NULL; + } + else // ²»Ó¦¸Ã³öÏÖ + { + printf("\nhspi tx mem error\n"); + break; + } + } + + tx_desc->txbuf_addr = (u32) mem_malloc(txlen + 1); + if (NULL == tx_desc->txbuf_addr) + { + printf("\nhspi tx data malloc error\n"); + break; + } + tx_desc->buf_addr[0] = tx_desc->txbuf_addr; +#endif + MEMCPY((char *) tx_desc->buf_addr[0], txbuf, txlen); + tx_desc->buf_info = txlen << 12; + tx_desc->valid_ctrl = SET_BIT(31); + tls_reg_write32(HR_SDIO_RXEN, 0x01); + tx_desc = (struct tls_hspi_tx_desc *) tx_desc->next_desc_addr; + g_slave_hspi.curr_tx_desc = tx_desc; + totallen -= txlen; + if (totallen <= 0) + break; + } + + return (len - totallen); +} + +#endif + + diff --git a/sdk/Platform/Drivers/i2c/wm_i2c.c b/sdk/Platform/Drivers/i2c/wm_i2c.c new file mode 100644 index 0000000..8c0443f --- /dev/null +++ b/sdk/Platform/Drivers/i2c/wm_i2c.c @@ -0,0 +1,319 @@ +/**************************************************************************//** + * @file wm_i2c.c + * @author + * @version + * @date + * @brief + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. All rights reserved. + *****************************************************************************/ + +#include +#include +#include +#include "wm_regs.h" +#include "wm_i2c.h" +#include "wm_cpu.h" +#include "wm_irq.h" + +#define I2C_FREQ_MAX (400000) +#define I2C_FREQ_MIN (100000) +#define I2C_WRITE (0x80) +#define I2C_READ (0x00) +typedef struct { + uint8_t addr; + uint8_t dev_addr; + uint8_t state; + uint8_t *buf; + uint16_t len; + uint16_t cnt; + uint8_t cmd; + void (*transfer_done)(void); +} i2c_desc; +enum { + START, + RESTART, + TRANSMIT, + PRERECEIVE, + RECEIVE, + STOP, + DONE, + IDLE, +}; +static i2c_desc i2c_transfer; + +void tls_i2c_init(u32 freq) +{ + u32 div = 0; + tls_sys_clk clk; + + if (freq < I2C_FREQ_MIN) + { + freq = I2C_FREQ_MIN; + } + else if (freq > I2C_FREQ_MAX) + { + freq = I2C_FREQ_MAX; + } + tls_sys_clk_get(&clk); + + div = (clk.apbclk * 1000000)/(5 * freq) - 1; + tls_reg_write32(HR_I2C_PRER_LO, div & 0xff); + tls_reg_write32(HR_I2C_PRER_HI, (div>>8) & 0xff); + + /** enable I2C | Disable Int*/ + tls_reg_write32(HR_I2C_CTRL, I2C_CTRL_INT_DISABLE | I2C_CTRL_ENABLE); + tls_irq_enable(I2C_IRQn); +} + +/** + * @brief send stop signal + * + */ +void tls_i2c_stop(void) +{ + tls_reg_write32(HR_I2C_CR_SR, I2C_CR_STO); + while(tls_reg_read32(HR_I2C_CR_SR) & I2C_SR_TIP); +} + +/** + * @brief waiting for ack signal + * @retval + * - \ref WM_FAILED + * - \ref WM_SUCCESS + */ +int tls_i2c_wait_ack(void) +{ + u16 errtime=0; + u32 value; + + while(tls_reg_read32(HR_I2C_CR_SR) & I2C_SR_TIP); + value = tls_reg_read32(HR_I2C_CR_SR); + while(value & I2C_SR_NAK) + { + errtime ++; + if(errtime > 512) + { + printf("wait ack err\n"); + tls_i2c_stop(); + return WM_FAILED; + } + value = tls_reg_read32(HR_I2C_CR_SR); + } + + return WM_SUCCESS; +} + + +/** + * @brief writes the data to data register of I2C module + * when \ifstart one the start signal will be sent followed by the \data + * when \ifstart zero only the \data will be send + * @param[in] data the data will be write to the data register of I2C module + * @param[in] ifstart when one send start signal, when zero don't + * @retval + * + */ +void tls_i2c_write_byte(u8 data,u8 ifstart) +{ + tls_reg_write32(HR_I2C_TX_RX, data); + if(ifstart) + tls_reg_write32(HR_I2C_CR_SR, I2C_CR_STA | I2C_CR_WR); + else + tls_reg_write32(HR_I2C_CR_SR, I2C_CR_WR); + while(tls_reg_read32(HR_I2C_CR_SR) & I2C_SR_TIP); +} + + +/** + * @brief get the data stored in data register of I2C module + * @param[in] ifack when one send ack after reading the data register,when zero don't + * @param[in] ifstop when one send stop signal after read, when zero do not send stop + * @retval the received data + */ +u8 tls_i2c_read_byte(u8 ifack,u8 ifstop) +{ + u8 data; + u32 value = I2C_CR_RD; + + if(!ifack) + value |= I2C_CR_NAK; + if(ifstop) + value |= I2C_CR_STO; + + tls_reg_write32(HR_I2C_CR_SR, value); + /** Waiting finish */ + while(tls_reg_read32(HR_I2C_CR_SR) & I2C_SR_TIP); + data = tls_reg_read32(HR_I2C_TX_RX); + + return data; +} + +/** + * @brief start write through int mode + * @param[in] devaddr the device address + * @param[in] wordaddr when one send stop signal after read, when zero do not send stop + * @param[in] buf the address point where data shoule be stored + * @param[in] len the length of data will be received + * @retval + * - \ref WM_FAILED + * - \ref WM_SUCCESS + */ +int wm_i2c_start_write_it(uint8_t devaddr, uint8_t wordaddr, uint8_t * buf, uint16_t len) +{ + if (buf == NULL) + { + return WM_FAILED; + } + I2C->TX_RX = devaddr; + i2c_transfer.dev_addr = devaddr; + i2c_transfer.state = START; + i2c_transfer.cmd = I2C_WRITE; + i2c_transfer.buf = buf; + i2c_transfer.len = len; + i2c_transfer.cnt = 0; + i2c_transfer.addr = wordaddr; + I2C->CR_SR = I2C_CR_STA | I2C_CR_WR; + return WM_SUCCESS; +} + +/** + * @brief start read through int mode + * @param[in] devaddr the device address + * @param[in] wordaddr when one send stop signal after read, when zero do not send stop + * @param[in] buf the address point where data shoule be stored + * @param[in] len the length of data will be received + * @retval + * - \ref WM_FAILED + * - \ref WM_SUCCESS + */ +int wm_i2c_start_read_it(uint8_t devaddr, uint8_t wordaddr, uint8_t * buf, uint16_t len) +{ + if (buf == NULL) + { + return WM_FAILED; + } + I2C->TX_RX = devaddr; + i2c_transfer.dev_addr = devaddr; + i2c_transfer.state = START; + i2c_transfer.cmd = I2C_READ; + i2c_transfer.buf = buf; + i2c_transfer.len = len; + i2c_transfer.cnt = 0; + i2c_transfer.addr = wordaddr; + I2C->CR_SR = I2C_CR_STA | I2C_CR_WR; + + return WM_SUCCESS; +} + +/** + * @brief This function is used to register i2c transfer done callback function. + * @param[in] done is the i2c transfer done callback function. + * @retval None + * @note None + */ +void wm_i2c_transfer_done_register(void (*done)(void)) +{ + i2c_transfer.transfer_done = done; +} + + +void I2C_IRQHandler(void) +{ + int i2c_sr; + i2c_sr = I2C->CR_SR; + I2C->CR_SR = 1; + if (i2c_sr & 0x20) + { + printf("I2C AL lost\r\n"); + } + if (i2c_sr & 0x01) + { + if ((i2c_sr & 0x80) == 0) + { + switch(i2c_transfer.state) + { + case START: + I2C->TX_RX = i2c_transfer.addr; + I2C->CR_SR = I2C_CR_WR; + if ((i2c_transfer.cmd & I2C_WRITE) == I2C_WRITE) + { + i2c_transfer.state = TRANSMIT; + } + else + { + i2c_transfer.state = RESTART; + } + break; + + case RESTART: + I2C->TX_RX = (i2c_transfer.dev_addr | 0x01); + I2C->CR_SR = (I2C_CR_STA | I2C_CR_WR); + i2c_transfer.state = PRERECEIVE; + break; + + case TRANSMIT: + I2C->TX_RX = i2c_transfer.buf[i2c_transfer.cnt++]; + I2C->CR_SR = I2C_CR_WR; + if (i2c_transfer.cnt == i2c_transfer.len) + { + i2c_transfer.state = STOP; + } + break; + + case PRERECEIVE: + i2c_transfer.state = RECEIVE; + I2C->CR_SR = I2C_CR_RD; + break; + case RECEIVE: + i2c_transfer.buf[i2c_transfer.cnt++] = I2C->TX_RX; + if (i2c_transfer.cnt == (i2c_transfer.len - 1)) + { + I2C->CR_SR = (I2C_CR_STO | I2C_CR_NAK | I2C_CR_RD); + i2c_transfer.state = STOP; + } + else if (i2c_transfer.len == 1) + { + I2C->CR_SR = (I2C_CR_STO | I2C_CR_NAK | I2C_CR_RD); + i2c_transfer.state = DONE; + if (i2c_transfer.transfer_done) + { + i2c_transfer.transfer_done(); + } + } + else + { + I2C->CR_SR = I2C_CR_RD; + } + break; + + case STOP: + I2C->CR_SR = I2C_CR_STO; + i2c_transfer.state = DONE; + if (i2c_transfer.transfer_done) + { + i2c_transfer.transfer_done(); + } + break; + } + } + else + { + if ((i2c_transfer.state == STOP) && i2c_transfer.cmd != I2C_WRITE) + { + i2c_transfer.buf[i2c_transfer.cnt] = I2C->TX_RX; + i2c_transfer.state = DONE; + if (i2c_transfer.transfer_done) + { + i2c_transfer.transfer_done(); + } + } + } + } +// if ((i2c_sr & 0x40) == 0) +// { +// i2c_transfer.state = IDLE; +// } +} + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ diff --git a/sdk/Platform/Drivers/i2s/wm_i2s.c b/sdk/Platform/Drivers/i2s/wm_i2s.c new file mode 100644 index 0000000..db089b0 --- /dev/null +++ b/sdk/Platform/Drivers/i2s/wm_i2s.c @@ -0,0 +1,584 @@ +/**************************************************************************//** + * @file wm_i2s.c + * @author + * @version + * @date + * @brief + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. All rights reserved. + *****************************************************************************/ + +#define TLS_CONFIG_I2S 1 + +#if TLS_CONFIG_I2S + +#include "wm_i2s.h" +#include "string.h" +#include "wm_debug.h" +#include "wm_irq.h" +#include "wm_config.h" +#include "wm_mem.h" + +#define I2S_CLK (160000000) + +#define WM_I2S_FIFO_LEN (8) +static tls_i2s_port_t tls_i2s_port; + +static void tls_i2s_fill_txfifo(void); + +/** + * @brief This function is used to initial i2s port. + * @param[in] opts the i2s setting options,if this param is NULL,this function will use the default options. + * @retval + * - \ref WM_SUCCESS + * - \ref WM_FAILED + * + */ +int tls_i2s_port_init(tls_i2s_options_t *opts) +{ + tls_i2s_options_t opt; + + tls_i2s_port.regs = I2S; + + if(NULL == opts) + { + memset(&opt, 0, sizeof(tls_i2s_options_t)); + opt.data_width = I2S_CTRL_DATABIT_16; + opt.format = I2S_CTRL_FORMAT_I2S; + opt.stereo_mono = I2S_CTRL_STERO; + opt.sample_rate = 8000; + opt.tx_en = 1; + tls_i2s_config(&opt); + } + else + { + tls_i2s_config(opts); + } + /* enable i2s interrupt */ + NVIC_ClearPendingIRQ(I2S_IRQn); + tls_irq_enable(I2S_INT); + + return WM_SUCCESS; +} + +/** + * @brief This function is used to initial i2s port. + * @param[in] opts the i2s setting options,if this param is NULL,this function will use the default options. + * + * @retval + * + * @note + */ +void tls_i2s_config(tls_i2s_options_t *opts) +{ + tls_i2s_port.regs->CTRL &= ~(I2S_CTRL_FORMAT_MASK | \ + I2S_CTRL_DATABIT_MASK | \ + I2S_CTRL_STEREO_MONO_MASK); + tls_i2s_port.regs->CTRL = opts->data_width | opts->format | opts->stereo_mono; + if (opts->rx_en) + { + TLS_I2S_RX_ENABLE(); + TLS_I2S_RX_FIFO_CLEAR(); + } + if (opts->tx_en) + { + TLS_I2S_TX_ENABLE(); + TLS_I2S_TX_FIFO_CLEAR(); + } + tls_i2s_set_freq(opts->sample_rate); +} + +/** + * @brief + * set the frequency of the i2s port. + * + * @param[in] freq + * the required frequency of the i2s module + * + * @retval + * + */ +void tls_i2s_set_freq(uint32_t freq) +{ + uint32_t div; + uint32_t reg; + uint8_t width, stereo; + + reg = I2S->CTRL; + width = (((reg>>4)&0x03)+1)<<3; + stereo = tls_bitband_read(HR_I2S_CTRL, I2S_CTRL_STEREO_MONO_Pos) ? 1:2; + div = (I2S_CLK + freq * width * stereo)/(freq * width * stereo) - 1; + + *(volatile uint32_t *)HR_CLK_I2S_CTL &= ~0xFF00; + *(volatile uint32_t *)HR_CLK_I2S_CTL |= (uint32_t)div<<8; +} + +/** + * @brief + * set the frequency of the i2s port. + * + * @param[in] freq + * the required frequency of the i2s module + * @param[in] exclk + * the frequency of the ext clock + * + * @retval + * + * @note + * this function will be called after tls_i2s_port_init function + */ +void wm_i2s_set_freq_exclk(uint32_t freq, uint32_t exclk) +{ + uint32_t div; + uint32_t temp; + uint8_t wdwidth, stereo; + temp = I2S->CTRL; + wdwidth = (((temp>>4)&0x03)+1)<<3; + stereo = tls_bitband_read(HR_I2S_CTRL, 22) ? 1:2; + div = (exclk * 2 + freq * wdwidth * stereo)/(2* freq * wdwidth * stereo) - 1; + *(volatile uint32_t *)0x40000718 &= ~0x3FF00; + *(volatile uint32_t *)0x40000718 |= (uint32_t)div<<8; + *(volatile uint32_t *)0x40000718 |= 0x01; +} +void I2S_IRQHandler(void) +{ + uint8_t rx_fifocnt; + + /** LZC */ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_LZC_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_LZC_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_LZC); + } + /** RZC */ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RZC_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RZC_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RZC); + } + /** Tx Done*/ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_TXDONE_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_TXDONE_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_TXDONE); + } + /** TxTH*/ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_TXTH_Pos)&& !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_TXTH_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_TXTH); + tls_i2s_fill_txfifo(); + } + /** TXOV*/ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_TXOV_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_TXOV_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_TXOV); + } + /** TXUD*/ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_TXUD_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_TXUD_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_TXUD); + } + /** Rx Done*/ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RXDONE_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RXDONE_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RXDONE); + } + /** RxTH */ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RXTH_Pos)&& !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RXTH_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RXTH); + rx_fifocnt = tls_i2s_port.regs->INT_STATUS & I2S_RX_FIFO_CNT_MASK; + if (tls_i2s_port.rx_buf.buf != NULL) + { + while(rx_fifocnt-- > 0) + { + tls_i2s_port.rx_buf.buf[tls_i2s_port.rx_buf.index] = tls_i2s_port.regs->RX; + tls_i2s_port.rx_buf.index++; + } + if (tls_i2s_port.rx_buf.index >= (tls_i2s_port.rx_buf.len)) + { + tls_i2s_port.rx_buf.buf = NULL; + tls_i2s_int_config(I2S_INT_MASK_RXTH, 0); + if (tls_i2s_port.rx_callback != NULL) + { + tls_i2s_port.rx_callback(tls_i2s_port.rx_buf.len); + } + tls_i2s_port.rx_buf.index = 0; + } + } + + } + /** RXOV*/ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RXOV_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RXOV_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RXOV); + } + /** RXUD*/ + if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RXUD_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RXUD_Pos)) + { + tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RXUD); + } +} + +/** + * @brief + * This function is used to register i2s rx interrupt. + * + * @param[in] rx_callback + * is the i2s interrupt call back function. + * + * @retval + * + */ + +void tls_i2s_rx_register( void (*rx_callback)(u16 len)) +{ + tls_i2s_port.rx_callback = rx_callback; +} + +/** + * @brief + * This function is used to register i2s tx interrupt. + * + * @param[in] tx_callback + * is the i2s interrupt call back function. + * + * @retval + * + */ +void tls_i2s_tx_register(void (* tx_callback)(u16 len)) +{ + tls_i2s_port.tx_callback = tx_callback; +} + +static void tls_i2s_fill_txfifo() +{ + s8 fifo_left = 0; + + if (tls_i2s_port.tx_buf.index < tls_i2s_port.tx_buf.len) + { + fifo_left = I2S_MAX_RXTXFIFO_LEVEL - ((tls_i2s_port.regs->INT_STATUS & I2S_TX_FIFO_CNT_MASK)>>4); + while (fifo_left--) + { + tls_i2s_port.regs->TX = *(uint32_t *)(tls_i2s_port.tx_buf.buf + tls_i2s_port.tx_buf.index); + tls_i2s_port.tx_buf.index ++; + } + if(tls_i2s_port.tx_buf.index >= tls_i2s_port.tx_buf.len) + { + while((tls_i2s_port.regs->INT_STATUS & I2S_TX_FIFO_CNT_MASK)>>4); + if(tls_i2s_port.tx_sem) + { + memset(&tls_i2s_port.tx_buf, 0, sizeof(tls_i2s_buf)); + tls_os_sem_release(tls_i2s_port.tx_sem); + return; + } + if (tls_i2s_port.tx_callback) + { + tls_i2s_port.tx_callback(tls_i2s_port.tx_buf.len); + } + } + } +} + +/** + * @brief + * This function is used to transfer data in blocking mode. + * + * @param[in] buf + * pointer to the transfering data. + * + * @param[in] len + * is the data length. + * + * @retval + * + */ +int tls_i2s_tx_block(uint32_t *buf, uint16_t len) +{ + uint8_t err; + + + err = tls_os_sem_create(&tls_i2s_port.tx_sem, 0); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("\ni2s tx sem create fail\n"); + return WM_FAILED; + } + TLS_I2S_TX_FIFO_CLEAR(); + tls_reg_write32(HR_I2S_INT_SRC, 0x1FF); + NVIC_ClearPendingIRQ(I2S_IRQn); + + tls_i2s_port.tx_buf.buf = buf; + tls_i2s_port.tx_buf.len = len; + tls_i2s_port.tx_buf.index = 0; + + tls_i2s_set_txth(5); + tls_i2s_int_config(I2S_INT_MASK_TXTH, 1); + TLS_I2S_TX_ENABLE(); + TLS_I2S_ENABLE(); + + tls_os_sem_acquire(tls_i2s_port.tx_sem, 0); + tls_os_sem_delete(tls_i2s_port.tx_sem); + tls_i2s_port.tx_sem = NULL; + TLS_I2S_TX_DISABLE(); + + return WM_SUCCESS; +} + +/** + * @brief + * This function is used to transfer data in non blocking mode. + * + * @param[in] buf + * is a buf for user data. + * + * @param[in] len + * is the data length. + * + * @param[in] tx_callback + * a function pointer,which will be called when the designated data sent + * + * @retval + * + * @note the \ref len in words + */ +int tls_i2s_tx_nonblock(uint32_t *buf, uint16_t len, void (*tx_callback)(u16 len)) +{ + tls_i2s_port.tx_buf.buf = (uint32_t *)buf; + tls_i2s_port.tx_buf.len = len; + tls_i2s_port.tx_buf.index = 0; + + tls_i2s_set_txth(5); + tls_i2s_int_config(I2S_INT_MASK_TXTH, 1); + tls_i2s_tx_register( tx_callback); + + TLS_I2S_TX_FIFO_CLEAR(); + TLS_I2S_TX_ENABLE(); + TLS_I2S_ENABLE(); + return WM_SUCCESS; +} + +/** + * @brief + * This function is used to receiver data in non blocking mode. + * + * @param[in] buf + * the buffer for storing the received data. + * + * @param[in] len + * the length of the data will be receiving + * + * @param[in] rx_callback + * a function pointer,which will be called when the demanded data have been received + * + * @retval + * + */ +int tls_i2s_rx_nonblock(uint32_t *buf, uint16_t len, void (*rx_callback)(u16 len)) +{ + tls_i2s_port.rx_buf.buf = buf; + tls_i2s_port.rx_buf.len = len; + tls_i2s_port.rx_buf.index = 0; + + tls_i2s_set_rxth(5); + tls_i2s_int_config(I2S_INT_MASK_RXTH, 1); + tls_i2s_rx_register( rx_callback); + + TLS_I2S_RX_FIFO_CLEAR(); + NVIC_ClearPendingIRQ(I2S_IRQn); + TLS_I2S_RX_ENABLE(); + TLS_I2S_ENABLE(); + + return WM_SUCCESS; +} + +/** + * @brief + * This function is used to the tx DMA configuration of the I2S module. + * + * @param[in] dma_channel + * the free dma channel number which returned by calling function tls_dma_request() + * + * @param[in] addr + * point to the data buffer will be sent + * + * @param[in] len + * length of the data to be transfered in bytes + * + * @retval + */ +void tls_i2s_tx_dma_config(uint8_t dma_channel, uint32_t * addr, uint16_t len) +{ + + struct tls_dma_descriptor DmaDesc; + + TLS_I2S_TX_DISABLE(); + TLS_I2S_TX_FIFO_CLEAR(); + /** Mask i2s txth interrupt*/ + tls_i2s_set_txth(4); + tls_i2s_int_config(I2S_INT_MASK_TXTH, 0); + DmaDesc.src_addr = (unsigned int) addr; + DmaDesc.dest_addr = (unsigned int)HR_I2S_TX; + + DmaDesc.dma_ctrl = TLS_DMA_DESC_CTRL_SRC_ADD_INC | TLS_DMA_DESC_CTRL_DATA_SIZE_WORD | + (len << 7); + DmaDesc.valid = TLS_DMA_DESC_VALID; + DmaDesc.next = NULL; + tls_dma_start(dma_channel, &DmaDesc, 0); + +} + +/** + * @brief + * datas transmit throuth DMA + * + * @param[in] addr + * point to the data buffer will be sent + * @param[in] len + * length of the data to be transfered in bytes + * @param[in] callback + * the callback function when datas transfer finished + * + * @retval + * + * @note the \ref len in bytes + */ +int tls_i2s_tx_dma(uint32_t * addr, uint16_t len, tls_i2s_callback callback) +{ + uint8_t tx_channel; + + TLS_I2S_TX_FIFO_CLEAR(); + tls_reg_write32(HR_I2S_INT_SRC, 0x1FF); + NVIC_ClearPendingIRQ(I2S_IRQn); + + TLS_I2S_TX_FIFO_CLEAR(); + + tx_channel = tls_dma_request(WM_I2S_TX_DMA_CHANNEL, TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_I2S_TX) | TLS_DMA_FLAGS_HARD_MODE); + if (tx_channel == 0) + { + return WM_FAILED; + } + if (tls_dma_stop(tx_channel)) + { + return WM_FAILED; + } + tls_i2s_tx_dma_config(tx_channel, addr, len); + tls_dma_irq_register(tx_channel, callback, NULL, TLS_DMA_IRQ_TRANSFER_DONE); + TLS_I2S_TXDMA_ENABLE(); + TLS_I2S_TX_ENABLE(); + TLS_I2S_ENABLE(); + + return WM_SUCCESS; +} + +/** + * @brief + * This function is used to the rx DMA configuration of the I2S module. + * + * @param[in] addr + * point to the storge buffer of the received data + * + * @param[in] len + * the storge buffer length in bytes + * + * @retval + */ +void tls_i2s_rx_dma_config(uint8_t dma_channel, uint32_t * addr, uint16_t len) +{ + + struct tls_dma_descriptor DmaDesc; + + tls_i2s_int_config(I2S_INT_MASK_RXTH, 0); + TLS_I2S_RX_DISABLE(); + TLS_I2S_RXDMA_DISABLE(); + TLS_I2S_RX_FIFO_CLEAR(); + /** Mask i2s rxth interrupt*/ + tls_i2s_set_rxth(4); + + tls_i2s_int_config(I2S_INT_MASK_RXTH, 0); + DmaDesc.src_addr = (int)HR_I2S_RX; + DmaDesc.dest_addr = (int)addr; + + DmaDesc.dma_ctrl = TLS_DMA_DESC_CTRL_DEST_ADD_INC | TLS_DMA_DESC_CTRL_DATA_SIZE_WORD | + (len << 7); + DmaDesc.valid = TLS_DMA_DESC_VALID; + DmaDesc.next = NULL; + tls_dma_start(dma_channel, &DmaDesc, 0); +} + +int tls_i2s_rx_dma(uint32_t * addr, uint16_t len, tls_i2s_callback callback) +{ + uint8_t rx_channel; + + rx_channel = tls_dma_request(WM_I2S_RX_DMA_CHANNEL, TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_I2S_RX) | TLS_DMA_FLAGS_HARD_MODE); + if (rx_channel == 0) + { + return WM_FAILED; + } + if (tls_dma_stop(rx_channel)) + { + return WM_FAILED; + } + tls_i2s_rx_dma_config(rx_channel, addr, len); + tls_dma_irq_register(rx_channel, callback, NULL, TLS_DMA_IRQ_TRANSFER_DONE); + TLS_I2S_RXDMA_ENABLE(); + NVIC_ClearPendingIRQ(I2S_IRQn); + TLS_I2S_RX_ENABLE(); + TLS_I2S_ENABLE(); + + return WM_SUCCESS; +} +#if 1 +uint8_t tst_flag = 0; + +void tls_i2s_tx_dma_callback() +{ + I2S->TX = 0x00; + while(!tls_bitband_read(HR_I2S_INT_SRC, 7)); + TLS_I2S_TX_DISABLE(); + DMA_CHNLCTRL_REG(WM_I2S_TX_DMA_CHANNEL) |= DMA_CHNL_CTRL_CHNL_OFF; + tls_dma_free(WM_I2S_TX_DMA_CHANNEL); + tst_flag = 1; +} +void tls_i2s_rx_dma_callback() +{ + TLS_I2S_RX_DISABLE(); + DMA_CHNLCTRL_REG(WM_I2S_RX_DMA_CHANNEL) |= DMA_CHNL_CTRL_CHNL_OFF; + tls_dma_free(WM_I2S_RX_DMA_CHANNEL); + tst_flag = 1; +} +void tls_i2s_rx_callback() +{ + TLS_I2S_RX_DISABLE(); + tst_flag = 1; +} +void tls_i2s_test() +{ + uint32_t *ptr = NULL; + uint32_t len; + uint32_t * i2s_tx_test = tls_mem_alloc(1024); + tls_i2s_port.opts.data_width = I2S_CTRL_DATABIT_16; + tls_i2s_port.opts.format = I2S_CTRL_FORMAT_I2S; + tls_i2s_port.opts.tx_en = 1; + tls_i2s_port.opts.sample_rate = 8000; + tls_i2s_port.opts.stereo_mono = I2S_CTRL_STERO; + tls_i2s_port_init(&tls_i2s_port.opts); + if (i2s_tx_test == NULL) + { + return; + } + ptr = i2s_tx_test; + for (len = 0; len < 256; len++) + { + *ptr++ = 0xABCD0200 + len; + } + i2s_tx_test[255] = 0xABCD02FA; + tls_i2s_tx_dma(i2s_tx_test, 1024,tls_i2s_tx_dma_callback); + while(!tst_flag); + tst_flag = 0; + tls_i2s_tx_dma(i2s_tx_test, 1024,tls_i2s_tx_dma_callback); + while(!tst_flag); + tst_flag = 0; +} +#endif +#endif /** TLS_CONFIG_I2S */ + + +/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/ diff --git a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c new file mode 100644 index 0000000..496f26f --- /dev/null +++ b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c @@ -0,0 +1,1408 @@ +/** + * @file wm_internal_fls.c + * + * @brief flash Driver Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#include +#include +#include +#include "wm_dbg.h" +#include "wm_mem.h" +#include "list.h" +#include "wm_flash_map.h" +#include "wm_internal_flash.h" +#include "wm_flash.h" +#include "wm_gpio.h" +#include "wm_io.h" + +void tls_spifls_cs_switch(enum tls_io_name flashcs, int openflag); +void tls_spifls_di_switch(enum tls_io_name flashdi, int openflag); +void tls_spifls_do_switch(enum tls_io_name flashdo, int openflag); +void tls_spifls_ck_switch(enum tls_io_name flashck, int openflag); +extern int tls_spifls_chip_erase(void); +extern int tls_spifls_read_id(u32 *id); +extern int tls_spifls_erase(u32 sector); +extern int tls_spifls_probe(void); + +static struct tls_inside_fls *inside_fls = NULL; +static u32 inner1flashsize = 0; /*first inner flash size*/ +static u32 inner2flashsize = 0; /*second inner flash size*/ + +u32 flashtotalsize = 0; + +static void writeEnable(void) +{ + M32(0x40002000) = 0x6; + M32(0x40002004) = 0x10000000; +} + +unsigned char readRID(void) +{ + M32(0x40002000) = 0x2c09F; + M32(0x40002004) = 0x10000000; + return M32(0x40002200) & 0xFF; +} + +void writeGDBpBit(char cmp, char bp4, char bp3, char bp2, char bp1, char bp0) +{ + int status = 0; + int bpstatus = 0; + + M32(0x40002000) = 0x0C005; + M32(0x40002004) = 0x10000000; + status = M32(0x40002200) & 0xFF; + + M32(0x40002000) = 0x0C035; + M32(0x40002004) = 0x10000000; + status |= (M32(0x40002200) & 0xFF) << 8; + + /*Write Enable*/ + M32(0x40002000) = 0x6; + M32(0x40002004) = 0x10000000; + + bpstatus = (bp4 << 6) | (bp3 << 5) | (bp2 << 4) | (bp1 << 3) | (bp0 << 2); + status = (status & 0xBF83) | bpstatus | (cmp << 14); + + M32(0x40002200) = status; + M32(0x40002000) = 0x1A001; + M32(0x40002004) = 0x10000000; +} + +void writeESMTBpBit(char cmp, char bp4, char bp3, char bp2, char bp1, char bp0) +{ + int status = 0; + int bpstatus = 0; + + M32(0x40002000) = 0x0C005; + M32(0x40002004) = 0x10000000; + status = M32(0x40002200) & 0xFF; + bpstatus = (bp4 << 6) | (bp3 << 5) | (bp2 << 4) | (bp1 << 3) | (bp0 << 2); + status = (status & 0x83) | bpstatus; + + /*Write Enable*/ + M32(0x40002000) = 0x6; + M32(0x40002004) = 0x10000000; + + bpstatus = (bp4 << 6) | (bp3 << 5) | (bp2 << 4) | (bp1 << 3) | (bp0 << 2); + status = (status & 0x83) | bpstatus | (cmp << 14); + + M32(0x40002200) = status; + M32(0x40002000) = 0x0A001; + M32(0x40002004) = 0x10000000; + + + M32(0x40002000) = 0x0C085; + M32(0x40002004) = 0x10000000; + status = M32(0x40002200) & 0xFF; + + /*Write Enable*/ + M32(0x40002000) = 0x6; + M32(0x40002004) = 0x10000000; + + status = (status & 0xBF) | (cmp << 6); + M32(0x40002200) = status; + M32(0x40002000) = 0x0A0C1; + M32(0x40002004) = 0x10000000; +} + +int flashunlock(void) +{ + switch(readRID()) + { + case SPIFLASH_MID_GD: + case SPIFLASH_MID_PUYA: + writeGDBpBit(0, 0, 0, 0, 0, 0); + break; + case SPIFLASH_MID_ESMT: + writeESMTBpBit(0, 0, 0, 0, 0, 0); + break; + default: + return -1; + } + return 0; +} + +int flashlock(void) +{ + switch(readRID()) + { + case SPIFLASH_MID_GD: + case SPIFLASH_MID_PUYA: + writeGDBpBit(0, 1, 1, 0, 1, 0); + break; + case SPIFLASH_MID_ESMT: + writeESMTBpBit(0, 1, 1, 0, 1, 0); + break; + default: + return -1;/*do not clear QIO Mode*/ + } + return 0; +} + +static int programSR(unsigned int cmd, unsigned long addr, unsigned char *buf, unsigned int sz) +{ + unsigned long base_addr = 0; + unsigned int size = 0; + + + if (sz > INSIDE_FLS_PAGE_SIZE) + { + sz = INSIDE_FLS_PAGE_SIZE; + } + + base_addr = 0x40002200; + size = sz; + while(size) + { + M32(base_addr) = *((unsigned long *)buf); + base_addr += 4; + buf += 4; + size -= 4; + } + + writeEnable(); + M32(0x40002000) = cmd | ((sz - 1) << 16); + M32(0x40002004) = 0x10000000 | ((addr & 0xFFFFF) << 8); + + return 0; +} + + +static int programPage (unsigned long adr, unsigned long sz, unsigned char *buf) +{ + programSR(0x80009002, adr, buf, sz); + return(0); +} + +static int eraseSR(unsigned int cmd, unsigned long addr) +{ + /*Write Enable*/ + writeEnable(); + M32(0x40002000) = cmd; + M32(0x40002004) = 0x10000000 | ((addr & 0xFFFFF) << 8); + + return 0; +} + +static int eraseSector (unsigned long adr) +{ + eraseSR(0x80000820, adr); + + return (0); // Finished without Errors +} + +static unsigned int getFlashDensity(void) +{ + unsigned char density = 0; + + M32(0x40002000) = 0x2c09F; + M32(0x40002004) = 0x10000000; + + density = ((M32(0x40002200) & 0xFFFFFF) >> 16) & 0xFF; + if ((density == 0x14) || (density == 0x13)) + { + return (1 << density); + } + + return 0; +} +/*sr start*/ +/************************************************************ +addr: +For GD, address:0x000,0x100 +For ESMT, address:0xFF000,0xFF100 +For WINBOND, address:0x1000 +*************************************************************/ +int readSR(unsigned int cmd, unsigned long addr, unsigned char *buf, unsigned long sz) +{ + int i = 0; + int word = sz / 4; + int byte = sz % 4; + unsigned long addr_read; + + M32(0x40002000) = cmd | ((sz - 1) << 16); + M32(0x40002004) = 0x10000000 | ((addr & 0xFFFFF) << 8); + + addr_read = 0x40002200; + for(i = 0; i < word; i ++) + { + M32(buf) = M32(addr_read); + buf += 4; + addr_read += 4; + } + + if(byte > 0) + { + M32(buf) = M32(addr_read); + buf += 3; //point last byte + while(byte) + { + *buf = 0; + buf --; + byte --; + } + } + + return 0; +} + +void flashSRRW(unsigned long offset, unsigned char *buf, unsigned long sz, unsigned char *backbuf, unsigned int backlen, unsigned int rd) +{ +#define SR_TOTAL_SZ (512) +#define SR_PROGRAM_SZE (256) + unsigned int i; + unsigned int j; + unsigned int baseaddr = 0; + unsigned int sectoroffset = 0; + unsigned int sectorsize = 0; + unsigned int sectornum = 0; + unsigned int remainsz; + unsigned int erasecmd = 0; + unsigned int readcmd = 0; + unsigned int writecmd = 0; + + unsigned char flashid = 0; + if (!buf || ((rd == 0) && ( !backbuf || (backlen < 512)))) + { + return; + } + + flashid = readRID(); + switch(flashid) + { + case SPIFLASH_MID_GD: + baseaddr = 0x0; + sectoroffset = 256; + sectorsize = 256; + sectornum = 2; + erasecmd = 0x80000844; + readcmd = 0xBC00C048; + writecmd = 0x80009042; + break; + + case SPIFLASH_MID_ESMT: + { + baseaddr = 0xFF000; + sectoroffset = 0; + sectorsize = 512; + sectornum = 1; + erasecmd = 0x80000820; + readcmd = 0xBC00C00B; + writecmd = 0x80009002; + + M32(0x40002000) = 0x3A; /*enter OTP*/ + M32(0x40002004) = 0x10000000; + } + break; + + case SPIFLASH_MID_PUYA: + baseaddr = 0x1000; + sectoroffset = 0x1000; + sectorsize = 256; + sectornum = 2; + erasecmd = 0x80000844; + readcmd = 0xBC00C048; + writecmd = 0x80009042; + + break; + + default: + { + } + break; + } + + for (i = 0 ; i < sectornum; i++) + { + readSR(readcmd, baseaddr + sectoroffset * i, backbuf + i * sectorsize, sectorsize); + } + + if (rd) + { + for(i = 0; i < sz; i++) //Copy + { + buf[i] = backbuf[i + offset]; + } + } + else + { + for (i = 0; i < sectornum ; i++) + { + eraseSR(erasecmd, baseaddr + sectoroffset * i); + } + + remainsz = (sz < (SR_TOTAL_SZ - offset)) ? sz : (SR_TOTAL_SZ - offset); + for(i = 0; i < remainsz; i++) + { + backbuf[i + offset] = buf[i]; + } + + for ( i = 0; i < sectornum; i++) + { + for (j = 0; j < (sectorsize / SR_PROGRAM_SZE); j++) + { + programSR(writecmd, baseaddr + sectoroffset * i + j * SR_PROGRAM_SZE, backbuf, SR_PROGRAM_SZE); + backbuf += SR_PROGRAM_SZE; + } + } + } + + + + if (SPIFLASH_MID_ESMT == flashid) + { + /*Write Disable*/ + M32(0x40002000) = 0x4; + M32(0x40002004) = 0x10000000; + } +} + +/*sr end*/ +int readByCMD(unsigned long addr, unsigned char *buf, unsigned long sz, unsigned int mode) +{ + int i = 0; + int word = sz / 4; + int byte = sz % 4; + unsigned long addr_read; + + if (mode & 0x1) /*QIO*/ + { + addr_read = 0xEC00C0EB; + } + else /*SPI*/ + { + addr_read = 0xBC00C00B; + } + M32(0x40002000) = addr_read | (((sz - 1) & 0x3FF) << 16); + M32(0x40002004) = 0x10000000 | ((addr & 0xFFFFF) << 8); + + addr_read = 0x40002200; + for(i = 0; i < word; i ++) + { + M32(buf) = M32(addr_read); + buf += 4; + addr_read += 4; + } + + if(byte > 0) + { + M32(buf) = M32(addr_read); + buf += 3; //point last byte + byte = 4 - byte; + while(byte) + { + *buf = 0; + buf --; + byte --; + } + } + + return 0; +} + +void writeSRLockBit(unsigned char srbitlock) +{ + int status = 0; + switch(readRID()) + { + case SPIFLASH_MID_PUYA: + { + if(srbitlock >= 8) + { + return; + } + + M32(0x40002000) = 0x0C005; + M32(0x40002004) = 0x10000000; + status = M32(0x40002200)&0xFF; + + M32(0x40002000) = 0x0C035; + M32(0x40002004) = 0x10000000; + status |= (M32(0x40002200)&0xFF)<<8; + + /*Write Enable*/ + M32(0x40002000) = 0x6; + M32(0x40002004) = 0x10000000; + + status |= (srbitlock)<<11; + + M32(0x40002200) = status; + M32(0x40002000) = 0x1A001; + M32(0x40002004) = 0x10000000; + } + break; + + default: + break; + } +} + + +int flashRead(unsigned long addr, unsigned char *buf, unsigned long sz) +{ + unsigned int flash_addr; + unsigned int sz_pagenum = 0; + unsigned int sz_remain = 0; + int i = 0; + int page_offset = addr & (INSIDE_FLS_PAGE_SIZE - 1); + + char *cache = NULL; + + cache = tls_mem_alloc(INSIDE_FLS_PAGE_SIZE); + if (cache == NULL) + { + TLS_DBGPRT_ERR("allocate sector cache memory fail!\n"); + return TLS_FLS_STATUS_ENOMEM; + } + flash_addr = addr & ~(INSIDE_FLS_PAGE_SIZE - 1); + readByCMD(flash_addr, (unsigned char *)cache, INSIDE_FLS_PAGE_SIZE, 0); + if (sz > INSIDE_FLS_PAGE_SIZE - page_offset) + { + MEMCPY(buf, cache + page_offset, INSIDE_FLS_PAGE_SIZE - page_offset); + buf += INSIDE_FLS_PAGE_SIZE - page_offset; + flash_addr += INSIDE_FLS_PAGE_SIZE; + + sz_pagenum = (sz - (INSIDE_FLS_PAGE_SIZE - page_offset)) / INSIDE_FLS_PAGE_SIZE; + sz_remain = (sz - (INSIDE_FLS_PAGE_SIZE - page_offset)) % INSIDE_FLS_PAGE_SIZE; + for (i = 0; i < sz_pagenum; i++) + { + + readByCMD(flash_addr, (unsigned char *)cache, INSIDE_FLS_PAGE_SIZE, 0); + MEMCPY(buf, cache, INSIDE_FLS_PAGE_SIZE); + buf += INSIDE_FLS_PAGE_SIZE; + flash_addr += INSIDE_FLS_PAGE_SIZE; + } + + if (sz_remain) + { + readByCMD(flash_addr, (unsigned char *)cache, sz_remain, 0); + MEMCPY(buf, cache, sz_remain); + } + } + else + { + MEMCPY(buf, cache + page_offset, sz); + } + tls_mem_free(cache); + + return 0; +} + +/** + * @brief This function is used to unlock flash protect area [0x0~0x2000]. + * + * @param None + * + * @return None + * + * @note None + */ +int tls_flash_unlock(void) +{ + return flashunlock(); +} + +/** + * @brief This function is used to lock flash protect area [0x0~0x2000]. + * + * @param None + * + * @return None + * + * @note None + */ +int tls_flash_lock(void) +{ + return flashlock(); +} + + +/** + * @brief This function is used to semaphore protect. + * + * @param None + * + * @return None + * + * @note None + */ +void tls_fls_sem_lock(void) +{ + if (inside_fls == NULL) + { + return; + } + tls_os_sem_acquire(inside_fls->fls_lock, 0); +} + + +/** + * @brief This function is used to semaphore protect cancel. + * + * @param None + * + * @return None + * + * @note None + */ +void tls_fls_sem_unlock(void) +{ + if (inside_fls == NULL) + { + return; + } + tls_os_sem_release(inside_fls->fls_lock); +} + + +/** + * @brief This function is used to read data from the flash. + * + * @param[in] addr is byte offset addr for read from the flash. + * @param[in] buf is user for data buffer of flash read + * @param[in] len is byte length for read. + * + * @retval TLS_FLS_STATUS_OK if read sucsess + * @retval TLS_FLS_STATUS_EPERM if flash driver module not beed installed + * @retval TLS_FLS_STATUS_EINVAL if arguments invalid + * @retval TLS_FLS_STATUS_EIO if read fail + * + * @note None + */ +int tls_fls_read(u32 addr, u8 *buf, u32 len) +{ + int err = TLS_FLS_STATUS_EINVAL; + u32 addrfor1M = 0; + u32 lenfor1M = 0; + u32 addrfor2M = 0; + u32 lenfor2M = 0; + + addrfor1M = addr < FLASH_1M_END_ADDR ? addr : 0xFFFFFFFF; + if (addrfor1M != 0xFFFFFFFF) + { + lenfor1M = (addr + len ) <= FLASH_1M_END_ADDR ? len : (FLASH_1M_END_ADDR - addr); + if (inside_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (((addrfor1M & (INSIDE_FLS_BASE_ADDR - 1)) >= inner1flashsize) || (lenfor1M == 0) || (buf == NULL)) + { + return TLS_FLS_STATUS_EINVAL; + } + + tls_os_sem_acquire(inside_fls->fls_lock, 0); + + flashRead(addrfor1M, buf, lenfor1M); + + err = TLS_FLS_STATUS_OK; + tls_os_sem_release(inside_fls->fls_lock); + } + + if (inner2flashsize) + { + addrfor2M = addr >= FLASH_1M_END_ADDR ? addr : ((addr + len ) > FLASH_1M_END_ADDR ? FLASH_1M_END_ADDR : 0xFFFFFFFF); + if (addrfor2M != 0xFFFFFFFF) + { + lenfor2M = len - lenfor1M; + lenfor2M = (addrfor2M + lenfor2M) <= (FLASH_BASE_ADDR | flashtotalsize) ? lenfor2M : ((FLASH_BASE_ADDR | flashtotalsize) - addrfor2M); + tls_spifls_cs_switch(WM_IO_PA_02, 1); + tls_spifls_ck_switch(WM_IO_PA_11, 1); + tls_spifls_di_switch(WM_IO_PA_03, 1); + tls_spifls_do_switch(WM_IO_PA_09, 1); + err = tls_spifls_read(addrfor2M & 0xFFFFF, buf + lenfor1M, lenfor2M); + tls_spifls_di_switch(WM_IO_PA_03, 0); + tls_spifls_do_switch(WM_IO_PA_09, 0); + tls_spifls_ck_switch(WM_IO_PA_11, 0); + tls_spifls_cs_switch(WM_IO_PA_02, 0); + + return err; + } + } + + return err; +} + +/** + * @brief This function is used to write data to the flash. + * + * @param[in] addr is byte offset addr for write to the flash + * @param[in] buf is the data buffer want to write to flash + * @param[in] len is the byte length want to write + * + * @retval TLS_FLS_STATUS_OK if write flash success + * @retval TLS_FLS_STATUS_EPERM if flash struct point is null + * @retval TLS_FLS_STATUS_ENODRV if flash driver is not installed + * @retval TLS_FLS_STATUS_EINVAL if argument is invalid + * @retval TLS_FLS_STATUS_EIO if io error + * + * @note None + */ +int tls_fls_write(u32 addr, u8 *buf, u32 len) +{ + u8 *cache; + unsigned int secpos; + unsigned int secoff; + unsigned int secremain; + unsigned int i; + unsigned int offaddr; + unsigned int writepage; + int err = 0; + + unsigned int cntforFF = 0; + u32 addrfor1M = 0; + u32 lenfor1M = 0; + u32 addrfor2M = 0; + u32 lenfor2M = 0; +// u32 cpu_sr = 0; +// u32 elapsedtime = 0; + + addrfor1M = addr < FLASH_1M_END_ADDR ? addr : 0xFFFFFFFF; + if (addrfor1M != 0xFFFFFFFF) + { + lenfor1M = (addr + len ) <= FLASH_1M_END_ADDR ? len : (FLASH_1M_END_ADDR - addr); + + if (inside_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (((addrfor1M & (INSIDE_FLS_BASE_ADDR - 1)) >= inner1flashsize) || (lenfor1M == 0) || (buf == NULL)) + { + return TLS_FLS_STATUS_EINVAL; + } + + tls_os_sem_acquire(inside_fls->fls_lock, 0); + + cache = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); + if (cache == NULL) + { + tls_os_sem_release(inside_fls->fls_lock); + TLS_DBGPRT_ERR("allocate sector cache memory fail!\n"); + return TLS_FLS_STATUS_ENOMEM; + } + + offaddr = addrfor1M & (INSIDE_FLS_BASE_ADDR - 1); //Offset of 0X08000000 + secpos = offaddr / INSIDE_FLS_SECTOR_SIZE; //Section addr + secoff = (offaddr % INSIDE_FLS_SECTOR_SIZE); //Offset in section + secremain = INSIDE_FLS_SECTOR_SIZE - secoff; // ����ʣ��ռ��С + + if(lenfor1M <= secremain) + { + secremain = lenfor1M; //Not bigger with remain size in section + } + while (1) + { + flashRead(secpos * INSIDE_FLS_SECTOR_SIZE, cache, INSIDE_FLS_SECTOR_SIZE); + cntforFF = 0; + for (i = 0; i < secremain; i++) // ���� + { + if(cache[i + secoff] == 0xFF) + { + cntforFF++; + } + cache[i + secoff] = buf[i]; + } + if( cntforFF != secremain) + { + eraseSector(secpos * INSIDE_FLS_SECTOR_SIZE); + } + if( (cntforFF != secremain ) || ((secoff == 0) && (secremain == 4096))) + { + for (i = 0; i < (INSIDE_FLS_SECTOR_SIZE / INSIDE_FLS_PAGE_SIZE); i++) + { + programPage(secpos * INSIDE_FLS_SECTOR_SIZE + i * INSIDE_FLS_PAGE_SIZE, INSIDE_FLS_PAGE_SIZE, &cache[i * INSIDE_FLS_PAGE_SIZE]); //Write + } + } + else + { + writepage = secoff / INSIDE_FLS_PAGE_SIZE == 15 ? 16 : (secoff + secremain) / INSIDE_FLS_PAGE_SIZE + 1; + for(i = (secoff / INSIDE_FLS_PAGE_SIZE); i < writepage; i++) + { + programPage(secpos * INSIDE_FLS_SECTOR_SIZE + i * INSIDE_FLS_PAGE_SIZE, INSIDE_FLS_PAGE_SIZE, &cache[i * INSIDE_FLS_PAGE_SIZE]); //Write + } + } + if(lenfor1M == secremain) + { + break; // ������ + } + else // д��δ���� + { + secpos++; // ������ַ��1 + secoff = 0; // ƫ��λ��Ϊ0 + buf += secremain; // ָ��ƫ�� + lenfor1M -= secremain; + if(lenfor1M > (INSIDE_FLS_SECTOR_SIZE)) + secremain = INSIDE_FLS_SECTOR_SIZE; // ��һ����������д���� + else + secremain = lenfor1M; //Next section will finish + } + } + + tls_mem_free(cache); + tls_os_sem_release(inside_fls->fls_lock); + } + + if (inner2flashsize) + { + addrfor2M = addr >= FLASH_1M_END_ADDR ? addr : ((addr + len ) > FLASH_1M_END_ADDR ? FLASH_1M_END_ADDR : 0xFFFFFFFF); + if (addrfor2M != 0xFFFFFFFF) + { + lenfor2M = len - lenfor1M; + lenfor2M = (addrfor2M + lenfor2M) <= (FLASH_BASE_ADDR | flashtotalsize) ? lenfor2M : ((FLASH_BASE_ADDR | flashtotalsize) - addrfor2M); + tls_spifls_cs_switch(WM_IO_PA_02, 1); + tls_spifls_ck_switch(WM_IO_PA_11, 1); + tls_spifls_di_switch(WM_IO_PA_03, 1); + tls_spifls_do_switch(WM_IO_PA_09, 1); + err = tls_spifls_write((addrfor2M & 0xFFFFF), buf + lenfor1M, lenfor2M); + tls_spifls_di_switch(WM_IO_PA_03, 0); + tls_spifls_do_switch(WM_IO_PA_09, 0); + tls_spifls_ck_switch(WM_IO_PA_11, 0); + tls_spifls_cs_switch(WM_IO_PA_02, 0); + + + return err; + } + } + return TLS_FLS_STATUS_OK; +} + +/** + * @brief This function is used to erase the appoint sector + * + * @param[in] sector sector num of the flash, 4K byte a sector + * + * @retval TLS_FLS_STATUS_OK if read sucsess + * @retval other if read fail + * + * @note None + */ +int tls_fls_erase(u32 sector) +{ + u32 addr; + int err = 0; + if (sector < (inner1flashsize / INSIDE_FLS_SECTOR_SIZE + INSIDE_FLS_BASE_ADDR / INSIDE_FLS_SECTOR_SIZE)) + { + if (inside_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + tls_os_sem_acquire(inside_fls->fls_lock, 0); + + addr = sector * INSIDE_FLS_SECTOR_SIZE; + + eraseSector(addr); + + tls_os_sem_release(inside_fls->fls_lock); + } + else if (sector < (flashtotalsize / INSIDE_FLS_SECTOR_SIZE + INSIDE_FLS_BASE_ADDR / INSIDE_FLS_SECTOR_SIZE)) + { + tls_spifls_cs_switch(WM_IO_PA_02, 1); + tls_spifls_ck_switch(WM_IO_PA_11, 1); + tls_spifls_di_switch(WM_IO_PA_03, 1); + tls_spifls_do_switch(WM_IO_PA_09, 1); + err = tls_spifls_erase(sector & 0xFF); + tls_spifls_di_switch(WM_IO_PA_03, 0); + tls_spifls_do_switch(WM_IO_PA_09, 0); + tls_spifls_ck_switch(WM_IO_PA_11, 0); + tls_spifls_cs_switch(WM_IO_PA_02, 0); + return err; + } + + return TLS_FLS_STATUS_OK; +} + + +static u8 *gsflscache = NULL; +//static u32 gsSecOffset = 0; +static u32 gsSector = 0; + + +/** + * @brief This function is used to flush the appoint sector + * + * @param None + * + * @return None + * + * @note The caller should use fls_lock semphore to protect flash operation! + */ +static void tls_fls_flush_sector(void) +{ + int i; + u32 addr; + if (gsSector < (inner1flashsize / INSIDE_FLS_SECTOR_SIZE + INSIDE_FLS_BASE_ADDR / INSIDE_FLS_SECTOR_SIZE)) + { + addr = gsSector * INSIDE_FLS_SECTOR_SIZE; + + eraseSector(addr); + for (i = 0; i < INSIDE_FLS_SECTOR_SIZE / INSIDE_FLS_PAGE_SIZE; i++) + { + programPage(gsSector * INSIDE_FLS_SECTOR_SIZE + + i * INSIDE_FLS_PAGE_SIZE, INSIDE_FLS_PAGE_SIZE, + &gsflscache[i * INSIDE_FLS_PAGE_SIZE]); + } + } + else if (gsSector < (flashtotalsize / INSIDE_FLS_SECTOR_SIZE + INSIDE_FLS_BASE_ADDR / INSIDE_FLS_SECTOR_SIZE)) + { + addr = gsSector * INSIDE_FLS_SECTOR_SIZE; + tls_spifls_cs_switch(WM_IO_PA_02, 1); + tls_spifls_ck_switch(WM_IO_PA_11, 1); + tls_spifls_di_switch(WM_IO_PA_03, 1); + tls_spifls_do_switch(WM_IO_PA_09, 1); + tls_spifls_write(addr & 0xFFFFF, gsflscache, INSIDE_FLS_SECTOR_SIZE); + tls_spifls_di_switch(WM_IO_PA_03, 0); + tls_spifls_do_switch(WM_IO_PA_09, 0); + tls_spifls_ck_switch(WM_IO_PA_11, 0); + tls_spifls_cs_switch(WM_IO_PA_02, 0); + } + //gsSecOffset = 0; + +} + + +/** + * @brief This function is used to fast write flash initialize + * + * @param None + * + * @retval TLS_FLS_STATUS_OK sucsess + * @retval other fail + * + * @note None + */ +int tls_fls_fast_write_init(void) +{ + + if (inside_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + if (NULL != gsflscache) + { + TLS_DBGPRT_ERR("tls_fls_fast_write_init installed!\n"); + return -1; + } + gsflscache = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); + if (NULL == gsflscache) + { + TLS_DBGPRT_ERR("tls_fls_fast_write_init malloc err!\n"); + return -1; + } + return TLS_FLS_STATUS_OK; +} + +/** + * @brief This function is used to destroy fast write flash + * + * @param None + * + * @return None + * + * @note None + */ +void tls_fls_fast_write_destroy(void) +{ + if (NULL != gsflscache) + { + if (inside_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return; + } + else + { + tls_os_sem_acquire(inside_fls->fls_lock, 0); + tls_fls_flush_sector(); + tls_os_sem_release(inside_fls->fls_lock); + } + + tls_mem_free(gsflscache); + gsflscache = NULL; + } +} + +/** + * @brief This function is used to fast write data to the flash. + * + * @param[in] addr is byte offset addr for write to the flash + * @param[in] buf is the data buffer want to write to flash + * @param[in] length is the byte length want to write + * + * @retval TLS_FLS_STATUS_OK success + * @retval other fail + * + * @note None + */ +int tls_fls_fast_write(u32 addr, u8 *buf, u32 length) +{ + + u32 sector, offset, maxlen, len; + + if (inside_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if(((addr & (INSIDE_FLS_BASE_ADDR - 1)) >= flashtotalsize) || (length == 0) || (buf == NULL)) + { + return TLS_FLS_STATUS_EINVAL; + } + tls_os_sem_acquire(inside_fls->fls_lock, 0); + + sector = addr / INSIDE_FLS_SECTOR_SIZE; + offset = addr % INSIDE_FLS_SECTOR_SIZE; + maxlen = INSIDE_FLS_SECTOR_SIZE; + + if ((sector != gsSector) && (gsSector != 0)) + { + tls_fls_flush_sector(); + } + gsSector = sector; + if (offset > 0) + { + maxlen -= offset; + } + while (length > 0) + { + len = (length > maxlen) ? maxlen : length; + MEMCPY(gsflscache + offset, buf, len); + if (offset + len >= INSIDE_FLS_SECTOR_SIZE) + { + tls_fls_flush_sector(); + gsSector++; + } + offset = 0; + maxlen = INSIDE_FLS_SECTOR_SIZE; + sector++; + buf += len; + length -= len; + } + + tls_os_sem_release(inside_fls->fls_lock); + + return TLS_FLS_STATUS_OK; +} + + +/** + * @brief This function is used to erase flash all chip + * + * @param None + * + * @retval TLS_FLS_STATUS_OK sucsess + * @retval other fail + * + * @note None + */ +int tls_fls_chip_erase(void) +{ + int i, j; + u8 *cache; + + if (inside_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + tls_os_sem_acquire(inside_fls->fls_lock, 0); + + cache = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); + if (cache == NULL) + { + tls_os_sem_release(inside_fls->fls_lock); + TLS_DBGPRT_ERR("allocate sector cache memory fail!\n"); + return TLS_FLS_STATUS_ENOMEM; + } + + + for( i = 0; i < ( inner1flashsize - (INSIDE_FLS_SECBOOT_ADDR & 0xFFFFF)) / INSIDE_FLS_SECTOR_SIZE; i ++) + { + flashRead(INSIDE_FLS_SECBOOT_ADDR + i * INSIDE_FLS_SECTOR_SIZE, cache, INSIDE_FLS_SECTOR_SIZE); + for (j = 0; j < INSIDE_FLS_SECTOR_SIZE; j++) + { + if (cache[j] != 0xFF) + { + eraseSector(INSIDE_FLS_SECBOOT_ADDR + i * INSIDE_FLS_SECTOR_SIZE); + break; + } + } + } + + if (inner2flashsize) + { + tls_spifls_cs_switch(WM_IO_PA_02, 1); + tls_spifls_ck_switch(WM_IO_PA_11, 1); + tls_spifls_di_switch(WM_IO_PA_03, 1); + tls_spifls_do_switch(WM_IO_PA_09, 1); + tls_spifls_chip_erase(); + tls_spifls_di_switch(WM_IO_PA_03, 0); + tls_spifls_do_switch(WM_IO_PA_09, 0); + tls_spifls_ck_switch(WM_IO_PA_11, 0); + tls_spifls_cs_switch(WM_IO_PA_02, 0); + } + + tls_mem_free(cache); + + tls_os_sem_release(inside_fls->fls_lock); + + return TLS_FLS_STATUS_OK; +} + + +/** + * @brief This function is used to get flash param + * + * @param[in] type the type of the param need to get + * @param[out] param point to addr of out param + * + * @retval TLS_FLS_STATUS_OK sucsess + * @retval other fail + * + * @note None + */ +int tls_fls_get_param(u8 type, void *param) +{ + int err; + + + if (inside_fls == NULL) + { + TLS_DBGPRT_ERR("flash driver module not beed installed!\n"); + return TLS_FLS_STATUS_EPERM; + } + + if (param == NULL) + { + return TLS_FLS_STATUS_EINVAL; + } + tls_os_sem_acquire(inside_fls->fls_lock, 0); + err = TLS_FLS_STATUS_OK; + switch (type) + { + case TLS_FLS_PARAM_TYPE_ID: + *((u32 *) param) = 0x2013; + break; + + case TLS_FLS_PARAM_TYPE_SIZE: + *((u32 *) param) = flashtotalsize; + break; + + case TLS_FLS_PARAM_TYPE_PAGE_SIZE: + *((u32 *) param) = INSIDE_FLS_PAGE_SIZE; + break; + + case TLS_FLS_PARAM_TYPE_PROG_SIZE: + *((u32 *) param) = INSIDE_FLS_PAGE_SIZE; + break; + + case TLS_FLS_PARAM_TYPE_SECTOR_SIZE: + *((u32 *) param) = INSIDE_FLS_SECTOR_SIZE; + break; + + default: + TLS_DBGPRT_WARNING("invalid parameter ID!\n"); + err = TLS_FLS_STATUS_EINVAL; + break; + } + tls_os_sem_release(inside_fls->fls_lock); + return err; +} + +/** + * @brief This function is used to initialize the flash module + * + * @param None + * + * @retval TLS_FLS_STATUS_OK sucsess + * @retval other fail + * + * @note None + */ +int tls_fls_init(void) +{ + struct tls_inside_fls *fls; + int err; + u32 id = 0; + + if (inside_fls != NULL) + { + TLS_DBGPRT_INFO("flash driver module has been installed!\n"); + return TLS_FLS_STATUS_EBUSY; + } + + fls = (struct tls_inside_fls *) tls_mem_alloc(sizeof(struct tls_inside_fls)); + if (fls == NULL) + { + TLS_DBGPRT_ERR("allocate @inside_fls fail!\n"); + return TLS_FLS_STATUS_ENOMEM; + } + + memset(fls, 0, sizeof(*fls)); + err = tls_os_sem_create(&fls->fls_lock, 1); + if (err != TLS_OS_SUCCESS) + { + tls_mem_free(fls); + TLS_DBGPRT_ERR("create semaphore @fls_lock fail!\n"); + return TLS_FLS_STATUS_ENOMEM; + } + inside_fls = fls; + + inner1flashsize = getFlashDensity(); + tls_spifls_cs_switch(WM_IO_PA_02, 1); + tls_spifls_ck_switch(WM_IO_PA_11, 1); + tls_spifls_di_switch(WM_IO_PA_03, 1); + tls_spifls_do_switch(WM_IO_PA_09, 1); + if (TLS_FLS_STATUS_OK == tls_spifls_read_id(&id)) + { + id = (id >> 16) & 0xFF; + inner2flashsize = (id ? (1 << id) : 0); + tls_spifls_probe(); + } + tls_spifls_di_switch(WM_IO_PA_03, 0); + tls_spifls_do_switch(WM_IO_PA_09, 0); + tls_spifls_ck_switch(WM_IO_PA_11, 0); + tls_spifls_cs_switch(WM_IO_PA_02, 0); + flashtotalsize = inner1flashsize + inner2flashsize; + + return TLS_FLS_STATUS_OK; +} + +int tls_fls_exit(void) +{ + TLS_DBGPRT_FLASH_INFO("Not support flash driver module uninstalled!\n"); + return TLS_FLS_STATUS_EPERM; +} + +void tls_spifls_di_switch(enum tls_io_name flashdi, int openflag) +{ + u32 optvalue = WM_IO_OPTION3; + int i = 0; + static u8 ioclose = 0; + u8 spidi[5][2] = {{WM_IO_PA_03, WM_IO_OPTION3}, + {WM_IO_PB_17, WM_IO_OPTION3}, + {WM_IO_PB_01, WM_IO_OPTION1}, + {WM_IO_PA_05, WM_IO_OPTION3}, + {WM_IO_PA_10, WM_IO_OPTION4} + }; + + if (openflag == 1) + { + for (i = 0; i < sizeof(spidi) / (2 * sizeof(u8)); i++) + { + optvalue = tls_io_cfg_get((enum tls_io_name)spidi[i][0]); + if (flashdi == (enum tls_io_name)spidi[i][0]) + { + if (optvalue != spidi[i][1]) + { + ioclose &= ~(1 << i); + tls_io_cfg_set(flashdi, spidi[i][1]); + } + } + else + { + if (optvalue == spidi[i][1]) + { + ioclose |= (1 << i); + tls_io_cfg_set((enum tls_io_name)spidi[i][0], WM_IO_OPTION5); + } + } + } + } + else + { + for (i = 0; i < sizeof(spidi) / (2 * sizeof(u8)); i++) + { + if (flashdi == (enum tls_io_name)spidi[i][0]) + { + tls_io_cfg_set(flashdi, WM_IO_OPTION5); + } + else + { + if (ioclose & (1 << i)) + { + ioclose &= ~(1 << i); + tls_io_cfg_set((enum tls_io_name)spidi[i][0], spidi[i][1]); + } + } + } + } +} + +void tls_spifls_do_switch(enum tls_io_name flashdo, int openflag) +{ + u32 optvalue = WM_IO_OPTION3; + int i = 0; + static u8 ioclose = 0; + u8 spido[5][2] = {{WM_IO_PB_18, WM_IO_OPTION3}, + {WM_IO_PB_02, WM_IO_OPTION1}, + {WM_IO_PA_04, WM_IO_OPTION3}, + {WM_IO_PA_09, WM_IO_OPTION4}, + {WM_IO_PA_10, WM_IO_OPTION4} + }; + + + if (openflag == 1) + { + for (i = 0; i < sizeof(spido) / (2 * sizeof(u8)); i++) + { + optvalue = tls_io_cfg_get((enum tls_io_name)spido[i][0]); + if (flashdo == (enum tls_io_name)spido[i][0]) + { + if (optvalue != spido[i][1]) + { + ioclose &= ~(1 << i); + tls_io_cfg_set(flashdo, spido[i][1]); + } + } + else + { + if (optvalue == spido[i][1]) + { + ioclose |= (1 << i); + tls_io_cfg_set((enum tls_io_name)spido[i][0], WM_IO_OPTION5); + } + } + } + } + else + { + for (i = 0; i < sizeof(spido) / (2 * sizeof(u8)); i++) + { + if (flashdo == (enum tls_io_name)spido[i][0]) + { + tls_io_cfg_set(flashdo, WM_IO_OPTION5); + } + else + { + if (ioclose & (1 << i)) + { + ioclose &= ~(1 << i); + tls_io_cfg_set((enum tls_io_name)spido[i][0], spido[i][1]); + } + } + } + } +} + +void tls_spifls_ck_switch(enum tls_io_name flashck, int openflag) +{ + u32 optvalue = WM_IO_OPTION3; + int i = 0; + static u8 ioclose = 0; + u8 spick[4][2] = {{WM_IO_PA_01, WM_IO_OPTION3}, + {WM_IO_PB_16, WM_IO_OPTION3}, + {WM_IO_PB_27, WM_IO_OPTION1}, + {WM_IO_PA_11, WM_IO_OPTION4} + }; + + if (openflag == 1) + { + for (i = 0; i < sizeof(spick) / (2 * sizeof(u8)); i++) + { + optvalue = tls_io_cfg_get((enum tls_io_name)spick[i][0]); + if (flashck == (enum tls_io_name)spick[i][0]) + { + if (optvalue != spick[i][1]) + { + ioclose &= ~(1 << i); + tls_io_cfg_set(flashck, spick[i][1]); + } + } + else + { + if (optvalue == spick[i][1]) + { + ioclose |= (1 << i); + tls_io_cfg_set((enum tls_io_name)spick[i][0], WM_IO_OPTION5); + } + } + } + } + else + { + for (i = 0; i < sizeof(spick) / (2 * sizeof(u8)); i++) + { + if (flashck == (enum tls_io_name)spick[i][0]) + { + tls_io_cfg_set(flashck, WM_IO_OPTION5); + } + else + { + if (ioclose & (1 << i)) + { + ioclose &= ~(1 << i); + tls_io_cfg_set((enum tls_io_name)spick[i][0], spick[i][1]); + } + } + } + } +} + + +void tls_spifls_cs_switch(enum tls_io_name flashcs, int openflag) +{ + u32 optvalue = WM_IO_OPTION3; + int i = 0; + static u8 ioclose = 0; + u8 spics[5][2] = {{WM_IO_PA_02, WM_IO_OPTION3}, + {WM_IO_PB_15, WM_IO_OPTION3}, + {WM_IO_PB_00, WM_IO_OPTION1}, + {WM_IO_PB_07, WM_IO_OPTION4}, + {WM_IO_PA_12, WM_IO_OPTION4} + }; + + if (openflag == 1) + { + for (i = 0; i < sizeof(spics) / (2 * sizeof(u8)); i++) + { + optvalue = tls_io_cfg_get((enum tls_io_name)spics[i][0]); + if (flashcs == (enum tls_io_name)spics[i][0]) + { + if (optvalue != spics[i][1]) + { + ioclose &= ~(1 << i); + tls_io_cfg_set(flashcs, spics[i][1]); + } + } + else + { + if (optvalue == spics[i][1]) + { + ioclose |= (1 << i); + tls_io_cfg_set((enum tls_io_name)spics[i][0], WM_IO_OPTION5); + } + } + } + } + else + { + for (i = 0; i < sizeof(spics) / (2 * sizeof(u8)); i++) + { + if (flashcs == (enum tls_io_name)spics[i][0]) + { + tls_io_cfg_set(flashcs, WM_IO_OPTION5); + } + else + { + if (ioclose & (1 << i)) + { + ioclose &= ~(1 << i); + tls_io_cfg_set((enum tls_io_name)spics[i][0], spics[i][1]); + } + } + } + } +} + diff --git a/sdk/Platform/Drivers/io/wm_io.c b/sdk/Platform/Drivers/io/wm_io.c new file mode 100644 index 0000000..4cb1afe --- /dev/null +++ b/sdk/Platform/Drivers/io/wm_io.c @@ -0,0 +1,277 @@ +/** + * @file wm_io.c + * + * @brief IO Driver Module + * + * @author lilm + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#include "wm_regs.h" +#include "wm_osal.h" +#include "wm_io.h" +#include "wm_dbg.h" +#include "tls_common.h" + +static void io_cfg_option1(enum tls_io_name name) +{ + u8 pin; + u16 offset; + + if ( (WM_IO_PB_25 == name) || (WM_IO_PB_26 == name) || (WM_IO_PB_29 == name) ) + { + TLS_DBGPRT_IO_ERR("io %u have no option1.\r\n", name); + return; + } + + if (name >= WM_IO_PB_00) + { + pin = name - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = name; + offset = 0; + } + + tls_reg_write32(HR_GPIO_AF_SEL + offset, tls_reg_read32(HR_GPIO_AF_SEL + offset) | BIT(pin)); /* gpio function */ + tls_reg_write32(HR_GPIO_AF_S1 + offset, tls_reg_read32(HR_GPIO_AF_S1 + offset) & (~BIT(pin))); + tls_reg_write32(HR_GPIO_AF_S0 + offset, tls_reg_read32(HR_GPIO_AF_S0 + offset) & (~BIT(pin))); + + return; +} + +static void io_cfg_option2(enum tls_io_name name) +{ + u8 pin; + u16 offset; + + if ( (WM_IO_PB_13 == name) || (WM_IO_PB_27 == name) || + (WM_IO_PB_31 == name) ) + { + TLS_DBGPRT_IO_ERR("io %u have no option2.\r\n", name); + return; + } + + if (name >= WM_IO_PB_00) + { + pin = name - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = name; + offset = 0; + } + + tls_reg_write32(HR_GPIO_AF_SEL + offset, tls_reg_read32(HR_GPIO_AF_SEL + offset) | BIT(pin)); /* gpio function */ + tls_reg_write32(HR_GPIO_AF_S1 + offset, tls_reg_read32(HR_GPIO_AF_S1 + offset) & (~BIT(pin))); + tls_reg_write32(HR_GPIO_AF_S0 + offset, tls_reg_read32(HR_GPIO_AF_S0 + offset) | BIT(pin)); + + return; +} + +static void io_cfg_option3(enum tls_io_name name) +{ + u8 pin; + u16 offset; + + if ( (name == WM_IO_PB_19) || (name == WM_IO_PB_20) || + (name >= WM_IO_PB_23) || (name == WM_IO_PA_00) || + (name == WM_IO_PA_07) || + ((name >= WM_IO_PA_13) && (name <= WM_IO_PA_15)) ) + { + TLS_DBGPRT_IO_ERR("io %u have no option3.\r\n", name); + return; + } + + if (name >= WM_IO_PB_00) + { + pin = name - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = name; + offset = 0; + } + + tls_reg_write32(HR_GPIO_AF_SEL + offset, tls_reg_read32(HR_GPIO_AF_SEL + offset) | BIT(pin)); /* gpio function */ + tls_reg_write32(HR_GPIO_AF_S1 + offset, tls_reg_read32(HR_GPIO_AF_S1 + offset) | BIT(pin)); + tls_reg_write32(HR_GPIO_AF_S0 + offset, tls_reg_read32(HR_GPIO_AF_S0 + offset) & (~BIT(pin))); + + return; +} + +static void io_cfg_option4(enum tls_io_name name) +{ + u8 pin; + u16 offset; + + if( (name == WM_IO_PA_00) || (name == WM_IO_PA_01) || + (name == WM_IO_PA_06) || + ((name >= WM_IO_PA_13) && (name <= WM_IO_PA_15)) || + ((name >= WM_IO_PB_00) && (name <= WM_IO_PB_02)) || + (name >= WM_IO_PB_19) ) + { + TLS_DBGPRT_IO_ERR("io %u have no option4.\r\n", name); + return; + } + + if (name >= WM_IO_PB_00) + { + pin = name - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = name; + offset = 0; + } + + tls_reg_write32(HR_GPIO_AF_SEL + offset, tls_reg_read32(HR_GPIO_AF_SEL + offset) | BIT(pin)); /* gpio function */ + tls_reg_write32(HR_GPIO_AF_S1 + offset, tls_reg_read32(HR_GPIO_AF_S1 + offset) | BIT(pin)); + tls_reg_write32(HR_GPIO_AF_S0 + offset, tls_reg_read32(HR_GPIO_AF_S0 + offset) | BIT(pin)); + + return; +} + +static void io_cfg_option5(enum tls_io_name name) +{ + u8 pin; + u16 offset; + + if (name >= WM_IO_PB_00) + { + pin = name - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = name; + offset = 0; + } + + tls_reg_write32(HR_GPIO_AF_SEL + offset, tls_reg_read32(HR_GPIO_AF_SEL + offset) & (~BIT(pin))); /* disable gpio function */ + + return; +} + +static void io_cfg_option6(enum tls_io_name name) +{ + u8 pin; + u16 offset; + + if( ((name >= WM_IO_PB_06)&&(name <= WM_IO_PB_18)) || + ((name >= WM_IO_PB_29)&&(name <= WM_IO_PB_31))) + { + TLS_DBGPRT_IO_ERR("io %u have no option6.\r\n", name); + return; + } + + if (name >= WM_IO_PB_00) + { + pin = name - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = name; + offset = 0; + } + + tls_reg_write32(HR_GPIO_AF_SEL + offset, tls_reg_read32(HR_GPIO_AF_SEL + offset) & (~BIT(pin))); /* disable gpio function */ + tls_reg_write32(HR_GPIO_DIR + offset, tls_reg_read32(HR_GPIO_DIR + offset) & (~BIT(pin))); + tls_reg_write32(HR_GPIO_PULL_EN + offset, tls_reg_read32(HR_GPIO_PULL_EN + offset) & (~BIT(pin))); + + return; +} + +/** + * @brief This function is used to config io function + * + * @param[in] name io name + * @param[in] option io function option, value is WM_IO_OPT*_*, also is WM_IO_OPTION1-6 + * + * @return None + * + * @note None + */ +void tls_io_cfg_set(enum tls_io_name name, u8 option) +{ + if (WM_IO_OPTION1 == option) + io_cfg_option1(name); + else if (WM_IO_OPTION2 == option) + io_cfg_option2(name); + else if (WM_IO_OPTION3 == option) + io_cfg_option3(name); + else if (WM_IO_OPTION4 == option) + io_cfg_option4(name); + else if (WM_IO_OPTION5 == option) + io_cfg_option5(name); + else if (WM_IO_OPTION6 == option) + io_cfg_option6(name); + else + TLS_DBGPRT_IO_ERR("invalid io option.\r\n"); + + return; +} + +/** + * @brief This function is used to get io function config + * + * @param[in] name io name + * + * @retval WM_IO_OPTION1~6 Mapping io function + * + * @note None + */ +int tls_io_cfg_get(enum tls_io_name name) +{ + u8 pin; + u16 offset; + u32 afsel,afs1,afs0,dir,pullen; + + + if (name >= WM_IO_PB_00) + { + pin = name - WM_IO_PB_00; + offset = TLS_IO_AB_OFFSET; + } + else + { + pin = name; + offset = 0; + } + + afsel = tls_reg_read32(HR_GPIO_AF_SEL + offset); + afs1 = tls_reg_read32(HR_GPIO_AF_S1 + offset); + afs0 = tls_reg_read32(HR_GPIO_AF_S0 + offset); + dir = tls_reg_read32(HR_GPIO_DIR + offset); + pullen = tls_reg_read32(HR_GPIO_PULL_EN + offset); + + if(afsel&BIT(pin)) + { + if((0==(afs1&BIT(pin))) && (0==(afs0&BIT(pin)))) + return WM_IO_OPTION1; + else if((0==(afs1&BIT(pin))) && (afs0&BIT(pin))) + return WM_IO_OPTION2; + else if((afs1&BIT(pin)) && (0==(afs0&BIT(pin)))) + return WM_IO_OPTION3; + else if((afs1&BIT(pin)) && (afs0&BIT(pin))) + return WM_IO_OPTION4; + } + else + { + if((!(dir&BIT(pin))) && (pullen&BIT(pin))) + return WM_IO_OPTION6; + else + return WM_IO_OPTION5; + } + + return 0; +} + + diff --git a/sdk/Platform/Drivers/iouart/wm_iouart.c b/sdk/Platform/Drivers/iouart/wm_iouart.c new file mode 100644 index 0000000..a9a4ddf --- /dev/null +++ b/sdk/Platform/Drivers/iouart/wm_iouart.c @@ -0,0 +1,270 @@ +/** + * @file wm_iouart.c + * + * @brief IO uart Driver Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#include +#include "wm_iouart.h" +#include "wm_debug.h" +#include "wm_irq.h" +#include "wm_config.h" +#include "wm_mem.h" +#include "wm_gpio.h" +#include "wm_timer.h" + + +#if 1//TLS_CONFIG_IOUART + +struct tls_io_uart io_uart; + + +#if !IO_UART_FOR_PRINT +void iouart0_timer_cb(void) +{ + int i = 0; + static u8 ch = 0; + +// if(io_uart.ifrx) + { + if (0 == io_uart.bitnum) // Æðʼλ + { + io_uart.bit[io_uart.bitcnt] = tls_gpio_read(IO_UART_RX); + + if (io_uart.bit[io_uart.bitcnt++] != 0) + { + io_uart.bitcnt = 0; + tls_timer_stop(io_uart.timerid); + tls_gpio_irq_enable(IO_UART_RX, WM_GPIO_IRQ_TRIG_LOW_LEVEL); + return; + } + +// if(io_uart.bitcnt == IO_UART_ONEBITE_SAMPLE_NUM) + { + io_uart.bitcnt = 0; + io_uart.bitnum ++; + ch = 0; + } + } + else if (io_uart.bitnum >= 1 && io_uart.bitnum <= 8) // Êý¾Ýλ + { + io_uart.bit[io_uart.bitcnt++] = tls_gpio_read(IO_UART_RX); + + if(io_uart.bitcnt == IO_UART_ONEBITE_SAMPLE_NUM) + { + for(i=0; i<(IO_UART_ONEBITE_SAMPLE_NUM-1); i++) + { + if(io_uart.bit[i] != io_uart.bit[i+1]) + { + io_uart.bitcnt = 0; + io_uart.bitnum = 0; + tls_timer_stop(io_uart.timerid); + tls_gpio_irq_enable(IO_UART_RX, WM_GPIO_IRQ_TRIG_LOW_LEVEL); + return; + } + } + if(io_uart.bit[0]) + { + ch |= (1 << (io_uart.bitnum - 1)); + } + io_uart.bitnum ++; + io_uart.bitcnt = 0; + } + } + else if (9 == io_uart.bitnum) // ֹͣλ + { + io_uart.bit[io_uart.bitcnt++] = tls_gpio_read(IO_UART_RX); + if(io_uart.bitcnt == IO_UART_ONEBITE_SAMPLE_NUM) + { + if(io_uart.bit[i] != io_uart.bit[i+1]) + { + io_uart.bitcnt = 0; + io_uart.bitnum = 0; + tls_timer_stop(io_uart.timerid); + tls_gpio_irq_enable(IO_UART_RX, WM_GPIO_IRQ_TRIG_LOW_LEVEL); + return; + } + } + if(io_uart.bit[0] == 0) + { + io_uart.bitcnt = 0; + io_uart.bitnum = 0; + tls_timer_stop(io_uart.timerid); + tls_gpio_irq_enable(IO_UART_RX, WM_GPIO_IRQ_TRIG_LOW_LEVEL); + return; + } + io_uart.recv.buf[io_uart.recv.head] = ch; + io_uart.recv.head =(io_uart.recv.head + 1) & (TLS_IO_UART_RX_BUF_SIZE -1); + io_uart.bitnum = 0; + io_uart.bitcnt = 0; + ch = 0; + io_uart.ifrx = 0; + tls_timer_stop(io_uart.timerid); + tls_gpio_irq_enable(IO_UART_RX, WM_GPIO_IRQ_TRIG_LOW_LEVEL); + } + } +} + +void iouart0_gpio_isr_callback(void *context) +{ + u16 ret; + + ret = tls_get_gpio_irq_status(IO_UART_RX); + if(ret) + { + tls_clr_gpio_irq_status(IO_UART_RX); + ret = tls_gpio_read(IO_UART_RX); + if(ret == 0) + { + io_uart.ifrx = 1; + tls_gpio_irq_disable(IO_UART_RX); + tls_timer_start(io_uart.timerid); + } + } +} +#endif + +void iouart_tx_byte(u8 datatoSend) +{ + u8 i, tmp; + u32 cpu_sr = 0; + + cpu_sr = tls_os_set_critical(); // ·¢ËÍÒ»¸öbyteµÄ¹ý³ÌÖв»Äܱ»´ò¶Ï£¬·ñÔò¿ÉÄÜ»áÓдíÎóÂë +/* Start bit */ + tls_gpio_write(IO_UART_TX, 0); + tls_delay_via_timer(io_uart.timercnt+1, 0); + + for (i = 0; i < 8; i++) + { + tmp = (datatoSend >> i) & 0x01; + + if (tmp == 0) + { + tls_gpio_write(IO_UART_TX, 0); + } + else + { + tls_gpio_write(IO_UART_TX, 1); + } + + tls_delay_via_timer(io_uart.timercnt+1, 0); + } + + tls_gpio_write(IO_UART_TX, 1); + tls_delay_via_timer(io_uart.timercnt+1, 0); + + tls_os_release_critical(cpu_sr); +} + +int tls_iouart_init(int bandrate) +{ + char *bufrx; + + memset(&io_uart, 0, sizeof(struct tls_io_uart)); +#if !IO_UART_FOR_PRINT + bufrx = tls_mem_alloc(TLS_IO_UART_RX_BUF_SIZE); + if (!bufrx) + return WM_FAILED; + memset(bufrx, 0, TLS_IO_UART_RX_BUF_SIZE); + io_uart.recv.buf = (u8 *) bufrx; + io_uart.recv.head = 0; + io_uart.recv.tail = 0; +#endif + + tls_gpio_cfg(IO_UART_TX, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_FLOATING); + tls_gpio_write(IO_UART_TX, 1); + + tls_gpio_cfg(IO_UART_RX, WM_GPIO_DIR_INPUT, WM_GPIO_ATTR_PULLHIGH); + + tls_gpio_isr_register(IO_UART_RX, iouart0_gpio_isr_callback, NULL); + tls_gpio_irq_enable(IO_UART_RX, WM_GPIO_IRQ_TRIG_LOW_LEVEL); + + io_uart.timercnt = 1000000 / bandrate; +#if !IO_UART_FOR_PRINT + struct tls_timer_cfg timer_cfg; + timer_cfg.unit = TLS_TIMER_UNIT_US; + timer_cfg.timeout = io_uart.timercnt/IO_UART_ONEBITE_SAMPLE_NUM; + timer_cfg.is_repeat = 1; + timer_cfg.callback = (tls_timer_irq_callback)iouart0_timer_cb; + timer_cfg.arg = NULL; + io_uart.timerid = tls_timer_create(&timer_cfg); +#endif + return WM_SUCCESS; +} + +int tls_iouart_destroy(void) +{ + tls_gpio_irq_disable(IO_UART_RX); + tls_timer_destroy(io_uart.timerid); + io_uart.timerid = 0xFF; + tls_mem_free(io_uart.recv.buf); + return WM_SUCCESS; +} + +int tls_iouart_read(u8 * buf, int bufsize) +{ + int data_cnt, buflen, bufcopylen; + + if (NULL == buf) + return WM_FAILED; + + data_cnt = + CIRC_CNT(io_uart.recv.head, io_uart.recv.tail, TLS_IO_UART_RX_BUF_SIZE); + + if (data_cnt >= bufsize) + { + buflen = bufsize; + } + else + { + buflen = data_cnt; + } + if ((io_uart.recv.tail + buflen) > TLS_IO_UART_RX_BUF_SIZE) + { + bufcopylen = (TLS_IO_UART_RX_BUF_SIZE - io_uart.recv.tail); + MEMCPY(buf, io_uart.recv.buf + io_uart.recv.tail, bufcopylen); + MEMCPY(buf + bufcopylen, io_uart.recv.buf, buflen - bufcopylen); + } + else + { + MEMCPY(buf, io_uart.recv.buf + io_uart.recv.tail, buflen); + } + io_uart.recv.tail = (io_uart.recv.tail + buflen) & (TLS_IO_UART_RX_BUF_SIZE - 1); + return buflen; +} + + +int tls_iouart_write(u8 * buf, int bufsize) +{ + if (NULL == buf || bufsize <= 0 || 1 == io_uart.ifrx) + return WM_FAILED; + + io_uart.iftx = 1; + + while (bufsize) + { + iouart_tx_byte(*buf); + bufsize--; + buf++; + } + + io_uart.iftx = 0; + + return WM_SUCCESS; +} + +int tls_iouart_output_char(int ch) +{ + if(ch == '\n') + iouart_tx_byte('\r'); + iouart_tx_byte((char)ch); + + return ch; +} + +#endif //TLS_CONFIG_IOUART + diff --git a/sdk/Platform/Drivers/irq/wm_irq.c b/sdk/Platform/Drivers/irq/wm_irq.c new file mode 100644 index 0000000..2dd1d95 --- /dev/null +++ b/sdk/Platform/Drivers/irq/wm_irq.c @@ -0,0 +1,149 @@ +/** + * @file wm_irq.c + * + * @brief interupt driver module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#include +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_config.h" +#include "wm_mem.h" +void tls_irq_handler_dummy(void *data) +{ +} + + +tls_irq_handler_t *intr_handler_vect[INTR_CNT]; + +u8 intr_counter; + +/** + * @brief This function is used to initial system interrupt. + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_irq_init(void) +{ +#if 0 + int i; + + for (i = 0; i < INTR_CNT; i++) + { + intr_handler_vect[i].handler = tls_irq_handler_dummy; + intr_handler_vect[i].data = NULL; + intr_handler_vect[i].name = "intr_dummy"; + intr_handler_vect[i].counter = 0; + } +#endif + intr_counter = 0; + +/* initailize VIC */ + tls_reg_write32(HR_VIC_INT_SELECT, 0); + tls_reg_write32(HR_VIC_VECT_ENABLE, 0); /* disable vector interrupt */ + tls_reg_write32(HR_VIC_INT_EN_CLR, 0xFFFFFFFF); +} + + +/** + * @brief This function is used to register interrupt. + * + * @param[in] vec_no interrupt no + * @param[in] handler + * @param[in] *data + * + * @return None + * + * @note None + */ +void tls_irq_register_handler(u8 vec_no, intr_handler_func handler, void *data) +{ +// ASSERT (vec_no >= 0 && vec_no <= 0x1f); + if (NULL == intr_handler_vect[vec_no]) + { + intr_handler_vect[vec_no] = tls_mem_alloc(sizeof(tls_irq_handler_t)); + } + if (intr_handler_vect[vec_no]) + { + intr_handler_vect[vec_no]->handler = handler; + intr_handler_vect[vec_no]->data = data; + // intr_handler_vect[vec_no].name = NULL; + intr_handler_vect[vec_no]->counter = 0; + } + +} + +extern void NVIC_Configration(int irqno, FunctionalState irqstatus); + +/** + * @brief This function is used to enable interrupt. + * + * @param[in] vec_no interrupt no + * + * @return None + * + * @note None + */ +void tls_irq_enable(u8 vec_no) +{ + NVIC_ClearPendingIRQ((IRQn_Type)vec_no); + NVIC_Configration(vec_no, ENABLE); +} + +/** + * @brief This function is used to disable interrupt. + * + * @param[in] vec_no interrupt no + * + * @return None + * + * @note None + */ +void tls_irq_disable(u8 vec_no) +{ + NVIC_Configration(vec_no, DISABLE); + NVIC_ClearPendingIRQ((IRQn_Type)vec_no); +} + +/** + * @brief This function is used to set interrupt priority. + * + * @param[in] vec_no interrupt no + * @param[in] priority priority + * + * @return None + * + * @note None + */ +void tls_irq_set_priority(u8 vec_no, u8 priority) +{ + NVIC_SetPriority(vec_no, priority); +} + +void OS_CPU_IRQ_ISR_Handler(void) +{ + u32 irq_status = tls_reg_read32(HR_VIC_IRQ_STATUS); + int i = 0; + intr_counter++; + + for (i = 0; i < INTR_CNT; i++) + { + if (irq_status & (1UL << i)) + { + intr_handler_vect[i]->handler((void *) intr_handler_vect[i]->data); + intr_handler_vect[i]->counter++; + } + } + intr_counter--; +/* clear interrupt */ + tls_reg_write32(HR_VIC_VECT_ADDR, 0); +} + diff --git a/sdk/Platform/Drivers/lcd/wm_lcd.c b/sdk/Platform/Drivers/lcd/wm_lcd.c new file mode 100644 index 0000000..08881c8 --- /dev/null +++ b/sdk/Platform/Drivers/lcd/wm_lcd.c @@ -0,0 +1,187 @@ +/**************************************************************************//** + * @file wm_lcd.c + * @author + * @version + * @date + * @brief + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. All rights reserved. + *****************************************************************************/ + +#include "wm_lcd.h" + +#define RTC_CLK (32000UL) + +/** + * @brief Initialize LCD Frame Counter + * @param[in] freq LCD reference refresh frequency in Hz that will be used + */ +void tls_lcd_fresh_ratio(uint16_t freq) +{ + uint8_t com_num; + + if (freq == 0) + { + freq = 60; + } + + com_num = tls_bitband_read(HR_LCD_CR, LCD_CR_MODE_Pos) ? 4 : 8; + LCD->FRAMECNT = RTC_CLK/(com_num * freq); +} + +/** + * @brief + * Turn on or clear a segment + * + * @param[in] com + * Which COM line to update + * + * @param[in] bit + * Bit index of which field to change + * + * @param[in] enable + * When one will set segment, when zero will clear segment + * + * @param[in] mode + * when one max configuration is 4x20, when zero max configuration is 8x16 + * + */ +void tls_lcd_seg_set(int com, int bit, int on_off) +{ + int mode; + + /** get the mode congfig when one max configuration is 4x20, when zero max configuration is 8x16 */ + mode = tls_bitband_read(HR_LCD_CR, LCD_CR_MODE_Pos); + + if (!mode && (com < 0 || com > 7) && (bit < 0 || bit > 15)) + { + return ; + } + if (mode && (com < 0 || com > 3) && (bit < 0 || bit > 19)) + { + return ; + } + + switch(com) + { + /** COM0 */ + case 0: + tls_bitband_write(HR_LCD_COM0_1_SEG, bit, on_off); + break; + + /** COM1 */ + case 1: + if (mode) + tls_bitband_write(HR_LCD_COM2_3_SEG, bit, on_off); + else + { + bit += 16; + tls_bitband_write(HR_LCD_COM0_1_SEG, bit, on_off); + } + break; + + /** COM2 */ + case 2: + if (mode) + tls_bitband_write(HR_LCD_COM4_5_SEG, bit, on_off); + else + tls_bitband_write(HR_LCD_COM2_3_SEG, bit, on_off); + break; + + /** COM3 */ + case 3: + if (mode) + tls_bitband_write(HR_LCD_COM6_7_SEG, bit, on_off); + else + { + bit += 16; + tls_bitband_write(HR_LCD_COM2_3_SEG, bit, on_off); + } + break; + + /** COM4 */ + case 4: + tls_bitband_write(HR_LCD_COM4_5_SEG, bit, on_off); + break; + + /** COM5 */ + case 5: + bit += 16; + tls_bitband_write(HR_LCD_COM4_5_SEG, bit, on_off); + break; + + /** COM6 */ + case 6: + tls_bitband_write(HR_LCD_COM6_7_SEG, bit, on_off); + break; + + /** COM7 */ + case 7: + bit += 16; + tls_bitband_write(HR_LCD_COM6_7_SEG, bit, on_off); + break; + + default: + break; + } +} + +/** + * @brief enable or disable the LCD module gpio + * + * @param[in] the config value per bit for one of LCD gpios, set 1 gpio output enable ,0 output disabled + * + */ +void tls_lcd_io_config(unsigned int val) +{ + tls_reg_write32(HR_LCD_IO_EN, val); +} + +/** + * @brief + * select the voltage of LCD module + * + */ +void tls_lcd_vlcd_sel(LCD_VlcdDef vlcd) +{ + LCD->CTRL &= ~LCD_VLCD_MASK; + LCD->CTRL |= vlcd; +} + + +/** + * @brief + * set the duty of LCD module + * + */ +void tls_lcd_duty_set(LCD_DutyDef duty) +{ + LCD->CTRL &= ~LCD_DUTY_MASK; + LCD->CTRL |= duty; +} + + +/** + * @brief + * set the bias of LCD module + * + */ +void tls_lcd_bias_set(LCD_BiasDef bias) +{ + LCD->CTRL &= ~LCD_BIAS_MASK; + LCD->CTRL |= bias; +} + +/** + * @brief + * initialize the lcd module + * + */ +void tls_lcd_init(tls_lcd_options_t *opts) +{ + LCD->CTRL = 0; + LCD->CTRL = opts->mode | opts->bias | opts->duty | opts->vlcd | (1 << 12); + tls_lcd_fresh_ratio(opts->fresh_rate); + TLS_LCD_ENABLE(opts->enable); + TLS_LCD_POWERDOWM(0); +} diff --git a/sdk/Platform/Drivers/pmu/wm_pmu.c b/sdk/Platform/Drivers/pmu/wm_pmu.c new file mode 100644 index 0000000..9b0c7e1 --- /dev/null +++ b/sdk/Platform/Drivers/pmu/wm_pmu.c @@ -0,0 +1,332 @@ +#include +#include "wm_debug.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_pwm.h" +#include "wm_gpio.h" +#include "wm_timer.h" +#include "wm_cpu.h" +#include "tls_common.h" +#include "wm_pmu.h" +#include "wm_wifi.h" + +struct pmu_irq_context { + tls_pmu_irq_callback callback; + void *arg; +}; + +static struct pmu_irq_context pmu_timer1_context = {0}; +static struct pmu_irq_context pmu_timer0_context = {0}; +static struct pmu_irq_context pmu_gpio_wake_context = {0}; +static struct pmu_irq_context pmu_sdio_wake_context = {0}; + + +void PMU_TIMER1_IRQHandler(void) +{ + if (tls_reg_read32(HR_PMU_INTERRUPT_SRC) & BIT(1)) /* timer1 interrupt */ + { + tls_reg_write32(HR_PMU_INTERRUPT_SRC, tls_reg_read32(HR_PMU_INTERRUPT_SRC) | BIT(1)); /* clear timer1 interrupt */ + + if (NULL != pmu_timer1_context.callback) + pmu_timer1_context.callback(pmu_timer1_context.arg); + } + return; +} + +void PMU_TIMER0_IRQHandler(void) +{ + if (tls_reg_read32(HR_PMU_INTERRUPT_SRC) & BIT(0)) /* timer0 interrupt */ + { + tls_reg_write32(HR_PMU_INTERRUPT_SRC, tls_reg_read32(HR_PMU_INTERRUPT_SRC) | BIT(0)); /* clear timer0 interrupt */ + tls_reg_write32(HR_PMU_TIMER0, tls_reg_read32(HR_PMU_TIMER0) & (~BIT(16))); + + if (NULL != pmu_timer0_context.callback) + pmu_timer0_context.callback(pmu_timer0_context.arg); + } + return; +} + +void PMU_GPIO_WAKE_IRQHandler(void) +{ + if (tls_reg_read32(HR_PMU_INTERRUPT_SRC) & BIT(2)) /* gpio wake interrupt */ + { + tls_reg_write32(HR_PMU_INTERRUPT_SRC, tls_reg_read32(HR_PMU_INTERRUPT_SRC) | BIT(2)); /* clear gpio wake interrupt */ + + if (NULL != pmu_gpio_wake_context.callback) + pmu_gpio_wake_context.callback(pmu_gpio_wake_context.arg); + } + return; +} + +void PMU_SDIO_WAKE_IRQHandler(void) +{ + if (tls_reg_read32(HR_PMU_INTERRUPT_SRC) & BIT(3)) /* sdio wake interrupt */ + { + tls_reg_write32(HR_PMU_INTERRUPT_SRC, tls_reg_read32(HR_PMU_INTERRUPT_SRC) | BIT(3)); /* clear sdio wake interrupt */ + + if (NULL != pmu_sdio_wake_context.callback) + pmu_sdio_wake_context.callback(pmu_sdio_wake_context.arg); + } + return; +} + + +/** + * @brief This function is used to register pmu timer1 interrupt + * + * @param[in] callback the pmu timer1 interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * pmu timer1 callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_pmu_timer1_isr_register(tls_pmu_irq_callback callback, void *arg) +{ + pmu_timer1_context.callback = callback; + pmu_timer1_context.arg = arg; + + tls_irq_enable(PMU_TIMER1_INT); + + return; +} + + +/** + * @brief This function is used to register pmu timer0 interrupt + * + * @param[in] callback the pmu timer0 interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * pmu timer0 callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_pmu_timer0_isr_register(tls_pmu_irq_callback callback, void *arg) +{ + pmu_timer0_context.callback = callback; + pmu_timer0_context.arg = arg; + + tls_irq_enable(PMU_TIMER0_INT); + + return; +} + + +/** + * @brief This function is used to register pmu gpio interrupt + * + * @param[in] callback the pmu gpio interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * pmu gpio callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_pmu_gpio_isr_register(tls_pmu_irq_callback callback, void *arg) +{ + pmu_gpio_wake_context.callback = callback; + pmu_gpio_wake_context.arg = arg; + + tls_irq_enable(PMU_GPIO_WAKEUP_INT); + + return; +} + + +/** + * @brief This function is used to register pmu sdio interrupt + * + * @param[in] callback the pmu sdio interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * pmu sdio callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_pmu_sdio_isr_register(tls_pmu_irq_callback callback, void *arg) +{ + pmu_sdio_wake_context.callback = callback; + pmu_sdio_wake_context.arg = arg; + + tls_irq_enable(PMU_SDIO_WAKEUP_INT); + + return; +} + +/** + * @brief This function is used to select pmu clk + * + * @param[in] bypass pmu clk whether or not use bypass mode + * ohter pmu clk use 32K by 40MHZ + * 0 pmu clk 32K by calibration circuit + * + * @return None + * + * @note None + */ +void tls_pmu_clk_select(u8 bypass) +{ + u32 val; + + val = tls_reg_read32(HR_PMU_PS_CR); + if(bypass) + { + val |= BIT(3); + } + else + { + val &= ~BIT(3); + } + val |= BIT(2); + tls_reg_write32(HR_PMU_PS_CR, val); +} + + +/** + * @brief This function is used to start pmu timer0 + * + * @param[in] second vlaue of timer0 count[s] + * + * @return None + * + * @note None + */ +void tls_pmu_timer0_start(u16 second) +{ + u32 val; + + val = second; + val |= BIT(16); + tls_reg_write32(HR_PMU_TIMER0, val); +} + + +/** + * @brief This function is used to stop pmu timer0 + * + * @param None + * + * @return None + * + * @note None + */ +void tls_pmu_timer0_stop(void) +{ + u32 val; + + val = tls_reg_read32(HR_PMU_TIMER0); + val &= ~BIT(16); + tls_reg_write32(HR_PMU_TIMER0, val); +} + + + +/** + * @brief This function is used to start pmu timer1 + * + * @param[in] second vlaue of timer1 count[ms] + * + * @return None + * + * @note None + */ +void tls_pmu_timer1_start(u16 msec) +{ + u32 val; + //ĬÈϲÉÓÃ×îСµ¥Î»1ms + val = (msec-1) | (1<<16) | (1<<17) | (0<<20) | (0<<24); + tls_reg_write32(HR_PMU_TIMER1, val); +} + + +/** + * @brief This function is used to stop pmu timer1 + * + * @param None + * + * @return None + * + * @note None + */ +void tls_pmu_timer1_stop(void) +{ + u32 val; + val = tls_reg_read32(HR_PMU_TIMER1); + val &= ~BIT(16); + val &= ~BIT(17); + tls_reg_write32(HR_PMU_TIMER1, val); +} + + + +/** + * @brief This function is used to start pmu goto standby + * + * @param None + * + * @return None + * + * @note None + */ +void tls_pmu_standby_start(void) +{ + u32 val; + + tls_irq_enable(PMU_GPIO_WAKEUP_INT); //ĬÈÏ´ò¿ªÖжÏΪÁËÇå³þIO»½ÐѵÄÖжϱê¼Ç + + val = tls_reg_read32(HR_PMU_PS_CR); + TLS_DBGPRT_INFO("goto standby here\n"); + val |= BIT(0); + tls_reg_write32(HR_PMU_PS_CR, val); +} + +/** + * @brief This function is used to close peripheral's clock + * + * @param[in] devices peripherals + * + * @return None + * + * @note None + */ +void tls_close_peripheral_clock(tls_peripheral_type_s devices) +{ + tls_reg_write32(HR_CLK_BASE_ADDR, tls_reg_read32(HR_CLK_BASE_ADDR) & ~(devices)); + + return; +} + +/** + * @brief This function is used to open peripheral's clock + * + * @param[in] devices peripherals + * + * @return None + * + * @note None + */ +void tls_open_peripheral_clock(tls_peripheral_type_s devices) +{ + tls_reg_write32(HR_CLK_BASE_ADDR, tls_reg_read32(HR_CLK_BASE_ADDR) | devices); + + return; +} + + diff --git a/sdk/Platform/Drivers/pwm/wm_pwm.c b/sdk/Platform/Drivers/pwm/wm_pwm.c new file mode 100644 index 0000000..4851b38 --- /dev/null +++ b/sdk/Platform/Drivers/pwm/wm_pwm.c @@ -0,0 +1,919 @@ +/** + * @file wm_pwm.c + * + * @brief pwm driver module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#include + +#include "wm_debug.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_pwm.h" +#include "wm_gpio.h" +#include "wm_cpu.h" +#include "tls_common.h" + + +typedef void (*pwm_irq_callback)(void); +static pwm_irq_callback pwm_callback; + +void PWM_IRQHandler(void) +{ + if (pwm_callback) + pwm_callback(); +} + +/** + * @brief This function is used to register the pwm interrupt callback function + * + * @param[in] callback the pwm interrupt callback function + * + * @return None + * + * @note None + */ +void tls_pwm_isr_register(void (*callback)(void)) +{ + pwm_callback = callback; + tls_irq_enable(PWM_INT); +} + +/** + * @brief This function is used to set duty ratio + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] duty Number of active levels + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_duty_config(u8 channel, u8 duty) +{ + u32 temp = 0; + + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + { + TLS_DBGPRT_ERR("duty param err\n"); + return WM_FAILED; + } + if (duty == 0) + { + tls_pwm_stop(channel); + return WM_SUCCESS; + } + + if (4 == channel) + { + temp = tls_reg_read32(HR_PWM_CH4_REG2) & ~0x0000FF00; + temp |= (duty << 8); + tls_reg_write32(HR_PWM_CH4_REG2, temp); /* duty radio */ + } + else + { + temp = tls_reg_read32(HR_PWM_CMPDAT) & ~(0xFF << channel * 8); + temp |= (duty << (channel * 8)); + tls_reg_write32(HR_PWM_CMPDAT, temp); /* duty radio */ + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set frequency + * + * @param[in] channel pwm channel NO., range form 0 to 4 + * @param[in] clkdiv clock divider, range 0 to 65535 + * @param[in] period the number of the counting clock cycle + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_freq_config(u8 channel, u16 clkdiv, u8 period) +{ + u32 temp = 0; + + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + { + TLS_DBGPRT_ERR("freq param err\n"); + return WM_FAILED; + } + + if (4 == channel) + { + temp = tls_reg_read32(HR_PWM_CH4_REG1) & ~0xFFFF0000; + temp |= (clkdiv << 16); + tls_reg_write32(HR_PWM_CH4_REG1, temp);/* clock divider */ + + temp = tls_reg_read32(HR_PWM_CH4_REG1) & ~0x0000FF00; + temp |= (period << 8); + tls_reg_write32(HR_PWM_CH4_REG1, temp); /* the number of the counting clock cycle */ + } + else + { + temp = tls_reg_read32(HR_PWM_CLKDIV01 + (channel / 2) * 4) & ~(0xFFFF << ((channel % 2) * 16)); + temp |= (clkdiv << ((channel % 2) * 16)); + tls_reg_write32(HR_PWM_CLKDIV01 + (channel / 2) * 4, temp);/* clock divider */ + + temp = tls_reg_read32(HR_PWM_PERIOD) & ~(0xFF << channel * 8); + temp |= (period << (channel * 8)); + tls_reg_write32(HR_PWM_PERIOD, temp);/* the number of the counting clock cycle */ + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set the output mode + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] mode pwm work mode for signal generate + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_out_mode_config(u8 channel, enum tls_pwm_out_mode mode) +{ + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + if (WM_PWM_OUT_MODE_BRAKE == mode) + tls_reg_write32(HR_PWM_BRKCTL, tls_reg_read32(HR_PWM_BRKCTL) | BIT(11 + channel));/* the brake mode */ + else if (WM_PWM_OUT_MODE_ALLSYC == mode) + { + if (channel != 0) + return WM_FAILED; + tls_reg_write32(HR_PWM_BRKCTL, tls_reg_read32(HR_PWM_BRKCTL) & ~0xF800); /* disable the brake mode */ + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(6)); /* enable the all synchronous mode mode */ + } + else if (WM_PWM_OUT_MODE_2SYC == mode) + { + if (channel != 0 && channel != 2) + return WM_FAILED; + tls_reg_write32(HR_PWM_BRKCTL, tls_reg_read32(HR_PWM_BRKCTL) & ~(0x1800< (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + if (4 == channel) + { + if (WM_PWM_CNT_TYPE_EDGE_ALLGN_CAP == cnt_type) + { + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) & (~BIT(4))); + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) & (~BIT(3))); + } + if (WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT == cnt_type) + { + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) & (~BIT(4))); + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) | BIT(3)); + } + else if (WM_PWM_CNT_TYPE_CENTER_ALIGN == cnt_type) + { + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) | BIT(4)); + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) & (~BIT(3))); + } + + } + else + { + if (WM_PWM_CNT_TYPE_EDGE_ALLGN_CAP == cnt_type && channel == 0) + { + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(17))); + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(16))); + } + if (WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT == cnt_type) + { + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(17 + channel * 2))); + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(16 + channel * 2)); + } + else if (WM_PWM_CNT_TYPE_CENTER_ALIGN == cnt_type) + { + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(17 + channel * 2)); + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(16 + channel * 2))); + } + else + return WM_FAILED; + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set whether to loop + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] loop_mode whether to loop + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_loop_mode_config(u8 channel, enum tls_pwm_loop_type loop_mode) +{ + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + if (4 == channel) + { + if (WM_PWM_LOOP_TYPE_LOOP == loop_mode) + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) | BIT(1)); + else + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) & (~BIT(1))); + } + else + { + if (WM_PWM_LOOP_TYPE_LOOP == loop_mode) + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(8 + channel)); + else + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(8 + channel))); + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set whether to inverse the output + + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] en ENABLE or DISABLE + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_out_inverse_cmd(u8 channel, bool en) +{ + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + if (4 == channel) + { + if (ENABLE == en) + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) | BIT(0)); + else + tls_reg_write32(HR_PWM_CH4_REG2, tls_reg_read32(HR_PWM_CH4_REG2) & (~BIT(0))); + } + else + { + if (ENABLE == en) + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(2 + channel)); + else + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(2 + channel))); + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set the number of period to be generated + * + * @param[in] channel pwm channel NO.,range form 0 to 4 + * @param[in] pnum the number of period to be generated,range from 0 to 255 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_stoptime_by_period_config(u8 channel, u8 pnum) +{ + u32 temp = 0; + + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + if (4 == channel) + { + temp = tls_reg_read32(HR_PWM_CH4_REG1) & ~0x000000FF; + temp |= pnum; + tls_reg_write32(HR_PWM_CH4_REG1, temp); + } + else + { + temp = tls_reg_read32(HR_PWM_PNUM) & ~(0xFF << channel * 8); + temp |= (pnum << (channel * 8)); + tls_reg_write32(HR_PWM_PNUM, temp); + + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set output enable + * + * @param[in] channel pwm channel NO.,channel 0 or channel 4 + * @param[in] en ENABLE or DISABLE + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_output_en_cmd(u8 channel, bool en) +{ + if(channel != 0 && channel != 4) + return WM_FAILED; + + if (4 == channel) + { + if (ENABLE == en) + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(2))); + else + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) | BIT(2)); + } + else + { + if (ENABLE == en) + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(12))); + else + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(12)); + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set the dead time + * + * @param[in] channel pwm channel NO.,channel 0 or channel 2 + * @param[in] dten whether enalbe the deat time, ENABLE or DISABLE + * @param[in] dtclkdiv dead zone clock divider, range 0 to 3 + * @param[in] dtcnt the number of the counting clock cycle, range 0 to 255 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_deadzone_config(u8 channel, bool dten, u8 dtclkdiv, u8 dtcnt) +{ + u32 temp = 0; + + if ((channel !=0 && channel != 2) || dtclkdiv > 3) + return WM_FAILED; + + if(ENABLE == dten) + { + temp = tls_reg_read32(HR_PWM_DTCTL) & ~0x00030000; + temp |= (dtclkdiv<<16); + tls_reg_write32(HR_PWM_DTCTL, temp);/* dead zone clock divider */ + + if (channel == 0 || channel == 1) + { + temp = tls_reg_read32(HR_PWM_DTCTL) & ~0x000000FF; + temp |= dtcnt; + tls_reg_write32(HR_PWM_DTCTL, temp);/* the number of the counting clock cycle */ + + tls_reg_write32(HR_PWM_DTCTL, tls_reg_read32(HR_PWM_CTL) | BIT(20)); /* whether enalbe the deat time */ + + } + else if (channel == 2 || channel == 3) + { + temp = tls_reg_read32(HR_PWM_DTCTL) & ~0x0000FF00; + temp |= (dtcnt<<8); + tls_reg_write32(HR_PWM_DTCTL, temp);/* the number of the counting clock cycle */ + + tls_reg_write32(HR_PWM_DTCTL, tls_reg_read32(HR_PWM_CTL) | BIT(21)); /* whether enalbe the deat time */ + } + } + else + { + if (channel == 0 || channel == 1) + { + tls_reg_write32(HR_PWM_DTCTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(20))); /* whether enalbe the deat time */ + } + else if (channel == 2 || channel == 3) + { + tls_reg_write32(HR_PWM_DTCTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(21))); /* whether enalbe the deat time */ + } + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set whether to inverse the capture input + * + * @param[in] channel pwm channel NO.,channel 0 or channel 4 + * @param[in] en ENABLE or DISABLE + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_capture_inverse_cmd(u8 channel, bool en) +{ + if (channel != 0 && channel != 4) + return WM_FAILED; + + if (channel == 0) + { + if (ENABLE == en) + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(25)); + else + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(25))); + } + else + { + if (ENABLE == en) + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) | BIT(0)); + else + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(0))); + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set break mode + * + * @param[in] channel pwm channel NO.,channel 0 or channel 4 + * @param[in] en whether enable the break mode,ENABLE or DISABLE + * @param[in] brok when break + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_brake_mode_config(u8 channel, bool en, enum tls_pwm_brake_out_level brok) +{ + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + if (ENABLE == en) + { + if (WM_PWM_BRAKE_OUT_HIGH == brok) + tls_reg_write32(HR_PWM_BRKCTL, tls_reg_read32(HR_PWM_BRKCTL) | BIT(3+channel)); + else + tls_reg_write32(HR_PWM_BRKCTL, tls_reg_read32(HR_PWM_BRKCTL) & (~BIT(3+channel))); + tls_reg_write32(HR_PWM_BRKCTL, tls_reg_read32(HR_PWM_BRKCTL) | BIT(11+channel)); + } + else + { + tls_reg_write32(HR_PWM_BRKCTL, tls_reg_read32(HR_PWM_BRKCTL) & (~BIT(11+channel))); + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to enable the capture mode + * + * @param[in] channel pwm channel NO.,channel 0 or channel 4 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_capture_mode_config(u8 channel) +{ + if (channel != 0 && channel != 4) + return WM_FAILED; + if (channel == 0) + { + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(24)); + } + else + { + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) | BIT(1)); + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set the interrupt about the number of period + * + * @param[in] channel pwm channel,range from 0 to 4 + * @param[in] en enble or disable + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_stoptime_irq_cmd(u8 channel, bool en) +{ + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + if (4 == channel) + { + if (en) + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) | BIT(4)); + else + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(4))); + } + else + { + if (en) + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) | BIT(channel)); + else + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(channel))); + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set the interrupt about the + capture + * + * @param[in] channel pwm channel,channel 0 or channel 4 + * @param[in] int_type interrupt type + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_capture_irq_type_config(u8 channel, enum tls_pwm_cap_int_type int_type) +{ + if (channel != 0 && channel != 4) + return WM_FAILED; + + if (0 == channel) + { + if (WM_PWM_CAP_RISING_FALLING_EDGE_INT == int_type) + { + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) | BIT(5)); + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) | BIT(6)); + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(7))); + } + else if (WM_PWM_CAP_RISING_EDGE_INT == int_type) + { + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) | BIT(5)); + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(6))); + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(7))); + } + else if (WM_PWM_CAP_FALLING_EDGE_INT == int_type) + { + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) | BIT(6)); + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(5))); + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(7))); + } + else if(WM_PWM_CAP_DMA_INT == int_type) + { + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) | BIT(7)); + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(5))); + tls_reg_write32(HR_PWM_INTEN, tls_reg_read32(HR_PWM_INTEN) & (~BIT(6))); + } + } + else if (4 == channel) + { + if (WM_PWM_CAP_RISING_FALLING_EDGE_INT == int_type) + { + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) | BIT(8)); + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) | BIT(9)); + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(10))); + } + else if (WM_PWM_CAP_RISING_EDGE_INT == int_type) + { + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) | BIT(8)); + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(9))); + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(10))); + } + else if (WM_PWM_CAP_FALLING_EDGE_INT == int_type) + { + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) | BIT(9)); + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(8))); + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(10))); + } + else if(WM_PWM_CAP_DMA_INT == int_type) + { + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) | BIT(10)); + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(8))); + tls_reg_write32(HR_PWM_CAP2CTL, tls_reg_read32(HR_PWM_CAP2CTL) & (~BIT(9))); + } + } + + return WM_SUCCESS; +} + +/** + * @brief This function is used to initial pwm(out mode) + * + * @param[in] pwm_param structure containing the initialization parameters + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_out_init(pwm_init_param pwm_param) +{ + int ret=0; + int pwm_status = 0; + + if (pwm_param.channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + if(tls_reg_read32(HR_PWM_CTL) & BIT(27 + pwm_param.channel)) + { + tls_pwm_stop(pwm_param.channel); + pwm_status = 1; + } + + /* set output mode */ + ret = tls_pwm_out_mode_config(pwm_param.channel, pwm_param.mode); + if (ret!=WM_SUCCESS) + return WM_FAILED; + + if (WM_PWM_OUT_MODE_MC == pwm_param.mode) + { + /* set dead time */ + ret = tls_pwm_deadzone_config(pwm_param.channel, pwm_param.dten, pwm_param.dtclkdiv, pwm_param.dtcnt); + if (ret!=WM_SUCCESS) + return WM_FAILED; + } + + /* set count type */ + tls_pwm_cnt_type_config(pwm_param.channel, pwm_param.cnt_type); + + /* set period value and duty radio */ + tls_pwm_freq_config(pwm_param.channel, pwm_param.clkdiv, pwm_param.period); + tls_pwm_duty_config(pwm_param.channel, pwm_param.duty); + + /* set cycle type */ + tls_pwm_loop_mode_config(pwm_param.channel, pwm_param.loop_type); + + /* set output whether is inverse */ + tls_pwm_out_inverse_cmd(pwm_param.channel, pwm_param.inverse_en); + + /* set period number of generating */ + tls_pwm_stoptime_by_period_config(pwm_param.channel, pwm_param.pnum); + + /* set interrupt of period number whether is enable */ + tls_pwm_stoptime_irq_cmd(pwm_param.channel, pwm_param.pnum_int); + + /* set output status */ + if (pwm_param.channel == 0 || pwm_param.channel == 4) + tls_pwm_output_en_cmd(pwm_param.channel, WM_PWM_OUT_EN_STATE_OUT); + if (pwm_param.mode == WM_PWM_OUT_MODE_ALLSYC && pwm_param.channel == 0) + tls_pwm_output_en_cmd(4, WM_PWM_OUT_EN_STATE_OUT); + if(pwm_status) + { + tls_pwm_start(pwm_param.channel); + } + return WM_SUCCESS; +} + +/** + * @brief This function is used to initial pwm(capture mode) + * + * @param[in] channel pwm channel, channel 0 or channel 4 + * @param[in] clkdiv clock divider, range 0 to 65535 + * @param[in] inverse_en whether the input signal is reversed + * @param[in] int_type interrupt type + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_cap_init(u8 channel, u16 clkdiv, bool inverse_en, enum tls_pwm_cap_int_type int_type) +{ + if (channel != 0 && channel != 4) + return WM_FAILED; + + /* set clock divider and period value */ + tls_pwm_freq_config(channel, clkdiv, 0xFF); + + /* set input of capture mode whether is inverse */ + tls_pwm_capture_inverse_cmd(channel, inverse_en); + + /* set the capture mode */ + tls_pwm_capture_mode_config(channel); + + /* set count type (only edge alignment in the capture mode) */ + tls_pwm_cnt_type_config(channel, WM_PWM_CNT_TYPE_EDGE_ALLGN_CAP); + + /* set output status */ + tls_pwm_output_en_cmd(channel, WM_PWM_OUT_EN_STATE_TRI); + + /* set cycle mode (must be set int the capture mode) */ + tls_pwm_loop_mode_config(channel, WM_PWM_LOOP_TYPE_LOOP); + + /* set interrupt type */ + tls_pwm_capture_irq_type_config(channel, int_type); + + return WM_SUCCESS; +} + +/** + * @brief This function is used to start pwm + * + * @param[in] channel pwm channel, range from 0 to 4 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_start(u8 channel) +{ + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) | BIT(27 + channel)); /* start counter */ + + return WM_SUCCESS; +} + +/** + * @brief This function is used to stop pwm + * + * @param[in] channel pwm channel, range from 0 to 4 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_stop(u8 channel) +{ + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return WM_FAILED; + + tls_reg_write32(HR_PWM_CTL, tls_reg_read32(HR_PWM_CTL) & (~BIT(27 + channel)));/* stop counter */ + + return WM_SUCCESS; +} + +/** + * @brief This function is used to set pwm's frequency + * + * @param[in] channel pwm channel no, range form 0 to 4 + * @param[in] freq frequency, range from 1 to 156250 + * + * @return None + * + * @note None + */ +void tls_pwm_freq_set(u8 channel, u32 freq) +{ + u16 clkdiv=0; + tls_sys_clk sysclk; + + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return; + + tls_sys_clk_get(&sysclk); + + clkdiv = sysclk.apbclk*UNIT_MHZ/256/freq; + tls_pwm_stop(channel); + tls_pwm_freq_config(channel, clkdiv, 255); + tls_pwm_start(channel); +} + +/** + * @brief This function is used to set duty radio + * + * @param[in] channel pwm channel NO., range form 0 to 4 + * @param[in] duty duty radio, range from 0 to 255 + * + * @return None + * + * @note None + */ +void tls_pwm_duty_set(u8 channel, u8 duty) +{ + if(channel > (PWM_CHANNEL_MAX_NUM - 1)) + return; + + tls_pwm_duty_config(channel, duty); +} + +/** + * @brief This function is used to initial pwm + * + * @param[in] channel pwm channel, range from 0 to 4 + * @param[in] freq freq range from 1 to 156250 + * @param[in] duty duty range from 0 to 255 + * @param[in] pnum period num,range from 0 to 255 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_init(u8 channel,u32 freq, u8 duty, u8 pnum) +{ + pwm_init_param pwm_param; + int ret=-1; + tls_sys_clk sysclk; + + tls_sys_clk_get(&sysclk); + + memset(&pwm_param, 0, sizeof(pwm_init_param)); + pwm_param.period = 255; + pwm_param.cnt_type = WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT; + pwm_param.loop_type = WM_PWM_LOOP_TYPE_LOOP; + pwm_param.mode = WM_PWM_OUT_MODE_INDPT; + pwm_param.inverse_en = DISABLE; + pwm_param.pnum = pnum; + pwm_param.pnum_int = DISABLE; + pwm_param.duty = duty; + pwm_param.channel = channel; + pwm_param.clkdiv = sysclk.apbclk*UNIT_MHZ/256/freq; +// printf("clkdiv:%d\n", pwm_param.clkdiv); + ret = tls_pwm_out_init(pwm_param); +// tls_pwm_start(channel); + + return ret; +} + +/** + * @brief This function is used to initial pwm + * + * @param[in] channel pwm channel, range from 0 to 4 + * @param[in] freq freq range from 1 to 156250 + * @param[in] duty duty range from 0 to 255 + * @param[in] pnum period num,range from 0 to 255 + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note None + */ +int tls_pwm_ineverse_init(u8 channel,u32 freq, u8 duty, u8 pnum) +{ + pwm_init_param pwm_param; + int ret=-1; + tls_sys_clk sysclk; + + tls_sys_clk_get(&sysclk); + + memset(&pwm_param, 0, sizeof(pwm_init_param)); + pwm_param.period = 255; + pwm_param.cnt_type = WM_PWM_CNT_TYPE_EDGE_ALIGN_OUT; + pwm_param.loop_type = WM_PWM_LOOP_TYPE_LOOP; + pwm_param.mode = WM_PWM_OUT_MODE_INDPT; + pwm_param.inverse_en = ENABLE; + pwm_param.pnum = pnum; + pwm_param.pnum_int = DISABLE; + pwm_param.duty = duty; + pwm_param.channel = channel; + pwm_param.clkdiv = sysclk.apbclk*UNIT_MHZ/256/freq; +// printf("clkdiv:%d\n", pwm_param.clkdiv); + ret = tls_pwm_out_init(pwm_param); +// tls_pwm_start(channel); + + return ret; +} diff --git a/sdk/Platform/Drivers/rtc/wm_rtc.c b/sdk/Platform/Drivers/rtc/wm_rtc.c new file mode 100644 index 0000000..16c6a2d --- /dev/null +++ b/sdk/Platform/Drivers/rtc/wm_rtc.c @@ -0,0 +1,170 @@ +/** + * @file wm_rtc.c + * + * @brief rtc Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#include +#include +#include +#include "wm_regs.h" +#include "wm_rtc.h" + +#include "wm_irq.h" +#include "tls_common.h" + +struct rtc_irq_context { + tls_rtc_irq_callback callback; + void *arg; +}; + +static struct rtc_irq_context rtc_context = {0}; + + +/** + * @brief This function is used to set pmu rtc time + * + * @param[in] tblock time value + * + * @return None + * + * @note None + */ +void tls_set_rtc(struct tm *tblock) +{ + int ctrl1 = 0; + int ctrl2 = 0; + + ctrl2 = tls_reg_read32(HR_PMU_RTC_CTRL2); /* disable */ + ctrl2 &= ~(1 << 16); + tls_reg_write32(HR_PMU_RTC_CTRL2, ctrl2); + + ctrl1 |= tblock->tm_sec; + ctrl1 |= tblock->tm_min << 8; + ctrl1 |= tblock->tm_hour << 16; + ctrl1 |= tblock->tm_mday << 24; + tls_reg_write32(HR_PMU_RTC_CTRL1, ctrl1); + + ctrl2 = 0; + ctrl2 |= tblock->tm_mon; + ctrl2 |= tblock->tm_year << 8; + tls_reg_write32(HR_PMU_RTC_CTRL2, ctrl2); + + ctrl2 = tls_reg_read32(HR_PMU_RTC_CTRL2); /* enable */ + ctrl2 |= (1 << 16); + tls_reg_write32(HR_PMU_RTC_CTRL2, ctrl2); +} + +/** + * @brief This function is used to get pmu rtc time + * + * @param[out] tblock time value + * + * @return None + * + * @note None + */ +void tls_get_rtc(struct tm *tblock) +{ + int ctrl1 = 0; + int ctrl2 = 0; + + ctrl1 = tls_reg_read32(HR_PMU_RTC_CTRL1); + ctrl2 = tls_reg_read32(HR_PMU_RTC_CTRL2); + tblock->tm_year = ((int)((int)ctrl2 & 0x00007f00) >> 8); + tblock->tm_mon = (ctrl2 & 0x0000000f); + tblock->tm_mday = (ctrl1 & 0x1f000000) >> 24; + tblock->tm_hour = (ctrl1 & 0x001f0000) >> 16; + tblock->tm_min = (ctrl1 & 0x00003f00) >> 8; + tblock->tm_sec = ctrl1 & 0x0000003f; +} + +void PMU_RTC_IRQHandler(void) +{ + if (tls_reg_read32(HR_PMU_INTERRUPT_SRC) & BIT(5)) /* rtc interrupt */ + { + tls_reg_write32(HR_PMU_INTERRUPT_SRC, BIT(5)); /* clear rtc interrupt */ + + if (NULL != rtc_context.callback) + rtc_context.callback(rtc_context.arg); + } + return; +} + +/** + * @brief This function is used to register pmu rtc interrupt + * + * @param[in] callback the rtc interrupt call back function + * @param[in] arg parameter of call back function + * + * @return None + * + * @note + * user not need clear interrupt flag. + * rtc callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +void tls_rtc_isr_register(tls_rtc_irq_callback callback, void *arg) +{ + rtc_context.callback = callback; + rtc_context.arg = arg; + +// tls_reg_write32(HR_PMU_INTERRUPT_MASK, tls_reg_read32(HR_PMU_INTERRUPT_MASK) & (~BIT(4))); + + tls_irq_enable(PMU_RTC_INT); + + return; +} + +/** + * @brief This function is used to start pmu rtc timer + * + * @param[in] tblock timer value + * + * @return None + * + * @note None + */ +void tls_rtc_timer_start(struct tm *tblock) +{ + int ctrl1 = 0; + int ctrl2 = 0; + + tls_irq_enable(PMU_RTC_INT); + + ctrl1 |= tblock->tm_sec; + ctrl1 |= tblock->tm_min << 8; + ctrl1 |= tblock->tm_hour << 16; + ctrl1 |= tblock->tm_mday << 24; + + ctrl2 |= tblock->tm_mon; + ctrl2 |= tblock->tm_year << 8; + tls_reg_write32(HR_PMU_RTC_CTRL2, ctrl2 | BIT(16)); + + tls_reg_write32(HR_PMU_RTC_CTRL1, ctrl1 | BIT(31));/* must set the enable */ + + return; +} + +/** + * @brief This function is used to stop pmu rtc timer + * + * @param None + * + * @return None + * + * @note This function also is used to clear rtc timer interrupt + */ +void tls_rtc_timer_stop(void) +{ + tls_irq_disable(PMU_RTC_INT); + + tls_reg_write32(HR_PMU_RTC_CTRL1, tls_reg_read32(HR_PMU_RTC_CTRL1) & (~BIT(31))); + + return; +} + diff --git a/sdk/Platform/Drivers/spi/wm_hostspi.c b/sdk/Platform/Drivers/spi/wm_hostspi.c new file mode 100644 index 0000000..f0c8f1d --- /dev/null +++ b/sdk/Platform/Drivers/spi/wm_hostspi.c @@ -0,0 +1,1425 @@ +/** + * @file wm_hostspi.c + * + * @brief host spi Driver Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ +#include +#include +#include +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_gpio.h" +#include "wm_hostspi.h" +#include "wm_dma.h" +#include "wm_dbg.h" +#include "wm_mem.h" +#include "wm_cpu.h" +#include "wm_spi_hal.h" +#include "wm_wl_task.h" +#include "tls_common.h" +#include "wm_pmu.h" + +static struct tls_spi_port *spi_port = NULL; + +#define MSG_QUEUE_SIZE (8) + +#define SPI_SCHEDULER_STK_SIZE (128) +static u32 spi_scheduler_stk[SPI_SCHEDULER_STK_SIZE]; + +#define SPI_SCHED_MSG_START_ENGINE (1) +#define SPI_SCHED_MSG_TX_FIFO_READY (2) +#define SPI_SCHED_MSG_RX_FIFO_READY (3) +#define SPI_SCHED_MSG_TRANSFER_COMPLETE (4) +#define SPI_SCHED_MSG_EXIT (5) +#define SPI_SCHED_MSG_END (6) +static void spi_start_transfer(u32 transfer_bytes); + +int tls_spi_async(struct tls_spi_message *message); +int tls_spi_sync(struct tls_spi_message *message); + +#ifdef SPI_USE_DMA +static void SpiMasterInit(u8 mode, u8 cs_active, u32 fclk) +{ + tls_sys_clk sysclk; + + SPIM_CHCFG_REG = SPI_CLEAR_FIFOS; + while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS); + + tls_sys_clk_get(&sysclk); //��ȡʵ��Ƶ�� + + SPIM_CLKCFG_REG = sysclk.apbclk*UNIT_MHZ/(fclk*2) - 1;; + SPIM_SPICFG_REG = 0; + SPIM_SPICFG_REG = SPI_FRAME_FORMAT_MOTO | SPI_SET_MASTER_SLAVE(SPI_MASTER) | mode; + SPIM_INTEN_REG = 0xff; /* Disable INT */ + + tls_dma_init(); +} + +int spiWaitIdle(void) +{ + unsigned long regVal; + unsigned long timeout = 0; + + do + { + timeout++; + if (timeout > 0x4FFFFF) // 5s + return TLS_SPI_STATUS_EBUSY; + regVal = SPIM_SPISTATUS_REG; + } + while (regVal & (1 << 12)); + + return TLS_SPI_STATUS_OK; +} + +static int SpiDmaBlockWrite(u8 * data, u32 len, u8 ifusecmd, u32 cmd) +{ + unsigned char dmaCh = 0; + struct tls_dma_descriptor DmaDesc; + u32 txlen, txlenbk; + u32 i, blocknum, blocksize = 0; + int ret = TLS_SPI_STATUS_OK; + int txcmdover = 0; + + if (NULL == data) + { + return TLS_SPI_STATUS_EINVAL; + } + if (spiWaitIdle()) + return TLS_SPI_STATUS_EBUSY; + SPIM_CHCFG_REG = SPI_CLEAR_FIFOS; + while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS); + + if (ifusecmd) + SPIM_TXDATA_REG = cmd; + + if (len % 4) + { + txlen = len & 0xfffffffc; // �����ֵ���󵥶��� + } + else + { + txlen = len; + } + + txlenbk = txlen; + if (txlen > 0) + { + blocknum = txlen / SPI_DMA_MAX_TRANS_SIZE; + + /* Request DMA Channel */ + dmaCh = tls_dma_request(1,TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_LSSPI_TX) | + TLS_DMA_FLAGS_HARD_MODE); + for (i = 0; i <= blocknum; i++) + { + DmaDesc.src_addr = (int) (data + i * SPI_DMA_MAX_TRANS_SIZE); + DmaDesc.dest_addr = HR_SPI_TXDATA_REG; + blocksize = (txlen > SPI_DMA_MAX_TRANS_SIZE) ? SPI_DMA_MAX_TRANS_SIZE : txlen; + + if (0 == blocksize) + break; + DmaDesc.dma_ctrl = TLS_DMA_DESC_CTRL_SRC_ADD_INC | + TLS_DMA_DESC_CTRL_DATA_SIZE_WORD | + TLS_DMA_DESC_CTRL_TOTAL_BYTES(blocksize); + DmaDesc.valid = TLS_DMA_DESC_VALID; + DmaDesc.next = NULL; + tls_dma_start(dmaCh, &DmaDesc, 0); + /* Enable SPI TX DMA */ + SPIM_MODECFG_REG = SPI_RX_TRIGGER_LEVEL(0) | SPI_TX_TRIGGER_LEVEL(0) | SPI_TX_DMA_ON; + SPIM_SPITIMEOUT_REG = SPI_TIMER_EN | SPI_TIME_OUT((u32) 0xffff); + + if (ifusecmd && 0 == i) + { + SPIM_CHCFG_REG = SPI_FORCE_SPI_CS_OUT | SPI_TX_CHANNEL_ON | + SPI_CONTINUE_MODE | SPI_START | + SPI_VALID_CLKS_NUM(((blocksize + 4) * 8)); + txcmdover = 1; + } + else + SPIM_CHCFG_REG = SPI_FORCE_SPI_CS_OUT | SPI_TX_CHANNEL_ON | SPI_CONTINUE_MODE | + SPI_START | SPI_VALID_CLKS_NUM((blocksize * 8)); + + if (spiWaitIdle()) + { + ret = TLS_SPI_STATUS_EBUSY; + break; + } + /* Wait Dma Channel Complete and Free Dma channel */ + if (tls_dma_wait_complt(dmaCh)) + { + ret = TLS_SPI_STATUS_EBUSY; + break; + } + txlen -= blocksize; + } + tls_dma_free(dmaCh); + } + // tx ����һ�����ֵļ����ֽ� + if (len > txlenbk) + { + u32 word32 = 0; + int temp = 0; + for (i = 0; i < (len - txlenbk); i++) + { + word32 |= (data[txlenbk + i] << (i * 8)); + } + SPIM_TXDATA_REG = word32; + SPIM_MODECFG_REG = SPI_RX_TRIGGER_LEVEL(0) | SPI_TX_TRIGGER_LEVEL(0); + SPIM_SPITIMEOUT_REG = SPI_TIMER_EN | SPI_TIME_OUT((u32) 0xffff); + if (ifusecmd && 0 == txcmdover) // ��Ҫ��������������û�з��ͳ�ȥ�����͵��ֽ�����Ҫ����4 + temp = 4; + SPIM_CHCFG_REG = SPI_FORCE_SPI_CS_OUT | SPI_TX_CHANNEL_ON | SPI_CONTINUE_MODE | + SPI_START | SPI_VALID_CLKS_NUM(((temp + len - txlenbk) * 8)); + if (spiWaitIdle()) + { + ret = TLS_SPI_STATUS_EBUSY; + } + } + SPIM_CHCFG_REG = 0x00000000; + SPIM_MODECFG_REG = 0x00000000; + SPIM_SPITIMEOUT_REG = 0x00000000; + + return ret; +} + +static int SpiDmaBlockRead(u8 * data, u32 len, u8 * txdata, u8 txlen) +{ + unsigned char dmaCh = 0; + struct tls_dma_descriptor DmaDesc; + u32 word32 = 0; + u32 i; + u32 rxlen, rxlenbk; + u8 blocknum; + u32 blocksize = 0; + int ret = TLS_SPI_STATUS_OK; + +// printf("\nentry SpiDmaBlockRead\n"); + if (NULL == data) + { + return TLS_SPI_STATUS_EINVAL; + } + if (spiWaitIdle()) + { + return TLS_SPI_STATUS_EBUSY; + } + + SPIM_CHCFG_REG = SPI_CLEAR_FIFOS; + while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS); + + if (len % 4) + { + rxlen = len & 0xfffffffc; // �����ֵ���󵥶�ȡ + } + else + { + rxlen = len; + } + rxlenbk = rxlen; + blocknum = rxlen / SPI_DMA_MAX_TRANS_SIZE; + + if (txlen > 0 && txlen <= 32) + { + for (i = 0; i < txlen; i++) + { + if ((i > 0) && (0 == i % 4)) + { + SPIM_TXDATA_REG = word32; + word32 = 0; + } + word32 |= (txdata[i] << ((i % 4) * 8)); + } + SPIM_TXDATA_REG = word32; + } + +/* Request DMA Channel */ + dmaCh = tls_dma_request(1,TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_LSSPI_RX) | + TLS_DMA_FLAGS_HARD_MODE); + DmaDesc.src_addr = HR_SPI_RXDATA_REG; + for (i = 0; i <= blocknum; i++) + { + if (rxlenbk > 0) + { + // ˵�����յ����ݴ���4 + // printf("\ni =%d\n",i); + DmaDesc.dest_addr = (int) (data + i * SPI_DMA_MAX_TRANS_SIZE); + blocksize = (rxlen > SPI_DMA_MAX_TRANS_SIZE) ? SPI_DMA_MAX_TRANS_SIZE : rxlen; + if (0 == blocksize) + break; + // printf("\nblocksize= %d\n",blocksize); + DmaDesc.dma_ctrl = + TLS_DMA_DESC_CTRL_DEST_ADD_INC | TLS_DMA_DESC_CTRL_BURST_SIZE1 | + TLS_DMA_DESC_CTRL_DATA_SIZE_WORD | + TLS_DMA_DESC_CTRL_TOTAL_BYTES(blocksize); + // word32 = DmaDesc.dma_ctrl; + // printf("\ndma ctrl = %x\n",DmaDesc.dma_ctrl); + DmaDesc.valid = TLS_DMA_DESC_VALID; + DmaDesc.next = NULL; + tls_dma_start(dmaCh, &DmaDesc, 0); + /* Enable SPI RX DMA */ + SPIM_MODECFG_REG = SPI_RX_TRIGGER_LEVEL(0) | SPI_TX_TRIGGER_LEVEL(0) | + SPI_RX_DMA_ON; + } + else + { + SPIM_MODECFG_REG = SPI_RX_TRIGGER_LEVEL(0) | SPI_TX_TRIGGER_LEVEL(0); // rx��������4��byte�����ÿ�DMA + } + SPIM_SPITIMEOUT_REG = SPI_TIMER_EN | SPI_TIME_OUT((u32) 0xffff); + if (0 == blocknum) + { + SPIM_CHCFG_REG = + SPI_FORCE_SPI_CS_OUT | SPI_RX_CHANNEL_ON | SPI_TX_CHANNEL_ON | + SPI_CONTINUE_MODE | SPI_START | + SPI_VALID_CLKS_NUM(((len + txlen) * 8)) | SPI_RX_INVALID_BITS(txlen *8); + } + else + { + if (0 == i) // ��һ����Ҫ��TX + { + SPIM_CHCFG_REG = + SPI_FORCE_SPI_CS_OUT | SPI_RX_CHANNEL_ON | SPI_TX_CHANNEL_ON + | SPI_CONTINUE_MODE | SPI_START | + SPI_VALID_CLKS_NUM(((blocksize + txlen) *8)) | SPI_RX_INVALID_BITS(txlen * 8); + } + else if (i == blocknum) + { + SPIM_CHCFG_REG = + SPI_FORCE_SPI_CS_OUT | SPI_RX_CHANNEL_ON | SPI_CONTINUE_MODE + | SPI_START | SPI_VALID_CLKS_NUM((blocksize + len - rxlenbk) * 8); + } + else + { + SPIM_CHCFG_REG = + SPI_FORCE_SPI_CS_OUT | SPI_RX_CHANNEL_ON | SPI_CONTINUE_MODE + | SPI_START | SPI_VALID_CLKS_NUM(blocksize * 8); + } + } + if (spiWaitIdle()) + { + ret = TLS_SPI_STATUS_EBUSY; + break; + } + + /* Wait Dma Channel Complete and Free Dma channel */ + if (tls_dma_wait_complt(dmaCh)) + { + ret = TLS_SPI_STATUS_EBUSY; + break; + } + rxlen -= blocksize; + } + tls_dma_free(dmaCh); + + if (len > rxlenbk) // ȡ���IJ���һ���ֵļ���byte + { + word32 = SPIM_RXDATA_REG; + *((int *) data + rxlenbk / 4) = word32; + } + SPIM_CHCFG_REG = 0x00000000; + SPIM_MODECFG_REG = 0x00000000; + SPIM_SPITIMEOUT_REG = 0x00000000; + return ret; +} +#endif + + +/** + * @brief This function is used to set SPI transfer mode. + * + * @param[in] type is the transfer type. + * type == SPI_BYTE_TRANSFER byte transfer + * type == SPI_WORD_TRANSFER word transfer + * type == SPI_DMA_TRANSFER DMA transfer + * + * @return None + * + * @note None + */ +void tls_spi_trans_type(u8 type) +{ + + spi_port->transtype = type; + if (SPI_WORD_TRANSFER == type) + { + spi_set_endian(0); + } + else if (SPI_DMA_TRANSFER == type) + { +#ifdef SPI_USE_DMA + SpiMasterInit(spi_port->mode, TLS_SPI_CS_LOW, spi_port->speed_hz); +#endif + } +} + +static void spi_message_init(struct tls_spi_message *m) +{ + memset(m, 0, sizeof(*m)); + dl_list_init(&m->transfers); +} + + +static void spi_complete(void *arg) +{ + tls_os_sem_t *sem; + + sem = (tls_os_sem_t *) arg; + tls_os_sem_release(sem); +} + +static u32 spi_fill_txfifo(struct tls_spi_transfer *current_transfer, + u32 current_remaining_bytes) +{ + u8 fifo_level; + u16 rw_words; + u16 rw_bytes; + u8 data8; + u8 i; + u32 data32 = 0; + u32 tx_remaining_bytes; + if ((current_transfer == NULL) || (current_remaining_bytes == 0)) + return 0; + + tx_remaining_bytes = current_remaining_bytes; + +// printf("ready to write to fifo size - %d.\n", tx_remaining_bytes); + spi_get_status(NULL, NULL, &fifo_level); + +// TLS_DBGPRT_SPI("\nfifo_level 0= %d\n",fifo_level); + rw_words = + ((fifo_level > tx_remaining_bytes) ? tx_remaining_bytes : fifo_level) / 4; + rw_bytes = + ((fifo_level > tx_remaining_bytes) ? tx_remaining_bytes : fifo_level) % 4; + +// TLS_DBGPRT_SPI("write to spi fifo words - %d, bytes - %d.\n", rw_words, +// rw_bytes); + +//�������17us + for (i = 0; i < rw_words; i++) + { + if (current_transfer->tx_buf) + { + if (SPI_BYTE_TRANSFER == spi_port->transtype) + { + data32 = 0; + data32 |= ((u8 *) current_transfer->tx_buf + + (current_transfer->len - tx_remaining_bytes))[0] << 24; + data32 |= ((u8 *) current_transfer->tx_buf + + (current_transfer->len - tx_remaining_bytes))[1] << 16; + data32 |= ((u8 *) current_transfer->tx_buf + + (current_transfer->len - tx_remaining_bytes))[2] << 8; + data32 |= ((u8 *) current_transfer->tx_buf + + (current_transfer->len - tx_remaining_bytes))[3] << 0; + } + else if (SPI_WORD_TRANSFER == spi_port->transtype) + { + data32 = *((u32 *) ((u8 *) current_transfer->tx_buf + + current_transfer->len - tx_remaining_bytes)); + } + } + else + { + data32 = 0xffffffff; + } + // TLS_DBGPRT_SPI("write to spi fifo word[%d]: 0x%x.\n", i, data32); + spi_data_put(data32); + tx_remaining_bytes -= 4; + } + + if (rw_bytes) + { + data32 = 0; + for (i = 0; i < rw_bytes; i++) + { + if (current_transfer->tx_buf) + { + data8 = ((u8 *) current_transfer->tx_buf)[current_transfer->len - + tx_remaining_bytes]; + } + else + { + data8 = 0xff; + } + if (SPI_BYTE_TRANSFER == spi_port->transtype) + data32 |= data8 << ((3 - i) * 8); + else if (SPI_WORD_TRANSFER == spi_port->transtype) + { + data32 |= data8 << (i * 8); + } + tx_remaining_bytes -= 1; + } + // TLS_DBGPRT_SPI("write to spi fifo word: 0x%x.\n", data32); + spi_data_put(data32); + } + + return (current_remaining_bytes - tx_remaining_bytes); +} + +static u32 spi_get_rxfifo(struct tls_spi_transfer *current_transfer, + u32 current_remaining_bytes) +{ + u8 fifo_level; + u8 rw_words; + u8 rw_bytes; + u8 data8 = 0; + u8 i; + u32 data32; + u32 rx_remaining_bytes; + + if ((current_transfer == NULL) || (current_remaining_bytes == 0)) + return 0; + + rx_remaining_bytes = current_remaining_bytes; + spi_get_status(NULL, &fifo_level, NULL); +// TLS_DBGPRT_SPI("rx fifo level: %d.\n", fifo_level); + + rw_words = fifo_level / 4; + rw_bytes = fifo_level % 4; + +// TLS_DBGPRT_SPI("rx data: %d words, %d bytes.\n", rw_words, rw_bytes); + +//���������10us + for (i = 0; i < rw_words; i++) + { + + data32 = spi_data_get(); + + // TLS_DBGPRT_SPI("rx data[%d](w): 0x%08x.\n", i, data32); + if (current_transfer->rx_buf) + { + + if (SPI_BYTE_TRANSFER == spi_port->transtype) + { + data32 = swap_32(data32); + (((u8 *) current_transfer->rx_buf + + (current_transfer->len - rx_remaining_bytes)))[0] = (u8) data32; + (((u8 *) current_transfer->rx_buf + + (current_transfer->len - rx_remaining_bytes)))[1] = (u8) (data32 >> 8); + (((u8 *) current_transfer->rx_buf + + (current_transfer->len - rx_remaining_bytes)))[2] = (u8) (data32 >> 16); + (((u8 *) current_transfer->rx_buf + + (current_transfer->len - rx_remaining_bytes)))[3] = (u8) (data32 >> 24); + } + else if (SPI_WORD_TRANSFER == spi_port->transtype) + { + *((u32 *) ((u8 *) current_transfer->rx_buf + + current_transfer->len - rx_remaining_bytes)) = data32; + } + } + rx_remaining_bytes -= 4; + + } + + if (rw_bytes) + { + data32 = spi_data_get(); + // TLS_DBGPRT_SPI("\nrx data=%x\n",data32); + if (current_transfer->rx_buf) + { + for (i = 0; i < rw_bytes; i++) + { + if (SPI_BYTE_TRANSFER == spi_port->transtype) + data8 = (u8) (data32 >> ((3 - i) * 8)); + else if (SPI_WORD_TRANSFER == spi_port->transtype) + data8 = (u8) (data32 >> (i * 8)); + + // TLS_DBGPRT_SPI("rx data[%d](b): 0x%02x.\n", i, data8); + ((u8 *) current_transfer->rx_buf)[current_transfer->len -rx_remaining_bytes] = data8; + rx_remaining_bytes -= 1; + } + } + else + { + rx_remaining_bytes -= rw_bytes; + } + } + + return (current_remaining_bytes - rx_remaining_bytes); +} + +static struct tls_spi_transfer *spi_next_transfer(struct tls_spi_message + *current_message) +{ + if (current_message == NULL) + { + return NULL; + } + + return dl_list_first(¤t_message->transfers, struct tls_spi_transfer, transfer_list); +} + +static struct tls_spi_message *spi_next_message(void) +{ + struct tls_spi_message *current_message; + + current_message = + dl_list_first(&spi_port->wait_queue, struct tls_spi_message, queue); + if (current_message == NULL) + { + return NULL; + } + + spi_port->current_transfer = spi_next_transfer(current_message); + current_message->status = SPI_MESSAGE_STATUS_INPROGRESS; + + return current_message; +} + +int gSpiCsFlag = 0; +static void spi_start_transfer(u32 transfer_bytes) +{ + if (spi_port->reconfig) + { + TLS_DBGPRT_SPI_INFO("reconfig the spi master controller.\n"); + spi_set_mode(spi_port->mode); + spi_set_chipselect_mode(spi_port->cs_active); + spi_set_sclk(spi_port->speed_hz); + + spi_port->reconfig = 0; + } + + spi_set_sclk_length(transfer_bytes * 8, 0); +// if(0 == gSpiCsFlag) + { + spi_set_chipselect_mode(SPI_CS_ACTIVE_MODE); + } + spi_sclk_start(); +} + +static void spi_continue_transfer(void) +{ + struct tls_spi_message *current_message; + struct tls_spi_transfer *current_transfer; + u32 transfer_bytes; + + current_message = spi_port->current_message; + current_transfer = spi_port->current_transfer; + if ((current_message == NULL) || (current_transfer == NULL)) + { + return; + } + transfer_bytes = + spi_get_rxfifo(current_transfer, spi_port->current_remaining_bytes); + + spi_port->current_remaining_bytes -= transfer_bytes; + if (spi_port->current_remaining_bytes == 0) + { + tls_os_sem_acquire(spi_port->lock, 0); + + dl_list_del(¤t_transfer->transfer_list); + spi_port->current_transfer = + spi_next_transfer(spi_port->current_message); + if (spi_port->current_transfer == NULL) + { +// tls_sys_clk_set(CPU_CLK_40M); + spi_set_chipselect_mode(SPI_CS_INACTIVE_MODE); + current_message->status = SPI_MESSAGE_STATUS_DONE; + dl_list_del(¤t_message->queue); + spi_port->current_message = spi_next_message(); + } + + tls_os_sem_release(spi_port->lock); + + // TLS_DBGPRT_SPI("get the next spi transfer pair.\n"); + current_transfer = spi_port->current_transfer; + if (current_transfer != NULL) + { + spi_port->current_remaining_bytes = current_transfer->len; + } + } + + transfer_bytes = + spi_fill_txfifo(current_transfer, spi_port->current_remaining_bytes); + + if (transfer_bytes) + { + spi_start_transfer(transfer_bytes); + } + + if (current_message->status == SPI_MESSAGE_STATUS_DONE) + { + // TLS_DBGPRT_SPI("current spi transaction finish and notify the + // submitter.\n"); + current_message->complete(current_message->context); + } +} + +static void spi_scheduler(void *data) +{ + u8 err; + u32 msg; + u32 transfer_bytes; + struct tls_spi_transfer *current_transfer; + + while (1) + { + err = tls_os_queue_receive(spi_port->msg_queue, (void **) &msg, 4, 0); + if (err == TLS_OS_SUCCESS) + { + switch (msg) + { + case SPI_SCHED_MSG_START_ENGINE: +// tls_sys_clk_set(CPU_CLK_80M); //80MHZ + if (spi_port->current_message) + { + TLS_DBGPRT_WARNING + ("spi transaction scheduler is running now!\n"); + break; + } + + TLS_DBGPRT_SPI_INFO + ("acquire the first transaction message in waiting queue.\n"); + tls_os_sem_acquire(spi_port->lock, 0); + + spi_port->current_message = spi_next_message(); + + tls_os_sem_release(spi_port->lock); + + // TLS_DBGPRT_SPI("acquire the first transfer pair in the + // current transaction message.\n"); + current_transfer = spi_port->current_transfer; + if (current_transfer == NULL) + { + break; + } + spi_port->current_remaining_bytes = current_transfer->len; + + // TLS_DBGPRT_SPI("current transfer lenght - %d.\n", + // spi_port->current_remaining_bytes); + + // TLS_DBGPRT_SPI("fill the tx fifo.\n"); + + transfer_bytes = + spi_fill_txfifo(current_transfer, spi_port->current_remaining_bytes); + + // printf("start the spi transfer - %d.\n", transfer_bytes); + spi_start_transfer(transfer_bytes); + + break; + + case SPI_SCHED_MSG_TX_FIFO_READY: + TLS_DBGPRT_SPI_INFO("process SPI_SCHED_MSG_TX_FIFO_READY.\n"); + break; + + case SPI_SCHED_MSG_RX_FIFO_READY: + TLS_DBGPRT_SPI_INFO("process SPI_SCHED_MSG_RX_FIFO_READY.\n"); + break; + + case SPI_SCHED_MSG_TRANSFER_COMPLETE: + spi_continue_transfer(); + break; + + case SPI_SCHED_MSG_EXIT: + break; + + default: + break; + } + } + } +} + +void SPI_LS_IRQHandler(void) +{ + + u32 int_status; + u32 int_mask; + int_status = spi_get_int_status(); +// printf("\nspi int sta=%x\n",int_status); + spi_clear_int_status(int_status); + + int_mask = spi_int_mask(); + int_status &= ~int_mask; + +// printf("spi interrupt - 0x%x.\n", int_status); + + if (int_status & SPI_INT_TX_FIFO_RDY) + { + tls_os_queue_send(spi_port->msg_queue, + (void *) SPI_SCHED_MSG_TX_FIFO_READY, 4); + } + + if (int_status & SPI_INT_RX_FIFO_RDY) + { + tls_os_queue_send(spi_port->msg_queue, + (void *) SPI_SCHED_MSG_RX_FIFO_READY, 4); + } + + if (int_status & SPI_INT_TRANSFER_DONE) + { + if (SPI_WORD_TRANSFER == spi_port->transtype) + spi_continue_transfer(); + else + tls_os_queue_send(spi_port->msg_queue, + (void *) SPI_SCHED_MSG_TRANSFER_COMPLETE, 4); + + } +} + + + /** + * @brief This function is used to setup the spi CPOL,CPHA,cs signal and clock. + * + * @param[in] mode is CPOL and CPHA type defined in TLS_SPI_MODE_0 to TLS_SPI_MODE_3 + * @param[in] cs_active is cs mode, defined as TLS_SPI_CS_LOW or TLS_SPI_CS_HIGH + * @param[in] fclk is spi clock,the unit is HZ. + * + * @retval TLS_SPI_STATUS_OK if setup success + * @retval TLS_SPI_STATUS_EMODENOSUPPORT if mode is not support + * @retval TLS_SPI_STATUS_EINVAL if cs_active is not support + * @retval TLS_SPI_STATUS_ECLKNOSUPPORT if fclk is not support + * + * @note None + */ +int tls_spi_setup(u8 mode, u8 cs_active, u32 fclk) +{ + tls_sys_clk sysclk; + + if ((spi_port->mode == mode) && (spi_port->cs_active == cs_active) + && (spi_port->speed_hz == fclk)) + { + TLS_DBGPRT_WARNING + ("@mode, @cs_activer, @fclk is the same as settings of the current spi master driver!\n"); + return TLS_SPI_STATUS_OK; + } + + switch (mode) + { + case TLS_SPI_MODE_0: + case TLS_SPI_MODE_1: + case TLS_SPI_MODE_2: + case TLS_SPI_MODE_3: + spi_port->mode = mode; + break; + + default: + TLS_DBGPRT_ERR("@mode is invalid!\n"); + return TLS_SPI_STATUS_EMODENOSUPPORT; + } + + if ((cs_active != TLS_SPI_CS_HIGH) && (cs_active != TLS_SPI_CS_LOW)) + { + TLS_DBGPRT_ERR("@cs_active is invalid!\n"); + return TLS_SPI_STATUS_EINVAL; + } + else + { + spi_port->cs_active = cs_active; + } + + tls_sys_clk_get(&sysclk); + + if ((fclk < TLS_SPI_FCLK_MIN) || (fclk > sysclk.apbclk*UNIT_MHZ/2)) //TLS_SPI_FCLK_MAX + { + TLS_DBGPRT_ERR("@fclk is invalid!\n"); + return TLS_SPI_STATUS_ECLKNOSUPPORT; + } + else + { + spi_port->speed_hz = fclk; + } + +#ifdef SPI_USE_DMA + if (SPI_DMA_TRANSFER == spi_port->transtype) + { + SpiMasterInit(mode, TLS_SPI_CS_LOW, fclk); + return TLS_SPI_STATUS_OK; + } +#endif + + spi_port->reconfig = 1; + + return TLS_SPI_STATUS_OK; +} + + +/** + * @brief This function is used to synchronous write command then read data by SPI. + * + * @param[in] txbuf is the write data buffer. + * @param[in] n_tx is the write data length. + * @param[in] rxbuf is the read data buffer. + * @param[in] n_rx is the read data length. + * + * @retval TLS_SPI_STATUS_OK if write success. + * @retval TLS_SPI_STATUS_EINVAL if argument is invalid. + * @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory. + * @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed. + * + * @note None + */ +int tls_spi_read_with_cmd(const u8 * txbuf, u32 n_tx, u8 * rxbuf, u32 n_rx) +{ + int status; + struct tls_spi_message message; + struct tls_spi_transfer x[2]; + + if ((txbuf == NULL) || (n_tx == 0) || (rxbuf == NULL) || (n_rx == 0)) + { + return TLS_SPI_STATUS_EINVAL; + } + +#ifdef SPI_USE_DMA + if (SPI_DMA_TRANSFER == spi_port->transtype) + { + if (n_rx > SPI_DMA_BUF_MAX_SIZE || n_tx > SPI_DMA_CMD_MAX_SIZE) + { + TLS_DBGPRT_ERR("\nread length too long\n"); + return TLS_SPI_STATUS_EINVAL; + } + tls_os_sem_acquire(spi_port->lock, 0); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_irq_disable(SPI0_INT); + + MEMCPY((u8 *) SPI_DMA_CMD_ADDR, txbuf, n_tx); + SpiDmaBlockRead((u8 *) SPI_DMA_BUF_ADDR, n_rx, (u8 *) SPI_DMA_CMD_ADDR, + n_tx); + MEMCPY(rxbuf, (u8 *) SPI_DMA_BUF_ADDR, n_rx); + { + uint32_t int_status; + int_status = spi_get_int_status(); + spi_clear_int_status(int_status); + tls_irq_enable(SPI_LS_IRQn); + } + + + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_os_sem_release(spi_port->lock); + return TLS_SPI_STATUS_OK; + } +#endif + + spi_message_init(&message); + + memset(x, 0, sizeof(x)); + if (n_tx) + { + x[0].len = n_tx; + x[0].tx_buf = txbuf; + dl_list_add_tail(&message.transfers, &x[0].transfer_list); + } + if (n_rx) + { + x[1].len = n_rx; + x[1].rx_buf = rxbuf; + dl_list_add_tail(&message.transfers, &x[1].transfer_list); + } + +/* do the i/o. */ + status = tls_spi_sync(&message); + + return status; +} + +/** + * @brief This function is used to synchronous read data by SPI. + * + * @param[in] buf is the buffer for saving SPI data. + * @param[in] len is the data length. + * + * @retval TLS_SPI_STATUS_OK if write success. + * @retval TLS_SPI_STATUS_EINVAL if argument is invalid. + * @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory. + * @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed. + * + * @note None + */ +int tls_spi_read(u8 * buf, u32 len) +{ + struct tls_spi_transfer t; + struct tls_spi_message m; + + if ((buf == NULL) || (len == 0)) + { + return TLS_SPI_STATUS_EINVAL; + } + +#ifdef SPI_USE_DMA + if (SPI_DMA_TRANSFER == spi_port->transtype) + { + u32 data32 = 0; + u16 rxBitLen; + u32 rdval1 = 0; + u32 i; + tls_os_sem_acquire(spi_port->lock, 0); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_irq_disable(SPI_LS_IRQn); + // ֱ�Ӵ��䣬��������ԭ����DMA����������ȡ4���ֽ����ڵ�����,SPI FIFO��ȡ��λΪword + if (len <= 4) + { + SPIM_CHCFG_REG = SPI_CLEAR_FIFOS; + while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS); + + rxBitLen = 8 * len; + rdval1 = + SPI_FORCE_SPI_CS_OUT | SPI_CS_LOW | SPI_TX_CHANNEL_ON | + SPI_RX_CHANNEL_ON | SPI_CONTINUE_MODE | SPI_START | + SPI_VALID_CLKS_NUM(rxBitLen); + SPIM_CHCFG_REG = rdval1; + spiWaitIdle(); + SPIM_CHCFG_REG |= SPI_CS_HIGH; + + data32 = SPIM_RXDATA_REG; + + for (i = 0; i < len; i++) + { + *(buf + i) = (u8) (data32 >> i * 8); + } + SPIM_CHCFG_REG = 0x00000000; + SPIM_MODECFG_REG = 0x00000000; + } + else // DMA���� + { + if (len > SPI_DMA_BUF_MAX_SIZE) + { + TLS_DBGPRT_ERR("\nread len too long\n"); + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_os_sem_release(spi_port->lock); + return TLS_SPI_STATUS_EINVAL; + } + SpiDmaBlockRead((u8 *) SPI_DMA_BUF_ADDR, len, NULL, 0); + MEMCPY(buf, (u8 *) SPI_DMA_BUF_ADDR, len); + } + { + uint32_t int_status; + int_status = spi_get_int_status(); + spi_clear_int_status(int_status); + tls_irq_enable(SPI_LS_IRQn); + } + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_os_sem_release(spi_port->lock); + return TLS_SPI_STATUS_OK; + } +#endif + + memset(&t, 0, sizeof(t)); + t.rx_buf = buf; + t.len = len; + + spi_message_init(&m); + + dl_list_add_tail(&m.transfers, &t.transfer_list); + + return tls_spi_sync(&m); +} + +/** + * @brief This function is used to synchronous write data by SPI. + * + * @param[in] buf is the user data. + * @param[in] len is the data length. + * + * @retval TLS_SPI_STATUS_OK if write success. + * @retval TLS_SPI_STATUS_EINVAL if argument is invalid. + * @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory. + * @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed. + * + * @note None + */ +int tls_spi_write(const u8 * buf, u32 len) +{ + struct tls_spi_transfer t; + struct tls_spi_message m; + + if ((buf == NULL) || (len == 0)) + { + return TLS_SPI_STATUS_EINVAL; + } + +#ifdef SPI_USE_DMA + if (SPI_DMA_TRANSFER == spi_port->transtype) + { + u32 data32 = 0; + u16 txBitLen; + u32 rdval1 = 0; + u32 i; + tls_os_sem_acquire(spi_port->lock, 0); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_irq_disable(SPI_LS_IRQn); + if (len <= 4) // ֱ�Ӵ��䣬��������ԭ����DMA����������������4���ֽڵ����ݣ�SPI + { + SPIM_CHCFG_REG = SPI_CLEAR_FIFOS; + while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS); + for (i = 0; i < len; i++) + { + data32 |= (((u8) (buf[i])) << (i * 8)); + } + SPIM_TXDATA_REG = data32; + txBitLen = 8 * len; + rdval1 = + SPI_FORCE_SPI_CS_OUT | SPI_CS_LOW | SPI_TX_CHANNEL_ON | + SPI_RX_CHANNEL_ON | SPI_CONTINUE_MODE | SPI_START | + SPI_VALID_CLKS_NUM(txBitLen); + SPIM_CHCFG_REG = rdval1; + spiWaitIdle(); + SPIM_CHCFG_REG |= SPI_CS_HIGH; + + SPIM_CHCFG_REG = 0x00000000; + SPIM_MODECFG_REG = 0x00000000; + } + else // DMA���� + { + if (len > SPI_DMA_BUF_MAX_SIZE) + { + TLS_DBGPRT_ERR("\nwrite len too long\n"); + { + uint32_t int_status; + int_status = spi_get_int_status(); + spi_clear_int_status(int_status); + tls_irq_enable(SPI_LS_IRQn); + } + + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_os_sem_release(spi_port->lock); + return TLS_SPI_STATUS_EINVAL; + } + MEMCPY((u8 *) SPI_DMA_BUF_ADDR, buf, len); + SpiDmaBlockWrite((u8 *) SPI_DMA_BUF_ADDR, len, 0, 0); + } + { + uint32_t int_status; + int_status = spi_get_int_status(); + spi_clear_int_status(int_status); + tls_irq_enable(SPI_LS_IRQn); + } + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_os_sem_release(spi_port->lock); + return TLS_SPI_STATUS_OK; + } + +#endif + memset(&t, 0, sizeof(t)); + t.tx_buf = buf; + t.len = len; + + spi_message_init(&m); + + dl_list_add_tail(&m.transfers, &t.transfer_list); + + return tls_spi_sync(&m); +} + +/** + * @brief This function is used to synchronous write 32bit command then write data by SPI. + * + * @param[in] cmd is the command data. + * @param[in] n_cmd is the command len,can not bigger than four + * @param[in] txbuf is the write data buffer. + * @param[in] n_tx is the write data length. + * + * @retval TLS_SPI_STATUS_OK if write success. + * @retval TLS_SPI_STATUS_EINVAL if argument is invalid. + * @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory. + * @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed. + * + * @note None + */ +int tls_spi_write_with_cmd(const u8 * cmd, u32 n_cmd, const u8 * txbuf, + u32 n_tx) +{ + int status; + struct tls_spi_message message; + struct tls_spi_transfer x[2]; + + if ((cmd == NULL) || (n_cmd == 0) || (txbuf == NULL) || (n_tx == 0)) + { + return TLS_SPI_STATUS_EINVAL; + } + +#ifdef SPI_USE_DMA + if (SPI_DMA_TRANSFER == spi_port->transtype) + { + if (n_tx > SPI_DMA_BUF_MAX_SIZE) + { + TLS_DBGPRT_ERR("\nwriten len too long\n"); + return TLS_SPI_STATUS_EINVAL; + } + tls_os_sem_acquire(spi_port->lock, 0); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_irq_disable(SPI_LS_IRQn); + MEMCPY((u8 *) SPI_DMA_BUF_ADDR, (u8 *) cmd, n_cmd); + MEMCPY((u8 *) (SPI_DMA_BUF_ADDR + n_cmd), txbuf, n_tx); + SpiDmaBlockWrite((u8 *) SPI_DMA_BUF_ADDR, (n_cmd + n_tx), 0, 0); + { + uint32_t int_status; + int_status = spi_get_int_status(); + spi_clear_int_status(int_status); + tls_irq_enable(SPI_LS_IRQn); + } + + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + tls_os_sem_release(spi_port->lock); + return TLS_SPI_STATUS_OK; + } +#endif + + spi_message_init(&message); + + memset(x, 0, sizeof(x)); + if (n_cmd) + { + x[0].len = n_cmd; + x[0].tx_buf = (const void *) cmd; + dl_list_add_tail(&message.transfers, &x[0].transfer_list); + } + if (n_tx) + { + x[1].len = n_tx; + x[1].tx_buf = txbuf; + dl_list_add_tail(&message.transfers, &x[1].transfer_list); + } + +/* do the i/o. */ + status = tls_spi_sync(&message); + + return status; +} + +/** + * @brief + * + * @param message + * + * @return + */ +int tls_spi_sync(struct tls_spi_message *message) +{ + int status; + u8 err; + tls_os_sem_t *sem; + + err = tls_os_sem_create(&sem, 0); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR + ("create spi transaction synchronizing semaphore fail!\n"); + return TLS_SPI_STATUS_ENOMEM; + } + + message->context = (void *) sem; + message->complete = spi_complete; + + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + status = tls_spi_async(message); + if (status == TLS_SPI_STATUS_OK) + { + TLS_DBGPRT_SPI_INFO("waiting spi transaction finishing!\n"); + tls_os_sem_acquire(sem, 0); + } + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + + tls_os_sem_delete(sem); + message->context = NULL; + message->complete = NULL; + + return status; +} + +/** + * @brief + * + * @param message + * + * @return + */ +int tls_spi_async(struct tls_spi_message *message) +{ + u8 need_sched; + struct tls_spi_transfer *transfer; + + if (spi_port == NULL) + { + TLS_DBGPRT_ERR("spi master driver module not beed installed!\n"); + return TLS_SPI_STATUS_ESHUTDOWN; + } + + if ((message == NULL) || (dl_list_empty(&message->transfers))) + { + TLS_DBGPRT_ERR("@message is NULL or @message->transfers is empty!\n"); + return TLS_SPI_STATUS_EINVAL; + } + + dl_list_for_each(transfer, &message->transfers, struct tls_spi_transfer, + transfer_list) + { + if (transfer->len == 0) + { + TLS_DBGPRT_ERR("\"@transfer->len\" belong to @message is 0!\n"); + return TLS_SPI_STATUS_EINVAL; + } + } + + tls_os_sem_acquire(spi_port->lock, 0); + + if (dl_list_empty(&spi_port->wait_queue)) + { + need_sched = 1; + } + else + { + need_sched = 0; + } + message->status = SPI_MESSAGE_STATUS_IDLE; + dl_list_add_tail(&spi_port->wait_queue, &message->queue); + + tls_os_sem_release(spi_port->lock); + + if (need_sched == 1) + { + tls_os_queue_send(spi_port->msg_queue, + (void *) SPI_SCHED_MSG_START_ENGINE, 4); + } + + return TLS_SPI_STATUS_OK; +} + + +/** + * @brief This function is used to initialize the SPI master driver. + * + * @param[in] None + * + * @retval TLS_SPI_STATUS_OK if initialize success + * @retval TLS_SPI_STATUS_EBUSY if SPI is already initialized + * @retval TLS_SPI_STATUS_ENOMEM if malloc SPI memory fail + * + * @note None + */ +int tls_spi_init(void) +{ + u8 err; + struct tls_spi_port *port; + + + if (spi_port != NULL) + { + TLS_DBGPRT_ERR("spi driver module has been installed!\n"); + return TLS_SPI_STATUS_EBUSY; + } + + TLS_DBGPRT_SPI_INFO("initialize spi master driver module.\n"); + + port = (struct tls_spi_port *) tls_mem_alloc(sizeof(struct tls_spi_port)); + if (port == NULL) + { + TLS_DBGPRT_ERR("allocate \"struct tls_spi_port\" fail!\n"); + return TLS_SPI_STATUS_ENOMEM; + } + + err = tls_os_sem_create(&port->lock, 1); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create semaphore @spi_port->lock fail!\n"); + tls_mem_free(port); + return TLS_SPI_STATUS_ENOMEM; + } + + err = tls_os_queue_create(&port->msg_queue, MSG_QUEUE_SIZE); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create message queue @spi_port->msg_queue fail!\n"); + tls_os_sem_delete(port->lock); + tls_mem_free(port); + return TLS_SPI_STATUS_ENOMEM; + } + + err = tls_os_task_create(NULL, "HOSTSPI", + spi_scheduler, + (void *) spi_port, + (void *) &spi_scheduler_stk, + SPI_SCHEDULER_STK_SIZE * sizeof(u32), + TLS_SPI_SCHEDULER_TASK_PRIO, 0); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create spi master driver scheduler task fail!\n"); + tls_os_sem_delete(port->lock); + tls_os_queue_delete(port->msg_queue); + tls_mem_free(port); + return TLS_SPI_STATUS_ENOMEM; + } + + port->speed_hz = SPI_DEFAULT_SPEED; /* Ĭ��2M */ + port->cs_active = SPI_CS_ACTIVE_MODE; + port->mode = SPI_DEFAULT_MODE; /* CPHA = 0,CPOL = 0 */ + port->reconfig = 0; + + dl_list_init(&port->wait_queue); + + port->current_message = NULL; + port->current_remaining_transfer = 0; + port->current_transfer = NULL; + port->current_remaining_bytes = 0; + + spi_port = port; + + TLS_DBGPRT_SPI_INFO("initialize spi master controller.\n"); + tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + spi_clear_fifo(); + spi_set_endian(1); + tls_spi_trans_type(SPI_BYTE_TRANSFER); + spi_set_mode(spi_port->mode); + spi_set_chipselect_mode(SPI_CS_INACTIVE_MODE); /* cs=1 ,Ƭѡ��Ч */ + spi_force_cs_out(1); /* Ƭѡ���������� */ + spi_set_sclk(spi_port->speed_hz); + + spi_set_tx_trigger_level(0); + spi_set_rx_trigger_level(7); + + spi_set_rx_channel(1); + spi_set_tx_channel(1); + spi_unmask_int(SPI_INT_TRANSFER_DONE /* | SPI_INT_RX_FIFO_RDY |SPI_INT_TX_FIFO_RDY */ ); + tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI); + + TLS_DBGPRT_SPI_INFO("register spi master interrupt handler.\n"); + + tls_irq_enable(SPI0_INT); + tls_irq_set_priority(SPI0_INT, IRQ_MAX_SYSCALL_PRIORITY); + + TLS_DBGPRT_SPI_INFO("spi master driver module initialization finish.\n"); + + return TLS_SPI_STATUS_OK; +} + +/** + * @brief + * + * @return + */ +int tls_spi_exit(void) +{ + TLS_DBGPRT_SPI_INFO("Not support spi master driver module uninstalled!\n"); + return TLS_SPI_STATUS_EPERM; +} + + + +/********************************************************************************************************** +* Description: This function is used to select SPI slave type. +* +* Arguments : slave is the slave type,defined as follow: +* slave == SPI_SLAVE_FLASH :flash +* slave == SPI_SLAVE_CARD : sd card +* +* Returns : Before communicate with different SPI device, must call the function. +**********************************************************************************************************/ +void tls_spi_slave_sel(u16 slave) +{ +// u16 ret; +/*gpio0����cs�ź�*/ + tls_gpio_cfg((enum tls_io_name) SPI_SLAVE_CONTROL_PIN, WM_GPIO_DIR_OUTPUT, + WM_GPIO_ATTR_FLOATING); + if (SPI_SLAVE_FLASH == slave) + { + tls_gpio_write((enum tls_io_name) SPI_SLAVE_CONTROL_PIN, 0); + // ret = tls_gpio_read(SPI_SLAVE_CONTROL_PIN); + // printf("\nflash gpio 0 ===%d\n",ret); + } + else if (SPI_SLAVE_CARD == slave) + { + tls_gpio_write((enum tls_io_name) SPI_SLAVE_CONTROL_PIN, 1); + // ret = tls_gpio_read(SPI_SLAVE_CONTROL_PIN); + // printf("\ncard gpio 0 ===%d\n",ret); + } +} diff --git a/sdk/Platform/Drivers/spi/wm_spi_hal.h b/sdk/Platform/Drivers/spi/wm_spi_hal.h new file mode 100644 index 0000000..b42d3c4 --- /dev/null +++ b/sdk/Platform/Drivers/spi/wm_spi_hal.h @@ -0,0 +1,303 @@ +/***************************************************************************** +* +* File Name : wm_spi_hal.h +* +* Description: host spi Driver Module +* +* Copyright (c) 2014 Winner Microelectronics Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-6-6 +*****************************************************************************/ +#include "wm_regs.h" +#include "list.h" +#include "wm_hostspi.h" +#include "wm_cpu.h" + + +static __inline void spi_set_mode(u8 mode); + + +static __inline void spi_set_mode(u8 mode) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_SPICFG_REG); + + switch (mode) { + case TLS_SPI_MODE_0: + reg_val &= ~(0x03U); + reg_val |= (SPI_SET_CPOL(0) | SPI_SET_CPHA(0)); + break; + + case TLS_SPI_MODE_1: + reg_val &= ~(0x03U); + reg_val |= (SPI_SET_CPOL(0) | SPI_SET_CPHA(1)); + break; + + case TLS_SPI_MODE_2: + reg_val &= ~(0x03U); + reg_val |= (SPI_SET_CPOL(1) | SPI_SET_CPHA(0)); + break; + + case TLS_SPI_MODE_3: + reg_val &= ~(0x03U); + reg_val |= (SPI_SET_CPOL(1) | SPI_SET_CPHA(1)); + break; + + default: + break; + } + + tls_reg_write32(HR_SPI_SPICFG_REG, reg_val); +} + +static __inline void spi_set_endian(u8 endian) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_SPICFG_REG); + + if (endian == 0) { + reg_val &= ~(0x01U << 3); + reg_val |= SPI_LITTLE_ENDIAN; + } else if(endian == 1) { + reg_val &= ~(0x01U << 3); + reg_val |= SPI_BIG_ENDIAN; + } + + tls_reg_write32(HR_SPI_SPICFG_REG, reg_val); +} + +static __inline void spi_set_chipselect_mode(u8 cs_active) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_CHCFG_REG); + + if (cs_active == 0) { + reg_val &= ~(0x01U << 2); + reg_val |= SPI_CS_LOW; + } else if(cs_active == 1) { + reg_val &= ~(0x01U << 2); + reg_val |= SPI_CS_HIGH; + } + + tls_reg_write32(HR_SPI_CHCFG_REG, reg_val); +} + +static __inline void spi_clear_fifo(void) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_CHCFG_REG); + + reg_val |= SPI_CLEAR_FIFOS; + + tls_reg_write32(HR_SPI_CHCFG_REG, reg_val); +} + +static __inline void spi_set_rx_channel(u8 on_off) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_CHCFG_REG); + + if (on_off == 0) { + reg_val &= ~(0x01U << 20); + reg_val |= SPI_RX_CHANNEL_OFF; + } else if(on_off == 1) { + reg_val &= ~(0x01U << 20); + reg_val |= SPI_RX_CHANNEL_ON; + } + + tls_reg_write32(HR_SPI_CHCFG_REG, reg_val); +} + +static __inline void spi_set_tx_channel(u8 on_off) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_CHCFG_REG); + + if (on_off == 0) { + reg_val &= ~(0x01U << 19); + reg_val |= SPI_TX_CHANNEL_OFF; + } else if(on_off == 1) { + reg_val &= ~(0x01U << 19); + reg_val |= SPI_TX_CHANNEL_ON; + } + + tls_reg_write32(HR_SPI_CHCFG_REG, reg_val); +} + +static __inline void spi_set_sclk_length(u16 sclk_num, u8 invalid_rx_sclk_num) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_CHCFG_REG); + + reg_val &= ~((0xffU << 23) | (0xffff << 3)); + reg_val |= SPI_VALID_CLKS_NUM(sclk_num) | SPI_RX_INVALID_BITS(invalid_rx_sclk_num); + + tls_reg_write32(HR_SPI_CHCFG_REG, reg_val); +} + +static __inline void spi_force_cs_out(u8 enable) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_CHCFG_REG); + + if (enable) { + reg_val |= SPI_FORCE_SPI_CS_OUT; + } else { + reg_val &= ~SPI_FORCE_SPI_CS_OUT; + } + + tls_reg_write32(HR_SPI_CHCFG_REG, reg_val); +} + +static __inline void spi_sclk_start(void) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_CHCFG_REG); + + reg_val |= SPI_START; + + tls_reg_write32(HR_SPI_CHCFG_REG, reg_val); +} + +static __inline void spi_set_sclk(u32 fclk) +{ + u32 reg_val; + tls_sys_clk sysclk; + + tls_sys_clk_get(&sysclk); + + reg_val = tls_reg_read32(HR_SPI_CLKCFG_REG); + + reg_val &= ~(0xffffU); + reg_val |= sysclk.apbclk*UNIT_MHZ/(fclk*2) - 1; + + tls_reg_write32(HR_SPI_CLKCFG_REG, reg_val); +} + +static __inline void spi_set_tx_trigger_level(u8 level) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_MODECFG_REG); + + reg_val &= ~(0x07U << 2); + reg_val |= SPI_TX_TRIGGER_LEVEL(level); + + tls_reg_write32(HR_SPI_MODECFG_REG, reg_val); +} + +static __inline void spi_set_rx_trigger_level(u8 level) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_MODECFG_REG); + + reg_val &= ~(0x07U << 6); + reg_val |= SPI_RX_TRIGGER_LEVEL(level); + + tls_reg_write32(HR_SPI_MODECFG_REG, reg_val); +} + +static __inline void spi_set_timeout(u32 timeout, u8 enable) +{ + u32 reg_val; + + reg_val = SPI_TIME_OUT(timeout); + reg_val |= enable ? SPI_TIMER_EN : 0; + + tls_reg_write32(HR_SPI_TIMEOUT_REG, reg_val); +} + +static __inline void spi_get_status(u8 *busy, u8 *rx_fifo_level, u8 *tx_fifo_level) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_STATUS_REG); + + if (busy) {*busy = SPI_IS_BUSY(reg_val);} + if (rx_fifo_level) {*rx_fifo_level = SPI_GET_RX_FIFO_CNT(reg_val);} + if (tx_fifo_level) {*tx_fifo_level = 32 - SPI_GET_TX_FIFO_CNT(reg_val);} +} + +static __inline u32 spi_int_mask(void) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_INT_MASK_REG); + + return reg_val & SPI_INT_MASK_ALL; +} + +static __inline void spi_mask_int(u32 mask) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_INT_MASK_REG); + + reg_val |= mask & SPI_INT_MASK_ALL; + + tls_reg_write32(HR_SPI_INT_MASK_REG, reg_val); +} + +static __inline void spi_unmask_int(u32 mask) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_INT_MASK_REG); + + reg_val &= ~(mask & SPI_INT_MASK_ALL); + + tls_reg_write32(HR_SPI_INT_MASK_REG, reg_val); +} + +static __inline u32 spi_get_int_status(void) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_INT_STATUS_REG); + + return reg_val; +} +static __inline uint8_t spi_i2s_get_busy_status(void) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_STATUS_REG); + + return SPI_IS_BUSY(reg_val); +} + +static __inline void spi_clear_int_status(u32 int_srcs) +{ + u32 reg_val; + + reg_val = tls_reg_read32(HR_SPI_INT_STATUS_REG); + + reg_val &= ~(int_srcs & SPI_INT_CLEAR_ALL); + reg_val |= int_srcs & SPI_INT_CLEAR_ALL; + + tls_reg_write32(HR_SPI_INT_STATUS_REG, reg_val); +} + +static __inline void spi_data_put(u32 data) +{ + tls_reg_write32(HR_SPI_TXDATA_REG, data); +} + +static __inline u32 spi_data_get(void) +{ + return tls_reg_read32(HR_SPI_RXDATA_REG); +} + diff --git a/sdk/Platform/Drivers/timer/wm_timer.c b/sdk/Platform/Drivers/timer/wm_timer.c new file mode 100644 index 0000000..fb41a4c --- /dev/null +++ b/sdk/Platform/Drivers/timer/wm_timer.c @@ -0,0 +1,319 @@ +/** + * @file wm_timer.c + * + * @brief Timer Driver Module + * + * @author dave + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#include "wm_type_def.h" +#include "wm_timer.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_cpu.h" + +#include "tls_common.h" + +enum tls_timer_id{ + TLS_TIMER_ID_0 = 0, // used by delay (sleep, msleep, usleep) + TLS_TIMER_ID_1, + TLS_TIMER_ID_2, + TLS_TIMER_ID_3, + TLS_TIMER_ID_4, + TLS_TIMER_ID_5, + TLS_TIMER_ID_MAX +}; + +#define TIM0_USED_BY_DELAY 0 + +#define WM_TIMER_IRQ_USE_LOOP 1 + +struct timer_irq_context { + tls_timer_irq_callback callback; + void *arg; +}; + +static struct timer_irq_context timer_context[TLS_TIMER_ID_MAX] = {{0,0}}; +u8 wm_timer_bitmap = 0; + +#if WM_TIMER_IRQ_USE_LOOP +static void timer_irq_callback(void *p) +{ + u8 i; + u32 value; + + value = tls_reg_read32(HR_TIMER0_5_CSR); + + value &= ~(TLS_TIMER_INT_CLR(TLS_TIMER_ID_0)); + + for (i = TLS_TIMER_ID_1; i < TLS_TIMER_ID_MAX; i++) + { + if (value & TLS_TIMER_INT_CLR(i)) + { + if (NULL != timer_context[i].callback) + timer_context[i].callback(timer_context[i].arg); + } + } + + tls_reg_write32(HR_TIMER0_5_CSR, value); + + return; +} +#else /* WM_TIMER_IRQ_USE_LOOP */ +static void timer_clear_irq(int timer_id) +{ + u8 i; + u32 value; + + value = tls_reg_read32(HR_TIMER0_5_CSR); + + for (i = TLS_TIMER_ID_0; i < TLS_TIMER_ID_MAX; i++) + { + value &= ~(TLS_TIMER_INT_CLR(i)); + } + + tls_reg_write32(HR_TIMER0_5_CSR, value | TLS_TIMER_INT_CLR(timer_id)); +} + +static void timer_irq_callback(void *p) +{ + u8 timer_id; + + timer_id = (u8)(u32)p; + + if (NULL != timer_context[timer_id].callback) + timer_context[timer_id].callback(timer_context[timer_id].arg); + + timer_clear_irq(timer_id); + + return; +} +#endif /* WM_TIMER_IRQ_USE_LOOP */ + +void TIM0_IRQHandler(void) +{ + timer_irq_callback((void *)TLS_TIMER_ID_0); +} +void TIM1_IRQHandler(void) +{ + timer_irq_callback((void *)TLS_TIMER_ID_1); +} +void TIM2_IRQHandler(void) +{ + timer_irq_callback((void *)TLS_TIMER_ID_2); +} +void TIM3_IRQHandler(void) +{ + timer_irq_callback((void *)TLS_TIMER_ID_3); +} +void TIM4_IRQHandler(void) +{ + timer_irq_callback((void *)TLS_TIMER_ID_4); +} +void TIM5_IRQHandler(void) +{ + timer_irq_callback((void *)TLS_TIMER_ID_5); +} + +/** + * @brief This function is used to create the timer + * + * @param[in] cfg timer configuration + * + * @retval WM_TIMER_ID_INVALID failed + * @retval other timer id[0~5] + * + * @note + * user not need clear interrupt flag. + * timer callback function is called in interrupt, + * so can not operate the critical data in the callback fuuction, + * recommendation to send messages to other tasks to operate it. + */ +u8 tls_timer_create(struct tls_timer_cfg *cfg) +{ + u8 i; + int timer_csr; + + for (i = TLS_TIMER_ID_1; i < TLS_TIMER_ID_MAX; i++) + { + if (!(wm_timer_bitmap & BIT(i))) + break; + } + + if (TLS_TIMER_ID_MAX == i) + return WM_TIMER_ID_INVALID; + + wm_timer_bitmap |= BIT(i); + timer_context[i].callback = cfg->callback; + timer_context[i].arg = cfg->arg; + + tls_sys_clk sysclk; + + tls_sys_clk_get(&sysclk); + tls_reg_write32(HR_TIMER_CFG, sysclk.apbclk-1); + + timer_csr = tls_reg_read32(HR_TIMER0_5_CSR); + if (!cfg->is_repeat) + timer_csr |= TLS_TIMER_ONE_TIME(i); + else + timer_csr &= ~(TLS_TIMER_ONE_TIME(i)); + if (TLS_TIMER_UNIT_MS == cfg->unit) + timer_csr |= TLS_TIMER_MS_UNIT(i); + else + timer_csr &= ~(TLS_TIMER_MS_UNIT(i)); + tls_reg_write32(HR_TIMER0_5_CSR, timer_csr | TLS_TIMER_INT_CLR(i)); + if(cfg->timeout){ + tls_reg_write32(HR_TIMER0_PRD + 0x04 * i, cfg->timeout); + } + + tls_irq_enable(TIMER0_INT + i); + + return i; +} + +/** + * @brief This function is used to start the timer + * + * @param[in] timer_id timer id[0~5] + * + * @return None + * + * @note None + */ +void tls_timer_start(u8 timer_id) +{ + if (!(wm_timer_bitmap & BIT(timer_id))) + return; + + tls_reg_write32(HR_TIMER0_5_CSR, tls_reg_read32(HR_TIMER0_5_CSR)|TLS_TIMER_INT_EN(timer_id)| TLS_TIMER_EN(timer_id)); + + return; +} + +/** + * @brief This function is used to stop the timer + * + * @param[in] timer_id timer id[0~5] + * + * @return None + * + * @note None + */ +void tls_timer_stop(u8 timer_id) +{ + if (!(wm_timer_bitmap & BIT(timer_id))) + return; + + tls_reg_write32(HR_TIMER0_5_CSR, tls_reg_read32(HR_TIMER0_5_CSR)|TLS_TIMER_INT_CLR(timer_id)); + tls_reg_write32(HR_TIMER0_5_CSR, tls_reg_read32(HR_TIMER0_5_CSR) &~ TLS_TIMER_EN(timer_id)); + + return; +} + +/** + * @brief This function is used to change a timer wait time + * + * @param[in] timer_id timer id[0~5] + * + * @param[in] newtime new wait time + * + * @retval None + * + * @note If the timer does not start, this function will start the timer + */ +void tls_timer_change(u8 timer_id, u32 newtime) +{ + if (!(wm_timer_bitmap & BIT(timer_id))) + return; + + tls_timer_stop(timer_id); + if (newtime) + tls_reg_write32(HR_TIMER0_PRD + 0x04 * timer_id, newtime); + tls_timer_start(timer_id); + + return; +} + +/** + * @brief This function is used to delete the timer + * + * @param[in] timer_id timer id[0~5] + * + * @return None + * + * @note None + */ +void tls_timer_destroy(u8 timer_id) +{ + if (!(wm_timer_bitmap & BIT(timer_id))) + return; + + tls_timer_stop(timer_id); + + timer_context[timer_id].callback = NULL; + timer_context[timer_id].arg = NULL; + + wm_timer_bitmap &= ~BIT(timer_id); + + return; +} + +/** + * @brief This function is create a delay to elapse + * + * @param[in] timeout the value writed into TMR0_PRD + * @param[in] m_flag millisecond flag in TIMER0 + * + * @return 0 + * + * @note None + */ +int tls_delay_via_timer(unsigned int timeout, unsigned int m_flag) +{ + int ret = 0; + tls_sys_clk sysclk; + int timer_csr = 0; + + if (0 == timeout) + return ret; + + /* + * no lock + * + */ + tls_irq_disable(TIMER0_INT + TIM0_USED_BY_DELAY); + tls_reg_write32(HR_TIMER0_PRD + 0x04 * TIM0_USED_BY_DELAY, + timeout); + + tls_sys_clk_get(&sysclk); + tls_reg_write32(HR_TIMER_CFG, sysclk.apbclk-1); + + timer_csr = tls_reg_read32(HR_TIMER0_5_CSR); + + if (TIMER_MS_UNIT_FLAG == m_flag) + timer_csr |= TLS_TIMER_MS_UNIT(TIM0_USED_BY_DELAY); + else if (TIMER_US_UNIT_FLAG == m_flag) + timer_csr &= ~(TLS_TIMER_MS_UNIT(TIM0_USED_BY_DELAY)); + + timer_csr |= TLS_TIMER_ONE_TIME(TIM0_USED_BY_DELAY); + timer_csr |= TLS_TIMER_INT_CLR(TIM0_USED_BY_DELAY); + timer_csr |= (TLS_TIMER_INT_EN(TIM0_USED_BY_DELAY)); + timer_csr |= TLS_TIMER_EN(TIM0_USED_BY_DELAY); + tls_reg_write32(HR_TIMER0_5_CSR, timer_csr); + + while (!(tls_reg_read32(HR_TIMER0_5_CSR) + & (TLS_TIMER_INT_CLR(TIM0_USED_BY_DELAY)))) + ; + + timer_csr |= TLS_TIMER_INT_CLR(TIM0_USED_BY_DELAY); + tls_reg_write32(HR_TIMER0_5_CSR, timer_csr); + tls_irq_enable(TIMER0_INT + TIM0_USED_BY_DELAY); + /* + * no unlock + * + */ + + return ret; +} + diff --git a/sdk/Platform/Drivers/uart/wm_uart.c b/sdk/Platform/Drivers/uart/wm_uart.c new file mode 100644 index 0000000..71cbfde --- /dev/null +++ b/sdk/Platform/Drivers/uart/wm_uart.c @@ -0,0 +1,1600 @@ +/** + * @file wm_uart.c + * + * @brief uart Driver Module + * + * @author dave + * + * Copyright (c) 2015 Winner Microelectronics Co., Ltd. + */ + +#include +#include "wm_regs.h" +#include "wm_uart.h" +#include "wm_debug.h" +#include "wm_irq.h" +#include "wm_config.h" +#include "wm_mem.h" +#include "wm_dma.h" +#include "wm_cpu.h" +#include "wm_pmu.h" +#include "wm_wifi.h" + +#include "misc.h" + +#if TLS_CONFIG_UART + +#define DEBUG_RX_LEN 0 +#define RX_CACHE_LIMIT 128 + +struct tls_uart_port uart_port[3]; +//void (*tx_sent_callback)(struct tls_uart_port *port) = NULL; +static void tls_uart_tx_chars(struct tls_uart_port *port); +extern s16 uart_tx_sent_callback(struct tls_uart_port *port); +void tls_uart_tx_callback_register(u16 uart_no, + s16(*tx_callback) (struct tls_uart_port * + port)); + +const u32 baud_rates[] = { + 2000000, 1500000, 1250000, + 1000000, 921600, 460800, + 230400, 115200, 57600, 38400, + 19200, 9600, 4800, 2400, + 1800, 1200, 600 +}; + +static void tls_uart_tx_enable(struct tls_uart_port *port); +static void tls_uart_tx_chars(struct tls_uart_port *port); + +void Debug_UartInit(void) +{ + u32 bd; + + u32 apbclk; + tls_sys_clk sysclk; + + tls_sys_clk_get(&sysclk); + apbclk = sysclk.apbclk * 1000000; + +/* baud rate register value = apb_clk/(16*115200) */ +/* ���APBʱ����40MHz�� */ +/* �����ʼĴ�����ֵ����Ϊ 115200 : 21 */ +/* 9600bps : 260 */ + bd = (apbclk / (16 * 115200) - + 1) | (((apbclk % (115200 * 16)) * 16 / (115200 * 16)) << 16); + #if WM_CONFIG_DEBUG_UART1 + { + tls_reg_write32(HR_UART1_BAUD_RATE_CTRL, bd); + /* Line control register : Normal,No parity,1 stop,8 bits, only use tx */ + tls_reg_write32(HR_UART1_LINE_CTRL, ULCON_WL8 | ULCON_TX_EN); + } + + #elif WM_CONFIG_DEBUG_UART2 + { + tls_reg_write32(HR_UART2_BAUD_RATE_CTRL, bd); + /* Line control register : Normal,No parity,1 stop,8 bits, only use tx */ + tls_reg_write32(HR_UART2_LINE_CTRL, ULCON_WL8 | ULCON_TX_EN); + } + #else + { + tls_reg_write32(HR_UART0_BAUD_RATE_CTRL, bd); + /* Line control register : Normal,No parity,1 stop,8 bits, only use tx */ + tls_reg_write32(HR_UART0_LINE_CTRL, ULCON_WL8 | ULCON_TX_EN); + + } + + #endif +} + +void UartRegInit(int uart_no) +{ + u32 bd; + u32 apbclk; + tls_sys_clk sysclk; + + tls_sys_clk_get(&sysclk); + apbclk = sysclk.apbclk * 1000000; + +/* baud rate register value = apb_clk/(16*115200) */ +/* ���APBʱ����40MHz�� */ +/* �����ʼĴ�����ֵ����Ϊ 115200 : 21 */ +/* 9600bps : 260 */ + if (TLS_UART_0 == uart_no) + { + bd = (apbclk / (16 * 115200) - 1) | (((apbclk % (115200 * 16)) * 16 / (115200 * 16)) << 16); + tls_reg_write32(HR_UART0_BAUD_RATE_CTRL, bd); + /* Line control register : Normal,No parity,1 stop,8 bits, only use tx */ + tls_reg_write32(HR_UART0_LINE_CTRL, ULCON_WL8 | ULCON_TX_EN); + + /* disable auto flow control */ + tls_reg_write32(HR_UART0_FLOW_CTRL, 0); + /* disable dma */ + tls_reg_write32(HR_UART0_DMA_CTRL, 0); + /* one byte tx */ + tls_reg_write32(HR_UART0_FIFO_CTRL, 0); + /* disable interrupt */ + tls_reg_write32(HR_UART0_INT_MASK, 0xFF); + } + + if (TLS_UART_1 == uart_no) + { + /* 4 byte tx, 8 bytes rx */ + tls_reg_write32(HR_UART1_FIFO_CTRL, (0x01 << 2) | (0x02 << 4)); + /* enable rx timeout, disable rx dma, disable tx dma */ + tls_reg_write32(HR_UART1_DMA_CTRL, (8 << 3) | (1 << 2)); + /* enable rx/timeout interrupt */ + tls_reg_write32(HR_UART1_INT_MASK, ~(3 << 2)); + + // uart1 + } + if (TLS_UART_2 == uart_no) + { + /* 4 byte tx, 8 bytes rx */ + tls_reg_write32(HR_UART2_FIFO_CTRL, (0x01 << 2) | (0x02 << 4)); + /* enable rx timeout, disable rx dma, disable tx dma */ + tls_reg_write32(HR_UART2_DMA_CTRL, (8 << 3) | (1 << 2)); + /* enable rx/timeout interrupt */ + tls_reg_write32(HR_UART2_INT_MASK, ~(3 << 2)); + } +} + +int tls_uart_check_baudrate(u32 baudrate) +{ + int i; + + for (i = 0; i < sizeof(baud_rates) / sizeof(u32); i++) + { + if (baudrate == baud_rates[i]) + return 1; + } +/* not found match baudrate */ + return -1; +} + +u32 tls_uart_get_baud_rate(struct tls_uart_port * port) +{ + if ((port != NULL) && (port->regs != NULL)) + return port->opts.baudrate; + else + return 0; +} + +int tls_uart_set_baud_rate_inside(struct tls_uart_port *port, u32 baudrate) +{ + int index; + u32 value; + u32 apbclk; + tls_sys_clk sysclk; + + + index = tls_uart_check_baudrate(baudrate); + if (index < 0) + return WM_FAILED; + port->regs->UR_FIFOC &= ~0x3; + port->regs->UR_FIFOC |= UFC_TX_FIFO_RESET|UFC_RX_FIFO_RESET; + tls_sys_clk_get(&sysclk); + apbclk = sysclk.apbclk * 1000000; + value = (apbclk / (16 * baudrate) - 1) | + (((apbclk % (baudrate * 16)) * 16 / (baudrate * 16)) << 16); + TLS_DBGPRT_INFO("value = 0x%x\n", value); + port->regs->UR_BD = value; + port->regs->UR_FIFOC &= ~0x3; + port->regs->UR_FIFOC |= UFC_TX_FIFO_RESET|UFC_RX_FIFO_RESET; + + port->opts.baudrate = baudrate; + TLS_DBGPRT_INFO("UR_BR = 0x%x\n", port->regs->UR_BD); + + return WM_SUCCESS; +} + + +int tls_uart_set_parity_inside(struct tls_uart_port *port, + TLS_UART_PMODE_T paritytype) +{ + if (port == NULL) + return WM_FAILED; + + port->opts.paritytype = paritytype; + + if (paritytype == TLS_UART_PMODE_DISABLED) + port->regs->UR_LC &= ~ULCON_PMD_EN; + else if (paritytype == TLS_UART_PMODE_EVEN) + { + port->regs->UR_LC &= ~ULCON_PMD_MASK; + port->regs->UR_LC |= ULCON_PMD_EVEN; + } + else if (paritytype == TLS_UART_PMODE_ODD) + { + port->regs->UR_LC &= ~ULCON_PMD_MASK; + port->regs->UR_LC |= ULCON_PMD_ODD; + } + else + return WM_FAILED; + + return WM_SUCCESS; + +} + +TLS_UART_PMODE_T tls_uart_get_parity(struct tls_uart_port * port) +{ + return port->opts.paritytype; +} + +int tls_uart_set_data_bits(struct tls_uart_port *port, + TLS_UART_CHSIZE_T charlength) +{ + if (!port) + return WM_FAILED; + + port->opts.charlength = charlength; + + port->regs->UR_LC &= ~ULCON_WL_MASK; + + if (charlength == TLS_UART_CHSIZE_5BIT) + port->regs->UR_LC |= ULCON_WL5; + else if (charlength == TLS_UART_CHSIZE_6BIT) + port->regs->UR_LC |= ULCON_WL6; + else if (charlength == TLS_UART_CHSIZE_7BIT) + port->regs->UR_LC |= ULCON_WL7; + else if (charlength == TLS_UART_CHSIZE_8BIT) + port->regs->UR_LC |= ULCON_WL8; + else + return WM_FAILED; + + return WM_SUCCESS; +} + +TLS_UART_CHSIZE_T tls_uart_get_data_bits(struct tls_uart_port * port) +{ + return port->opts.charlength; +} + +int tls_uart_set_stop_bits_inside(struct tls_uart_port *port, + TLS_UART_STOPBITS_T stopbits) +{ + if (!port) + return WM_FAILED; + + port->opts.stopbits = stopbits; + + if (stopbits == TLS_UART_TWO_STOPBITS) + port->regs->UR_LC |= ULCON_STOP_2; + else + port->regs->UR_LC &= ~ULCON_STOP_2; + + return WM_SUCCESS; +} + +TLS_UART_STOPBITS_T tls_uart_get_stop_bits(struct tls_uart_port * port) +{ + return port->opts.stopbits; +} + +TLS_UART_STATUS_T tls_uart_set_flow_ctrl(struct tls_uart_port * port, + TLS_UART_FLOW_CTRL_MODE_T flow_ctrl) +{ + TLS_UART_STATUS_T status = TLS_UART_STATUS_OK; + + if (!port) + return TLS_UART_STATUS_ERROR; + +// port->opts.flow_ctrl = flow_ctrl; +// //�����������޸ģ�Ϊ�����͸����ATָ��������Լ��޸�flowctrl���ã����Dz������ǹ̶������ +//printf("\nport %d flow ctrl==%d\n",port->uart_no,flow_ctrl); + switch (flow_ctrl) + { + case TLS_UART_FLOW_CTRL_NONE: + port->regs->UR_FC = 0; + break; + case TLS_UART_FLOW_CTRL_HARDWARE: + if (TLS_UART_FLOW_CTRL_HARDWARE == port->opts.flow_ctrl) + { + port->regs->UR_FC = (1UL << 0) | (6UL << 2); + } + break; + default: + return TLS_UART_STATUS_ERROR; + } + + return status; +} + +void tls_uart_set_fc_status(int uart_no, TLS_UART_FLOW_CTRL_MODE_T status) +{ + struct tls_uart_port *port; + + if (TLS_UART_0 == uart_no) + { + port = &uart_port[0]; + } + else if (TLS_UART_1 == uart_no) + { + port = &uart_port[1]; + } + else + { + port = &uart_port[2]; + } + + port->fcStatus = status; + //printf("\nset fc status=%d\n",status); + tls_uart_set_flow_ctrl(port, status); + if (TLS_UART_FLOW_CTRL_HARDWARE == port->opts.flow_ctrl && 0 == status && port->hw_stopped) // ׼���ر�����ʱ������tx�Ѿ�ֹͣ����Ҫ�ٴ�tx + { + tls_uart_tx_enable(port); + tls_uart_tx_chars(port); + port->hw_stopped = 0; + } +} + +void tls_uart_rx_disable(struct tls_uart_port *port) +{ + u32 ucon; + + ucon = port->regs->UR_LC; + ucon &= ~ULCON_RX_EN; + port->regs->UR_LC = ucon; +} + +void tls_uart_rx_enable(struct tls_uart_port *port) +{ + port->regs->UR_LC |= ULCON_RX_EN; +} + +static void tls_uart_tx_enable(struct tls_uart_port *port) +{ + u32 ucon; + +// printf("tx e\n"); + ucon = port->regs->UR_LC; + ucon |= ULCON_TX_EN; + port->regs->UR_LC = ucon; +} + +static void tls_uart_tx_disable(struct tls_uart_port *port) +{ + u32 ucon; + +// printf("tx d\n"); + ucon = port->regs->UR_LC; + ucon &= ~ULCON_TX_EN; + port->regs->UR_LC = ucon; +} + + +int tls_uart_config(struct tls_uart_port *port, struct tls_uart_options *opts) +{ + if (NULL == port || NULL == opts) + return WM_FAILED; +/* set the baud rate */ + tls_uart_set_baud_rate_inside(port, opts->baudrate); +/* set the parity */ + tls_uart_set_parity_inside(port, opts->paritytype); +/* set the number of data bits */ + tls_uart_set_data_bits(port, opts->charlength); +/* set the number of stop bits */ + tls_uart_set_stop_bits_inside(port, opts->stopbits); +/* Set flow control */ + port->opts.flow_ctrl = opts->flow_ctrl; + tls_uart_set_flow_ctrl(port, opts->flow_ctrl); + +/* config uart interrupt register */ +/* if (port->uart_mode == TLS_UART_MODE_INT) */// Ĭ��ʹ���жϵķ�ʽ + { + /* clear interrupt */ + port->regs->UR_INTS = 0xFFFFFFFF; + /* enable interupt */ + port->regs->UR_INTM = 0x0; + port->regs->UR_DMAC = + (4UL << UDMA_RX_FIFO_TIMEOUT_SHIFT) | UDMA_RX_FIFO_TIMEOUT; + } + +/* config FIFO control */ + port->regs->UR_FIFOC = UFC_TX_FIFO_LVL_16_BYTE | UFC_RX_FIFO_LVL_16_BYTE | + UFC_TX_FIFO_RESET | UFC_RX_FIFO_RESET; + port->regs->UR_LC &= ~(ULCON_TX_EN | ULCON_RX_EN); + port->regs->UR_LC |= ULCON_RX_EN | ULCON_TX_EN; + + return WM_SUCCESS; +} + +/** + * @brief handle a change of clear-to-send state + * @param[in] port: uart_port structure for the open port + * @param[in] status: new clear to send status, nonzero if active + */ +static void uart_handle_cts_change(struct tls_uart_port *port, + unsigned int status) +{ +//printf("\ncts change port->fcStatus=%d,port->opts.flow_ctrl=%d,status=%d\n",port->fcStatus,port->opts.flow_ctrl,status); + if (((1 == port->fcStatus) + && (port->opts.flow_ctrl == TLS_UART_FLOW_CTRL_HARDWARE)) + && (port->uart_no == TLS_UART_1)) + { + if (port->hw_stopped) + { + if (status) + { + port->hw_stopped = 0; + tls_uart_tx_enable(port); + tls_uart_tx_chars(port); + } + } + else + { + if (!status) + { + port->hw_stopped = 1; + tls_uart_tx_disable(port); + } + } + } +} + +int tls_uart_poll_put_char(struct tls_uart_port *port, int ch) +{ + if (ch == '\n') + { + while (port->regs->UR_FIFOS & 0x3F) /* wait THR is empty */ + ; + port->regs->UR_TXW = '\r'; /* output CR */ + } + while (port->regs->UR_FIFOS & 0x3F) /* wait THR is empty */ + ; + port->regs->UR_TXW = (char) ch; + + return ch; +} + +int tls_uart_poll_get_char(struct tls_uart_port *port) +{ + while ((port->regs->UR_FIFOS & UFS_RX_FIFO_CNT_MASK) >> 6) + return (int) port->regs->UR_RXW; + return 0; +} + +static void uart_tx_finish_callback(void *arg) +{ + if (arg) + { + tls_mem_free(arg); + } +} + +int tls_uart_tx_remain_len(struct tls_uart_port *port) +{ + tls_uart_tx_msg_t *tx_msg = NULL; + u16 buf_len = 0; + u32 cpu_sr; + cpu_sr = tls_os_set_critical(); + dl_list_for_each(tx_msg, &port->tx_msg_pending_list, tls_uart_tx_msg_t, + list) + { + buf_len += tx_msg->buflen; + } + tls_os_release_critical(cpu_sr); + return TLS_UART_TX_BUF_SIZE - buf_len; +} + +/** + * @brief This function is used to fill tx buffer. + * @param[in] port: is the uart port. + * @param[in] buf: is the user buffer. + * @param[in] count: is the user data length + * @retval + */ +int tls_uart_fill_buf(struct tls_uart_port *port, char *buf, u32 count) +{ + tls_uart_tx_msg_t *uart_tx_msg; +// int c; + int ret = 0; + u32 cpu_sr; + + uart_tx_msg = tls_mem_alloc(sizeof(tls_uart_tx_msg_t)); + if (uart_tx_msg == NULL) + { + TLS_DBGPRT_ERR("mem err\n"); + return -1; + } + dl_list_init(&uart_tx_msg->list); + uart_tx_msg->buf = tls_mem_alloc(count); + if (uart_tx_msg->buf == NULL) + { + tls_mem_free(uart_tx_msg); + TLS_DBGPRT_ERR("mem err 1 count=%d\n", count); + return -1; + } + memcpy(uart_tx_msg->buf, buf, count); + uart_tx_msg->buflen = count; + uart_tx_msg->offset = 0; + uart_tx_msg->finish_callback = uart_tx_finish_callback; + uart_tx_msg->callback_arg = uart_tx_msg->buf; + cpu_sr = tls_os_set_critical(); + dl_list_add_tail(&port->tx_msg_pending_list, &uart_tx_msg->list); + tls_os_release_critical(cpu_sr); + return ret; +} + +/** + * @brief free the data buffer has been transmitted. + * @param[in] port: is the uart port. + * @retval + */ +s16 tls_uart_free_tx_sent_data(struct tls_uart_port *port) +{ + tls_uart_tx_msg_t *tx_msg = NULL; + u32 cpu_sr = tls_os_set_critical(); + while (!dl_list_empty(&port->tx_msg_to_be_freed_list)) + { + tx_msg = dl_list_first(&port->tx_msg_to_be_freed_list, tls_uart_tx_msg_t, list); + dl_list_del(&tx_msg->list); + tls_os_release_critical(cpu_sr); + // printf("tx_msg=%p\n", tx_msg); + if (tx_msg->buf != NULL) + { + tx_msg->buf = NULL; + if (tx_msg->finish_callback) + tx_msg->finish_callback(tx_msg->callback_arg); + tls_mem_free(tx_msg); + } + cpu_sr = tls_os_set_critical(); + } + tls_os_release_critical(cpu_sr); + + return 0; +} + +/** + * @brief This function is used to start transfer data. + * @param[in] port: is the uart port. + * @retval + */ +void tls_uart_tx_chars_start(struct tls_uart_port *port) +{ + struct dl_list *pending_list = &port->tx_msg_pending_list; + tls_uart_tx_msg_t *tx_msg = NULL; + int tx_count; + u32 cpu_sr; + +/* send some chars */ + tx_count = 32; + cpu_sr = tls_os_set_critical(); + if (!dl_list_empty(pending_list)) + { + + tx_msg = dl_list_first(pending_list, tls_uart_tx_msg_t, list); + while (tx_count-- > 0 && tx_msg->offset < tx_msg->buflen) + { + /* ���tx fifo�Ƿ����� */ + if ((port->regs->UR_FIFOS & UFS_TX_FIFO_CNT_MASK) == + port->tx_fifofull) + { + break; + } + port->regs->UR_TXW = tx_msg->buf[tx_msg->offset]; + tx_msg->offset++; + port->icount.tx++; + } + + if (tx_msg->offset >= tx_msg->buflen) + { + dl_list_del(&tx_msg->list); + dl_list_add_tail(&port->tx_msg_to_be_freed_list, &tx_msg->list); + tls_os_release_critical(cpu_sr); + + // if(tx_sent_callback) + // tx_sent_callback(port); + if (port->tx_callback) + port->tx_callback(port); + }else{ + tls_os_release_critical(cpu_sr); + } + }else{ + tls_os_release_critical(cpu_sr); + + if (port->tx_sem) + tls_os_sem_release(port->tx_sem); + + } + + return; +} + +/** + * @brief This function is used to continue transfer data. + * @param[in] port: is the uart port. + * @retval + */ +static void tls_uart_tx_chars(struct tls_uart_port *port) +{ + struct dl_list *pending_list = &port->tx_msg_pending_list; + tls_uart_tx_msg_t *tx_msg = NULL; + int tx_count = 64; +// int len; +// u32 cpu_sr; + u8 fifofull = 0; + + if (dl_list_empty(pending_list)) + { + // tls_uart_tx_disable(port); + + if (port->tx_sem) + tls_os_sem_release(port->tx_sem); + + return; + } + +// cpu_sr = tls_os_set_critical(); + while (!dl_list_empty(pending_list) && !fifofull) + { + tx_msg = dl_list_first(pending_list, tls_uart_tx_msg_t, list); + while (tx_count-- > 0 && tx_msg->offset < tx_msg->buflen) + { + /* ���tx fifo�Ƿ����� */ + if ((port->regs->UR_FIFOS & UFS_TX_FIFO_CNT_MASK) == + port->tx_fifofull) + { + fifofull = 1; + break; + } + port->regs->UR_TXW = tx_msg->buf[tx_msg->offset]; + tx_msg->offset++; + port->icount.tx++; + } + if (tx_msg->offset >= tx_msg->buflen) + { + // cpu_sr = tls_os_set_critical(); + dl_list_del(&tx_msg->list); + dl_list_add_tail(&port->tx_msg_to_be_freed_list, &tx_msg->list); + // tls_os_release_critical(cpu_sr); + // if(tx_sent_callback) + // tx_sent_callback(port); + + if (port->tx_callback) + port->tx_callback(port); + } + } +// tls_os_release_critical(cpu_sr); +#if 0 + len = uart_circ_chars_pending(xmit); + if (len < WAKEUP_CHARS) + { + if (port->tx_callback) + port->tx_callback(port); + } + + + if (uart_circ_empty(xmit)) + tls_uart_tx_disable(port); +#endif +} + +#if DEBUG_RX_LEN +static int tls_rx_len = 0; + +int get_rx_len(void) +{ + return tls_rx_len; +} + +void clear_rx_len(void) +{ + tls_rx_len = 0; +} +#endif + +void tls_set_uart_rx_status(int uart_no, int status) +{ + u32 cpu_sr; + struct tls_uart_port *port; + + if (TLS_UART_1 == uart_no) + { + port = &uart_port[1]; + // TLS_DBGPRT_INFO("port%d set rx status=%d,prev + // rxstatus=%d\n",uart_no,status,port->rxstatus); + if ((TLS_UART_RX_DISABLE == port->rxstatus + && TLS_UART_RX_DISABLE == status) + || (TLS_UART_RX_ENABLE == port->rxstatus + && TLS_UART_RX_ENABLE == status)) + return; + + if (TLS_UART_RX_DISABLE == status) + { + // TLS_DBGPRT_INFO("\nopts + // flowctrl=%d,fcstatus=%d\n",port->opts.flow_ctrl,port->fcStatus); + if ((TLS_UART_FLOW_CTRL_HARDWARE == port->opts.flow_ctrl) + && (TLS_UART_FLOW_CTRL_HARDWARE == port->fcStatus)) + { + cpu_sr = tls_os_set_critical(); + // ��rxfifo trigger level interrupt��overrun error + port->regs->UR_INTM |= ((0x1 << 2) | (0x01 << 8)); + port->rxstatus = TLS_UART_RX_DISABLE; + tls_os_release_critical(cpu_sr); + // TLS_DBGPRT_INFO(" rx int mask status=%d\n",port->rxstatus); + } + } + else + { + // TLS_DBGPRT_INFO("start unmask\n"); + cpu_sr = tls_os_set_critical(); + uart_port[1].regs->UR_INTM &= ~((0x1 << 2) | (0x01 << 8)); + port->rxstatus = TLS_UART_RX_ENABLE; + tls_os_release_critical(cpu_sr); + // TLS_DBGPRT_INFO("rx int unmask status=%d\n",port->rxstatus); + } + } +} + +void UART0_IRQHandler(void) +{ + struct tls_uart_port *port = &uart_port[0]; + struct tls_uart_circ_buf *recv = &port->recv; + u32 intr_src; + u32 rx_fifocnt; + u32 fifos; + u8 ch; + u32 rxlen = 0; + +/* check interrupt status */ + intr_src = port->regs->UR_INTS; + port->regs->UR_INTS = intr_src; + + if ((intr_src & UART_RX_INT_FLAG) && (0 == (port->regs->UR_INTM & UIS_RX_FIFO))) + { + rx_fifocnt = (port->regs->UR_FIFOS >> 6) & 0x3F; +#if DEBUG_RX_LEN + tls_rx_len += rx_fifocnt; +#endif + while (rx_fifocnt-- > 0) + { + ch = (u8) port->regs->UR_RXW; + if (intr_src & UART_RX_ERR_INT_FLAG) + { + port->regs->UR_INTS |= UART_RX_ERR_INT_FLAG; + TLS_DBGPRT_INFO("\nrx err=%x,c=%d,ch=%x\n", intr_src, rx_fifocnt, ch); + /* not insert to buffer */ + continue; + } + if (CIRC_SPACE(recv->head, recv->tail, TLS_UART_RX_BUF_SIZE) <= 2) + { + TLS_DBGPRT_INFO("\nrx buf overrun int_src=%x\n", intr_src); + if (TLS_UART_FLOW_CTRL_HARDWARE == port->fcStatus) + { + tls_set_uart_rx_status(port->uart_no, TLS_UART_RX_DISABLE); + rx_fifocnt = 0; // �����Ӳ�����أ��رս��գ������һ���ַ��Ž�����buffer�� + } + else + break; + } + + /* insert the character into the buffer */ + recv->buf[recv->head] = ch; + recv->head = (recv->head + 1) & (TLS_UART_RX_BUF_SIZE - 1); + rxlen++; + } + if (port->rx_callback != NULL) + { + port->rx_callback((u16) rxlen); + } + } + + if (intr_src & UART_TX_INT_FLAG) + { + tls_uart_tx_chars(port); + } + + if (intr_src & UIS_CTS_CHNG) + { + fifos = port->regs->UR_FIFOS; + uart_handle_cts_change(port, fifos & UFS_CST_STS); + } + return; +} + +void UART1_IRQHandler(void) +{ + struct tls_uart_port *port = &uart_port[1]; + struct tls_uart_circ_buf *recv = &port->recv; + u32 intr_src; + u32 rx_fifocnt; + u32 fifos; + u8 ch = 0; + u8 escapefifocnt = 0; + u32 rxlen = 0; + +/* check interrupt status */ + intr_src = port->regs->UR_INTS; + port->regs->UR_INTS = intr_src; + + if (intr_src & UIS_OVERRUN) + { + port->regs->UR_INTS |= UIS_OVERRUN; + if(port->tx_dma_on) + { + tls_reg_write32((int)&port->regs->UR_DMAC, (tls_reg_read32((int)&port->regs->UR_DMAC) & ~0x01)); + tls_reg_write32((int)&port->regs->UR_DMAC, (tls_reg_read32((int)&port->regs->UR_DMAC) | 0x01)); + } + } + if ((intr_src & UART_RX_INT_FLAG) && (0 == (port->regs->UR_INTM & UIS_RX_FIFO))) + { + rx_fifocnt = (port->regs->UR_FIFOS >> 6) & 0x3F; +#if DEBUG_RX_LEN + tls_rx_len += rx_fifocnt; +#endif + escapefifocnt = rx_fifocnt; + port->plus_char_cnt = 0; + rxlen = rx_fifocnt; + + if (CIRC_SPACE(recv->head, recv->tail, TLS_UART_RX_BUF_SIZE) <= RX_CACHE_LIMIT) + { + recv->tail = (recv->tail + RX_CACHE_LIMIT) & (TLS_UART_RX_BUF_SIZE - 1); + } + + while (rx_fifocnt-- > 0) + { + ch = (u8) port->regs->UR_RXW; + if (intr_src & UART_RX_ERR_INT_FLAG) + { + port->regs->UR_INTS |= UART_RX_ERR_INT_FLAG; + TLS_DBGPRT_INFO("\nrx err=%x,c=%d,ch=%x\n", intr_src, rx_fifocnt, ch); + continue; + } + recv->buf[recv->head] = ch; + recv->head = (recv->head + 1) & (TLS_UART_RX_BUF_SIZE - 1); + } + + if( escapefifocnt==3 && ch=='+') + { + switch(recv->head-1) + { + case 0: + if(recv->buf[TLS_UART_RX_BUF_SIZE-1]=='+' && recv->buf[TLS_UART_RX_BUF_SIZE-2]=='+') + port->plus_char_cnt = 3; + break; + case 1: + if(recv->buf[0]=='+' && recv->buf[TLS_UART_RX_BUF_SIZE-1]=='+') + port->plus_char_cnt = 3; + break; + default: + if(recv->buf[recv->head-2]=='+' && recv->buf[recv->head-3]=='+') + port->plus_char_cnt = 3; + break; + } + } + if (port->rx_callback!=NULL) + { + port->rx_callback((u16) rxlen); + } + } + if (intr_src & UART_TX_INT_FLAG) + { + tls_uart_tx_chars(port); + } + if (intr_src & UIS_CTS_CHNG) + { + fifos = port->regs->UR_FIFOS; + uart_handle_cts_change(port, fifos & UFS_CST_STS); + } + return; +} + +void UART2_IRQHandler(void) +{ + struct tls_uart_port *port = &uart_port[2]; + struct tls_uart_circ_buf *recv = &port->recv; + u32 intr_src; + u32 rx_fifocnt; + u32 fifos; + u8 ch; + u32 rxlen = 0; + +/* check interrupt status */ + intr_src = port->regs->UR_INTS; + port->regs->UR_INTS = intr_src; + + if (intr_src & UIS_OVERRUN) + { + port->regs->UR_INTS |= UIS_OVERRUN; + if(port->tx_dma_on) + { + tls_reg_write32((int)&port->regs->UR_DMAC, (tls_reg_read32((int)&port->regs->UR_DMAC) & ~0x01)); + tls_reg_write32((int)&port->regs->UR_DMAC, (tls_reg_read32((int)&port->regs->UR_DMAC) | 0x01)); + } + } + if ((intr_src & UART_RX_INT_FLAG) && (0 == (port->regs->UR_INTM & UIS_RX_FIFO))) + { + rx_fifocnt = (port->regs->UR_FIFOS >> 6) & 0x3F; +#if DEBUG_RX_LEN + tls_rx_len += rx_fifocnt; +#endif + while (rx_fifocnt-- > 0) + { + ch = (u8) port->regs->UR_RXW; + /* break, stop bit error parity error, not include overrun err */ + if (intr_src & UART_RX_ERR_INT_FLAG) + { + port->regs->UR_INTS |= UART_RX_ERR_INT_FLAG; + TLS_DBGPRT_INFO("\nrx err=%x,c=%d,ch=%x\n", intr_src, rx_fifocnt, ch); + /* not insert to buffer */ + continue; + } + // if (CIRC_SPACE(recv->head, recv->tail, TLS_UART_RX_BUF_SIZE) == 0) + if (CIRC_SPACE(recv->head, recv->tail, TLS_UART_RX_BUF_SIZE) <= 2) + { + TLS_DBGPRT_INFO("\nrx buf overrun int_src=%x\n", intr_src); + if (TLS_UART_FLOW_CTRL_HARDWARE == port->fcStatus) + { + tls_set_uart_rx_status(port->uart_no, TLS_UART_RX_DISABLE); + rx_fifocnt = 0; // �����Ӳ�����أ��رս��գ������һ���ַ��Ž�����buffer�� + } + else + break; + } + + /* insert the character into the buffer */ + recv->buf[recv->head] = ch; + recv->head = (recv->head + 1) & (TLS_UART_RX_BUF_SIZE - 1); + rxlen++; + } + if(port->rx_callback != NULL && rxlen) + { + port->rx_callback((u16) rxlen); + } + + } + + if (intr_src & UART_TX_INT_FLAG) + { + tls_uart_tx_chars(port); + } + + if (intr_src & UIS_CTS_CHNG) + { + fifos = port->regs->UR_FIFOS; + uart_handle_cts_change(port, fifos & UFS_CST_STS); + } + return; +} + +/** + * @brief This function is used to initial uart port. + * + * @param[in] uart_no: is the uart number. + * - \ref TLS_UART_0 + * - \ref TLS_UART_1 + * - \ref TLS_UART_2 + * @param[in] opts: is the uart setting options,if this param is NULL,this function will use the default options. + * @param[in] modeChoose:; choose uart2 mode or 7816 mode when uart_no is TLS_UART_2, 0 for uart2 mode and 1 for 7816 mode. + * + * @retval + * - \ref WM_SUCCESS + * - \ref WM_FAILED + * + * @note When the system is initialized, the function has been called, so users can not call the function. + */ +int tls_uart_port_init(u16 uart_no, tls_uart_options_t * opts, u8 modeChoose) +{ + struct tls_uart_port *port; + int ret; + char *bufrx; // ,*buftx + tls_uart_options_t opt; + + + + UartRegInit(uart_no); + if (uart_no > TLS_UART_2) + { + return WM_FAILED; + } + + if (uart_port[uart_no].recv.buf) + { + tls_mem_free(uart_port[uart_no].recv.buf); + uart_port[uart_no].recv.buf = NULL; + } + + memset(&uart_port[uart_no], 0, sizeof(struct tls_uart_port)); + port = &uart_port[uart_no]; + + if (TLS_UART_0 == uart_no) + { + port->regs = (TLS_UART_REGS_T *) HR_UART0_BASE_ADDR; + } + else if (TLS_UART_1 == uart_no) + { + port->regs = (TLS_UART_REGS_T *) HR_UART1_BASE_ADDR; + } + else if (TLS_UART_2 == uart_no) + { + port->regs = (TLS_UART_REGS_T *) HR_UART2_BASE_ADDR; + (modeChoose == 1)?(port->regs->UR_LC |= (1 << 24)):(port->regs->UR_LC &= ~(0x1000000)); + } + + port->uart_no = uart_no; + + if (NULL == opts) + { + opt.baudrate = UART_BAUDRATE_B115200; + opt.charlength = TLS_UART_CHSIZE_8BIT; + opt.flow_ctrl = TLS_UART_FLOW_CTRL_NONE; + opt.paritytype = TLS_UART_PMODE_DISABLED; + opt.stopbits = TLS_UART_ONE_STOPBITS; + ret = tls_uart_config(port, &opt); + } + else + { + ret = tls_uart_config(port, opts); + } + + if (ret != WM_SUCCESS) + return WM_FAILED; + port->rxstatus = TLS_UART_RX_ENABLE; + port->uart_irq_no = (uart_no == TLS_UART_0) ? UART0_INT : ((uart_no == TLS_UART_1) ? UART1_INT : UART2_INT); + tls_irq_disable(port->uart_irq_no); + + if (port->recv.buf == NULL){ + bufrx = tls_mem_alloc(TLS_UART_RX_BUF_SIZE); + if (!bufrx) + return WM_FAILED; + memset(bufrx, 0, TLS_UART_RX_BUF_SIZE); + port->recv.buf = (u8 *) bufrx; + } + port->recv.head = 0; + port->recv.tail = 0; + port->tx_fifofull = 16; + dl_list_init(&port->tx_msg_pending_list); + dl_list_init(&port->tx_msg_to_be_freed_list); + tls_uart_tx_callback_register(uart_no, tls_uart_free_tx_sent_data); + +/* enable uart interrupt */ + tls_irq_enable(port->uart_irq_no); + return WM_SUCCESS; +} + +/** + * @brief This function is used to register uart rx interrupt. + * + * @param[in] uart_no: is the uart numer. + * @param[in] callback: is the uart rx interrupt call back function. + * + * @retval + * + * @note This function should be called after the fucntion tls_uart_port_init() or it won't work. + */ +void tls_uart_rx_callback_register(u16 uart_no, s16(*rx_callback) (u16 len)) +{ + if (TLS_UART_0 == uart_no) + uart_port[0].rx_callback = rx_callback; + else if (TLS_UART_1 == uart_no) + uart_port[1].rx_callback = rx_callback; + else if (TLS_UART_2 == uart_no) + uart_port[2].rx_callback = rx_callback; +} + +/** + * @brief This function is used to register uart tx interrupt. + * + * @param[in] uart_no: is the uart numer. + * @param[in] callback: is the uart tx interrupt call back function. + * + * @retval + */ +void tls_uart_tx_callback_register(u16 uart_no, + s16(*tx_callback) (struct tls_uart_port * + port)) +{ + if (TLS_UART_0 == uart_no) + uart_port[0].tx_callback = tx_callback; + else if (TLS_UART_1 == uart_no) + uart_port[1].tx_callback = tx_callback; + else if (TLS_UART_2 == uart_no) + uart_port[2].tx_callback = tx_callback; +} + +//void tls_uart_tx_sent_register(void (*tx_sent_callbk)(struct tls_uart_port *port)) +//{ +// tx_sent_callback = tx_sent_callbk; +//} + +/** + * @brief This function is used to copy circular buffer data to user buffer. + * @param[in] uart_no: is the uart numer. + * @param[in] buf: is the user buffer. + * @param[in] readsize: is the user read size. + * @retval -1:failed + * other:real length to written in buf + */ +int tls_uart_read(u16 uart_no, u8 * buf, u16 readsize) +{ + int data_cnt, buflen, bufcopylen; + struct tls_uart_port *port = NULL; + struct tls_uart_circ_buf *recv; + + if (NULL == buf || readsize < 1) + return WM_FAILED; + + if (TLS_UART_0 == uart_no) + port = &uart_port[0]; + else if (TLS_UART_1 == uart_no) + port = &uart_port[1]; + else if (TLS_UART_2 == uart_no) + port = &uart_port[2]; + + recv = &port->recv; + data_cnt = CIRC_CNT(recv->head, recv->tail, TLS_UART_RX_BUF_SIZE); + if (data_cnt >= readsize) + { + buflen = readsize; + } + else // ������ݲ�����ֱ�ӷ���0 + { + buflen = data_cnt; + } + if ((recv->tail + buflen) > TLS_UART_RX_BUF_SIZE) + { + bufcopylen = (TLS_UART_RX_BUF_SIZE - recv->tail); + MEMCPY(buf, recv->buf + recv->tail, bufcopylen); + MEMCPY(buf + bufcopylen, recv->buf, buflen - bufcopylen); + } + else + { + MEMCPY(buf, recv->buf + recv->tail, buflen); + } + recv->tail = (recv->tail + buflen) & (TLS_UART_RX_BUF_SIZE - 1); + return buflen; +} + + +u8 *testbuf; +u16 flag = 0; +u16 datalen = 0; + +#ifdef TEST_UART +/********************************************************************************************************** +* Description: This function is used to receive uart data. +* +* Arguments : buf is a buf for saving received data. +* +* Returns : Return received data length. +* Notes : +**********************************************************************************************************/ +s16 tls_uart_rx_test(u16 len) +{ + + if (datalen < (4096 - len)) + { + // MEMCPY(testbuf + datalen, buf, len); + tls_uart_read(TLS_UART_1, testbuf + datalen, len); + flag = 1; + datalen += len; + } + else + { + TLS_DBGPRT_INFO("\rx buffer over\n"); + } + return WM_SUCCESS; +} + + +void tls_uart_test(void) +{ + int len = 0; + + TLS_DBGPRT_INFO("\nentry tls_uart_test\n"); + testbuf = tls_mem_alloc(4096); + if (!testbuf) + return; + memset(testbuf, 0, 4096); +// tls_uart_cfg_user_mode(); + tls_uart_rx_callback_register(TLS_UART_1, tls_uart_rx_test); +#if 0 + tls_user_uart_set_baud_rate(UART_BAUDRATE_B38400); + tls_user_uart_set_stop_bits(TLS_UART_ONE_STOPBITS); + tls_user_uart_set_parity(TLS_UART_PMODE_DISABLED); +#endif + TLS_DBGPRT_INFO("\nentry while\n"); + while (1) + { + // TLS_DBGPRT_INFO("\n==============flag=%d\n",flag); + + OSTimeDly(0x100); + if (flag) + { + TLS_DBGPRT_INFO("\n\rrx data len = %d\n%s\n\r", datalen, testbuf); + // tls_uart_tx("\n\r",2); + tls_uart_write_sync(TLS_UART_1, testbuf, datalen); + len = tls_uart_tx_length(TLS_UART_1); + printf("\ntx len=%d\n", len); + + len = tls_uart_tx_length(); + printf("\ntx len 0=%d\n", len); + // tls_uart_tx("\n\r",2); + tls_uart_write(TLS_UART_1, "abcde", 5); + len = tls_uart_tx_length(TLS_UART_1); + printf("\ntx len 1=%d\n", len); + flag = 0; + datalen = 0; + } + // len = tls_uart_tx_length(); + // printf("\ntx len 2=%d\n",len); + + } + +} + +#endif +/********************************************************************************************************** +* Description: This function is used to config uart port for user mode. +* +* Arguments : +* +* Returns : +* Notes : If user want to use uart function,must call this function. +**********************************************************************************************************/ +#if 0 +void tls_uart_cfg_user_mode(void) +{ + struct tls_uart_port *port = &uart_port[1]; + + port->ifusermode = 1; + memset(port->recv.buf, 0, TLS_UART_RX_BUF_SIZE); + port->recv.head = 0; + port->recv.tail = 0; + port->icount.rx = 0; + + port->rx_callback_bk = port->rx_callback; + port->rx_callback = NULL; + + port->tx_callback_bk = port->tx_callback; + port->tx_callback = NULL; +} +#endif +/********************************************************************************************************** +* Description: This function is used to disable uart user mode. +* +* Arguments : +* +* Returns : +* Notes : If user want to resume uart old function for AT commond,must call this function. +**********************************************************************************************************/ +#if 0 +void tls_uart_disable_user_mode(void) +{ + struct tls_uart_port *port = &uart_port[1]; + + port->ifusermode = 0; + memset(port->recv.buf, 0, TLS_UART_RX_BUF_SIZE); + port->recv.head = 0; + port->recv.tail = 0; + + port->rx_callback = port->rx_callback_bk; + port->rx_callback_bk = NULL; + + port->tx_callback = port->tx_callback_bk; + port->tx_callback_bk = NULL; +} +#endif + +#if 0 +static s16 tls_uart_tx_cb(struct tls_uart_port *port) +{ + int ret_len = 0; + + if (NULL == port) + return WM_FAILED; + if (port->buf_len > 0) // uart1,���������ݴ������֮��ʣ��Ĵ����������ٷ��뻺���� + { + ret_len = tls_uart_fill_buf(port, port->buf_ptr, port->buf_len); + // TLS_DBGPRT_INFO("\ntx cb write len=%d",ret_len); + + if (ret_len >= 0) + { + if (port->buf_len >= ret_len) + { + port->buf_len -= ret_len; + port->buf_ptr += ret_len; + } + } + } + + return WM_SUCCESS; +} +#endif + + +/** + * @brief This function is used to transfer data throuth DMA. + * + * @param[in] buf is a buf for saving user data + * @param[in] writesize is the user data length + * @param[in] cmpl_callback function point,when the transfer is completed, the function will be called. + * @param[in] uart_no is the uart number + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note Only uart1 support DMA transfer. + */ +int tls_uart_dma_write(char *buf, u16 writesize, void (*cmpl_callback) (void *p), u16 uart_no) +{ + unsigned char dmaCh = 0; + struct tls_dma_descriptor DmaDesc; + struct tls_uart_port *port = &uart_port[uart_no]; + + if (NULL == buf || writesize < 1 || writesize >= 4096) + { + TLS_DBGPRT_ERR("param err\n"); + return WM_FAILED; + } + if (port->tx_dma_on) + { + TLS_DBGPRT_ERR("transmiting,wait\n"); + return WM_FAILED; + } +// port->tx_dma_cmpl_callbk = cmpl_callback; + + tls_reg_write32(HR_DMA_CHNL_SEL, uart_no); + +/* Request DMA Channel */ + dmaCh = tls_dma_request(2, + TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_UART_TX) | TLS_DMA_FLAGS_HARD_MODE); + if (dmaCh != 2) + { + TLS_DBGPRT_ERR("dma request err\n"); + return WM_FAILED; + } + tls_reg_write32((int)&port->regs->UR_DMAC, (tls_reg_read32((int)&port->regs->UR_DMAC) & ~0x01)); + tls_dma_irq_register(dmaCh, cmpl_callback, (void *)(uintptr_t)uart_no, TLS_DMA_IRQ_TRANSFER_DONE); + DmaDesc.src_addr = (int) buf; + DmaDesc.dest_addr = (int)&port->regs->UR_TXW; + DmaDesc.dma_ctrl = TLS_DMA_DESC_CTRL_SRC_ADD_INC | TLS_DMA_DESC_CTRL_DATA_SIZE_BYTE | + (writesize << 7); + DmaDesc.valid = TLS_DMA_DESC_VALID; + DmaDesc.next = NULL; + tls_dma_start(dmaCh, &DmaDesc, 0); + /* Enable uart TX DMA */ + port->tx_dma_on = TRUE; + tls_reg_write32((int)&port->regs->UR_DMAC, (tls_reg_read32((int)&port->regs->UR_DMAC) | 0x01)); + tls_reg_write32(HR_DMA_INT_MASK, + (tls_reg_read32(HR_DMA_INT_MASK) & ~(0x01 << 5))); + +/* enable dma interrupt */ + tls_irq_enable(DMA2_INT); + + return WM_SUCCESS; +} + +/** + * @brief This function is used to transfer data asynchronous. + * + * @param[in] uart_no is the uart number + * @param[in] buf is a buf for saving user data. + * @param[in] writesize is the data length + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note The function only start transmission, fill buffer in the callback function. + */ +int tls_uart_write_async(u16 uart_no, char *buf, u16 writesize) +{ + struct tls_uart_port *port = NULL; + int ret; + + if (NULL == buf || writesize < 1) + { + TLS_DBGPRT_ERR("param err\n"); + return WM_FAILED; + } + + if (TLS_UART_1 == uart_no) + port = &uart_port[1]; + else if (TLS_UART_0 == uart_no) + port = &uart_port[0]; + else if (TLS_UART_2 == uart_no) + port = &uart_port[2]; + + ret = tls_uart_fill_buf(port, buf, writesize); + + if (0 == ret) + { + tls_uart_tx_chars_start(port); + } + + return ret; +} + +/** + * @brief get the data length has been transmitted. + * + * @param[in] uart_no is the uart number + * + * @retval the length has been transmitted + * + */ +int tls_uart_tx_length(u16 uart_no) +{ + struct tls_uart_port *port = &uart_port[uart_no]; + + return port->icount.tx; +} + +/** + * @brief This function is used to transfer data synchronous. + * + * @param[in] uart_no is the uart number + * @param[in] buf is a buf for saving user data. + * @param[in] writesize is the data length + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + * @note The function only start transmission, fill buffer in the callback function. + */ +int tls_uart_write(u16 uart_no, char *buf, u16 writesize) +{ + struct tls_uart_port *port = NULL; + u8 err; + int ret = 0; + + if (TLS_UART_1 == uart_no) + port = &uart_port[1]; + else if (TLS_UART_0 == uart_no) + port = &uart_port[0]; + else if (TLS_UART_2 == uart_no) + port = &uart_port[2]; + + err = tls_os_sem_create(&port->tx_sem, 0); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("\ntx sem create fail\n"); + return WM_FAILED; + } + + ret = tls_uart_write_async(uart_no, buf, writesize); + if (0 == ret) + { + tls_os_sem_acquire(port->tx_sem, 0); + } + tls_os_sem_delete(port->tx_sem); + port->tx_sem = NULL; + + return WM_SUCCESS; +} + + +/********************************************************************************************************** +* Description: This function is used to register uart rx interrupt. +* +* Arguments : callback is the uart rx interrupt call back function. +* +* Returns : +* +**********************************************************************************************************/ +#if 0 +void tls_user_uart_rx_register(s16(*rx_callback) (char *buf, u16 len)) +{ + tls_uart_rx_callback_register(TLS_UART_1, rx_callback); +} +#endif +/********************************************************************************************************** +* Description: This function is used to register uart tx interrupt. +* +* Arguments : callback is the uart tx interrupt call back function. +* +* Returns : +* +**********************************************************************************************************/ +#if 0 +void tls_user_uart_tx_register(s16(*tx_callback) (struct tls_uart_port * port)) +{ + tls_uart_tx_callback_register(TLS_UART_1, tx_callback); +} +#endif +/** + * @brief This function is used to set uart parity. + * + * @param[in] paritytype is a parity type defined in TLS_UART_PMODE_T + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + */ +int tls_uart_set_parity(u16 uart_no, TLS_UART_PMODE_T paritytype) +{ + if (TLS_UART_1 == uart_no) + return tls_uart_set_parity_inside(&uart_port[1], paritytype); + else if (TLS_UART_0 == uart_no) + return tls_uart_set_parity_inside(&uart_port[0], paritytype); + else if (TLS_UART_2 == uart_no) + return tls_uart_set_parity_inside(&uart_port[2], paritytype); + return WM_FAILED; +} + +/** + * @brief This function is used to set uart baudrate. + * + * @param[in] uart_no is the uart number + * @param[in] baudrate is the baudrate user want used,the unit is HZ. + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + */ +int tls_uart_set_baud_rate(u16 uart_no, u32 baudrate) +{ + if (TLS_UART_1 == uart_no) + return tls_uart_set_baud_rate_inside(&uart_port[1], baudrate); + else if (TLS_UART_0 == uart_no) + return tls_uart_set_baud_rate_inside(&uart_port[0], baudrate); + else if (TLS_UART_2 == uart_no) + return tls_uart_set_baud_rate_inside(&uart_port[2], baudrate); + return WM_FAILED; +} + +/** + * @brief This function is used to set uart stop bits. + * + * @param[in] uart_no is the uart number + * @param[in] stopbits is a stop bit type defined in TLS_UART_STOPBITS_T. + * + * @retval WM_SUCCESS success + * @retval WM_FAILED failed + * + */ +int tls_uart_set_stop_bits(u16 uart_no, TLS_UART_STOPBITS_T stopbits) +{ + if (TLS_UART_1 == uart_no) + return tls_uart_set_stop_bits_inside(&uart_port[1], stopbits); + else if (TLS_UART_0 == uart_no) + return tls_uart_set_stop_bits_inside(&uart_port[0], stopbits); + else if (TLS_UART_2 == uart_no) + return tls_uart_set_stop_bits_inside(&uart_port[2], stopbits); + return WM_FAILED; +} + +int tls_uart_dma_off(u16 uart_no) +{ + uart_port[uart_no].tx_dma_on = FALSE; + return WM_SUCCESS; +} + +int tls_uart_output_char(int ch) +{ +#if WM_CONFIG_DEBUG_UART1 + tls_reg_write32(HR_UART1_INT_MASK, 0x3); + if(ch == '\n') + { + while (tls_reg_read32(HR_UART1_FIFO_STATUS)&0x3F); + tls_reg_write32(HR_UART1_TX_WIN, '\r'); + } + while(tls_reg_read32(HR_UART1_FIFO_STATUS)&0x3F); + tls_reg_write32(HR_UART1_TX_WIN, (char)ch); + tls_reg_write32(HR_UART1_INT_MASK, 0x0); +#elif WM_CONFIG_DEBUG_UART2 + tls_reg_write32(HR_UART2_INT_MASK, 0x3); + if(ch == '\n') + { + while (tls_reg_read32(HR_UART2_FIFO_STATUS)&0x3F); + tls_reg_write32(HR_UART2_TX_WIN, '\r'); + } + while(tls_reg_read32(HR_UART2_FIFO_STATUS)&0x3F); + tls_reg_write32(HR_UART2_TX_WIN, (char)ch); + tls_reg_write32(HR_UART2_INT_MASK, 0x0); +#else + tls_reg_write32(HR_UART0_INT_MASK, 0x3); + if(ch == '\n') + { + while (tls_reg_read32(HR_UART0_FIFO_STATUS)&0x3F); + tls_reg_write32(HR_UART0_TX_WIN, '\r'); + } + while(tls_reg_read32(HR_UART0_FIFO_STATUS)&0x3F); + tls_reg_write32(HR_UART0_TX_WIN, (char)ch); + tls_reg_write32(HR_UART0_INT_MASK, 0x0); +#endif + + return ch; +} +#endif +//TLS_CONFIG_UART diff --git a/sdk/Platform/Drivers/watchdog/wm_watchdog.c b/sdk/Platform/Drivers/watchdog/wm_watchdog.c new file mode 100644 index 0000000..5f261c9 --- /dev/null +++ b/sdk/Platform/Drivers/watchdog/wm_watchdog.c @@ -0,0 +1,160 @@ +/** + * @file wm_watchdog.c + * + * @brief watchdog Driver Module + * + * @author kevin + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + */ +#include "wm_debug.h" +#include "wm_regs.h" +#include "wm_irq.h" +#include "wm_cpu.h" +#include "wm_watchdog.h" + +static u32 wdg_jumpclear_flag = 0; /*0:donot jump clear, 1: jump clear, 2:close wdg*/ +// void WDG_IRQHandler(void) +// { +// printf("WDG IRQ\n"); while(1) {}; +// } + +/** + * @brief This function is used to clear watchdog irq in case watchdog reset + * + * @param None + * + * @return None + * + * @note None + */ +void tls_watchdog_clr(void) +{ + if (0 == wdg_jumpclear_flag) + { + tls_reg_write32(HR_WDG_INT_CLR, 0x01); + } +} + +/** + * @brief This function is used to init watchdog + * + * @param[in] usec microseconds + * + * @return None + * + * @note None + */ +void tls_watchdog_init(u32 usec) +{ + +// tls_sys_clk sysclk; +// u32 apbclkcoff = 0; + +// tls_sys_clk_get(&sysclk); + tls_irq_enable(WATCHDOG_INT); + + //apbclkcoff = 40/sysclk.apbclk; + + tls_reg_write32(HR_WDG_LOAD_VALUE, 40 * usec); /* 40M dominant frequency: 40 * 10^6 * (usec / 10^6) */ + tls_reg_write32(HR_WDG_CTRL, 0x3); /* enable irq & reset */ +} + + +/** + * @brief This function is used to deinit watchdog + * + * @param[in] None + * + * @return None + * + * @note None + */ +void tls_watchdog_deinit(void) +{ + tls_irq_disable(WATCHDOG_INT); + tls_reg_write32(HR_WDG_CTRL, 0); + tls_reg_write32(HR_WDG_INT_CLR, 0x01); +} + +/** + * @brief This function is used to start calculating elapsed time. + * + * @param[in] None + * + * @return elapsed time, unit:millisecond + * + * @note None + */ +void tls_watchdog_start_cal_elapsed_time(void) +{ + if (tls_reg_read32(HR_WDG_CTRL)&0x3) + { + wdg_jumpclear_flag = 1; + tls_reg_write32(HR_WDG_INT_CLR, 0x01); + } + else + { + wdg_jumpclear_flag = 2; + tls_watchdog_init(5*1000*1000); + } +} + +/** + * @brief This function is used to stop calculating & return elapsed time. + * + * @param[in] none + * + * @return elapsed time, unit:millisecond + * + * @note None + */ +u32 tls_watchdog_stop_cal_elapsed_time(void) +{ +#define RT_TIME_BASE (40) + u32 val = 0; + + switch (wdg_jumpclear_flag) + { + case 1: + { + val = (tls_reg_read32(HR_WDG_LOAD_VALUE) - tls_reg_read32(HR_WDG_CUR_VALUE))/RT_TIME_BASE; + wdg_jumpclear_flag = 0; + } + break; + + case 2: + { + val = (tls_reg_read32(HR_WDG_LOAD_VALUE) - tls_reg_read32(HR_WDG_CUR_VALUE))/RT_TIME_BASE; + wdg_jumpclear_flag = 0; + tls_watchdog_deinit(); + } + break; + + default: + wdg_jumpclear_flag = 0; + break; + } + + return val; +} + +/** + * @brief This function is used to reset system + * + * @param None + * + * @return None + * + * @note None + */ +void tls_sys_reset(void) +{ + tls_sys_clk_set(CPU_CLK_40M); + tls_os_set_critical(); + tls_reg_write32(HR_WDG_LOCK, 0x1ACCE551); + tls_reg_write32(HR_WDG_LOAD_VALUE, 0x100); + tls_reg_write32(HR_WDG_CTRL, 0x3); + tls_reg_write32(HR_WDG_LOCK, 1); +} + diff --git a/sdk/Platform/Drivers/wm_driver.c b/sdk/Platform/Drivers/wm_driver.c new file mode 100644 index 0000000..d33f8ec --- /dev/null +++ b/sdk/Platform/Drivers/wm_driver.c @@ -0,0 +1,26 @@ +#ifndef WM_DRIVER +#define WM_DRIVER +#include "../Platform/Drivers/uart/wm_uart.c" +#include "../Platform/Drivers/timer/wm_timer.c" +#include "../Platform/Drivers/spi/wm_hostspi.c" +#include "../Platform/Drivers/rtc/wm_rtc.c" +#include "../Platform/Drivers/pwm/wm_pwm.c" +#include "../Platform/Drivers/irq/wm_irq.c" +#include "../Platform/Drivers/i2c/wm_i2c.c" +#include "../Platform/Drivers/hspi/wm_hspi.c" +#include "../Platform/Drivers/gpio/wm_gpio.c" +#include "../Platform/Drivers/efuse/wm_efuse.c" +#include "../Platform/Drivers/dma/wm_dma.c" +#include "../Platform/Drivers/cpu/wm_cpu.c" +#include "../Platform/Drivers/adc/wm_adc.c" +#include "../Platform/Drivers/io/wm_io.c" +#include "../Platform/Drivers/i2s/wm_i2s.c" +#include "../Platform/Drivers/lcd/wm_lcd.c" +#include "../Platform/Drivers/pmu/wm_pmu.c" +#include "../Platform/Drivers/7816/wm_7816.c" +#include "../Platform/Drivers/internalflash/wm_internal_fls.c" +#include "../Platform/Drivers/flash/wm_fls.c" +#include "../Platform/Drivers/flash/wm_fls_gd25qxx.c" +#include "../Platform/Drivers/watchdog/wm_watchdog.c" +#include "../Platform/Drivers/gpio/wm_gpio_afsel.c" +#endif //WM_DRIVER diff --git a/sdk/Platform/Inc/bitops.h b/sdk/Platform/Inc/bitops.h new file mode 100644 index 0000000..f3bcbc2 --- /dev/null +++ b/sdk/Platform/Inc/bitops.h @@ -0,0 +1,116 @@ + +#ifndef BITOPS_H +#define BITOPS_H + +#include "wm_osal.h" + +/* + * These functions are the basis of our bit ops. + * + * First, the atomic bitops. These use native endian. + */ +static __inline void set_bit(unsigned int bit, volatile unsigned long *p) +{ + u32 cpu_sr; + unsigned long mask = 1 << (bit & 31); + + p += bit >> 5; + + cpu_sr = tls_os_set_critical(); + *p |= mask; + tls_os_release_critical(cpu_sr); +} + +static __inline void clear_bit(unsigned int bit, volatile unsigned long *p) +{ + u32 cpu_sr; + unsigned long mask = 1 << (bit & 31); + + p += bit >> 5; + + cpu_sr = tls_os_set_critical(); + *p &= ~mask; + tls_os_release_critical(cpu_sr); +} + +static __inline void change_bit(unsigned int bit, volatile unsigned long *p) +{ + u32 cpu_sr; + unsigned long mask = 1 << (bit & 31); + + p += bit >> 5; + + cpu_sr = tls_os_set_critical(); + *p ^= mask; + tls_os_release_critical(cpu_sr); +} + +static __inline int +test_and_set_bit(unsigned int bit, volatile unsigned long *p) +{ + u32 cpu_sr; + unsigned int res; + unsigned long mask = 1 << (bit & 31); + + p += bit >> 5; + + cpu_sr = tls_os_set_critical(); + res = *p; + *p = res | mask; + tls_os_release_critical(cpu_sr); + + return res & mask; +} + +static __inline int +test_and_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + u32 cpu_sr; + unsigned int res; + unsigned long mask = 1 << (bit & 31); + + p += bit >> 5; + + cpu_sr = tls_os_set_critical(); + res = *p; + *p = res & ~mask; + tls_os_release_critical(cpu_sr); + + return res & mask; +} + +static __inline int +test_bit(unsigned int bit, volatile unsigned long *p) +{ + u32 cpu_sr; + unsigned int res; + unsigned long mask = 1 << (bit & 31); + + p += bit >> 5; + + cpu_sr = tls_os_set_critical(); + res = *p; + tls_os_release_critical(cpu_sr); + + return res & mask; +} + +static __inline int +test_and_change_bit(unsigned int bit, volatile unsigned long *p) +{ + u32 cpu_sr; + unsigned int res; + unsigned long mask = 1 << (bit & 31); + + p += bit >> 5; + + cpu_sr = tls_os_set_critical(); + res = *p; + *p = res ^ mask; + tls_os_release_critical(cpu_sr); + + return res & mask; +} + + +#endif /* BITOPS_H */ diff --git a/sdk/Platform/Inc/libtommath.h b/sdk/Platform/Inc/libtommath.h new file mode 100644 index 0000000..3374352 --- /dev/null +++ b/sdk/Platform/Inc/libtommath.h @@ -0,0 +1,192 @@ +#ifndef __LIBTOMMATH_H__ +#define __LIBTOMMATH_H__ + +typedef signed short int16; + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define BN_MP_INVMOD_C +#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would + * require BN_MP_EXPTMOD_FAST_C instead */ +#define BN_S_MP_MUL_DIGS_C +#define BN_MP_INVMOD_SLOW_C +#define BN_S_MP_SQR_C +#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this + * would require other than mp_reduce */ + +#ifdef LTM_FAST + +/* Use faster div at the cost of about 1 kB */ +#define BN_MP_MUL_D_C + +/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */ +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MUL_2_C + +/* Include faster sqr at the cost of about 0.5 kB in code */ +#define BN_FAST_S_MP_SQR_C + +#else /* LTM_FAST */ + +#define BN_MP_DIV_SMALL +#define BN_MP_INIT_MULTI_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_ABS_C +#endif /* LTM_FAST */ + +/* Current uses do not require support for negative exponent in exptmod, so we + * can save about 1.5 kB in leaving out invmod. */ +#define LTM_NO_NEG_EXP + +/* from tommath.h */ + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#define OPT_CAST(x) + +typedef unsigned int mp_digit; +//typedef u64 mp_word; +typedef unsigned long long mp_word; + +#define XMALLOC tls_mem_alloc +#define XFREE tls_mem_free +#define XREALLOC os_realloc + + +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) + +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +typedef int mp_err; + +/* define this to use lower memory usage routines (exptmods mostly) */ +#define MP_LOW_MEM + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int16 used, alloc, sign; + mp_digit *dp; +} mp_int; + + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + + void mp_reverse (unsigned char *s, int len); +#ifdef BN_MP_INIT_MULTI_C + int mp_init_multi(mp_int *mp, ...); +#endif +#ifdef BN_MP_CLEAR_MULTI_C + void mp_clear_multi(mp_int *mp, ...); +#endif + int mp_lshd(mp_int * a, int b); + void mp_set(mp_int * a, mp_digit b); + void mp_clamp(mp_int * a); + void mp_exch(mp_int * a, mp_int * b); + void mp_rshd(mp_int * a, int b); + void mp_zero(mp_int * a); + int mp_mod_2d(mp_int * a, int b, mp_int * c); + int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d); + int mp_init_copy(mp_int * a, mp_int * b); + int mp_mul_2d(mp_int * a, int b, mp_int * c); +#ifndef LTM_NO_NEG_EXP + int mp_div_2(mp_int * a, mp_int * b); + int mp_invmod(mp_int * a, mp_int * b, mp_int * c); + int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c); +#endif /* LTM_NO_NEG_EXP */ + int mp_copy(mp_int * a, mp_int * b); + int mp_count_bits(mp_int * a); + int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d); + int mp_mod(mp_int * a, mp_int * b, mp_int * c); + int mp_grow(mp_int * a, int size); + int mp_cmp_mag(mp_int * a, mp_int * b); +#ifdef BN_MP_ABS_C + int mp_abs(mp_int * a, mp_int * b); +#endif + int mp_sqr(mp_int * a, mp_int * b); + int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); + int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); + int mp_2expt(mp_int * a, int b); + int mp_reduce_setup(mp_int * a, mp_int * b); + int mp_reduce(mp_int * x, mp_int * m, mp_int * mu); + int mp_init_size(mp_int * a, int size); +#ifdef BN_MP_EXPTMOD_FAST_C + int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +#endif /* BN_MP_EXPTMOD_FAST_C */ +#ifdef BN_FAST_S_MP_SQR_C + int fast_s_mp_sqr (mp_int * a, mp_int * b); +#endif /* BN_FAST_S_MP_SQR_C */ +#ifdef BN_MP_MUL_D_C + int mp_mul_d (mp_int * a, mp_digit b, mp_int * c); +#endif /* BN_MP_MUL_D_C */ +#ifdef BN_MP_MUL_2_C +/* b = a*2 */ +int mp_mul_2(mp_int * a, mp_int * b); +#endif + int mp_init_for_read_unsigned_bin(mp_int *a, mp_digit len); + void mp_clear (mp_int * a); +int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y); +int wpa_mp_init (mp_int * a); +int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c); +int mp_to_unsigned_bin_nr (mp_int * a, unsigned char *b); +int mp_to_unsigned_bin (mp_int * a, unsigned char *b); +int mp_unsigned_bin_size (mp_int * a); +int mp_add (mp_int * a, mp_int * b, mp_int * c); +int mp_cmp (mp_int * a, mp_int * b); +int mp_sub (mp_int * a, mp_int * b, mp_int * c); +int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d); +int mp_cmp_d(mp_int * a, mp_digit b); + +#ifdef BN_MP_MONTGOMERY_SETUP_C +int mp_montgomery_setup (mp_int * n, mp_digit * rho); +#endif +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +int mp_montgomery_calc_normalization (mp_int * a, mp_int * b); +#endif +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho); +#endif + +#endif //__LIBTOMMATH_H__ + diff --git a/sdk/Platform/Inc/md5.h b/sdk/Platform/Inc/md5.h new file mode 100644 index 0000000..e2925a9 --- /dev/null +++ b/sdk/Platform/Inc/md5.h @@ -0,0 +1,52 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MD5_H +#define MD5_H + +#include "wm_type_def.h" +//typedef unsigned int size_t; +#define MD5_MAC_LEN 16 + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +int md5(const u8 *addr, int len, u8 *mac); + +#ifdef CONFIG_FIPS +int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac); +int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +#else /* CONFIG_FIPS */ +#define hmac_md5_vector_non_fips_allow hmac_md5_vector +#define hmac_md5_non_fips_allow hmac_md5 +#endif /* CONFIG_FIPS */ + +#endif /* MD5_H */ + diff --git a/sdk/Platform/Inc/misc.h b/sdk/Platform/Inc/misc.h new file mode 100644 index 0000000..4720153 --- /dev/null +++ b/sdk/Platform/Inc/misc.h @@ -0,0 +1,206 @@ +/** + ****************************************************************************** + * @file misc.h + * @author MCD Application Team + * @version V3.6.1 + * @date 05-March-2012 + * @brief This file contains all the functions prototypes for the miscellaneous + * firmware library functions (add-on to CMSIS functions). + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __MISC_H +#define __MISC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "wm_regs.h" + +/** @addtogroup W600_Driver + * @{ + */ + +/** @addtogroup MISC + * @{ + */ + +/** @defgroup MISC_Exported_Types + * @{ + */ + +/** + * @brief NVIC Init Structure definition + */ + +typedef struct +{ + uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled. + This parameter can be a value of @ref IRQn_Type */ + + uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel + specified in NVIC_IRQChannel. This parameter can be a value + between 0 and 15 as described in the table @ref NVIC_Priority_Table */ + + uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified + in NVIC_IRQChannel. This parameter can be a value + between 0 and 15 as described in the table @ref NVIC_Priority_Table */ + + FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel + will be enabled or disabled. + This parameter can be set either to ENABLE or DISABLE */ +} NVIC_InitTypeDef; + +/** + * @} + */ + +/** @defgroup NVIC_Priority_Table + * @{ + */ + +/** +@code + The table below gives the allowed values of the pre-emption priority and subpriority according + to the Priority Grouping configuration performed by NVIC_PriorityGroupConfig function + ============================================================================================================================ + NVIC_PriorityGroup | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority | Description + ============================================================================================================================ + NVIC_PriorityGroup_0 | 0 | 0-15 | 0 bits for pre-emption priority + | | | 4 bits for subpriority + ---------------------------------------------------------------------------------------------------------------------------- + NVIC_PriorityGroup_1 | 0-1 | 0-7 | 1 bits for pre-emption priority + | | | 3 bits for subpriority + ---------------------------------------------------------------------------------------------------------------------------- + NVIC_PriorityGroup_2 | 0-3 | 0-3 | 2 bits for pre-emption priority + | | | 2 bits for subpriority + ---------------------------------------------------------------------------------------------------------------------------- + NVIC_PriorityGroup_3 | 0-7 | 0-1 | 3 bits for pre-emption priority + | | | 1 bits for subpriority + ---------------------------------------------------------------------------------------------------------------------------- + NVIC_PriorityGroup_4 | 0-15 | 0 | 4 bits for pre-emption priority + | | | 0 bits for subpriority + ============================================================================================================================ +@endcode +*/ + +/** + * @} + */ + +/** @defgroup MISC_Exported_Constants + * @{ + */ + +/** @defgroup Vector_Table_Base + * @{ + */ + +#define NVIC_VectTab_RAM ((uint32_t)0x20000000) +#define NVIC_VectTab_FLASH ((uint32_t)0x08000000) +#define IS_NVIC_VECTTAB(VECTTAB) (((VECTTAB) == NVIC_VectTab_RAM) || \ + ((VECTTAB) == NVIC_VectTab_FLASH)) +/** + * @} + */ + +/** @defgroup System_Low_Power + * @{ + */ + +#define NVIC_LP_SEVONPEND ((uint8_t)0x10) +#define NVIC_LP_SLEEPDEEP ((uint8_t)0x04) +#define NVIC_LP_SLEEPONEXIT ((uint8_t)0x02) +#define IS_NVIC_LP(LP) (((LP) == NVIC_LP_SEVONPEND) || \ + ((LP) == NVIC_LP_SLEEPDEEP) || \ + ((LP) == NVIC_LP_SLEEPONEXIT)) +/** + * @} + */ + +/** @defgroup Preemption_Priority_Group + * @{ + */ + +#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority + 4 bits for subpriority */ +#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority + 3 bits for subpriority */ +#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority + 2 bits for subpriority */ +#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority + 1 bits for subpriority */ +#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority + 0 bits for subpriority */ + +#define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \ + ((GROUP) == NVIC_PriorityGroup_1) || \ + ((GROUP) == NVIC_PriorityGroup_2) || \ + ((GROUP) == NVIC_PriorityGroup_3) || \ + ((GROUP) == NVIC_PriorityGroup_4)) + +#define IS_NVIC_PREEMPTION_PRIORITY(PRIORITY) ((PRIORITY) < 0x10) + +#define IS_NVIC_SUB_PRIORITY(PRIORITY) ((PRIORITY) < 0x10) + +#define IS_NVIC_OFFSET(OFFSET) ((OFFSET) < 0x000FFFFF) + +/** + * @} + */ + +/** @defgroup SysTick_clock_source + * @{ + */ + +#define SysTick_CLKSource_HCLK_Div8 ((uint32_t)0xFFFFFFFB) +#define SysTick_CLKSource_HCLK ((uint32_t)0x00000004) +#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \ + ((SOURCE) == SysTick_CLKSource_HCLK_Div8)) +/** + * @} + */ + +/** + * @} + */ + +/** @defgroup MISC_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup MISC_Exported_Functions + * @{ + */ + +void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); +void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); +void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset); +void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState); +void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource); + +#ifdef __cplusplus +} +#endif + +#endif /* __MISC_H */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. *****END OF FILE****/ diff --git a/sdk/Platform/Inc/os.h b/sdk/Platform/Inc/os.h new file mode 100644 index 0000000..9330ce0 --- /dev/null +++ b/sdk/Platform/Inc/os.h @@ -0,0 +1,47 @@ + +#ifndef OS_H +#define OS_H + +#include +#include +#include +#include "wm_mem.h" + +#ifndef os_strlen +#define os_strlen(s) strlen(s) +#endif + +void * os_zalloc(u32 size); +void * os_realloc(void *ptr, size_t size); +char * os_strdup(const char *s); + +#define os_time_after(a, b) ((long)(b) - (long)(a) < 0) + +#ifndef os_memcpy +#define os_memcpy(d, s, n) MEMCPY((d), (s), (n)) +#endif +#ifndef os_memmove +#define os_memmove(d, s, n) memmove((d), (s), (n)) +#endif +#ifndef os_memset +#define os_memset(s, c, n) memset(s, c, n) +#endif +#ifndef os_memcmp +#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) +#endif +#ifndef os_strstr +#define os_strstr(h, n) strstr((h), (n)) +#endif +#ifndef os_strcmp +#define os_strcmp(s1, s2) strcmp((s1), (s2)) +#endif +#ifndef os_strncmp +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#endif +#ifndef os_strchr +#define os_strchr(s, c) strchr((s), (c)) +#endif +#ifndef os_strcpy +#define os_strcpy(s1,s2) strcpy((s1),(s2)) +#endif +#endif diff --git a/sdk/Platform/Inc/rc4.h b/sdk/Platform/Inc/rc4.h new file mode 100644 index 0000000..a80a946 --- /dev/null +++ b/sdk/Platform/Inc/rc4.h @@ -0,0 +1,38 @@ +#ifndef RC4_H +#define RC4_H +#include "wm_crypto_hard.h" + +void Arc4Init(psCipherContext_t *ctx, unsigned char *key, uint32 keylen); +int32 Arc4_skip(psCipherContext_t *ctx, unsigned char *in, + unsigned char *out, size_t skip, uint32 len); +/** + * rc4_skip - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @skip: number of bytes to skip from the beginning of the RC4 stream + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * Returns: 0 on success, -1 on failure + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len); +/** + * rc4 - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * Returns: 0 on success, -1 on failure + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ + +int rc4(const u8 *key, size_t keylen, u8 *data, size_t data_len); + +#endif /* end of RC4_H */ diff --git a/sdk/Platform/Inc/sha1.h b/sdk/Platform/Inc/sha1.h new file mode 100644 index 0000000..7b3cd05 --- /dev/null +++ b/sdk/Platform/Inc/sha1.h @@ -0,0 +1,46 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef SHA1_H +#define SHA1_H + +#include "tls_common.h" +#define SHA1_HASH_SIZE 20 + +#define SHA1_MAC_LEN 20 + +struct SHA1Context { + u32 state[5]; + u32 count[2]; + unsigned char buffer[64]; +}; + +void SHA1Transform(u32 state[5], const unsigned char buffer[64]); +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); +int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen); +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +void SHA1Init(struct SHA1Context* context); +void SHA1Update(struct SHA1Context* context, const void *_data, u32 len); +void SHA1Final(unsigned char digest[20], struct SHA1Context* context); +int sha1(const u8 *addr, int len, u8 *mac); + +#endif /* SHA1_H */ diff --git a/sdk/Platform/Inc/sha256.h b/sdk/Platform/Inc/sha256.h new file mode 100644 index 0000000..734652d --- /dev/null +++ b/sdk/Platform/Inc/sha256.h @@ -0,0 +1,53 @@ +/* + * SHA256 hash implementation and interface functions + * Copyright (c) 2003-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef SHA256_H +#define SHA256_H +#include "common.h" +#include "os.h" + +#define SHA256_MAC_LEN 32 + +#define SHA256_BLOCK_SIZE 64 + +struct sha256_state { + u64 length; + u32 state[8], curlen; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +void wpa_sha256_init(struct sha256_state *md); +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen); +int sha256_done(struct sha256_state *md, unsigned char *out); + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +void sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); + +#endif /* SHA256_H */ diff --git a/sdk/Platform/Inc/tls_common.h b/sdk/Platform/Inc/tls_common.h new file mode 100644 index 0000000..4bd5348 --- /dev/null +++ b/sdk/Platform/Inc/tls_common.h @@ -0,0 +1,253 @@ +#ifndef TLS_COMMON_H +#define TLS_COMMON_H + +#include "wm_osal.h" +#include "wm_config.h" + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +#define BIT(x) (1UL << (x)) + +#define time_after(a, b) ((long)(b) - (long)(a) < 0) +#define time_before(a,b) time_after(b,a) + +/* + * These are the defined Ethernet Protocol ID's. + */ + +#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */ +#define ETH_P_PUP 0x0200 /* Xerox PUP packet */ +#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */ +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_X25 0x0805 /* CCITT X.25 */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ +#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */ +#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */ +#define ETH_P_DEC 0x6000 /* DEC Assigned proto */ +#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */ +#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */ +#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */ +#define ETH_P_LAT 0x6004 /* DEC LAT */ +#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */ +#define ETH_P_CUST 0x6006 /* DEC Customer use */ +#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */ +#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */ +#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ +#define ETH_P_ATALK 0x809B /* Appletalk DDP */ +#define ETH_P_AARP 0x80F3 /* Appletalk AARP */ +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_P_IPX 0x8137 /* IPX over DIX */ +#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ +#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */ +#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol + * defined in draft-wilson-wrec-wccp-v2-00.txt */ +#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */ +#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */ +#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */ +#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */ +#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport + * over Ethernet + */ +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#define ETH_P_WAPI 0x88B4 /* WAPI authentication */ +#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ +#define ETH_P_TIPC 0x88CA /* TIPC */ +#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ +#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ + +#define ETH_HLEN 14 /* Total octets in header. */ + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif +#ifndef ETH_P_ALL +#define ETH_P_ALL 0x0003 +#endif +#ifndef ETH_P_80211_ENCAP +#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */ +#endif +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL ETH_P_PAE +#endif /* ETH_P_EAPOL */ +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif /* ETH_P_RSN_PREAUTH */ +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ + +#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b)))) +#define STRUCT_PACKED __attribute__ ((packed)) + +#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" + +/** + * compare_ether_addr - Compare two Ethernet addresses + * @addr1: Pointer to a six-byte array containing the Ethernet address + * @addr2: Pointer other six-byte array containing the Ethernet address + * + * Compare two ethernet addresses, returns 0 if equal + */ +static __inline unsigned compare_ether_addr(const u8 *addr1, const u8 *addr2) +{ + return !((addr1[0] == addr2[0]) && (addr1[1] == addr2[1]) && (addr1[2] == addr2[2]) && \ + (addr1[3] == addr2[3]) && (addr1[4] == addr2[4]) && (addr1[5] == addr2[5])); +} + +static __inline int is_zero_ether_addr(const u8 *a) +{ + return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); +} + +static __inline int is_broadcast_ether_addr(const u8 *a) +{ + return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; +} + +static __inline int is_multicast_ether_addr(const u8 *addr) +{ + return (0x01 & addr[0]); +} + +static __inline unsigned short swap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static __inline unsigned int swap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} + +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) swap_16(n) +#define host_to_be16(n) swap_16(n) +#define le_to_host32(n) (n) +#define le_to_host64(n) (n) +#define be_to_host32(n) swap_32(n) +#define host_to_be32(n) swap_32(n) + +static __inline u16 get_unaligned_le16(const u8 *p) +{ + return p[0] | p[1] << 8; +} + +static __inline u32 get_unaligned_le32(const u8 *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static __inline void put_unaligned_le16(u16 val, u8 *p) +{ + *p++ = val; + *p++ = val >> 8; +} + +static __inline void put_unaligned_le32(u32 val, u8 *p) +{ + put_unaligned_le16(val >> 16, p + 2); + put_unaligned_le16(val, p); +} + +static __inline u16 get_unaligned_be16(const u8 *p) +{ + return be_to_host16(get_unaligned_le16(p)); +} + +static __inline u32 get_unaligned_be32(const u8 *p) +{ + return be_to_host32(get_unaligned_le32(p)); +} + +static __inline void put_unaligned_be16(u16 val, u8 *p) +{ + *p++ = val >> 8; + *p++ = val; +} + +static __inline void put_unaligned_be32(u32 val, u8 *p) +{ + put_unaligned_be16(val >> 16, p); + put_unaligned_be16(val, p + 2); +} + + +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise __attribute__((bitwise)) +#else +#define __force +#define __bitwise +#endif + +typedef u16 __bitwise be16; +typedef u16 __bitwise le16; +typedef u32 __bitwise be32; +typedef u32 __bitwise le32; +typedef u64 __bitwise be64; +typedef u64 __bitwise le64; + +/* + * This is an Ethernet frame header. + */ +struct ethhdr { + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + unsigned short h_proto; /* packet type ID field */ +} __attribute__((packed)); + + + +typedef struct { + volatile int counter; +} atomic_t; + +struct kref { + atomic_t refcount; +}; + +static __inline int atomic_sub_return(int i, atomic_t *v) +{ + u32 cpu_sr; + int val; + + cpu_sr = tls_os_set_critical(); + val = v->counter; + v->counter = val -= i; + tls_os_release_critical(cpu_sr); + + return val; +} + +static __inline int atomic_add_return(int i, atomic_t *v) +{ + u32 cpu_sr; + int val; + + cpu_sr = tls_os_set_critical(); + val = v->counter; + v->counter = val += i; + tls_os_release_critical(cpu_sr); + + return val; +} + + +#define atomic_set(v,i) (((v)->counter) = (i)) +#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) +#define atomic_inc(v) (void) atomic_add_return(1, v) +#define atomic_dec(v) (void) atomic_sub_return(1, v) + +#endif /* end of TLS_COMMON_H */ diff --git a/sdk/Platform/Inc/tls_ieee80211.h b/sdk/Platform/Inc/tls_ieee80211.h new file mode 100644 index 0000000..9234fc7 --- /dev/null +++ b/sdk/Platform/Inc/tls_ieee80211.h @@ -0,0 +1,12 @@ + + +#ifndef TLS_IEEE80211_H +#define TLS_IEEE80211_H + +#include "tls_common.h" +#if (GCC_COMPILE==1) +#include "wm_ieee80211_gcc.h" +#else +#include "wm_ieee80211.h" +#endif +#endif /* end of TLS_IEEE80211_H */ diff --git a/sdk/Platform/Inc/tls_wireless.h b/sdk/Platform/Inc/tls_wireless.h new file mode 100644 index 0000000..21d61fc --- /dev/null +++ b/sdk/Platform/Inc/tls_wireless.h @@ -0,0 +1,434 @@ +#ifndef TLS_WIRELESS_H +#define TLS_WIRELESS_H + +#include "tls_common.h" +#include "wm_osal.h" +#include "list.h" +//#include "netif.h" + +struct tls_wif; +struct wpa_supplicant; + +/* Maximum size of the SSID */ +#define IW_SSID_MAX_SIZE 32 +#if 0 +#define IEEE80211_MODE_INFRA 0 +#define IEEE80211_MODE_IBSS 1 +#define IEEE80211_MODE_AP 2 +#endif +//#define IEEE80211_RATE_MODE_B BIT(0) +//#define IEEE80211_RATE_MODE_G BIT(1) +//#define IEEE80211_RATE_MODE_BG BIT(2) +//#define IEEE80211_RATE_MODE_N BIT(3) +//#define IEEE80211_RATE_MODE_BGN BIT(4) + +enum ieee80211_wireless_mode { + IEEE80211_MODE_11B = 0, + IEEE80211_MODE_11G, + IEEE80211_MODE_11NG_HT20, + IEEE80211_MODE_11NG_HT40PLUS, + IEEE80211_MODE_11NG_HT40MINUS, + IEEE80211_MODE_MAX, +}; + +#define IW_AUTH_INDEX 0x0FFF +#define IW_AUTH_FLAGS 0xF000 + +#define IW_AUTH_WPA_VERSION 0 +#define IW_AUTH_CIPHER_PAIRWISE 1 +#define IW_AUTH_CIPHER_GROUP 2 +#define IW_AUTH_KEY_MGMT 3 +#define IW_AUTH_TKIP_COUNTERMEASURES 4 +#define IW_AUTH_DROP_UNENCRYPTED 5 +#define IW_AUTH_80211_AUTH_ALG 6 +#define IW_AUTH_WPA_ENABLED 7 +#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define IW_AUTH_ROAMING_CONTROL 9 +#define IW_AUTH_PRIVACY_INVOKED 10 +#define IW_AUTH_CIPHER_GROUP_MGMT 11 +#define IW_AUTH_MFP 12 + +/* IW_AUTH_WPA_VERSION values (bit field) */ +#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 +#define IW_AUTH_WPA_VERSION_WPA 0x00000002 +#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 + +/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT + * values (bit field) */ +#define IW_AUTH_CIPHER_NONE 0x00000001 +#define IW_AUTH_CIPHER_WEP40 0x00000002 +#define IW_AUTH_CIPHER_TKIP 0x00000004 +#define IW_AUTH_CIPHER_CCMP 0x00000008 +#define IW_AUTH_CIPHER_WEP104 0x00000010 +#define IW_AUTH_CIPHER_AES_CMAC 0x00000020 + +/* IW_AUTH_KEY_MGMT values (bit field) */ +#define IW_AUTH_KEY_MGMT_802_1X 1 +#define IW_AUTH_KEY_MGMT_PSK 2 + +/* IW_AUTH_80211_AUTH_ALG values (bit field) */ +#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define IW_AUTH_ALG_SHARED_KEY 0x00000002 +#define IW_AUTH_ALG_LEAP 0x00000004 + +#define IW_ENCODE_ALG_NONE 0 +#define IW_ENCODE_ALG_WEP 1 +#define IW_ENCODE_ALG_TKIP 2 +#define IW_ENCODE_ALG_CCMP 3 +#define IW_ENCODE_ALG_PMK 4 +#define IW_ENCODE_ALG_AES_CMAC 5 + +#define IW_MAX_FREQUENCIES 32 + +#define IW_SCAN_TYPE_ACTIVE 0 +#define IW_SCAN_TYPE_PASSIVE 1 + +struct iw_scan_req { + u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ + u8 ssid_len; + u8 num_channels; /* num entries in channel_list; + * 0 = scan all allowed channels */ + u8 bssid[ETH_ALEN]; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or + * individual address of a specific BSS */ + + u8 ssid[IW_SSID_MAX_SIZE]; + u32 extra_ies_len; + u8 extra_ies[0]; + + //struct iw_freq channel_list[IW_MAX_FREQUENCIES]; +}; + +#if 0 +struct iw_bssid_ex { + u32 length; + u8 bssid[ETH_ALEN]; + u16 freq; + u32 ssid_len; + u8 ssid[IW_SSID_MAX_SIZE]; + u32 privacy; + int rssi; + u8 tsf[8]; + u16 beacon_int; + u16 capabilities; + u32 ie_len; + u8 ies[0]; +} __attribute__ ((packed)); + +struct iw_bss_info { + u8 bssid[ETH_ALEN]; + u8 mode; + u8 channel; + u8 privacy; + u8 ssid_len; + u8 rssi; + u8 ssid[32]; +}; + +struct iw_scan_results { + u32 count; + struct iw_bssid_ex bssid[1]; +}; + +struct iw_scan_bss { + u32 count; + u32 length; /* bss info total length */ + struct iw_bss_info bss[1]; +}; +#endif + +/** + * struct beacon_parameters - beacon parameters + * + * Used to configure the beacon for an interface. + * + * @head: head portion of beacon (before TIM IE) + * or %NULL if not changed + * @tail: tail portion of beacon (after TIM IE) + * or %NULL if not changed + * @interval: beacon interval or zero if not changed + * @dtim_period: DTIM period or zero if not changed + * @head_len: length of @head + * @tail_len: length of @tail + */ +struct iw_beacon_parameters { + u8 *head, *tail; + int interval, dtim_period; + int head_len, tail_len; +}; + + +struct iw_ssid_params { + u8 ssid[IW_SSID_MAX_SIZE]; + u8 ssid_len; + u32 ie_len; + u8 *ie; +}; + +struct iw_key_params { + u8 key[32]; + u8 tsc[32]; + u32 key_len; + u32 tsc_len; + u16 cipher; + bool pairwise; + bool default_key; + u32 key_idx; + u8 addr[ETH_ALEN]; +}; + +struct iw_sta_add_params { + const u8 *addr; + u16 aid; + u16 capability; + u16 listen_interval; + u8 mode; + const u8 *supp_rates; + u32 supp_rates_len; + u32 flags; /* bitmask of WPA_STA_* flags */ +}; + +/** + * struct beacon_parameters - beacon parameters + * + * Used to configure the beacon for an interface. + * + * @head: head portion of beacon (before TIM IE) + * or %NULL if not changed + * @tail: tail portion of beacon (after TIM IE) + * or %NULL if not changed + * @interval: beacon interval or zero if not changed + * @dtim_period: DTIM period or zero if not changed + * @head_len: length of @head + * @tail_len: length of @tail + */ +struct beacon_parameters { + u8 *head, *tail; + int interval, dtim_period; + int head_len, tail_len; +}; + +struct wl_event_reload { + bool reload; +}; + +struct wl_event_join_ibss_info { + u8 bssid[ETH_ALEN]; + u32 freq; +}; + +struct wl_event_mic_err { + u8 bssid[ETH_ALEN]; + u8 addr[ETH_ALEN]; + bool group_mic_err; /* TRUE: group, FALSE: pairwise */ +}; + +struct wl_event_rx_eapol { + u8 *src_addr; + u8 *eapol_body; + u32 eapol_body_len; +#if TLS_CONFIG_AP + u8 mode; +#endif +}; + +struct wl_event_rx_mgmt { + u8 *mgmt; + u32 mgmt_len; +}; + +struct wl_event_rx_from_unknown { + u8 addr[ETH_ALEN]; +}; + +struct wl_event_assoc_info { + /** + * reassoc - Flag to indicate association or reassociation + */ + int reassoc; + + /** + * req_ies - (Re)Association Request IEs + * + * If the driver generates WPA/RSN IE, this event data must be + * returned for WPA handshake to have needed information. If + * wpa_supplicant-generated WPA/RSN IE is used, this + * information event is optional. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + const u8 *req_ies; + + /** + * req_ies_len - Length of req_ies in bytes + */ + u32 req_ies_len; + + /** + * resp_ies - (Re)Association Response IEs + * + * Optional association data from the driver. This data is not + * required WPA, but may be useful for some protocols and as + * such, should be reported if this is available to the driver + * interface. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + u8 *resp_ies; + + /** + * resp_ies_len - Length of resp_ies in bytes + */ + u32 resp_ies_len; + + /** + * addr - Station address (for AP mode) + */ + u8 *addr; + + u8 *bssid; + + /** + * freq - Frequency of the operational channel in MHz + */ + unsigned int freq; +}; + +struct tls_wl_event_ops { + int (*ibss_joined)(struct tls_wif *wif, + struct wl_event_join_ibss_info *info); + int (*assoc)(struct tls_wif *wif, + struct wl_event_assoc_info *info); + int (*disassoc)(struct tls_wif *wif); + int (*scan_completed)(struct tls_wif *wif); + int (*mic_err)(struct tls_wif *wif, + struct wl_event_mic_err *info); + int (*rx_eapol)(struct tls_wif *wif, + struct wl_event_rx_eapol *eapol); + int (*rx_mgmt)(struct tls_wif *wif, + struct wl_event_rx_mgmt *mgmt); + int (*mac_wdg)(struct tls_wif *wif); +#if TLS_CONFIG_AP_OPT_PS + int (*beacon_done)(struct tls_wif *wif); + int (*rx_ps)(struct tls_wif *wif, + struct wl_event_rx_mgmt *mgmt); + int (*rx_pspoll)(struct tls_wif *wif, + struct wl_event_rx_from_unknown *rx_pspoll); + int (*sta_active)(struct tls_wif *wif, + struct wl_event_rx_from_unknown *rx_pspoll); +#endif + int (*rx_from_unknown_sta)(struct tls_wif *wif, + struct wl_event_rx_from_unknown *rx_from_unknown); + int (*net_down)(struct tls_wif *wif); + int (*net_fail)(struct tls_wif *wif); + int (*net_up)(struct tls_wif *wif); + int (*update_stat)(struct tls_wif *wif, void *cur_bss);/* struct ieee80211_bss *cur_bss */ +}; + +/* sk_buff allocated by wlan driver */ +struct sk_buff { + struct dl_list list; + u8 *buf; + u32 buf_len; + u32 flags; +}; + +struct tls_wif { + //void *priv; + struct ieee80211_if_data *priv; + struct wpa_supplicant *wpa_s; + struct tls_wl_event_ops *ops; +#if TLS_CONFIG_SOFTAP_11N + struct hostapd_iface *apif; +#endif + + //struct netif *ethif; + //bool net_up; + bool wlan_create; + int (*rx_data_cb)(const u8 *bssid, u8 *buf, u32 buf_len); +#if TLS_CONFIG_AP + bool wmm_set; + void *client_event_callback; +#if TLS_CONFIG_AP_OPT_FWD + int (*rx_ip_cb)(const u8 *bssid, u8 *buf, u32 buf_len); +#endif +#endif + +#if 0 + int (*send)(struct tls_wif *wif, struct sk_buff *skb); + int (*rx_data_cb)(struct tls_wif *wif, struct sk_buff *skb); + int (*tx_mgmt)(struct tls_wif *wif, struct sk_buff *skb); + int (*send_eapol)(struct tls_wif *wif, struct sk_buff *skb); + int (*send_beacon)(struct tls_wif *wif, struct sk_buff *skb); +#endif +}; + +void tls_wl_print_stats(struct tls_wif *wif); + +int tls_wl_if_scan(struct tls_wif *wif, + struct iw_scan_req *scan_req, u16 size); +int tls_wl_if_scan_result2(struct tls_wif *wif, + u8 *ssid, u32 ssid_len, u8 *buf, u32 buf_size); +int tls_wl_if_scan_result(struct tls_wif *wif, u8 *buf, u32 buf_size); +void tls_wl_if_sta_flush(struct tls_wif *wif, u8 mode); +int tls_wl_if_sta_deauth(struct tls_wif *wif, u8 *own_addr, + const u8 *addr, int reason); +int tls_wl_if_sta_disassoc(struct tls_wif *wif, u8 *own_addr, + const u8 *addr, int reason); +#if TLS_CONFIG_AP +int tls_wl_if_set_tx_queue_params(struct tls_wif *wif, int queue, + int aifs, int cw_min, int cw_max, int burst_time); +void tls_wl_if_set_sta_flags(struct tls_wif *wif, u8 *addr, u32 flags); +int tls_wl_if_send_channel_switch(struct tls_wif *wif, u8 *ownaddr, u8 newch); +void tls_wl_if_switch_channel_width(struct tls_wif *wif, u8 *ownaddr); +void tls_wl_if_channel_info_updata(struct tls_wif *wif); +#if TLS_CONFIG_SOFTAP_11N +void tls_wl_if_get_ht_param(struct tls_wif *wif, u16 *cap, u8 *mcs, u8 *mpdu); +void tls_wl_if_set_sta_ht_param(struct tls_wif *wif, u8 *mac, u8 *ht); +#endif +#endif +int tls_wl_if_set_mode(struct tls_wif *wif, u16 mode); +int tls_wl_if_clear_mode(struct tls_wif *wif, u16 mode); +int tls_wl_if_set_bssid(struct tls_wif *wif, const u8 *addr, u32 mode); +int tls_wl_if_set_ssid(struct tls_wif *wif, struct iw_ssid_params *params); +int tls_wl_if_set_auth(struct tls_wif *wif, u16 flag, u32 value, u8 mode); +int tls_wl_if_set_ps_mode(struct tls_wif *wif, int powersave); +int tls_wl_if_set_freq(struct tls_wif *wif, + int freq); +int tls_wl_if_send_eapol(struct tls_wif *wif, + u8 *buf, u32 len, bool is_apsta); +int tls_wl_if_xmit(struct tls_wif *wif, void *buf, int len, bool is_apsta, bool not_delay); +int tls_wl_if_add_key(struct tls_wif *wif, + struct iw_key_params *params); +int tls_wl_if_remove_key(struct tls_wif *wif, + u32 cipher, + u32 key_idx, + bool pairwise, + u8 *addr); +int tls_wl_if_send_mlme(struct tls_wif *wif, + u8 *buf, u32 len); +int tls_wl_if_set_rate_mode(struct tls_wif *wif, + u32 rate_mode); +int tls_wl_if_set_beacon(struct tls_wif *wif, + struct iw_beacon_parameters *params); +int tls_wl_if_del_beacon(struct tls_wif *wif); +int tls_wl_if_sta_add(struct tls_wif *wif, + struct iw_sta_add_params *params); +int tls_wl_if_sta_remove(struct tls_wif *wif, u8 *addr); +int tls_wl_if_get_inact_sec(struct tls_wif *wif, const u8 *addr); +int tls_wl_if_get_scan_res(struct tls_wif *wif, u8 *buf, u32 buf_size); +int tls_wl_if_disconnect(struct tls_wif *wif); +int tls_wl_if_tx(struct tls_wif *wif, + u8 *buf, u32 buflen, bool last_packet, bool is_apsta, bool not_delay); +int tls_wl_if_set_max_rate(struct tls_wif *wif, u8 max_rate_idx); +int tls_wl_if_get_max_rate(struct tls_wif *wif, u8 *max_rate_idx); +int tls_wl_if_set_adhoc(struct tls_wif *wif, int adhoc_automode); +int tls_wl_if_ps(int wake_up); +int tls_wl_if_standby(int type, int delay, int wake_time); +int tls_wl_if_sleep(int type, int delay, int wake_time); +void tls_wl_if_set_errno(int eno); +int tls_wl_if_get_errno(void); +void tls_wl_if_perror(const char *info); +const char *tls_wl_if_get_errinfo(int eno); +#endif /* end of TLS_WIRELESS_H */ diff --git a/sdk/Platform/Inc/utils.h b/sdk/Platform/Inc/utils.h new file mode 100644 index 0000000..9b77578 --- /dev/null +++ b/sdk/Platform/Inc/utils.h @@ -0,0 +1,39 @@ +/************************************************************************** + * File Name : utils.h + * Author : + * Version : 1.0 + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ +#ifndef UTILS_H +#define UTILS_H + +int chk_crc8(u8 *ptr, u32 len); +u8 get_crc8(u8 *ptr, u32 len); +u8 calculate_crc8(u8 crc8, u8 *ptr, u32 len); +u32 get_crc32(u8 *data, u32 data_size); +u32 checksum(u32 *data, u32 length, u32 init); +int atodec(char ch); +int strtodec(int *dec, char *str); +int atohex(char ch); +int strtohex(u32 *hex, char *str); +int strtohexarray(u8 array[], int cnt, char *str); +int strtoip(u32 *ipadr, char * str); +void iptostr(u32 ip, char *str); +void mactostr(u8 mac[], char *str); + +int hex_to_digit(int c); +int digit_to_hex(int c); +int hexstr_to_unit(char *buf, u32 *d); +int string_to_uint(char *buf, u32 *d); +int string_to_ipaddr(const char *buf, u8 *addr); +char * strdup(const char *s); +char * strndup(const char *s, size_t len); + +int sendchar(int ch); + +#endif /* UTILS_H */ diff --git a/sdk/Platform/Inc/wm_dbg.h b/sdk/Platform/Inc/wm_dbg.h new file mode 100644 index 0000000..d25cc27 --- /dev/null +++ b/sdk/Platform/Inc/wm_dbg.h @@ -0,0 +1,93 @@ +#ifndef __WM_DBG_H__ +#define __WM_DBG_H__ + +#include "wm_debug.h" + +/* Define the module switcher */ +#define TLS_FLASH_DBG TLS_DBG_OFF +#define TLS_SPI_DBG TLS_DBG_OFF +#define TLS_IO_DBG TLS_DBG_OFF +#define TLS_DMA_DBG TLS_DBG_OFF +#define TLS_WL_DBG TLS_DBG_OFF +#define TLS_WPA_DBG TLS_DBG_OFF + +/* flash info */ +#if (TLS_FLASH_DBG && TLS_DBG_LEVEL_INFO) +#define TLS_DBGPRT_FLASH_INFO(f, a...) __TLS_DBGPRT_INFO(f, ##a) +#else +#define TLS_DBGPRT_FLASH_INFO(f, a...) +#endif + +/* flash warnning */ +#if (TLS_FLASH_DBG && TLS_DBG_LEVEL_WARNING) +#define TLS_DBGPRT_FLASH_WARNING(f, a...) __TLS_DBGPRT_WARNING(f, ##a) +#else +#define TLS_DBGPRT_FLASH_WARNING(f, a...) +#endif + +/* flash error */ +#if (TLS_FLASH_DBG && TLS_DBG_LEVEL_ERR) +#define TLS_DBGPRT_FLASH_ERR(f, a...) __TLS_DBGPRT_ERR(f, ##a) +#else +#define TLS_DBGPRT_FLASH_ERR(f, a...) +#endif + + +#if (TLS_SPI_DBG && TLS_DBG_LEVEL_INFO) +#define TLS_DBGPRT_SPI_INFO(f, a...) __TLS_DBGPRT_INFO(f, ##a) +#else +#define TLS_DBGPRT_SPI_INFO(f, a...) +#endif + +#if (TLS_SPI_DBG && TLS_DBG_LEVEL_WARNING) +#define TLS_DBGPRT_SPI_WARNING(f, a...) __TLS_DBGPRT_WARNING(f, ##a) +#else +#define TLS_DBGPRT_SPI_WARNING(f, a...) +#endif + +#if (TLS_SPI_DBG && TLS_DBG_LEVEL_ERR) +#define TLS_DBGPRT_SPI_ERR(f, a...) __TLS_DBGPRT_ERR(f, ##a) +#else +#define TLS_DBGPRT_SPI_ERR(f, a...) +#endif + + +#if (TLS_IO_DBG && TLS_DBG_LEVEL_INFO) +#define TLS_DBGPRT_IO_INFO(f, a...) __TLS_DBGPRT_INFO(f, ##a) +#else +#define TLS_DBGPRT_IO_INFO(f, a...) +#endif + +#if (TLS_IO_DBG && TLS_DBG_LEVEL_WARNING) +#define TLS_DBGPRT_IO_WARNING(f, a...) __TLS_DBGPRT_WARNING(f, ##a) +#else +#define TLS_DBGPRT_IO_WARNING(f, a...) +#endif + +#if (TLS_IO_DBG && TLS_DBG_LEVEL_ERR) +#define TLS_DBGPRT_IO_ERR(f, a...) __TLS_DBGPRT_ERR(f, ##a) +#else +#define TLS_DBGPRT_IO_ERR(f, a...) +#endif + + +#if (TLS_DMA_DBG && TLS_DBG_LEVEL_INFO) +#define TLS_DBGPRT_DMA_INFO(f, a...) __TLS_DBGPRT_INFO(f, ##a) +#else +#define TLS_DBGPRT_DMA_INFO(f, a...) +#endif + +#if (TLS_DMA_DBG && TLS_DBG_LEVEL_WARNING) +#define TLS_DBGPRT_DMA_WARNING(f, a...) __TLS_DBGPRT_WARNING(f, ##a) +#else +#define TLS_DBGPRT_DMA_WARNING(f, a...) +#endif + +#if (TLS_DMA_DBG && TLS_DBG_LEVEL_ERR) +#define TLS_DBGPRT_DMA_ERR(f, a...) __TLS_DBGPRT_ERR(f, ##a) +#else +#define TLS_DBGPRT_DMA_ERR(f, a...) +#endif + +#endif /* __DBG_H__ */ + diff --git a/sdk/Platform/Sys/CMakeLists.txt b/sdk/Platform/Sys/CMakeLists.txt new file mode 100644 index 0000000..100f132 --- /dev/null +++ b/sdk/Platform/Sys/CMakeLists.txt @@ -0,0 +1,43 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WMMAIN) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin ") +STRING(APPEND CMAKE_C_FLAGS " -DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/wm_main.c" + "${CMAKE_CURRENT_SOURCE_DIR}/tls_sys.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmmain.a) \ No newline at end of file diff --git a/sdk/Platform/Sys/tls_sys.c b/sdk/Platform/Sys/tls_sys.c new file mode 100644 index 0000000..4617c3e --- /dev/null +++ b/sdk/Platform/Sys/tls_sys.c @@ -0,0 +1,581 @@ +/************************************************************************** + * File Name : tls_sys.c + * Author : + * Version : + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ +#include "wm_config.h" +#include "wm_osal.h" +#include "tls_sys.h" +#include "wm_mem.h" +#include "wm_debug.h" +#include "wm_params.h" +#include "wm_regs.h" +#include +#include +#include +#include "wm_wifi.h" +#include "wm_netif.h" +#include "wm_sockets.h" +#include "wm_include.h" +#if TLS_CONFIG_AP +#include "wm_dhcp_server.h" +#include "wm_dns_server.h" +#include "wm_cpu.h" +#endif +#include "wm_wl_task.h" +#if TLS_CONFIG_RMMS +#if (GCC_COMPILE==1) +#include "wm_cmdp_hostif_gcc.h" +#else +#include "wm_cmdp_hostif.h" +#endif +#endif +#include "misc.h" + + +struct tls_sys_msg +{ + u32 msg; + void *data; +}; +#define SYS_TASK_STK_SIZE 512 + +static tls_os_queue_t *msg_queue; + +#if TLS_DBG_LEVEL_DUMP +void TLS_DBGPRT_DUMP(char *p, u32 len) +{ + int i; + if (TLS_DBG_LEVEL_DUMP) + { + wm_printf("dump length : %d\n", len); + for (i = 0; i < len; i++) + { + wm_printf("%02X ", p[i]); + if ((i + 1) % 16 == 0 && (i + 1) % 32 != 0) + { + wm_printf("- "); + } + if ((i + 1) % 32 == 0) + { + wm_printf("\n"); + } + if (i == 2000) + { + wm_printf("\n"); + break; + } + } + wm_printf("\n"); + } +} + +//------------------------------------------------------------------------- + +#endif + +static void sys_net_up() +{ + ip_addr_t ip_addr, net_mask, gateway, dns1, dns2; + struct tls_param_ip ip_param; + bool enable = FALSE; + + tls_param_get(TLS_PARAM_ID_IP, &ip_param, FALSE); + if (ip_param.dhcp_enable) + { + ip_addr_set_zero(&ip_addr); + ip_addr_set_zero(&net_mask); + ip_addr_set_zero(&gateway); + tls_netif_set_addr( &ip_addr, &net_mask, &gateway); + tls_netif_set_up(); + tls_dhcp_start(); + } + else + { + tls_dhcp_stop(); + tls_netif_set_up(); + + MEMCPY((char*)ip_2_ip4(&ip_addr), &ip_param.ip, 4); + MEMCPY((char*)ip_2_ip4(&net_mask), &ip_param.netmask, 4); + MEMCPY((char*)ip_2_ip4(&gateway), &ip_param.gateway, 4); + tls_netif_set_addr( &ip_addr, &net_mask, &gateway); + MEMCPY((char*)ip_2_ip4(&dns1), &ip_param.dns1, 4); + MEMCPY((char*)ip_2_ip4(&dns2), &ip_param.dns2, 4); + tls_netif_dns_setserver(0, &dns1); + tls_netif_dns_setserver(1, &dns2); + + /*when DHCP is disabled, Use static IP without IP_NET_UP Reporting, + set wifi powersaving flag according to TLS_PARAM_ID_PSM here.*/ + tls_param_get(TLS_PARAM_ID_PSM, &enable, TRUE); + tls_wifi_set_psflag(enable, FALSE); + } + + return ; +} + +//------------------------------------------------------------------------- + +#if TLS_CONFIG_AP +static void sys_net2_up() +{ + ip_addr_t ip_addr, net_mask, gateway; + struct tls_param_ip ip_param; + u8 dnsname[32]; + + dnsname[0] = '\0'; + tls_param_get(TLS_PARAM_ID_DNSNAME, dnsname, 0); + tls_param_get(TLS_PARAM_ID_SOFTAP_IP, &ip_param, FALSE); + + tls_netif2_set_up(); + + MEMCPY((char*)ip_2_ip4(&ip_addr), &ip_param.ip, 4); + MEMCPY((char*)ip_2_ip4(&net_mask), &ip_param.netmask, 4); + MEMCPY((char*)ip_2_ip4(&gateway), &ip_param.gateway, 4); + tls_netif2_set_addr(&ip_addr, &net_mask, &gateway); + + if (ip_param.dhcp_enable) + { + tls_dhcps_start(); + } + + if ('\0' != dnsname[0]) + { + tls_dnss_start(dnsname); + } + + return ; +} + +//------------------------------------------------------------------------- + +static void sys_net2_down() +{ + tls_dnss_stop(); + + tls_dhcps_stop(); + + tls_netif2_set_down(); + + return ; +} +#endif + +static void sys_net_down() +{ + struct tls_param_ip ip_param; + +#if TLS_CONFIG_RMMS + tls_rmms_stop(); +#endif + + tls_param_get(TLS_PARAM_ID_IP, &ip_param, FALSE); + if (ip_param.dhcp_enable) + { + tls_dhcp_stop(); + } + tls_netif_set_status(0); + tls_netif_set_down(); + + /* Try to reconnect if auto_connect is set*/ + tls_auto_reconnect(); + + return ; +} + +//------------------------------------------------------------------------- + +#if TLS_CONFIG_AP +static void tls_auto_reconnect_softap(void) +{ + struct tls_param_ssid ssid; + struct tls_softap_info_t *apinfo; + struct tls_ip_info_t *ipinfo; + struct tls_param_ip ip_param; + struct tls_param_key key; + + apinfo = tls_mem_alloc(sizeof(struct tls_softap_info_t)); + if (apinfo == NULL) + { + return ; + } + ipinfo = tls_mem_alloc(sizeof(struct tls_ip_info_t)); + if (ipinfo == NULL) + { + tls_mem_free(apinfo); + return ; + } + + tls_param_get(TLS_PARAM_ID_SOFTAP_SSID, (void*) &ssid, TRUE); + memcpy(apinfo->ssid, ssid.ssid, ssid.ssid_len); + apinfo->ssid[ssid.ssid_len] = '\0'; + + tls_param_get(TLS_PARAM_ID_SOFTAP_ENCRY, (void*) &apinfo->encrypt, TRUE); + tls_param_get(TLS_PARAM_ID_SOFTAP_CHANNEL, (void*) &apinfo->channel, TRUE); + + tls_param_get(TLS_PARAM_ID_SOFTAP_KEY, (void*) &key, TRUE); + apinfo->keyinfo.key_len = key.key_length; + apinfo->keyinfo.format = key.key_format; + apinfo->keyinfo.index = key.key_index; + memcpy(apinfo->keyinfo.key, key.psk, key.key_length); + + tls_param_get(TLS_PARAM_ID_SOFTAP_IP, &ip_param, TRUE); + /*ipé…�置信æ�¯:ip地å�€ï¼ŒæŽ©ç �,dnså��ç§°*/ + memcpy(ipinfo->ip_addr, ip_param.ip, 4); + memcpy(ipinfo->netmask, ip_param.netmask, 4); + tls_param_get(TLS_PARAM_ID_DNSNAME, ipinfo->dnsname, FALSE); + + tls_wifi_softap_create(apinfo, ipinfo); + tls_mem_free(apinfo); + tls_mem_free(ipinfo); + return ; +} + +//------------------------------------------------------------------------- + +#endif +void tls_auto_reconnect(void) +{ + struct tls_param_ssid ssid; + u8 auto_reconnect = 0xff; + u8 wireless_protocol = 0; +#if 0 + u8 i = 0; + while (i < 200) + { + if (tls_wifi_get_oneshot_flag()) + { + /*oneshot config ongoing,do not reconnect WiFi*/ + return ; + } tls_os_time_delay(1); + i++; + } +#endif + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_reconnect); + if (auto_reconnect == WIFI_AUTO_CNT_OFF) + { + return ; + } + else if (auto_reconnect == WIFI_AUTO_CNT_TMP_OFF) + { + auto_reconnect = WIFI_AUTO_CNT_ON; + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_reconnect); + return ; //tmparary return, for active "DISCONNECT" , such as AT CMD + } + tls_param_get(TLS_PARAM_ID_WPROTOCOL, (void*) &wireless_protocol, TRUE); + switch (wireless_protocol) + { + case TLS_PARAM_IEEE80211_INFRA: +#if TLS_CONFIG_AP + case (TLS_PARAM_IEEE80211_INFRA | TLS_PARAM_IEEE80211_SOFTAP): +#endif + { + struct tls_param_original_key origin_key; + struct tls_param_bssid bssid; + tls_param_get(TLS_PARAM_ID_BSSID, (void*) &bssid, TRUE); + tls_param_get(TLS_PARAM_ID_SSID, (void*) &ssid, TRUE); + tls_param_get(TLS_PARAM_ID_ORIGIN_KEY, (void*) &origin_key, TRUE); + + if (bssid.bssid_enable) + { + if(ssid.ssid_len) + { + tls_wifi_connect_by_ssid_bssid(ssid.ssid, ssid.ssid_len, bssid.bssid, origin_key.psk, origin_key.key_length); + } + else + { + tls_wifi_connect_by_bssid(bssid.bssid, origin_key.psk, origin_key.key_length); + } + } + else if(ssid.ssid_len) + { + tls_wifi_connect(ssid.ssid, ssid.ssid_len, origin_key.psk, origin_key.key_length); + } + + } + break; +#if TLS_CONFIG_AP + case TLS_PARAM_IEEE80211_SOFTAP: + { + tls_auto_reconnect_softap(); + } + break; +#endif + default: + break; + } + + return ; +} + +//------------------------------------------------------------------------- + +#if TLS_CONFIG_RMMS +static void tls_proc_rmms(struct rmms_msg *msg) +{ + int err; + struct tls_hostif *hif = tls_get_hostif(); + + if (0 == hif->rmms_status) + { + hif->rmms_status = 1; + err = tls_hostif_cmd_handler(HOSTIF_RMMS_AT_CMD, (char*)msg, 6+strlen( + (char*)(msg->CmdStr))); + if (0 != err) + { + tls_mem_free(msg); + hif->rmms_status = 0; + } + } + + return ; +} + +//------------------------------------------------------------------------- + +#endif + +/* + * sys task stack + */ +static u32 sys_task_stk[SYS_TASK_STK_SIZE]; + +void tls_sys_task(void *data) +{ + u8 err; + struct tls_sys_msg *msg; + u8 auto_reconnect = WIFI_AUTO_CNT_OFF; + + //u8 oneshotflag = 0; + //u8 auto_mode = 0; + for (;;) + { + err = tls_os_queue_receive(msg_queue, (void **) &msg, 0, 0); + if (!err) + { + switch (msg->msg) + { + case SYS_MSG_NET_UP: + sys_net_up(); + break; +#if TLS_CONFIG_AP + case SYS_MSG_NET2_UP: + sys_net2_up(); + break; + case SYS_MSG_NET2_DOWN: + sys_net2_down(); + break; + case SYS_MSG_NET2_FAIL: + sys_net2_down(); + tls_auto_reconnect(); + break; +#endif + case SYS_MSG_NET_DOWN: + sys_net_down(); + break; + case SYS_MSG_CONNECT_FAILED: + sys_net_down(); + break; + case SYS_MSG_AUTO_MODE_RUN: + /*restore WiFi auto reconnect Tmp OFF*/ + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, + &auto_reconnect); + if (auto_reconnect == WIFI_AUTO_CNT_TMP_OFF) + { + auto_reconnect = WIFI_AUTO_CNT_ON; + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, + &auto_reconnect); + } + + tls_auto_reconnect(); + break; +#if TLS_CONFIG_RMMS + case SYS_MSG_RMMS: + tls_proc_rmms(msg->data); + break; +#endif + default: + break; + } + tls_mem_free(msg); + } + else + { + + } + } +} + +//------------------------------------------------------------------------- + +void tls_sys_send_msg(u32 msg, void *data) +{ + struct tls_sys_msg *pmsg; + + pmsg = tls_mem_alloc(sizeof(struct tls_sys_msg)); + if (NULL != pmsg) + { + memset(pmsg, 0, sizeof(struct tls_sys_msg)); + pmsg->msg = msg; + pmsg->data = data; + tls_os_queue_send(msg_queue, pmsg, 0); + } + + return ; +} + +//------------------------------------------------------------------------- + +void tls_sys_auto_mode_run(void) +{ + tls_sys_send_msg(SYS_MSG_AUTO_MODE_RUN, NULL); +} + +//------------------------------------------------------------------------- + +static void tls_sys_net_up(void) +{ + tls_sys_send_msg(SYS_MSG_NET_UP, NULL); +} + +//------------------------------------------------------------------------- + +#if TLS_CONFIG_AP +static void tls_sys_net2_up(void) +{ + tls_sys_send_msg(SYS_MSG_NET2_UP, NULL); +} + +//------------------------------------------------------------------------- + +static void tls_sys_net2_down(void) +{ + tls_sys_send_msg(SYS_MSG_NET2_DOWN, NULL); +} + +static void tls_sys_net2_fail(void) +{ + tls_sys_send_msg(SYS_MSG_NET2_FAIL, NULL); +} +#endif +static void tls_sys_net_down(void) +{ + tls_sys_send_msg(SYS_MSG_NET_DOWN, NULL); +} + +//------------------------------------------------------------------------- + +static void tls_sys_connect_failed(void) +{ + tls_sys_send_msg(SYS_MSG_CONNECT_FAILED, NULL); +} + +//------------------------------------------------------------------------- + +static void sys_net_status_changed(u8 status) +{ +#if TLS_CONFIG_TLS_DEBUG + struct tls_ethif *ethif; +#endif +#if TLS_CONFIG_AP + u8 wireless_protocol = 0; +#endif + bool enable = FALSE; + + switch (status) + { + case NETIF_WIFI_JOIN_SUCCESS: + TLS_DBGPRT_INFO("join net success\n"); + tls_sys_net_up(); + break; + case NETIF_WIFI_JOIN_FAILED: + { + TLS_DBGPRT_INFO("join net failed\n"); + } + tls_sys_connect_failed(); + break; + case NETIF_WIFI_DISCONNECTED: + { + TLS_DBGPRT_INFO("net disconnected\n"); + } + tls_sys_net_down(); + break; + case NETIF_IP_NET_UP: + tls_netif_set_status(1); + /*when DHCP enable, IP_NET_UP Report, set wifi powersaving flag according to TLS_PARAM_ID_PSM*/ + tls_param_get(TLS_PARAM_ID_PSM, &enable, TRUE); + tls_wifi_set_psflag(enable, FALSE); + +#if TLS_CONFIG_TLS_DEBUG + ethif = tls_netif_get_ethif(); + TLS_DBGPRT_INFO("net up ==> ip = %v\n", ethif->ip_addr.addr); +#endif +#if TLS_CONFIG_AP + tls_param_get(TLS_PARAM_ID_WPROTOCOL, (void*) &wireless_protocol, + TRUE); + if ((TLS_PARAM_IEEE80211_SOFTAP &wireless_protocol) && + (WM_WIFI_JOINED != tls_wifi_softap_get_state())) + { + tls_auto_reconnect_softap(); + } +#endif +//#if TLS_CONFIG_RMMS +// tls_rmms_start(); +//#endif + break; +#if TLS_CONFIG_AP + case NETIF_WIFI_SOFTAP_SUCCESS: + TLS_DBGPRT_INFO("softap create success.\n"); + tls_sys_net2_up(); + break; + case NETIF_WIFI_SOFTAP_FAILED: + TLS_DBGPRT_INFO("softap create failed.\n"); + tls_sys_net2_fail(); + break; + case NETIF_WIFI_SOFTAP_CLOSED: + TLS_DBGPRT_INFO("softap closed.\n"); + tls_sys_net2_down(); + break; + case NETIF_IP_NET2_UP: +#if TLS_CONFIG_TLS_DEBUG + ethif = tls_netif_get_ethif2(); + TLS_DBGPRT_INFO("net up ==> ip = %v\n", ethif->ip_addr.addr); +#endif + break; +#endif + default: + break; + } +} + +//------------------------------------------------------------------------- + +int tls_sys_init() +{ + int err; + + /* create messge queue */ +#define SYS_MSG_SIZE 20 + + err = tls_os_queue_create(&msg_queue, SYS_MSG_SIZE); + if (err) + { + return - 1; + } + + /* create task */ + tls_os_task_create(NULL, "Sys Task", tls_sys_task, (void*)0, (void*) + &sys_task_stk, /* task's stack start address */ + SYS_TASK_STK_SIZE *sizeof(u32), /* task's stack size, unit:byte */ + TLS_SYS_TASK_PRIO, 0); + + tls_netif_add_status_event(sys_net_status_changed); + + return 0; +} + diff --git a/sdk/Platform/Sys/tls_sys.h b/sdk/Platform/Sys/tls_sys.h new file mode 100644 index 0000000..2f2e87e --- /dev/null +++ b/sdk/Platform/Sys/tls_sys.h @@ -0,0 +1,22 @@ + +#ifndef TLS_SYS_H +#define TLS_SYS_H +#include "wm_type_def.h" +#include "wm_params.h" + +#define SYS_MSG_NET_UP 1 +#define SYS_MSG_NET_DOWN 2 +#define SYS_MSG_CONNECT_FAILED 3 +#define SYS_MSG_AUTO_MODE_RUN 4 + +#define SYS_MSG_NET2_UP 5 +#define SYS_MSG_NET2_DOWN 6 +#define SYS_MSG_NET2_FAIL 7 + +#define SYS_MSG_RMMS 8 + +int tls_sys_init(void); +void tls_auto_reconnect(void); + + +#endif /* end of TLS_SYS_H */ diff --git a/sdk/Platform/Sys/wm_main.c b/sdk/Platform/Sys/wm_main.c new file mode 100644 index 0000000..0420580 --- /dev/null +++ b/sdk/Platform/Sys/wm_main.c @@ -0,0 +1,402 @@ +/***************************************************************************** +* +* File Name : wm_main.c +* +* Description: wm main +* +* Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd. +* All rights reserved. +* +* Author : +* +* Date : 2014-6-14 +*****************************************************************************/ +#include +#include "wm_irq.h" +#include "tls_sys.h" + +#include "wm_regs.h" +#include "wm_type_def.h" +#include "wm_timer.h" +#include "wm_irq.h" +#include "wm_params.h" +#include "wm_hostspi.h" +#include "wm_flash.h" +#include "wm_fls_gd25qxx.h" +#include "wm_internal_flash.h" +#include "wm_efuse.h" +#include "wm_debug.h" +#include "wm_netif.h" +#include "wm_at_ri_init.h" +#include "wm_config.h" +#include "wm_osal.h" +#include "wm_http_client.h" +#include "wm_cpu.h" +#include "misc.h" +#include "wm_webserver.h" +#include "wm_io.h" +#include "wm_mem.h" +#include "wm_wl_task.h" +#include "wm_wl_timers.h" +#include "wm_watchdog.h" +#ifdef TLS_CONFIG_HARD_CRYPTO +#include "wm_crypto_hard.h" +#endif +#include "wm_gpio_afsel.h" +#include "wm_pmu.h" + + +/* c librayr mutex */ +tls_os_sem_t *libc_sem; +/*---------------------------------------------------------------------------- + * Standard Library multithreading interface + *---------------------------------------------------------------------------*/ + +#ifndef __MICROLIB +/*--------------------------- _mutex_initialize -----------------------------*/ + +int _mutex_initialize (u32 *mutex) { + /* Allocate and initialize a system mutex. */ + //tls_os_sem_create(&libc_sem, 1); + //mutex = (u32 *)libc_sem; + return(1); +} + + +/*--------------------------- _mutex_acquire --------------------------------*/ + +void _mutex_acquire (u32 *mutex) { + //u8 err; + /* Acquire a system mutex, lock stdlib resources. */ + tls_os_sem_acquire(libc_sem, 0); +} + + +/*--------------------------- _mutex_release --------------------------------*/ + +void _mutex_release (u32 *mutex) { + /* Release a system mutex, unlock stdlib resources. */ + tls_os_sem_release(libc_sem); +} + +#endif + +#define TASK_START_STK_SIZE 1200 /* Size of each task's stacks (# of WORDs) */ + +u32 TaskStartStk[TASK_START_STK_SIZE]; + +#ifdef TLS_CONFIG_HTTP_CLIENT_TASK +u8 pSession_flag=0; +#endif + + +#define FW_MAJOR_VER 0x03 +#define FW_MINOR_VER 0x04 +#define FW_PATCH_VER 0x08 + +const char FirmWareVer[4] = { + 'G', + FW_MAJOR_VER, /* Main version */ + FW_MINOR_VER, /* Subversion */ + FW_PATCH_VER /* Internal version */ + }; +const char HwVer[6] = { + 'H', + 0x1, + 0x0, + 0x0, + 0x0, + 0x0 +}; +extern const char WiFiVer[]; +extern u8 tx_gain_group[]; +extern void *tls_wl_init(u8 *tx_gain, u8* mac_addr, u8 *hwver); +extern int wpa_supplicant_init(u8 *mac_addr); +extern void tls_sys_auto_mode_run(void); +extern void tls_spi_slave_sel(u16 slave); +extern void EarlyInit(void); +extern void UserMain(void); +extern void tls_fls_layout_init(void); +extern void Debug_UartInit(void); + + +void task_start (void *data); + +void tls_os_timer_init(void) +{ + tls_sys_clk sysclk; + + tls_sys_clk_get(&sysclk); + SysTick_Config(sysclk.cpuclk*UNIT_MHZ/HZ); + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); +} +/****************/ +/* main program */ +/****************/ + +void vApplicationIdleHook( void ) +{ + /* clear watch dog interrupt */ + tls_watchdog_clr(); + +#if !defined(__CC_ARM) + __asm volatile ("wfi"); +#else + __WFI(); +#endif + + return; +} + +void tls_exception_handler(unsigned int *sp, unsigned int exceptinfo) +{ + int i = 0; + u32 value = 0; + value = (*(volatile int *)0xE000ED28); + switch(exceptinfo) + { + case 1: + printf("mem fault:\n"); + if (value&0xFF) + { + printf("E000_ED28 Val:%x\n", value&0xFF); + } + break; + case 2: + printf("bus fault:\n"); + if ((value>>8)&0xFF) + { + printf("E000_ED29 Val:%x\n", ((value>>8)&0xFF)); + } + break; + case 3: + printf("usage fault:\n"); + if ((value>>16)&0x3FF) + { + printf("E000_ED2A Val:%x\n", ((value>>16)&0x3FF)); + } + break; + case 0: + default: + printf("hard fault:\n"); + value = *(volatile int *)0xE000ED2C; + if (value) + { + printf("E000_ED2C Val:%x\n", value); + } + break; + } + + // printf("Stack:%08p\n", sp); + // printf("StackInfo:\n"); + for (i = 0; i < 16; i++) + { + printf("%08x\n", *sp++); + } + + while(1); +} +void wm_gpio_config() +{ + /* must call first */ + wm_gpio_af_disable(); + + /* UART0_TX-PA04 UART0_RX-PA05 */ + wm_uart0_tx_config(WM_IO_PA_04); + wm_uart0_rx_config(WM_IO_PA_05); + + /* UART1_RX-PB11 UART1_TX-PB12 */ + wm_uart1_rx_config(WM_IO_PB_11); + wm_uart1_tx_config(WM_IO_PB_12); +#if WM_CONFIG_DEBUG_UART2 + /* UART2_RX-PA00 UART2_TX-PA01 */ + wm_uart2_rx_config(WM_IO_PA_00); + wm_uart2_tx_scio_config(WM_IO_PA_01); +#endif + + extern void pre_gpio_config(); + pre_gpio_config(); + /*MASTER SPI configuratioin*/ + //wm_spi_cs_config(WM_IO_PB_15); + //wm_spi_ck_config(WM_IO_PB_16); + wm_spi_di_config(WM_IO_PB_17); + //wm_spi_do_config(WM_IO_PB_18); +} + +bool debug_monitor_enable(void) { + volatile uint32_t *dhcsr = (uint32_t*)0xE000EDF0; + if ((*dhcsr & 0x1) != 0) { + printf("Halting Debug Enabled - Can't Enable Monitor Mode Debug!"); + return false; + } + + volatile uint32_t *demcr = (uint32_t*)0xE000EDFC; + const uint32_t mon_en_bit = 16; + *demcr |= 1 << mon_en_bit; + + // Priority for DebugMonitor Exception is bits[7:0]. + // We will use the lowest priority so other ISRs can + // fire while in the DebugMonitor Interrupt + volatile uint32_t *shpr3 = (uint32_t *)0xE000ED20; + *shpr3 = 0xff; + + printf("Monitor Mode Debug Enabled!"); + return true; +} + +void tls_fix_wifi_irq_priority() +{ + tls_irq_set_priority(RX_INT, IRQ_MAX_SYSCALL_PRIORITY); +} + +int main(void) +{ + tls_sys_clk_set(CPU_CLK_80M); + + tls_pmu_clk_select(0); + + tls_os_init(NULL); + + Debug_UartInit();/*initialize debug uart for mapping to printf*/ + + /* before use malloc() function, must create mutex used by c_lib */ + tls_os_sem_create(&libc_sem, 1); + + EarlyInit(); + + { + tls_os_task_create(NULL, NULL, + task_start, + (void *)0, + (void *)&TaskStartStk[0], /* task's stack start address */ + TASK_START_STK_SIZE * sizeof(u32), /* task's stack size, unit:byte */ + 1, + 0); + } + + + /* initial os ticker */ + tls_os_timer_init(); + + //debug_monitor_enable(); + + tls_os_start_scheduler(); + + + return 0; +} + + +void disp_version_info(void) +{ + TLS_DBGPRT_INFO("\n\n"); + TLS_DBGPRT_INFO("****************************************************************\n"); + TLS_DBGPRT_INFO("* *\n"); + TLS_DBGPRT_INFO("* Copyright (C) 2014 WinnerMicro Co. Ltd. *\n"); + TLS_DBGPRT_INFO("* All rights reserved. *\n"); + TLS_DBGPRT_INFO("* WinnerMicro Firmware Version: %x.%x.%X *\n", + FirmWareVer[1], FirmWareVer[2], FirmWareVer[3]); + TLS_DBGPRT_INFO("* WinnerMicro Hardware Version: %x.%x.%x.%x.%x *\n", + HwVer[1], HwVer[2], HwVer[3],HwVer[4],HwVer[5]); + TLS_DBGPRT_INFO("* *\n"); + TLS_DBGPRT_INFO("* WinnerMicro Wi-Fi Lib Version: %x.%x.%x *\n", + WiFiVer[0], WiFiVer[1], WiFiVer[2]); + TLS_DBGPRT_INFO("****************************************************************\n"); +} + + +/***************************************************************************** + * Function Name // task_start + * Descriptor // before create multi_task, we create a task_start task + * // in this example, this task display the cpu usage + * Input + * Output + * Return + ****************************************************************************/ +void task_start (void *data) +{ + u8 mac_addr[6] = {0x00,0x25,0x08,0x09,0x01,0x0F}; + /* must call first to configure gpio Alternate functions according the hardware design */ + wm_gpio_config(); + +#if TLS_CONFIG_HARD_CRYPTO + tls_crypto_init(); +#endif + +#if (TLS_CONFIG_LS_SPI) + tls_spi_init(); + tls_spifls_init(); +#endif + + tls_fls_init(); + + /*initialize flash layout parameter according to image type*/ + tls_fls_layout_init(); + + /*PARAM GAIN,MAC default*/ + tls_ft_param_init(); + tls_param_load_factory_default(); + tls_param_init(); /*add param to init sysparam_lock sem*/ + + tls_get_tx_gain(&tx_gain_group[0]); + TLS_DBGPRT_INFO("tx gain "); + TLS_DBGPRT_DUMP((char *)(&tx_gain_group[0]), 12); + + if (tls_wifi_mem_cfg(WIFI_MEM_START_ADDR, 7, 7)) /*wifi tx&rx mem customized interface*/ + { + TLS_DBGPRT_INFO("wl mem initial failured\n"); + } + + tls_get_mac_addr(&mac_addr[0]); + TLS_DBGPRT_INFO("mac addr "); + TLS_DBGPRT_DUMP((char *)(&mac_addr[0]), 6); + + if(tls_wl_init(NULL, &mac_addr[0], NULL) == NULL){ + TLS_DBGPRT_INFO("wl driver initial failured\n"); + } + if (wpa_supplicant_init(mac_addr)) { + TLS_DBGPRT_INFO("supplicant initial failured\n"); + } + + tls_fix_wifi_irq_priority(); + + tls_ethernet_init(); + + tls_sys_init(); +/*HOSTIF&UART*/ +#if TLS_CONFIG_HOSTIF + tls_hostif_init(); + +#if (TLS_CONFIG_HS_SPI) + tls_hspi_init(); +#endif + +#if TLS_CONFIG_UART + //tls_uart_init(); +#endif + +#if TLS_CONFIG_HTTP_CLIENT_TASK + // http_client_task_init(); +#endif + +#endif + + /* call user main function */ + UserMain(); + tls_sys_auto_mode_run(); + + NVIC_SystemLPConfig(NVIC_LP_SLEEPDEEP, ENABLE); + + for (;;) + { +#if 1 + tls_os_time_delay(0x10000000); +#else + //printf("start up\n"); + extern void tls_os_disp_task_stat_info(void); + tls_os_disp_task_stat_info(); + tls_os_time_delay(1000); +#endif + } +} + diff --git a/sdk/Src/App/OTA/CMakeLists.txt b/sdk/Src/App/OTA/CMakeLists.txt new file mode 100644 index 0000000..e24b8d2 --- /dev/null +++ b/sdk/Src/App/OTA/CMakeLists.txt @@ -0,0 +1,43 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WMOTA) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin ") +STRING(APPEND CMAKE_C_FLAGS "-DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/wm_http_fwup.c" + "${CMAKE_CURRENT_SOURCE_DIR}/wm_socket_fwup.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmota.a) \ No newline at end of file diff --git a/sdk/Src/App/OTA/wm_http_fwup.c b/sdk/Src/App/OTA/wm_http_fwup.c new file mode 100644 index 0000000..97be5fc --- /dev/null +++ b/sdk/Src/App/OTA/wm_http_fwup.c @@ -0,0 +1,237 @@ +#include +#include +#include "wm_socket_fwup.h" +#include "wm_fwup.h" +#include "wm_http_fwup.h" +#include "wm_debug.h" +#include "wm_mem.h" + +#if TLS_CONFIG_HTTP_CLIENT + +#define HTTP_CLIENT_BUFFER_SIZE 2048 +#define USE_BREAK_POINT_RESUME 1 +#define RECV_TIMEOUT 5 + + + +enum ota_state {PREPARE_PACKET, SETUP_LINK_AND_REQ, RECV_RSP, HANDLE_HEADER, HANDLE_BODY, SHUTDOWN_LINK, QUIT_OTA}; + +int http_fwup(HTTPParameters ClientParams) +{ + char* Buffer = NULL; + char token[32]; + char headRange[32] = {0}; + int nRetCode = 0; + u32 content_length=0, size=32; + u32 partLen; + u32 totalLen = 0; + u32 recvLen = 0; + T_BOOTER *booter; +#if USE_BREAK_POINT_RESUME == 0 + u32 breakFlag = 0; + u32 breakLen = 0; +#endif + struct pbuf *p; + HTTP_SESSION_HANDLE pHTTP; + enum ota_state now_state = PREPARE_PACKET; + + Buffer = (char*)tls_mem_alloc(HTTP_CLIENT_BUFFER_SIZE); + if (!Buffer) + return HTTP_CLIENT_ERROR_NO_MEMORY; + memset(Buffer, 0, HTTP_CLIENT_BUFFER_SIZE); + + while(1) + { + switch(now_state) + { + case PREPARE_PACKET: + { + memset(token, 0, 32); + size = 32; + pHTTP = HTTPClientOpenRequest(0); +#if USE_BREAK_POINT_RESUME + HTTPClientSetVerb(pHTTP,VerbGet); + sprintf(headRange, "bytes=%d-", recvLen); + if((nRetCode = HTTPClientAddRequestHeaders(pHTTP,"Range", headRange, 1))!= HTTP_CLIENT_SUCCESS){ + now_state = QUIT_OTA; + } +#else + if( recvLen != 0 ){ + breakFlag = 1; + breakLen = 0; + } +#endif + now_state = SETUP_LINK_AND_REQ; + } + break; + + case SETUP_LINK_AND_REQ: + { + if( (nRetCode = HTTPClientSendRequest(pHTTP,ClientParams.Uri,NULL,0,FALSE,0,0)) != HTTP_CLIENT_SUCCESS ){ + tls_os_time_delay(HZ*2); + now_state = SHUTDOWN_LINK; + } + else + now_state = RECV_RSP; + } + break; + + case RECV_RSP: + { + if((nRetCode = HTTPClientRecvResponse(pHTTP, RECV_TIMEOUT)) != HTTP_CLIENT_SUCCESS) + now_state = SHUTDOWN_LINK; + else + now_state = HANDLE_HEADER; + } + break; + + case HANDLE_HEADER: + { + if((nRetCode = HTTPClientFindFirstHeader(pHTTP, "content-length", (CHAR *)token, &size)) != HTTP_CLIENT_SUCCESS){ + HTTPClientFindCloseHeader(pHTTP); + now_state = SHUTDOWN_LINK; + } + else + { + HTTPClientFindCloseHeader(pHTTP); + content_length = atol(strstr(token,":")+1); + printf("content_length: %d\n", content_length); + now_state = HANDLE_BODY; + if(recvLen == 0){ + nRetCode = socket_fwup_accept(0, ERR_OK); + if(nRetCode != ERR_OK) + now_state = QUIT_OTA; + } + } + } + break; + + case HANDLE_BODY: + { + partLen = 0; + while(nRetCode != HTTP_CLIENT_EOS ) + { + u32 nSize = HTTP_CLIENT_BUFFER_SIZE; + nRetCode = HTTPClientReadData(pHTTP,Buffer+3,nSize,RECV_TIMEOUT,&nSize); + if( recvLen == 0 ){ + //fileSize = headerSize(fixed: 56) + appCodeSize + booter =(T_BOOTER *) (Buffer+3); + if (TRUE == tls_fwup_img_header_check(booter)) + { + totalLen = booter->upd_img_len + sizeof(T_BOOTER); + //OTA_ResetProgress(); + //OTA_SetTotalBytes(totalLen); + } + else + { + now_state = QUIT_OTA; + break; + } + } + + if(nRetCode != HTTP_CLIENT_SUCCESS && nRetCode != HTTP_CLIENT_EOS){ + now_state = SHUTDOWN_LINK; + break; + } +#if USE_BREAK_POINT_RESUME == 0 + if(breakFlag == 1){ + breakLen += nSize; + if(breakLen <= recvLen){ + continue; + } + else{ + Buffer = Buffer+3+nSize-(breakLen-recvLen)-3; + nSize = (breakLen-recvLen); + breakFlag = 0; + } + } +#endif + + p = pbuf_alloc(PBUF_TRANSPORT, nSize + 3, PBUF_REF); + while( !p){ + tls_os_time_delay(1); + p = pbuf_alloc(PBUF_TRANSPORT, nSize + 3, PBUF_REF); + } + + if(recvLen == 0) + *(Buffer+0) = SOCKET_FWUP_START; + else if(nRetCode == HTTP_CLIENT_EOS && recvLen == (totalLen-nSize)) + *(Buffer+0) = SOCKET_FWUP_END; + else + *(Buffer+0) = SOCKET_FWUP_DATA; + + *(Buffer+1) = (nSize>>8) & 0xFF; + *(Buffer+2) = nSize & 0xFF; + p->payload = Buffer; + p->len = p->tot_len = nSize + 3; + // Send received data to fwup thread and deal with socket issues. + s8 ret = socket_fwup_recv(0, p, ERR_OK); + if(ret != ERR_OK){ + now_state = SHUTDOWN_LINK; + break; + } + else{ + //OTA_IncrementProgress(nSize); + recvLen += nSize; + partLen += nSize; + //printf("download %d / %d\n", recvLen, totalLen); //Status is being printed in our source + if(partLen == content_length){ + now_state = SHUTDOWN_LINK; + break; + } + } + } + if (now_state == QUIT_OTA); + else + now_state = SHUTDOWN_LINK; + } + break; + + case SHUTDOWN_LINK: + { + if(pHTTP){ + HTTPClientCloseRequest(&pHTTP); + (recvLen == totalLen)?(now_state = QUIT_OTA):(now_state = PREPARE_PACKET); + } + } + break; + + default: + break; + } + //printf("now_state %d\n", now_state); + if(now_state == QUIT_OTA) + break; + } + + tls_mem_free(Buffer); + if(pHTTP) + HTTPClientCloseRequest(&pHTTP); + if(ClientParams.Verbose == TRUE) + { + printf("\n\nHTTP Client terminated %d (got %d kb)\n\n",nRetCode,(recvLen/ 1024)); + } + if(nRetCode) + socket_fwup_err(0, nRetCode); + return nRetCode; +} + +int t_http_fwup(char *url) +{ + HTTPParameters httpParams; + memset(&httpParams, 0, sizeof(HTTPParameters)); + if (url == NULL) + { + httpParams.Uri = "http://192.168.1.100:8080/WM_W600_SEC.img"; + } + else + { + httpParams.Uri = url; + } + printf("Location: %s\n",httpParams.Uri); + httpParams.Verbose = TRUE; + return http_fwup(httpParams); +} + +#endif //TLS_CONFIG_HTTP_CLIENT && TLS_CONFIG_SOCKET_RAW + diff --git a/sdk/Src/App/OTA/wm_http_fwup.h b/sdk/Src/App/OTA/wm_http_fwup.h new file mode 100644 index 0000000..5c9147f --- /dev/null +++ b/sdk/Src/App/OTA/wm_http_fwup.h @@ -0,0 +1,35 @@ +/***************************************************************************** +* +* File Name : wm_http_fwup.h +* +* Description: Http firmware update header file. +* +* Copyright (c) 2014 Winner Microelectronics Co., Ltd. +* All rights reserved. +* +* Author : wanghf +* +* Date : 2014-6-5 +*****************************************************************************/ + +#ifndef WM_HTTP_FWUP_H +#define WM_HTTP_FWUP_H + +#include "wm_type_def.h" +#include "wm_http_client.h" + +/*************************************************************************** +* Function: http_fwup +* Description: Download the firmware from internet by http and upgrade it. +* +* Input: ClientParams: The parameters of connecting http server. +* +* Output: None +* +* Return: 0-success, other- failed +* +* Date : 2014-6-5 +****************************************************************************/ +int http_fwup(HTTPParameters ClientParams); + +#endif //WM_HTTP_FWUP_H diff --git a/sdk/Src/App/OTA/wm_socket_fwup.c b/sdk/Src/App/OTA/wm_socket_fwup.c new file mode 100644 index 0000000..d3ea7ac --- /dev/null +++ b/sdk/Src/App/OTA/wm_socket_fwup.c @@ -0,0 +1,157 @@ +#include "wm_config.h" + +#include +#include "wm_debug.h" +#include "wm_socket_fwup.h" +#include "list.h" +#include "wm_fwup.h" +#include "wm_sockets.h" +#include "wm_mem.h" + +#define SOCKET_FWUP_PACK_SIZE 2048 +static struct socket_fwup_pack current_pack; +static u16 current_offset; +static u32 session_id; + +static void free_current_pack() +{ + session_id = 0; + if(current_pack.data != NULL) + { + tls_mem_free(current_pack.data); + memset(¤t_pack, 0, sizeof(struct socket_fwup_pack)); + } +} + +s8 socket_fwup_recv(u8 skt_num, struct pbuf *p, s8 err) +{ + u16 copylen; + int offset = 0; + int ret = 0; + do + { + if(current_pack.operation < SOCKET_FWUP_START) + { + current_offset = 0; + copylen = pbuf_copy_partial(p, ¤t_pack.operation, 1, offset); + //TLS_DBGPRT_ERR("operation = %d, p->tot_len = %d\n", current_pack.operation, p->tot_len ); + offset += copylen; + copylen = pbuf_copy_partial(p, ¤t_pack.datalen, 2, offset); + //TLS_DBGPRT_ERR("datalen copylen = %d\n", copylen); + current_pack.datalen = ntohs(current_pack.datalen); + TLS_DBGPRT_ERR("datalen = %d\n", current_pack.datalen); + offset += copylen; + if(current_pack.operation > SOCKET_FWUP_DATA || current_pack.operation < SOCKET_FWUP_START) + { + TLS_DBGPRT_ERR("current_pack.operation = %d is invalid\n", current_pack.operation); + goto err;//return ERR_ABRT; + } + if(current_pack.operation == SOCKET_FWUP_START) + { + session_id = tls_fwup_enter(TLS_FWUP_IMAGE_SRC_WEB); + if(session_id == 0) + { + TLS_DBGPRT_ERR("session_id = %d is invalid\n", session_id); + goto err;//return ERR_ABRT; + } + } + + if (current_pack.datalen == 0) + { + //tls_socket_close(skt_num); + goto err;//return ERR_ABRT; + } + } + else if(current_pack.datalen > 0 && current_pack.datalen <= SOCKET_FWUP_PACK_SIZE) + { + copylen = pbuf_copy_partial(p, ¤t_pack.data[current_offset], current_pack.datalen-current_offset, offset); + //TLS_DBGPRT_INFO("copylen=%d, current_pack.data = %02x%02x%02x%02x%02x%02x", copylen, current_pack.data[0], current_pack.data[1], current_pack.data[2], current_pack.data[3], current_pack.data[4], current_pack.data[5]); + offset += copylen; + current_offset += copylen; + if(current_offset == current_pack.datalen) + { + ret = tls_fwup_request_sync(session_id, current_pack.data, current_pack.datalen); + if(ret != TLS_FWUP_STATUS_OK) + { + TLS_DBGPRT_ERR("up data error.\n"); + goto err;//return ERR_ABRT; + } + + if(current_pack.operation == SOCKET_FWUP_END) + { +#if TLS_CONFIG_SOCKET_RAW + tls_socket_close(skt_num); +#endif + tls_fwup_exit(session_id); + free_current_pack(); + break; + } + current_pack.operation = 0; + } + } + else if(current_pack.datalen == 0) + { + if(current_pack.operation == SOCKET_FWUP_END) + { +#if TLS_CONFIG_SOCKET_RAW + tls_socket_close(skt_num); +#endif + tls_fwup_exit(session_id); + free_current_pack(); + break; + } + current_pack.operation = 0; + } + }while(offset < p->tot_len); + if (p) + pbuf_free(p); + return ERR_OK; + +err: + if (p) + pbuf_free(p); + return ERR_ABRT; +} + +void socket_fwup_err(u8 skt_num, s8 err) +{ + TLS_DBGPRT_INFO("socket num=%d, err= %d\n", skt_num, err); + tls_fwup_exit(session_id); + free_current_pack(); +} + +s8 socket_fwup_poll(u8 skt_num) +{ + //printf("socketpoll skt_num : %d\n", skt_num); + return ERR_OK; +} + +s8 socket_fwup_accept(u8 skt_num, s8 err) +{ + TLS_DBGPRT_INFO("accept socket num=%d, err= %d\n", skt_num, err); + if(ERR_OK == err) + { + //ret = tls_fwup_init(); + //if(ret == TLS_FWUP_STATUS_EMEM) + // return ERR_ABRT; + if(session_id == 0 || tls_fwup_current_state(session_id) == TLS_FWUP_STATE_UNDEF) + { + memset(¤t_pack, 0, sizeof(struct socket_fwup_pack)); + current_pack.data = tls_mem_alloc(SOCKET_FWUP_PACK_SIZE); + if(NULL == current_pack.data) + { + printf("\nmalloc socket fwup pack fail\n"); + return ERR_ABRT; + } + memset(current_pack.data, 0, SOCKET_FWUP_PACK_SIZE); + return ERR_OK; + } + else + { + TLS_DBGPRT_ERR("session_id=%d is not current session id\n", session_id); + return ERR_ABRT; + } + } + return err; +} + diff --git a/sdk/Src/App/OTA/wm_socket_fwup.h b/sdk/Src/App/OTA/wm_socket_fwup.h new file mode 100644 index 0000000..778b51d --- /dev/null +++ b/sdk/Src/App/OTA/wm_socket_fwup.h @@ -0,0 +1,23 @@ +#ifndef TLS_SOCKET_FWUP_H +#define TLS_SOCKET_FWUP_H + +#include "wm_socket.h" + +#define SOCKET_FWUP_PORT 65533 + +#define SOCKET_FWUP_START 1 +#define SOCKET_FWUP_END 2 +#define SOCKET_FWUP_DATA 3 + +struct socket_fwup_pack{ + u8 operation; + u16 datalen; + u8 * data; +}; + +s8 socket_fwup_recv(u8 skt_num, struct pbuf *p, s8 err); +void socket_fwup_err(u8 skt_num, s8 err); +s8 socket_fwup_poll(u8 skt_num); +s8 socket_fwup_accept(u8 skt_num, s8 err); + +#endif diff --git a/sdk/Src/App/dhcpserver/CMakeLists.txt b/sdk/Src/App/dhcpserver/CMakeLists.txt new file mode 100644 index 0000000..160eb09 --- /dev/null +++ b/sdk/Src/App/dhcpserver/CMakeLists.txt @@ -0,0 +1,42 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WMDHCPSERVER) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin ") +STRING(APPEND CMAKE_C_FLAGS " -DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/dhcp_server.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmdhcpserver.a) \ No newline at end of file diff --git a/sdk/Src/App/dhcpserver/dhcp_server.c b/sdk/Src/App/dhcpserver/dhcp_server.c new file mode 100644 index 0000000..a41ac45 --- /dev/null +++ b/sdk/Src/App/dhcpserver/dhcp_server.c @@ -0,0 +1,1102 @@ +/************************************************************************** + * File Name : dhcp_server.c + * Author : + * Version : + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ +#ifndef WM_W600 +#include +#else +#ifndef assert +#define assert(n) +#endif +#endif +#include +#include +#include + +#include "tls_common.h" +#include "wm_mem.h" +#include "wm_debug.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/dhcp.h" +#include "lwip/prot/dhcp.h" +#include "netif/ethernetif.h" +#include "dhcp_server.h" + +#if TLS_CONFIG_AP + +#define ETHARP_FLAG_FIND_ONLY 2 +#define ETHARP_FLAG_STATIC_ENTRY 4 + + + +extern u8 *wpa_supplicant_get_mac(void); + + +/* ÊÇ·ñ¸ù¾Ý¿Í»§¶Ëdhcp±¨ÎÄÖеÄbroadcast±êÖ¾À´»ØÓ¦£¬²»Ê¹ÓÃÔòͳһʹÓù㲥»Ø¸´ */ +#define DHCPS_CHECK_BROADCAST_FLAG +#ifdef DHCPS_CHECK_BROADCAST_FLAG +#define IS_BROADCAST_SEND(x) (((x) >> 15) == 1 ? TRUE : FALSE) +#endif + +static DHCP_SERVER DhcpServer; +static DHCP_MSG DhcpMsg; + +#define DHCP_SET_OPTION_SUBNET_MASK(buffer, mask, len) \ + { \ + INT32U tmp; \ + *buffer++ = DHCP_OPTION_ID_SUBNET_MASK; \ + *buffer++ = 4; \ + tmp = mask; \ + MEMCPY(buffer, &tmp, 4); \ + buffer += 4; \ + len += 6; \ + } + +#define DHCP_SET_OPTION_GW(buffer, gw, len) \ + { \ + INT32U tmp; \ + *buffer++ = DHCP_OPTION_ID_DEF_GW; \ + *buffer++ = 4; \ + tmp = gw; \ + MEMCPY(buffer, &tmp, 4); \ + buffer += 4; \ + len += 6; \ + } + +#define DHCP_SET_OPTION_DNS(buffer, dns1, dns2, len) \ + { \ + INT32U tmp; \ + *buffer++ = DHCP_OPTION_ID_DNS_SERVER; \ + *buffer++ = 8; \ + tmp = dns1; \ + MEMCPY(buffer, &tmp, 4); \ + buffer += 4; \ + tmp = dns2; \ + MEMCPY(buffer, &tmp, 4); \ + buffer += 4; \ + len += 10; \ + } + +#define DHCP_SET_OPTION_SERVER_ID(buffer, ip, len) \ + { \ + INT32U tmp; \ + *buffer++ = DHCP_OPTION_ID_SERVER_ID; \ + *buffer++ = 4; \ + tmp = ip; \ + MEMCPY(buffer, &tmp, 4); \ + buffer += 4; \ + len += 6; \ + } + +#define DHCP_SET_OPTION_MSG_TYPE(buffer, type, len) \ + { \ + *buffer++ = DHCP_OPTION_ID_MSG_TYPE; \ + *buffer++ = 1; \ + *buffer++ = type; \ + len += 3; \ + } + +#define DHCP_SET_OPTION_LEASE_TIME(buffer, time, len) \ + { \ + INT32U tmp; \ + *buffer++ = DHCP_OPTION_ID_LEASE_TIME; \ + *buffer++ = 4; \ + tmp = htonl(time); \ + MEMCPY(buffer, &tmp, 4); \ + buffer += 4; \ + len += 6; \ + } + +#define DHCP_SET_OPTION_END(buffer, len) \ + { \ + *buffer++ = DHCP_OPTION_ID_END; \ + len ++; \ + } + +#if 0 +static void _PostMsgToSysQ(PDHCP_CLIENT pClient, INT16U nMsgId) +{ + + TSYSC_MSG * Msg; + INT8U Err; + INT8U Len; + + Len = 9; + Msg = tls_mem_alloc(sizeof(TSYSC_MSG) + Len); + if (Msg == NULL){return;} + + Msg->Id = nMsgId; + Msg->Len = Len; + Msg->Argc = 0; + + MEMCPY(&Msg->Argv[0], pClient->MacAddr, 6); + MEMCPY(&Msg->Argv[0] + 6, (void *)&pClient->IpAddr, 4); + + Err = OSQPost(Que_Sys, (void *)Msg); + assert(Err == OS_NO_ERR); + UNUSED_ARG(Err); + +} +#endif + +static bool _CheckMacIsValid(u8 *mac) +{ +#define STA_MAC_BUF_LEN 64 + u32 cnt; + u32 sta_num = 0; + u8 *sta_buf; + bool ret = FALSE; + struct tls_sta_info_t *sta; + + sta_buf = tls_mem_alloc(STA_MAC_BUF_LEN); + if (!sta_buf) + return FALSE;/* ϵͳ×ÊÔ´²»×㣬ÎÞÐèÔÙÈÃclient½ÓÈë */ + + memset(sta_buf, 0, STA_MAC_BUF_LEN); + tls_wifi_get_authed_sta_info(&sta_num, sta_buf, STA_MAC_BUF_LEN); + sta = (struct tls_sta_info_t *)sta_buf; + for (cnt = 0; cnt < sta_num; cnt++) + { + if (!compare_ether_addr(mac, sta->mac_addr)) + { + ret = TRUE;/* ±¾SOFTAPϵÄclient²ÅÓèÒÔ·ÖÅäIP */ + break; + } + sta++; + } + tls_mem_free(sta_buf); + + return ret; +} + +static void _DhcpTickHandle(void * Arg) +{ + INT8U i; + PDHCP_CLIENT pClient; + + if(DhcpServer.Enable == 0) + { + return; + } + + for(i = 0; i < DHCPS_HISTORY_CLIENT_NUM; i++) + { + pClient = &DhcpServer.Clients[i]; + if(pClient->State == DHCP_CLIENT_STATE_REQUEST) + { + if(pClient->Timeout && (--pClient->Timeout == 0)) + { + /* Timeout for the client's request frame. */ + memset(pClient->MacAddr, 0, 6); + pClient->State = DHCP_CLIENT_STATE_IDLE; + } + } + else if(pClient->State == DHCP_CLIENT_STATE_BIND) + { +#if 1 + if(pClient->Lease && (--pClient->Lease == 0)) + { + /* The lease time over. */ + pClient->State = DHCP_CLIENT_STATE_IDLE; + // _PostMsgToSysQ(pClient, SYSC_MSG_IP_RELEASE); + } + else if(_CheckMacIsValid(pClient->MacAddr) == 0) + { + /* The client leave the wireless network. */ + pClient->State = DHCP_CLIENT_STATE_IDLE; + // _PostMsgToSysQ(pClient, SYSC_MSG_IP_RELEASE); + } +#endif + } + } + + if(DhcpServer.Enable) sys_timeout(DHCP_TICK_TIME, _DhcpTickHandle, NULL); +} + +static INT8U _ParseDhcpOptions(PDHCP_MSG pMsg, INT8U * pMsgType, INT32U * pReqIpAddr, INT32U * pServerId) +{ + INT8U * pDhcpOptions, * pEnd; + INT32U Len; + INT8U Ret; + + pDhcpOptions = pMsg->Options; + pEnd = (INT8U *)(pMsg) + sizeof(DHCP_MSG); + Len = 0; + Ret = 0; + while((*pDhcpOptions != DHCP_OPTION_ID_END) && (pDhcpOptions < pEnd)) + { + if (*pDhcpOptions == DHCP_OPTION_ID_PAD) + { + /* Skip the pad. */ + pDhcpOptions += 1; + } + else + { + Len = (*(pDhcpOptions + 1)); + if(*pDhcpOptions == DHCP_OPTION_ID_MSG_TYPE) + { + /* Get the message type. */ + pDhcpOptions += 2; + *pMsgType = *pDhcpOptions; + Ret |= 0x01; + } + else if(*pDhcpOptions == DHCP_OPTION_ID_REQ_IP_ADDR) + { + /* Get the client's requested ip address. */ + pDhcpOptions += 2; + MEMCPY((INT8U*)pReqIpAddr, pDhcpOptions,4 ); + Ret |= 0x02; + } + else if(*pDhcpOptions == DHCP_OPTION_ID_SERVER_ID) + { + /* Get the server's ip address. */ + pDhcpOptions += 2; + MEMCPY((INT8U*)pServerId, pDhcpOptions,4 ); + Ret |= 0x04; + } + else + { + /* Dropped the other options. */ + pDhcpOptions += 2; + } + pDhcpOptions += Len; + } + } + return Ret; +} + +static PDHCP_CLIENT _ClientTableLookup(INT8U * MacAddr, INT8U MsgType, INT32U ReqIpAddr, INT32U ServerId) +{ + INT8U i; + INT8U IpUnavailable; + PDHCP_CLIENT pClient; + PDHCP_CLIENT pFreeClient; + PDHCP_CLIENT pReqClient; + PDHCP_CLIENT pMyHistoryClient; + PDHCP_CLIENT pHistoryClient; + PDHCP_CLIENT pMyClient; + PDHCP_CLIENT pReturnClient; + + pFreeClient = NULL; + pReqClient = NULL; + pHistoryClient= NULL; + pMyHistoryClient = NULL; + pMyClient = NULL; + pReturnClient = NULL; + IpUnavailable = 0; + + for(i = 0; i < DHCPS_HISTORY_CLIENT_NUM; i++) + { + pClient = &DhcpServer.Clients[i]; + if(pClient->State == DHCP_CLIENT_STATE_IDLE) + { + if((pMyHistoryClient == NULL) && (memcmp(pClient->MacAddr, MacAddr, 6) == 0)) + { + /* Get my history entry. */ + pMyHistoryClient = pClient; + } + + if((pReqClient == NULL) && ip_addr_cmp(&pClient->IpAddr, (ip_addr_t *)&ReqIpAddr) ) + { + /* Get the idle entry that hold my requested ip address. */ + pReqClient = pClient; + } + + if((pFreeClient == NULL) && (memcmp(pClient->MacAddr, "\x00\x00\x00\x00\x00\x00", 6) == 0) ) + { + /* Get the first free entry. */ + pFreeClient = pClient; + } + + if((pHistoryClient == NULL) && (memcmp(pClient->MacAddr, "\x00\x00\x00\x00\x00\x00", 6) != 0)) + { + /* Get the first histoy entry that not belong to me. */ + pHistoryClient = pClient; + } + } + else + { + if(memcmp(pClient->MacAddr, MacAddr, 6) == 0) + { + /* Is negotiating ip address or has negotiated now. */ + pMyClient = pClient; + } + else if(ip_addr_cmp(&pClient->IpAddr, (ip_addr_t *)&ReqIpAddr)) + { + /* The requested ip address is allocated. */ + IpUnavailable = 1; + } + } + } + + switch(MsgType) + { + case DHCP_MSG_DISCOVER: + if(pMyClient) + { + /* Amazing!!The client restart the negotiation. */ + pMyClient->State = DHCP_CLIENT_STATE_IDLE; + + if(pReqClient) + { + /* The client request another ip address and that address is not allocated now. */ + if(pMyClient->State != DHCP_CLIENT_STATE_BIND) + { + memset(pMyClient->MacAddr, 0, 6); + } + else + { + // _PostMsgToSysQ(pMyClient, SYSC_MSG_IP_RELEASE); + } + pReturnClient = pReqClient; + } + else + { + /* The client request the same ip address. */ + pReturnClient = pMyClient; + } + } + else + { + /* A new negotiation! */ + + /* + The IP address allocation priority(high to low): + 1. the client's request ip address. + 2. the client's history ip address. + 3. a totally free ip address. + 4. the other client's history ip address. + */ + if(pReqClient) + { + /* The client's request ip address. */ + pReturnClient = pReqClient; + } + else if(pMyHistoryClient) + { + /* The client's history ip address. */ + pReturnClient = pMyHistoryClient; + } + else if(pFreeClient) + { + /* A totally free ip address. */ + pReturnClient = pFreeClient; + } + else if(pHistoryClient) + { + /* The other client's history ip address. */ + pReturnClient = pHistoryClient; + } + else + { + /* The IP pool is full, so return null. */ + pReturnClient = NULL; + } + } + break; + + case DHCP_MSG_REQUEST: + if(pMyClient) + { + if(IpUnavailable == 1) + { + /* The client request a new address that was allocated, so return null. */ + pReturnClient = NULL; + + if(pMyClient->State != DHCP_CLIENT_STATE_BIND) + { + memset(pMyClient->MacAddr, 0, 6); + } + pMyClient->State = DHCP_CLIENT_STATE_IDLE; + } + else + { + if(pReqClient) + { + /* The client request a new address that is not allocated. */ + if(pMyClient->State != DHCP_CLIENT_STATE_BIND) + { + memset(pMyClient->MacAddr, 0, 6); + } + else + { + // _PostMsgToSysQ(pMyClient, SYSC_MSG_IP_RELEASE); + } + + pMyClient->State = DHCP_CLIENT_STATE_IDLE; + if((ServerId == 0) || (ip_addr_get_ip4_u32(&DhcpServer.ServerIpAddr) == ServerId)) + { + /* The client request the new address and that is free, allocate it. */ + pReturnClient = pReqClient; + } + else + { + /* The client refuses my offer and request another ip address. */ + pReturnClient = NULL; + } + } + else if((ServerId == 0) || (ip_addr_get_ip4_u32(&DhcpServer.ServerIpAddr) == ServerId)) + { + /* The client request this address that has been allocated to it(a little abnorm) or the client renew the lease time. */ + pReturnClient = pMyClient; + } + else + { + /* The client refuses my offer. */ + pReturnClient = NULL; + } + } + } + else + { + if(IpUnavailable == 1) + { + /* The requested ip address was allocated, so return null. */ + pReturnClient = NULL; + } + else if((ServerId == 0) || (ip_addr_get_ip4_u32(&DhcpServer.ServerIpAddr) == ServerId)) + { + /* The client request my free address, so allocate it. */ + pReturnClient = pReqClient; + } + else + { + /* The client request the address from another server, but send it to me, so return null. */ + pReturnClient = NULL; + } + } + break; + + case DHCP_MSG_RELEASE: + /* The client release the ip address. */ + if(pMyClient) + { + if(pMyClient->State != DHCP_CLIENT_STATE_BIND) + { + memset(pMyClient->MacAddr, 0, 6); + } + else + { + // _PostMsgToSysQ(pClient, SYSC_MSG_IP_RELEASE); + } + pMyClient->State = DHCP_CLIENT_STATE_IDLE; + } + pReturnClient = NULL; + break; + + case DHCP_MSG_DECLINE: + /* The client refuses my offer directly. */ + if(pMyClient) + { + if(pMyClient->State != DHCP_CLIENT_STATE_BIND) + { + memset(pMyClient->MacAddr, 0, 6); + } + else + { + // _PostMsgToSysQ(pClient, SYSC_MSG_IP_RELEASE); + } + pMyClient->State = DHCP_CLIENT_STATE_IDLE; + } + pReturnClient = NULL; + break; + + default: + /* Dropped the other frames. */ + pReturnClient = NULL; + break; + } + + if(pReturnClient) + { + /* Updata the client's MAC address. */ + MEMCPY(pReturnClient->MacAddr, MacAddr, 6); + } + + return pReturnClient; +} + +static void _DHCPNakGenAndSend(INT8U * pClientMacAddr, INT32U Xid) +{ + INT32U Len; + INT8U * Body; + PDHCP_MSG pDhcpMsg; + struct pbuf * pDhcpBuf; + + pDhcpBuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(DHCP_MSG), PBUF_RAM); + if(pDhcpBuf == NULL) + { + return; + } + + pDhcpMsg = &DhcpMsg; + memset(pDhcpMsg, 0, sizeof(*pDhcpMsg)); + + /* Initialize the DHCP message header. */ + pDhcpMsg->Op = DHCP_OP_REPLY; + pDhcpMsg->HType = DHCP_HWTYPE_ETHERNET; + pDhcpMsg->HLen = 6; + pDhcpMsg->Xid = htonl(Xid); + pDhcpMsg->Siaddr = ip_addr_get_ip4_u32(&DhcpServer.ServerIpAddr); + MEMCPY(pDhcpMsg->Chaddr, pClientMacAddr, 6); + pDhcpMsg->Magic = htonl(DHCP_MAGIC); + + Len = 240; + Body = &pDhcpMsg->Options[0]; + + /* Set the message type. */ + DHCP_SET_OPTION_MSG_TYPE(Body, DHCP_MSG_NAK, Len); + + DHCP_SET_OPTION_END(Body, Len); + + pbuf_take(pDhcpBuf, (const void *)pDhcpMsg, Len); + pbuf_realloc(pDhcpBuf, Len); + + /* Send broadcast to the DHCP client. */ + udp_sendto(DhcpServer.Socket, pDhcpBuf, IP_ADDR_BROADCAST, DHCP_CLIENT_PORT); + + TLS_DBGPRT_INFO("sent dhcp nak, ClientMacAddr="MACSTR", ServerIp=%d.%d.%d.%d\n", + MAC2STR(pClientMacAddr), + ip4_addr1(&DhcpServer.ServerIpAddr), ip4_addr2(&DhcpServer.ServerIpAddr), + ip4_addr3(&DhcpServer.ServerIpAddr), ip4_addr4(&DhcpServer.ServerIpAddr)); + pbuf_free(pDhcpBuf); +} + +static void _DHCPAckGenAndSend(PDHCP_CLIENT pClient, INT8U * pClientMacAddr, INT32U Xid, INT16U Flags) +{ + INT32U Len; + INT8U * Body; + PDHCP_MSG pDhcpMsg; + struct pbuf * pDhcpBuf; + + pDhcpBuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(DHCP_MSG), PBUF_RAM); + if(pDhcpBuf == NULL) + { + return; + } + + pDhcpMsg = &DhcpMsg; + memset(pDhcpMsg, 0, sizeof(*pDhcpMsg)); + + /* Initialize the DHCP message header. */ + pDhcpMsg->Op = DHCP_OP_REPLY; + pDhcpMsg->HType = DHCP_HWTYPE_ETHERNET; + pDhcpMsg->HLen = 6; + pDhcpMsg->Xid = htonl(Xid); + pDhcpMsg->Yiaddr = ip_addr_get_ip4_u32(&pClient->IpAddr); + pDhcpMsg->Siaddr = 0; + MEMCPY(pDhcpMsg->Chaddr, pClientMacAddr, 6); + pDhcpMsg->Magic = htonl(DHCP_MAGIC); + + Len = 240; + Body = &pDhcpMsg->Options[0]; + + /* Set the message type. */ + DHCP_SET_OPTION_MSG_TYPE(Body, DHCP_MSG_ACK, Len); + + /* Set the lease time. */ + DHCP_SET_OPTION_LEASE_TIME(Body, DHCP_DEFAULT_LEASE_TIME, Len); + /* Set the server's ip address */ + DHCP_SET_OPTION_SERVER_ID(Body, ip_addr_get_ip4_u32(&DhcpServer.ServerIpAddr), Len); + + /* Set the subnet mask. */ + DHCP_SET_OPTION_SUBNET_MASK(Body, ip_addr_get_ip4_u32(&DhcpServer.SubnetMask), Len); + + /* Set the default gatway's ip address. */ + DHCP_SET_OPTION_GW(Body, ip_addr_get_ip4_u32(&DhcpServer.GateWay), Len); + + /* Set the dns server's ip address. */ + DHCP_SET_OPTION_DNS(Body, ip_addr_get_ip4_u32(&DhcpServer.Dns1), ip_addr_get_ip4_u32(&DhcpServer.Dns2), Len); + DHCP_SET_OPTION_END(Body, Len); + + pbuf_take(pDhcpBuf, (const void *)pDhcpMsg, Len); + pbuf_realloc(pDhcpBuf, Len); + +#ifdef DHCPS_CHECK_BROADCAST_FLAG + if (IS_BROADCAST_SEND(Flags)) + { +#endif + /* Send broadcast to the DHCP client. */ + udp_sendto(DhcpServer.Socket, pDhcpBuf, IP_ADDR_BROADCAST, DHCP_CLIENT_UDP_PORT); +#ifdef DHCPS_CHECK_BROADCAST_FLAG + } + else + { +// etharp_update_arp_entry(tls_get_netif(), (ip_addr_t *)(&pClient->IpAddr), (struct eth_addr *)pClientMacAddr, ETHARP_FLAG_FIND_ONLY); + etharp_update_arp_entry(tls_get_netif(), (ip_addr_t *)(&pClient->IpAddr), (struct eth_addr *)pClientMacAddr, ETHARP_FLAG_STATIC_ENTRY); + udp_sendto(DhcpServer.Socket, pDhcpBuf, (ip_addr_t *)(&pClient->IpAddr), DHCP_CLIENT_UDP_PORT); + } +#endif + + TLS_DBGPRT_INFO("sent dhcp ack, ClientMacAddr="MACSTR", GivenIpAddr=%d.%d.%d.%d, ServerIp=%d.%d.%d.%d\n", + MAC2STR(pClientMacAddr), + ip4_addr1(&pClient->IpAddr), ip4_addr2( &pClient->IpAddr), + ip4_addr3(&pClient->IpAddr), ip4_addr4( &pClient->IpAddr), + ip4_addr1(&DhcpServer.ServerIpAddr), ip4_addr2(&DhcpServer.ServerIpAddr), + ip4_addr3(&DhcpServer.ServerIpAddr), ip4_addr4(&DhcpServer.ServerIpAddr) ); + + pbuf_free(pDhcpBuf); +} + +static void _DHCPOfferGenAndSend(PDHCP_CLIENT pClient, INT8U * pClientMacAddr, INT32U Xid, INT16U Flags) +{ + INT32U Len; + INT8U * Body; + PDHCP_MSG pDhcpMsg; + struct pbuf * pDhcpBuf; + + pDhcpBuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(DHCP_MSG), PBUF_RAM); + if(pDhcpBuf == NULL) + { + return; + } + + pDhcpMsg = &DhcpMsg; + memset(pDhcpMsg, 0, sizeof(*pDhcpMsg)); + + /* Initialize the DHCP message header. */ + pDhcpMsg->Op = DHCP_OP_REPLY; + pDhcpMsg->HType = DHCP_HWTYPE_ETHERNET; + pDhcpMsg->HLen = 6; + pDhcpMsg->Xid = htonl(Xid); + pDhcpMsg->Yiaddr = ip_addr_get_ip4_u32(&pClient->IpAddr); + pDhcpMsg->Siaddr = 0; + MEMCPY(pDhcpMsg->Chaddr, pClientMacAddr, 6); + pDhcpMsg->Magic = htonl(DHCP_MAGIC); + + Len = 240; + Body = &pDhcpMsg->Options[0]; + + /* Set the message type. */ + DHCP_SET_OPTION_MSG_TYPE(Body, DHCP_MSG_OFFER, Len); + + /* Set the lease time. */ + DHCP_SET_OPTION_LEASE_TIME(Body, DHCP_DEFAULT_LEASE_TIME, Len); + /* Set the server's ip address */ + DHCP_SET_OPTION_SERVER_ID(Body, ip_addr_get_ip4_u32(&DhcpServer.ServerIpAddr), Len); + + /* Set the subnet mask. */ + DHCP_SET_OPTION_SUBNET_MASK(Body, ip_addr_get_ip4_u32(&DhcpServer.SubnetMask), Len); + + /* Set the default gatway's ip address. */ + DHCP_SET_OPTION_GW(Body, ip_addr_get_ip4_u32(&DhcpServer.GateWay), Len); + + /* Set the dns server's ip address. */ + DHCP_SET_OPTION_DNS(Body, ip_addr_get_ip4_u32(&DhcpServer.Dns1), ip_addr_get_ip4_u32(&DhcpServer.Dns2), Len); + DHCP_SET_OPTION_END(Body, Len); + + pbuf_take(pDhcpBuf, (const void *)pDhcpMsg, Len); + pbuf_realloc(pDhcpBuf, Len); + +#ifdef DHCPS_CHECK_BROADCAST_FLAG + if (IS_BROADCAST_SEND(Flags)) + { +#endif + /* Send broadcast to the DHCP client. */ + udp_sendto(DhcpServer.Socket, pDhcpBuf, IP_ADDR_BROADCAST, DHCP_CLIENT_UDP_PORT); +#ifdef DHCPS_CHECK_BROADCAST_FLAG + } + else + { +// etharp_update_arp_entry(tls_get_netif(), (ip_addr_t *)(&pClient->IpAddr), (struct eth_addr *)pClientMacAddr, ETHARP_FLAG_FIND_ONLY); + etharp_update_arp_entry(tls_get_netif(), (ip_addr_t *)(&pClient->IpAddr), (struct eth_addr *)pClientMacAddr, ETHARP_FLAG_STATIC_ENTRY); + udp_sendto(DhcpServer.Socket, pDhcpBuf, (ip_addr_t *)(&pClient->IpAddr), DHCP_CLIENT_UDP_PORT); + } +#endif + + TLS_DBGPRT_INFO("sent dhcp offer, ClientMacAddr="MACSTR", GivenIpAddr=%d.%d.%d.%d, ServerIp=%d.%d.%d.%d\n", + MAC2STR(pClientMacAddr), + ip4_addr1(&pClient->IpAddr), ip4_addr2(&pClient->IpAddr), + ip4_addr3(&pClient->IpAddr), ip4_addr4(&pClient->IpAddr), + ip4_addr1( &DhcpServer.ServerIpAddr), ip4_addr2( &DhcpServer.ServerIpAddr), + ip4_addr3( &DhcpServer.ServerIpAddr), ip4_addr4( &DhcpServer.ServerIpAddr) + ); + + pbuf_free(pDhcpBuf); +} + +static void _CleanClientHistory(INT8U * pClientMacAddr) +{ + INT8U i; + PDHCP_CLIENT pClient; + + for(i = 0; i < DHCPS_HISTORY_CLIENT_NUM; i++) + { + pClient = &DhcpServer.Clients[i]; + if((pClient->State == DHCP_CLIENT_STATE_IDLE) && (memcmp(pClient->MacAddr, pClientMacAddr, 6) == 0)) + { + /* Clean the history client's Mac address. */ + memset(pClient->MacAddr, 0, 6); + } + } +} + +static void _DhcpClientSMEHandle(PDHCP_CLIENT pClient, INT8U MsgType, INT32U Xid, INT8U * MacAddr, INT16U Flags) +{ + switch(pClient->State) + { + case DHCP_CLIENT_STATE_IDLE: + if(MsgType == DHCP_MSG_DISCOVER) + { + /* Receive the "DISCOVER" frame, switch the state to "SELECT". */ + pClient->State = DHCP_CLIENT_STATE_SELECT; + } + else if(MsgType == DHCP_MSG_REQUEST) + { + /* If the requested ip is not allocated, allocate it. */ + _DHCPAckGenAndSend(pClient, pClient->MacAddr, Xid, Flags); + pClient->Lease = DHCP_DEFAULT_LEASE_TIME; + pClient->State = DHCP_CLIENT_STATE_BIND; + _CleanClientHistory(pClient->MacAddr); +// _PostMsgToSysQ(pClient, SYSC_MSG_IP_ALLOCATED); + break; + } + + case DHCP_CLIENT_STATE_SELECT: + /* Receive the "DISCOVER" frame, send "OFFER" to the client. */ + _DHCPOfferGenAndSend(pClient, pClient->MacAddr, Xid, Flags); + pClient->Timeout = DHCP_DEFFAULT_TIMEOUT; + pClient->State = DHCP_CLIENT_STATE_REQUEST; + break; + + case DHCP_CLIENT_STATE_REQUEST: +// _PostMsgToSysQ(pClient, SYSC_MSG_IP_ALLOCATED); + case DHCP_CLIENT_STATE_BIND: + /* Send ACK to the client, if receive the "REQUEST" frame to select the offer or renew the DHCP lease. */ + _DHCPAckGenAndSend(pClient, pClient->MacAddr, Xid, Flags); + pClient->Lease = DHCP_DEFAULT_LEASE_TIME; + pClient->State = DHCP_CLIENT_STATE_BIND; + _CleanClientHistory(pClient->MacAddr); + break; + + default: + break; + } +} + +ip_addr_t *DHCPS_GetIpByMac(const INT8U *MacAddr) +{ + INT8U i; + PDHCP_CLIENT pClient; + ip_addr_t *IpAddr = NULL; + + for(i = 0; i < DHCPS_HISTORY_CLIENT_NUM; i++) + { + pClient = &DhcpServer.Clients[i]; + if (0 == compare_ether_addr(MacAddr, pClient->MacAddr)) + { + IpAddr = &pClient->IpAddr; + break; + } + } + + return IpAddr; +} + +INT8U *DHCPS_GetMacByIp(const ip_addr_t *ipaddr) +{ + INT8U i; + PDHCP_CLIENT pClient; + INT8U *macaddr = NULL; + + for(i = 0; i < DHCPS_HISTORY_CLIENT_NUM; i++) + { + pClient = &DhcpServer.Clients[i]; + if (pClient->IpAddr.addr == ipaddr->addr) + { + macaddr = pClient->MacAddr; + break; + } + } + + return macaddr; +} + +/* numdns 0/1 --> dns 1/2 */ +void DHCPS_SetDns(INT8U numdns, INT32U dns) +{ + if (0 == numdns) + ip_addr_set_ip4_u32(&DhcpServer.Dns1, dns); + if (1 == numdns) + ip_addr_set_ip4_u32(&DhcpServer.Dns2, dns); + return; +} + +/*------------------------------------------------------------------------- + Description: + When an incoming DHCP message is to me, this function process it and trigger the state machine. + Arguments: + Arg: Pointer to the user supplied argument. + Pcb: Pointer to the udp_pcb which received data. + P: Pointer to the packet buffer that was received. + Addr: The remote IP address from which the packet was received. + Port: The remote port from which the packet was received . + Return Value: + None. + Note: +-------------------------------------------------------------------------*/ +void DHCPS_RecvCb(void *Arg, struct udp_pcb *Pcb, struct pbuf *P, ip_addr_t *Addr, INT16U Port) +{ +#if 1 + INT16U Flags; + INT32U Xid; + INT8U MsgType; + PDHCP_MSG pDhcpMsg; + INT32U MsgLen; + + ip_addr_t ReqIpAddr; + ip_addr_t ServerId; + + INT8U ClientMacAddr[6]; + PDHCP_CLIENT pClient; + INT8U* MacAddr; + + do + { + pDhcpMsg = &DhcpMsg; + memset(pDhcpMsg, 0, sizeof(DHCP_MSG)); + + /* Copy the DHCP message. */ + MsgLen = pbuf_copy_partial(P, (void *)pDhcpMsg, sizeof(DHCP_MSG), 0); + + /* Filter out the frame that is not request frame or has wrong magic number or has wrong hardware address type. */ + if((MsgLen == 0) || + (pDhcpMsg->Op != DHCP_OP_REQUEST) || + (ntohl(pDhcpMsg->Magic) != DHCP_MAGIC) || + (pDhcpMsg->HType != DHCP_HWTYPE_ETHERNET)) + { + break; + } + + + MacAddr = wpa_supplicant_get_mac(); + if (0 == memcmp(MacAddr, pDhcpMsg->Chaddr, 6)) + { + TLS_DBGPRT_INFO("drop form ours dhcp packet\n"); + break; + } + + if (!_CheckMacIsValid(pDhcpMsg->Chaddr)) + { + TLS_DBGPRT_INFO("drop form router's dhcp packet\n"); + break; + } + + /* Parse the packet to get message type, ip address requested by client and server ID. */ + MsgType = 0xff; + memset(&ReqIpAddr, 0, sizeof(ip_addr_t)); + memset(&ServerId, 0, sizeof(ip_addr_t)); + if((_ParseDhcpOptions(pDhcpMsg, &MsgType, &ip4_addr_get_u32(&ReqIpAddr), &ip4_addr_get_u32(&ServerId)) & 0x01) == 0) + { + break; + } + + if (DHCP_MSG_INFORM == MsgType) + { + TLS_DBGPRT_INFO("drop dhcp inform packet\n"); + break; + } + + /* Get the Xid and client's MAC address. */ + Xid = ntohl(pDhcpMsg->Xid); + Flags = ntohs(pDhcpMsg->Flags); + MEMCPY(ClientMacAddr, pDhcpMsg->Chaddr, 6); + TLS_DBGPRT_INFO("ClientMacAddr=%x, MsgType=%x, ReqIpAddr=%x, ServerId=%x\n", ClientMacAddr, MsgType, ReqIpAddr, ServerId); + + TLS_DBGPRT_INFO("ClientMacAddr="MACSTR", MsgType=%x, ReqIpAddr=%d.%d.%d.%d, ServerIp=%d.%d.%d.%d\n", + MAC2STR(ClientMacAddr), MsgType, + ip4_addr1(&ReqIpAddr), ip4_addr2(&ReqIpAddr), ip4_addr3(&ReqIpAddr), ip4_addr4(&ReqIpAddr), + ip4_addr1(&ServerId), ip4_addr2(&ServerId), ip4_addr3(&ServerId), ip4_addr4(&ServerId)); + + /* Get the client entry that is free or negotiating. */ + pClient = _ClientTableLookup(ClientMacAddr, MsgType, ip4_addr_get_u32(&ReqIpAddr), ip4_addr_get_u32(&ServerId)); + if(pClient == NULL) + { + if((MsgType != DHCP_MSG_RELEASE) && (MsgType != DHCP_MSG_DECLINE)) + { + /* Ip is already allocated, so send nack. */ + _DHCPNakGenAndSend(ClientMacAddr, Xid); + } + break; + } + + /* Push to client state machine. */ + _DhcpClientSMEHandle(pClient, MsgType, Xid, ClientMacAddr, Flags); + }while(0); + + pbuf_free(P); +#endif +} + +/*------------------------------------------------------------------------- + Description: + This function is used to delete the dhcp client entry on the server. + Arguments: + MacAddr: Specify the MAC Address of the dhcp client entry to be delete. + Return Value: + The DHCP Server error code: + DHCPS_ERR_SUCCESS - No error + DHCPS_ERR_INACTIVE - The Server is inactive + DHCPS_ERR_PARAM - The input parameter error + DHCPS_ERR_NOT_FOUND - Not fount this client + Note: +-------------------------------------------------------------------------*/ +INT8S DHCPS_ClientDelete(INT8U * MacAddr) +{ + INT8U i; + PDHCP_CLIENT pClient; + + /* Check the server is active now. */ + if(DhcpServer.Enable == 0) + { + return DHCPS_ERR_INACTIVE; + } + + if(MacAddr == NULL) + { + return DHCPS_ERR_PARAM; + } + + for(i = 0; i < DHCPS_HISTORY_CLIENT_NUM; i++) + { + pClient = &DhcpServer.Clients[i]; + if((pClient->State != DHCP_CLIENT_STATE_IDLE) && (memcmp(pClient->MacAddr, MacAddr, 6) == 0)) + { + if(pClient->State != DHCP_CLIENT_STATE_BIND) + { + /* For negotiating client, return. */ + return DHCPS_ERR_NOT_BIND; + } + else + { + /* For bind client, delete it directly. */ + pClient->State = DHCP_CLIENT_STATE_IDLE; + return DHCPS_ERR_SUCCESS; + } + } + } + + return DHCPS_ERR_NOT_FOUND; +} + +/*------------------------------------------------------------------------- + Description: + This function is used to start DHCP Server for a network interface. + Arguments: + Netif: Pointer to the Lwip network interface. + Return Value: + The DHCP Server error code: + DHCPS_ERR_SUCCESS - No error + DHCPS_ERR_MEM - Out of memory + DHCPS_ERR_LINKDOWN - The NI is inactive + Note: + The dhcp server must be started after the network interface was actived. +-------------------------------------------------------------------------*/ +INT8S DHCPS_Start(struct netif *Netif) +{ + INT32U Val, Mask, tmp, i; + PDHCP_CLIENT pClient; + + /* Check the network interface is active now. */ + if(netif_is_up(Netif) == 0) + { + return DHCPS_ERR_LINKDOWN; + } + memset(&DhcpServer, 0, sizeof(DhcpServer)); + + /* Calculate the start ip address of the server's ip pool. */ + Val = ntohl(ip_addr_get_ip4_u32(&Netif->ip_addr)); + Mask = ntohl(ip_addr_get_ip4_u32(&Netif->netmask)); + tmp = (Val & (~Mask)); + tmp = ((tmp + 1) % (~Mask)) ? ((tmp + 1) % (~Mask)) : 1; + Val = htonl((Val & Mask) | tmp); + + /* Configure the DHCP Server. */ + ip_addr_set(&DhcpServer.ServerIpAddr, &Netif->ip_addr); + ip_addr_set(&DhcpServer.StartIpAddr, (ip_addr_t *)&Val); + ip_addr_set(&DhcpServer.SubnetMask, &Netif->netmask); + ip_addr_set(&DhcpServer.GateWay, &Netif->ip_addr); + ip_addr_set(&DhcpServer.Dns1, &Netif->ip_addr); + + /* Set the default lease time - 2 hours. */ + DhcpServer.LeaseTime = DHCP_DEFAULT_LEASE_TIME; + + /* Initialize the free DHCP clients. */ + for(i = 0; i < DHCPS_HISTORY_CLIENT_NUM; i++) + { + pClient = &DhcpServer.Clients[i]; + /* Set the initial client state is "IDLE". */ + pClient->State = DHCP_CLIENT_STATE_IDLE; + + /* Set the ip address to the client. */ + Val = ntohl(ip_addr_get_ip4_u32(&DhcpServer.StartIpAddr)); + tmp = (Val & (~Mask)); + tmp = ((tmp + i) % (~Mask)) ? ((tmp + i) % (~Mask)) : 1; + Val = htonl((Val & Mask) | tmp); + ip_addr_set(&pClient->IpAddr, (ip_addr_t *)&Val); + + /* Set the default lease time. */ + pClient->Lease = DHCP_DEFAULT_LEASE_TIME; + } + + /* Allocate a UDP PCB. */ + DhcpServer.Socket = udp_new(); + if(DhcpServer.Socket == NULL) + { + return DHCPS_ERR_MEM; + } + + /* Set up local and remote port for the pcb. */ + udp_bind(DhcpServer.Socket, IP_ADDR_ANY, DHCP_SERVER_UDP_PORT); + + /* bind multicast&broadcast netif */ + udp_bind_multicast_netif(DhcpServer.Socket, &Netif->ip_addr); + + /* Set up the recv callback and argument. */ + udp_recv(DhcpServer.Socket, (udp_recv_fn)DHCPS_RecvCb, Netif); + + /* Start the DHCP Server tick timer. */ + sys_timeout(DHCP_TICK_TIME, _DhcpTickHandle, NULL); + + /* Enable the DHCP Server. */ + DhcpServer.Enable = 1; + + return DHCPS_ERR_SUCCESS; +} + +/*------------------------------------------------------------------------- + Description: + This function is used to disable dhcp server. + Arguments: + None. + Return Value: + None. + Note: +-------------------------------------------------------------------------*/ +void DHCPS_Stop(void) +{ + /* Disable the dhcp server's service. */ + DhcpServer.Enable = 0; + + /* Stop the tick timer. */ + sys_untimeout(_DhcpTickHandle, NULL); + + /* Release the socket. */ + if(DhcpServer.Socket) + { + udp_remove(DhcpServer.Socket); + } + memset(&DhcpServer, 0, sizeof(DhcpServer)); +} +#endif + diff --git a/sdk/Src/App/dhcpserver/dhcp_server.h b/sdk/Src/App/dhcpserver/dhcp_server.h new file mode 100644 index 0000000..54f205d --- /dev/null +++ b/sdk/Src/App/dhcpserver/dhcp_server.h @@ -0,0 +1,163 @@ +/************************************************************************** + * File Name : dhcp_server.h + * Author : + * Version : + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ +#ifndef __DHCP_SERVER_H_175137__ +#define __DHCP_SERVER_H_175137__ + +#include "wm_config.h" + +#if TLS_CONFIG_AP +#include "wm_sockets.h" + +#define DHCPS_ERR_SUCCESS 0 +#define DHCPS_ERR_LINKDOWN -1 +#define DHCPS_ERR_PARAM -2 +#define DHCPS_ERR_MEM -3 +#define DHCPS_ERR_NOT_BIND -4 +#define DHCPS_ERR_NOT_FOUND -5 +#define DHCPS_ERR_INACTIVE -6 + +#define DHCPS_HISTORY_CLIENT_NUM 8 + +#define DHCP_DEFAULT_LEASE_TIME 7200 /* 2 Hours. */ +#define DHCP_DEFAULT_LEASE_TIME_MS 7200000 /* 7200000ms */ +#define DHCP_TICK_TIME 1000 /* 1s. */ +#define DHCP_DEFFAULT_TIMEOUT 10 /* 500ms */ + +#define DHCP_HWTYPE_ETHERNET 1 + +/* udp port number of dhcp. */ +#define DHCP_CLIENT_UDP_PORT 68 +#define DHCP_SERVER_UDP_PORT 67 + +/* Magic number in DHCP packet. */ +#define DHCP_MAGIC 0x63825363 + +#define DHCP_OP_REQUEST 1 +#define DHCP_OP_REPLY 2 + +#define DHCP_OPTION_ID_PAD 0 +#define DHCP_OPTION_ID_SUBNET_MASK 1 +#define DHCP_OPTION_ID_DEF_GW 3 +#define DHCP_OPTION_ID_DNS_SERVER 6 +#define DHCP_OPTION_ID_LEASE_TIME 51 +#define DHCP_OPTION_ID_MSG_TYPE 53 +#define DHCP_OPTION_ID_SERVER_ID 54 +#define DHCP_OPTION_ID_REQ_IP_ADDR 50 +#define DHCP_OPTION_ID_END 255 + +#define DHCP_MSG_DISCOVER 1 +#define DHCP_MSG_OFFER 2 +#define DHCP_MSG_REQUEST 3 +#define DHCP_MSG_DECLINE 4 +#define DHCP_MSG_ACK 5 +#define DHCP_MSG_NAK 6 +#define DHCP_MSG_RELEASE 7 +#define DHCP_MSG_INFORM 8 + +#ifdef INT8U +#undef INT8U +#endif +typedef unsigned char INT8U; + +#ifdef INT8S +#undef INT8S +#endif +typedef signed char INT8S; + +#ifdef INT16U +#undef INT16U +#endif +typedef unsigned short INT16U; + +#ifdef INT32U +#undef INT32U +#endif +typedef unsigned int INT32U; + +typedef enum __DHCP_CLIENT_STATE +{ + DHCP_CLIENT_STATE_IDLE=0, + DHCP_CLIENT_STATE_SELECT, + DHCP_CLIENT_STATE_REQUEST, + DHCP_CLIENT_STATE_BIND +}DHCP_CLIENT_STATE; + +typedef struct __DHCP_CLIENT +{ + DHCP_CLIENT_STATE State; + INT32U Timeout; + /* Attention!!! MUST BE __align(4) */ + ip_addr_t IpAddr; + INT8U MacAddr[6]; + INT32U Lease; +}DHCP_CLIENT, *PDHCP_CLIENT; + +typedef struct __DHCP_SERVER +{ + INT8U Enable; + struct udp_pcb * Socket; + /* Attention!!! MUST BE __align(4) */ + ip_addr_t ServerIpAddr; + ip_addr_t StartIpAddr; + ip_addr_t SubnetMask; + ip_addr_t GateWay; + ip_addr_t Dns1; + ip_addr_t Dns2; + INT32U LeaseTime; + DHCP_CLIENT Clients[DHCPS_HISTORY_CLIENT_NUM]; +}DHCP_SERVER, *PDHCP_SERVER; + +#define DHCPS_HADDR_SIZE 16 +#define DHCPS_SNAME_SIZE 64 +#define DHCPS_FILENAME_SIZE 128 +#define DHCPS_OPTIONS_LEN 312 +typedef struct __DHCP_MSG +{ + INT8U Op; + INT8U HType; + INT8U HLen; + INT8U HOps; + + INT32U Xid; + + INT16U Secs; + INT16U Flags; + + INT32U Ciaddr; + + INT32U Yiaddr; + + INT32U Siaddr; + + INT32U Giaddr; + + INT8U Chaddr[DHCPS_HADDR_SIZE]; + + INT8U Sname[DHCPS_SNAME_SIZE]; + + INT8U File[DHCPS_FILENAME_SIZE]; + + INT32U Magic; + + INT8U Options[DHCPS_OPTIONS_LEN - 4]; +}DHCP_MSG, *PDHCP_MSG; + +INT8S DHCPS_Start(struct netif *Netif); +void DHCPS_Stop(void); +INT8S DHCPS_ClientDelete(INT8U * MacAddr); +void DHCPS_RecvCb(void *Arg, struct udp_pcb *Pcb, struct pbuf *P, ip_addr_t *Addr, INT16U Port); +void DHCPS_SetDns(INT8U numdns, INT32U dns); +ip_addr_t *DHCPS_GetIpByMac(const INT8U *mac_addr); +INT8U *DHCPS_GetMacByIp(const ip_addr_t *ipaddr); +#endif +#endif /* __DHCP_SERVER_H_175137__ */ + diff --git a/sdk/Src/App/dnsserver/CMakeLists.txt b/sdk/Src/App/dnsserver/CMakeLists.txt new file mode 100644 index 0000000..af16b9c --- /dev/null +++ b/sdk/Src/App/dnsserver/CMakeLists.txt @@ -0,0 +1,42 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WMDNSSERVER) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin ") +STRING(APPEND CMAKE_C_FLAGS "-DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/dns_server.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmdnsserver.a) \ No newline at end of file diff --git a/sdk/Src/App/dnsserver/dns_server.c b/sdk/Src/App/dnsserver/dns_server.c new file mode 100644 index 0000000..63e8a86 --- /dev/null +++ b/sdk/Src/App/dnsserver/dns_server.c @@ -0,0 +1,390 @@ +/************************************************************************** + * File Name : dns_server.c + * Author : + * Version : + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ +#ifndef WM_W600 +#include +#else +#ifndef assert +#define assert(n) +#endif +#endif +#include +#include +#include + +#include "tls_common.h" +#include "wm_mem.h" +#include "wm_debug.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/memp.h" +#include "lwip/udp.h" + +#include "netif/ethernetif.h" + +#include "dns_server.h" + +#include "wm_wifi_oneshot.h" + +#if TLS_CONFIG_AP +static DNS_SERVER DnsServer; + +static INT32S _DnsCompareName(INT8U * MyDns, INT8U * Query) +{ + INT8U n; + + if((strlen((const char *)Query) - 1) > 32) + { + return 1; + } + + do + { + n = *Query++; + /* @see RFC 1035 - 4.1.4. Message compression. */ + if((n & 0xc0) == 0xc0) + { + /* Compressed name. */ + break; + } + else + { + /* Not compressed name. */ + while(n > 0) + { + if((*MyDns) != (*Query)) + { + return 1; + } + ++Query; + ++MyDns; + --n; + }; + ++MyDns; + } + } while (*Query != 0); + + return ((*(--MyDns) == 0) ? 0 : 1); +} + +static void _DNSNameErrGenAndSend(ip_addr_t *Addr, INT16U Port, PDNS_QUERY pDnsQuery, INT8U * QueryName, INT16U TansactionId) +{ + INT32U Len; + INT8U * Body; + PDNS_HEADER pDnsHeader; + struct pbuf * pDnsBuf; + INT8U * pDnsReply; + + Len = ((sizeof(DNS_HEADER) + (strlen((const char *)QueryName) + 1) + sizeof(DNS_QUERY) + 3) >> 2) << 2; + pDnsReply = tls_mem_alloc(Len); + if(pDnsReply == NULL) + { + return; + } + + pDnsHeader = (PDNS_HEADER)pDnsReply; + Body = (INT8U *)(pDnsHeader + 1); + Len = 0; + + /* Header. */ + pDnsHeader->TansactionId = TansactionId; + pDnsHeader->DnsFlag1 = DNS_FLAG1_RESPONSE; + pDnsHeader->DnsFlag2 = DNS_FLAG2_ERR_NAME; + pDnsHeader->Quentions = htons(1); + pDnsHeader->AnswerRR = 0; + pDnsHeader->AuthorityRR = 0; + pDnsHeader->AdditionalRR = 0; + Len += sizeof(DNS_HEADER); + + /* Querry. */ + MEMCPY(Body, QueryName, strlen((const char *)QueryName) + 1); + Body += strlen((const char *)QueryName) + 1; + Len += strlen((const char *)QueryName) + 1; + MEMCPY(Body, pDnsQuery, sizeof(DNS_QUERY)); + Len += sizeof(DNS_QUERY); + + pDnsBuf = pbuf_alloc(PBUF_TRANSPORT, Len, PBUF_RAM); + if(pDnsBuf == NULL) + { + tls_mem_free(pDnsReply); + return; + } + pbuf_take(pDnsBuf, (const void *) pDnsReply, Len); + + /* Send to the client. */ + udp_sendto(DnsServer.Socket, pDnsBuf, Addr, Port); + pbuf_free(pDnsBuf); + tls_mem_free(pDnsReply); +} + +static void _DNSAnswerGenAndSend(ip_addr_t *Addr, INT16U Port, PDNS_QUERY pDnsQuery, INT8U * QueryName, INT16U TansactionId) +{ + INT32U Len; + INT8U * Body; + INT32U ServerIpAddr; + PDNS_HEADER pDnsHeader; + DNS_ANSWER DnsAnswer; + struct pbuf * pDnsBuf; + INT8U * pDnsReply; + INT16U Tmp; + + Len = ((sizeof(DNS_HEADER) + (strlen((const char *)QueryName) + 1) + sizeof(DNS_QUERY) + 2 + sizeof(DNS_ANSWER) + 2 + 4 + 3) >> 2) << 2; + pDnsReply = tls_mem_alloc(Len); + if(pDnsReply == NULL) + { + return; + } + + pDnsHeader = (PDNS_HEADER)pDnsReply; + Body = (INT8U *)(pDnsHeader + 1); + Len = 0; + + /* Header. */ + pDnsHeader->TansactionId = TansactionId; + pDnsHeader->DnsFlag1 = DNS_FLAG1_RESPONSE; + pDnsHeader->DnsFlag2 = DNS_FLAG2_ERR_NONE; + pDnsHeader->Quentions = htons(1); + pDnsHeader->AnswerRR = htons(1); + pDnsHeader->AuthorityRR = 0; + pDnsHeader->AdditionalRR = 0; + Len += sizeof(DNS_HEADER); + + /* Querry. */ + MEMCPY(Body, QueryName, strlen((const char *)QueryName) + 1); + Body += strlen((const char *)QueryName) + 1; + Len += strlen((const char *)QueryName) + 1; + MEMCPY(Body, pDnsQuery, sizeof(DNS_QUERY)); + Body += sizeof(DNS_QUERY); + Len += sizeof(DNS_QUERY); + + /* NAME: provided as offset to first occurence in response. */ + Tmp = DNS_NAME_OFFSET | sizeof(DNS_HEADER); + Tmp = htons(Tmp); + MEMCPY(Body, &Tmp, sizeof(INT16U)); + Body += sizeof(INT16U); + Len += sizeof(INT16U); + + /* Answer. */ + DnsAnswer.Type = htons(DNS_RRTYPE_A); + DnsAnswer.Class = htons(DNS_RRCLASS_IN); + DnsAnswer.Ttl = htonl(DNS_DEFAULT_TTL); + MEMCPY(Body, &DnsAnswer, sizeof(DNS_ANSWER)); + Body += sizeof(DNS_ANSWER); + Len += sizeof(DNS_ANSWER); + + /* Length. */ + Tmp = htons(4); + MEMCPY(Body, &Tmp, sizeof(INT16U)); + Body += sizeof(INT16U); + Len += sizeof(INT16U); + + /* IP Address. */ + ServerIpAddr = ip_addr_get_ip4_u32(&DnsServer.HostIp); + MEMCPY(Body, &ServerIpAddr, 4); + Body += 4; + Len += 4; + + pDnsBuf = pbuf_alloc(PBUF_TRANSPORT, Len, PBUF_RAM); + if(pDnsBuf == NULL) + { + tls_mem_free(pDnsReply); + return; + } + pbuf_take(pDnsBuf, (const void *) pDnsReply, Len); + + /* Send to the client. */ + udp_sendto(DnsServer.Socket, pDnsBuf, Addr, Port); + pbuf_free(pDnsBuf); + tls_mem_free(pDnsReply); +} + +/* DNSS_RecvCb */ +/*------------------------------------------------------------------------- + Description: + When an incoming DNS message is to me, this function process it and trigger the state machine. + + Arguments: + Arg: Pointer to the user supplied argument. + Pcb: Pointer to the udp_pcb which received data. + P: Pointer to the packet buffer that was received. + Addr: The remote IP address from which the packet was received. + Port: The remote port from which the packet was received . + + Return Value: + None. + + Note: + +-------------------------------------------------------------------------*/ +void DNSS_RecvCb(void *Arg, struct udp_pcb *Pcb, struct pbuf *P, ip_addr_t *Addr, INT16U Port) +{ + PDNS_HEADER pDnsHeader; + DNS_QUERY DnsQuery; + //INT16U nQuestions, nAnswers; + INT8U * pDnsName; + INT8U * pDnsMsg; + + do + { + pDnsMsg = tls_mem_alloc(P->tot_len); + if(pDnsMsg == NULL) + { + break; + } + pbuf_copy_partial(P, pDnsMsg, P->tot_len, 0); + + pDnsHeader = (PDNS_HEADER)pDnsMsg; + + /* Get the quention number and answer number. */ + //nQuestions = ntohs(pDnsHeader->Quentions); + //nAnswers = ntohs(pDnsHeader->AnswerRR); + + /* Filter out the response frame and the unstandard query frame. */ + if((pDnsHeader->DnsFlag1 & DNS_FLAG1_RESPONSE) || ((pDnsHeader->DnsFlag1 & (0xf << 3)) != DNS_FLAG1_OPCODE_STANDARD)) + { + break; + } + + /* Locate the dns name. */ + pDnsName = (INT8U *)(pDnsHeader + 1); + + /* Get the query class and type. */ + MEMCPY(&DnsQuery, (pDnsName + strlen((const char *)pDnsName) + 1), sizeof(DnsQuery)); + + /* Check the query class and type. */ + if((DnsQuery.Class != htons(DNS_RRCLASS_IN)) && (DnsQuery.Type != htons(DNS_RRTYPE_A))) + { + break; + } + + if ((_DnsCompareName(DnsServer.DnsName, pDnsName) != 0) && + !((1 == 0 /*tls_wifi_get_oneshot_flag()*/) && + (2 == 0 /*tls_wifi_get_oneshot_config_mode()*/))) + { + /* Not my dns name, so notify the client name error. */ +#if TLS_CONFIG_AP + struct netif *netif = tls_get_netif(); + if (!netif_is_up(netif)) +#endif + _DNSNameErrGenAndSend(Addr, Port, &DnsQuery, pDnsName, pDnsHeader->TansactionId); + } + else + { + /* My dns name, so send the answer to the client. */ + _DNSAnswerGenAndSend(Addr, Port, &DnsQuery, pDnsName, pDnsHeader->TansactionId); + } + }while(0); + + if(pDnsMsg) + { + tls_mem_free(pDnsMsg); + } + pbuf_free(P); +} + +/* DNSS_Config */ +/*------------------------------------------------------------------------- + Description: + This function is used to updata the server's dns name. + Arguments: + DnsName : Pointer the server's dns name. + + Return Value: + The DNS Server error code: + DNSS_ERR_SUCCESS - No error + DNSS_ERR_PARAM - Input parameter error + Note: + The length of the DNS name must be less than 32. +-------------------------------------------------------------------------*/ +INT8S DNSS_Config(INT8U * DnsName) +{ + if((DnsName == NULL) || (strlen((const char *)DnsName) > 32)) + { + /* The length of the DNS name must be less than 32. */ + return DNSS_ERR_PARAM; + } + + memset(DnsServer.DnsName, 0, 32); + MEMCPY(DnsServer.DnsName, DnsName, strlen((const char *)DnsName)); + + return DNSS_ERR_SUCCESS; +} + +/* DNSS_Start */ +/*------------------------------------------------------------------------- + Description: + This function is used to start the dns server's service. + Arguments: + DnsName : Specify the server's dns name + Netif: Pointer to the Lwip network interface. + + Return Value: + The DNS Server error code: + DNSS_ERR_SUCCESS - No error + DNSS_ERR_PARAM - Input parameter error + DNSS_ERR_MEM - Out of memory + DNSS_ERR_LINKDOWN - The NI is inactive + Note: + The dns server must be started after the network interface was actived. +-------------------------------------------------------------------------*/ +INT8S DNSS_Start(struct netif *Netif, INT8U * DnsName) +{ + if((Netif == NULL) || (strlen((const char *)DnsName) > 32)) + { + return DNSS_ERR_PARAM; + } + + if(netif_is_up(Netif) == 0) + { + return DNSS_ERR_LINKDOWN; + } + + memset(&DnsServer, 0, sizeof(DnsServer)); + + MEMCPY(DnsServer.DnsName, DnsName, strlen((const char *)DnsName)); + ip_addr_set(&DnsServer.HostIp, &Netif->ip_addr); + + DnsServer.Socket = udp_new(); + if(DnsServer.Socket == NULL) + { + return DNSS_ERR_MEM; + } + + /* Set up local and remote port for the pcb. */ + udp_bind(DnsServer.Socket, IP_ADDR_ANY, DNS_SERVER_PORT); + + /* Set up the recv callback and argument. */ + udp_recv(DnsServer.Socket, (udp_recv_fn)DNSS_RecvCb, Netif); + + return DNSS_ERR_SUCCESS; +} + +/* DNSS_Stop */ +/*------------------------------------------------------------------------- + Description: + This function is used to stop the dns server's service. + Arguments: + None. + + Return Value: + None. + + Note: + +-------------------------------------------------------------------------*/ +void DNSS_Stop(void) +{ + if(DnsServer.Socket) udp_remove(DnsServer.Socket); +} +#endif + diff --git a/sdk/Src/App/dnsserver/dns_server.h b/sdk/Src/App/dnsserver/dns_server.h new file mode 100644 index 0000000..4f6955e --- /dev/null +++ b/sdk/Src/App/dnsserver/dns_server.h @@ -0,0 +1,128 @@ +/************************************************************************** + * File Name : dns_server.h + * Author : + * Version : + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ +#ifndef __DNS_SERVER_H_175233__ +#define __DNS_SERVER_H_175233__ + +#if TLS_CONFIG_AP +#define DNSS_ERR_SUCCESS 0 +#define DNSS_ERR_LINKDOWN 1 +#define DNSS_ERR_PARAM 2 +#define DNSS_ERR_MEM 3 + +#define DNS_DEFAULT_TTL 3600 /* a day. */ + +#define DNS_SERVER_PORT 53 + +/* DNS field TYPE used for "Resource Records". */ +#define DNS_RRTYPE_A 1 /* a host address. */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server. */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX). */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias. */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority. */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL). */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL). */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL). */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL). */ +#define DNS_RRTYPE_WKS 11 /* a well known service description. */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer. */ +#define DNS_RRTYPE_HINFO 13 /* host information. */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information. */ +#define DNS_RRTYPE_MX 15 /* mail exchange. */ +#define DNS_RRTYPE_TXT 16 /* text strings. */ + +/* DNS field CLASS used for "Resource Records". */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs).*/ +#define DNS_RRCLASS_CH 3 /* the CHAOS class. */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87].*/ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit. */ + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x80 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +#define DNS_NAME_OFFSET 0xC000 + +#ifdef INT8U +#undef INT8U +#endif +typedef unsigned char INT8U; + +#ifdef INT8S +#undef INT8S +#endif +typedef signed char INT8S; + +#ifdef INT16U +#undef INT16U +#endif +typedef unsigned short INT16U; + +#ifdef INT32U +#undef INT32U +#endif +typedef unsigned int INT32U; + +#ifdef INT32S +#undef INT32S +#endif +typedef signed int INT32S; + +typedef struct __DNS_SERVER +{ + struct udp_pcb * Socket; + INT8U DnsName[32]; + /* Attention!!! MUST BE __align(4) */ + ip_addr_t HostIp; +}DNS_SERVER; + +typedef struct __DNS_HEADER +{ + INT16U TansactionId; + INT8U DnsFlag1; + INT8U DnsFlag2; + INT16U Quentions; + INT16U AnswerRR; + INT16U AuthorityRR; + INT16U AdditionalRR; +}DNS_HEADER, *PDNS_HEADER; + +typedef struct _DNS_QUERY +{ + INT16U Type; + INT16U Class; +}DNS_QUERY, *PDNS_QUERY; + +typedef struct __DNS_ANSWER +{ + INT16U Type; + INT16U Class; + INT32U Ttl; +}DNS_ANSWER, *PDNS_ANSWER; + +void DNSS_RecvCb(void *Arg, struct udp_pcb *Pcb, struct pbuf *P, ip_addr_t *Addr, INT16U Port); +INT8S DNSS_Config(INT8U * DnsName); +INT8S DNSS_Start(struct netif *Netif, INT8U * DnsName); +void DNSS_Stop(void); +#endif +#endif /* __DNS_SERVER_H_175233__ */ + diff --git a/sdk/Src/App/httpclient/CMakeLists.txt b/sdk/Src/App/httpclient/CMakeLists.txt new file mode 100644 index 0000000..1417d4b --- /dev/null +++ b/sdk/Src/App/httpclient/CMakeLists.txt @@ -0,0 +1,46 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WMHTTPCLIENT) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin ") +STRING(APPEND CMAKE_C_FLAGS "-DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/HTTPClient.c" + "${CMAKE_CURRENT_SOURCE_DIR}/HTTPClientAuth.c" + "${CMAKE_CURRENT_SOURCE_DIR}/HTTPClientString.c" + "${CMAKE_CURRENT_SOURCE_DIR}/HTTPClientWrapper.c" + "${CMAKE_CURRENT_SOURCE_DIR}/wm_httpclient_task.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmhttpclient.a) \ No newline at end of file diff --git a/sdk/Src/App/httpclient/HTTPClient.c b/sdk/Src/App/httpclient/HTTPClient.c new file mode 100644 index 0000000..b5f82bd --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClient.c @@ -0,0 +1,3790 @@ + +/////////////////////////////////////////////////////////////////////////////// +// +// Module Name: +// HTTPClient.c +// +// Abstract: Partially Implements the client side of the HTTP 1.1 Protocol as +// Defined in RFC 2616,2617 +// Platform: Any that supports standard C calls and Berkeley sockets +// +// Author: Eitan Michaelson +// Version: 1.0 +// Date 7/1/2006 +// Opens: HTTP Proxy authentication support (not fully tested). +// HTTP Keep Alive not impliamented +// HTTPS not implemented +// The HTTP counters are not 100% correct (will be fixed) +// +/////////////////////////////////////////////////////////////////////////////// + +#include "HTTPClient.h" +#include "HTTPClientAuth.h" // Crypto support (Digest, MD5) +#include "HTTPClientString.h" // String utilities +#include "wm_mem.h" +#include "wm_debug.h" + +#if TLS_CONFIG_HTTP_CLIENT +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientSetLocalConnection +// Purpose : TBD +// Gets : +// Returns : +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientSetLocalConnection (HTTP_SESSION_HANDLE pSession, UINT32 nPort) +{ + + return 0; +} +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientOpenRequest +// Purpose : Allocate memory for a new HTTP Session +// Gets : FLAGS +// Returns : HTTP Session handle +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +HTTP_SESSION_HANDLE HTTPClientOpenRequest (HTTP_CLIENT_SESSION_FLAGS Flags) +{ + + + P_HTTP_SESSION pHTTPSession = NULL; // Handle to the session pointer + UINT32 nAllocationSize; // Size of the dynamically allocated buffer + + // Attempt to allocate the buffer + pHTTPSession = (P_HTTP_SESSION)tls_mem_alloc(ALIGN(sizeof(HTTP_SESSION))); + + // Did we succeed? + if(!pHTTPSession) + { + // Null pointer is returned upon error + return 0; + } + // Reset the allocated space to zeros + memset(pHTTPSession,0x00,sizeof(HTTP_SESSION)); + + // Allocate space for the incoming and outgoing headers + // Check if the headers buffer is resizable + if(HTTP_CLIENT_MEMORY_RESIZABLE) + { + // Memory is resizable, so use the init defined size or the maximum buffer size (which ever is smaller) + nAllocationSize = MIN(HTTP_CLIENT_MAX_SEND_RECV_HEADERS,HTTP_CLIENT_INIT_SEND_RECV_HEADERS); + + } + else + { + // Memory is not resizable so simply use the maximum defined size + nAllocationSize = HTTP_CLIENT_MAX_SEND_RECV_HEADERS; + } + nAllocationSize = ALIGN(nAllocationSize); + // Allocate the headers buffer + pHTTPSession->HttpHeaders.HeadersBuffer.pParam = (CHAR*)tls_mem_alloc(nAllocationSize); + // Check the returned pointer + if(!pHTTPSession->HttpHeaders.HeadersBuffer.pParam) + { + // tls_mem_alloc() error, free the containing structure and exit. + tls_mem_free(pHTTPSession); + return 0; + + } + + // Reset the headers allocated memory + memset(pHTTPSession->HttpHeaders.HeadersBuffer.pParam ,0x00,nAllocationSize); + // Set the buffer length + pHTTPSession->HttpHeaders.HeadersBuffer.nLength = nAllocationSize; + + // Set default values in the session structure + HTTPClientSetVerb((UINT32)pHTTPSession,(HTTP_VERB)HTTP_CLIENT_DEFAULT_VERB); // Default HTTP verb + pHTTPSession->HttpUrl.nPort = HTTP_CLIENT_DEFAULT_PORT; // Default TCP port + pHTTPSession->HttpConnection.HttpSocket = HTTP_INVALID_SOCKET; // Invalidate the socket + // Set the outgoing headers pointers + pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam; + // Set our state + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_INIT; + + // Save the flags + pHTTPSession->HttpFlags = Flags; + + // Reset the status + pHTTPSession->HttpHeadersInfo.nHTTPStatus = 0; + // Return an allocated session pointer (cast to UINT32 first) + return (HTTP_SESSION_HANDLE)pHTTPSession; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientReset +// Purpose : Reset the HTTP session and make it ready for another call +// Gets : FLAGS +// Returns : HTTP Session handle +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientReset (HTTP_SESSION_HANDLE pSession) +{ + + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + return HTTPIntrnSessionReset(pHTTPSession,TRUE); + +} + +/////////////////////////////////////////////////////////////////////////////// +// Function : HTTPClientCloseRequest +// Purpose : Closes any active connection and free any used memory +// Gets : a Session handle +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientCloseRequest (HTTP_SESSION_HANDLE *pSession) +{ + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)*(pSession); + if(!pHTTPSession) + { + // User passed a bad pointer + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + if(pHTTPSession->HttpUrl.Url != NULL) + { + TLS_DBGPRT_INFO("free pHTTPSession->HttpUrl.Url = %s\n", pHTTPSession->HttpUrl.Url); + tls_mem_free(pHTTPSession->HttpUrl.Url); + pHTTPSession->HttpUrl.Url = NULL; + } + // Check for a valid pointer to the HTTP headers + if(pHTTPSession->HttpHeaders.HeadersBuffer.pParam) + { + // Release the used memory + tls_mem_free(pHTTPSession->HttpHeaders.HeadersBuffer.pParam); + pHTTPSession->HttpHeaders.HeadersBuffer.pParam = NULL; + } + // Close any active socket connection + HTTPIntrnConnectionClose(pHTTPSession); + // free the session structure + tls_mem_free(pHTTPSession); + + pHTTPSession = 0; // NULL the pointer + *(pSession) = 0; + + return HTTP_CLIENT_SUCCESS; +} + +#ifdef _HTTP_DEBUGGING_ +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientSetDebugHook +// Purpose : Sets the HTTP debug function pointer +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientSetDebugHook (HTTP_SESSION_HANDLE pSession, + E_HTTPDebug *pDebug) // [IN] Function pointer to the the caller debugging function) +{ + + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + pHTTPSession->pDebug = pDebug; +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPClientSetDebugHook",NULL,0,"Debugging hook set, return code is %d",HTTP_CLIENT_SUCCESS); + } +#endif + + + return HTTP_CLIENT_SUCCESS; +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientSetVerb +// Purpose : Sets the HTTP verb for the outgoing request +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientSetVerb (HTTP_SESSION_HANDLE pSession, + HTTP_VERB HttpVerb) // [IN] Http verb (GET POST HEAD act') +{ + + P_HTTP_SESSION pHTTPSession = NULL; + + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPClientSetVerb",NULL,0,"Selected Verb is %d",(INT32)HttpVerb); + } +#endif + + // Cache the verb (as integer) for later use + pHTTPSession->HttpHeaders.HttpVerb = HttpVerb; + + // Convert the Verb parameter into its equivalent string representation + switch (HttpVerb) + { + case VerbGet: + strcpy(pHTTPSession->HttpHeaders.Verb,"GET"); + break; + case VerbHead: + if(!HTTP_CLIENT_ALLOW_HEAD_VERB) + { + return HTTP_CLIENT_ERROR_BAD_VERB; + } +#if HTTP_CLIENT_ALLOW_HEAD_VERB + strcpy(pHTTPSession->HttpHeaders.Verb,"HEAD"); + break; +#endif + case VerbPost: + strcpy(pHTTPSession->HttpHeaders.Verb,"POST"); + break; + case VerbPut: + strcpy(pHTTPSession->HttpHeaders.Verb,"PUT"); + break; + default: + // Unknown verb + return HTTP_CLIENT_ERROR_BAD_VERB; + }; + + return HTTP_CLIENT_SUCCESS; +} +#if TLS_CONFIG_HTTP_CLIENT_AUTH +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientSetVerb +// Purpose : Sets the HTTP authentication schema +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientSetAuth (HTTP_SESSION_HANDLE pSession, + HTTP_AUTH_SCHEMA AuthSchema, // The type of the authentication to perform + void *pReserved) +{ + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPClientSetAuth",NULL,0,"Selected authentication is %d",(INT32)AuthSchema); + } +#endif + + switch(AuthSchema) + { +#if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC + case(AuthSchemaBasic): + strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"basic"); + break; +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC +#if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST + case(AuthSchemaDigest): + strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"digest"); + break; +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST + case(AuthSchemaKerberos): + strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"negotiate"); + break; + }; + + if(AuthSchema >= AuthNotSupported) + { + return HTTP_CLIENT_ERROR_BAD_AUTH; + } + + pHTTPSession->HttpCredentials.CredAuthSchema = AuthSchema; + return HTTP_CLIENT_SUCCESS; +} +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientSetCredentials +// Purpose : Sets credentials for the target host +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientSetCredentials (HTTP_SESSION_HANDLE pSession, + CHAR *pUserName, // [IN] Null terminated string containing the sessions user name + CHAR *pPassword) // [IN] Null terminated string containing the sessions password +{ + UINT32 nLength; + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + // Get the length of the user name string and see if it's not too long + nLength = strlen(pUserName); + if(nLength > HTTP_CLIENT_MAX_USERNAME_LENGTH) + { + return HTTP_CLIENT_ERROR_LONG_INPUT; + } + + // Get the length of the password string and see if it's not too long + nLength = strlen(pPassword); + if(nLength > HTTP_CLIENT_MAX_PASSWORD_LENGTH) + { + return HTTP_CLIENT_ERROR_LONG_INPUT; + } + + // Copy them into our internal buffer + pHTTPSession->HttpCredentials.CredUser = pUserName; + pHTTPSession->HttpCredentials.CredPassword = pPassword; + + // Set the authentication Boolean flag + pHTTPSession->HttpHeadersInfo.HaveCredentials = TRUE; + + return HTTP_CLIENT_SUCCESS; +} + +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH + +#if TLS_CONFIG_HTTP_CLIENT_PROXY +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientSetProxy +// Purpose : Sets all the proxy related parameters +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientSetProxy (HTTP_SESSION_HANDLE pSession, + CHAR *pProxyHost, // [IN] Null terminated string containing the host name + UINT16 nPort, // [IN] The proxy port number + CHAR *pUserName, // [IN] User name for proxy authentication (can be null) + CHAR *pPassword) // [IN] User password for proxy authentication (can be null) +{ + P_HTTP_SESSION pHTTPSession = NULL; + UINT32 nLength; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPClientSetProxy",NULL,0,"Proxy host %s,Port %d, Username %s,Password %s",pProxyHost,nPort,pUserName,pPassword); + } +#endif + + nLength = strlen(pProxyHost); + if(nLength > HTTP_CLIENT_MAX_PROXY_HOST_LENGTH) + { + return HTTP_CLIENT_ERROR_LONG_INPUT; + } + // Cache the user supplied information in the internal session structure + pHTTPSession->HttpProxy.ProxyHost = pProxyHost; + if(pUserName) + { + nLength = strlen(pUserName); + if(nLength > HTTP_CLIENT_MAX_USERNAME_LENGTH) + { + return HTTP_CLIENT_ERROR_LONG_INPUT; + } + + // Proxy user name (for Proxy server authentication) + pHTTPSession->HttpProxy.ProxtUser = pUserName; + } + if(pPassword) + { + + // Get the length of the password string and see if it's not too long + nLength = strlen(pPassword); + if(nLength > HTTP_CLIENT_MAX_PASSWORD_LENGTH) + { + return HTTP_CLIENT_ERROR_LONG_INPUT; + } + // Proxy password (for proxy server authentication) + pHTTPSession->HttpProxy.ProxyPassword = pPassword; + } + // Proxy TCP port + pHTTPSession->HttpProxy.nProxyPort = nPort; + // Set the Proxy flag in the connection structure + pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_USINGPROXY; + // Set the proxy auyjentication schema + if(pPassword && pUserName) + { + pHTTPSession->HttpProxy.ProxyAuthSchema = HTTP_CLIENT_DEFAULT_PROXY_AUTH; + } + return HTTP_CLIENT_SUCCESS; +} +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientAddRequestHeaders +// Purpose : Add headers to the outgoing request +// Gets : +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientAddRequestHeaders (HTTP_SESSION_HANDLE pSession, + CHAR *pHeaderName, // [IN] The Headers name + CHAR *pHeaderData, // [IN] The headers data + BOOL nInsert) // [IN] Reserved could be any +{ + + UINT32 nRetCode; + UINT32 nHeaderLength,nDataLength; + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPClientAddRequestHeaders",NULL,0,"Adding Header %s: %s",pHeaderName,pHeaderData); + } +#endif + + // Get the elements length + nHeaderLength = strlen(pHeaderName); + nDataLength = strlen(pHeaderData); + + // Call the internal function to add the headers to our session buffer + nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,pHeaderName,nHeaderLength,pHeaderData,nDataLength); + + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientSendRequest +// Purpose : This function builds the request headers, performs a DNS resolution , +// opens the connection (if it was not opened yet by a previous request or if it has closed) +// and sends the request headers. +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientSendRequest (HTTP_SESSION_HANDLE pSession, + CHAR *pUrl, // [IN] The requested URL + VOID *pData, // [IN] Data to post to the server + UINT32 nDataLength, // [IN] Length of posted data + BOOL TotalLength, // [IN] + UINT32 nTimeout, // [IN] Operation timeout + UINT32 nClientPort) // [IN] Client side port 0 for none +{ + + UINT32 nRetCode; // Function call return code + UINT32 nBytes; // Bytes counter (socket operations) + UINT32 nUrlLength; // Length of the given Url + P_HTTP_SESSION pHTTPSession = NULL; // Session pointer + CHAR ContentLength[32]; + do + { + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPClientSendRequest",NULL,0,"Url: %s",pUrl); + } +#endif + + // Set the operation timeout counters + pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime(); + // 0 makes us use the default defined value + pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout); + // Store the cliebt port for later usage + pHTTPSession->HttpConnection.HttpClientPort = nClientPort; + + // Parse the URL + nUrlLength = strlen(pUrl); + nRetCode = HTTPIntrnSetURL(pHTTPSession,pUrl,nUrlLength); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } + + // Create the default headers + // Add the "Host" header. we should handle a special case of port incorporated within the host name. + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_URLANDPORT) != HTTP_CLIENT_FLAG_URLANDPORT) + { + // The case where we don't have the port + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession, + "Host",4, + pHTTPSession->HttpUrl.UrlHost.pParam, + pHTTPSession->HttpUrl.UrlHost.nLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + } + else + { + // We have the port so use a deferent element + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Host",4,pHTTPSession->HttpUrl.UrlHost.pParam, + (pHTTPSession->HttpUrl.UrlPort.pParam - pHTTPSession->HttpUrl.UrlHost.pParam) -1 )) != HTTP_CLIENT_SUCCESS) + { + break; + } + } + // We are in a post request without knowing the total length in advance so return error or use chunking + if(pHTTPSession->HttpHeaders.HttpVerb == VerbPost && TotalLength == FALSE) + { + // If the user specified the chunked flag + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) + { + // Add the Transfer-Encoding: header + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Transfer-Encoding",17,"chunked",7))!= HTTP_CLIENT_SUCCESS) + { + break;; + } + } + else if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_MULTIPART_FORM) == HTTP_CLIENT_FLAG_MULTIPART_FORM) + { + } + else + { + // Not a supported operation - unknown length + nRetCode = HTTP_CLIENT_ERROR_HEADER_NO_LENGTH; + break; + } + } + + // Add the "User-Agent" header + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"User-Agent",10,HTTP_CLIENT_DEFAULT_AGENT,strlen(HTTP_CLIENT_DEFAULT_AGENT)))!= HTTP_CLIENT_SUCCESS) + { + break; + } + // Add the "Keep-Alive" header (if requested by the caller) + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_KEEP_ALIVE) == HTTP_CLIENT_FLAG_KEEP_ALIVE) + { + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_USINGPROXY) != HTTP_CLIENT_FLAG_USINGPROXY) + { + // No proxy keep alive: + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Connection",10,"Keep-Alive",10))!= HTTP_CLIENT_SUCCESS) + { + break; + } + } +#if TLS_CONFIG_HTTP_CLIENT_PROXY + else // proxy keep alive + { + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Proxy-Connection",15,"Keep-Alive",10))!= HTTP_CLIENT_SUCCESS) + { + break; + } + } +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + } + + // If we know the total length in advance (only when posting) + if((pHTTPSession->HttpHeaders.HttpVerb == VerbPost || pHTTPSession->HttpHeaders.HttpVerb == VerbPut)&& TotalLength == TRUE) + { + // set the total content length header + pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength = nDataLength; // Store for later usage + memset(ContentLength,0,32); + IToA(ContentLength,nDataLength); // Convert the buffer length to a string value + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Content-Length",14,ContentLength,strlen(ContentLength)))!= HTTP_CLIENT_SUCCESS) + { + break; + } + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_MULTIPART_FORM) != HTTP_CLIENT_FLAG_MULTIPART_FORM) + { + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Content-Type",12,"application/x-www-form-urlencoded",33))!= HTTP_CLIENT_SUCCESS) + { + break; + } + } + } + + // Add the "Cache control" header (if requested by the caller) + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_NO_CACHE) == HTTP_CLIENT_FLAG_NO_CACHE) + { + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Cache-Control",13,"no-cache",8))!= HTTP_CLIENT_SUCCESS) + { + break; + } + } + + + // Now we can connect to the remote server and send the leading request followed by the HTTP headers + // Check for timeout + if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_TIME_OUT; + break; + } + // Handle connection close message (reconnect) + if(pHTTPSession->HttpHeadersInfo.Connection == FALSE) + { + // Gracefully close the connection and set the socket as invalid + if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) + { + HTTPIntrnConnectionClose(pHTTPSession); + } + // Connect to the remote server (or proxy) + nRetCode = HTTPIntrnConnectionOpen(pHTTPSession); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } + } + +#if TLS_CONFIG_HTTP_CLIENT_AUTH + // Send the request along with the rest of the headers + if(pHTTPSession->HttpCredentials.CredAuthSchema != AuthSchemaNone +#if TLS_CONFIG_HTTP_CLIENT_PROXY + ||pHTTPSession->HttpProxy.ProxyAuthSchema != AuthSchemaNone +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + ) // If we have to authenticate we should use the HEAD verb + { + if(HTTP_CLIENT_ALLOW_HEAD_VERB) // HEAD should not be ussed if not defined + { + if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,VerbHead)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the state flag + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HEAD_SENT; + } + else + { + // Simply use the verb that was set by the caller without changing to HEAD + if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // This the case where the caller know the total length to receive in advance + // and he wishes to send the data right away + if(pHTTPSession->HttpHeaders.HttpVerb == VerbPost && TotalLength == TRUE) + { + + // Send the data + nBytes = nDataLength; + if((nRetCode = HTTPIntrnSend(pHTTPSession,pData,&nBytes)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the session state + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_POST_SENT; + } + + } + + // Retrive and analyze the Headers + if((nRetCode = HTTPIntrnHeadersReceive(pHTTPSession,nTimeout)) != HTTP_CLIENT_SUCCESS) + { + break; + } + if(pHTTPSession->HttpHeadersInfo.nHTTPStatus != 401 && pHTTPSession->HttpHeadersInfo.nHTTPStatus != 407) + { + + nRetCode = HTTP_CLIENT_ERROR_AUTH_MISMATCH; + break; + } + // Authenticate + if((nRetCode = HTTPIntrnAuthenticate(pHTTPSession)) != HTTP_CLIENT_SUCCESS) + { + break; + } + + } + else +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH + { + // No authentication use the verb that was requested by the caller + if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTP_CLIENT_SUCCESS) + { + break; + } + } + // This the case where the caller know the total length to receive in advance + // and he wishes to send the data right away + if((pHTTPSession->HttpHeaders.HttpVerb == VerbPost || pHTTPSession->HttpHeaders.HttpVerb == VerbPut) + && (TotalLength == TRUE || (pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_MULTIPART_FORM) == HTTP_CLIENT_FLAG_MULTIPART_FORM)) + { + + // Send the data + nBytes = nDataLength; + if((nRetCode = HTTPIntrnSend(pHTTPSession,pData,&nBytes)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the session state + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_POST_SENT; + } + + }while(0); + + + + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientRecvResponse +// Purpose : Receives the response header on the connection and parses it. +// Performs any required authentication. +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientRecvResponse (HTTP_SESSION_HANDLE pSession, + UINT32 nTimeout) // [IN] Timeout for the operation +{ + + UINT32 nRetCode; // Function return code + P_HTTP_SESSION pHTTPSession = NULL; // Session pointer + + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + do + { + if((nRetCode = HTTPIntrnHeadersReceive(pHTTPSession, nTimeout)) != HTTP_CLIENT_SUCCESS) + { + break; + } + } while(0); + + return nRetCode; + +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientWriteData +// Purpose : Write data to the remote server +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientWriteData (HTTP_SESSION_HANDLE pSession, + VOID *pBuffer, + UINT32 nBufferLength, + UINT32 nTimeout) +{ + + UINT32 nRetCode = HTTP_CLIENT_SUCCESS; + UINT32 nBytes; + CHAR Chunk[HTTP_CLIENT_MAX_CHUNK_HEADER]; + + P_HTTP_SESSION pHTTPSession = NULL; + + + // Cast the handle to our internal structure and check the pointer validity + pHTTPSession = (P_HTTP_SESSION)pSession; + do + { + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + + // Set the operation timeout counters + pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime(); + pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout); + + // Did the caller specified chunked sending? + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) + { + // Prep the chunk Header and send it + memset(Chunk,0x00,HTTP_CLIENT_MAX_CHUNK_HEADER); + HTTPStrLToH(Chunk,nBufferLength); + strcat(Chunk,HTTP_CLIENT_CRLF); + + // Send the leading CrLf (only after the first chunk) + if(pHTTPSession->HttpCounters.nSentChunks >= 1) + { + nBytes = 2;; + nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF,&nBytes); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } + } + + // Send the chunk header + nBytes = strlen(Chunk); + nRetCode = HTTPIntrnSend(pHTTPSession,Chunk,&nBytes); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } + } + + // Send the data + nBytes = nBufferLength; + nRetCode = HTTPIntrnSend(pHTTPSession,pBuffer,&nBytes); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } + + // If we are using chunks then.. + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) + { + // Set the chunks count + pHTTPSession->HttpCounters.nSentChunks++; + + // If it was the last chunk (0) we should re-get the headers from the server reply + if(nBufferLength == 0) + { + // Send the trailing CrLf + nBytes = 2;; + nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF,&nBytes); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } + // Get the remote headers (since the last chunk was transmitted we can expect the server to start the reply) + if((nRetCode = HTTPIntrnHeadersReceive(pHTTPSession,nTimeout)) != HTTP_CLIENT_SUCCESS) + { + break; + } + } + } + + } while(0); + + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientReadData +// Purpose : Read data from the server. Parse out the chunks data. +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + + UINT32 HTTPClientReadData (HTTP_SESSION_HANDLE pSession, + VOID *pBuffer, // [IN OUT] a pointer to a buffer that will be filled with the servers response + UINT32 nBytesToRead, // [IN] The size of the buffer (numbers of bytes to read) + UINT32 nTimeout, // [IN] operation timeout in seconds + UINT32 *nBytesRecived) // [OUT] Count of the bytes that ware received in this operation + { + + UINT32 nBytes = 0; + UINT32 nRetCode = 0 ; + INT32 nProjectedBytes = 0; // Should support negative numbers + CHAR *pNullPtr; + BOOL EndOfStream = FALSE; + + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + // If the last verb that was used was HEAD there is no point to get this data (chanses are that we will endup with timeout) + if(pHTTPSession->HttpHeaders.HttpVerb == VerbHead) + { + return HTTP_CLIENT_EOS; + + } + + // Set the operation timeout counters + pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime(); + pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout); + + + nBytes = nBytesToRead - 1; // We will spare 1 byte for the trailing null termination + *((CHAR*)pBuffer) = 0; // Null terminate the user supplied buffer + *(nBytesRecived) = 0; // Set the return bytes count to 0 + + // We can read the data only if we got valid headers (and not authentication requests for example) + if((pHTTPSession->HttpState & HTTP_CLIENT_STATE_HEADERS_PARSED) != HTTP_CLIENT_STATE_HEADERS_PARSED) + { + return HTTP_CLIENT_ERROR_BAD_STATE; + } + + // Is it a chunked mode transfer? + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_CHUNKED) == HTTP_CLIENT_FLAG_CHUNKED) + { + + // How many bytes left until the next chunk? + if(pHTTPSession->HttpCounters.nBytesToNextChunk == 0) + { + // Read the chunk header and get its length + if(HTTPIntrnGetRemoteChunkLength(pHTTPSession) != HTTP_CLIENT_SUCCESS) + { + // Could not parse the chunk parameter + return HTTP_CLIENT_ERROR_CHUNK; + } + + // 0 Bytes chunk, we should return end of stream + if(pHTTPSession->HttpCounters.nRecivedChunkLength == 0) + { + return HTTP_CLIENT_EOS; + } + } + // If we are about to read pass the next chunk, reduce the read bytes so we will read + // non HTML data + nProjectedBytes = pHTTPSession->HttpCounters.nBytesToNextChunk - nBytes; + if ( nProjectedBytes <= 0) + { + // Set the correct bytes count we should read + nBytes = pHTTPSession->HttpCounters.nBytesToNextChunk; + } + } + + // Do we have the content length? + if(pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0) + { + // Length of the projected buffer + nProjectedBytes = pHTTPSession->HttpCounters.nRecivedBodyLength + nBytes; + // If we are going to read more then the known content length then.. + if(nProjectedBytes >= (INT32)pHTTPSession->HttpHeadersInfo.nHTTPContentLength) + { + // Reduce the received bytes count to the correct size + nBytes = pHTTPSession->HttpHeadersInfo.nHTTPContentLength - pHTTPSession->HttpCounters.nRecivedBodyLength; + + } + } + // Receive the data from the socket + nRetCode = HTTPIntrnRecv(pHTTPSession,(CHAR*)pBuffer,&nBytes,FALSE); + // Set the return bytes count + *(nBytesRecived) = nBytes; // + 1; Fixed 11/9/2005 + + // Pointer to the end of the buffer + pNullPtr = (CHAR*)pBuffer + nBytes ; + // And null terminate + *pNullPtr = 0; + + // Socket read went OK + if(nRetCode == HTTP_CLIENT_SUCCESS) + { + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPClientReadData",NULL,0,"Reading %d bytes",nBytes); + } +#endif + // Set the HTTP counters + pHTTPSession->HttpCounters.nRecivedBodyLength += nBytes; + // If we know the total content length and.. + if(pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0) + { + // If total received body is equal or greater then the known content length then.. + if( pHTTPSession->HttpCounters.nRecivedBodyLength >= pHTTPSession->HttpHeadersInfo.nHTTPContentLength) + { + // Raise a flag to signal end of stream + EndOfStream = TRUE; + } + } + // Is it a chunked mode transfer? + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_CHUNKED) == HTTP_CLIENT_FLAG_CHUNKED) + { + // We are a little closer to the next chunk now + pHTTPSession->HttpCounters.nBytesToNextChunk -= nBytes; + } + // Is it End of stream? + if(EndOfStream == TRUE) + { + // So exit + return HTTP_CLIENT_EOS; + } + } + + return nRetCode ; +} +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientGetInfo +// Purpose : Fill the users structure with the session information +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientGetInfo (HTTP_SESSION_HANDLE pSession, HTTP_CLIENT *HTTPClient) +{ + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + // Reset the users info structure + memset(HTTPClient,0x00,sizeof(HTTP_CLIENT)); + + HTTPClient->HTTPStatusCode = pHTTPSession->HttpHeadersInfo.nHTTPStatus; + HTTPClient->RequestBodyLengthSent = pHTTPSession->HttpCounters.nSentBodyBytes; + HTTPClient->ResponseBodyLengthReceived = pHTTPSession->HttpCounters.nRecivedBodyLength; + HTTPClient->TotalResponseBodyLength = pHTTPSession->HttpHeadersInfo.nHTTPContentLength; + HTTPClient->HttpState = pHTTPSession->HttpState; + + return HTTP_CLIENT_SUCCESS; + + +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientFindFirstHeader +// Purpose : Initiate the headr searching functions +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientFindFirstHeader (HTTP_SESSION_HANDLE pSession, CHAR *pSearchClue,CHAR *pHeaderBuffer, UINT32 *nLength) +{ + + P_HTTP_SESSION pHTTPSession = NULL; + UINT32 nClueLength; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + nClueLength = strlen(pSearchClue); // See if we are not to long + if(nClueLength >= HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE) + { + return HTTP_CLIENT_ERROR_HEADER_BIG_CLUE; + } + else + { + pHTTPSession->HttpHeaders.SearchClue = pSearchClue; + pHTTPSession->HttpHeaders.HeaderSearch.nLength = 0; + pHTTPSession->HttpHeaders.HeaderSearch.pParam = NULL; + } + + return HTTPClientGetNextHeader(pSession, pHeaderBuffer, nLength); +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientFindCloseHeader +// Purpose : Terminate a headers search session +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPClientFindCloseHeader (HTTP_SESSION_HANDLE pSession) +{ + + P_HTTP_SESSION pHTTPSession = NULL; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + pHTTPSession->HttpHeaders.HeaderSearch.nLength = 0; + pHTTPSession->HttpHeaders.HeaderSearch.pParam = NULL; + + return HTTP_CLIENT_SUCCESS; + +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPClientGetNextHeader +// Purpose : Terminate a headers search session +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// +UINT32 HTTPClientGetNextHeader (HTTP_SESSION_HANDLE pSession, CHAR *pHeaderBuffer, UINT32 *nLength) +{ + + P_HTTP_SESSION pHTTPSession = NULL; + UINT32 nOffset = 0; + UINT32 nRetCode; + HTTP_PARAM HttpHeader; + CHAR *pPtr; + + // Cast the handle to our internal structure and check the pointers validity + pHTTPSession = (P_HTTP_SESSION)pSession; + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + if(pHTTPSession->HttpHeaders.HeaderSearch.nLength > 0) // We must adjust the search offset since it is not the fierst iteration + { + nOffset = pHTTPSession->HttpHeaders.HeaderSearch.pParam - pHTTPSession->HttpHeaders.HeadersIn.pParam; + } + // Search for the next header + nRetCode = HTTPIntrnHeadersFind(pHTTPSession,pHTTPSession->HttpHeaders.SearchClue,&HttpHeader,TRUE,nOffset); + + if(nRetCode == HTTP_CLIENT_SUCCESS) + { + if(HttpHeader.nLength > *(nLength)) // Check for sufficiant length + { + *(nLength) = HttpHeader.nLength; + pHeaderBuffer[0] = 0; // Reset the users buffer + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + + pPtr = HttpHeader.pParam; + nOffset = 0; + if(*pPtr == 0x0d) + { + nOffset++; + pPtr++; + } + if(*pPtr == 0x0a) + { + nOffset++; + pPtr++; + } + + strncpy(pHeaderBuffer,pPtr,HttpHeader.nLength - nOffset); + pHeaderBuffer[HttpHeader.nLength - nOffset] = 0; + *(nLength) = HttpHeader.nLength - nOffset; + pHTTPSession->HttpHeaders.HeaderSearch.pParam = HttpHeader.pParam + HttpHeader.nLength; + pHTTPSession->HttpHeaders.HeaderSearch.nLength++; + + return HTTP_CLIENT_SUCCESS; + + } + + return nRetCode; + +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnSetURL +// Purpose : Parse the user's URL +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnSetURL (P_HTTP_SESSION pHTTPSession, + CHAR *pUrl, // [IN] a null terminated string containing the Url we should retrieve + UINT32 nUrlLength) // [IN] The length the Url string +{ + + UINT32 nUrlOffset; // Offset in bytes within the Url string + HTTP_URL *pUrlPtr; // a Pointer to the Url structure (within the global session structure) + CHAR *pPtr; // a Pointer for the Url port (Used in the parsing process) + CHAR UrlPort[16]; // a temporary byte array for the Url port conversion operation (string to number) + + // Check for the session pointer validity + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + + // Get the length of the Url + nUrlLength = strlen(pUrl); + + // Check if it is not longer than the permitted length + if((nUrlLength + 1) > HTTP_CLIENT_MAX_URL_LENGTH) + { + return HTTP_CLIENT_ERROR_LONG_INPUT; + } + + if(pHTTPSession->HttpUrl.Url != NULL) + { + TLS_DBGPRT_INFO("free pHTTPSession->HttpUrl.Url = %x\n", pHTTPSession->HttpUrl.Url); + tls_mem_free(pHTTPSession->HttpUrl.Url); + pHTTPSession->HttpUrl.Url = NULL; + } + pHTTPSession->HttpUrl.Url = tls_mem_alloc(nUrlLength+2); + if(pHTTPSession->HttpUrl.Url == NULL) + return HTTP_CLIENT_ERROR_NO_MEMORY; + memset(pHTTPSession->HttpUrl.Url, 0, nUrlLength+2); + //TLS_DBGPRT_INFO("HttpUrl.Url=%x, size=%d\n", pHTTPSession->HttpUrl.Url, nUrlLength+2); + // Point the local pointer on the global structure + pUrlPtr = &pHTTPSession->HttpUrl; + // Copy to the internal buffer + MEMCPY(pUrlPtr->Url, pUrl, nUrlLength); + nUrlOffset = 0; + + // Get the Url base ("http" or "https") + if(HTTPStrSearch(pUrlPtr->Url,":",nUrlOffset,nUrlLength,&pUrlPtr->UrlBsee) == FALSE) + { + return HTTP_CLIENT_ERROR_BAD_URL; + } + // Increase the offset parameter + nUrlOffset += pUrlPtr->UrlBsee.nLength; +#if TLS_CONFIG_HTTP_CLIENT_SECURE + // If we can parse the string "HTTPS" we can assume a secured session + if(HTTPStrInsensitiveCompare(pUrlPtr->UrlBsee.pParam,"https",pUrlPtr->UrlBsee.nLength) == TRUE) + { + // Set the secured flags on the session + pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_URLHTTPS; + pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_SECURE; + // ToDo: Init TLS (GetProtocol) +#ifdef _HTTP_BUILD_AMT + // OS_GET_CLIENT_SUBSET_PROTOCOL(TRUE,&pHTTPSession->pSecProtocol); +#endif + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnSetURL",NULL,0,"SSL Protected Url %s",pUrl); + } +#endif + } + else // it should be "http" +#endif //TLS_CONFIG_HTTP_CLIENT_SECURE + { + if(HTTPStrInsensitiveCompare(pUrlPtr->UrlBsee.pParam,"http",pUrlPtr->UrlBsee.nLength) == FALSE) + { + return HTTP_CLIENT_ERROR_BAD_URL; // cOULD NOT DETECT http or https prefix + + } + } + // Look for standard Url elements + if(HTTPStrSearch(pUrlPtr->Url,"://",nUrlOffset,3,0) == FALSE) + { + return HTTP_CLIENT_ERROR_BAD_URL; // Could not detect "://" + } + // Increase the offset parameter + nUrlOffset += 3; + + // Get the host name + if(HTTPStrSearch(pUrlPtr->Url,"/",nUrlOffset,(nUrlLength - nUrlOffset),&pUrlPtr->UrlHost) == FALSE) + { + pUrlPtr->Url[nUrlLength] = '/'; + nUrlLength++; + if(HTTPStrSearch(pUrlPtr->Url,"/",nUrlOffset,(nUrlLength - nUrlOffset),&pUrlPtr->UrlHost) == FALSE) + { + return HTTP_CLIENT_ERROR_BAD_URL; + } + } + + nUrlOffset += pUrlPtr->UrlHost.nLength; + + // Do we have the port within the hostname? + if(HTTPStrSearch(pUrlPtr->Url,":", + (nUrlOffset - pUrlPtr->UrlHost.nLength), + pUrlPtr->UrlHost.nLength, + &pUrlPtr->UrlPort) == TRUE) + { + if((pUrlPtr->UrlHost.nLength - pUrlPtr->UrlPort.nLength) < 10) + { + // To-Do: check the actual port length before the memcpy + pUrlPtr->UrlPort.pParam += pUrlPtr->UrlPort.nLength + 1; + MEMCPY(UrlPort,pUrlPtr->UrlPort.pParam,15); + pUrlPtr->UrlPort.nLength = 0; + pPtr = UrlPort; + while(*pPtr && pPtr++) + { + + pUrlPtr->UrlPort.nLength++; + if(*pPtr == '/') + { + *pPtr = 0; + pUrlPtr->nPort = (UINT16)atol(UrlPort); + pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_URLANDPORT; + break; + } + } + } + else + { + // Port too big + return HTTP_CLIENT_ERROR_BAD_URL; + } + } + + // Get the request body + pUrlPtr->UrlRequest.pParam = pUrlPtr->Url + nUrlOffset; + pUrlPtr->UrlRequest.nLength = nUrlLength - nUrlOffset; +#if TLS_CONFIG_HTTP_CLIENT_SECURE + // If we got SSL url with no port we should set the default ssl port + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_URLHTTPS) == HTTP_CLIENT_FLAG_URLHTTPS) + { + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_URLANDPORT) != HTTP_CLIENT_FLAG_URLANDPORT) + { + pHTTPSession->HttpUrl.nPort = HTTP_CLIENT_DEFAULT_SSL_PORT; + + } + + } +#endif //TLS_CONFIG_HTTP_CLIENT_SECURE + // Set the state flag + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_URL_PARSED; + + return HTTP_CLIENT_SUCCESS; + +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnResizeBuffer +// Purpose : Resize the HTTP headers buffer and reset the pointers +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnResizeBuffer (P_HTTP_SESSION pHTTPSession, + UINT32 nNewBufferSize) // [IN] The new (and larger) buffer size +{ + + CHAR *pPtr = NULL; + UINT32 nCurrentBufferSize; + + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + // If the new buffer size is less or equal to the current buffer size then.. + if(pHTTPSession->HttpHeaders.HeadersBuffer.nLength >= nNewBufferSize) + { + + // Return an error (bad buffer) + return HTTP_CLIENT_ERROR_BUFFER_RSIZE; + } + + // If the new buffer size is bigger then the defined maximum buffer size then.. + if(nNewBufferSize > HTTP_CLIENT_MAX_SEND_RECV_HEADERS) + { + // Return an error (bad buffer) + return HTTP_CLIENT_ERROR_BUFFER_RSIZE; + } + // Current buffer size is the sum of the incoming and outgoing headers strings lengths + nCurrentBufferSize = pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength; + // Allocate a new buffer with the requested buffer size + nNewBufferSize=ALIGN(nNewBufferSize); + //TLS_DBGPRT_INFO("nNewBufferSize=%d\n", nNewBufferSize); + pPtr = (CHAR*)tls_mem_alloc(nNewBufferSize); + if(!pPtr) + { + // malloc() error + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + + // Copy the memory only if there is data to copy + if(nCurrentBufferSize > 0) + { + MEMCPY(pPtr,pHTTPSession->HttpHeaders.HeadersBuffer.pParam,nCurrentBufferSize); + // Reset the rest of the buffer + memset(pPtr + nCurrentBufferSize, 0x00,(nNewBufferSize - nCurrentBufferSize)); + } + else + { + // Reset the entire buffer (no previous buffer was copied) + memset(pPtr,0x00,nNewBufferSize); + } + + tls_mem_free(pHTTPSession->HttpHeaders.HeadersBuffer.pParam); + + pHTTPSession->HttpHeaders.HeadersBuffer.pParam = pPtr; + pHTTPSession->HttpHeaders.HeadersBuffer.nLength = nNewBufferSize; + + // Refresh the pointers + pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam; + if(pHTTPSession->HttpHeaders.HeadersIn.pParam) + { + pHTTPSession->HttpHeaders.HeadersIn.pParam =pHTTPSession->HttpHeaders.HeadersBuffer.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength; + } + return HTTP_CLIENT_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnRemoveHeader +// Purpose : Removes an HTTP headers by its name +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnHeadersRemove (P_HTTP_SESSION pHTTPSession, + CHAR *pHeaderName) // [IN] The header's name + +{ + + HTTP_PARAM HttpParam; + UINT32 nRetCode = HTTP_CLIENT_SUCCESS; + UINT32 nBytes; + + if(!pHTTPSession) // Pointer validation check + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + // First see if we have that header in our outgoing headers buffer + do + { + + if((nRetCode = HTTPIntrnHeadersFind(pHTTPSession,pHeaderName,&HttpParam,FALSE,0)) != HTTP_CLIENT_SUCCESS) + { + // Could not find this header + break; + } + // Calculate the new headers length + nBytes = (HttpParam.pParam - pHTTPSession->HttpHeaders.HeadersOut.pParam); + nBytes -= HttpParam.nLength; + + + // Copy the memory + MEMCPY(HttpParam.pParam, HttpParam.pParam + HttpParam.nLength,nBytes); + + // Set the new length + pHTTPSession->HttpHeaders.HeadersOut.nLength -= HttpParam.nLength; + + // Reset the buffer from it's modified end to it's previous end + memset(pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength,0x00,HttpParam.nLength); + + } while(0); + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnHeadersRemove",NULL,0,"Removing Header %",pHeaderName); + } +#endif + + + return nRetCode; + +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnHeadersAdd +// Purpose : Add HTTP headers to the outgoing headers buffers +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnHeadersAdd (P_HTTP_SESSION pHTTPSession, + CHAR *pHeaderName, // [IN] The header's name + UINT32 nNameLength, // [IN] Name length + CHAR *pHeaderData, // [IN] The Header's data + UINT32 nDataLength) // [IN] Data length +{ + CHAR *pPtr; + UINT32 nProjectedHeaderLength; + UINT32 nProjectedBufferLength; + INT32 nCurrentfreeSpace; + INT32 nProjectedfreeSpace; + UINT32 nRetCode; + + if(!pHTTPSession) // pointer validation check + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + nProjectedHeaderLength = nNameLength + nDataLength + 4; + nProjectedBufferLength = nProjectedHeaderLength + pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength; + nCurrentfreeSpace = pHTTPSession->HttpHeaders.HeadersBuffer.nLength - (pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength); + nProjectedfreeSpace = nCurrentfreeSpace - nProjectedHeaderLength; + + // Check total size limit + if(nProjectedBufferLength > HTTP_CLIENT_MAX_SEND_RECV_HEADERS) + { + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + + if((INT32)nProjectedfreeSpace < 0) + { + if(HTTP_CLIENT_MEMORY_RESIZABLE == FALSE) + { + // Need more space but we can't grow beyond the current size + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + else + { + // We can resizes so.. + nRetCode = HTTPIntrnResizeBuffer(pHTTPSession,nProjectedBufferLength + HTTP_CLIENT_MEMORY_RESIZE_FACTOR); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + return nRetCode; + } + } + } + + // Move the incoming headers data within the buffer so we will have space for the added headers + if(pHTTPSession->HttpHeaders.HeadersIn.pParam) + { + // Move the data and reset the data in the offset. + MEMCPY(pHTTPSession->HttpHeaders.HeadersIn.pParam + nProjectedHeaderLength , + pHTTPSession->HttpHeaders.HeadersIn.pParam, + pHTTPSession->HttpHeaders.HeadersIn.nLength); + // Reset the space created + memset(pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength, + 0x00, + nProjectedHeaderLength); + + } + + pPtr = pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength; + // Create the new header + MEMCPY(pPtr,pHeaderName,nNameLength); + pPtr += nNameLength; + MEMCPY(pPtr,": ",2); + pPtr += 2; + MEMCPY(pPtr,pHeaderData,nDataLength); + pPtr += nDataLength; + MEMCPY(pPtr,HTTP_CLIENT_CRLF,2); + pPtr += 2; + + // Set the new length + pHTTPSession->HttpHeaders.HeadersOut.nLength += nProjectedHeaderLength; + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnHeadersAdd",NULL,0,"Adding Header %s: %s",pHeaderName,pHeaderData); + } +#endif + + return HTTP_CLIENT_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnConnectionClose +// Purpose : Closes an active socket connection and invalidate the socket handle +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPIntrnConnectionClose (P_HTTP_SESSION pHTTPSession) +{ + + INT32 nRetCode = HTTP_CLIENT_SUCCESS; + + do + { + if(!pHTTPSession) // Validate the session pointer + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + + // If we have a valid socket then.. + if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET)// INVALID_SOCKET + { +#if TLS_CONFIG_HTTP_CLIENT_SECURE + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) + { + // TLS Close + nRetCode = HTTPWrapperSSLClose(pHTTPSession->ssl, pHTTPSession->HttpConnection.HttpSocket); + } +#endif //TLS_CONFIG_HTTP_CLIENT_SECURE + // Gracefully close it + shutdown(pHTTPSession->HttpConnection.HttpSocket,0x02); + closesocket(pHTTPSession->HttpConnection.HttpSocket); + // And invalidate the socket + pHTTPSession->HttpConnection.HttpSocket = HTTP_INVALID_SOCKET; + + break;; + } + else + { + // Not a valid socket error + nRetCode = HTTP_CLIENT_ERROR_SOCKET_INVALID; + break; + } + + } while(0); + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnConnectionClose",NULL,0,""); + } +#endif + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnConnectionOpen +// Purpose : Opens a socket connection to the remote host or proxy server +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnConnectionOpen (P_HTTP_SESSION pHTTPSession) +{ + INT32 nRetCode = HTTP_CLIENT_SUCCESS; // a function return code value + UINT32 nNullOffset; // a helper value to null terminate a given string + //int nNonBlocking = 1; // non blocking mode parameter + CHAR Backup; // a container for a char value (helps in temporary null termination) + // HTTP_HOSTNET *HostEntry; // Socket host entry pointer + ULONG Address = 0; + HTTP_SOCKADDR_IN ServerAddress; // Socket address structure + HTTP_SOCKADDR_IN LoaclAddress; // Socket address structure (for client binding) + do + { + + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + // Use an existing connection if valid + if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) + { + // Set the state flag + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HOST_CONNECTED; + return HTTP_CLIENT_SUCCESS; + } + // Zero the socket events + FD_ZERO(&pHTTPSession->HttpConnection.FDRead); + FD_ZERO(&pHTTPSession->HttpConnection.FDWrite); + FD_ZERO(&pHTTPSession->HttpConnection.FDError); + + if(pHTTPSession->HttpConnection.HttpSocket == HTTP_INVALID_SOCKET) + { + + // Create a TCP/IP stream socket + pHTTPSession->HttpConnection.HttpSocket = socket(AF_INET, // Address family + SOCK_STREAM, // Socket type + IPPROTO_TCP); // Protocol + TLS_DBGPRT_INFO("pHTTPSession->HttpConnection.HttpSocket=%d\n", pHTTPSession->HttpConnection.HttpSocket); + } + + // Exit if we don't have a valid socket + if(pHTTPSession->HttpConnection.HttpSocket == HTTP_INVALID_SOCKET) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_INVALID; + break; + } + // Set non blocking socket + /* + nRetCode = ioctlsocket(pHTTPSession->HttpConnection.HttpSocket, FIONBIO, &nNonBlocking); + if(nRetCode != 0) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_CANT_SET; + break; + } + */ + // Resolve the target host name + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_USINGPROXY) != HTTP_CLIENT_FLAG_USINGPROXY) + { + // No proxy, directly resolving the host name + // Prep the parameter + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_URLANDPORT) == HTTP_CLIENT_FLAG_URLANDPORT) + { + nNullOffset = pHTTPSession->HttpUrl.UrlHost.nLength - pHTTPSession->HttpUrl.UrlPort.nLength - 1; + } + else + { + nNullOffset = pHTTPSession->HttpUrl.UrlHost.nLength; + } + + Backup = HTTPStrExtract(pHTTPSession->HttpUrl.UrlHost.pParam,nNullOffset,0); + // Resolve the host name + nRetCode = HostByName(pHTTPSession->HttpUrl.UrlHost.pParam,&Address); + + // Restore from backup (fix the buffer) + HTTPStrExtract(pHTTPSession->HttpUrl.UrlHost.pParam,nNullOffset,Backup); + + } +#if TLS_CONFIG_HTTP_CLIENT_PROXY + else + { + // Using a Proxy server so resolve the proxy host name + nRetCode = HostByName(pHTTPSession->HttpProxy.ProxyHost,&Address); + } +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + // See if we have a valid response from the net resolve operation + /* + if(nRetCode) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_RESOLVE; + break; + } + */ + // Reset the address structures + memset(&ServerAddress, 0, sizeof(HTTP_SOCKADDR_IN)); + memset(&LoaclAddress, 0, sizeof(HTTP_SOCKADDR_IN)); + + // Fill in the address structure + ServerAddress.sin_family = AF_INET; +#ifdef _HTTP_BUILD_AMT + ServerAddress.sin_len = sizeof(HTTP_SOCKADDR_IN); + ServerAddress.sin_addr.s_addr = htonl(Address); // Server's address +#endif + ServerAddress.sin_addr.s_addr = Address; // Server's address + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_USINGPROXY) != HTTP_CLIENT_FLAG_USINGPROXY) + { + // Use the remote web server port + ServerAddress.sin_port = htons(pHTTPSession->HttpUrl.nPort); // Host Port number + } +#if TLS_CONFIG_HTTP_CLIENT_PROXY + else + { + // Use the proxy port + ServerAddress.sin_port = htons(pHTTPSession->HttpProxy.nProxyPort); // Proxy Port number + } +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + // Client-side Binding + if(pHTTPSession->HttpConnection.HttpClientPort != 0) + { + LoaclAddress.sin_family = AF_INET; +#ifdef _HTTP_BUILD_AMT + LoaclAddress.sin_len = sizeof(HTTP_SOCKADDR_IN); +#endif + LoaclAddress.sin_port = htons((unsigned short)pHTTPSession->HttpConnection.HttpClientPort); + + nRetCode = bind(pHTTPSession->HttpConnection.HttpSocket, + (HTTP_SOCKADDR*)&LoaclAddress, + sizeof(HTTP_SOCKADDR_IN)); + + if(nRetCode != 0) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_BIND; + } + } +#if TLS_CONFIG_HTTP_CLIENT_SECURE + // Connect using TLS or otherwise clear connection + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) + { // Is it a TLS connection? + nRetCode = HTTPWrapperSSLConnect(&pHTTPSession->ssl,pHTTPSession->HttpConnection.HttpSocket, // Socket + (HTTP_SOCKADDR*)&ServerAddress, // Server address + sizeof(HTTP_SOCKADDR), // Length of server address structure + "desktop"); // Hostname (ToDo: Fix this) + } + else // Non TLS so.. +#endif //TLS_CONFIG_HTTP_CLIENT_SECURE + { + nRetCode = connect(pHTTPSession->HttpConnection.HttpSocket, // Socket + (HTTP_SOCKADDR*)&ServerAddress, // Server address + sizeof(HTTP_SOCKADDR)); // Length of server address structure + } + + // The socket was set to be asyn so we should check the error being returned from connect() + nRetCode = SocketGetErr(pHTTPSession->HttpConnection.HttpSocket); + if(nRetCode == 0 || nRetCode == HTTP_EWOULDBLOCK || nRetCode == HTTP_EINPROGRESS) + { + // Set TLS Nego flag to flase + pHTTPSession->HttpConnection.TlsNego = FALSE; + // Set the Write fd_sets for a socket connection event + FD_SET(pHTTPSession->HttpConnection.HttpSocket, &pHTTPSession->HttpConnection.FDWrite); + // Set the state flag + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HOST_CONNECTED; + // We have connected so set the return value to success + nRetCode = HTTP_CLIENT_SUCCESS; + break; + } + else + { + // Socket connection problem + nRetCode = HTTP_CLIENT_ERROR_SOCKET_CONNECT; + break; + } + }while(0); +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnConnectionOpen",NULL,0,""); + } +#endif + + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnSend +// Purpose : Send data to the remote server (Asynchronous sockets) +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnSend (P_HTTP_SESSION pHTTPSession, + CHAR *pData, // [IN] a pointer to the data to be sent + UINT32 *nLength) // [IN OUT] Length of data to send and the transmitted bytes count +{ + + INT32 nSocketEvents; // Socket events center + INT32 nRetCode = HTTP_CLIENT_SUCCESS; // a function return code value + HTTP_TIMEVAL Timeval = { 1 , 0 }; // Timeout value for the socket() method + HTTP_CONNECTION *pConnection = NULL; // Pointer for the connection structure + + + do + { + // Validate the session pointer + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + + // Have a pointer on the internal connection structure for simplifying code reading + pConnection = &pHTTPSession->HttpConnection; + + while(1) + { + + // Check for timeout + if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_TIME_OUT; + break; + } + if(pConnection->HttpSocket == HTTP_INVALID_SOCKET) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_INVALID; + break; + } + // Reset socket events , only Error, since we don't want to get + // a repeated Write events (socket is connected) + + FD_SET(pConnection->HttpSocket, &pConnection->FDError); + + // See if we got any events on the socket + nSocketEvents = select((pConnection->HttpSocket + 1), 0, + &pConnection->FDWrite, + &pConnection->FDError, + &Timeval); + + if(nSocketEvents < 0) // No events on the socket + { + *(nLength) = 0; + break; // To-Do: This might be an error + } + + if(nSocketEvents == 0) // No new events so + { + continue; // restart this loop + } + + // Socket is writable (we are connected) so send the data + if(FD_ISSET(pConnection->HttpSocket ,&pConnection->FDWrite)) + { +#if TLS_CONFIG_HTTP_CLIENT_SECURE + // Send the data + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) + { + // TLS Protected connection + if(pConnection->TlsNego == FALSE) + { + nRetCode = HTTPWrapperSSLNegotiate((HTTP_SESSION_HANDLE)pHTTPSession,pConnection->HttpSocket,0,0,"desktop"); + if(nRetCode != 0) + { + // TLS Error + nRetCode = HTTP_CLIENT_ERROR_TLS_NEGO; + break; + } + pConnection->TlsNego = TRUE; + } + nRetCode = HTTPWrapperSSLSend(pHTTPSession->ssl,pConnection->HttpSocket,pData,*(nLength),0); + } + else +#endif //TLS_CONFIG_HTTP_CLIENT_SECURE + { + nRetCode = send(pConnection->HttpSocket,pData,*(nLength),0); + } + if(nRetCode == SOCKET_ERROR) + { + nRetCode = SocketGetErr(pHTTPSession->HttpConnection.HttpSocket); + nRetCode = HTTP_CLIENT_ERROR_SOCKET_SEND; + break; + } + //TLS_DBGPRT_INFO("%s", pData); + // The data was sent to the remote server + *(nLength) = nRetCode; + nRetCode = HTTP_CLIENT_SUCCESS; + break; + } + + // We had a socket related error + if(FD_ISSET(pConnection->HttpSocket ,&pConnection->FDError)) + { + FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDError); + *(nLength) = 0; + // To-Do: Handle this case + nRetCode = HTTP_CLIENT_ERROR_SOCKET_SEND; + break; + } + } + } while(0); + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnSend",pData,*(nLength),""); + } +#endif + + + return nRetCode; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnRecv +// Purpose : Receive data from the connected socket using asynchronous sockets +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnRecv (P_HTTP_SESSION pHTTPSession, + CHAR *pData, // [IN] a pointer for a buffer that receives the data + UINT32 *nLength, // [IN OUT] Length of the buffer and the count of the received bytes + BOOL PeekOnly) // [IN] State if we should only peek the socket (default is no) +{ + INT32 nSocketEvents; + #if TLS_CONFIG_HTTP_CLIENT_SECURE + INT32 nRetCode = pHTTPSession->ssl_more_data; + #else + INT32 nRetCode = 0; + #endif + HTTP_TIMEVAL Timeval = { 0, 50000 }; + HTTP_CONNECTION *pConnection = NULL; + + do + { + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + + // Set a pointer on the session internal connection structure (simplify code reading) + pConnection = &pHTTPSession->HttpConnection; + while(1) + { + // Check for timeout + if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_TIME_OUT; + break; + } + if(pConnection->HttpSocket == HTTP_INVALID_SOCKET) + { + nRetCode = HTTP_CLIENT_ERROR_SOCKET_INVALID; + break; + } + + if(nRetCode != SOCKET_SSL_MORE_DATA){ + // Reset socket events + FD_SET(pConnection->HttpSocket, &pConnection->FDRead); + FD_SET(pConnection->HttpSocket, &pConnection->FDError); + + // See if we got any events on the socket + nSocketEvents = select(pConnection->HttpSocket + 1, &pConnection->FDRead, + 0, + &pConnection->FDError, + &Timeval); + } + else + { + nSocketEvents = 1; + } + if(nSocketEvents < 0) // Error or no new socket events + { + *(nLength) = 0; + break; + } + if(nSocketEvents == 0) + { + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // This is a simple bypass for the TSL session (for some reason the socket read event is not set so + // The pending bytes on the socket are being checked manualy. + // TLS hack: + /* + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) + { + nRetCode = HTTPWrapperSSLRecvPending(pConnection->HttpSocket); + if(nRetCode > 0) + { + // Recive without being notified by the socket event + if((nRetCode = HTTPWrapperSSLRecv(pConnection->HttpSocket,pData,*(nLength),0)) == SOCKET_ERROR) + { + // Socket error + nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; + break; + } + *(nLength) = nRetCode; + // Break on no data or server connection reset + if ( nRetCode == 0 || nRetCode == HTTP_ECONNRESET) + { + // Connection closed, simply break - this is not an error + nRetCode = HTTP_CLIENT_EOS; // Signal end of stream + break; + } + // We have successfully got the data from the server + nRetCode = HTTP_CLIENT_SUCCESS; + break; + } + } + */ + // End Of the TLS bypass section + // + //////////////////////////////////////////////////////////////////////////////////////////////// + + continue; // select() timed out - restart this loop + } + if(nRetCode == SOCKET_SSL_MORE_DATA || FD_ISSET(pConnection->HttpSocket ,&pConnection->FDRead)) // Are there any read events on the socket ? + { + if(nRetCode != SOCKET_SSL_MORE_DATA){ + // Clear the event + FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDRead); + } + // Socket is readable so so read the data + if(PeekOnly == FALSE) + { +#if TLS_CONFIG_HTTP_CLIENT_SECURE + // Get the data (secuure) + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) + { + if((nRetCode = HTTPWrapperSSLRecv(pHTTPSession->ssl,pConnection->HttpSocket,pData,*(nLength),0)) == SOCKET_ERROR) + { + // Socket error + nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; + break; + } + pHTTPSession->ssl_more_data = (nRetCode == SOCKET_SSL_MORE_DATA ? SOCKET_SSL_MORE_DATA : 0); + } + else // Get the data (non secuure) +#endif //TLS_CONFIG_HTTP_CLIENT_SECURE + { + if((nRetCode = recv(pConnection->HttpSocket,pData,*(nLength),0)) == SOCKET_ERROR) + { + // Socket error + nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; + break; + } + } + + } + else + { + // Only peek te socket + if((nRetCode = recv(pConnection->HttpSocket,pData,*(nLength),MSG_PEEK)) == SOCKET_ERROR) + { + // Socket error + nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; + break; + } + + } + if(nRetCode != SOCKET_SSL_MORE_DATA) + *(nLength) = nRetCode; + // Break on no data or server connection reset + // MSDN: If the connection has been gracefully closed, the return value is zero. + if ( nRetCode == 0 || HTTPWrapperGetSocketError(pConnection->HttpSocket)== HTTP_ECONNRESET) + { + // Connection closed, simply break - this is not an error + nRetCode = HTTP_CLIENT_EOS; // Signal end of stream + break; + } + // We have successfully got the data from the server + nRetCode = HTTP_CLIENT_SUCCESS; + break; + } + + // We had a socket related error + if(nRetCode != SOCKET_SSL_MORE_DATA && FD_ISSET(pConnection->HttpSocket ,&pConnection->FDError)) + { + FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDError); + *(nLength) = 0; + + // To-Do: Handle this case + nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; + break; + + } + } + }while(0); + + return nRetCode; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnGetRemoteChunkLength +// Purpose : Receive (byte by byte) the chunk parameter (while in chunk mode receive) and +// Convert the HEX string into an integer +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnGetRemoteChunkLength (P_HTTP_SESSION pHTTPSession) +{ + + UINT32 nBytesRead = 1; + UINT32 nRetCode = HTTP_CLIENT_SUCCESS; + UINT32 nBytesCount = 0; + CHAR ChunkHeader[HTTP_CLIENT_MAX_CHUNK_HEADER]; + CHAR *pPtr; + + do + { + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + + // Read byte by byte until we get a CrLf + pPtr = ChunkHeader; // Get a pointer to the received buffer + *pPtr = 0; // Terminate with null + + while(nBytesRead > 0) + { + // Receive a single byte + nRetCode = HTTPIntrnRecv(pHTTPSession,pPtr,&nBytesRead,FALSE); + // Did we succeed? + if(nRetCode == HTTP_CLIENT_SUCCESS && nBytesRead > 0) + { + // Increment the bytes count + nBytesCount += nBytesRead; + if(nBytesRead > HTTP_CLIENT_MAX_CHUNK_HEADER) + { + // Error chunk buffer is full + nRetCode = HTTP_CLIENT_ERROR_CHUNK_TOO_BIG; + break; + } + // Don't Process if the fist 2 bytes are CrLf. + if(! ((nBytesCount == 1 && *pPtr == 0x0d) || (nBytesCount == 2 && *pPtr == 0x0a))) + { + // Advance the pointer by the received data length + pPtr += nBytesRead; + // Look for CrLf in the last 2 bytes + if(memcmp(pPtr - 2,HTTP_CLIENT_CRLF,2) == 0) + { + // Chunk Header was received + *pPtr = 0; // null terminate the chunk parameter + pHTTPSession->HttpCounters.nRecivedChunkLength = HTTPStrHToL(ChunkHeader); // Convert to a number + // Set the HTTP counters + pHTTPSession->HttpCounters.nBytesToNextChunk = pHTTPSession->HttpCounters.nRecivedChunkLength; + break; + } + } + } + else // Socket Error + { + nRetCode = 0; + break; + } + } + } while(0); + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnGetRemoteChunkLength",NULL,0,"Next chunk is %d bytes",pHTTPSession->HttpCounters.nRecivedChunkLength); + } +#endif + + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnGetRemoteHeaders +// Purpose : Perform a socket receive (byte by byte) until all the HTTP headers are received +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnGetRemoteHeaders (P_HTTP_SESSION pHTTPSession) +{ + + UINT32 nBytesRead = 1; + UINT32 nRetCode = HTTP_CLIENT_SUCCESS; + UINT32 nProjectedHeaderLength; + UINT32 nProjectedBufferLength; + INT32 nCurrentfreeSpace; + INT32 nProjectedfreeSpace; + CHAR *pPtr; + + do + { + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + + // Read byte by byte until we get CrLf followed by CrLf + // Set the incoming headers pointer + + if(!pHTTPSession->HttpHeaders.HeadersIn.pParam) + { + /// The incoming headers starts where the outgoing headers ends + pHTTPSession->HttpHeaders.HeadersIn.pParam = pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength; + } + + // Receive until we get all the headers or any other error event + while(nBytesRead > 0) + { + + // Size of the projected buffer we are going to receive + nProjectedHeaderLength = nBytesRead; + // Size of the projected total incoming buffer + nProjectedBufferLength = nProjectedHeaderLength + pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength; + // Current free space on the incoming headers buffer + nCurrentfreeSpace = pHTTPSession->HttpHeaders.HeadersBuffer.nLength - (pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength); + // Projected free space after the completion of the receive + nProjectedfreeSpace = nCurrentfreeSpace - nProjectedHeaderLength; + + // Check total size limit + if(nProjectedBufferLength > HTTP_CLIENT_MAX_SEND_RECV_HEADERS) + { + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + + if((INT32)nProjectedfreeSpace < 0) + { + if(HTTP_CLIENT_MEMORY_RESIZABLE == FALSE) + { + // Need more space but we can't grow beyond the current size + nRetCode = HTTP_CLIENT_ERROR_NO_MEMORY; + break; + } + else + { + // We can resizes so.. + nRetCode = HTTPIntrnResizeBuffer(pHTTPSession,nProjectedBufferLength + HTTP_CLIENT_MEMORY_RESIZE_FACTOR); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } + } + } + // Jump to the beginning of the incoming headers (just after the end of the outgoing headers) + pPtr = pHTTPSession->HttpHeaders.HeadersIn.pParam + pHTTPSession->HttpHeaders.HeadersIn.nLength; + // Read a single byte + nRetCode = HTTPIntrnRecv(pHTTPSession,pPtr,&nBytesRead,FALSE); + + // ToDo: Break if not getting HTTP on the first 4 bytes + if(nRetCode == HTTP_CLIENT_SUCCESS && nBytesRead > 0) + { + // Advance the pointer by 1 byte + pPtr += nBytesRead; + // Increase the total receive length + pHTTPSession->HttpHeaders.HeadersIn.nLength++; + + // Set the HTTP counters + pHTTPSession->HttpCounters.nRecivedHeaderLength++; + + if(memcmp(pPtr - 4,HTTP_CLIENT_CRLFX2,4) == 0) + { + // Headers were received + break; + } + } + else + { + TLS_DBGPRT_INFO("HTTPIntrnRecv nRetCode = %d\n", nRetCode); + if(nRetCode != HTTP_CLIENT_ERROR_SOCKET_TIME_OUT) + nRetCode = HTTP_CLIENT_ERROR_HEADER_RECV; // This was marked out for some reason + break; + } + } + }while(0); + + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnGetRemoteHeaders",NULL,0,"Got %d bytes",pHTTPSession->HttpHeaders.HeadersIn.nLength); + } +#endif + + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnHeadersFind +// Purpose : Look for a header (insensitive search) by its name +// Gets : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnHeadersFind (P_HTTP_SESSION pHTTPSession,CHAR *pHeaderName, + HTTP_PARAM *pParam, // [OUT] HTTP parameter structure that holds the search results + BOOL IncommingHeaders, // [IN] Indicate if we are to search in the incoming or outgoing headers + UINT32 nOffset) // [IN] Optionaly privide an offset to start looking from +{ + CHAR *pHeaderEnd; + CHAR *Header; // To-Do: Use pointers insted of fixed length + UINT32 nLength; + UINT32 nRetCode = HTTP_CLIENT_ERROR_HEADER_NOT_FOUND; + + do + { + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + + // Reset the input parameter structure + pParam->pParam = NULL; + pParam->nLength = 0; + // Get the requested header length + nLength = strlen(pHeaderName); + if(nLength > (HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE - 3)) + { + // Error : header search clue too big + nRetCode = HTTP_CLIENT_ERROR_HEADER_BIG_CLUE; + break; + } + // Build the searched header name , add a leading CrLf before the header name and trailing ":" + Header = tls_mem_alloc(HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE); + if(Header == NULL) + return HTTP_CLIENT_ERROR_NO_MEMORY; + memset(Header,0x00,HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE); + strcpy(Header,HTTP_CLIENT_CRLF); + strcat(Header,pHeaderName); + strcat(Header,":"); + // Case insensitive search for the header name (search the incoming headers) + if(IncommingHeaders == TRUE) + { + pParam->pParam = HTTPStrCaseStr(pHTTPSession->HttpHeaders.HeadersIn.pParam + nOffset, + pHTTPSession->HttpHeaders.HeadersIn.nLength, + Header); + } + else + { + // Optionally search the outgoing headers + pParam->pParam = HTTPStrCaseStr(pHTTPSession->HttpHeaders.HeadersOut.pParam + nOffset, + pHTTPSession->HttpHeaders.HeadersOut.nLength, + Header); + } + tls_mem_free(Header); + if(pParam->pParam) // Did we find it? + { + // Search for the token end (trailing CrLf) + pHeaderEnd = strstr(pParam->pParam + 2,HTTP_CLIENT_CRLF); + if(pHeaderEnd) + { + // Get the length (up to the CrLf) + pParam->nLength = pHeaderEnd - pParam->pParam; + nRetCode = HTTP_CLIENT_SUCCESS; + break; + + } + } + }while(0); + + // Could not find the header + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnHeadersSend +// Purpose : Build and send the HTTP request. this includes the HTTP headers +// and any required authentication data +// Gets : +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnHeadersSend(P_HTTP_SESSION pHTTPSession, + HTTP_VERB HttpVerb) // [IN] Argument that can bypass the requested verb + // Can be used for evaluating a HEAD request +{ + + UINT32 nBytes; + UINT32 nRetCode = HTTP_CLIENT_SUCCESS; + CHAR RequestCmd[16]; + CHAR ContentLength[32]; + BOOL RestoreHeadersFlag = FALSE; + HTTP_VERB HttpCachedVerb; + + if(!pHTTPSession) + { + // Bad session pointer error + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnHeadersSend",NULL, + 0,"Using Verb: %d",(INT32)HttpVerb); + } +#endif + // Cache the original VERB + HttpCachedVerb = pHTTPSession->HttpHeaders.HttpVerb; + + do + { + + // Set the verb (temporarily) + if(pHTTPSession->HttpHeaders.HttpVerb != HttpVerb) + { + if((nRetCode = HTTPClientSetVerb((HTTP_SESSION_HANDLE)pHTTPSession,HttpVerb)) != HTTP_CLIENT_SUCCESS) + { + break; + } + } + + // Remeber this state for later usage + pHTTPSession->HttpHeaders.HttpLastVerb = pHTTPSession->HttpHeaders.HttpVerb; + + // If this is a head request we should temporary remove the chunking header and the content length header + if(pHTTPSession->HttpHeaders.HttpVerb == VerbHead) + { + + // If send in chunks flag was set + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) + { + // Chunking + if((nRetCode = HTTPIntrnHeadersRemove(pHTTPSession,"Transfer-Encoding")) != HTTP_CLIENT_SUCCESS) + { + break; + } + } + // Content-Length + if(pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength > 0) // Attempt to remove only if it was previusly set + { + if((nRetCode = HTTPIntrnHeadersRemove(pHTTPSession,"Content-Length")) != HTTP_CLIENT_SUCCESS) + { + break; + } + } + + RestoreHeadersFlag = TRUE; // So it would be restored later + } + // Request Verb + nBytes = strlen(pHTTPSession->HttpHeaders.Verb) + 1; + memset(RequestCmd,0x00,16); + strcpy(RequestCmd,pHTTPSession->HttpHeaders.Verb); + strcat(RequestCmd," "); + if((nRetCode = HTTPIntrnSend(pHTTPSession,RequestCmd,&nBytes)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; + + + // Request URI + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_USINGPROXY) != HTTP_CLIENT_FLAG_USINGPROXY) + { + nBytes = pHTTPSession->HttpUrl.UrlRequest.nLength; + if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.UrlRequest.pParam,&nBytes)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; + } +#if TLS_CONFIG_HTTP_CLIENT_PROXY + else + { + nBytes = strlen(pHTTPSession->HttpUrl.Url); + if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.Url,&nBytes)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; + } +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + // Request HTTP Version + memset(RequestCmd,0x00,16); + strcpy(RequestCmd," "); + strcat(RequestCmd,HTTP_CLIENT_DEFAULT_VER); + strcat(RequestCmd,HTTP_CLIENT_CRLF); + nBytes = strlen(RequestCmd); + if((nRetCode = HTTPIntrnSend(pHTTPSession,RequestCmd,&nBytes)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; + + // Request headers + nBytes = pHTTPSession->HttpHeaders.HeadersOut.nLength; + if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpHeaders.HeadersOut.pParam,&nBytes)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; + +#if TLS_CONFIG_HTTP_CLIENT_AUTH + // Optionally add authentication headers and send them (for host or proxy authentication) + if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_UNAUTHORIZED +#if TLS_CONFIG_HTTP_CLIENT_PROXY + || pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + ) + { + + if((nRetCode = HTTPIntrnAuthHandler(pHTTPSession)) != HTTP_CLIENT_SUCCESS) + { + break; + } + } +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH + + // Request terminating CrLf + nBytes = strlen(HTTP_CLIENT_CRLF); + if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF,&nBytes)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; + + // Restore the verb + if(pHTTPSession->HttpHeaders.HttpVerb != HttpCachedVerb) + { + if((nRetCode = HTTPClientSetVerb((HTTP_SESSION_HANDLE)pHTTPSession,HttpCachedVerb)) != HTTP_CLIENT_SUCCESS) + { + break; + } + } + + if(RestoreHeadersFlag == TRUE) + { + // Restore chunking header (since it was temporarily removed for the head request + // Add the Transfer-Encoding: header + if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) + { + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Transfer-Encoding",17,"chunked",7))!= HTTP_CLIENT_SUCCESS) + { + break; + } + } + // Restore the content length + if(pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength > 0) // Attempt to remove only if it was previusly set + { + IToA(ContentLength,pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength); // Convert the buffer length to a string value + if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Content-Length",14,ContentLength,strlen(ContentLength)))!= HTTP_CLIENT_SUCCESS) + { + return nRetCode; + } + } + } + // Set the session stage + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_REQUEST_SENT; + + } while(0); + + + return nRetCode; // end of HTTPIntrnSendHeaders() +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnHeadersParse +// Purpose : Parse the HTTP incoming headers. +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnHeadersParse (P_HTTP_SESSION pHTTPSession) +{ + + CHAR *pPtr; // a pointer that points on the incoming headers + UINT32 nTokenLength = 0; // Length of the parsed token + UINT32 nRetCode = HTTP_CLIENT_SUCCESS; // a function return code value +#if TLS_CONFIG_HTTP_CLIENT_AUTH + UINT32 nOffset = 0; // Bytes offset (strings comperision) + BOOL AuthHeaders = FALSE; // While we are searching the authentication methods +#endif + CHAR *HTTPToken; // Buffer for the parsed HTTP token + HTTP_PARAM HTTPParam; // A generic pointer\length parameter for parsing + + + do + { + // Validate the session pointer + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnHeadersParse",pHTTPSession->HttpHeaders.HeadersIn.pParam,pHTTPSession->HttpHeaders.HeadersIn.nLength,"[Incomming Headers]"); + } +#endif + + + // Set a pointer on the incoming headers + pPtr = pHTTPSession->HttpHeaders.HeadersIn.pParam; + + // Detect the leading HTTP string + if(HTTPStrInsensitiveCompare(pPtr,"http",4) != TRUE) + { + nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; + break; + } + + // Get the HTTP Version + while ((*pPtr) && (*pPtr != 0x20)) + { + nTokenLength++; + pPtr++; // Move to the first space + } + strncpy(pHTTPSession->HttpHeadersInfo.HTTPVersion, + pPtr - nTokenLength, + MIN(15,nTokenLength)); + pPtr++; + + // Get the HTTP status code + HTTPToken = tls_mem_alloc(HTTP_CLIENT_MAX_TOKEN_LENGTH); + if(HTTPToken == NULL) + return HTTP_CLIENT_ERROR_NO_MEMORY; + memset(HTTPToken,0x00,HTTP_CLIENT_MAX_TOKEN_LENGTH); + nTokenLength = 0; + while ((*pPtr) && (*pPtr != 0x20)) + { + nTokenLength++; + pPtr++; // Move to the next space + } + strncpy(HTTPToken,(pPtr - nTokenLength),MIN(HTTP_CLIENT_MAX_TOKEN_LENGTH,nTokenLength)); + + pHTTPSession->HttpHeadersInfo.nHTTPStatus = atol(HTTPToken); + + // Search for content length + pHTTPSession->HttpHeadersInfo.nHTTPContentLength = 0; // Default no unknown length + // Look for the token + if(HTTPIntrnHeadersFind(pHTTPSession,"content-length",&HTTPParam,TRUE,0) == HTTP_CLIENT_SUCCESS) + { + + memset(HTTPToken,0x00,HTTP_CLIENT_MAX_TOKEN_LENGTH); // Reset the token buffer + nTokenLength = HTTP_CLIENT_MAX_TOKEN_LENGTH; // Set the buffer length + // Attempt to extract the token + if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength)) + { + // Convert the content-length into an integer. + pHTTPSession->HttpHeadersInfo.nHTTPContentLength = atol(HTTPToken); + } + } + + // Search for connection status + pHTTPSession->HttpHeadersInfo.Connection = TRUE; // Default status where no server connection header was detected + // Look for token (can be standard connection or a proxy connection) + if( (HTTPIntrnHeadersFind(pHTTPSession,"connection",&HTTPParam,TRUE,0) == HTTP_CLIENT_SUCCESS) || + (HTTPIntrnHeadersFind(pHTTPSession,"proxy-connection",&HTTPParam,TRUE,0) == HTTP_CLIENT_SUCCESS)) + { + memset(HTTPToken,0x00,HTTP_CLIENT_MAX_TOKEN_LENGTH); + nTokenLength = HTTP_CLIENT_MAX_TOKEN_LENGTH; + // Attempt to extract the token + if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength)) + { + // Is this a keep alive session? + pHTTPSession->HttpHeadersInfo.Connection = HTTPStrInsensitiveCompare(HTTPToken,"keep-alive",0); + // Is it a closed session + if(HTTPStrInsensitiveCompare(HTTPToken,"close",0) == TRUE) + { + pHTTPSession->HttpHeadersInfo.Connection = FALSE; + } + } + } + + // Search for chunking mode transfer + pHTTPSession->HttpFlags = pHTTPSession->HttpFlags &~ HTTP_CLIENT_FLAG_CHUNKED; // Remove the flag + if(HTTPIntrnHeadersFind(pHTTPSession,"transfer-encoding",&HTTPParam,TRUE,0) == HTTP_CLIENT_SUCCESS) + { + memset(HTTPToken,0x00,HTTP_CLIENT_MAX_TOKEN_LENGTH); + nTokenLength = HTTP_CLIENT_MAX_TOKEN_LENGTH; + if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength)) + { + // If the chunks token was find then set the session flag accordingly + if(HTTPStrInsensitiveCompare(HTTPToken,"chunked",0) == TRUE) + { + pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_CHUNKED; + } + } + } + tls_mem_free(HTTPToken); +#if TLS_CONFIG_HTTP_CLIENT_AUTH + // Look for the authentication header + while(AuthHeaders == FALSE) // address multiple authentication methods presented by the server + { + if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_UNAUTHORIZED) + { + // Double check for the "www-authenticate" header token + if(HTTPIntrnHeadersFind(pHTTPSession,"www-authenticate",&pHTTPSession->HttpAuthHeader.AuthHeader,TRUE,nOffset) != HTTP_CLIENT_SUCCESS) + { + if(nOffset > 0) // an authentication header was found but not the right one so adjust the error + { + nRetCode = HTTP_CLIENT_ERROR_AUTH_MISMATCH; + } + else + { + nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; + } + + break; + } + + // Make sure that we get an authentication header that maches the caller requested schema + pPtr = HTTPStrCaseStr(pHTTPSession->HttpAuthHeader.AuthHeader.pParam, + pHTTPSession->HttpAuthHeader.AuthHeader.nLength, + pHTTPSession->HttpCredentials.AuthSchemaName); + if(pPtr) + { + AuthHeaders = TRUE; + } + else + { + // Simply pass the point where the last "www" was found + nOffset = (pHTTPSession->HttpAuthHeader.AuthHeader.pParam - pHTTPSession->HttpHeaders.HeadersIn.pParam) + 3; + } + } + else + { + AuthHeaders = TRUE; + } + } +#if TLS_CONFIG_HTTP_CLIENT_PROXY + // Is this a proxy authentication header? + if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) + { + // Double check for the "Proxy-Authentication" header token + if (HTTPIntrnHeadersFind(pHTTPSession,"proxy-authenticate",&pHTTPSession->HttpAuthHeader.AuthHeader,TRUE,0) != HTTP_CLIENT_SUCCESS) + { + nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; + break; + } + } +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH + // Do we have a redirection response? + if( (pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OBJECT_MOVED) || + (pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OBJECT_MOVED_PERMANENTLY)) + { + // Check for the "Location" header token + if (HTTPIntrnHeadersFind(pHTTPSession,"location",&pHTTPSession->HttpHeadersInfo.HttpRedirectURL,TRUE,0) != HTTP_CLIENT_SUCCESS) + { + // Protocol violation, we got a redirect code without the host name to redirect to + nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; + break; + } + // Fix the pointers location (address the "Location: " prefix) + pHTTPSession->HttpHeadersInfo.HttpRedirectURL.pParam += 12; + pHTTPSession->HttpHeadersInfo.HttpRedirectURL.nLength -= 12; + + } + + }while(0); + + + return nRetCode; +} +#if TLS_CONFIG_HTTP_CLIENT_AUTH +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnAuthenticate +// Purpose : +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnAuthenticate(P_HTTP_SESSION pHTTPSession) +{ + UINT32 nRetCode = HTTP_CLIENT_SUCCESS; // Function call return code + UINT32 nBytes = 32; + UINT32 nTotalBytes = 0; + CHAR ErrorPage[32]; + BOOL NewConnection = FALSE; + + + do + { + // Validate the session pointer + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + + // Handle connection close message (reconnect) + if(pHTTPSession->HttpHeadersInfo.Connection == FALSE) + { + // Gracefully close the connection and set the socket as invalid + if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) + { + HTTPIntrnConnectionClose(pHTTPSession); + } + // Connect to the remote server (or proxy) + nRetCode = HTTPIntrnConnectionOpen(pHTTPSession); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } + + NewConnection = TRUE; + } + + // Analyze the security headers and optionally build the authentication reply header + if((nRetCode = HTTPIntrnParseAuthHeader(pHTTPSession))!= HTTP_CLIENT_SUCCESS) + { + break; + } + // We have to recive any HTML data here inorder to "Clear" the socket buffer for later usage + // Note: We should skip this when the HEAD verb was used + while(NewConnection == FALSE && pHTTPSession->HttpHeaders.HttpLastVerb != VerbHead && pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0 && nBytes > 0) + { + ErrorPage[0] = 0; + if((nRetCode = HTTPIntrnRecv(pHTTPSession,ErrorPage,&nBytes,FALSE)) != HTTP_CLIENT_SUCCESS) + { + break; + } + + nTotalBytes += nBytes; + if(nTotalBytes >= pHTTPSession->HttpHeadersInfo.nHTTPContentLength) + { + break; + } + } + + // Re-Send the headers after having analyzed the authorizaton headers + if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTP_CLIENT_SUCCESS) + { + break; + } + + }while(0); + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnAuthenticate",NULL,0,""); + } +#endif + + return nRetCode; + +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnParseAuthHeader +// Purpose : Parse the HTTP headers for the required authentication method +// Gets : +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnParseAuthHeader(P_HTTP_SESSION pHTTPSession) +{ + + CHAR *pPtrStart, *pPtrEnd; + + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } +#if TLS_CONFIG_HTTP_CLIENT_PROXY + if(pHTTPSession->HttpProxy.ProxyAuthSchema != AuthSchemaNone) + { + // for proxy authentication simply assume basic and exit + return HTTP_CLIENT_SUCCESS; + } +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + // Advance the pointer in the string and break on the first space + pPtrEnd = pHTTPSession->HttpAuthHeader.AuthHeader.pParam + pHTTPSession->HttpAuthHeader.AuthHeader.nLength; + pPtrStart = pHTTPSession->HttpAuthHeader.AuthHeader.pParam; + // Jump to the first space + while ((pPtrEnd - pPtrStart) > 0 && *pPtrStart != 0x20) pPtrStart++; + + do + { +#if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC + if(HTTPStrCaseStr(pPtrStart,8,"basic")) + { + pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA = AuthSchemaBasic; + break; + } +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC +#if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST + if(HTTPStrCaseStr(pPtrStart,8,"digest")) + { + pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA = AuthSchemaDigest; + break; + } +#endif + if(HTTPStrCaseStr(pPtrStart,8,"negotiate")) // Note that this could be NLM negotiation as well (which is not supported) + { + pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA = AuthSchemaKerberos; + break; + } + // To-Do: Add any other supported authentication method + } + while(0); + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnParseAuthHeader",pHTTPSession->HttpAuthHeader.AuthHeader.pParam, + pHTTPSession->HttpAuthHeader.AuthHeader.nLength,"[Incomming Auth Headers: %d]",pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA); + } +#endif + + // If we could not detect the authentication schema return an error + if(pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA == AuthSchemaNone) + { + return HTTP_CLIENT_ERROR_BAD_AUTH; + } + + //Make sure we are going to authenticate with the method specified by the caller + if(pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA != (UINT32)pHTTPSession->HttpCredentials.CredAuthSchema) + { + return HTTP_CLIENT_ERROR_AUTH_MISMATCH; + } + + + return HTTP_CLIENT_SUCCESS; + +} +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnAuthHandler +// Purpose : Differentiate between the authenticate method that we have to implement and perform +// the required operation. +// Gets : +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnAuthHandler (P_HTTP_SESSION pHTTPSession) +{ + + UINT32 nRetCode = HTTP_CLIENT_SUCCESS; + + if(!pHTTPSession) + { + // Bad session pointer error + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } +#if TLS_CONFIG_HTTP_CLIENT_PROXY + if(pHTTPSession->HttpProxy.ProxyAuthSchema != AuthSchemaNone) + { + // For proxy authentication simply assume basic and exit + // Basic authentication + nRetCode = HTTPIntrnAuthSendBasic(pHTTPSession); + return nRetCode; + } +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnAuthHandler",NULL, + 0,""); + } +#endif + + // Use the correct authentication method as requested by the server + switch(pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA) + { +#if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC + case AuthSchemaBasic: + { + // Basic authentication + nRetCode = HTTPIntrnAuthSendBasic(pHTTPSession); + break; + + } +#endif +#if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST + case AuthSchemaDigest: + { + // Digest authentication + nRetCode = HTTPIntrnAuthSendDigest(pHTTPSession); + break; + + } +#endif + case AuthSchemaKerberos: + { + // ToDo: impliament the Kerberos nego authentication here + nRetCode = HTTP_CLIENT_ERROR_NOT_IMPLEMENTED; + break; + + } + default: + { + // Not supported method + return HTTP_CLIENT_ERROR_BAD_AUTH; // Not implemented error + } + + }; + + // This session requested an authentication so.. + pHTTPSession->HttpCredentials.Authentication = TRUE; + return nRetCode; +} + +#if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnAuthSendBasic +// Purpose : Handle basic authentication for direst host connection and proxy authentication +// Gets : +// Returns : +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + + +static UINT32 HTTPIntrnAuthSendBasic (P_HTTP_SESSION pHTTPSession) +{ + + UINT32 nSegmentLength; + UINT32 nRetCode; + CHAR* Cred = NULL; // Credentials (Clear) + CHAR* Cred64 = NULL; // Credentials (64 bit encoded) + UINT32 nSrcLength, nDestLength; + CHAR* pPtr; + CHAR* INITIAL_HDR = "Authorization: Basic "; + CHAR* INITIAL_PROXY_HDR = "Proxy-Authorization: Basic "; + + + do + { + if(!pHTTPSession) + { + nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; + break; + } + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnAuthSendBasic",NULL, + 0,""); + } +#endif + Cred = tls_mem_alloc(HTTP_CLIENT_MAX_64_ENCODED_CRED/2); + if(Cred == NULL) + return HTTP_CLIENT_ERROR_NO_MEMORY; + Cred64 = tls_mem_alloc(HTTP_CLIENT_MAX_64_ENCODED_CRED); + if(Cred64 == NULL) + { + tls_mem_free(Cred); + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + memset(Cred,0x00,HTTP_CLIENT_MAX_64_ENCODED_CRED /2); + memset(Cred64,0x00,HTTP_CLIENT_MAX_64_ENCODED_CRED); + + + switch (pHTTPSession->HttpHeadersInfo.nHTTPStatus) + { + case( HTTP_STATUS_UNAUTHORIZED): // For host authentication + { + + // Copy the clear text credentials to a format of user:password + strcpy(Cred,pHTTPSession->HttpCredentials.CredUser); + strcat(Cred,":"); + strcat(Cred,pHTTPSession->HttpCredentials.CredPassword); + nSrcLength = strlen(Cred); + nDestLength = HTTP_CLIENT_MAX_64_ENCODED_CRED; + nSegmentLength = strlen(INITIAL_HDR); + // Build and send the data first the hard-coded static portion + if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + // Convert to base 64 + HTTPBase64Encoder((unsigned char *)Cred64,(const unsigned char *)Cred,nSrcLength); + nDestLength = strlen(Cred64); + + }; + break; +#if TLS_CONFIG_HTTP_CLIENT_PROXY + case (HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED): // For Proxy authentication + { + // Copy the clear text credentials to a format of user:password + strcpy(Cred,pHTTPSession->HttpProxy.ProxtUser); + strcat(Cred,":"); + strcat(Cred,pHTTPSession->HttpProxy.ProxyPassword); + nSrcLength = strlen(Cred); + nDestLength = HTTP_CLIENT_MAX_64_ENCODED_CRED; + + // Convert to base 64 + HTTPBase64Encoder((unsigned char *)Cred64,(unsigned char *)Cred,nSrcLength); + nDestLength = strlen(Cred64); + nSegmentLength = strlen(INITIAL_PROXY_HDR); + // Build and send the data first the hard-coded static portion + if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_PROXY_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + }; + break; +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + default: + { + tls_mem_free(Cred); + tls_mem_free(Cred64); + return HTTP_CLIENT_ERROR_BAD_AUTH; // Wrong status for this function + }; + }; + + // Send the base 64 encoded data + pPtr = Cred64; + if((nRetCode = HTTPIntrnSend(pHTTPSession,pPtr, &nDestLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nDestLength; + + // Terminating CRLF + nSegmentLength = strlen(HTTP_CLIENT_CRLF); + if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + + } while (0); + if(Cred != NULL) + { + tls_mem_free(Cred); + tls_mem_free(Cred64); + } + return nRetCode; + +} +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC + +#if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnAuthSendDigest +// Purpose : Handle digest authentication for direct host connection and proxy authentication +// Gets : +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnAuthSendDigest (P_HTTP_SESSION pHTTPSession) +{ + CHAR Cnonce[33]; + UINT32 nSegmentLength; + UINT32 nRetCode; + UINT32 nAlgType = 0; // a flag for the algorithem type (default to MD5) + HTTP_PARAM HttpParamOpq,HttpParamRealm,HttpParamNonce,HttpParamQop,HttpParamAlg; // Pointers and lengths of the dynamic sections + // of the Digest response. + + + // Fragments of the Digest client response (The hard coded text portion of the response) + CHAR* INITIAL_HDR = "Authorization: Digest username=\""; + CHAR* INITIAL_PROXY_HDR = "Proxy-Authorization: Digest username=\""; + CHAR* REALEM_HDR = "\", realm=\""; + CHAR* QOP_HDR = "\", qop=\""; + CHAR* ALGO_HDR = "\", algorithm=\""; + CHAR* URI_HDR = "\", uri=\""; + CHAR* NONCE_HDR = "\", nonce=\""; + CHAR* NC_HDR = "\", nc=00000001, cnonce=\""; // To-Do: This should be tested!! + CHAR* RSP_HDR = "\", response=\""; + CHAR* OPQ_HDR = "\", opaque=\""; + // Digest Calculation related + HASHHEX HA1; + HASHHEX HA2 = ""; + HASHHEX Response; + + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnAuthSendDigest",NULL, + 0,""); + } +#endif + + // Generate random Cnonce number + HTTPDigestGenerateCNonce(Cnonce); + + switch (pHTTPSession->HttpHeadersInfo.nHTTPStatus) + { + case( HTTP_STATUS_UNAUTHORIZED): // For host authentication + { + // "Authorization: Digest username=" + nSegmentLength = strlen(INITIAL_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + return nRetCode; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + }; + break; +#if TLS_CONFIG_HTTP_CLIENT_PROXY + case (HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED): // For Proxy authentication + { + // "Proxy-Authorization: Digest username=" + nSegmentLength = strlen(INITIAL_PROXY_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_PROXY_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + return nRetCode; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + }; + break; +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + default: + { + return HTTP_CLIENT_ERROR_BAD_AUTH; // Wrong status for this function + }; + }; + + do + { + + // "Authorization: Digest username="username + nSegmentLength = strlen(pHTTPSession->HttpCredentials.CredUser); + if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpCredentials.CredUser, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + // "Authorization: Digest username="username", realm=" + nSegmentLength = strlen(REALEM_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,REALEM_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + // "Authorization: Digest username="username", realm="realm + if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"realm", &HttpParamRealm)) != HTTP_CLIENT_SUCCESS) + { + break; + } + if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamRealm.pParam, &HttpParamRealm.nLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamRealm.nLength; + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + nSegmentLength = strlen(QOP_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,QOP_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"qop", &HttpParamQop)) != HTTP_CLIENT_SUCCESS) + { + break; + } + if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamQop.pParam, &HttpParamQop.nLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamQop.nLength; + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", + + nSegmentLength = strlen(ALGO_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,ALGO_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"algorithm", &HttpParamAlg)) != HTTP_CLIENT_SUCCESS) + { + + // The server did not state its required algorithm so use the default + HttpParamAlg.pParam = HTTP_CLIENT_DEFAULT_DIGEST_AUTH; + HttpParamAlg.nLength = strlen(HTTP_CLIENT_DEFAULT_DIGEST_AUTH); + } + // Get the algorithem type + if(HTTPStrInsensitiveCompare(HttpParamAlg.pParam ,"md5",3 ) == TRUE) + { + if(HTTPStrInsensitiveCompare(HttpParamAlg.pParam ,"md5-sess", HttpParamAlg.nLength) == TRUE) + { + nAlgType = 1; + } + + } + else + { + // Error algorithem not supported + nRetCode = HTTP_CLIENT_ERROR_NO_DIGEST_ALG; + break; + } + + // Send the algorithem + if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamAlg.pParam, &HttpParamAlg.nLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamAlg.nLength; + + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", uri=" + nSegmentLength = strlen(URI_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,URI_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", uri="/....Service + nSegmentLength = strlen(pHTTPSession->HttpUrl.UrlRequest.pParam); + if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.UrlRequest.pParam, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", uri="/....Service", nonce=" + nSegmentLength = strlen(NONCE_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,NONCE_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", uri="/....Service", nonce="7a5c... + if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"nonce", &HttpParamNonce)) != HTTP_CLIENT_SUCCESS) + { + break; + } + if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamNonce.pParam, &HttpParamNonce.nLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamNonce.nLength; + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce=" + nSegmentLength = strlen(NC_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,NC_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341... + nSegmentLength = strlen(Cnonce); + if((nRetCode = HTTPIntrnSend(pHTTPSession,Cnonce, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + // Send the opaque data if we got it from the server + if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"opaque", &HttpParamOpq)) == HTTP_CLIENT_SUCCESS) + { + + nSegmentLength = strlen(OPQ_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,OPQ_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamOpq.pParam, &HttpParamOpq.nLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamOpq.nLength; + + } + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341...", response=" + nSegmentLength = strlen(RSP_HDR); + if((nRetCode = HTTPIntrnSend(pHTTPSession,RSP_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + // Calculate response + HTTPDigestCalcHA1(nAlgType, pHTTPSession->HttpCredentials.CredUser, + HttpParamRealm.pParam,HttpParamRealm.nLength , + pHTTPSession->HttpCredentials.CredPassword , + HttpParamNonce.pParam, HttpParamNonce.nLength, + Cnonce, HA1); + + HTTPDigestCalcResponse(HA1, + HttpParamNonce.pParam, HttpParamNonce.nLength, + "00000001", Cnonce, + HttpParamQop.pParam,HttpParamQop.nLength, pHTTPSession->HttpHeaders.Verb, + pHTTPSession->HttpUrl.UrlRequest.pParam,pHTTPSession->HttpUrl.UrlRequest.nLength, + HA2, Response); + + // "Authorization: Digest username="username", realm="myRealm", qop="auth", + // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341...", response="8bbf2... + nSegmentLength = strlen(Response); + if((nRetCode = HTTPIntrnSend(pHTTPSession,Response, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + + // Terminate 0x24 (") + nSegmentLength = 1; + if((nRetCode = HTTPIntrnSend(pHTTPSession,"\"", &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + // Terminating CRLF + nSegmentLength = strlen(HTTP_CLIENT_CRLF); + if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the counters + pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; + + } while(0); + + return nRetCode; // End of digest respobse sending +} +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnSessionReset +// Purpose : Reset the session data for the next operation +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnSessionReset (P_HTTP_SESSION pHTTPSession, BOOL EntireSession) +{ + UINT32 nActionTimeout; // For restoring a parameter after this reset + UINT32 nAllocationSize; + + // Validate the pointer + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + pHTTPSession->pDebug("HTTPIntrnSessionReset",NULL, + 0,""); + } +#endif + + + memset(pHTTPSession->HttpHeaders.HeadersIn.pParam,0x00,pHTTPSession->HttpHeaders.HeadersIn.nLength); + pHTTPSession->HttpHeaders.HeadersIn.nLength = 0; + + + // Reset the HTTP counters + nActionTimeout = pHTTPSession->HttpCounters.nActionTimeout; + memset(&pHTTPSession->HttpCounters,0x00,sizeof(HTTP_COUNTERS)); + pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime(); + // Restore the parameter + pHTTPSession->HttpCounters.nActionTimeout = nActionTimeout; + // Reset the authentication flag + pHTTPSession->HttpCredentials.Authentication = FALSE; + + + if(EntireSession == TRUE) // Partial reset, clear only the incoming headers + { + if(pHTTPSession->HttpUrl.Url != NULL) + { + tls_mem_free(pHTTPSession->HttpUrl.Url); + pHTTPSession->HttpUrl.Url = NULL; + } + memset(&pHTTPSession->HttpUrl,0,sizeof(HTTP_URL)); + nAllocationSize = pHTTPSession->HttpHeaders.HeadersBuffer.nLength; + // Reset the headers allocated memory + memset(pHTTPSession->HttpHeaders.HeadersBuffer.pParam ,0x00,nAllocationSize); + + // Set default values in the session structure + HTTPClientSetVerb((UINT32)pHTTPSession,(HTTP_VERB)HTTP_CLIENT_DEFAULT_VERB); // Default HTTP verb + pHTTPSession->HttpUrl.nPort = HTTP_CLIENT_DEFAULT_PORT; // Default TCP port + // Set the outgoing headers pointers + memset(&pHTTPSession->HttpHeaders.HeadersIn,0,sizeof(HTTP_PARAM)); + memset(&pHTTPSession->HttpHeaders.HeadersOut,0,sizeof(HTTP_PARAM)); + + pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam; + // Set our state + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_INIT; + + memset(&pHTTPSession->HttpHeadersInfo,0,sizeof(HTTP_HEADERS_INFO)); + if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) + { + pHTTPSession->HttpHeadersInfo.Connection = TRUE; + } + memset(&pHTTPSession->HttpAuthHeader,0,sizeof(HTTP_AUTH_HEADER)); +#if TLS_CONFIG_HTTP_CLIENT_PROXY + memset(&pHTTPSession->HttpProxy,0,sizeof(HTTP_PROXY)); +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + + } + + return HTTP_CLIENT_SUCCESS; + +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnHeadersReceive +// Purpose : Receives the response header on the connection and parses it. +// Performs any required authentication. +// Returns : HTTP Status +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnHeadersReceive (P_HTTP_SESSION pHTTPSession, + UINT32 nTimeout) // [IN] Timeout for the operation + +{ + + UINT32 nRetCode; // Function call return code + UINT32 nCount = 0; + if(!pHTTPSession) + { + // Bad session pointer error + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + do + { + // Set the operation time out if was set by the caller + if(nTimeout > 0) + { + // 0 makes us use the default defined value + pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout); + + } + // Reset the incoming headers + if((nRetCode = HTTPIntrnSessionReset(pHTTPSession,FALSE)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Get the server response + if((nRetCode = HTTPIntrnGetRemoteHeaders(pHTTPSession)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the session state + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HEADERS_RECIVED; + + // Parse the response headers + if((nRetCode = HTTPIntrnHeadersParse(pHTTPSession)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Set the session state + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HEADERS_PARSED; + + // Set the session stage upon seccess + if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OK) + { + pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HEADERS_OK; + } + // Handle 100 continue message + if(pHTTPSession->HttpHeadersInfo.nHTTPStatus != HTTP_STATUS_CONTINUE) + { + nCount++; + } +#ifdef _HTTP_DEBUGGING_ + if(pHTTPSession->pDebug) + { + if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_CONTINUE) + { + pHTTPSession->pDebug("HTTPIntrnHeadersReceive",NULL,0,"100 Continue Header"); + } + } +#endif + }while(nCount < 1); + + return nRetCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnGetTicks +// Purpose : Like GetTickCount() (implemented with time.h) +// Gets : void +// Returns : System ticks +// Last updated : 01/09/200515/05/2005 +// Author Name : Eitan Michaelson +// Notes : Assuming 1000 ticks per sec, should be implemented by the OS +// +/////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnSessionGetUpTime(VOID) +{ + + return GetUpTime(); + +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPIntrnSessionEvalTimeout +// Purpose : Check if we have to break the operation and return a time out error +// Gets : a pointer to the session structure +// Returns : BOOL, True if we have to break +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +static BOOL HTTPIntrnSessionEvalTimeout(P_HTTP_SESSION pHTTPSession) +{ + + UINT32 nElapsedTime; // Integer for calculating the elapsed time + + // Validate the session pointer + if(!pHTTPSession) + { + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + + // Calculate the elapsed time since the last call + nElapsedTime = HTTPIntrnSessionGetUpTime() - pHTTPSession->HttpCounters.nActionStartTime; + // If the elapsed time is greater then the time out value we should return true + if(nElapsedTime >= pHTTPSession->HttpCounters.nActionTimeout) + { + return TRUE; + } + + return FALSE; +} + +#endif //TLS_CONFIG_HTTP_CLIENT + diff --git a/sdk/Src/App/httpclient/HTTPClient.h b/sdk/Src/App/httpclient/HTTPClient.h new file mode 100644 index 0000000..5437be2 --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClient.h @@ -0,0 +1,318 @@ + +#ifndef _HTTP_CLIENT +#define _HTTP_CLIENT + +#include "wm_config.h" +#include "HTTPClientWrapper.h" +#include "HTTPClientCommon.h" +#include "wm_http_client.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /////////////////////////////////////////////////////////////////////////////// + // + // Section : HTTP API global definitions + // Last updated : 01/09/2005 + // + /////////////////////////////////////////////////////////////////////////////// +#ifdef _HTTP_BUILD_AMT +#define CMSI_HTTPCLIENT_PROTOCOL_GUID {0x471b2c0e, 0x6137, 0x4d55, 0x92, 0x36, 0xdd, 0x0f, 0xdb, 0xc2, 0x52, 0xfb} +#endif + + // Debug hook + // #define _HTTP_DEBUGGING_ // Simply dumps more debugging data to the console + + // API Version +#define HTTP_CLIENT_VERSION_MINOR 0 +#define HTTP_CLIENT_VERSION_MAJOR 1 + + // Global default sizes +#define HTTP_CLIENT_MAX_SEND_RECV_HEADERS 1024 // Maximum Send and receive buffers size +#define HTTP_CLIENT_INIT_SEND_RECV_HEADERS 2048 // If we can resize the buffers this would be the initial size + +#define HTTP_CLIENT_MAX_USERNAME_LENGTH 16 // Maximum length the user name (host and proxy authentication) +#define HTTP_CLIENT_MAX_PASSWORD_LENGTH 16 // Maximum length for the password + // Maximum length for the base 64 encoded credentials (twice the size of the user name and password max parameters) +#define HTTP_CLIENT_MAX_64_ENCODED_CRED ((HTTP_CLIENT_MAX_USERNAME_LENGTH + HTTP_CLIENT_MAX_PASSWORD_LENGTH) * 2) + 4 +#define HTTP_CLIENT_MAX_CHUNK_HEADER 64 // Maximum length for the received chunk header (hex - string) size +#define HTTP_CLIENT_MAX_PROXY_HOST_LENGTH 64 // Maximum length for the proxy host name +#define HTTP_CLIENT_MAX_TOKEN_LENGTH 512 // Maximum length for an HTTP token data (authentication header elements) +#define HTTP_CLIENT_MAX_TOKEN_NAME_LENGTH 32 // Maximum length for an HTTP authorization token name ("qop") +#define HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE 1024 // Maximum length for a search clue string (Headers searching) +#define HTTP_CLIENT_ALLOW_HEAD_VERB 0 // Can we use the HTTP HEAD verb in our outgoing requests? + +#define HTTP_CLIENT_MEMORY_RESIZABLE FALSE // Permission to dynamically resize the headers buffer +#define HTTP_CLIENT_MEMORY_RESIZE_FACTOR 16 // Factor for memory resizing operation + +#define HTTP_CLIENT_DEFAULT_PORT 80 // Default HTTP port +#define HTTP_CLIENT_DEFAULT_SSL_PORT 443 // Default HTTPS port +#define HTTP_CLIENT_DEFAULT_VERB 0 // GET +#define HTTP_CLIENT_DEFAULT_VER "HTTP/1.1" // We will send this in the outgoing header +#define HTTP_CLIENT_DEFAULT_PROXY_VER "HTTP/1.0" // We will send this in the outgoing header (proxy) +#define HTTP_CLIENT_DEFAULT_AGENT "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)" +#define HTTP_CLIENT_DEFAULT_TIMEOUT 30 // Default timeout in seconds +#define HTTP_CLIENT_DEFAULT_KEEP_ALIVE 30 // Default Keep-alive value in seconds +#define HTTP_CLIENT_DEFAULT_DIGEST_AUTH "MD5" // This is for bypassing a known bug in AMT05.. +#define HTTP_CLIENT_DEFAULT_PROXY_AUTH AuthSchemaBasic // Basic + +#define HTTP_CLIENT_CRLF "\r\n" // End of line macro +#define HTTP_CLIENT_CRLFX2 "\r\n\r\n" // Double End of line macro + + // HTTP Session internal API flags + // Note: Not intended to be set the by the API user +#define HTTP_CLIENT_FLAG_SECURE 0x00000010 // The session is secured using TLS +#define HTTP_CLIENT_FLAG_URLANDPORT 0x00000020 // Url has a port within +#define HTTP_CLIENT_FLAG_URLHTTPS 0x00000040 // Url has a https prefix +#define HTTP_CLIENT_FLAG_USINGPROXY 0x00000080 // Operation will be performed using a proxy server +#define HTTP_CLIENT_FLAG_CHUNKED 0x00000100 // The incoming data is chunked + + // HTTP Status codes +#define HTTP_STATUS_OK 200 // The request has succeeded +#define HTTP_STATUS_UNAUTHORIZED 401 // The request requires user authentic +#define HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED 407 // The client must first authenticate itself with the proxy + + // Redirection (Note: there are more 30x codes, those are the most popular) +#define HTTP_STATUS_OBJECT_MOVED 302 // Page redirection notification +#define HTTP_STATUS_OBJECT_MOVED_PERMANENTLY 301 // Page redirection notification +#define HTTP_STATUS_CONTINUE 100 // Page continue message + + + // MIN AMX macro +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + // HTTP timeout macro for selecting the default value if the caller passed 0 (no timeout) to the function +#define HTTP_TIMEOUT(nTimeout) (((nTimeout) > (0)) ? (nTimeout * HZ) : (HTTP_CLIENT_DEFAULT_TIMEOUT * HZ)) + + // 32 bit alignment macro +#define ALIGN(size) ((size & 0xfffffffc) + ((size & 3) ? 4 : 0)) + + +#ifdef _HTTP_DEBUGGING_ + typedef VOID _stdcall E_HTTPDebug(const char *,const char*,UINT32,char *,...); // HTTPDebug hook function +#endif + + + /////////////////////////////////////////////////////////////////////////////// + // + // Section : HTTP API internals structures + // Last updated : 01/09/2005 + // + /////////////////////////////////////////////////////////////////////////////// + + // Generic parameter structure contains a pointer to the buffer and its length + + typedef struct _HTTP_PARAM + { + + CHAR *pParam; + UINT32 nLength; + + } HTTP_PARAM; + + // HTTP socket events + typedef struct _HTTP_CONNECTION + { + + fd_set FDRead; // socket read event + fd_set FDWrite; // socket write event + fd_set FDError; // socket error event + INT32 HttpSocket; // The underling socket + UINT32 HttpStartTime; // Time stamp for the session + UINT32 HttpClientPort; // For client side binding + BOOL TlsNego; // TLS negotiation flag + + } HTTP_CONNECTION; + + // Request URL + typedef struct _HTTP_URL + { + + HTTP_PARAM UrlBsee; // a pointer and length to the "http" section of the URL + HTTP_PARAM UrlHost; // a pointer and length to the host section of the URL + HTTP_PARAM UrlPort; // a pointer and length to the PORT (if was specified section) + HTTP_PARAM UrlRequest; // a pointer and length of the request section of the URL + UINT16 nPort; // the PORT that we should use (could be default or the one found within the URL) + CHAR* Url; // a buffer for the URL + + }HTTP_URL; + // HTTP headers (incoming and outgoing) + typedef struct _HTTP_HEADERS + { + + HTTP_PARAM HeadersBuffer; // a pointer and length of the complete Headers (in\out) buffer + HTTP_PARAM HeadersOut; // a pointer and length of the outgoing HTTP headers + HTTP_PARAM HeadersIn; // a pointer and length of the incoming headers + HTTP_PARAM HeaderSearch; // Index and pointer for the header search functions + HTTP_VERB HttpVerb; // the HTTP verb that was used in the session + HTTP_VERB HttpLastVerb; // the HTTP verb that was last transmited to the server + CHAR *SearchClue; + CHAR Verb[16]; // the actual string buffer of the HTTP verb + + + }HTTP_HEADERS; + + // HTTP headers (parsed headers information) + typedef struct _HTTP_HEADERS_INFO + { + HTTP_PARAM HttpRedirectURL; // Stores the redirection URL if we got a 301 or 303 return code + UINT32 nHTTPStatus; // the HTTP status code (200 401 407 act') + UINT32 nHTTPContentLength; // the Content length if specified of the returned data + UINT32 nHTTPPostContentLength;// the Content-Length of the POSTed data (if known) + BOOL Connection; // True = Keep alive or undefined, False = Closed + BOOL ValidHeaders; // a flag that indicates if the incoming header ware parsed OK and found to be valid + BOOL HaveCredentials; // a flag that indicates if we have credentials for the session + CHAR HTTPVersion[16]; // HTTP version string buffer (for example: "HTTP 1.1") + + }HTTP_HEADERS_INFO; + + // Authentication parameters that ware extracted from the incoming headers + typedef struct _HTTP_AUTH_HEADER + { + + HTTP_PARAM AuthHeader; // the pointer and length of the authentication header + UINT32 HTTP_AUTH_SCHEMA; // Its schema (could be any of the supported) + + }HTTP_AUTH_HEADER; + // Proxy related data + typedef struct _HTTP_PROXY + { + CHAR* ProxyHost; + CHAR* ProxtUser; + CHAR* ProxyPassword; + UINT16 nProxyPort; + CHAR AuthSchemaName[16]; // The authentication schema name (for string comperission) + HTTP_AUTH_SCHEMA ProxyAuthSchema; + + }HTTP_PROXY; + + // HTTP User credentials + typedef struct _HTTP_CREDENTIALS + { + + CHAR* CredUser; + CHAR* CredPassword; + CHAR AuthSchemaName[16]; // The authentication schema name (for string comperission) + HTTP_AUTH_SCHEMA CredAuthSchema; // The schema that calle has selected for the session + BOOL Authentication; // a flag that indicates that this session has requested a user authentication + + }HTTP_CREDENTIALS; + // HTTP Counters + typedef struct _HTTP_COUNTERS + { + + UINT32 nRecivedHeaderLength; // Bytes count of the incoming header + UINT32 nRecivedBodyLength; // Bytes count of the incoming body length + UINT32 nRecivedChunkLength; // The next chunk length in bytes + UINT32 nBytesToNextChunk; // How many bytes we have to read until we can expect the next chunk + UINT32 nActionStartTime; // Operation start time + UINT32 nActionTimeout; // Timeout for the session + UINT32 nSentChunks; // Count of sent chunks + UINT32 nSentBodyBytes; // Count of body bytes that ware sent + UINT32 nSentHeaderBytes; // Count of header bytes thhat ware sent + + }HTTP_COUNTERS; + + // HTTP Client Session data + typedef struct _HTTP_REQUEST + { + + HTTP_URL HttpUrl; + HTTP_HEADERS HttpHeaders; + HTTP_HEADERS_INFO HttpHeadersInfo; + HTTP_AUTH_HEADER HttpAuthHeader; + HTTP_PROXY HttpProxy; + HTTP_CREDENTIALS HttpCredentials; + HTTP_CONNECTION HttpConnection; + HTTP_COUNTERS HttpCounters; + UINT32 HttpState; + UINT32 HttpFlags; +#ifdef _HTTP_DEBUGGING_ + E_HTTPDebug *pDebug; +#endif +#if TLS_CONFIG_HTTP_CLIENT_SECURE + tls_ssl_t *ssl; + int ssl_more_data; +#endif + } HTTP_SESSION, *P_HTTP_SESSION; + + + /***********move to wm_http_client.h************** + // HTTP Type Definitions + typedef UINT32 HTTP_SESSION_HANDLE; + typedef UINT32 HTTP_CLIENT_SESSION_FLAGS; + */ + typedef tls_http_session_handle_t HTTP_SESSION_HANDLE; + typedef tls_http_session_flags_t HTTP_CLIENT_SESSION_FLAGS; + + /////////////////////////////////////////////////////////////////////////////// + // + // Section : HTTP API public interface + // Last updated : 01/09/2005 + // + /////////////////////////////////////////////////////////////////////////////// + + /***********move to wm_http_client.h************** + HTTP_SESSION_HANDLE HTTPClientOpenRequest (HTTP_CLIENT_SESSION_FLAGS Flags); + UINT32 HTTPClientCloseRequest (HTTP_SESSION_HANDLE *pSession); + UINT32 HTTPClientSetAuth (HTTP_SESSION_HANDLE pSession, HTTP_AUTH_SCHEMA AuthSchema, void *pReserved); + UINT32 HTTPClientSetCredentials (HTTP_SESSION_HANDLE pSession, CHAR *pUserName, CHAR *pPassword); + UINT32 HTTPClientSetProxy (HTTP_SESSION_HANDLE pSession, CHAR *pProxyName, UINT16 nPort, CHAR *pUserName, CHAR *pPassword); + UINT32 HTTPClientSetVerb (HTTP_SESSION_HANDLE pSession, HTTP_VERB HttpVerb); + UINT32 HTTPClientAddRequestHeaders (HTTP_SESSION_HANDLE pSession, CHAR *pHeaderName, CHAR *pHeaderData, BOOL nInsert); + UINT32 HTTPClientSendRequest (HTTP_SESSION_HANDLE pSession, CHAR *pUrl, VOID *pData, UINT32 nDataLength, BOOL TotalLength, UINT32 nTimeout,UINT32 nClientPort); + UINT32 HTTPClientWriteData (HTTP_SESSION_HANDLE pSession, VOID *pBuffer, UINT32 nBufferLength, UINT32 nTimeout); + UINT32 HTTPClientRecvResponse (HTTP_SESSION_HANDLE pSession, UINT32 nTimeout); + UINT32 HTTPClientReadData (HTTP_SESSION_HANDLE pSession, VOID *pBuffer, UINT32 nBytesToRead, UINT32 nTimeout, UINT32 *nBytesRecived); + UINT32 HTTPClientGetInfo (HTTP_SESSION_HANDLE pSession, HTTP_CLIENT *HTTPClient); + + UINT32 HTTPClientFindFirstHeader (HTTP_SESSION_HANDLE pSession, CHAR *pSearchClue,CHAR *pHeaderBuffer, UINT32 *nLength); + UINT32 HTTPClientGetNextHeader (HTTP_SESSION_HANDLE pSession, CHAR *pHeaderBuffer, UINT32 *nLength); + UINT32 HTTPClientFindCloseHeader (HTTP_SESSION_HANDLE pSession); + */ + UINT32 HTTPClientSetLocalConnection (HTTP_SESSION_HANDLE pSession, UINT32 nPort); +#ifdef _HTTP_DEBUGGING_ + UINT32 HTTPClientSetDebugHook (HTTP_SESSION_HANDLE pSession,E_HTTPDebug *pDebug); +#endif + + + /////////////////////////////////////////////////////////////////////////////// + // + // Section : HTTP API private function + // Last updated : 01/09/2005 + // + /////////////////////////////////////////////////////////////////////////////// + +static UINT32 HTTPIntrnResizeBuffer (P_HTTP_SESSION pHTTPSession, UINT32 nNewSize); +static UINT32 HTTPIntrnSetURL (P_HTTP_SESSION pHTTPSession, CHAR *pUrl,UINT32 nUrlLength); +static UINT32 HTTPIntrnConnectionClose (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnConnectionOpen (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnGetRemoteHeaders (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnGetRemoteChunkLength (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnSend (P_HTTP_SESSION pHTTPSession, CHAR *pData,UINT32 *nLength); +static UINT32 HTTPIntrnRecv (P_HTTP_SESSION pHTTPSession, CHAR *pData,UINT32 *nLength,BOOL PeekOnly); +static UINT32 HTTPIntrnParseAuthHeader (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnAuthHandler (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnAuthSendDigest (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnAuthSendBasic (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnAuthenticate (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnHeadersAdd (P_HTTP_SESSION pHTTPSession, CHAR *pHeaderName, UINT32 nNameLength, CHAR *pHeaderData, UINT32 nDataLength); +static UINT32 HTTPIntrnHeadersRemove (P_HTTP_SESSION pHTTPSession, CHAR *pHeaderName); +static UINT32 HTTPIntrnHeadersReceive (P_HTTP_SESSION pHTTPSession, UINT32 nTimeout); +static UINT32 HTTPIntrnHeadersSend (P_HTTP_SESSION pHTTPSession, HTTP_VERB HttpVerb); +static UINT32 HTTPIntrnHeadersParse (P_HTTP_SESSION pHTTPSession); +static UINT32 HTTPIntrnHeadersFind (P_HTTP_SESSION pHTTPSession, CHAR *pHeaderName, HTTP_PARAM *pParam,BOOL IncommingHeaders,UINT32 nOffset); +static UINT32 HTTPIntrnSessionReset (P_HTTP_SESSION pHTTPSession, BOOL EntireSession); +static UINT32 HTTPIntrnSessionGetUpTime (VOID); +static BOOL HTTPIntrnSessionEvalTimeout (P_HTTP_SESSION pHTTPSession); + +#ifdef __cplusplus +} +#endif + +#endif //_HTTP_CLIENT diff --git a/sdk/Src/App/httpclient/HTTPClientAuth.c b/sdk/Src/App/httpclient/HTTPClientAuth.c new file mode 100644 index 0000000..4546d6c --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClientAuth.c @@ -0,0 +1,673 @@ + +/////////////////////////////////////////////////////////////////////////////// +// Module Name: +// HTTPClientAuth.c +// +// Abstract: Handle Digest, MD5 and 64 Bit Encoding +// +// Platform: Any that supports standard C calls +/////////////////////////////////////////////////////////////////////////////// +#include "wm_config.h" +#include "wm_mem.h" +#include "HTTPClientAuth.h" + +#if TLS_CONFIG_HTTP_CLIENT + +#if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPBase64Encoder +// Purpose : Converts a given string into a base64 encoded buffer. +// Last updated : 01/09/200515/05/2005 +// Author Name : Eitan Michaelson +// Notes : +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HTTPBase64Encoder(unsigned char *out, const unsigned char *in, int inlen) +// [OUT] out A pointer to a char to hold the converted string +// [IN] in String to convert +// [IN] inlen Length of the string to be converted + +{ + for (; inlen >= 3; inlen -= 3) + { + *out++ = base64digits[in[0] >> 2]; + *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *out++ = base64digits[in[2] & 0x3f]; + in += 3; + } + + if (inlen > 0) + { + unsigned char fragment; + + *out++ = base64digits[in[0] >> 2]; + fragment = (in[0] << 4) & 0x30; + + if (inlen > 1) + fragment |= in[1] >> 4; + + *out++ = base64digits[fragment]; + *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c]; + *out++ = '='; + } + + *out = '\0'; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPBase64Decoder +// Purpose : Converts a given base64 string into a bytes buffer. +// Last updated : 01/09/200515/05/2005 +// Author Name : Eitan Michaelson +// Notes : +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HTTPBase64Decoder(char *out, const char *in) +{ // [OUT] out Where to save the converted string + // [IN] in String to convert + + int len = 0; + register unsigned char digit1, digit2, digit3, digit4; + + if (in[0] == '+' && in[1] == ' ') + in += 2; + if (*in == '\r') + return(0); + + do { + + digit1 = in[0]; + if (DECODE64(digit1) == BAD) + return(-1); + digit2 = in[1]; + if (DECODE64(digit2) == BAD) + return(-1); + digit3 = in[2]; + if (digit3 != '=' && DECODE64(digit3) == BAD) + return(-1); + digit4 = in[3]; + if (digit4 != '=' && DECODE64(digit4) == BAD) + return(-1); + in += 4; + *out++ = (DECODE64(digit1) << 2) | (DECODE64(digit2) >> 4); + ++len; + if (digit3 != '=') + { + *out++ = ((DECODE64(digit2) << 4) & 0xf0) | (DECODE64(digit3) >> 2); + ++len; + if (digit4 != '=') + { + *out++ = ((DECODE64(digit3) << 6) & 0xc0) | DECODE64(digit4); + ++len; + } + } + } while (*in && *in != '\r' && digit4 != '='); + + return (len); +} +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC + +#if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Purpose : The following code implements the calculations of H(A1), H(A2), +// request-digest and response-digest +// Last updated : 01/09/200515/05/2005 +// Author Name : Public Domain\RFC2617 +// Notes : Digest Access Authentication +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : GenerateCNonce +// Purpose : Generates a 32 byte random hexadecimal string such as "4f6ba982..." +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HTTPDigestGenerateCNonce(char *outbuff) +{ + int i,num; + InitRandomeNumber(); + for(i = 0; i < 32; i++) { + num = GetRandomeNumber(); + switch(num) { + case 0: case 1: case 2: case 3: case 4: case 5: + case 6: case 7: case 8: case 9: + outbuff[i] = '0' + num; + break; + case 10: case 11: case 12: case 13: case 14: case 15: + outbuff[i] = 'a' + (num-10); + break; + default: + outbuff[i] = 'f'; + } + } + outbuff[32] = 0; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : DigestCvtHex +// Purpose : CConvert to HEX +// Last updated : 15/05/2005 +// Author Name : Public Domain\RFC2617 +// Notes : +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +void HTTPDigestCvtHex(IN HASH Bin,OUT HASHHEX Hex) +{ + unsigned short i; + unsigned char j; + + for (i = 0; i < HASHLEN; i++) { + j = (Bin[i] >> 4) & 0xf; + if (j <= 9) + Hex[i*2] = (j + '0'); + else + Hex[i*2] = (j + 'a' - 10); + j = Bin[i] & 0xf; + if (j <= 9) + Hex[i*2+1] = (j + '0'); + else + Hex[i*2+1] = (j + 'a' - 10); + }; + Hex[HASHHEXLEN] = '\0'; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : DigestCalcHA1 +// Purpose : Calculate H(A1) as per spec +// Last updated : 15/05/2005 +// Author Name : Public Domain\RFC2617 +// Notes : +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HTTPDigestCalcHA1( + IN int nAlg, /* 0 = MD5, 1 = MD5-Sess */ + IN char * pszUserName, + IN char * pszRealm, + IN int nRealmLength, + IN char * pszPassword, + IN char * pszNonce, + IN int nNonceLength, + IN char * pszCNonce, + OUT HASHHEX SessionKey + ) +{ + MD5_CTX Md5Ctx; + HASH HA1; + HASHHEX HASess; + HASH HAll; + + HTTPMD5Init(&Md5Ctx); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszUserName, strlen(pszUserName)); //Daniel casting + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszRealm, nRealmLength); //Daniel + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszPassword, strlen(pszPassword)); //Daniel + HTTPMD5Final((unsigned char *)HA1, &Md5Ctx); + + if (nAlg == 1) /* MD5-Sess */ + { + HTTPDigestCvtHex(HA1, HASess); + HTTPMD5Init(&Md5Ctx); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)HASess, HASHHEXLEN); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszNonce, nNonceLength); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszCNonce, strlen(pszCNonce)); + HTTPMD5Final((unsigned char *)HAll, &Md5Ctx); + HTTPDigestCvtHex(HAll, SessionKey); + return; + + } + + HTTPDigestCvtHex(HA1, SessionKey); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : DigestCalcResponse +// Purpose : Calculate request-digest/response-digest as per HTTP Digest spec +// Last updated : 15/05/2005 +// Author Name : Public Domain\RFC2617 +// Notes : +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HTTPDigestCalcResponse( + IN HASHHEX HA1, // H(A1) + IN char * pszNonce, // nonce from server + IN int nNonceLength, // Length of nonce + IN char * pszNonceCount, // 8 hex digits + IN char * pszCNonce, // client nonce */ + IN char * pszQop, // qop-value: "", "auth", "auth-int" + IN int nQopLength, // qop param length + IN char * pszMethod, // method from the request + IN char * pszDigestUri, // requested URL + IN int nDigestUriLebgth, // Uri Length + IN HASHHEX HEntity, // H(entity body) if qop="auth-int" + OUT HASHHEX Response // request-digest or response-digest + ) +{ + MD5_CTX Md5Ctx; + HASH HA2; + HASH RespHash; + HASHHEX HA2Hex; + + // Calculate H(A2) + HTTPMD5Init(&Md5Ctx); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszMethod, strlen(pszMethod)); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszDigestUri, nDigestUriLebgth); + if (stricmp(pszQop, "auth-int") == 0) + { + + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)HEntity, HASHHEXLEN); + }; + HTTPMD5Final((unsigned char *)HA2, &Md5Ctx); + HTTPDigestCvtHex(HA2, HA2Hex); + + // Calculate response + HTTPMD5Init(&Md5Ctx); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)HA1, HASHHEXLEN); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszNonce, nNonceLength); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + if (*pszQop) + { + + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszNonceCount, strlen(pszNonceCount)); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszCNonce, strlen(pszCNonce)); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)pszQop, nQopLength); + HTTPMD5Update(&Md5Ctx, (const unsigned char *)":", 1); + }; + HTTPMD5Update(&Md5Ctx, (const unsigned char *)HA2Hex, HASHHEXLEN); + HTTPMD5Final((unsigned char *)RespHash, &Md5Ctx); + HTTPDigestCvtHex(RespHash, Response); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Purpose : This code implements the MD5 message-digest algorithm. +// The algorithm is due to Ron Rivest. This code was +// written by Colin Plumb in 1993, no copyright is claimed. +// This code is in the public domain; do with it what you wish. +// Equivalent code is available from RSA Data Security, Inc. +// This code has been tested against that, and is equivalent, +// except that you don't need to include two pages of legalese +// with every copy. +// Usage : To compute the message digest of a chunk of bytes, declare an +// MD5Context structure, pass it to MD5Init, call MD5Update as +// needed on buffers full of bytes, and then call MD5Final, which +// will fill a supplied 16-byte array with the digest. +// Last updated : 15/05/2005 +// Author Name : Public Domain +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +#ifndef HIGHFIRST +#define HTTPMD5ByteReverse(buf, len) /* Nothing */ +#else +void HTTPMD5ByteReverse(unsigned char *buf, unsigned longs); +#ifndef ASM_MD5 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : ByteReverse +// Purpose : Little\Big Endian support +// Gets : +// Returns : +// Last updated : 15/05/2005 +// Author Name : Public Domain +// Notes : this code is harmless on little-endian machines. +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HTTPMD5ByteReverse(unsigned char *buf, unsigned longs) +{ + UINT32 t; + do { + t = (UINT32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(UINT32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : MD5Init +// Purpose : Initialize the MD5Context structure +// Gets : MD5Context structure +// Returns : +// Last updated : 15/05/2005 +// Author Name : Public Domain +// Notes : Start MD5 accumulation. Set bit count to 0 and buffer to mysterious +// initialization constants. +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +void HTTPMD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : MD5Update +// Purpose : Update the MD5Context structure with the target byte array +// Gets : MD5Context structure, buffer and length +// Returns : +// Last updated : 15/05/2005 +// Author Name : Public Domain +// Notes : Update context to reflect the concatenation of another buffer full of bytes. +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HTTPMD5Update(struct MD5Context *ctx, UCHAR const *buf, unsigned len) +{ + UINT32 t; + + // Update bitcount + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((UINT32) len << 3)) < t) + ctx->bits[1]++; // Carry from low to high + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; // Bytes already in shsInfo->data + + // Handle any leading odd-sized chunks + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + MEMCPY(p, buf, len); + return; + } + MEMCPY(p, buf, t); + HTTPMD5ByteReverse(ctx->in, 16); + HTTPMD5Transform(ctx->buf, (UINT32 *) ctx->in); + buf += t; + len -= t; + } + // Process data in 64-byte chunks + + while (len >= 64) { + MEMCPY(ctx->in, buf, 64); + HTTPMD5ByteReverse(ctx->in, 16); + HTTPMD5Transform(ctx->buf, (UINT32 *) ctx->in); + buf += 64; + len -= 64; + } + + // Handle any remaining bytes of data. + + MEMCPY(ctx->in, buf, len); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : MD5Final +// Purpose : Finalize. +// Gets : Output digest structure, MD5Context structure +// Returns : +// Last updated : 15/05/2005 +// Author Name : Public Domain +// Notes : Final wrapup - pad to 64-byte boundary with the bit pattern +// 1 0* (64-bit count of bits processed, MSB-first). +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + + +void HTTPMD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + // Compute number of bytes mod 64 + count = (ctx->bits[0] >> 3) & 0x3F; + + // Set the first char of padding to 0x80. This is safe since there is + // always at least one byte free + p = ctx->in + count; + *p++ = 0x80; + + // Bytes of padding needed to make 64 bytes + count = 64 - 1 - count; + + // Pad out to 56 mod 64 */ + if (count < 8) { + // Two lots of padding: Pad the first block to 64 bytes + memset(p, 0, count); + HTTPMD5ByteReverse(ctx->in, 16); + HTTPMD5Transform(ctx->buf, (UINT32 *) ctx->in); + + // Now fill the next block with 56 bytes + memset(ctx->in, 0, 56); + } else { + // Pad block to 56 bytes + memset(p, 0, count - 8); + } + HTTPMD5ByteReverse(ctx->in, 14); + + // Append length in bits and transform + ((UINT32 *) ctx->in)[14] = ctx->bits[0]; + ((UINT32 *) ctx->in)[15] = ctx->bits[1]; + + HTTPMD5Transform(ctx->buf, (UINT32 *) ctx->in); + HTTPMD5ByteReverse((unsigned char *) ctx->buf, 4); + MEMCPY(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); // In case it's sensitive +} + +#ifndef ASM_MD5 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : +// Purpose : The four core functions - F1 is optimized somewhat +// Last updated : 15/05/2005 +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +// This is the central step in the MD5 algorithm. +#ifdef __PUREC__ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f /*(x, y, z)*/ + data, w = w<>(32-s), w += x ) +#else +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Function : MD5Transform +// Purpose : The core of the MD5 algorithm, this alters an existing MD5 hash to +// reflect the addition of 16 longwords of new data. MD5Update blocks +// the data and converts bytes into longwords for this routine. +// Last updated : 15/05/2005 +// Author Name : Public Domain +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +void HTTPMD5Transform(UINT32 buf[4], UINT32 const in[16]) +{ + register UINT32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + +#ifdef __PUREC__ // PureC Weirdness... (GG) + MD5STEP(F1(b,c,d), a, b, c, d, in[0] + 0xd76aa478L, 7); + MD5STEP(F1(a,b,c), d, a, b, c, in[1] + 0xe8c7b756L, 12); + MD5STEP(F1(d,a,b), c, d, a, b, in[2] + 0x242070dbL, 17); + MD5STEP(F1(c,d,a), b, c, d, a, in[3] + 0xc1bdceeeL, 22); + MD5STEP(F1(b,c,d), a, b, c, d, in[4] + 0xf57c0fafL, 7); + MD5STEP(F1(a,b,c), d, a, b, c, in[5] + 0x4787c62aL, 12); + MD5STEP(F1(d,a,b), c, d, a, b, in[6] + 0xa8304613L, 17); + MD5STEP(F1(c,d,a), b, c, d, a, in[7] + 0xfd469501L, 22); + MD5STEP(F1(b,c,d), a, b, c, d, in[8] + 0x698098d8L, 7); + MD5STEP(F1(a,b,c), d, a, b, c, in[9] + 0x8b44f7afL, 12); + MD5STEP(F1(d,a,b), c, d, a, b, in[10] + 0xffff5bb1L, 17); + MD5STEP(F1(c,d,a), b, c, d, a, in[11] + 0x895cd7beL, 22); + MD5STEP(F1(b,c,d), a, b, c, d, in[12] + 0x6b901122L, 7); + MD5STEP(F1(a,b,c), d, a, b, c, in[13] + 0xfd987193L, 12); + MD5STEP(F1(d,a,b), c, d, a, b, in[14] + 0xa679438eL, 17); + MD5STEP(F1(c,d,a), b, c, d, a, in[15] + 0x49b40821L, 22); + + MD5STEP(F2(b,c,d), a, b, c, d, in[1] + 0xf61e2562L, 5); + MD5STEP(F2(a,b,c), d, a, b, c, in[6] + 0xc040b340L, 9); + MD5STEP(F2(d,a,b), c, d, a, b, in[11] + 0x265e5a51L, 14); + MD5STEP(F2(c,d,a), b, c, d, a, in[0] + 0xe9b6c7aaL, 20); + MD5STEP(F2(b,c,d), a, b, c, d, in[5] + 0xd62f105dL, 5); + MD5STEP(F2(a,b,c), d, a, b, c, in[10] + 0x02441453L, 9); + MD5STEP(F2(d,a,b), c, d, a, b, in[15] + 0xd8a1e681L, 14); + MD5STEP(F2(c,d,a), b, c, d, a, in[4] + 0xe7d3fbc8L, 20); + MD5STEP(F2(b,c,d), a, b, c, d, in[9] + 0x21e1cde6L, 5); + MD5STEP(F2(a,b,c), d, a, b, c, in[14] + 0xc33707d6L, 9); + MD5STEP(F2(d,a,b), c, d, a, b, in[3] + 0xf4d50d87L, 14); + MD5STEP(F2(c,d,a), b, c, d, a, in[8] + 0x455a14edL, 20); + MD5STEP(F2(b,c,d), a, b, c, d, in[13] + 0xa9e3e905L, 5); + MD5STEP(F2(a,b,c), d, a, b, c, in[2] + 0xfcefa3f8L, 9); + MD5STEP(F2(d,a,b), c, d, a, b, in[7] + 0x676f02d9L, 14); + MD5STEP(F2(c,d,a), b, c, d, a, in[12] + 0x8d2a4c8aL, 20); + + MD5STEP(F3(b,c,d), a, b, c, d, in[5] + 0xfffa3942L, 4); + MD5STEP(F3(a,b,c), d, a, b, c, in[8] + 0x8771f681L, 11); + MD5STEP(F3(d,a,b), c, d, a, b, in[11] + 0x6d9d6122L, 16); + MD5STEP(F3(c,d,a), b, c, d, a, in[14] + 0xfde5380cL, 23); + MD5STEP(F3(b,c,d), a, b, c, d, in[1] + 0xa4beea44L, 4); + MD5STEP(F3(a,b,c), d, a, b, c, in[4] + 0x4bdecfa9L, 11); + MD5STEP(F3(d,a,b), c, d, a, b, in[7] + 0xf6bb4b60L, 16); + MD5STEP(F3(c,d,a), b, c, d, a, in[10] + 0xbebfbc70L, 23); + MD5STEP(F3(b,c,d), a, b, c, d, in[13] + 0x289b7ec6L, 4); + MD5STEP(F3(a,b,c), d, a, b, c, in[0] + 0xeaa127faL, 11); + MD5STEP(F3(d,a,b), c, d, a, b, in[3] + 0xd4ef3085L, 16); + MD5STEP(F3(c,d,a), b, c, d, a, in[6] + 0x04881d05L, 23); + MD5STEP(F3(b,c,d), a, b, c, d, in[9] + 0xd9d4d039L, 4); + MD5STEP(F3(a,b,c), d, a, b, c, in[12] + 0xe6db99e5L, 11); + MD5STEP(F3(d,a,b), c, d, a, b, in[15] + 0x1fa27cf8L, 16); + MD5STEP(F3(c,d,a), b, c, d, a, in[2] + 0xc4ac5665L, 23); + + MD5STEP(F4(b,c,d), a, b, c, d, in[0] + 0xf4292244L, 6); + MD5STEP(F4(a,b,c), d, a, b, c, in[7] + 0x432aff97L, 10); + MD5STEP(F4(d,a,b), c, d, a, b, in[14] + 0xab9423a7L, 15); + MD5STEP(F4(c,d,a), b, c, d, a, in[5] + 0xfc93a039L, 21); + MD5STEP(F4(b,c,d), a, b, c, d, in[12] + 0x655b59c3L, 6); + MD5STEP(F4(a,b,c), d, a, b, c, in[3] + 0x8f0ccc92L, 10); + MD5STEP(F4(d,a,b), c, d, a, b, in[10] + 0xffeff47dL, 15); + MD5STEP(F4(c,d,a), b, c, d, a, in[1] + 0x85845dd1L, 21); + MD5STEP(F4(b,c,d), a, b, c, d, in[8] + 0x6fa87e4fL, 6); + MD5STEP(F4(a,b,c), d, a, b, c, in[15] + 0xfe2ce6e0L, 10); + MD5STEP(F4(d,a,b), c, d, a, b, in[6] + 0xa3014314L, 15); + MD5STEP(F4(c,d,a), b, c, d, a, in[13] + 0x4e0811a1L, 21); + MD5STEP(F4(b,c,d), a, b, c, d, in[4] + 0xf7537e82L, 6); + MD5STEP(F4(a,b,c), d, a, b, c, in[11] + 0xbd3af235L, 10); + MD5STEP(F4(d,a,b), c, d, a, b, in[2] + 0x2ad7d2bbL, 15); + MD5STEP(F4(c,d,a), b, c, d, a, in[9] + 0xeb86d391L, 21); +#else + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); +#endif + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif + +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST + +#endif //TLS_CONFIG_HTTP_CLIENT + diff --git a/sdk/Src/App/httpclient/HTTPClientAuth.h b/sdk/Src/App/httpclient/HTTPClientAuth.h new file mode 100644 index 0000000..2232aec --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClientAuth.h @@ -0,0 +1,93 @@ + +#ifndef HTTP_CLIENT_AUTH_H +#define HTTP_CLIENT_AUTH_H + +#include "HTTPClientString.h" // Cross platform support +#if TLS_CONFIG_HTTP_CLIENT_AUTH +#include "md5.h" +#endif +#define HASHLEN 16 +#define HASHHEXLEN 32 +#define IN +#define OUT + +typedef char HASH[HASHLEN]; +typedef char HASHHEX[HASHHEXLEN+1]; +//typedef unsigned long uint32; +#undef isascii +#define isascii(_Char) ( (unsigned)(_Char) < 0x80 ) +// Base 64 Related +#define DECODE64(c) (isascii(c) ? base64val[c] : BAD) +#define BAD 0xFF //-1 + +static const char base64digits[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const char base64val[] = { +BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, +BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, +BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD, 62, BAD,BAD,BAD, 63, +52, 53, 54, 55, 56, 57, 58, 59, 60, 61,BAD,BAD, BAD,BAD,BAD,BAD, +BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,BAD, BAD,BAD,BAD,BAD, +BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, +41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,BAD, BAD,BAD,BAD,BAD +}; + +void HTTPBase64Encoder(unsigned char *out, const unsigned char *in, int inlen); +int HTTPBase64Decoder(char *out, const char *in); + + +// Digest Related +// Generates a 32 byte random hexadecimal string such as "4f6ba982..." +void HTTPDigestGenerateCNonce(char *outbuff); + +// Calculate H(A1) as per HTTP Digest spec +void HTTPDigestCalcHA1( + IN int nAlg, /* 0 = MD5, 1 = MD5-Sess */ + IN char * pszUserName, + IN char * pszRealm, + IN int nRealmLength, + IN char * pszPassword, + IN char * pszNonce, + IN int nNonceLength, + IN char * pszCNonce, + OUT HASHHEX SessionKey + ); + +// Calculate request-digest/response-digest as per HTTP Digest spec +void HTTPDigestCalcResponse( + IN HASHHEX HA1, // H(A1) + IN char * pszNonce, // nonce from server + IN int nNonceLength, // Length of nonce + IN char * pszNonceCount, // 8 hex digits + IN char * pszCNonce, // client nonce + IN char * pszQop, // qop-value: "", "auth", "auth-int" + IN int nQopLength, // qop param length + IN char * pszMethod, // method from the request + IN char * pszDigestUri, // requested URL + IN int nDigestUriLebgth, // Uri Length + IN HASHHEX HEntity, // H(entity body) if qop="auth-int" + OUT HASHHEX Response // request-digest or response-digest + ); + +// MD5 structures and functions +/*struct MD5Context +{ + u32 buf[4]; + u32 bits[2]; + unsigned char in[64]; +}; +*/ +#if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST +void HTTPMD5Init (struct MD5Context *context); +void HTTPMD5Update (struct MD5Context *context, unsigned char const *buf,unsigned len); +void HTTPMD5Final (unsigned char digest[16], struct MD5Context *context); +void HTTPMD5Transform (u32 buf[4], u32 const in[16]); +#endif + +// This is needed to make RSAREF happy on some MS-DOS compilers. +typedef struct MD5Context MD5_CTX; + +#endif + diff --git a/sdk/Src/App/httpclient/HTTPClientCommon.h b/sdk/Src/App/httpclient/HTTPClientCommon.h new file mode 100644 index 0000000..7857e51 --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClientCommon.h @@ -0,0 +1,42 @@ + +/////////////////////////////////////////////////////////////////////////////// +// +// Module Name: +// CmsiHTTPClientCommon.h +// +// Abstract: Coomon structs and types for the HTTP protocol API +// Author: Eitan Michaelso +// Version: 1.0 +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _HTTPCLIENT_PROTOCOL_H_ +#define _HTTPCLIENT_PROTOCOL_H_ +#include "wm_type_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + + // Global default sizes +#define HTTP_CLIENT_MAX_URL_LENGTH 512 // Maximum length for an HTTP Url parameter + + // HTTP Session flags (Public flags) +#define HTTP_CLIENT_FLAG_KEEP_ALIVE 0x00000001 // Set the keep alive header +#define HTTP_CLIENT_FLAG_SEND_CHUNKED 0x00000002 // The outgoing should chunked +#define HTTP_CLIENT_FLAG_NO_CACHE 0x00000004 // Set the no cache header +#define HTTP_CLIENT_FLAG_ASYNC 0x00000008 // Currently not implemented + + // HTTP status internal flags +#define HTTP_CLIENT_STATE_PRE_INIT 0x00000000 // Starting stage +#define HTTP_CLIENT_STATE_INIT 0x00000001 // API was initialized (memory was allocated) +#define HTTP_CLIENT_STATE_URL_PARSED 0x00000002 // Url was parsed +#define HTTP_CLIENT_STATE_HOST_CONNECTED 0x00000004 // HEAD verb was sent +#define HTTP_CLIENT_STATE_HEAD_SENT 0x00000008 // Post verb was sent +#define HTTP_CLIENT_STATE_POST_SENT 0x00000010 // HTTP requet was sent +#define HTTP_CLIENT_STATE_REQUEST_SENT 0x00000020 // HTTP request was sent +#define HTTP_CLIENT_STATE_HEADERS_RECIVED 0x00000040 // Headers ware recived from the server +#define HTTP_CLIENT_STATE_HEADERS_PARSED 0x00000080 // HTTP headers ware parsed +#define HTTP_CLIENT_STATE_HEADERS_OK 0x00000100 // Headers status was OK + +#endif // _HTTPCLIENT_PROTOCOL_H_ diff --git a/sdk/Src/App/httpclient/HTTPClientString.c b/sdk/Src/App/httpclient/HTTPClientString.c new file mode 100644 index 0000000..52c6375 --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClientString.c @@ -0,0 +1,433 @@ + +/////////////////////////////////////////////////////////////////////////////// +// +// Module Name: +// HTTPClientString.c +// +// Abstract: Helper function (string parsing related) for HTTPClient.c module +// +// Platform: Any that supports standard C calls +// +/////////////////////////////////////////////////////////////////////////////// +#include "wm_config.h" +#include "HTTPClient.h" +#include "HTTPClientWrapper.h" // Cross platform support + +#if TLS_CONFIG_HTTP_CLIENT +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPStrInsensitiveCompare +// Purpose : Same as strcmp() only case insensitive +// Returns : BOOL - TRUE if destination string is identical to the source +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +BOOL HTTPStrInsensitiveCompare(const CHAR *pSrc, // [IN] a pointer to the source string + const CHAR* pDest, // [IN] a pointer to the string we should search for + UINT32 nLength) // [IN] The bytes range we should search in +{ + + // Lower case comparison + UINT32 nPosition; + UINT32 nDestLength; + const CHAR *pSrcIn, *pDestIn; + CHAR a,b; + pSrcIn = pSrc; + pDestIn = pDest; + + nPosition = 0; + nDestLength = strlen(pDest); + + if(nLength == 0) + { + nLength = strlen(pSrc); + } + if(nDestLength != nLength) + { + return FALSE; + } + + while(pSrcIn || pDestIn) + { + + if(nLength > 0 && nPosition == nLength) + { + return TRUE; + } + + a = *pSrcIn; + b = *pDestIn; + + + if(*pSrcIn >= 64 && *pSrcIn <= 90) + { + // Upper case to lower case + a = *pSrcIn + 32; + } + + if(*pDestIn >= 64 && *pDestIn <= 90) + { + // Upper case to lower case + b = *pDestIn + 32; + } + + if(a != b) + { + return FALSE; + } + + pSrcIn++; + pDestIn++; + nPosition++; + } + return TRUE; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPStrExtract +// Purpose : Extract a string by placing null in the offset parameter +// Returns : a pointer to the new string +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +CHAR HTTPStrExtract(CHAR *pParam, // [IN] a pointer to the input parameter + UINT32 nOffset, // [IN] the offset position (where we should null terminate the string) + CHAR Restore) // [IN] if this is not 0 we should restore it (instead of the null) + // and reverse the effect. +{ + CHAR Replaced; + + if(!pParam) + { + return 0; + } + // We should restore + if(Restore != 0) + { + pParam[nOffset] = Restore; + return Restore; + } + else + { + Replaced = pParam[nOffset]; + pParam[nOffset] = 0; + return Replaced; + + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPStrSearch +// Purpose : Search a string within another and return its pointer and a length +// Returns : BOOL - TRUE on success +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +BOOL HTTPStrSearch(CHAR *pSrc, // [IN] The source string + CHAR *pSearched, // [IN] Parameter to search for + UINT32 nOffset, // [IN] Offset from the source string start position + UINT32 nScope, // [IN] Length in bytes we should search in + HTTP_PARAM *HttpParam) // [IN OUT] The Pointer\Length value that will be returned on success +{ + + CHAR *pSrcStart; + CHAR *pDstStart; + CHAR nOrigCharacter; + UINT32 nPosition = 0; + + do + { + pSrcStart = pSrc + nOffset; + nOrigCharacter = pSrcStart[nScope]; + + // Temporarily null terminate + pSrcStart[nScope] = 0; + + pDstStart = strstr(pSrcStart,pSearched); + if(!pDstStart) + { + break; + } + + nPosition = pDstStart - pSrcStart + 1; + + } while(0); + + // Remove the null termination + pSrcStart[nScope] = nOrigCharacter; + + if(!nPosition) + { + return FALSE; + } + + if(HttpParam) + { + + HttpParam->nLength = nPosition -1; + HttpParam->pParam = pSrcStart; + } + + return TRUE; +} + +int stricmp(const char *s1, const char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + return HTTPStrInsensitiveCompare(s1, s2, MAX(len1, len2)); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPStrCaseStr +// Purpose : Same as strstr() only case insensitive +// Returns : a pointer to the position of the searched string (or 0 on error) +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +CHAR *HTTPStrCaseStr(const char *pSrc,UINT32 nSrcLength,const char *pFind) +{ + const char *ptr = pSrc; + const char *ptr2; + UINT32 iLength = 0; + + while(1) + { + if(iLength >= nSrcLength) + { + break; + } + ptr = strchr(pSrc,toupper((unsigned char)*pFind)); + ptr2 = strchr(pSrc,tolower((unsigned char)*pFind)); + if (!ptr) + { + ptr = ptr2; + } + if (!ptr) + { + break; + } + if (ptr2 && (ptr2 < ptr)) { + ptr = ptr2; + } + if (HTTPStrInsensitiveCompare(ptr,pFind,strlen(pFind))) + { + return (char *) ptr; + } + pSrc = ptr+1; + iLength++; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPStrCaseStr +// Purpose : +// Gets : +// Returns : +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + + +CHAR* HTTPStrGetToken (CHAR *pSrc, UINT32 nSrcLength, CHAR *pDest, UINT32 *nDestLength) +{ + + // Get text between the ":" and \r\n or end of string. + CHAR *pStart = pSrc; + CHAR *pEnd; + UINT32 nTokenLength = 0; + UINT32 nPosition = 0; + + pStart = strchr(pSrc,':') + 1; + if(pStart) + { + pEnd = pStart ; + // First pass, count required space + while ((*pEnd) && (*pEnd != '\r') && (*pEnd != '\n')) + { + if(*pEnd != 0x20) + { + nTokenLength++; + } + + if(nSrcLength && nPosition > nSrcLength) + { + break; + } + pEnd++; + nPosition++; + } + + if(nTokenLength > *(nDestLength)) + { + *(nDestLength) = nTokenLength; + pDest = NULL; + return pDest; + } + + // Second pass copy into the destination buffer + pEnd = pStart; + *(nDestLength) = nTokenLength; + nTokenLength = 0; + // First pass, count required space + while ((*pEnd) && (*pEnd != '\r') && (*pEnd != '\n')) + { + if(*pEnd != 0x20) + { + pDest[nTokenLength++] = *pEnd; + } + pEnd++; + } + + pDest[nTokenLength] = 0; + + } + + + return pDest; +} + +#if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST +/////////////////////////////////////////////////////////////////////////////// +// +// Function : HTTPStrGetDigestToken +// Purpose : +// Gets : +// Returns : +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +UINT32 HTTPStrGetDigestToken (HTTP_PARAM pParamSrc, CHAR *pSearched, HTTP_PARAM *pParamDest) +{ + + CHAR Token[HTTP_CLIENT_MAX_TOKEN_NAME_LENGTH]; + CHAR *pPtrStart, *pPtrEnd = NULL, *pPtrEndSrc; + BOOL Brackets = FALSE; + + // Build the searched token + memset(Token,0x00,HTTP_CLIENT_MAX_TOKEN_NAME_LENGTH); + strcpy(Token,pSearched); + strcat(Token,"="); + + // Reset destination values + pParamDest->nLength = 0; + pParamDest->pParam = 0; + + pPtrEndSrc = pParamSrc.pParam + pParamSrc.nLength; + + pPtrStart = HTTPStrCaseStr(pParamSrc.pParam,pParamSrc.nLength,Token); + if(pPtrStart) + { + // Found the token so jump to the end of it + pPtrStart += strlen(Token); + // jump passed any spaces that may be + while ((*pPtrStart) && (*pPtrStart == 0x20) && (pPtrStart != pPtrEndSrc)) pPtrStart++; + // Any Brackets around the string? + if(*pPtrStart == 0x22) Brackets = TRUE; + + + switch (Brackets) + { + + case TRUE: + // Find the next brackets + pPtrStart++; + pPtrEnd = pPtrStart; + while ((*pPtrEnd) && (*pPtrEnd != 0x22) && (*pPtrEnd != 0x0d) && (*pPtrEnd != 0x0a) && (pPtrStart != pPtrEndSrc)) pPtrEnd++; + break; + + case FALSE: + // Find the next space or comma (0x2c) + pPtrEnd = pPtrStart; + while ((*pPtrEnd) && (*pPtrEnd != 0x20) && (*pPtrEnd != 0x2c) && (*pPtrEnd != 0x0d) && (*pPtrEnd != 0x0a) && (pPtrStart != pPtrEndSrc)) pPtrEnd++; + break; + + }; + + pParamDest->nLength = (pPtrEnd - pPtrStart); + pParamDest->pParam = pPtrStart; + + return HTTP_CLIENT_SUCCESS; + } + return HTTP_CLIENT_ERROR_NO_DIGEST_TOKEN; + + +} +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH + +///////////////////////////////////////////////////////////////////////////////// +// Function : HTTPStrHToL +// Purpose : Convert a hex string "0x00" to long 0 +// Gets : a pointer to the Hex string +// Last updated : 15/05/2005 +// +/////////////////////////////////////////////////////////////////////////////// + + +UINT32 HTTPStrHToL (CHAR * s) +{ + UINT32 i , nn, digit; + UINT32 n ; + + n = i = nn = 0; + do + { + if ( isalnum((unsigned char)s[i]) ) { + s[i] = toupper((unsigned char)s[i]) ; + if (s[i] == 'X') nn=n=0; else { + digit = (isalpha((unsigned char)s[i]) ? (s[i] - 'A' + 10) : s[i] - '0') ; + if ( digit > 15 ) digit = 15; + n = n * 16 + digit; + if (n |= 0) nn++; + if (nn == 8) break;} + } + i++; + } + while ( s[i] |= 0 ); + return n ; +} + +///////////////////////////////////////////////////////////////////////////////// +// Function : HTTPStrLToH +// Purpose : Convert a long to hex string 0 to "00" +// Gets : +// Last updated : 15/05/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +CHAR* HTTPStrLToH (CHAR * dest,UINT32 nSrc) +{ + + char *hex = "0123456789abcdef"; + INT32 i; + + if (nSrc == 0) { + dest[0] = '0'; + dest[1] = 0; + } + else + { + for(i = 28; ((nSrc >> i) & 0xf) == 0; i -= 4); + for(; i >= 0u; i -= 4) { + *dest++ = hex[(nSrc >> i) & 0xf]; + } + *dest = 0; + } + + return dest; +} + +#endif //TLS_CONFIG_HTTP_CLIENT + diff --git a/sdk/Src/App/httpclient/HTTPClientString.h b/sdk/Src/App/httpclient/HTTPClientString.h new file mode 100644 index 0000000..8b4b424 --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClientString.h @@ -0,0 +1,23 @@ + +#ifndef _HTTP_CLIENT_STRING +#define _HTTP_CLIENT_STRING + +#include "HTTPClientWrapper.h" // Cross platform support +#include "HTTPClient.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Section : HTTP Api global definitions +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// +int stricmp(const char *s1, const char *s2) ; +BOOL HTTPStrInsensitiveCompare (const CHAR *pSrc, const CHAR* pDest, UINT32 nLength); +BOOL HTTPStrSearch (CHAR *pSrc, CHAR *pSearched, UINT32 nOffset, UINT32 nScope,HTTP_PARAM *HttpParam); +CHAR HTTPStrExtract (CHAR *pParam,UINT32 nOffset,CHAR Restore); +CHAR* HTTPStrCaseStr (const CHAR *pSrc, UINT32 nSrcLength, const CHAR *pFind); +CHAR* HTTPStrGetToken (CHAR *pSrc, UINT32 nSrcLength, CHAR *pDest, UINT32 *nDestLength); +UINT32 HTTPStrGetDigestToken (HTTP_PARAM pParamSrc, CHAR *pSearched, HTTP_PARAM *pParamDest); +UINT32 HTTPStrHToL (CHAR * s); +CHAR* HTTPStrLToH (CHAR * dest,UINT32 nSrc); +#endif diff --git a/sdk/Src/App/httpclient/HTTPClientWrapper.c b/sdk/Src/App/httpclient/HTTPClientWrapper.c new file mode 100644 index 0000000..cf10edf --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClientWrapper.c @@ -0,0 +1,754 @@ + +#include "lwip/arch.h" +#include "HTTPClientWrapper.h" +#include "random.h" +#include "wm_osal.h" +#include "wm_sockets.h" +#include "lwip/sockets.h" +#include "wm_debug.h" +#include "HTTPClient.h" +#if TLS_CONFIG_HTTP_CLIENT + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : Stdc: HTTPWrapperIsAscii +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : Same as stdc: isascii +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HTTPWrapperIsAscii(int c) +{ + return (!(c & ~0177)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : Stdc: HTTPWrapperToUpper +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : Convert character to uppercase. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HTTPWrapperToUpper(int c) +{ + // -32 + if(HTTPWrapperIsAscii(c) > 0) + { + if(c >= 97 && c <= 122) + { + return (c - 32); + } + } + + return c; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : Stdc: HTTPWrapperToLower +// Last updated : 13/06/2006 +// Author Name : Eitan Michaelson +// Notes : Convert character to lowercase. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HTTPWrapperToLower(int c) +{ + // +32 + if(HTTPWrapperIsAscii(c) > 0) + { + if(c >= 65 && c <= 90) + { + return (c + 32); + } + } + + return c; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : Stdc: isalpha +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : returns nonzero if c is a particular representation of an alphabetic character +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HTTPWrapperIsAlpha(int c) +{ + + if(HTTPWrapperIsAscii(c) > 0) + { + if( (c >= 97 && c <= 122) || (c >= 65 && c <= 90)) + { + return c; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : Stdc: isalnum +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : returns nonzero if c is a particular representation of an alphanumeric character +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HTTPWrapperIsAlNum(int c) +{ + if(HTTPWrapperIsAscii(c) > 0) + { + + if(HTTPWrapperIsAlpha(c) > 0) + { + return c; + } + + if( c >= 48 && c <= 57) + { + return c; + } + + } + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : HTTPWrapper_itoa +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : same as stdc itoa() // hmm.. allmost the same +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +char* HTTPWrapperItoa(char *s,int a) +{ + + unsigned int b; + if(a > 2147483647) + { + return 0; // overflow + } + + if (a < 0) b = -a, *s++ = '-'; + else b = a; + for(;a;a=a/10) s++; + for(*s='\0';b;b=b/10) *--s=b%10+'0'; + return s; + +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : HTTPWrapper_ShutDown +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : Handles parameter changes in the socket shutdown() function in AMT +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +int HTTPWrapperShutDown (int s,int how) +{ + return shutdown(s,how); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : HTTPWrapper_GetSocketError +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : WSAGetLastError Wrapper (Win32 Specific) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HTTPWrapperGetSocketError (int s) +{ + return errno; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : HTTPWrapper_GetRandomeNumber +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : GetRandom number for Win32 & AMT +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void HTTPWrapperInitRandomeNumber() +{ + srand((unsigned int)tls_os_get_time()); +} + +int HTTPWrapperGetRandomeNumber() +{ + int num; + num = (int)(((double) rand()/ ((double)RAND_MAX+1)) * 16); + return num; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : HTTPWrapper_GetRTC +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : Get uptime under Win32 & AMT +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +long HTTPWrapperGetUpTime() +{ + + long lTime = 0; + + lTime = tls_os_get_time(); + return lTime; + +} + +#endif //TLS_CONFIG_HTTP_CLIENT + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Section : HTTPWrapper_GetHostByName +// Last updated : 15/05/2005 +// Author Name : Eitan Michaelson +// Notes : gethostbyname for Win32 (supports the AMT edition of the function) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +unsigned long HTTPWrapperGetHostByName(char *name,unsigned long *address) +{ + HTTP_HOSTNET *HostEntry; + int iPos = 0, iLen = 0,iNumPos = 0,iDots =0; + long iIPElement; + char c = 0; + char Num[4]; + int iHostType = 0; // 0 : numeric IP + + // Check if the name is an IP or host + iLen = strlen(name); + for(iPos = 0; iPos <= iLen;iPos++) + { + c = name[iPos]; + if((c >= 48 && c <= 57) || (c == '.') ) + { + // c is numeric or dot + if(c != '.') + { + // c is numeric + if(iNumPos > 3) + { + iHostType++; + break; + } + Num[iNumPos] = c; + Num[iNumPos + 1] = 0; + iNumPos ++; + } + else + { + iNumPos = 0; + iDots++; + iIPElement = atol(Num); + if(iIPElement > 256 || iDots > 3) + { + return 0; // error invalid IP + } + } + } + else + { + break; // this is an alpha numeric address type + } + } + + if(c == 0 && iHostType == 0 && iDots == 3) + { + iIPElement = atol(Num); + if(iIPElement > 256) + { + return 0; // error invalid IP + } + } + else + { + iHostType++; + } + + if(iHostType > 0) + { + + HostEntry = gethostbyname(name); + if(HostEntry) + { + *(address) = *((u_long*)HostEntry->h_addr_list[0]); + + //*(address) = (unsigned long)HostEntry->h_addr_list[0]; + return 1; // Error + } + else + { + return 0; // OK + } + } + + else // numeric address - no need for DNS resolve + { + *(address) = inet_addr(name); + return 1; + + } +} + + +#if TLS_CONFIG_HTTP_CLIENT_SECURE +#define ALLOW_ANON_CONNECTIONS 1 +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int HTTPWrapperSSLNegotiate(HTTP_SESSION_HANDLE pSession,int s,const struct sockaddr *name,int namelen,char *hostname) +{ + return 0; +} + +#if 1 + +#if 0 +#define PSLL_PRT printf +#else +#define PSLL_PRT(x, ...) +#endif + +/************************ memory manage ************************/ + + +void *platform_malloc(uint32_t size) +{ + return tls_mem_alloc(size); +} + + +void platform_free(void *ptr) +{ + tls_mem_free(ptr); +} + + + +/************************ mutex manage ************************/ + +void *platform_mutex_init(void) +{ + tls_os_sem_t *mutex = NULL; + + tls_os_sem_create(&mutex,1); + + return mutex; +} + + +void platform_mutex_lock(void *mutex) +{ + tls_os_sem_acquire(mutex,0); +} + + +void platform_mutex_unlock(void *mutex) +{ + tls_os_sem_release(mutex); +} + + +void platform_mutex_destroy(void *mutex) +{ + if (!mutex) + return; + tls_os_sem_delete(mutex); +} + +static int ctr_drbg_random( void *p_rng, unsigned char *output, size_t output_len ) +{ + return random_get_bytes( output, output_len ); +} + +static void my_debug( void *ctx, int level, const char *str ) +{ + PSLL_PRT("%s", str); +} + +static int net_is_blocking( void ) +{ + switch( errno ) + { +#if defined EAGAIN + case EAGAIN: +#endif +#if defined EWOULDBLOCK && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + return( 1 ); + } + return( 0 ); +} + +static int net_recv( void *ctx, unsigned char *buf, size_t len ) +{ + int ret; + int skt = (int)(long) ctx; +#if 0 + fd_set read_set; + struct timeval tv; + + FD_ZERO(&read_set); + FD_SET(skt, &read_set); + tv.tv_sec = 5; + tv.tv_usec = 0; + + ret = select(skt + 1, &read_set, NULL, NULL, &tv); + if (ret > 0) + { + if (FD_ISSET(skt, &read_set)) + { +#endif + ret = recv( skt, buf, len, 0); + + if( ret < 0 ) + { + if( net_is_blocking() != 0 ) + return( POLARSSL_ERR_NET_WANT_READ ); + + if( errno == EPIPE || errno == ECONNRESET ) + return( POLARSSL_ERR_NET_CONN_RESET ); + + if( errno == EINTR ) + return( POLARSSL_ERR_NET_WANT_READ ); + + return( POLARSSL_ERR_NET_RECV_FAILED ); + } + +#if 0 + FD_CLR(skt, &read_set); + } + else + { + PSLL_PRT("\r\n\r\nssl select no\r\n\r\n"); + } + } + else if (0 == ret) + { + PSLL_PRT("\r\n\r\nssl recv timeout\r\n\r\n"); + } + else + { + PSLL_PRT("\r\n\r\nssl select error\r\n\r\n"); + } +#endif + + return ret; +} + +/* + * Write at most 'len' characters + */ +static int net_send( void *ctx, const unsigned char *buf, size_t len ) +{ + int ret = send( (long) ctx, buf, len, 0); + + if( ret < 0 ) + { + if( net_is_blocking() != 0 ) + return( POLARSSL_ERR_NET_WANT_WRITE ); + + if( errno == EPIPE || errno == ECONNRESET ) + return( POLARSSL_ERR_NET_CONN_RESET ); + + if( errno == EINTR ) + return( POLARSSL_ERR_NET_WANT_WRITE ); + + return( POLARSSL_ERR_NET_SEND_FAILED ); + } + + return( ret ); +} + +void *platform_ssl_connect(void *tcp_fd, + const char *server_cert, + int server_cert_len) +{ + int ret; + ssl_context *ssl; + ssl_session *ssn; + x509_cert *cacert = NULL; + + ssl = platform_malloc(sizeof(ssl_context)); + if (!ssl) + return NULL; + + ssn = platform_malloc(sizeof(ssl_session)); + if (!ssn) + { + platform_free(ssl); + return NULL; + } + + if (server_cert) + { + cacert = platform_malloc(sizeof(x509_cert)); + if (!cacert) + { + platform_free(ssl); + platform_free(ssn); + return NULL; + } + + memset(cacert, 0, sizeof(x509_cert)); + ret = x509parse_crt(cacert, (unsigned char *) server_cert, server_cert_len); + if( ret != 0 ) + { + PSLL_PRT( " failed\n ! x509parse_crt returned %d\n\n", ret ); + platform_free(ssl); + platform_free(ssn); + if(cacert) + platform_free(cacert); + return NULL; + } + } + + memset(ssl, 0, sizeof(ssl_context)); + if( ( ret = ssl_init( ssl ) ) != 0 ) + { + PSLL_PRT( " failed\n ! ssl_init returned %d\n\n", ret ); + if(cacert) + x509_free( cacert ); + platform_free(ssl); + platform_free(ssn); + if(cacert) + platform_free(cacert); + return NULL; + } + + ssl_set_endpoint( ssl, SSL_IS_CLIENT ); + if (server_cert) + ssl_set_authmode( ssl, SSL_VERIFY_REQUIRED ); + else + ssl_set_authmode( ssl, SSL_VERIFY_NONE ); + + ssl_set_rng( ssl, ctr_drbg_random, NULL ); + ssl_set_dbg( ssl, my_debug, NULL); + ssl_set_bio( ssl, net_recv, tcp_fd, net_send, tcp_fd ); + + ssl_set_ciphersuites( ssl, ssl_default_ciphersuites ); + + memset(ssn, 0, sizeof(ssl_session)); + ssl_set_session( ssl, 1, 600, ssn ); + + if (server_cert) + ssl_set_ca_chain( ssl, cacert, NULL, NULL); + + while( ( ret = ssl_handshake( ssl ) ) != 0 ) + { + if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE ) + { + PSLL_PRT( " failed\n ! ssl_handshake returned %d\n\n", ret ); + goto fail; + } + PSLL_PRT("INFO: ssl_handshake returned %d\n\n", ret); + } + + if (server_cert) + { + if( ( ret = ssl_get_verify_result( ssl ) ) != 0 ) + { + PSLL_PRT( "Verifying peer X.509 certificate failed\n" ); + + if( ( ret & BADCERT_EXPIRED ) != 0 ) + PSLL_PRT( " ! server certificate has expired\n" ); + + if( ( ret & BADCERT_REVOKED ) != 0 ) + PSLL_PRT( " ! server certificate has been revoked\n" ); + + if( ( ret & BADCERT_CN_MISMATCH ) != 0 ) + PSLL_PRT( " ! CN mismatch \n"); + + if( ( ret & BADCERT_NOT_TRUSTED ) != 0 ) + PSLL_PRT( " ! self-signed or not signed by a trusted CA\n" ); + + PSLL_PRT( "\n" ); + + ssl_close_notify( ssl ); + goto fail; + } + else + { + PSLL_PRT( "Verifying X.509 certificate pass.\n" ); + } + } + + //ssl->read_lock = platform_mutex_init(); + ssl->write_lock = platform_mutex_init(); + + //printf("ssl read/write lock 0x%p/0x%p\n", ssl->read_lock, ssl->write_lock); + PSLL_PRT("ssl rdwr lock 0x%p\n", ssl->write_lock); + + return ssl; + +fail: + if(cacert) + x509_free( cacert ); + ssl_free( ssl ); + platform_free(ssl); + platform_free(ssn); + if(cacert) + platform_free(cacert); + return NULL; +} + + +int platform_ssl_close(void *ssl) +{ + ssl_context *sslt = (ssl_context *)ssl; + + platform_mutex_lock(sslt->write_lock); + + ssl_close_notify( sslt ); + + x509_free( sslt->ca_chain ); + + platform_free(sslt->ca_chain); + + platform_free(sslt->session); + + platform_mutex_unlock(sslt->write_lock); + platform_mutex_destroy(sslt->write_lock); + //platform_mutex_destroy(sslt->read_lock); + + ssl_free(sslt); + + platform_free(sslt); + + return 0; +} + +int platform_ssl_send(void *ssl, const char *buf, int len) +{ + int ret; + ssl_context *sslt = (ssl_context *)ssl; + + platform_mutex_lock(sslt->write_lock); + + while( ( ret = ssl_write( sslt, (u8 *)buf, len ) ) <= 0 ) + { + if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE ) + { + PSLL_PRT( " failed\n ! ssl_write returned %d, errno %d\n", ret , errno); + break; + } + } + + platform_mutex_unlock(sslt->write_lock); + + return ret; +} + +int platform_ssl_recv(void *ssl, char *buf, int len) +{ + int ret; + ssl_context *sslt = (ssl_context *)ssl; + + platform_mutex_lock(sslt->write_lock); + //platform_mutex_lock(sslt->read_lock); + + do + { + ret = ssl_read( sslt, (u8 *)buf, len ); + + if( ret == POLARSSL_ERR_NET_WANT_READ || ret == POLARSSL_ERR_NET_WANT_WRITE ) + continue; + + //if( ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY ) + // break; + + if( ret < 0 ) + { + PSLL_PRT( "failed\n ! ssl_read returned %d, errno %d\n", ret , errno); + break; + } + else if( ret == 0 ) + { + PSLL_PRT("\n\nEOF, errno %d\n\n", errno); + break; + } + else + { + //PSLL_PRT( " %d bytes read\n\n%s", ret, (char *) buf ); + break; + } + } while(1); + + platform_mutex_unlock(sslt->write_lock); + //platform_mutex_unlock(sslt->read_lock); + //PSLL_PRT(" <== platform_ssl_recv ret %d\n", ret); + return ret; +} + +int HTTPWrapperSSLConnect(tls_ssl_t **ssl_p,int fd,const struct sockaddr *name,int namelen,char *hostname) +{ + int rc; + tls_ssl_t *ssl = NULL; + + if(name) + { + char *host_ip = inet_ntoa(((struct sockaddr_in*)name)->sin_addr); + + rc = connect(fd, // Socket + name, // Server address + sizeof(struct sockaddr)); + if(rc) + { + TLS_DBGPRT_ERR("host_ip=%s\n", host_ip); + TLS_DBGPRT_ERR("Connection Failed: %d. Exiting\n", rc); + return rc; + } + } + ssl = platform_ssl_connect((void *)fd, NULL, 0); + + if(ssl == NULL) + return -1; + *ssl_p = ssl; + return 0; +} +int HTTPWrapperSSLSend(tls_ssl_t *ssl, int s,char *sndbuf, int len,int flags) +{ + return platform_ssl_send(ssl, sndbuf, len); +} + +int HTTPWrapperSSLRecv(tls_ssl_t *ssl,int s,char *buf, int len,int flags) +{ + int ret = platform_ssl_recv(ssl, buf, len); + if(ssl->in_msglen > 0) + return SOCKET_SSL_MORE_DATA; + return ret; +} +int HTTPWrapperSSLRecvPending(tls_ssl_t *ssl) +{ + return ssl_get_bytes_avail(ssl); +} +int HTTPWrapperSSLClose(tls_ssl_t *ssl, int s) +{ + if(ssl == NULL) + return 0; + return platform_ssl_close(ssl); +} + +#endif //TLS_CONFIG_USE_POLARSSL +#endif //TLS_CONFIG_HTTP_CLIENT_SECURE diff --git a/sdk/Src/App/httpclient/HTTPClientWrapper.h b/sdk/Src/App/httpclient/HTTPClientWrapper.h new file mode 100644 index 0000000..62232f9 --- /dev/null +++ b/sdk/Src/App/httpclient/HTTPClientWrapper.h @@ -0,0 +1,121 @@ + +#ifndef HTTP_CLIENT_WRAPPER +#define HTTP_CLIENT_WRAPPER + +#include "wm_config.h" +#include "lwip/arch.h" + +#if TLS_CONFIG_HTTP_CLIENT_SECURE +#include "polarssl/config.h" +#include "polarssl/ssl.h" +#endif +// Compilation mode +//#define _HTTP_BUILD_WIN32 // Set Windows Build flag + + +/////////////////////////////////////////////////////////////////////////////// +// +// Section : Microsoft Windows Support +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +//#include +#include +#include +//#include +//#include + +#include "wm_type_def.h" +#include "wm_sockets.h" +#include "wm_http_client.h" +// Generic types +// Sockets (Winsock wrapper) + +#define HTTP_ECONNRESET (ECONNRESET) +#define HTTP_EINPROGRESS (EINPROGRESS) +#define HTTP_EWOULDBLOCK (EWOULDBLOCK) + +#define SOCKET_ERROR (-1) +#define SOCKET_SSL_MORE_DATA (-2) + +/////////////////////////////////////////////////////////////////////////////// +// +// Section : Functions that are not supported by the AMT stdc framework +// So they had to be specificaly added. +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" { +#endif + +#if TLS_CONFIG_HTTP_CLIENT_SECURE +typedef ssl_context tls_ssl_t; +#endif + + + // STDC Wrapper implimentation + int HTTPWrapperIsAscii (int c); + int HTTPWrapperToUpper (int c); + int HTTPWrapperToLower (int c); + int HTTPWrapperIsAlpha (int c); + int HTTPWrapperIsAlNum (int c); + char* HTTPWrapperItoa (char *buff,int i); + void HTTPWrapperInitRandomeNumber (void); + long HTTPWrapperGetUpTime (void); + int HTTPWrapperGetRandomeNumber (void); + int HTTPWrapperGetSocketError (int s); + unsigned long HTTPWrapperGetHostByName (char *name,unsigned long *address); + int HTTPWrapperShutDown (int s,int in); + // SSL Wrapper prototypes +#if TLS_CONFIG_HTTP_CLIENT_SECURE + int HTTPWrapperSSLConnect (tls_ssl_t **ssl_p,int s,const struct sockaddr *name,int namelen,char *hostname); + int HTTPWrapperSSLNegotiate (tls_http_session_handle_t pSession,int s,const struct sockaddr *name,int namelen,char *hostname); + int HTTPWrapperSSLSend (tls_ssl_t *ssl,int s,char *buf, int len,int flags); + int HTTPWrapperSSLRecv (tls_ssl_t *ssl,int s,char *buf, int len,int flags); + int HTTPWrapperSSLClose (tls_ssl_t *ssl, int s); + int HTTPWrapperSSLRecvPending (tls_ssl_t *ssl); +#endif + // Global wrapper Functions +#define IToA HTTPWrapperItoa +#define GetUpTime HTTPWrapperGetUpTime +#define SocketGetErr HTTPWrapperGetSocketError +#define HostByName HTTPWrapperGetHostByName +#define InitRandomeNumber HTTPWrapperInitRandomeNumber +#define GetRandomeNumber HTTPWrapperGetRandomeNumber + +#ifdef __cplusplus +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// Section : Global type definitions +// Last updated : 01/09/2005 +// +/////////////////////////////////////////////////////////////////////////////// + +//#ifndef NULL +//#define NULL 0 +//#endif +//#define TRUE 1 +//#define FALSE 0 + +#ifdef u_long +#undef u_long +#endif +typedef unsigned long u_long; + +// Global socket structures and definitions +#define HTTP_INVALID_SOCKET (-1) +typedef struct sockaddr_in HTTP_SOCKADDR_IN; +typedef struct timeval HTTP_TIMEVAL; +typedef struct hostent HTTP_HOSTNET; +typedef struct sockaddr HTTP_SOCKADDR; +typedef struct in_addr HTTP_INADDR; + + +#endif // HTTP_CLIENT_WRAPPER diff --git a/sdk/Src/App/httpclient/wm_http_compile.c b/sdk/Src/App/httpclient/wm_http_compile.c new file mode 100644 index 0000000..acce9ee --- /dev/null +++ b/sdk/Src/App/httpclient/wm_http_compile.c @@ -0,0 +1,8 @@ +#ifndef WM_HTTP_COMPILE_FILES +#define WM_HTTP_COMPILE_FILES +#include "HTTPClient.c" +#include "HTTPClientAuth.c" +#include "HTTPClientString.c" +#include "HTTPClientWrapper.c" +#include "wm_httpclient_task.c" +#endif diff --git a/sdk/Src/App/httpclient/wm_httpclient_if.c b/sdk/Src/App/httpclient/wm_httpclient_if.c new file mode 100644 index 0000000..46d143b --- /dev/null +++ b/sdk/Src/App/httpclient/wm_httpclient_if.c @@ -0,0 +1,307 @@ +#include "wm_type_def.h" +#include "wm_http_client.h" +#include "HTTPClient.h" +#include "HTTPClientWrapper.h" + +/*************************************************************************** +* Function: HTTPClientOpenRequest +* Description: Allocate memory for a new HTTP Session. +* +* Input: Flags: HTTP Session internal API flags, 0 should be passed here. +* +* Output: None +* +* Return: HTTP Session handle +* +* Date : 2014-6-6 +****************************************************************************/ +tls_http_session_handle_t tls_http_client_open_request(tls_http_session_flags_t flags) +{ + return HTTPClientOpenRequest(flags); +} + +/*************************************************************************** +* Function: HTTPClientCloseRequest +* Description: Closes any active connection and free any used memory. +* +* Input: pSession: HTTP Session handle. +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_close_request(tls_http_session_handle_t *session) +{ + return HTTPClientCloseRequest(session); +} + +#if TLS_CONFIG_HTTP_CLIENT_AUTH +/*************************************************************************** +* Function: HTTPClientSetAuth +* Description: Sets the HTTP authentication schema. +* +* Input: pSession: HTTP Session handle. +* AuthSchema: HTTP Supported authentication methods. +* pReserved: Reserved parameter. +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_set_auth(tls_http_session_handle_t session, + tls_http_auth_achema_t auth_schema, + void *reserved) +{ + return HTTPClientSetAuth(session, (HTTP_AUTH_SCHEMA)auth_schema, reserved); +} + +/*************************************************************************** +* Function: HTTPClientSetCredentials +* Description: Sets credentials for the target host. +* +* Input: pSession: HTTP Session handle. +* pUserName: User name. +* pPassword: Password. +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_set_credentials(tls_http_session_handle_t session, + char *user_name, char *password) +{ + return HTTPClientSetCredentials(session, (CHAR *)user_name, (CHAR *)password); +} +#endif + +#if TLS_CONFIG_HTTP_CLIENT_PROXY +/*************************************************************************** +* Function: HTTPClientSetProxy +* Description: Sets all the proxy related parameters. +* +* Input: pSession: HTTP Session handle. +* pProxyName: The host name. +* nPort: The proxy port number. +* pUserName: User name for proxy authentication (can be null). +* pPassword: User password for proxy authentication (can be null). +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_set_proxy(tls_http_session_handle_t session, char *proxy_name, + u16 port, char *user_name, char *password) +{ + return HTTPClientSetProxy(session, (CHAR *)proxy_name, port, (CHAR *)user_name, (CHAR *)password); +} +#endif + +/*************************************************************************** +* Function: HTTPClientSetVerb +* Description: Sets the HTTP verb for the outgoing request. +* +* Input: pSession: HTTP Session handle. +* HttpVerb: HTTP supported verbs +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_set_verb(tls_http_session_handle_t session, tls_http_verb_t http_verb) +{ + return HTTPClientSetVerb(session, (HTTP_VERB)http_verb); +} + +/*************************************************************************** +* Function: HTTPClientAddRequestHeaders +* Description: Add headers to the outgoing request. +* +* Input: pSession: HTTP Session handle. +* pHeaderName: The Headers name +* pHeaderData: The headers data +* nInsert: Reserved could be any +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_add_request_headers(tls_http_session_handle_t session, char *header_name, + char *header_data, bool insert) +{ + return HTTPClientAddRequestHeaders(session, (CHAR *)header_name, (CHAR *)header_data, (BOOL)insert); +} + +/*************************************************************************** +* Function: HTTPClientSendRequest +* Description: This function builds the request headers, performs a DNS resolution , +* opens the connection (if it was not opened yet by a previous request or if it has closed) +* and sends the request headers. +* +* Input: pSession: HTTP Session handle. +* pUrl: The requested URL +* pData: Data to post to the server +* nDataLength: Length of posted data +* TotalLength: Valid only when http method is post. +* TRUE: Post data to http server. +* FALSE: In a post request without knowing the total length in advance so return error or use chunking. +* nTimeout: Operation timeout +* nClientPort: Client side port 0 for none +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_send_request(tls_http_session_handle_t session, char *url, void *data, + u32 data_len, bool total_len, u32 time_out, u32 client_port) +{ + return HTTPClientSendRequest(session, (CHAR *)url, data, data_len, (BOOL)total_len, time_out, client_port); +} + +/*************************************************************************** +* Function: HTTPClientWriteData +* Description: Write data to the remote server. +* +* Input: pSession: HTTP Session handle. +* pBuffer: Data to write to the server. +* nBufferLength: Length of wtitten data. +* nTimeout: Timeout for the operation. +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_write_data(tls_http_session_handle_t session, void *buffer, + u32 buffer_len, u32 time_out) +{ + return HTTPClientWriteData(session, buffer, buffer_len, time_out); +} + +/*************************************************************************** +* Function: HTTPClientRecvResponse +* Description: Receives the response header on the connection and parses it. +* Performs any required authentication. +* +* Input: pSession: HTTP Session handle. +* nTimeout: Timeout for the operation. +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_recv_response(tls_http_session_handle_t session, u32 time_out) +{ + return HTTPClientRecvResponse(session, time_out); +} + +/*************************************************************************** +* Function: HTTPClientReadData +* Description: Read data from the server. Parse out the chunks data. +* +* Input: pSession: HTTP Session handle. +* nBytesToRead: The size of the buffer (numbers of bytes to read) +* nTimeout: Operation timeout in seconds +* +* Output: pBuffer: A pointer to a buffer that will be filled with the servers response +* nBytesRecived: Count of the bytes that were received in this operation +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_read_data(tls_http_session_handle_t session, void *buffer, + u32 bytes_to_read, u32 time_out, u32 *bytes_recived) +{ + return HTTPClientReadData(session, buffer, bytes_to_read, time_out, bytes_recived); +} + +/*************************************************************************** +* Function: HTTPClientGetInfo +* Description: Fill the users structure with the session information. +* +* Input: pSession: HTTP Session handle. +* +* Output: HTTPClient: The session information. +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_get_info(tls_http_session_handle_t session, tls_http_client_t *http_client) +{ + return HTTPClientGetInfo(session, http_client); +} + +/*************************************************************************** +* Function: HTTPClientFindFirstHeader +* Description: Initiate the headr searching functions and find the first header. +* +* Input: pSession: HTTP Session handle. +* pSearchClue: Search clue. +* +* Output: pHeaderBuffer: A pointer to a buffer that will be filled with the header name and value. +* nLength: Count of the bytes that were received in this operation. +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_find_first_header(tls_http_session_handle_t session, char *search_clue, + char *header_buffer, u32 *length) +{ + return HTTPClientFindFirstHeader(session, (CHAR *)search_clue, (CHAR *)header_buffer, length); +} + +/*************************************************************************** +* Function: HTTPClientGetNextHeader +* Description: Find the next header. +* +* Input: pSession: HTTP Session handle. +* +* Output: pHeaderBuffer: A pointer to a buffer that will be filled with the header name and value. +* nLength: Count of the bytes that were received in this operation. +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_get_next_header(tls_http_session_handle_t session, + char *header_buffer, u32 *length) +{ + return HTTPClientGetNextHeader(session, (CHAR *)header_buffer, length); +} + +/*************************************************************************** +* Function: HTTPClientFindCloseHeader +* Description: Terminate a headers search session. +* +* Input: pSession: HTTP Session handle. +* +* Output: None +* +* Return: HTTP Status +* +* Date : 2014-6-6 +****************************************************************************/ +int tls_http_client_find_close_header(tls_http_session_handle_t session) +{ + return HTTPClientFindCloseHeader(session); +} + diff --git a/sdk/Src/App/httpclient/wm_httpclient_task.c b/sdk/Src/App/httpclient/wm_httpclient_task.c new file mode 100644 index 0000000..a653819 --- /dev/null +++ b/sdk/Src/App/httpclient/wm_httpclient_task.c @@ -0,0 +1,235 @@ +#include +#include "wm_include.h" +#include "wm_config.h" +#include "wm_http_client.h" +#include "lwip/sys.h" +#include "wm_wl_task.h" + +#if TLS_CONFIG_HTTP_CLIENT_TASK +#define HTTP_CLIENT_STK_SIZE 256 +static sys_mbox_t http_client_mbox = SYS_MBOX_NULL; +static OS_STK httpClientStk[HTTP_CLIENT_STK_SIZE]; +#define HTTP_CLIENT_BUFFER_SIZE 1024 +extern u8 pSession_flag; +static UINT32 http_snd_req_local( + HTTP_SESSION_HANDLE pHTTP, HTTPParameters ClientParams, HTTP_VERB verb, CHAR* pSndData, u32 dataLen, + http_client_recv_callback_fn recv_fn) +{ + INT32 nRetCode; + UINT32 nSize,nTotal = 0; + CHAR* Buffer = NULL; + UINT32 nSndDataLen ; + CHAR token[32]; + UINT32 size=32; + char *pRecvData=NULL; + u32 totallen; + do + { + Buffer = (CHAR*)tls_mem_alloc(HTTP_CLIENT_BUFFER_SIZE); + if(Buffer == NULL) + { + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + memset(Buffer, 0, HTTP_CLIENT_BUFFER_SIZE); + //printf("\nHTTP Client v1.0\n\n"); + nSndDataLen = (pSndData==NULL ? 0 : dataLen); + // Set the Verb + nRetCode = HTTPClientSetVerb(pHTTP,verb); + if(nRetCode != HTTP_CLIENT_SUCCESS) + { + break; + } +#if TLS_CONFIG_HTTP_CLIENT_AUTH + // Set authentication + if(ClientParams.AuthType != AuthSchemaNone) + { + if((nRetCode = HTTPClientSetAuth(pHTTP,ClientParams.AuthType,NULL)) != HTTP_CLIENT_SUCCESS) + { + break; + } + + // Set authentication + if((nRetCode = HTTPClientSetCredentials(pHTTP,ClientParams.UserName,ClientParams.Password)) != HTTP_CLIENT_SUCCESS) + { + break; + } + } +#endif //TLS_CONFIG_HTTP_CLIENT_AUTH +#if TLS_CONFIG_HTTP_CLIENT_PROXY + // Use Proxy server + if(ClientParams.UseProxy == TRUE) + { + if((nRetCode = HTTPClientSetProxy(pHTTP,ClientParams.ProxyHost,ClientParams.ProxyPort,NULL,NULL)) != HTTP_CLIENT_SUCCESS) + { + + break; + } + } +#endif //TLS_CONFIG_HTTP_CLIENT_PROXY + if((nRetCode = HTTPClientSendRequest(pHTTP,ClientParams.Uri,pSndData,nSndDataLen,verb==VerbPost || verb==VerbPut,0,0)) != HTTP_CLIENT_SUCCESS) + { + break; + } + // Retrieve the the headers and analyze them + if((nRetCode = HTTPClientRecvResponse(pHTTP,30)) != HTTP_CLIENT_SUCCESS) + { + break; + } + memset(token, 0, 32); + if((nRetCode = HTTPClientFindFirstHeader(pHTTP, "content-length", token, &size)) != HTTP_CLIENT_SUCCESS) + { + totallen = HTTP_CLIENT_BUFFER_SIZE; + } + else + { + totallen = atol(strstr(token,":")+1); + + } + HTTPClientFindCloseHeader(pHTTP); + //printf("Start to receive data from remote server...\n"); + // Get the data until we get an error or end of stream code + pSession_flag = 0; + while(nRetCode == HTTP_CLIENT_SUCCESS || nRetCode != HTTP_CLIENT_EOS) + { + // Set the size of our buffer + nSize = HTTP_CLIENT_BUFFER_SIZE; + // Get the data + nRetCode = HTTPClientReadData(pHTTP,Buffer,nSize,300,&nSize); + if(nRetCode != HTTP_CLIENT_SUCCESS && nRetCode != HTTP_CLIENT_EOS) + break; + pRecvData = tls_mem_alloc(nSize); + if(pRecvData == NULL) + { + nRetCode = HTTP_CLIENT_ERROR_NO_MEMORY; + break; + } + memcpy(pRecvData, Buffer, nSize); + recv_fn(pHTTP, pRecvData, totallen, nSize); + } + } while(0); // Run only once + if(Buffer) + tls_mem_free(Buffer); + if(pHTTP) + HTTPClientCloseRequest(&pHTTP); + if(ClientParams.Verbose == TRUE) + { + printf("\n\nHTTP Client terminated %d (got %d kb)\n\n",nRetCode,(nTotal/ 1024)); + } + return nRetCode; +} + +static void http_client_rx(void *sdata) +{ + int ret; + void *msg; + http_client_msg *http_msg; + for(;;) + { + sys_arch_mbox_fetch(&http_client_mbox, (void **)&msg, 0); + http_msg = (http_client_msg *)msg; + ret = http_snd_req_local(http_msg->pSession, + http_msg->param, + http_msg->method, + http_msg->sendData, + http_msg->dataLen, + http_msg->recv_fn); + //printf("http_client_rx ret=%d\n", ret); + if(ret == HTTP_CLIENT_SUCCESS || ret == HTTP_CLIENT_EOS) + { + ; + } + else + { + if(http_msg->err_fn != NULL) + http_msg->err_fn(http_msg->pSession, ret); + } + if(http_msg->sendData != NULL) + tls_mem_free(http_msg->sendData); + if(http_msg->param.Uri) + tls_mem_free(http_msg->param.Uri); + tls_mem_free(http_msg); + } + +} + + +int http_client_task_init(void) +{ + if(sys_mbox_new(&http_client_mbox, 32) != ERR_OK) { + return WM_FAILED; + } + tls_os_task_create(NULL, "HTTPCLNT", + http_client_rx, + NULL, + (void *)httpClientStk, /* task's stack start address */ + HTTP_CLIENT_STK_SIZE * sizeof(u32), /* task's stack size, unit:byte */ + TLS_HTTP_CLIENT_TASK_PRIO, + 0); + return WM_SUCCESS; +} + +int http_client_post(http_client_msg * msg) +{ + int len; + err_t err; + http_client_msg * msg1 = tls_mem_alloc(sizeof(http_client_msg)); + if(!msg1) + return HTTP_CLIENT_ERROR_NO_MEMORY; + memset(msg1, 0, sizeof(http_client_msg)); + memcpy(msg1, msg, sizeof(http_client_msg)); + if(msg->dataLen > 0) + { + msg1->sendData = tls_mem_alloc(msg->dataLen); + if(msg1->sendData == NULL) + { + tls_mem_free(msg1); + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + memcpy(msg1->sendData, msg->sendData, msg->dataLen); + } + else + msg1->sendData = NULL; + if(msg->param.Uri != NULL) + { + len = strlen(msg->param.Uri); + msg1->param.Uri = tls_mem_alloc(len + 1); + if(msg1->param.Uri == NULL) + { + if(msg1->sendData) + tls_mem_free(msg1->sendData); + tls_mem_free(msg1); + return HTTP_CLIENT_ERROR_NO_MEMORY; + } + memset(msg1->param.Uri, 0, len + 1); + memcpy(msg1->param.Uri,msg->param.Uri,len); + } + + // Open the HTTP request handle + msg->pSession= HTTPClientOpenRequest(0); + if(!msg->pSession) + { + if(msg1->sendData) + tls_mem_free(msg1->sendData); + if(msg1->param.Uri) + tls_mem_free(msg1->param.Uri); + tls_mem_free(msg1); + return HTTP_CLIENT_ERROR_INVALID_HANDLE; + } + msg1->pSession = msg->pSession; + err = sys_mbox_trypost(&http_client_mbox, msg1); + //printf("post err=%d\n", err); + if(err != ERR_OK) + { + HTTPClientCloseRequest(&msg->pSession); + if(msg1->sendData) + tls_mem_free(msg1->sendData); + if(msg1->param.Uri) + tls_mem_free(msg1->param.Uri); + tls_mem_free(msg1); + } + return err; +} + + +#endif //TLS_CONFIG_HTTP_CLIENT_TASK + diff --git a/sdk/Src/Network/CMakeLists.txt b/sdk/Src/Network/CMakeLists.txt new file mode 100644 index 0000000..a072df0 --- /dev/null +++ b/sdk/Src/Network/CMakeLists.txt @@ -0,0 +1,91 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WMLWIP) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin ") +STRING(APPEND CMAKE_C_FLAGS "-DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/api2.0.3/tls_sockets.c" + "${CMAKE_CURRENT_SOURCE_DIR}/api2.0.3/tls_netconn.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/dns.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/def.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/init.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/mem.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/memp.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/netif.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/pbuf.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/raw.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/stats.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/sys.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/tcp.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/tcp_in.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/tcp_out.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/udp.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/inet_chksum.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ip.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/timeouts.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/alg.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv4/ip4_frag.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv4/ip4_addr.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv4/ip4.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv4/dhcp.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv4/etharp.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv4/igmp.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv4/icmp.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv4/autoip.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/ip6_frag.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/ip6_addr.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/ip6.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/dhcp6.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/ethip6.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/icmp6.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/inet6.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/mld6.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/core/ipv6/nd6.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/sys_arch.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/netif/ethernetif.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/netif/ethernet.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/netif/wm_ethernet.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/api/tcpip.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/api/api_lib.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/api/api_msg.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/api/err.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/api/netbuf.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/api/netdb.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/api/netifapi.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/api/sockets.c" + "${CMAKE_CURRENT_SOURCE_DIR}/lwip2.0.3/apps/mdns/mdns.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmlwip.a) \ No newline at end of file diff --git a/sdk/Src/Network/api2.0.3/tls_netconn.c b/sdk/Src/Network/api2.0.3/tls_netconn.c new file mode 100644 index 0000000..b86cf42 --- /dev/null +++ b/sdk/Src/Network/api2.0.3/tls_netconn.c @@ -0,0 +1,1685 @@ +/************************************************************************** + * File Name : tls_netconn.c + * Author : + * Version : + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ +#include "wm_config.h" + +#if TLS_CONFIG_SOCKET_RAW + +#include "tls_netconn.h" +#include "wm_debug.h" +#include "wm_regs.h" +#include "lwip/sys.h" +#include "lwip/tcp.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" +#include +#include "wm_sockets.h" + +struct tls_netconn *p_net_conn[TLS_MAX_NETCONN_NUM]; +#if CONN_SEM_NOT_FREE +sys_sem_t conn_op_completed[TLS_MAX_NETCONN_NUM] = {NULL}; +#endif + +//static err_t net_tcp_sent_cb(void *arg, struct tcp_pcb *pcb, u16_t len); +static void net_tcp_err_cb(void *arg, err_t err); +static err_t net_tcp_poll_cb(void *arg, struct tcp_pcb *pcb); +static void net_free_socket(int socketno); + +//static err_t net_do_writemore(struct tls_netconn *conn); + +#define BUF_CIRC_CNT_TO_END(head,tail,size) \ + ({int end = (size) - (tail); \ + int n = ((head) + end) & ((size)-1); \ + n < end ? n : end;}) + + +u32 current_src_ip = 0; +void tls_net_set_sourceip(u32 ipvalue) +{ + current_src_ip = ipvalue; +} + +u32 tls_net_get_sourceip(void) +{ + return current_src_ip; +} + +struct tls_netconn *get_server_conn(struct tls_netconn *conn) +{ + struct tls_netconn *server_conn = NULL; + + do{ + server_conn = dl_list_first(&conn->list, struct tls_netconn, list); + conn = server_conn; + }while((server_conn != NULL) && server_conn->client); + + return server_conn; +} + +static struct tls_netconn *net_alloc_socket(struct tls_netconn *conn) +{ + int sock=-1, i=0, j=0; + u32 cpu_sr; + struct tls_netconn * conn_t = NULL; + + for (i = 0; i < TLS_MAX_NETCONN_NUM; i++) + { + if(p_net_conn[i] == NULL) + { + sock = i; + break; + } + } + + if (i == TLS_MAX_NETCONN_NUM){ + TLS_DBGPRT_ERR("\nnet_alloc_socket error\n"); + return NULL; + } + + if(conn != NULL) + { + j = dl_list_len(&conn->list); + if(j>=4) + { + TLS_DBGPRT_ERR("list len > 4\n"); + sock = -1; + } + } + if(sock < 0) + { + TLS_DBGPRT_ERR("sock < 0\n"); + return NULL; + } + cpu_sr = tls_os_set_critical(); + conn_t = tls_mem_alloc(sizeof(struct tls_netconn)); + tls_os_release_critical(cpu_sr); + if (NULL != conn_t) { + p_net_conn[sock] = conn_t; + memset(conn_t, 0, sizeof(struct tls_netconn)); + conn_t->used = true; + conn_t->state = NETCONN_STATE_NONE; + conn_t->skt_num = sock + 1;//TLS_MAX_NETCONN_NUM + + dl_list_init(&conn_t->list); +#if CONN_SEM_NOT_FREE + TLS_DBGPRT_INFO("conn_op_completed[%d]=%x\n",sock, conn_op_completed[sock]); + if(NULL == conn_op_completed[sock]) + { + if(sys_sem_new(&conn_op_completed[sock], 0) != ERR_OK) + { + net_free_socket(conn_t->skt_num); + } + } +#else + if (sys_sem_new(&conn_t->op_completed, 0) != ERR_OK) { + net_free_socket(conn_t->skt_num); + } +#endif + } + TLS_DBGPRT_INFO("net_alloc_socket conn ptr = 0x%x\n", conn_t); + return conn_t; + +// return NULL; +} + +static void net_free_socket(int socketno) +{ + int index; + u32 cpu_sr; + struct tls_netconn *conn = NULL; + + conn = tls_net_get_socket(socketno); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used); + return; + } + TLS_DBGPRT_INFO("conn ptr = 0x%x\n", conn); +#if !CONN_SEM_NOT_FREE + if (NULL != conn->op_completed) + { + sys_sem_free(&conn->op_completed); + } +#endif + conn->used = false; + if(conn->client && conn->list.prev != NULL && conn->list.prev != &conn->list) + { + TLS_DBGPRT_INFO("del from list.\n"); + cpu_sr = tls_os_set_critical(); + dl_list_del(&conn->list); + tls_os_release_critical(cpu_sr); + } + index = conn->skt_num - 1;//TLS_MAX_NETCONN_NUM - + if (conn->pcb.tcp) + { + tcp_close(conn->pcb.tcp); + conn->pcb.tcp = NULL; + } + tls_mem_free(conn); + cpu_sr = tls_os_set_critical(); + conn = NULL; + p_net_conn[index] = NULL; + tls_os_release_critical(cpu_sr); +} + +static void net_send_event_to_hostif(struct tls_netconn *conn, + int event) +{ + struct tls_socket_desc *skt_desc = conn->skd; + TLS_DBGPRT_INFO("skt_desc->state_changed: 0x%x, event=%d\n", skt_desc->state_changed, event); + if(skt_desc->state_changed) + { + skt_desc->state_changed(conn->skt_num, event, conn->state); + } +} + +static void net_tcp_close_connect(int socketno) +{ + err_t err; + struct tls_netconn *conn = NULL; + conn = tls_net_get_socket(socketno); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used); + return; + } + if (NULL == conn->pcb.tcp){ + TLS_DBGPRT_ERR("\ntcp=null\n"); + return; + } + /* Set back some callback pointers */ + tcp_arg(conn->pcb.tcp, NULL); + if (conn->pcb.tcp->state == LISTEN) { + tcp_accept(conn->pcb.tcp, NULL); + } else { + /* some callbacks have to be reset if tcp_close is not successful */ + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + tcp_sent(conn->pcb.tcp, NULL); + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } + err = tcp_close(conn->pcb.tcp); + if (err) + err = tcp_shutdown(conn->pcb.tcp, 1, 1); + if (err == ERR_OK) { + /* Closing succeeded */ + TLS_DBGPRT_INFO("tcp %d closed\n", conn->skt_num); + conn->state = NETCONN_STATE_NONE; + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + net_free_socket(socketno); + /* Trigger select() in socket layer. Make sure everybody notices activity + on the connection, error first! */ + } else { + /* Closing failed, restore some of the callbacks */ + /* Closing of listen pcb will never fail! */ + LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN)); + //tcp_sent(conn->pcb.tcp, net_tcp_sent_cb); + tcp_poll(conn->pcb.tcp, net_tcp_poll_cb, 4); + tcp_err(conn->pcb.tcp, net_tcp_err_cb); + tcp_arg(conn->pcb.tcp, (void *)socketno); + /* don't restore recv callback: we don't want to receive any more data */ + } +} + +static err_t net_tcp_poll_cb(void *arg, struct tcp_pcb *pcb) +{ + err_t err_ret = ERR_OK; + struct tls_netconn *conn = NULL; + struct tls_netconn *server_conn = NULL; + int socketno = (int)arg; + conn = tls_net_get_socket(socketno); + if(conn == NULL || TRUE != conn->used || NULL == pcb) + { + TLS_DBGPRT_ERR("\nconn=%x,used=%d,pcb=%x\n",conn, conn->used, pcb); + return ERR_ARG; + } + //TLS_DBGPRT_INFO("net_tcp_poll_cb start\n"); +/* + if (conn == NULL || pcb == NULL) { + TLS_DBGPRT_ERR("\npcb=null\n"); + return ERR_CLSD; + } +*/ + if (conn->write_state && (conn->state == NETCONN_STATE_CONNECTED)) { + //net_do_writemore(conn); + } else if (conn->state == NETCONN_STATE_CLOSED) { + net_tcp_close_connect(socketno); + } else { + if ((pcb->state == CLOSE_WAIT) || (pcb->state == CLOSED)) { + if (conn->pcb.tcp != NULL) { + tcp_arg(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + tcp_recv(conn->pcb.tcp, NULL); + tcp_sent(conn->pcb.tcp, NULL); + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + tcp_close(conn->pcb.tcp); + conn->state = NETCONN_STATE_NONE; + net_send_event_to_hostif(conn, NET_EVENT_TCP_DISCONNECT); + conn->pcb.tcp = NULL; + net_free_socket(socketno); + } + } + } + + if (conn){ + if(conn->client && conn->idle_time > 0) + { + TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n", conn->skt_num, conn->client); + // server_conn = dl_list_first(&conn->list, struct tls_netconn, list); + server_conn = get_server_conn(conn); + if(server_conn) + { + --conn->idle_time; + if(conn->idle_time == 0) + { + tcp_abort(pcb); + //tls_socket_close(conn->skt_num); + TLS_DBGPRT_INFO("update conn->idle_time %d\n", conn->idle_time); + return ERR_ABRT; + } + } + } + if((conn->skd) != NULL && (conn->skd->pollf) != NULL) + { + err_ret = conn->skd->pollf(conn->skt_num); + if(err_ret == ERR_ABRT) + tcp_abort(pcb); + return err_ret; + } + } + + return ERR_OK; + +} + +static void net_tcp_err_cb(void *arg, err_t err) +{ + struct tls_netconn *conn = NULL; + struct tcp_pcb *pcb = NULL; + int socketno = (int)arg; + u8 event = NET_EVENT_TCP_CONNECT_FAILED; + + conn = tls_net_get_socket(socketno); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used); + return; + } + pcb = conn->pcb.tcp; + TLS_DBGPRT_INFO("tcp err = %d, pcb==%x, conn==%x, skt==%x\n", err, (char *)pcb, (char *)conn, conn->skd); + + if (pcb) { + tcp_arg(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_err(pcb, NULL); + if (!conn->client) { + tcp_accept(pcb, NULL); + } + if(err == ERR_OK) + { + tcp_close(pcb); + } + if(conn->state != NETCONN_STATE_NONE) + { + conn->state = NETCONN_STATE_NONE; + event = NET_EVENT_TCP_DISCONNECT; + } + + net_send_event_to_hostif(conn, event); + if(conn->skd->errf != NULL) + { + conn->skd->errf(conn->skt_num, err); + } + conn->pcb.tcp = NULL; + net_free_socket(socketno); + //printf("\nfree socket\n"); + } +} + +#if (RAW_SOCKET_USE_CUSTOM_PBUF) +static struct raw_sk_pbuf_custom* +raw_sk_alloc_pbuf_custom(void) +{ + return (struct raw_sk_pbuf_custom*)mem_malloc(sizeof(struct raw_sk_pbuf_custom)); +} + + +static void +raw_sk_free_pbuf_custom(struct raw_sk_pbuf_custom* p) +{ + if(p != NULL) + { + mem_free(p); + p = NULL; + } +} + +static void +raw_sk_free_pbuf_custom_fn(struct pbuf *p) +{ + struct raw_sk_pbuf_custom *pcr = (struct raw_sk_pbuf_custom*)p; + + if(p != NULL) + { + //printf("\nraw_sk_free_pbuf_custom_fn,len=%d, custom pcb=%x, conn=%x, conn pcb=%x\n\n",p->tot_len, pcr->pcb, pcr->conn, ((struct tls_netconn *)pcr->conn)->pcb.tcp); + if(TRUE == ((struct tls_netconn *)pcr->conn)->used && pcr->pcb == ((struct tls_netconn *)pcr->conn)->pcb.tcp) + { + tcp_recved((struct tcp_pcb *)pcr->pcb, p->tot_len); + } + + if (pcr->original != NULL) { + // printf("\ngo to pbuf_free,original pbuf=%x\n",pcr->original); + pbuf_free(pcr->original); + } + raw_sk_free_pbuf_custom(pcr); + } +} + +#endif + + +/** + * Receive callback function for TCP connect. + */ +static err_t net_tcp_recv_cb(void *arg, + struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct tls_netconn *conn; + struct tls_netconn *server_conn = NULL; + err_t err_ret = ERR_OK; + u16 datalen = 0; +#if (RAW_SOCKET_USE_CUSTOM_PBUF) + struct raw_sk_pbuf_custom *pcr = NULL; + struct pbuf *newpbuf = NULL; +#endif + struct pbuf *p_tmp,*p_next; + int socketno = -1; + + // TLS_DBGPRT_INFO("net_tcp_recv_cb err=%d\n", err); + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("must have a pcb argument", pcb != NULL); + LWIP_ASSERT("must have an argument", arg < 0); + socketno = (int)arg; + conn = tls_net_get_socket(socketno); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used); + return ERR_ARG; + } + LWIP_ASSERT("tcp recv for wrong pcb!", conn->pcb.tcp == pcb); + + if (err) { + /* tcp is disconnect */ + TLS_DBGPRT_INFO("err code = %d\n", err); + err_ret = err; +#if 0 + if (conn->pcb.tcp != NULL) { + tcp_arg(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + tcp_recv(conn->pcb.tcp, NULL); + tcp_sent(conn->pcb.tcp, NULL); + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + tcp_close(conn->pcb.tcp); + conn->state = NETCONN_STATE_NONE; + net_send_event_to_hostif(conn, NET_EVENT_TCP_DISCONNECT); + conn->pcb.tcp = NULL; + net_free_socket(conn); + } +#endif + if(conn->skd != NULL && conn->skd->recvf != NULL) + { + TLS_DBGPRT_INFO("conn->skd->recvf to call."); + err_ret = conn->skd->recvf(conn->skt_num, p, err); + if(err_ret == ERR_ABRT) + tcp_abort(pcb); + p = NULL; + } + + if (p){ + pbuf_free(p); + p = NULL; + } + return err_ret; + } + + if (pcb == NULL ||conn == NULL) {//p == NULL || + TLS_DBGPRT_INFO("pcb = 0x%x, p = 0x%x\n", pcb ,p); + return ERR_VAL; + } + + if(p == NULL) + { + TLS_DBGPRT_ERR("received 0\n"); + net_tcp_err_cb((void *)socketno, ERR_OK); + return ERR_OK; + } +#if 0//(RAW_SOCKET_USE_CUSTOM_PBUF) + pcr = raw_sk_alloc_pbuf_custom(); + if(pcr == NULL) + { + return ERR_MEM; + } + //newpbuf = pbuf_alloced_custom(PBUF_RAW, p->tot_len, PBUF_REF, &pcr->pc, p->payload, p->tot_len); + + newpbuf = &(pcr->pc); + newpbuf->len = 0; + newpbuf->payload = NULL; + newpbuf->tot_len = p->tot_len; + newpbuf->next = p; + newpbuf->flags = PBUF_FLAG_IS_CUSTOM; + newpbuf->type = PBUF_REF; + newpbuf->ref =1; + + if (newpbuf == NULL) { + raw_sk_free_pbuf_custom(pcr); + return ERR_MEM; + } + pcr->original = p; + pcr->param = pcb; + pcr->pc.custom_free_function = raw_sk_free_pbuf_custom_fn; +#endif +#if !RAW_SOCKET_USE_CUSTOM_PBUF + tcp_recved(pcb, p->tot_len); +#endif + + if(conn->client && conn->idle_time > 0) + { + TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n", conn->skt_num, conn->client); +// server_conn = dl_list_first(&conn->list, struct tls_netconn, list); + server_conn = get_server_conn(conn); + TLS_DBGPRT_INFO("server_conn=%p\n", server_conn); + if(server_conn) + { + conn->idle_time = server_conn->idle_time; + TLS_DBGPRT_INFO("update conn->idle_time %d\n", conn->idle_time); + } + } + if(conn->skd->recvf != NULL) + { + tls_net_set_sourceip(ip_addr_get_ip4_u32(&pcb->remote_ip)); + datalen = p->tot_len; + if (conn->skd->recvwithipf != NULL){ + //conn->skd->recvwithipf(conn->skt_num, datalen, (u8 *)(&(pcb->remote_ip.addr)), pcb->remote_port, ERR_OK); + err_ret = conn->skd->recvwithipf(conn->skt_num, datalen, (u8 *)(&(ip_2_ip4(&pcb->remote_ip)->addr)), pcb->remote_port, ERR_OK); + if(err_ret == ERR_ABRT) + tcp_abort(pcb); + } + + p_next = p; + for(p_tmp = p; p_tmp != NULL; ) + { + p_next = p_tmp->next; + p_tmp->next = NULL; + if(err_ret == ERR_ABRT) + { + pbuf_free(p_tmp); + } + else if(err_ret == ERR_OK) + { + if(p_next != NULL) + printf("\npbufcat p next=%x\n",(u32)p_next); +#if (RAW_SOCKET_USE_CUSTOM_PBUF) + pcr = raw_sk_alloc_pbuf_custom(); + if(pcr == NULL) + { + return ERR_MEM; + } + + newpbuf = pbuf_alloced_custom(PBUF_RAW, p_tmp->len, PBUF_REF, &pcr->pc, p_tmp->payload, p_tmp->len); + if (newpbuf == NULL) { + raw_sk_free_pbuf_custom(pcr); + return ERR_MEM; + } + pcr->original = p_tmp; + pcr->conn = conn; + pcr->pcb = pcb; + pcr->pc.custom_free_function = raw_sk_free_pbuf_custom_fn; + + err_ret = conn->skd->recvf(conn->skt_num, newpbuf, ERR_OK); +#else + err_ret = conn->skd->recvf(conn->skt_num, p_tmp, ERR_OK); +#endif + if(err_ret == ERR_ABRT) + tcp_abort(pcb); + } + p_tmp = p_next; + } + + } + else + { +#if 0//(RAW_SOCKET_USE_CUSTOM_PBUF) + raw_sk_free_pbuf_custom(pcr); +#endif + if (p) + pbuf_free(p); + } + + return err_ret; +} + +#if 0 +static err_t net_tcp_sent_cb(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct tls_netconn *conn = (struct tls_netconn *)arg; + + //TLS_DBGPRT_INFO("s\n"); + if (conn->write_state && (conn->state == NETCONN_STATE_CONNECTED)) { + net_do_writemore(conn); + } else if (conn->state == NETCONN_STATE_CLOSED) { + net_tcp_close_connect(conn); + } + + return ERR_OK; +} +#endif + +/** + * tcp connnect callback + */ +static err_t net_tcp_connect_cb(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct tls_netconn *conn; + err_t err_ret = ERR_OK; + int socketno = -1; + + socketno = (int)arg; + conn = tls_net_get_socket(socketno); + if(conn == NULL || conn->used != TRUE){ + TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used); + tcp_abort(pcb); + return ERR_ABRT; + } + //pcb = conn->pcb.tcp; + if ((conn->proto == TLS_NETCONN_TCP) && (err == ERR_OK)) { + TLS_DBGPRT_INFO("net_tcp_connect_cb =====> state : %d\n", pcb->state); + + conn->state = NETCONN_STATE_CONNECTED; + net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECTED); + } else { + TLS_DBGPRT_INFO("the err is =%d\n", err); + } + + if(conn->skd != NULL && conn->skd->connf != NULL) + { + err_ret = conn->skd->connf(conn->skt_num, err); + if(err_ret == ERR_ABRT) + tcp_abort(pcb); + return err_ret; + } + + return err; +} + +/** + * Accept callback function for TCP netconns. + */ +static err_t net_tcp_accept_cb(void *arg, + struct tcp_pcb *newpcb, err_t err) +{ + err_t err_ret = ERR_OK; + struct tls_netconn *conn = NULL; + struct tls_netconn *newconn; + struct tcp_pcb *pcb; + u32 cpu_sr; + int socketno = -1; + + //TLS_DBGPRT_INFO("=====>\n"); +#if 0 + if (conn->tcp_connect_cnt >= TLS_MAX_SOCKET_NUM) { + return ERR_ABRT; + } +#endif + socketno = (int)arg; + conn = tls_net_get_socket(socketno); + if(conn == NULL || conn->used != TRUE){ + TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used); + tcp_abort(newpcb); + return ERR_ABRT; + } + + if (conn){ + tcp_accepted(conn->pcb.tcp); + } + + + newconn = net_alloc_socket(conn); + //printf("\nnew conn==%x, new pcb==%x\n",newconn,newpcb); + if (!newconn) { + /* connection aborted */ + /* not happen */ + tcp_abort(newpcb); + //printf("\r\nnet_tcp_accept_cb err\r\n"); + return ERR_ABRT; + } + + //conn->tcp_connect_cnt++; + + newconn->used = true; + newconn->pcb.tcp = newpcb; + newconn->client = true; + newconn->idle_time = conn->idle_time; + newconn->proto = TLS_NETCONN_TCP; + newconn->state = NETCONN_STATE_CONNECTED; + pcb = newconn->pcb.tcp; + ip_set_option(newconn->pcb.tcp, SOF_KEEPALIVE); + newconn->skd = conn->skd; + tcp_arg(pcb, (void *)(newconn->skt_num)); + tcp_recv(pcb, net_tcp_recv_cb); + //tcp_sent(pcb, net_tcp_sent_cb); + tcp_poll(pcb, net_tcp_poll_cb, 2); + tcp_err(pcb, net_tcp_err_cb); + cpu_sr = tls_os_set_critical(); + dl_list_add_tail(&conn->list, &newconn->list); + tls_os_release_critical(cpu_sr); + newconn->port = host_to_le16(pcb->remote_port); + newconn->localport = conn->localport; + // ip_addr_set(&newconn->addr, &pcb->remote_ip); + ip_addr_copy(newconn->addr, pcb->remote_ip); + net_send_event_to_hostif(newconn, NET_EVENT_TCP_JOINED); + if(conn->skd->acceptf != NULL) + { + err_ret =conn->skd->acceptf(newconn->skt_num, err); + if(err_ret == ERR_ABRT) + { + tcp_abort(pcb); + } + } + //conn->state = NETCONN_STATE_CONNECTED; + + return err_ret; +} + +/** + * Receive callback function for UDP netconns. + */ +static void net_udp_recv_cb(void *arg, struct udp_pcb *pcb, + struct pbuf *p, const ip_addr_t *srcaddr, u16_t port) +{ + struct tls_netconn *conn; + u32 datalen = 0; + int socketno = -1; + + //TLS_DBGPRT_INFO("=====>\n"); + LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ + LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_udp must have an argument", arg < 0); + socketno = (int)arg; + conn = tls_net_get_socket(socketno); + if(conn == NULL || conn->used != TRUE || NULL == pcb){ + TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used); + if (p != NULL) + pbuf_free(p); + return; + } + LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); +/* + if (conn == NULL || pcb == NULL) { + TLS_DBGPRT_INFO("if (conn == NULL || pcb == NULL) \n"); + if (p != NULL) + pbuf_free(p); + return; + } +*/ + if(conn->skd->recvf != NULL) + { + datalen = p->tot_len; + /*if Address is broadcast, update source IP according to source address and source port according to sender*/ + +// if ((conn->addr.addr == IPADDR_BROADCAST)||((conn->addr.addr & 0xFF) == 0xFF)) + if((ip_addr_get_ip4_u32(&conn->addr) == IPADDR_BROADCAST) || ((ip_addr_get_ip4_u32(&conn->addr) & 0xFF) == 0xFF)) + { + tls_net_set_sourceip(ip_addr_get_ip4_u32(srcaddr)); + conn->port = port; + }else{ + tls_net_set_sourceip(ip_addr_get_ip4_u32(&conn->addr)); + } + + + if (conn->skd->recvwithipf != NULL){ + conn->skd->recvwithipf(conn->skt_num, datalen, (u8 *)(&(ip_2_ip4(srcaddr)->addr)), port, ERR_OK); + } + + conn->skd->recvf(conn->skt_num, p, ERR_OK); + } + else + { + if (p){ + pbuf_free(p); + p = NULL; + } + } + + return; +} + + +/** + * Start create TCP connection + */ +static err_t net_tcp_start(struct tls_netconn *conn) +{ + struct tcp_pcb *pcb; + err_t err; + u16 localportnum = conn->localport; + + + //TLS_DBGPRT_INFO("=====>\n"); + conn->pcb.tcp = tcp_new(); + pcb = conn->pcb.tcp; + + if (pcb == NULL) { + TLS_DBGPRT_INFO("could not allocate tcp pcb\n"); + return ERR_VAL; + } + + tcp_arg(pcb, (void *)(conn->skt_num)); + + if (conn->client) { + TLS_DBGPRT_INFO("pcb = 0x%x, conn->addr = 0x%x, port = %d, localport=%d\n", + pcb, ip_addr_get_ip4_u32(&conn->addr), conn->port, conn->localport); + tcp_err(pcb, net_tcp_err_cb); + tcp_recv(pcb, net_tcp_recv_cb); + //tcp_nagle_disable(pcb); + if (pcb->recv != NULL) { + TLS_DBGPRT_INFO("pcb->recv != NULL\n"); + } + //tcp_sent(pcb, net_tcp_sent_cb); + tcp_poll(pcb, net_tcp_poll_cb, 4); + ip_set_option(pcb, SOF_KEEPALIVE); + if(localportnum > 0 && localportnum <= 0xFFFF) + { + err = tcp_bind(pcb, IP_ADDR_ANY, localportnum); + if (err != ERR_OK) + { + TLS_DBGPRT_INFO("tcp bind failed %d\n", err); + return err; + } + } + err = tcp_connect(pcb, &conn->addr, conn->port, net_tcp_connect_cb); + if (err != ERR_OK) { + TLS_DBGPRT_INFO("tcp connect failed %d\n", err); + return err; + } + if (conn->localport == 0){ + conn->localport = pcb->local_port; + } + } else { + TLS_DBGPRT_INFO("pcb ptr = 0x%x, conn->port = %d\n", + pcb, conn->port); + ip_set_option(pcb, SOF_REUSEADDR); + err = tcp_bind(pcb, IP_ADDR_ANY, conn->port); + if (err != ERR_OK) { + TLS_DBGPRT_INFO("tcp bind failed %d\n", err); + return err; + } + conn->pcb.tcp = tcp_listen(pcb); + //ip_set_option(conn->pcb.tcp, SOF_KEEPALIVE); + if (conn->pcb.tcp == NULL) { + tcp_close(pcb); + /* create tcp sever failed */ + TLS_DBGPRT_INFO("tcp listen failed\n"); + return ERR_VAL; + } + tcp_arg(conn->pcb.tcp, (void *)(conn->skt_num)); + tcp_accept(conn->pcb.tcp, net_tcp_accept_cb); + } + + return ERR_OK; +} + +/** + * Start create UDP connection + */ +static err_t net_udp_start(struct tls_netconn *conn) +{ + struct udp_pcb *udppcb; + u16 localportnum = conn->localport; + + udppcb = udp_new(); + + if(udppcb == NULL) { + return ERR_MEM; + } + conn->pcb.udp = udppcb; + if (udp_bind(udppcb, IP_ADDR_ANY, localportnum)!= ERR_OK){ + TLS_DBGPRT_INFO("udp connect failed\n"); + return ERR_CONN; + } + + if (conn->localport == 0){ + conn->localport = udppcb->local_port; + } + + /* Set a receive callback for the upcb */ + udp_recv(udppcb, net_udp_recv_cb, (void *)(conn->skt_num)); + + return ERR_OK; +} +#if 0 +static err_t net_do_writemore(struct tls_netconn *conn) +{ + err_t err = ERR_OK; + void *dataptr; + u16 len, available; + u8 write_finished = 0; + u32 diff; + u8 apiflags = TCP_WRITE_FLAG_COPY; + u8 dontblock = 0; + + dataptr = (u8_t*)conn->current_msg->dataptr + conn->current_msg->write_offset; + diff = conn->current_msg->len - conn->current_msg->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; + apiflags |= TCP_WRITE_FLAG_MORE; + } else { + len = (u16_t)diff; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; + if (dontblock){ + if (!len) { + err = ERR_WOULDBLOCK; + goto err_mem; + } + } else { + apiflags |= TCP_WRITE_FLAG_MORE; + } + } + err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); + +err_mem: + if (err == ERR_OK) { + conn->current_msg->write_offset += len; + if ((conn->current_msg->write_offset == conn->current_msg->len) || dontblock) { + /* everything was written */ + write_finished = 1; + conn->current_msg->write_offset = 0; + } + tcp_output(conn->pcb.tcp); + } else if ((err == ERR_MEM) && !dontblock) { + /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called + we do NOT return to the application thread, since ERR_MEM is + only a temporary error! */ + + /* tcp_write returned ERR_MEM, try tcp_output anyway */ + tcp_output(conn->pcb.tcp); + + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + write_finished = 1; + conn->current_msg->len = 0; + } + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + conn->current_msg->err = err; + conn->write_state = false; + + tls_mem_free(conn->current_msg); + conn->current_msg = NULL; + } + return ERR_OK; +} +#endif + +static err_t net_skt_tcp_send(struct tls_net_msg *net_msg) +{ + struct tcp_pcb *pcb = NULL; + struct tls_netconn *conn; + err_t err; + //TLS_DBGPRT_INFO("=====>\n"); + conn = tls_net_get_socket(net_msg->skt_no); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("conn =%x,used=%d\n", conn, conn->used); + return ERR_ARG; + } + pcb = conn->pcb.tcp; + /* + When tcp error occured, lwip will delete the pcb and sometimes GSKT. + This function maybe registered by GSKT_TimerSend, so we must check if GSKT has been delted!!! + */ + err = tcp_write(pcb, net_msg->dataptr, net_msg->len, TCP_WRITE_FLAG_COPY); + if (err == ERR_OK){ + //sys_sem_signal(&net_msg->conn->op_completed); + tcp_output(pcb); + } + else + { + // TLS_DBGPRT_INFO("err:%d\n", err); + } + return err; +} + + +/** + * Send data on a UDP pcb + */ +static void net_do_send(void *ctx) +{ + struct tls_net_msg *msg = (struct tls_net_msg *)ctx; + struct tls_netconn *conn = NULL; + struct pbuf *p; + int socketno = msg->skt_no; + + //TLS_DBGPRT_INFO("=====>\n"); + + p = msg->p; + conn = tls_net_get_socket(socketno); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("conn =%x,used=%d\n", conn, conn->used); + if(p) + pbuf_free(p); +#if CONN_SEM_NOT_FREE + sys_sem_signal(&conn_op_completed[socketno - 1]); +#endif + + return; + } +#if LWIP_CHECKSUM_ON_COPY + if (ip_addr_isany(&msg->addr)) { + msg->err = udp_send_chksum(conn->pcb.udp, p, + 0, 0); + } else { + msg->err = udp_sendto_chksum(conn->pcb.udp, p, + &msg->addr, msg->port, + 0, 0); + } +#else /* LWIP_CHECKSUM_ON_COPY */ + if (ip_addr_isany(&msg->addr)) { + msg->err = udp_send(conn->pcb.udp, p); + } else { + msg->err = udp_sendto(conn->pcb.udp, p, + &msg->addr, msg->port); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + + pbuf_free(p); +#if CONN_SEM_NOT_FREE + sys_sem_signal(&conn_op_completed[socketno - 1]); +#else + conn = tls_net_get_socket(socketno); + if(conn && TRUE == conn->used) + { + sys_sem_signal(&conn->op_completed); + } +#endif +} + +/** + * Send data on a TCP pcb + */ +static void net_do_write(void *ctx) +{ + struct tls_net_msg *net_msg = (struct tls_net_msg *)ctx; + struct tls_netconn *conn = NULL; + struct tls_netconn *server_conn = NULL; + + int socketno = -1; + //TLS_DBGPRT_INFO("=====>\n"); + socketno = net_msg->skt_no; + conn = tls_net_get_socket(socketno); + if(conn == NULL ||TRUE != conn->used) + { + TLS_DBGPRT_ERR("\n conn=%x,used=%d\n",conn, conn->used); +#if CONN_SEM_NOT_FREE + sys_sem_signal(&conn_op_completed[socketno - 1]); +#endif + return; + } + + //TLS_DBGPRT_INFO("s=%d,p=0x%x\n", conn->state, conn->pcb.tcp); + +#if 0 + if (ERR_IS_FATAL(conn->last_err)) { + net_msg->err = conn->last_err; + } +#endif + if (conn->proto == TLS_NETCONN_TCP) { +#if LWIP_TCP + if (conn->state != NETCONN_STATE_CONNECTED) { + /* netconn is connecting, closing or in blocking write */ + net_msg->err = ERR_INPROGRESS; + } else if (conn->pcb.tcp != NULL) { + conn->write_state = true; + //conn->write_offset = 0; + net_msg->err = net_skt_tcp_send(net_msg); + /* for both cases: if do_writemore was called, don't ACK the APIMSG + since do_writemore ACKs it! */ + } else { + net_msg->err = ERR_CONN; + TLS_DBGPRT_INFO("==>err=%d\n", net_msg->err); + } + if(conn->client && conn->idle_time > 0) + { + TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n", conn->skt_num, conn->client); +// server_conn = dl_list_first(&conn->list, struct tls_netconn, list); + server_conn = get_server_conn(conn); + TLS_DBGPRT_INFO("server_conn=%p\n", server_conn); + if(server_conn) + { + conn->idle_time = server_conn->idle_time; + TLS_DBGPRT_INFO("update conn->idle_time %d\n", conn->idle_time); + } + } +#else /* LWIP_TCP */ + net_msg->err = ERR_VAL; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + net_msg->err = ERR_VAL; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + //tls_mem_free(net_msg->dataptr); + //if(net_msg->err != ERR_OK) + { + //TLS_DBGPRT_INFO("conn->proto=%d, err=%d\n", conn->proto, net_msg->err); + //TLS_DBGPRT_INFO("free net_msg->dataptr=%p\n", net_msg->dataptr); + +#if CONN_SEM_NOT_FREE + sys_sem_signal(&conn_op_completed[socketno - 1]); +#else + conn = tls_net_get_socket(socketno); + if(conn && TRUE == conn->used) + { + sys_sem_signal(&conn->op_completed); + } +#endif + } +#if 0 + tls_mem_free(net_msg); +#endif +} + +static void do_create_connect(void *ctx) +{ + struct tls_net_msg *net_msg = (struct tls_net_msg *)ctx; + struct tls_netconn *conn; + err_t err; + int socketno = -1; + //TLS_DBGPRT_INFO("=====>\n"); + socketno = net_msg->skt_no; + conn = tls_net_get_socket(socketno); + if(conn == NULL || TRUE != conn->used) + { + tls_mem_free(net_msg); + TLS_DBGPRT_ERR("\nconn=%x,usde=%d\n",conn, conn->used); + return; + } + TLS_DBGPRT_INFO("conn ptr = 0x%x, conn->skt_num=%d, conn->client=%d\n", conn, conn->skt_num, conn->client); + + switch (conn->proto) { + case TLS_NETCONN_UDP: + err = net_udp_start(conn); + if (err != ERR_OK){ + conn->state = NETCONN_STATE_NONE; + net_send_event_to_hostif(conn, NET_EVENT_UDP_START_FAILED); + net_free_socket(socketno); + }else{ + conn->state = NETCONN_STATE_CONNECTED; + net_send_event_to_hostif(conn, NET_EVENT_UDP_START); + } + break; + case TLS_NETCONN_TCP: + err = net_tcp_start(conn); + if (err != ERR_OK){ + conn->state = NETCONN_STATE_NONE; + net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECT_FAILED); + net_free_socket(socketno); + }else{ + if (!conn->client){ + net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECTED); + } + //conn->state = NETCONN_STATE_CONNECTED; + } + break; + default: + /* Unsupported netconn type, e.g. protocol disabled */ + break; + } + tls_mem_free(net_msg); + return; +} + +static void do_close_connect(void *ctx) +{ + struct tls_net_msg *net_msg = (struct tls_net_msg *)ctx; + struct tls_netconn *conn = NULL; + struct tls_netconn *client_conn; + int socketno = net_msg->skt_no; + int i; + int sktNums[TLS_MAX_SOCKET_NUM] = {-1};// 2*TLS_MAX_NETCONN_NUM + + conn = tls_net_get_socket(socketno); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("conn==%x,used=%d\n",conn, conn->used); + tls_mem_free(net_msg); + return; + } + + switch (conn->proto) { + case TLS_NETCONN_UDP: + if (conn->pcb.udp != NULL) { + conn->pcb.udp->recv_arg = NULL; + udp_remove(conn->pcb.udp); + conn->pcb.udp = NULL; + } + conn->state = NETCONN_STATE_CLOSED; + break; + case TLS_NETCONN_TCP: + if (!conn->client) { + /* it's a server, close connected client */ + i=0; + dl_list_for_each(client_conn, &conn->list, struct tls_netconn, list){ + if (client_conn->used) { + client_conn->state = NETCONN_STATE_CLOSED; + sktNums[i++] = client_conn->skt_num; + } + } + + while(i-->0) + { + //client_conn = tls_net_get_socket(sktNums[i]); + net_tcp_close_connect(sktNums[i]); + } + } + + conn->state = NETCONN_STATE_CLOSED; + net_tcp_close_connect(socketno); + break; + default: + break; + } + net_free_socket(socketno); + tls_mem_free(net_msg); +} + +int tls_socket_create(struct tls_socket_desc * skd) +{ + struct tls_netconn *conn = NULL; + struct tls_net_msg *net_msg; + err_t err = ERR_OK; + + //TLS_DBGPRT_INFO("=====>socket_type=%d, skd->protocol=%d\n", socket_type, skd->protocol); + + if ((skd->protocol == SOCKET_PROTO_TCP) || (skd->protocol == SOCKET_PROTO_UDP)) { + conn = net_alloc_socket(NULL); + + if (conn) { + conn->client = (skd->cs_mode == SOCKET_CS_MODE_CLIENT); + conn->proto = skd->protocol; + if (conn->client){ + IP_SET_TYPE(&skd->ip_addr, IPADDR_TYPE_V4); + ip_addr_copy(conn->addr, skd->ip_addr); + conn->state = NETCONN_STATE_NONE; + } + else { + if(conn->proto == 1) + ip_addr_set(&conn->addr, IP_ADDR_BROADCAST); + else + ip_addr_set(&conn->addr, IP_ADDR_ANY); + conn->state = NETCONN_STATE_WAITING; + } + conn->port = skd->port; + conn->localport = skd->localport; + conn->idle_time = skd->timeout; + conn->skd = skd; + //conn->cmd_mode = cmd_mode; + + net_msg = tls_mem_alloc(sizeof(*net_msg)); + if (net_msg == NULL) { + net_free_socket(conn->skt_num); + return -1; + } + memset(net_msg, 0, sizeof(*net_msg)); + net_msg->skt_no = conn->skt_num; + err = tcpip_callback_with_block(do_create_connect, net_msg, 0); + if (err) { + conn->state = NETCONN_STATE_NONE; + net_free_socket(conn->skt_num); + tls_mem_free(net_msg); + } + } else { + return 0; + } + } else { + err = ERR_VAL; + } + + if (conn) + return conn->skt_num; + else + return -1; +} + +int tls_socket_get_status(u8 socket, u8 *buf, u32 bufsize) +{ + struct tls_skt_status_t *skt_status; + struct tls_skt_status_ext_t *skts_ext; + struct tls_netconn *conn; + struct tls_netconn *client_conn; + struct tls_netconn *server_conn; + int remain_len; + + skt_status = (struct tls_skt_status_t *)buf; + + if (bufsize < sizeof(struct tls_skt_status_t)) + { + TLS_DBGPRT_ERR("\nbufsize=%d\n",bufsize); + return ERR_VAL; + } + + if (socket < 1 || socket > 20) + { + TLS_DBGPRT_ERR("\nskt num=%d\n",socket); + return ERR_VAL; + } + + memset(buf, 0, bufsize); + + conn = tls_net_get_socket(socket); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("conn==%x,used=%d\n",conn, conn->used); + return ERR_VAL; + } + remain_len = bufsize - sizeof(u32); + skts_ext = (struct tls_skt_status_ext_t *)(buf + sizeof(u32)); + if(conn == NULL){ + skt_status->socket_cnt = 1; + skts_ext->protocol = SOCKET_PROTO_TCP; + skts_ext->status = 0; + skts_ext->socket = socket; + skts_ext->host_ipaddr[0] = 0; + skts_ext->host_ipaddr[1] = 0; + skts_ext->host_ipaddr[2] = 0; + skts_ext->host_ipaddr[3] = 0; + return 0; + } + skts_ext->protocol = (enum tls_socket_protocol)conn->proto; +//TLS_DBGPRT_INFO("conn->client=%d, conn->state=%d, conn->used=%d\n", conn->client, conn->state,conn->used); + if (conn->client) { + skt_status->socket_cnt = 1; + if (conn->used) { + MEMCPY(skts_ext->host_ipaddr, (u8 *)ip_2_ip4(&conn->addr), 4); +// server_conn = dl_list_first(&conn->list, struct tls_netconn, list); + server_conn = get_server_conn(conn); + if(server_conn != NULL){ + // TLS_DBGPRT_INFO("server_conn->skt_num = %d\n", server_conn->skt_num); + skts_ext->local_port = server_conn->localport; + } + else + skts_ext->local_port = conn->localport; + skts_ext->remote_port = conn->port; + skts_ext->status = conn->state; /* connected */ + skts_ext->socket = conn->skt_num; + } else { + skts_ext->status = conn->state; /* disconnect */ + skts_ext->socket = conn->skt_num; + } +//TLS_DBGPRT_INFO("skts_ext->socket=%d, skts_ext->status=%d, conn->state=%d\n", skts_ext->socket, skts_ext->status, conn->state); + } else { + struct tls_ethif * ethif; + ethif = tls_netif_get_ethif(); + skt_status->socket_cnt = 1; + if (conn->used) { + skts_ext->status = conn->state; /* listen */ + //MEMCPY(skts_ext->host_ipaddr, &conn->addr.addr, 4); + //MEMCPY(skts_ext->host_ipaddr, (char *)&sys->ethif->nif->ip_addr.addr, 4); + //MEMCPY(&conn->addr.addr, (char *)&sys->ethif->nif->ip_addr.addr, 4); + //MEMCPY(skts_ext->host_ipaddr, (char *)ðif->ip_addr.addr, 4); + MEMCPY(skts_ext->host_ipaddr, (char *)(ip_2_ip4(ðif->ip_addr)), 4); + if(conn->proto == 1){ + skts_ext->remote_port = conn->port; + skts_ext->local_port = conn->localport; + if(skts_ext->local_port==0) + skts_ext->local_port = skts_ext->remote_port; + MEMCPY(skts_ext->host_ipaddr, IP_ADDR_BROADCAST, 4); + }else{ + skts_ext->local_port = conn->port; + skts_ext->remote_port = 0; + } + skts_ext->socket = conn->skt_num; + + remain_len -= sizeof(struct tls_skt_status_ext_t); + + dl_list_for_each(client_conn, &conn->list, struct tls_netconn, list){ + if (remain_len < sizeof(struct tls_skt_status_ext_t)) + break; + if (client_conn->used) { + skts_ext++; + skts_ext->status = client_conn->state; /* connect */ + skt_status->socket_cnt++; + MEMCPY(skts_ext->host_ipaddr, (char *)ip_2_ip4(&client_conn->addr), 4); + skts_ext->local_port = conn->port; + skts_ext->remote_port = client_conn->port; + skts_ext->socket = client_conn->skt_num; + skts_ext->protocol = (enum tls_socket_protocol)client_conn->proto; + remain_len -= sizeof(struct tls_skt_status_ext_t); + } + } + } else { + skts_ext->status = conn->state; + skts_ext->socket = conn->skt_num; + } + } + return 0; +} + +struct tls_netconn * tls_net_get_socket(u8 socket) +{ + struct tls_netconn *conn = NULL; + + if (socket < 1 || socket > TLS_MAX_NETCONN_NUM )//* 2 + { + TLS_DBGPRT_ERR("skt num=%d\n", socket); + return NULL; + } + + conn = p_net_conn[socket - 1]; + //TLS_DBGPRT_INFO("conn->skt_num = %d\n", conn->skt_num); + return conn; +} + +int tls_socket_close(u8 socket) +{ + struct tls_net_msg *net_msg; + struct tls_netconn *conn; + err_t err; +//TLS_DBGPRT_INFO("socket=%d.\n", socket); + if (socket < 1 || socket > TLS_MAX_NETCONN_NUM)//*2 + { + TLS_DBGPRT_ERR("skt num=%d\n", socket); + return -1; + } + + conn = tls_net_get_socket(socket); + + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("conn==%x,used=%d\n",conn, conn->used); + return -1; + } + net_msg = tls_mem_alloc(sizeof(*net_msg)); + if (net_msg == NULL) { + TLS_DBGPRT_ERR("\nmem err\n"); + return -1; + } + memset(net_msg, 0, sizeof(*net_msg)); + net_msg->skt_no = socket; + err = tcpip_callback_with_block(do_close_connect, net_msg, 0); + if (err) { + tls_mem_free(net_msg); + } + + return err; + +} + +static err_t netconn_msg(tcpip_callback_fn function, struct tls_net_msg *net_msg, u8_t block) +{ + err_t err = ERR_OK; + struct tls_netconn *conn = NULL; + conn = tls_net_get_socket(net_msg->skt_no); + if(conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("netconn_msg conn=%x,used=%d\n",conn, conn->used); + return ERR_ARG; + } +#if 1 + err = tcpip_callback_with_block(function, net_msg, block); + if(err) + { + //TLS_DBGPRT_INFO("free net_msg->dataptr=%p\n", net_msg->dataptr); + //tls_mem_free(net_msg->dataptr); + TLS_DBGPRT_INFO("callback end, err=%d\n", err); + // tls_mem_free(net_msg); + return err; + } +#if CONN_SEM_NOT_FREE + sys_arch_sem_wait(&conn_op_completed[net_msg->skt_no - 1], 0); +#else + conn = tls_net_get_socket(net_msg->skt_no); + if(conn != NULL && conn->op_completed != NULL) + { + sys_arch_sem_wait(&conn->op_completed, 0); + conn = tls_net_get_socket(net_msg->skt_no); + if(conn == NULL || TRUE != conn->used) + { + printf("wait error, conn is freeed\n"); + } + } +#endif + err = net_msg->err; + return err; +#else + struct tls_net_msg *net_msg_item; + if(conn->current_msg == NULL) + { + conn->current_msg = net_msg; + } + else + { + dl_list_add_tail(&conn->current_msg->list, &net_msg->list); + } + //TLS_DBGPRT_INFO("callback\n"); + err = tcpip_callback_with_block(function, conn->current_msg, block); + if(err) + { + TLS_DBGPRT_INFO("callback end, err=%d\n", err); + return err; + } + sys_arch_sem_wait(&conn->op_completed, 0); + //TLS_DBGPRT_INFO("callback end\n"); + if(conn->current_msg->err) + return conn->current_msg->err; + + dl_list_for_each(net_msg_item, &conn->current_msg->list, struct tls_net_msg, list){ + err = tcpip_callback_with_block(function, net_msg_item, block); + if(err) + break; + sys_arch_sem_wait(&conn->op_completed, 0); + if(net_msg_item->err) + break; + dl_list_del(&net_msg_item->list); + tls_mem_free(net_msg_item); + } + if(dl_list_empty(&conn->current_msg->list)) + { + tls_mem_free(conn->current_msg); + conn->current_msg = NULL; + } + else + { + tls_mem_free(conn->current_msg); + conn->current_msg = net_msg_item; + } + return ERR_OK; +#endif +} + + +int tls_socket_udp_sendto(u16 localport, u8 *ip_addr, u16 port, void *pdata, u16 len) +{ + struct tls_netconn *conn; + struct tls_net_msg *net_msg = NULL; + struct pbuf *p; + int i; + int err = 0; + bool found = false; + + /* find match socket */ + for (i = 0; iused + && (localport== conn->port) + && (conn->proto == TLS_NETCONN_UDP)) { + found = true; + break; + } + } + if (!found) { + TLS_DBGPRT_ERR("\nfound=0\n"); + err = -1; + goto out; + } + + net_msg = tls_mem_alloc(sizeof(struct tls_net_msg)); + if(!net_msg) + return ERR_MEM; + + net_msg->len = len; + net_msg->dataptr = pdata; + + /* udp */ + while (1) { + p = pbuf_alloc(PBUF_TRANSPORT, net_msg->len, PBUF_REF); + if (p != NULL) { + break; + } else { + /* delay 1 ticks */ + tls_os_time_delay(1); + } + } + p->payload = (void*)net_msg->dataptr; + p->len = p->tot_len = net_msg->len; + + net_msg->err = ERR_VAL;/* for debug : catch not set err */ + net_msg->p = p; + net_msg->port = port; + net_msg->skt_no = conn->skt_num; + MEMCPY((char *)&(ip_2_ip4(&net_msg->addr)->addr), ip_addr, 4); + IP_SET_TYPE(&net_msg->addr, IPADDR_TYPE_V4); + err = netconn_msg(net_do_send, net_msg, 0); +out: + if( net_msg) /*Who allocates free it*/ + tls_mem_free(net_msg); + return err; +} + + +int tls_socket_send(u8 skt_num, void *pdata, u16 len) +{ + struct tls_net_msg net_msg[1] = {0}; + struct tls_netconn *conn; + struct tls_netconn *client_conn; + struct pbuf *p; + err_t err = ERR_OK; + u32 socket; + u32 errornum = 0; + + socket = skt_num; + + dl_list_init(&net_msg->list); + net_msg->len = len; + net_msg->dataptr = pdata; + if (socket < 1 || socket > TLS_MAX_NETCONN_NUM)//*2 + { + TLS_DBGPRT_ERR("\nskt num=%d\n",socket); + return ERR_VAL; + } + + conn = tls_net_get_socket(socket); + if (conn == NULL || TRUE != conn->used) + { + TLS_DBGPRT_ERR("\nconn=%x,used=%d\n",conn, conn->used); + return ERR_VAL; + } + if (conn->proto == TLS_NETCONN_UDP) + { + net_msg->skt_no = skt_num; + net_msg->err = ERR_VAL;/* for debug : catch not set err */ + do{ + p = pbuf_alloc(PBUF_TRANSPORT, net_msg->len, PBUF_REF); + if (p != NULL) { + break; + } else { + TLS_DBGPRT_INFO("pbuf_alloc err.\n"); + return ERR_VAL; + } + }while (0); + p->payload = (void*)net_msg->dataptr; + p->len = p->tot_len = net_msg->len; + + net_msg->p = p; + net_msg->port = conn->port; + // ip_addr_set(&net_msg->addr, &conn->addr); + ip_addr_copy(net_msg->addr, conn->addr); + + err = netconn_msg(net_do_send, net_msg, 0); + } + else if (conn->proto == TLS_NETCONN_TCP) + { + if (conn->client) + { + net_msg->skt_no = skt_num; + net_msg->err = ERR_VAL;/* for debug : catch not set err */ + err = netconn_msg(net_do_write, net_msg, 0); + } + else + { + dl_list_for_each(client_conn, &conn->list, struct tls_netconn, list) + { + //printf("\nclient conn=%x,used==%d, sock num=%d, net conn=%x\n",client_conn, client_conn->used, client_conn->skt_num, p_net_conn[client_conn->skt_num - 1]); + if (client_conn && TRUE == client_conn->used && client_conn == p_net_conn[client_conn->skt_num - 1]) + { + net_msg->skt_no = client_conn->skt_num; + net_msg->err = ERR_VAL;/* for debug : catch not set err */ + err = netconn_msg(net_do_write, net_msg, 0); + if(err) + { + // TLS_DBGPRT_ERR("\nerr:%d\n",err); + } + } + else + { + struct tls_netconn *conn_tmp; + conn_tmp = tls_net_get_socket(socket); + errornum ++; + if(NULL == conn_tmp || errornum >= 4) + { + TLS_DBGPRT_ERR("\nconn=%x, errnum=%d\n", conn_tmp, errornum); + err = ERR_VAL; + break; + } + } + } + + } + } + else + { + err = -1; + } + return err; +} + +int tls_net_init() +{ + //int i; + memset(p_net_conn, 0, sizeof(struct tls_netconn *) * TLS_MAX_NETCONN_NUM); + return 0; +} + +#endif //TLS_CONFIG_SOCKET_RAW + diff --git a/sdk/Src/Network/api2.0.3/tls_netconn.h b/sdk/Src/Network/api2.0.3/tls_netconn.h new file mode 100644 index 0000000..4286382 --- /dev/null +++ b/sdk/Src/Network/api2.0.3/tls_netconn.h @@ -0,0 +1,93 @@ +/************************************************************************** + * File Name : tls_netconn.h + * Author : + * Version : + * Date : + * Description : + * + * Copyright (c) 2014 Winner Microelectronics Co., Ltd. + * All rights reserved. + * + ***************************************************************************/ + +#ifndef TLS_NETCONN_H +#define TLS_NETCONN_H + +#include "tls_common.h" +#include "lwip/ip_addr.h" +#include "list.h" +#include "lwip/err.h" +#include "wm_socket.h" +#include "arch/sys_arch.h" +#include "lwip/pbuf.h" +#include "wm_netif.h" + +#define TLS_NETCONN_TCP 0 +#define TLS_NETCONN_UDP 1 + +#define RAW_SOCKET_USE_CUSTOM_PBUF LWIP_SUPPORT_CUSTOM_PBUF + +#define CONN_SEM_NOT_FREE 1 + +/** A netconn descriptor */ +struct tls_netconn { + struct dl_list list; + struct tls_socket_desc *skd; + u8 skt_num; + bool used; + + /* client or server mode */ + bool client; + ip_addr_t addr; + u16 port; + u16 localport; + u16 proto; + /* the lwIP internal protocol control block */ + union { + struct ip_pcb *ip; + struct tcp_pcb *tcp; + struct udp_pcb *udp; + struct raw_pcb *raw; + } pcb; + bool write_state; + u16 write_offset; + struct tls_net_msg *current_msg; + u8 tcp_connect_cnt; + u8 state; + err_t last_err; +#if !CONN_SEM_NOT_FREE + sys_sem_t op_completed; +#endif + u32 idle_time; + +}; + +struct tls_net_msg { + struct dl_list list; + //struct tls_netconn *conn; + void *dataptr; + struct pbuf *p; + ip_addr_t addr; + u16 port; + u32 len; + u32 write_offset; + err_t err; + int skt_no; +}; +#if (RAW_SOCKET_USE_CUSTOM_PBUF) +struct raw_sk_pbuf_custom{ + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; + /*custom pbuf free function used*/ + void *conn; + void *pcb; +}; +#endif + +int tls_net_init(void); +struct tls_netconn * tls_net_get_socket(u8 socket); + +#endif /* end of TLS_NETCONN_H */ + diff --git a/sdk/Src/Network/api2.0.3/tls_sockets.c b/sdk/Src/Network/api2.0.3/tls_sockets.c new file mode 100644 index 0000000..900a48a --- /dev/null +++ b/sdk/Src/Network/api2.0.3/tls_sockets.c @@ -0,0 +1,122 @@ +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sockets.h" + +extern struct hostent* lwip_gethostbyname(const char *name); +int +accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + return lwip_accept(s, addr, addrlen); +} + +int +bind(int s, const struct sockaddr *name, socklen_t namelen) +{ + return lwip_bind(s, name, namelen); +} + +int +shutdown(int s, int how) +{ + return lwip_shutdown(s, how); +} + +int +closesocket(int s) +{ + return lwip_close(s); +} + +int +connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + return lwip_connect(s, name, namelen); +} + +int +getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getsockname(s, name, namelen); +} + +int +getpeername(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getpeername(s, name, namelen); +} + +int +setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + return lwip_setsockopt(s, level, optname, optval, optlen); +} + +int +getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + return lwip_getsockopt(s, level, optname, optval, optlen); +} + +int +listen(int s, int backlog) +{ + return lwip_listen(s, backlog); +} + +int +recv(int s, void *mem, size_t len, int flags) +{ + return lwip_recv(s, mem, len, flags); +} + +int +recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + return lwip_recvfrom(s, mem, len, flags, from, fromlen); +} + +int +send(int s, const void *data, size_t size, int flags) +{ + return lwip_send(s, data, size, flags); +} + +int +sendto(int s, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen) +{ + return lwip_sendto(s, data, size, flags, to, tolen); +} + +int +socket(int domain, int type, int protocol) +{ + return lwip_socket(domain, type, protocol); +} + +int +select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout) +{ + return lwip_select(maxfdp1, readset, writeset, exceptset, timeout); +} + +int +ioctlsocket(int s, long cmd, void *argp) +{ + return lwip_ioctl(s, cmd, argp); +} + +int +fcntl(int s, int cmd, int val) +{ + return lwip_fcntl(s, cmd, val); +} +struct hostent* gethostbyname(const char *name){ + return lwip_gethostbyname(name); +} + +#endif /* LWIP_SOCKET */ diff --git a/sdk/Src/Network/lwip2.0.3.c b/sdk/Src/Network/lwip2.0.3.c new file mode 100644 index 0000000..a67244f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3.c @@ -0,0 +1,54 @@ +#ifndef WM_LWIP_COMPILE_FILES +#define WM_LWIP_COMPILE_FILES +#include "wm_config.h" +#include "../Src/Network/api2.0.3/tls_sockets.c" +#include "../Src/Network/api2.0.3/tls_netconn.c" +#include "../Src/Network/lwip2.0.3/core/def.c" +#include "../Src/Network/lwip2.0.3/core/dns.c" +#include "../Src/Network/lwip2.0.3/core/init.c" +#include "../Src/Network/lwip2.0.3/core/mem.c" +#include "../Src/Network/lwip2.0.3/core/memp.c" +#include "../Src/Network/lwip2.0.3/core/netif.c" +#include "../Src/Network/lwip2.0.3/core/pbuf.c" +#include "../Src/Network/lwip2.0.3/core/raw.c" +#include "../Src/Network/lwip2.0.3/core/stats.c" +#include "../Src/Network/lwip2.0.3/core/sys.c" +#include "../Src/Network/lwip2.0.3/core/tcp.c" +#include "../Src/Network/lwip2.0.3/core/tcp_in.c" +#include "../Src/Network/lwip2.0.3/core/tcp_out.c" +#include "../Src/Network/lwip2.0.3/core/udp.c" +#include "../Src/Network/lwip2.0.3/core/inet_chksum.c" +#include "../Src/Network/lwip2.0.3/core/ip.c" +#include "../Src/Network/lwip2.0.3/core/timeouts.c" +#include "../Src/Network/lwip2.0.3/core/alg.c" +#include "../Src/Network/lwip2.0.3/core/ipv4/ip4_frag.c" +#include "../Src/Network/lwip2.0.3/core/ipv4/ip4_addr.c" +#include "../Src/Network/lwip2.0.3/core/ipv4/ip4.c" +#include "../Src/Network/lwip2.0.3/core/ipv4/dhcp.c" +#include "../Src/Network/lwip2.0.3/core/ipv4/etharp.c" +#include "../Src/Network/lwip2.0.3/core/ipv4/igmp.c" +#include "../Src/Network/lwip2.0.3/core/ipv4/icmp.c" +#include "../Src/Network/lwip2.0.3/core/ipv4/autoip.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/ip6_frag.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/ip6_addr.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/ip6.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/dhcp6.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/ethip6.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/icmp6.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/inet6.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/mld6.c" +#include "../Src/Network/lwip2.0.3/core/ipv6/nd6.c" +#include "../Src/Network/lwip2.0.3/sys_arch.c" +#include "../Src/Network/lwip2.0.3/netif/ethernetif.c" +#include "../Src/Network/lwip2.0.3/netif/ethernet.c" +#include "../Src/Network/lwip2.0.3/netif/wm_ethernet.c" +#include "../Src/Network/lwip2.0.3/api/tcpip.c" +#include "../Src/Network/lwip2.0.3/api/api_lib.c" +#include "../Src/Network/lwip2.0.3/api/api_msg.c" +#include "../Src/Network/lwip2.0.3/api/err.c" +#include "../Src/Network/lwip2.0.3/api/netbuf.c" +#include "../Src/Network/lwip2.0.3/api/netdb.c" +#include "../Src/Network/lwip2.0.3/api/netifapi.c" +#include "../Src/Network/lwip2.0.3/api/sockets.c" + +#endif // WM_LWIP_COMPILE_FILES diff --git a/sdk/Src/Network/lwip2.0.3/FILES b/sdk/Src/Network/lwip2.0.3/FILES new file mode 100644 index 0000000..0be0741 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/FILES @@ -0,0 +1,15 @@ +api/ - The code for the high-level wrapper API. Not needed if + you use the lowel-level call-back/raw API. + +apps/ - Higher layer applications that are specifically programmed + with the lwIP low-level raw API. + +core/ - The core of the TPC/IP stack; protocol implementations, + memory and buffer management, and the low-level raw API. + +include/ - lwIP include files. + +netif/ - Generic network interface device drivers are kept here. + +For more information on the various subdirectories, check the FILES +file in each directory. diff --git a/sdk/Src/Network/lwip2.0.3/Filelists.mk b/sdk/Src/Network/lwip2.0.3/Filelists.mk new file mode 100644 index 0000000..7d30bb8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/Filelists.mk @@ -0,0 +1,181 @@ +# +# Copyright (c) 2001, 2002 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. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +# SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +# OF SUCH DAMAGE. +# +# This file is part of the lwIP TCP/IP stack. +# +# Author: Adam Dunkels +# + +# COREFILES, CORE4FILES: The minimum set of files needed for lwIP. +COREFILES=$(LWIPDIR)/core/init.c \ + $(LWIPDIR)/core/def.c \ + $(LWIPDIR)/core/dns.c \ + $(LWIPDIR)/core/inet_chksum.c \ + $(LWIPDIR)/core/ip.c \ + $(LWIPDIR)/core/mem.c \ + $(LWIPDIR)/core/memp.c \ + $(LWIPDIR)/core/netif.c \ + $(LWIPDIR)/core/pbuf.c \ + $(LWIPDIR)/core/raw.c \ + $(LWIPDIR)/core/stats.c \ + $(LWIPDIR)/core/sys.c \ + $(LWIPDIR)/core/tcp.c \ + $(LWIPDIR)/core/tcp_in.c \ + $(LWIPDIR)/core/tcp_out.c \ + $(LWIPDIR)/core/timeouts.c \ + $(LWIPDIR)/core/udp.c + +CORE4FILES=$(LWIPDIR)/core/ipv4/autoip.c \ + $(LWIPDIR)/core/ipv4/dhcp.c \ + $(LWIPDIR)/core/ipv4/etharp.c \ + $(LWIPDIR)/core/ipv4/icmp.c \ + $(LWIPDIR)/core/ipv4/igmp.c \ + $(LWIPDIR)/core/ipv4/ip4_frag.c \ + $(LWIPDIR)/core/ipv4/ip4.c \ + $(LWIPDIR)/core/ipv4/ip4_addr.c + +CORE6FILES=$(LWIPDIR)/core/ipv6/dhcp6.c \ + $(LWIPDIR)/core/ipv6/ethip6.c \ + $(LWIPDIR)/core/ipv6/icmp6.c \ + $(LWIPDIR)/core/ipv6/inet6.c \ + $(LWIPDIR)/core/ipv6/ip6.c \ + $(LWIPDIR)/core/ipv6/ip6_addr.c \ + $(LWIPDIR)/core/ipv6/ip6_frag.c \ + $(LWIPDIR)/core/ipv6/mld6.c \ + $(LWIPDIR)/core/ipv6/nd6.c + +# APIFILES: The files which implement the sequential and socket APIs. +APIFILES=$(LWIPDIR)/api/api_lib.c \ + $(LWIPDIR)/api/api_msg.c \ + $(LWIPDIR)/api/err.c \ + $(LWIPDIR)/api/netbuf.c \ + $(LWIPDIR)/api/netdb.c \ + $(LWIPDIR)/api/netifapi.c \ + $(LWIPDIR)/api/sockets.c \ + $(LWIPDIR)/api/tcpip.c + +# NETIFFILES: Files implementing various generic network interface functions +NETIFFILES=$(LWIPDIR)/netif/ethernet.c \ + $(LWIPDIR)/netif/slipif.c + +# SIXLOWPAN: 6LoWPAN +SIXLOWPAN=$(LWIPDIR)/netif/lowpan6.c \ + +# PPPFILES: PPP +PPPFILES=$(LWIPDIR)/netif/ppp/auth.c \ + $(LWIPDIR)/netif/ppp/ccp.c \ + $(LWIPDIR)/netif/ppp/chap-md5.c \ + $(LWIPDIR)/netif/ppp/chap_ms.c \ + $(LWIPDIR)/netif/ppp/chap-new.c \ + $(LWIPDIR)/netif/ppp/demand.c \ + $(LWIPDIR)/netif/ppp/eap.c \ + $(LWIPDIR)/netif/ppp/ecp.c \ + $(LWIPDIR)/netif/ppp/eui64.c \ + $(LWIPDIR)/netif/ppp/fsm.c \ + $(LWIPDIR)/netif/ppp/ipcp.c \ + $(LWIPDIR)/netif/ppp/ipv6cp.c \ + $(LWIPDIR)/netif/ppp/lcp.c \ + $(LWIPDIR)/netif/ppp/magic.c \ + $(LWIPDIR)/netif/ppp/mppe.c \ + $(LWIPDIR)/netif/ppp/multilink.c \ + $(LWIPDIR)/netif/ppp/ppp.c \ + $(LWIPDIR)/netif/ppp/pppapi.c \ + $(LWIPDIR)/netif/ppp/pppcrypt.c \ + $(LWIPDIR)/netif/ppp/pppoe.c \ + $(LWIPDIR)/netif/ppp/pppol2tp.c \ + $(LWIPDIR)/netif/ppp/pppos.c \ + $(LWIPDIR)/netif/ppp/upap.c \ + $(LWIPDIR)/netif/ppp/utils.c \ + $(LWIPDIR)/netif/ppp/vj.c \ + $(LWIPDIR)/netif/ppp/polarssl/arc4.c \ + $(LWIPDIR)/netif/ppp/polarssl/des.c \ + $(LWIPDIR)/netif/ppp/polarssl/md4.c \ + $(LWIPDIR)/netif/ppp/polarssl/md5.c \ + $(LWIPDIR)/netif/ppp/polarssl/sha1.c + +# LWIPNOAPPSFILES: All LWIP files without apps +LWIPNOAPPSFILES=$(COREFILES) \ + $(CORE4FILES) \ + $(CORE6FILES) \ + $(APIFILES) \ + $(NETIFFILES) \ + $(PPPFILES) \ + $(SIXLOWPAN) + +# SNMPFILES: SNMPv2c agent +SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \ + $(LWIPDIR)/apps/snmp/snmp_core.c \ + $(LWIPDIR)/apps/snmp/snmp_mib2.c \ + $(LWIPDIR)/apps/snmp/snmp_mib2_icmp.c \ + $(LWIPDIR)/apps/snmp/snmp_mib2_interfaces.c \ + $(LWIPDIR)/apps/snmp/snmp_mib2_ip.c \ + $(LWIPDIR)/apps/snmp/snmp_mib2_snmp.c \ + $(LWIPDIR)/apps/snmp/snmp_mib2_system.c \ + $(LWIPDIR)/apps/snmp/snmp_mib2_tcp.c \ + $(LWIPDIR)/apps/snmp/snmp_mib2_udp.c \ + $(LWIPDIR)/apps/snmp/snmp_msg.c \ + $(LWIPDIR)/apps/snmp/snmpv3.c \ + $(LWIPDIR)/apps/snmp/snmp_netconn.c \ + $(LWIPDIR)/apps/snmp/snmp_pbuf_stream.c \ + $(LWIPDIR)/apps/snmp/snmp_raw.c \ + $(LWIPDIR)/apps/snmp/snmp_scalar.c \ + $(LWIPDIR)/apps/snmp/snmp_table.c \ + $(LWIPDIR)/apps/snmp/snmp_threadsync.c \ + $(LWIPDIR)/apps/snmp/snmp_traps.c \ + $(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c \ + $(LWIPDIR)/apps/snmp/snmpv3_dummy.c + +# HTTPDFILES: HTTP server +HTTPDFILES=$(LWIPDIR)/apps/httpd/fs.c \ + $(LWIPDIR)/apps/httpd/httpd.c + +# LWIPERFFILES: IPERF server +LWIPERFFILES=$(LWIPDIR)/apps/lwiperf/lwiperf.c + +# SNTPFILES: SNTP client +SNTPFILES=$(LWIPDIR)/apps/sntp/sntp.c + +# MDNSFILES: MDNS responder +MDNSFILES=$(LWIPDIR)/apps/mdns/mdns.c + +# NETBIOSNSFILES: NetBIOS name server +NETBIOSNSFILES=$(LWIPDIR)/apps/netbiosns/netbiosns.c + +# TFTPFILES: TFTP server files +TFTPFILES=$(LWIPDIR)/apps/tftp/tftp_server.c + +# MQTTFILES: MQTT client files +MQTTFILES=$(LWIPDIR)/apps/mqtt/mqtt.c + +# LWIPAPPFILES: All LWIP APPs +LWIPAPPFILES=$(SNMPFILES) \ + $(HTTPDFILES) \ + $(LWIPERFFILES) \ + $(SNTPFILES) \ + $(MDNSFILES) \ + $(NETBIOSNSFILES) \ + $(TFTPFILES) \ + $(MQTTFILES) diff --git a/sdk/Src/Network/lwip2.0.3/api/api_lib.c b/sdk/Src/Network/lwip2.0.3/api/api_lib.c new file mode 100644 index 0000000..dda05d3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/api_lib.c @@ -0,0 +1,1010 @@ +/** + * @file + * Sequential API External module + * + * @defgroup netconn Netconn API + * @ingroup sequential_api + * Thread-safe, to be called from non-TCPIP threads only. + * TX/RX handling based on @ref netbuf (containing @ref pbuf) + * to avoid copying data around. + * + * @defgroup netconn_common Common functions + * @ingroup netconn + * For use with TCP and UDP + * + * @defgroup netconn_tcp TCP only + * @ingroup netconn + * TCP only functions + * + * @defgroup netconn_udp UDP only + * @ingroup netconn + * UDP only functions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +/* This is the part of the API that is linked with + the application */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api.h" +#include "lwip/memp.h" + +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/priv/api_msg.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/priv/tcpip_priv.h" + +#include + +#define API_MSG_VAR_REF(name) API_VAR_REF(name) +#define API_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct api_msg, name) +#define API_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM) +#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL) +#define API_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_API_MSG, name) + +static err_t netconn_close_shutdown(struct netconn *conn, u8_t how); + +/** + * Call the lower part of a netconn_* function + * This function is then running in the thread context + * of tcpip_thread and has exclusive access to lwIP core code. + * + * @param fn function to call + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK if the function was called, another err_t if not + */ +static err_t +netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg) +{ + err_t err; + +#ifdef LWIP_DEBUG + /* catch functions that don't set err */ + apimsg->err = ERR_VAL; +#endif /* LWIP_DEBUG */ + +#if LWIP_NETCONN_SEM_PER_THREAD + apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg)); + if (err == ERR_OK) { + return apimsg->err; + } + return err; +} + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is also created. + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) +{ + struct netconn *conn; + API_MSG_VAR_DECLARE(msg); + API_MSG_VAR_ALLOC_RETURN_NULL(msg); + + conn = netconn_alloc(t, callback); + if (conn != NULL) { + err_t err; + + API_MSG_VAR_REF(msg).msg.n.proto = proto; + API_MSG_VAR_REF(msg).conn = conn; + err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg)); + if (err != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ +#if !LWIP_NETCONN_SEM_PER_THREAD + LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); + sys_sem_free(&conn->op_completed); +#endif /* !LWIP_NETCONN_SEM_PER_THREAD */ + sys_mbox_free(&conn->recvmbox); + memp_free(MEMP_NETCONN, conn); + API_MSG_VAR_FREE(msg); + return NULL; + } + } + API_MSG_VAR_FREE(msg); + return conn; +} + +/** + * @ingroup netconn_common + * Close a netconn 'connection' and free its resources. + * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate + * after this returns. + * + * @param conn the netconn to delete + * @return ERR_OK if the connection was deleted + */ +err_t +netconn_delete(struct netconn *conn) +{ + err_t err; + API_MSG_VAR_DECLARE(msg); + + /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ + if (conn == NULL) { + return ERR_OK; + } + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).conn = conn; +#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER + /* get the time we started, which is later compared to + sys_now() + conn->send_timeout */ + API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now(); +#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ +#if LWIP_TCP + API_MSG_VAR_REF(msg).msg.sd.polls_left = + ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1; +#endif /* LWIP_TCP */ +#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ + err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + if (err != ERR_OK) { + return err; + } + + netconn_free(conn); + + return ERR_OK; +} + +/** + * Get the local or remote IP address and port of a netconn. + * For RAW netconns, this returns the protocol instead of a port! + * + * @param conn the netconn to query + * @param addr a pointer to which to save the IP address + * @param port a pointer to which to save the port (or protocol for RAW) + * @param local 1 to get the local IP address, 0 to get the remote one + * @return ERR_CONN for invalid connections + * ERR_OK if the information was retrieved + */ +err_t +netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).conn = conn; + API_MSG_VAR_REF(msg).msg.ad.local = local; +#if LWIP_MPU_COMPATIBLE + err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg)); + *addr = msg->msg.ad.ipaddr; + *port = msg->msg.ad.port; +#else /* LWIP_MPU_COMPATIBLE */ + msg.msg.ad.ipaddr = addr; + msg.msg.ad.port = port; + err = netconn_apimsg(lwip_netconn_do_getaddr, &msg); +#endif /* LWIP_MPU_COMPATIBLE */ + API_MSG_VAR_FREE(msg); + + return err; +} + +/** + * @ingroup netconn_common + * Bind a netconn to a specific local IP address and port. + * Binding one netconn twice might not always be checked correctly! + * + * @param conn the netconn to bind + * @param addr the local IP address to bind the netconn to + * (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses) + * @param port the local port to bind the netconn to (not used for RAW) + * @return ERR_OK if bound, any other err_t on failure + */ +err_t +netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); + +#if LWIP_IPV4 + /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */ + if (addr == NULL) { + addr = IP4_ADDR_ANY; + } +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV4 && LWIP_IPV6 + /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY, + * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind + */ + if ((netconn_get_ipv6only(conn) == 0) && + ip_addr_cmp(addr, IP6_ADDR_ANY)) { + addr = IP_ANY_TYPE; + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).conn = conn; + API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr); + API_MSG_VAR_REF(msg).msg.bc.port = port; + err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + return err; +} + +/** + * @ingroup netconn_common + * Connect a netconn to a specific remote IP address and port. + * + * @param conn the netconn to connect + * @param addr the remote IP address to connect to + * @param port the remote port to connect to (no used for RAW) + * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise + */ +err_t +netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); + +#if LWIP_IPV4 + /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */ + if (addr == NULL) { + addr = IP4_ADDR_ANY; + } +#endif /* LWIP_IPV4 */ + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).conn = conn; + API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr); + API_MSG_VAR_REF(msg).msg.bc.port = port; + err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + return err; +} + +/** + * @ingroup netconn_udp + * Disconnect a netconn from its current peer (only valid for UDP netconns). + * + * @param conn the netconn to disconnect + * @return See @ref err_t + */ +err_t +netconn_disconnect(struct netconn *conn) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).conn = conn; + err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + return err; +} + +/** + * @ingroup netconn_tcp + * Set a TCP netconn into listen mode + * + * @param conn the tcp netconn to set to listen mode + * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1 + * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns + * don't return any error (yet?)) + */ +err_t +netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) +{ +#if LWIP_TCP + API_MSG_VAR_DECLARE(msg); + err_t err; + + /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */ + LWIP_UNUSED_ARG(backlog); + + LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).conn = conn; +#if TCP_LISTEN_BACKLOG + API_MSG_VAR_REF(msg).msg.lb.backlog = backlog; +#endif /* TCP_LISTEN_BACKLOG */ + err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + return err; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(backlog); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * @ingroup netconn_tcp + * Accept a new connection on a TCP listening netconn. + * + * @param conn the TCP listen netconn + * @param new_conn pointer where the new connection is stored + * @return ERR_OK if a new connection has been received or an error + * code otherwise + */ +err_t +netconn_accept(struct netconn *conn, struct netconn **new_conn) +{ +#if LWIP_TCP + void *accept_ptr; + struct netconn *newconn; +#if TCP_LISTEN_BACKLOG + API_MSG_VAR_DECLARE(msg); +#endif /* TCP_LISTEN_BACKLOG */ + + LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;); + *new_conn = NULL; + LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;); + + if (ERR_IS_FATAL(conn->last_err)) { + /* don't recv on fatal errors: this might block the application task + waiting on acceptmbox forever! */ + return conn->last_err; + } + if (!sys_mbox_valid(&conn->acceptmbox)) { + return ERR_CLSD; + } + +#if TCP_LISTEN_BACKLOG + API_MSG_VAR_ALLOC(msg); +#endif /* TCP_LISTEN_BACKLOG */ + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { +#if TCP_LISTEN_BACKLOG + API_MSG_VAR_FREE(msg); +#endif /* TCP_LISTEN_BACKLOG */ + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + newconn = (struct netconn *)accept_ptr; + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + + if (accept_ptr == &netconn_aborted) { + /* a connection has been aborted: out of pcbs or out of netconns during accept */ + /* @todo: set netconn error, but this would be fatal and thus block further accepts */ +#if TCP_LISTEN_BACKLOG + API_MSG_VAR_FREE(msg); +#endif /* TCP_LISTEN_BACKLOG */ + return ERR_ABRT; + } + if (newconn == NULL) { + /* connection has been aborted */ + /* in this special case, we set the netconn error from application thread, as + on a ready-to-accept listening netconn, there should not be anything running + in tcpip_thread */ + NETCONN_SET_SAFE_ERR(conn, ERR_CLSD); +#if TCP_LISTEN_BACKLOG + API_MSG_VAR_FREE(msg); +#endif /* TCP_LISTEN_BACKLOG */ + return ERR_CLSD; + } +#if TCP_LISTEN_BACKLOG + /* Let the stack know that we have accepted the connection. */ + API_MSG_VAR_REF(msg).conn = newconn; + /* don't care for the return value of lwip_netconn_do_recv */ + netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); +#endif /* TCP_LISTEN_BACKLOG */ + + *new_conn = newconn; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(new_conn); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * @ingroup netconn_common + * Receive data: actual implementation that doesn't care whether pbuf or netbuf + * is received + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf/netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +static err_t +netconn_recv_data(struct netconn *conn, void **new_buf) +{ + void *buf = NULL; + u16_t len; +#if LWIP_TCP + API_MSG_VAR_DECLARE(msg); +#if LWIP_MPU_COMPATIBLE + msg = NULL; +#endif +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + if (!sys_mbox_valid(&conn->recvmbox)) { + /* This happens when calling this function after receiving FIN */ + return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD; + } + } +#endif /* LWIP_TCP */ + LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + + if (ERR_IS_FATAL(conn->last_err)) { + /* don't recv on fatal errors: this might block the application task + waiting on recvmbox forever! */ + /* @todo: this does not allow us to fetch data that has been put into recvmbox + before the fatal error occurred - is that a problem? */ + return conn->last_err; + } +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + API_MSG_VAR_ALLOC(msg); + } +#endif /* LWIP_TCP */ + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + API_MSG_VAR_FREE(msg); + } +#endif /* LWIP_TCP */ + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + /* Let the stack know that we have taken the data. */ + /* @todo: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + API_MSG_VAR_REF(msg).conn = conn; + if (buf != NULL) { + API_MSG_VAR_REF(msg).msg.r.len = ((struct pbuf *)buf)->tot_len; + } else { + API_MSG_VAR_REF(msg).msg.r.len = 1; + } + + /* don't care for the return value of lwip_netconn_do_recv */ + netconn_apimsg(lwip_netconn_do_recv, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + /* If we are closed, we indicate that we no longer wish to use the socket */ + if (buf == NULL) { + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + if (conn->pcb.ip == NULL) { + /* race condition: RST during recv */ + return conn->last_err == ERR_OK ? ERR_RST : conn->last_err; + } + /* RX side is closed, so deallocate the recvmbox */ + netconn_close_shutdown(conn, NETCONN_SHUT_RD); + /* Don' store ERR_CLSD as conn->err since we are only half-closed */ + return ERR_CLSD; + } + len = ((struct pbuf *)buf)->tot_len; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ +#if (LWIP_UDP || LWIP_RAW) + { + LWIP_ASSERT("buf != NULL", buf != NULL); + len = netbuf_len((struct netbuf*)buf); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + +#if LWIP_SO_RCVBUF + SYS_ARCH_DEC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len)); + + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +} + +/** + * @ingroup netconn_tcp + * Receive data (in form of a pbuf) from a TCP netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + * ERR_ARG if conn is not a TCP netconn + */ +err_t +netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf) +{ + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) && + NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;); + + return netconn_recv_data(conn, (void **)new_buf); +} + +/** + * @ingroup netconn_common + * Receive data (in form of a netbuf containing a packet buffer) from a netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +err_t +netconn_recv(struct netconn *conn, struct netbuf **new_buf) +{ +#if LWIP_TCP + struct netbuf *buf = NULL; + err_t err; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + struct pbuf *p = NULL; + /* This is not a listening netconn, since recvmbox is set */ + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + return ERR_MEM; + } + + err = netconn_recv_data(conn, (void **)&p); + if (err != ERR_OK) { + memp_free(MEMP_NETBUF, buf); + return err; + } + LWIP_ASSERT("p != NULL", p != NULL); + + buf->p = p; + buf->ptr = p; + buf->port = 0; + ip_addr_set_zero(&buf->addr); + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ + { +#if (LWIP_UDP || LWIP_RAW) + return netconn_recv_data(conn, (void **)new_buf); +#endif /* (LWIP_UDP || LWIP_RAW) */ + } +} + +/** + * @ingroup netconn_udp + * Send data (in form of a netbuf) to a specific remote IP address and port. + * Only to be used for UDP and RAW netconns (not TCP). + * + * @param conn the netconn over which to send data + * @param buf a netbuf containing the data to send + * @param addr the remote IP address to which to send the data + * @param port the remote port to which to send the data + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port) +{ + if (buf != NULL) { + ip_addr_set(&buf->addr, addr); + buf->port = port; + return netconn_send(conn, buf); + } + return ERR_VAL; +} + +/** + * @ingroup netconn_udp + * Send data over a UDP or RAW netconn (that is already connected). + * + * @param conn the UDP or RAW netconn over which to send data + * @param buf a netbuf containing the data to send + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_send(struct netconn *conn, struct netbuf *buf) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len)); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).conn = conn; + API_MSG_VAR_REF(msg).msg.b = buf; + err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + return err; +} + +/** + * @ingroup netconn_tcp + * Send data over a TCP netconn. + * + * @param conn the TCP netconn over which to send data + * @param dataptr pointer to the application buffer that contains the data to send + * @param size size of the application data to send + * @param apiflags combination of following flags : + * - NETCONN_COPY: data will be copied into memory belonging to the stack + * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent + * - NETCONN_DONTBLOCK: only write the data if all data can be written at once + * @param bytes_written pointer to a location that receives the number of written bytes + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + u8_t dontblock; + + LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;); + if (size == 0) { + return ERR_OK; + } + dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); +#if LWIP_SO_SNDTIMEO + if (conn->send_timeout != 0) { + dontblock = 1; + } +#endif /* LWIP_SO_SNDTIMEO */ + if (dontblock && !bytes_written) { + /* This implies netconn_write() cannot be used for non-blocking send, since + it has no way to return the number of bytes written. */ + return ERR_VAL; + } + + API_MSG_VAR_ALLOC(msg); + /* non-blocking write sends as much */ + API_MSG_VAR_REF(msg).conn = conn; + API_MSG_VAR_REF(msg).msg.w.dataptr = dataptr; + API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags; + API_MSG_VAR_REF(msg).msg.w.len = size; +#if LWIP_SO_SNDTIMEO + if (conn->send_timeout != 0) { + /* get the time we started, which is later compared to + sys_now() + conn->send_timeout */ + API_MSG_VAR_REF(msg).msg.w.time_started = sys_now(); + } else { + API_MSG_VAR_REF(msg).msg.w.time_started = 0; + } +#endif /* LWIP_SO_SNDTIMEO */ + + /* For locking the core: this _can_ be delayed on low memory/low send buffer, + but if it is, this is done inside api_msg.c:do_write(), so we can use the + non-blocking version here. */ + err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg)); + if ((err == ERR_OK) && (bytes_written != NULL)) { + if (dontblock) { + /* nonblocking write: maybe the data has been sent partly */ + *bytes_written = API_MSG_VAR_REF(msg).msg.w.len; + } else { + /* blocking call succeeded: all data has been sent if it */ + *bytes_written = size; + } + } + API_MSG_VAR_FREE(msg); + + return err; +} + +/** + * @ingroup netconn_tcp + * Close or shutdown a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close or shutdown + * @param how fully close or only shutdown one side? + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +static err_t +netconn_close_shutdown(struct netconn *conn, u8_t how) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + LWIP_UNUSED_ARG(how); + + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).conn = conn; +#if LWIP_TCP + /* shutting down both ends is the same as closing */ + API_MSG_VAR_REF(msg).msg.sd.shut = how; +#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER + /* get the time we started, which is later compared to + sys_now() + conn->send_timeout */ + API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now(); +#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ + API_MSG_VAR_REF(msg).msg.sd.polls_left = + ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1; +#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ +#endif /* LWIP_TCP */ + err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + return err; +} + +/** + * @ingroup netconn_tcp + * Close a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_close(struct netconn *conn) +{ + /* shutting down both ends is the same as closing */ + return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR); +} + +/** + * @ingroup netconn_tcp + * Shut down one or both sides of a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to shut down + * @param shut_rx shut down the RX side (no more read possible after this) + * @param shut_tx shut down the TX side (no more write possible after this) + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx) +{ + return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)); +} + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** + * @ingroup netconn_udp + * Join multicast groups for UDP netconns. + * + * @param conn the UDP netconn for which to change multicast addresses + * @param multiaddr IP address of the multicast group to join or leave + * @param netif_addr the IP address of the network interface on which to send + * the igmp message + * @param join_or_leave flag whether to send a join- or leave-message + * @return ERR_OK if the action was taken, any err_t on error + */ +err_t +netconn_join_leave_group(struct netconn *conn, + const ip_addr_t *multiaddr, + const ip_addr_t *netif_addr, + enum netconn_igmp join_or_leave) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); + +#if LWIP_IPV4 + /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */ + if (multiaddr == NULL) { + multiaddr = IP4_ADDR_ANY; + } + if (netif_addr == NULL) { + netif_addr = IP4_ADDR_ANY; + } +#endif /* LWIP_IPV4 */ + + API_MSG_VAR_REF(msg).conn = conn; + API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr); + API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr); + API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave; + err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + return err; +} +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +/** + * @ingroup netconn_common + * Execute a DNS query, only one IP address is returned + * + * @param name a string representation of the DNS host name to query + * @param addr a preallocated ip_addr_t where to store the resolved IP address + * @param dns_addrtype IP address type (IPv4 / IPv6) + * @return ERR_OK: resolving succeeded + * ERR_MEM: memory error, try again later + * ERR_ARG: dns client not initialized or invalid hostname + * ERR_VAL: dns server response was invalid + */ +#if LWIP_IPV4 && LWIP_IPV6 +err_t +netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype) +#else +err_t +netconn_gethostbyname(const char *name, ip_addr_t *addr) +#endif +{ + API_VAR_DECLARE(struct dns_api_msg, msg); +#if !LWIP_MPU_COMPATIBLE + sys_sem_t sem; +#endif /* LWIP_MPU_COMPATIBLE */ + err_t err; + err_t cberr; + + LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); +#if LWIP_MPU_COMPATIBLE + if (strlen(name) >= DNS_MAX_NAME_LENGTH) { + return ERR_ARG; + } +#endif + + API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM); +#if LWIP_MPU_COMPATIBLE + strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH-1); + API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH-1] = 0; +#else /* LWIP_MPU_COMPATIBLE */ + msg.err = &err; + msg.sem = &sem; + API_VAR_REF(msg).addr = API_VAR_REF(addr); + API_VAR_REF(msg).name = name; +#endif /* LWIP_MPU_COMPATIBLE */ +#if LWIP_IPV4 && LWIP_IPV6 + API_VAR_REF(msg).dns_addrtype = dns_addrtype; +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +#if LWIP_NETCONN_SEM_PER_THREAD + API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else /* LWIP_NETCONN_SEM_PER_THREAD*/ + err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0); + if (err != ERR_OK) { + API_VAR_FREE(MEMP_DNS_API_MSG, msg); + return err; + } +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + cberr = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg)); + if (cberr != ERR_OK) { +#if !LWIP_NETCONN_SEM_PER_THREAD + sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem)); +#endif /* !LWIP_NETCONN_SEM_PER_THREAD */ + API_VAR_FREE(MEMP_DNS_API_MSG, msg); + return cberr; + } + sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem)); +#if !LWIP_NETCONN_SEM_PER_THREAD + sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem)); +#endif /* !LWIP_NETCONN_SEM_PER_THREAD */ + +#if LWIP_MPU_COMPATIBLE + *addr = msg->addr; + err = msg->err; +#endif /* LWIP_MPU_COMPATIBLE */ + + API_VAR_FREE(MEMP_DNS_API_MSG, msg); + return err; +} +#endif /* LWIP_DNS*/ + +#if LWIP_NETCONN_SEM_PER_THREAD +void +netconn_thread_init(void) +{ + sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET(); + if ((sem == NULL) || !sys_sem_valid(sem)) { + /* call alloc only once */ + LWIP_NETCONN_THREAD_SEM_ALLOC(); + LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET())); + } +} + +void +netconn_thread_cleanup(void) +{ + sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET(); + if ((sem != NULL) && sys_sem_valid(sem)) { + /* call free only once */ + LWIP_NETCONN_THREAD_SEM_FREE(); + } +} +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + +#endif /* LWIP_NETCONN */ diff --git a/sdk/Src/Network/lwip2.0.3/api/api_msg.c b/sdk/Src/Network/lwip2.0.3/api/api_msg.c new file mode 100644 index 0000000..0307f91 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/api_msg.c @@ -0,0 +1,1953 @@ +/** + * @file + * Sequential API Internal module + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/priv/api_msg.h" + +#include "lwip/ip.h" +#include "lwip/ip_addr.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" + +#include "lwip/memp.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/mld6.h" +#include "lwip/priv/tcpip_priv.h" + +#include + +/* netconns are polled once per second (e.g. continue write on memory error) */ +#define NETCONN_TCP_POLL_INTERVAL 2 + +#define SET_NONBLOCKING_CONNECT(conn, val) do { if (val) { \ + (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0) +#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0) + +/* forward declarations */ +#if LWIP_TCP +#if LWIP_TCPIP_CORE_LOCKING +#define WRITE_DELAYED , 1 +#define WRITE_DELAYED_PARAM , u8_t delayed +#else /* LWIP_TCPIP_CORE_LOCKING */ +#define WRITE_DELAYED +#define WRITE_DELAYED_PARAM +#endif /* LWIP_TCPIP_CORE_LOCKING */ +static err_t lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM); +static err_t lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM); +#endif + +#if LWIP_TCPIP_CORE_LOCKING +#define TCPIP_APIMSG_ACK(m) NETCONN_SET_SAFE_ERR((m)->conn, (m)->err) +#else /* LWIP_TCPIP_CORE_LOCKING */ +#define TCPIP_APIMSG_ACK(m) do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0) +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +#if LWIP_TCP +u8_t netconn_aborted; +#endif /* LWIP_TCP */ + +#if LWIP_RAW +/** + * Receive callback function for RAW netconns. + * Doesn't 'eat' the packet, only copies it and sends it to + * conn->recvmbox + * + * @see raw.h (struct raw_pcb.recv) for parameters and return value + */ +static u8_t +recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr) +{ + struct pbuf *q; + struct netbuf *buf; + struct netconn *conn; + + LWIP_UNUSED_ARG(addr); + conn = (struct netconn *)arg; + + if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) { +#if LWIP_SO_RCVBUF + int recv_avail; + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) { + return 0; + } +#endif /* LWIP_SO_RCVBUF */ + /* copy the whole packet into new pbufs */ + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + if (pbuf_copy(q, p) != ERR_OK) { + pbuf_free(q); + q = NULL; + } + } + + if (q != NULL) { + u16_t len; + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(q); + return 0; + } + + buf->p = q; + buf->ptr = q; + ip_addr_copy(buf->addr, *ip_current_src_addr()); + buf->port = pcb->protocol; + + len = q->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return 0; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + } + } + + return 0; /* do not eat the packet */ +} +#endif /* LWIP_RAW*/ + +#if LWIP_UDP +/** + * Receive callback function for UDP netconns. + * Posts the packet to conn->recvmbox or deletes it on memory error. + * + * @see udp.h (struct udp_pcb.recv) for parameters + */ +static void +recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port) +{ + struct netbuf *buf; + struct netconn *conn; + u16_t len; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ + LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_udp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + + if (conn == NULL) { + pbuf_free(p); + return; + } + + LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if (!sys_mbox_valid(&conn->recvmbox) || + ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if (!sys_mbox_valid(&conn->recvmbox)) { +#endif /* LWIP_SO_RCVBUF */ + pbuf_free(p); + return; + } + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(p); + return; + } else { + buf->p = p; + buf->ptr = p; + ip_addr_set(&buf->addr, addr); + buf->port = port; +#if LWIP_NETBUF_RECVINFO + { + /* get the UDP header - always in the first pbuf, ensured by udp_input */ + const struct udp_hdr* udphdr = (const struct udp_hdr*)ip_next_header_ptr(); +#if LWIP_CHECKSUM_ON_COPY + buf->flags = NETBUF_FLAG_DESTADDR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + ip_addr_set(&buf->toaddr, ip_current_dest_addr()); + buf->toport_chksum = udphdr->dest; + } +#endif /* LWIP_NETBUF_RECVINFO */ + } + + len = p->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } +} +#endif /* LWIP_UDP */ + +#if LWIP_TCP +/** + * Receive callback function for TCP netconns. + * Posts the packet to conn->recvmbox, but doesn't delete it on errors. + * + * @see tcp.h (struct tcp_pcb.recv) for parameters and return value + */ +static err_t +recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netconn *conn; + u16_t len; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + + if (conn == NULL) { + return ERR_VAL; + } + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + if (!sys_mbox_valid(&conn->recvmbox)) { + /* recvmbox already deleted */ + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + return ERR_OK; + } + /* Unlike for UDP or RAW pcbs, don't check for available space + using recv_avail since that could break the connection + (data is already ACKed) */ + + /* don't overwrite fatal errors! */ + if (err != ERR_OK) { + NETCONN_SET_SAFE_ERR(conn, err); + } + + if (p != NULL) { + len = p->tot_len; + } else { + len = 0; + } + + if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) { + /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */ + return ERR_MEM; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + + return ERR_OK; +} + +/** + * Poll callback function for TCP netconns. + * Wakes up an application thread that waits for a connection to close + * or data to be sent. The application thread then takes the + * appropriate action to go on. + * + * Signals the conn->sem. + * netconn_close waits for conn->sem if closing failed. + * + * @see tcp.h (struct tcp_pcb.poll) for parameters and return value + */ +static err_t +poll_tcp(void *arg, struct tcp_pcb *pcb) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + lwip_netconn_do_writemore(conn WRITE_DELAYED); + } else if (conn->state == NETCONN_CLOSE) { +#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER + if (conn->current_msg && conn->current_msg->msg.sd.polls_left) { + conn->current_msg->msg.sd.polls_left--; + } +#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */ + lwip_netconn_do_close_internal(conn WRITE_DELAYED); + } + /* @todo: implement connect timeout here? */ + + /* Did a nonblocking write fail before? Then check available write-space. */ + if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + } + + return ERR_OK; +} + +/** + * Sent callback function for TCP netconns. + * Signals the conn->sem and calls API_EVENT. + * netconn_write waits for conn->sem if send buffer is low. + * + * @see tcp.h (struct tcp_pcb.sent) for parameters and return value + */ +static err_t +sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn) { + if (conn->state == NETCONN_WRITE) { + lwip_netconn_do_writemore(conn WRITE_DELAYED); + } else if (conn->state == NETCONN_CLOSE) { + lwip_netconn_do_close_internal(conn WRITE_DELAYED); + } + + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); + } + } + + return ERR_OK; +} + +/** + * Error callback function for TCP netconns. + * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. + * The application thread has then to decide what to do. + * + * @see tcp.h (struct tcp_pcb.err) for parameters + */ +static void +err_tcp(void *arg, err_t err) +{ + struct netconn *conn; + enum netconn_state old_state; + + conn = (struct netconn *)arg; + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + conn->pcb.tcp = NULL; + + /* reset conn->state now before waking up other threads */ + old_state = conn->state; + conn->state = NETCONN_NONE; + + if (old_state == NETCONN_CLOSE) { + /* RST during close: let close return success & dealloc the netconn */ + err = ERR_OK; + NETCONN_SET_SAFE_ERR(conn, ERR_OK); + } else { + /* no check since this is always fatal! */ + SYS_ARCH_SET(conn->last_err, err); + } + + /* @todo: the type of NETCONN_EVT created should depend on 'old_state' */ + + /* Notify the user layer about a connection error. Used to signal select. */ + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + /* Try to release selects pending on 'read' or 'write', too. + They will get an error if they actually try to read or write. */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + /* pass NULL-message to recvmbox to wake up pending recv */ + if (sys_mbox_valid(&conn->recvmbox)) { + /* use trypost to prevent deadlock */ + sys_mbox_trypost(&conn->recvmbox, NULL); + } + /* pass NULL-message to acceptmbox to wake up pending accept */ + if (sys_mbox_valid(&conn->acceptmbox)) { + /* use trypost to preven deadlock */ + sys_mbox_trypost(&conn->acceptmbox, NULL); + } + + if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) || + (old_state == NETCONN_CONNECT)) { + /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary + since the pcb has already been deleted! */ + int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + + if (!was_nonblocking_connect) { + sys_sem_t* op_completed_sem; + /* set error return code */ + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + conn->current_msg->err = err; + op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem)); + conn->current_msg = NULL; + /* wake up the waiting task */ + NETCONN_SET_SAFE_ERR(conn, err); + sys_sem_signal(op_completed_sem); + } + } else { + LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); + } +} + +/** + * Setup a tcp_pcb with the correct callback function pointers + * and their arguments. + * + * @param conn the TCP netconn to setup + */ +static void +setup_tcp(struct netconn *conn) +{ + struct tcp_pcb *pcb; + + pcb = conn->pcb.tcp; + tcp_arg(pcb, conn); + tcp_recv(pcb, recv_tcp); + tcp_sent(pcb, sent_tcp); + tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL); + tcp_err(pcb, err_tcp); +} + +/** + * Accept callback function for TCP netconns. + * Allocates a new netconn and posts that to conn->acceptmbox. + * + * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value + */ +static err_t +accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + struct netconn *newconn; + struct netconn *conn = (struct netconn *)arg; + + if (conn == NULL) { + return ERR_VAL; + } + if (!sys_mbox_valid(&conn->acceptmbox)) { + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n")); + return ERR_VAL; + } + + if (newpcb == NULL) { + /* out-of-pcbs during connect: pass on this error to the application */ + if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + return ERR_VAL; + } + + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state))); + + /* We have to set the callback here even though + * the new socket is unknown. newconn->socket is marked as -1. */ + newconn = netconn_alloc(conn->type, conn->callback); + if (newconn == NULL) { + /* outof netconns: pass on this error to the application */ + if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + return ERR_MEM; + } + newconn->pcb.tcp = newpcb; + setup_tcp(newconn); + /* no protection: when creating the pcb, the netconn is not yet known + to the application thread */ + newconn->last_err = err; + + /* handle backlog counter */ + tcp_backlog_delayed(newpcb); + + if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) { + /* When returning != ERR_OK, the pcb is aborted in tcp_process(), + so do nothing here! */ + /* remove all references to this netconn from the pcb */ + struct tcp_pcb* pcb = newconn->pcb.tcp; + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_poll(pcb, NULL, 0); + tcp_err(pcb, NULL); + /* remove reference from to the pcb from this netconn */ + newconn->pcb.tcp = NULL; + /* no need to drain since we know the recvmbox is empty. */ + sys_mbox_free(&newconn->recvmbox); + sys_mbox_set_invalid(&newconn->recvmbox); + netconn_free(newconn); + return ERR_MEM; + } else { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Create a new pcb of a specific type. + * Called from lwip_netconn_do_newconn(). + * + * @param msg the api_msg_msg describing the connection type + */ +static void +pcb_new(struct api_msg *msg) +{ + enum lwip_ip_addr_type iptype = (enum lwip_ip_addr_type)IPADDR_TYPE_V4; + + LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); + +#if LWIP_IPV6 && LWIP_IPV4 + /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */ + if(NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) { + iptype = IPADDR_TYPE_ANY; + } +#endif + + /* Allocate a PCB for this connection */ + switch(NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto); + if (msg->conn->pcb.raw != NULL) { +#if LWIP_IPV6 + /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */ + if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) { + msg->conn->pcb.raw->chksum_reqd = 1; + msg->conn->pcb.raw->chksum_offset = 2; + } +#endif /* LWIP_IPV6 */ + raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new_ip_type(iptype); + if (msg->conn->pcb.udp != NULL) { +#if LWIP_UDPLITE + if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } +#endif /* LWIP_UDPLITE */ + if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + } + udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new_ip_type(iptype); + if (msg->conn->pcb.tcp != NULL) { + setup_tcp(msg->conn); + } + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->err = ERR_VAL; + return; + } + if (msg->conn->pcb.ip == NULL) { + msg->err = ERR_MEM; + } +} + +/** + * Create a new pcb of a specific type inside a netconn. + * Called from netconn_new_with_proto_and_callback. + * + * @param m the api_msg_msg describing the connection type + */ +void +lwip_netconn_do_newconn(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + msg->err = ERR_OK; + if (msg->conn->pcb.tcp == NULL) { + pcb_new(msg); + } + /* Else? This "new" connection already has a PCB allocated. */ + /* Is this an error condition? Should it be deleted? */ + /* We currently just are happy and return. */ + + TCPIP_APIMSG_ACK(msg); +} + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is NOT created! + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_alloc(enum netconn_type t, netconn_callback callback) +{ + struct netconn *conn; + int size; + + conn = (struct netconn *)memp_malloc(MEMP_NETCONN); + if (conn == NULL) { + return NULL; + } + + conn->last_err = ERR_OK; + conn->type = t; + conn->pcb.tcp = NULL; + + /* If all sizes are the same, every compiler should optimize this switch to nothing */ + switch(NETCONNTYPE_GROUP(t)) { +#if LWIP_RAW + case NETCONN_RAW: + size = DEFAULT_RAW_RECVMBOX_SIZE; + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + size = DEFAULT_UDP_RECVMBOX_SIZE; + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + size = DEFAULT_TCP_RECVMBOX_SIZE; + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); + goto free_and_return; + } + + if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { + goto free_and_return; + } +#if !LWIP_NETCONN_SEM_PER_THREAD + if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { + sys_mbox_free(&conn->recvmbox); + goto free_and_return; + } +#endif + +#if LWIP_TCP + sys_mbox_set_invalid(&conn->acceptmbox); +#endif + conn->state = NETCONN_NONE; +#if LWIP_SOCKET + /* initialize socket to -1 since 0 is a valid socket */ + conn->socket = -1; +#endif /* LWIP_SOCKET */ + conn->callback = callback; +#if LWIP_TCP + conn->current_msg = NULL; + conn->write_offset = 0; +#endif /* LWIP_TCP */ +#if LWIP_SO_SNDTIMEO + conn->send_timeout = 0; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + conn->recv_timeout = 0; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; + conn->recv_avail = 0; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_SO_LINGER + conn->linger = -1; +#endif /* LWIP_SO_LINGER */ + conn->flags = 0; + return conn; +free_and_return: + memp_free(MEMP_NETCONN, conn); + return NULL; +} + +/** + * Delete a netconn and all its resources. + * The pcb is NOT freed (since we might not be in the right thread context do this). + * + * @param conn the netconn to free + */ +void +netconn_free(struct netconn *conn) +{ + LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL); + LWIP_ASSERT("recvmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("acceptmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + +#if !LWIP_NETCONN_SEM_PER_THREAD + sys_sem_free(&conn->op_completed); + sys_sem_set_invalid(&conn->op_completed); +#endif + + memp_free(MEMP_NETCONN, conn); +} + +/** + * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in + * these mboxes + * + * @param conn the netconn to free + * @bytes_drained bytes drained from recvmbox + * @accepts_drained pending connections drained from acceptmbox + */ +static void +netconn_drain(struct netconn *conn) +{ + void *mem; +#if LWIP_TCP + struct pbuf *p; +#endif /* LWIP_TCP */ + + /* This runs in tcpip_thread, so we don't need to lock against rx packets */ + + /* Delete and drain the recvmbox. */ + if (sys_mbox_valid(&conn->recvmbox)) { + while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { +#if LWIP_TCP + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) { + if (mem != NULL) { + p = (struct pbuf*)mem; + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_recved(conn->pcb.tcp, p->tot_len); + } + pbuf_free(p); + } + } else +#endif /* LWIP_TCP */ + { + netbuf_delete((struct netbuf *)mem); + } + } + sys_mbox_free(&conn->recvmbox); + sys_mbox_set_invalid(&conn->recvmbox); + } + + /* Delete and drain the acceptmbox. */ +#if LWIP_TCP + if (sys_mbox_valid(&conn->acceptmbox)) { + while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) { + if (mem != &netconn_aborted) { + struct netconn *newconn = (struct netconn *)mem; + /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */ + /* pcb might be set to NULL already by err_tcp() */ + /* drain recvmbox */ + netconn_drain(newconn); + if (newconn->pcb.tcp != NULL) { + tcp_abort(newconn->pcb.tcp); + newconn->pcb.tcp = NULL; + } + netconn_free(newconn); + } + } + sys_mbox_free(&conn->acceptmbox); + sys_mbox_set_invalid(&conn->acceptmbox); + } +#endif /* LWIP_TCP */ +} + +#if LWIP_TCP +/** + * Internal helper function to close a TCP netconn: since this sometimes + * doesn't work at the first attempt, this function is called from multiple + * places. + * + * @param conn the TCP netconn to close + */ +static err_t +lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM) +{ + err_t err; + u8_t shut, shut_rx, shut_tx, close; + u8_t close_finished = 0; + struct tcp_pcb* tpcb; +#if LWIP_SO_LINGER + u8_t linger_wait_required = 0; +#endif /* LWIP_SO_LINGER */ + + LWIP_ASSERT("invalid conn", (conn != NULL)); + LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)); + LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); + LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + + tpcb = conn->pcb.tcp; + shut = conn->current_msg->msg.sd.shut; + shut_rx = shut & NETCONN_SHUT_RD; + shut_tx = shut & NETCONN_SHUT_WR; + /* shutting down both ends is the same as closing + (also if RD or WR side was shut down before already) */ + if (shut == NETCONN_SHUT_RDWR) { + close = 1; + } else if (shut_rx && + ((tpcb->state == FIN_WAIT_1) || + (tpcb->state == FIN_WAIT_2) || + (tpcb->state == CLOSING))) { + close = 1; + } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) { + close = 1; + } else { + close = 0; + } + + /* Set back some callback pointers */ + if (close) { + tcp_arg(tpcb, NULL); + } + if (tpcb->state == LISTEN) { + tcp_accept(tpcb, NULL); + } else { + /* some callbacks have to be reset if tcp_close is not successful */ + if (shut_rx) { + tcp_recv(tpcb, NULL); + tcp_accept(tpcb, NULL); + } + if (shut_tx) { + tcp_sent(tpcb, NULL); + } + if (close) { + tcp_poll(tpcb, NULL, 0); + tcp_err(tpcb, NULL); + } + } + /* Try to close the connection */ + if (close) { +#if LWIP_SO_LINGER + /* check linger possibilites before calling tcp_close */ + err = ERR_OK; + /* linger enabled/required at all? (i.e. is there untransmitted data left?) */ + if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) { + if ((conn->linger == 0)) { + /* data left but linger prevents waiting */ + tcp_abort(tpcb); + tpcb = NULL; + } else if (conn->linger > 0) { + /* data left and linger says we should wait */ + if (netconn_is_nonblocking(conn)) { + /* data left on a nonblocking netconn -> cannot linger */ + err = ERR_WOULDBLOCK; + } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= + (conn->linger * 1000)) { + /* data left but linger timeout has expired (this happens on further + calls to this function through poll_tcp */ + tcp_abort(tpcb); + tpcb = NULL; + } else { + /* data left -> need to wait for ACK after successful close */ + linger_wait_required = 1; + } + } + } + if ((err == ERR_OK) && (tpcb != NULL)) +#endif /* LWIP_SO_LINGER */ + { + err = tcp_close(tpcb); + } + } else { + err = tcp_shutdown(tpcb, shut_rx, shut_tx); + } + if (err == ERR_OK) { + close_finished = 1; +#if LWIP_SO_LINGER + if (linger_wait_required) { + /* wait for ACK of all unsent/unacked data by just getting called again */ + close_finished = 0; + err = ERR_INPROGRESS; + } +#endif /* LWIP_SO_LINGER */ + } else { + if (err == ERR_MEM) { + /* Closing failed because of memory shortage, try again later. Even for + nonblocking netconns, we have to wait since no standard socket application + is prepared for close failing because of resource shortage. + Check the timeout: this is kind of an lwip addition to the standard sockets: + we wait for some time when failing to allocate a segment for the FIN */ +#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER + s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT; +#if LWIP_SO_SNDTIMEO + if (conn->send_timeout > 0) { + close_timeout = conn->send_timeout; + } +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_LINGER + if (conn->linger >= 0) { + /* use linger timeout (seconds) */ + close_timeout = conn->linger * 1000U; + } +#endif + if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) { +#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ + if (conn->current_msg->msg.sd.polls_left == 0) { +#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ + close_finished = 1; + if (close) { + /* in this case, we want to RST the connection */ + tcp_abort(tpcb); + err = ERR_OK; + } + } + } else { + /* Closing failed for a non-memory error: give up */ + close_finished = 1; + } + } + if (close_finished) { + /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */ + sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + conn->current_msg->err = err; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (err == ERR_OK) { + if (close) { + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + /* Trigger select() in socket layer. Make sure everybody notices activity + on the connection, error first! */ + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + } + if (shut_rx) { + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + if (shut_tx) { + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + } + NETCONN_SET_SAFE_ERR(conn, err); +#if LWIP_TCPIP_CORE_LOCKING + if (delayed) +#endif + { + /* wake up the application task */ + sys_sem_signal(op_completed_sem); + } + return ERR_OK; + } + if (!close_finished) { + /* Closing failed and we want to wait: restore some of the callbacks */ + /* Closing of listen pcb will never fail! */ + LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN)); + if (shut_tx) { + tcp_sent(tpcb, sent_tcp); + } + /* when waiting for close, set up poll interval to 500ms */ + tcp_poll(tpcb, poll_tcp, 1); + tcp_err(tpcb, err_tcp); + tcp_arg(tpcb, conn); + /* don't restore recv callback: we don't want to receive any more data */ + } + /* If closing didn't succeed, we get called again either + from poll_tcp or from sent_tcp */ + LWIP_ASSERT("err != ERR_OK", err != ERR_OK); + return err; +} +#endif /* LWIP_TCP */ + +/** + * Delete the pcb inside a netconn. + * Called from netconn_delete. + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_delconn(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + enum netconn_state state = msg->conn->state; + LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */ + (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP)); +#if LWIP_NETCONN_FULLDUPLEX + /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */ + if (state != NETCONN_NONE) { + if ((state == NETCONN_WRITE) || + ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) { + /* close requested, abort running write/connect */ + sys_sem_t* op_completed_sem; + LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL); + op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg); + msg->conn->current_msg->err = ERR_CLSD; + msg->conn->current_msg = NULL; + msg->conn->write_offset = 0; + msg->conn->state = NETCONN_NONE; + NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD); + sys_sem_signal(op_completed_sem); + } + } +#else /* LWIP_NETCONN_FULLDUPLEX */ + if (((state != NETCONN_NONE) && + (state != NETCONN_LISTEN) && + (state != NETCONN_CONNECT)) || + ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) { + /* This means either a blocking write or blocking connect is running + (nonblocking write returns and sets state to NONE) */ + msg->err = ERR_INPROGRESS; + } else +#endif /* LWIP_NETCONN_FULLDUPLEX */ + { + LWIP_ASSERT("blocking connect in progress", + (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn)); + msg->err = ERR_OK; + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + + if (msg->conn->pcb.tcp != NULL) { + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + raw_remove(msg->conn->pcb.raw); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp->recv_arg = NULL; + udp_remove(msg->conn->pcb.udp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->msg.sd.shut = NETCONN_SHUT_RDWR; + msg->conn->current_msg = msg; +#if LWIP_TCPIP_CORE_LOCKING + if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + lwip_netconn_do_close_internal(msg->conn); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing + the application thread, so we can return at this point! */ + return; +#endif /* LWIP_TCP */ + default: + break; + } + msg->conn->pcb.tcp = NULL; + } + /* tcp netconns don't come here! */ + + /* @todo: this lets select make the socket readable and writable, + which is wrong! errfd instead? */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); + } + if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) { + TCPIP_APIMSG_ACK(msg); + } +} + +/** + * Bind a pcb contained in a netconn + * Called from netconn_bind. + * + * @param m the api_msg_msg pointing to the connection and containing + * the IP address and port to bind to + */ +void +lwip_netconn_do_bind(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_VAL; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr)); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port); + break; +#endif /* LWIP_TCP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has + * been established (or reset by the remote host). + * + * @see tcp.h (struct tcp_pcb.connected) for parameters and return values + */ +static err_t +lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netconn *conn; + int was_blocking; + sys_sem_t* op_completed_sem = NULL; + + LWIP_UNUSED_ARG(pcb); + + conn = (struct netconn *)arg; + + if (conn == NULL) { + return ERR_VAL; + } + + LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT); + LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect", + (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn)); + + if (conn->current_msg != NULL) { + conn->current_msg->err = err; + op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + } + if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) { + setup_tcp(conn); + } + was_blocking = !IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + LWIP_ASSERT("blocking connect state error", + (was_blocking && op_completed_sem != NULL) || + (!was_blocking && op_completed_sem == NULL)); + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + NETCONN_SET_SAFE_ERR(conn, ERR_OK); + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + if (was_blocking) { + sys_sem_signal(op_completed_sem); + } + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Connect a pcb contained inside a netconn + * Called from netconn_connect. + * + * @param m the api_msg_msg pointing to the connection and containing + * the IP address and port to connect to + */ +void +lwip_netconn_do_connect(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + if (msg->conn->pcb.tcp == NULL) { + /* This may happen when calling netconn_connect() a second time */ + msg->err = ERR_CLSD; + } else { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr)); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + /* Prevent connect while doing any other action. */ + if (msg->conn->state == NETCONN_CONNECT) { + msg->err = ERR_ALREADY; + } else if (msg->conn->state != NETCONN_NONE) { + msg->err = ERR_ISCONN; + } else { + setup_tcp(msg->conn); + msg->err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), + msg->msg.bc.port, lwip_netconn_do_connected); + if (msg->err == ERR_OK) { + u8_t non_blocking = netconn_is_nonblocking(msg->conn); + msg->conn->state = NETCONN_CONNECT; + SET_NONBLOCKING_CONNECT(msg->conn, non_blocking); + if (non_blocking) { + msg->err = ERR_INPROGRESS; + } else { + msg->conn->current_msg = msg; + /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()), + when the connection is established! */ +#if LWIP_TCPIP_CORE_LOCKING + LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + return; + } + } + } + break; +#endif /* LWIP_TCP */ + default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); + break; + } + } + /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(), + so use TCPIP_APIMSG_ACK() here. */ + TCPIP_APIMSG_ACK(msg); +} + +/** + * Disconnect a pcb contained inside a netconn + * Only used for UDP netconns. + * Called from netconn_disconnect. + * + * @param m the api_msg_msg pointing to the connection to disconnect + */ +void +lwip_netconn_do_disconnect(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + +#if LWIP_UDP + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { + udp_disconnect(msg->conn->pcb.udp); + msg->err = ERR_OK; + } else +#endif /* LWIP_UDP */ + { + msg->err = ERR_VAL; + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Set a TCP pcb contained in a netconn into listen mode + * Called from netconn_listen. + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_listen(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + if (msg->conn->state == NETCONN_NONE) { + struct tcp_pcb* lpcb; + if (msg->conn->pcb.tcp->state != CLOSED) { + /* connection is not closed, cannot listen */ + msg->err = ERR_VAL; + } else { + err_t err; + u8_t backlog; +#if TCP_LISTEN_BACKLOG + backlog = msg->msg.lb.backlog; +#else /* TCP_LISTEN_BACKLOG */ + backlog = TCP_DEFAULT_LISTEN_BACKLOG; +#endif /* TCP_LISTEN_BACKLOG */ +#if LWIP_IPV4 && LWIP_IPV6 + /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY, + * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen + */ + if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) && + (netconn_get_ipv6only(msg->conn) == 0)) { + /* change PCB type to IPADDR_TYPE_ANY */ + IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip, IPADDR_TYPE_ANY); + IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY); + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err); + + if (lpcb == NULL) { + /* in this case, the old pcb is still allocated */ + msg->err = err; + } else { + /* delete the recvmbox and allocate the acceptmbox */ + if (sys_mbox_valid(&msg->conn->recvmbox)) { + /** @todo: should we drain the recvmbox here? */ + sys_mbox_free(&msg->conn->recvmbox); + sys_mbox_set_invalid(&msg->conn->recvmbox); + } + msg->err = ERR_OK; + if (!sys_mbox_valid(&msg->conn->acceptmbox)) { + msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE); + } + if (msg->err == ERR_OK) { + msg->conn->state = NETCONN_LISTEN; + msg->conn->pcb.tcp = lpcb; + tcp_arg(msg->conn->pcb.tcp, msg->conn); + tcp_accept(msg->conn->pcb.tcp, accept_function); + } else { + /* since the old pcb is already deallocated, free lpcb now */ + tcp_close(lpcb); + msg->conn->pcb.tcp = NULL; + } + } + } + } else if (msg->conn->state == NETCONN_LISTEN) { + /* already listening, allow updating of the backlog */ + msg->err = ERR_OK; + tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog); + } + } else { + msg->err = ERR_ARG; + } + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a RAW or UDP pcb contained in a netconn + * Called from netconn_send + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_send(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) { + msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: +#if LWIP_CHECKSUM_ON_COPY + if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) { + msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } else { + msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p, + &msg->msg.b->addr, msg->msg.b->port, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } +#else /* LWIP_CHECKSUM_ON_COPY */ + if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) { + msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + break; +#endif /* LWIP_UDP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Indicate data has been received from a TCP pcb contained in a netconn + * Called from netconn_recv + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_recv(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + msg->err = ERR_OK; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + u32_t remaining = msg->msg.r.len; + do { + u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining; + tcp_recved(msg->conn->pcb.tcp, recved); + remaining -= recved; + } while (remaining != 0); + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if TCP_LISTEN_BACKLOG +/** Indicate that a TCP pcb has been accepted + * Called from netconn_accept + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_accepted(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + msg->err = ERR_OK; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + tcp_backlog_accepted(msg->conn->pcb.tcp); + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* TCP_LISTEN_BACKLOG */ + +/** + * See if more data needs to be written from a previous call to netconn_write. + * Called initially from lwip_netconn_do_write. If the first call can't send all data + * (because of low memory or empty send-buffer), this function is called again + * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the + * blocking application thread (waiting in netconn_write) is released. + * + * @param conn netconn (that is currently in state NETCONN_WRITE) to process + * @return ERR_OK + * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished + */ +static err_t +lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM) +{ + err_t err; + const void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + size_t diff; + u8_t dontblock; + u8_t apiflags; + + LWIP_ASSERT("conn != NULL", conn != NULL); + LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", + conn->write_offset < conn->current_msg->msg.w.len); + + apiflags = conn->current_msg->msg.w.apiflags; + dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); + +#if LWIP_SO_SNDTIMEO + if ((conn->send_timeout != 0) && + ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) { + write_finished = 1; + if (conn->write_offset == 0) { + /* nothing has been written */ + err = ERR_WOULDBLOCK; + conn->current_msg->msg.w.len = 0; + } else { + /* partial write */ + err = ERR_OK; + conn->current_msg->msg.w.len = conn->write_offset; + conn->write_offset = 0; + } + } else +#endif /* LWIP_SO_SNDTIMEO */ + { + dataptr = (const u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; + diff = conn->current_msg->msg.w.len - conn->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; + apiflags |= TCP_WRITE_FLAG_MORE; + } else { + len = (u16_t)diff; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; + if (dontblock) { + if (!len) { + err = ERR_WOULDBLOCK; + goto err_mem; + } + } else { + apiflags |= TCP_WRITE_FLAG_MORE; + } + } + LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); + err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); + /* if OK or memory error, check available space */ + if ((err == ERR_OK) || (err == ERR_MEM)) { +err_mem: + if (dontblock && (len < conn->current_msg->msg.w.len)) { + /* non-blocking write did not write everything: mark the pcb non-writable + and let poll_tcp check writable space to mark the pcb writable again */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; + } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) { + /* The queued byte- or pbuf-count exceeds the configured low-water limit, + let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + } + + if (err == ERR_OK) { + err_t out_err; + conn->write_offset += len; + if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) { + /* return sent length */ + conn->current_msg->msg.w.len = conn->write_offset; + /* everything was written */ + write_finished = 1; + } + out_err = tcp_output(conn->pcb.tcp); + if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) { + /* If tcp_output fails with fatal error or no route is found, + don't try writing any more but return the error + to the application thread. */ + err = out_err; + write_finished = 1; + conn->current_msg->msg.w.len = 0; + } + } else if (err == ERR_MEM) { + /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called. + For blocking sockets, we do NOT return to the application + thread, since ERR_MEM is only a temporary error! Non-blocking + will remain non-writable until sent_tcp/poll_tcp is called */ + + /* tcp_write returned ERR_MEM, try tcp_output anyway */ + err_t out_err = tcp_output(conn->pcb.tcp); + if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) { + /* If tcp_output fails with fatal error or no route is found, + don't try writing any more but return the error + to the application thread. */ + err = out_err; + write_finished = 1; + conn->current_msg->msg.w.len = 0; + } else if (dontblock) { + /* non-blocking write is done on ERR_MEM */ + err = ERR_WOULDBLOCK; + write_finished = 1; + conn->current_msg->msg.w.len = 0; + } + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + write_finished = 1; + conn->current_msg->msg.w.len = 0; + } + } + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + conn->current_msg->err = err; + conn->current_msg = NULL; + conn->write_offset = 0; + conn->state = NETCONN_NONE; + NETCONN_SET_SAFE_ERR(conn, err); +#if LWIP_TCPIP_CORE_LOCKING + if (delayed) +#endif + { + sys_sem_signal(op_completed_sem); + } + } +#if LWIP_TCPIP_CORE_LOCKING + else { + return ERR_MEM; + } +#endif + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a TCP pcb contained in a netconn + * Called from netconn_write + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_write(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { +#if LWIP_TCP + if (msg->conn->state != NETCONN_NONE) { + /* netconn is connecting, closing or in blocking write */ + msg->err = ERR_INPROGRESS; + } else if (msg->conn->pcb.tcp != NULL) { + msg->conn->state = NETCONN_WRITE; + /* set all the variables used by lwip_netconn_do_writemore */ + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0); + msg->conn->current_msg = msg; + msg->conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE); + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + lwip_netconn_do_writemore(msg->conn); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG + since lwip_netconn_do_writemore ACKs it! */ + return; + } else { + msg->err = ERR_CONN; + } +#else /* LWIP_TCP */ + msg->err = ERR_VAL; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Return a connection's local or remote address + * Called from netconn_getaddr + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_getaddr(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + if (msg->conn->pcb.ip != NULL) { + if (msg->msg.ad.local) { + ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->local_ip); + } else { + ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->remote_ip); + } + + msg->err = ERR_OK; + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.ad.local) { + API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol; + } else { + /* return an error as connecting is only a helper for upper layers */ + msg->err = ERR_CONN; + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.ad.local) { + API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port; + } else { + if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) { + msg->err = ERR_CONN; + } else { + API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; + } + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + if ((msg->msg.ad.local == 0) && + ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) { + /* pcb is not connected and remote name is requested */ + msg->err = ERR_CONN; + } else { + API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port); + } + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("invalid netconn_type", 0); + break; + } + } else { + msg->err = ERR_CONN; + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Close or half-shutdown a TCP pcb contained in a netconn + * Called from netconn_close + * In contrast to closing sockets, the netconn is not deallocated. + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_close(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + +#if LWIP_TCP + enum netconn_state state = msg->conn->state; + /* First check if this is a TCP netconn and if it is in a correct state + (LISTEN doesn't support half shutdown) */ + if ((msg->conn->pcb.tcp != NULL) && + (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) && + ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) { + /* Check if we are in a connected state */ + if (state == NETCONN_CONNECT) { + /* TCP connect in progress: cannot shutdown */ + msg->err = ERR_CONN; + } else if (state == NETCONN_WRITE) { +#if LWIP_NETCONN_FULLDUPLEX + if (msg->msg.sd.shut & NETCONN_SHUT_WR) { + /* close requested, abort running write */ + sys_sem_t* write_completed_sem; + LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL); + write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg); + msg->conn->current_msg->err = ERR_CLSD; + msg->conn->current_msg = NULL; + msg->conn->write_offset = 0; + msg->conn->state = NETCONN_NONE; + state = NETCONN_NONE; + NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD); + sys_sem_signal(write_completed_sem); + } else { + LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD); + /* In this case, let the write continue and do not interfere with + conn->current_msg or conn->state! */ + msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0); + } + } + if (state == NETCONN_NONE) { +#else /* LWIP_NETCONN_FULLDUPLEX */ + msg->err = ERR_INPROGRESS; + } else { +#endif /* LWIP_NETCONN_FULLDUPLEX */ + if (msg->msg.sd.shut & NETCONN_SHUT_RD) { + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + } + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->conn->current_msg = msg; +#if LWIP_TCPIP_CORE_LOCKING + if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + lwip_netconn_do_close_internal(msg->conn); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */ + return; + } + } else +#endif /* LWIP_TCP */ + { + msg->err = ERR_CONN; + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** + * Join multicast groups for UDP netconns. + * Called from netconn_join_leave_group + * + * @param m the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_join_leave_group(void *m) +{ + struct api_msg *msg = (struct api_msg*)m; + + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { +#if LWIP_UDP +#if LWIP_IPV6 && LWIP_IPV6_MLD + if (NETCONNTYPE_ISIPV6(msg->conn->type)) { + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)), + ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); + } else { + msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)), + ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); + } + } + else +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + { +#if LWIP_IGMP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)), + ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr))); + } else { + msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)), + ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr))); + } +#endif /* LWIP_IGMP */ + } +#endif /* LWIP_UDP */ +#if (LWIP_TCP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_TCP || LWIP_RAW) */ + } + } else { + msg->err = ERR_CONN; + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +/** + * Callback function that is called when DNS name is resolved + * (or on timeout). A waiting application thread is waked up by + * signaling the semaphore. + */ +static void +lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + /* we trust the internal implementation to be correct :-) */ + LWIP_UNUSED_ARG(name); + + if (ipaddr == NULL) { + /* timeout or memory error */ + API_EXPR_DEREF(msg->err) = ERR_VAL; + } else { + /* address was resolved */ + API_EXPR_DEREF(msg->err) = ERR_OK; + API_EXPR_DEREF(msg->addr) = *ipaddr; + } + /* wake up the application task waiting in netconn_gethostbyname */ + sys_sem_signal(API_EXPR_REF_SEM(msg->sem)); +} + +/** + * Execute a DNS query + * Called from netconn_gethostbyname + * + * @param arg the dns_api_msg pointing to the query + */ +void +lwip_netconn_do_gethostbyname(void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + u8_t addrtype = +#if LWIP_IPV4 && LWIP_IPV6 + msg->dns_addrtype; +#else + LWIP_DNS_ADDRTYPE_DEFAULT; +#endif + + API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name, + API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype); + if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) { + /* on error or immediate success, wake up the application + * task waiting in netconn_gethostbyname */ + sys_sem_signal(API_EXPR_REF_SEM(msg->sem)); + } +} +#endif /* LWIP_DNS */ + +#endif /* LWIP_NETCONN */ diff --git a/sdk/Src/Network/lwip2.0.3/api/err.c b/sdk/Src/Network/lwip2.0.3/api/err.c new file mode 100644 index 0000000..6e9ab76 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/err.c @@ -0,0 +1,115 @@ +/** + * @file + * Error Management module + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/err.h" +#include "lwip/def.h" +#include "lwip/sys.h" + +#include "lwip/errno.h" + +#if !NO_SYS +/** Table to quickly map an lwIP error (err_t) to a socket error + * by using -err as an index */ +static const int err_to_errno_table[] = { + 0, /* ERR_OK 0 No error, everything OK. */ + ENOMEM, /* ERR_MEM -1 Out of memory error. */ + ENOBUFS, /* ERR_BUF -2 Buffer error. */ + EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */ + EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ + EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */ + EINVAL, /* ERR_VAL -6 Illegal value. */ + EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */ + EADDRINUSE, /* ERR_USE -8 Address in use. */ + EALREADY, /* ERR_ALREADY -9 Already connecting. */ + EISCONN, /* ERR_ISCONN -10 Conn already established.*/ + ENOTCONN, /* ERR_CONN -11 Not connected. */ + -1, /* ERR_IF -12 Low-level netif error */ + ECONNABORTED, /* ERR_ABRT -13 Connection aborted. */ + ECONNRESET, /* ERR_RST -14 Connection reset. */ + ENOTCONN, /* ERR_CLSD -15 Connection closed. */ + EIO /* ERR_ARG -16 Illegal argument. */ +}; + +int +err_to_errno(err_t err) +{ + if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) { + return EIO; + } + return err_to_errno_table[-err]; +} +#endif /* !NO_SYS */ + +#ifdef LWIP_DEBUG + +static const char *err_strerr[] = { + "Ok.", /* ERR_OK 0 */ + "Out of memory error.", /* ERR_MEM -1 */ + "Buffer error.", /* ERR_BUF -2 */ + "Timeout.", /* ERR_TIMEOUT -3 */ + "Routing problem.", /* ERR_RTE -4 */ + "Operation in progress.", /* ERR_INPROGRESS -5 */ + "Illegal value.", /* ERR_VAL -6 */ + "Operation would block.", /* ERR_WOULDBLOCK -7 */ + "Address in use.", /* ERR_USE -8 */ + "Already connecting.", /* ERR_ALREADY -9 */ + "Already connected.", /* ERR_ISCONN -10 */ + "Not connected.", /* ERR_CONN -11 */ + "Low-level netif error.", /* ERR_IF -12 */ + "Connection aborted.", /* ERR_ABRT -13 */ + "Connection reset.", /* ERR_RST -14 */ + "Connection closed.", /* ERR_CLSD -15 */ + "Illegal argument." /* ERR_ARG -16 */ +}; + +/** + * Convert an lwip internal error to a string representation. + * + * @param err an lwip internal err_t + * @return a string representation for err + */ +const char * +lwip_strerr(err_t err) +{ + if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_strerr))) { + return "Unknown error."; + } + return err_strerr[-err]; +} + +#endif /* LWIP_DEBUG */ diff --git a/sdk/Src/Network/lwip2.0.3/api/netbuf.c b/sdk/Src/Network/lwip2.0.3/api/netbuf.c new file mode 100644 index 0000000..6a5a182 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/netbuf.c @@ -0,0 +1,246 @@ +/** + * @file + * Network buffer management + * + * @defgroup netbuf Network buffers + * @ingroup netconn + * Network buffer descriptor for @ref netconn. Based on @ref pbuf internally + * to avoid copying data around.\n + * Buffers must not be shared accross multiple threads, all functions except + * netbuf_new() and netbuf_delete() are not thread-safe. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netbuf.h" +#include "lwip/memp.h" + +#include + +/** + * @ingroup netbuf + * Create (allocate) and initialize a new netbuf. + * The netbuf doesn't yet contain a packet buffer! + * + * @return a pointer to a new netbuf + * NULL on lack of memory + */ +struct +netbuf *netbuf_new(void) +{ + struct netbuf *buf; + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf != NULL) { + memset(buf, 0, sizeof(struct netbuf)); + } + return buf; +} + +/** + * @ingroup netbuf + * Deallocate a netbuf allocated by netbuf_new(). + * + * @param buf pointer to a netbuf allocated by netbuf_new() + */ +void +netbuf_delete(struct netbuf *buf) +{ + if (buf != NULL) { + if (buf->p != NULL) { + pbuf_free(buf->p); + buf->p = buf->ptr = NULL; + } + memp_free(MEMP_NETBUF, buf); + } +} + +/** + * @ingroup netbuf + * Allocate memory for a packet buffer for a given netbuf. + * + * @param buf the netbuf for which to allocate a packet buffer + * @param size the size of the packet buffer to allocate + * @return pointer to the allocated memory + * NULL if no memory could be allocated + */ +void * +netbuf_alloc(struct netbuf *buf, u16_t size) +{ + LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;); + + /* Deallocate any previously allocated memory. */ + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + if (buf->p == NULL) { + return NULL; + } + LWIP_ASSERT("check that first pbuf can hold size", + (buf->p->len >= size)); + buf->ptr = buf->p; + return buf->p->payload; +} + +/** + * @ingroup netbuf + * Free the packet buffer included in a netbuf + * + * @param buf pointer to the netbuf which contains the packet buffer to free + */ +void +netbuf_free(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = buf->ptr = NULL; +} + +/** + * @ingroup netbuf + * Let a netbuf reference existing (non-volatile) data. + * + * @param buf netbuf which should reference the data + * @param dataptr pointer to the data to reference + * @param size size of the data + * @return ERR_OK if data is referenced + * ERR_MEM if data couldn't be referenced due to lack of memory + */ +err_t +netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size) +{ + LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (buf->p == NULL) { + buf->ptr = NULL; + return ERR_MEM; + } + ((struct pbuf_rom*)buf->p)->payload = dataptr; + buf->p->len = buf->p->tot_len = size; + buf->ptr = buf->p; + return ERR_OK; +} + +/** + * @ingroup netbuf + * Chain one netbuf to another (@see pbuf_chain) + * + * @param head the first netbuf + * @param tail netbuf to chain after head, freed by this function, may not be reference after returning + */ +void +netbuf_chain(struct netbuf *head, struct netbuf *tail) +{ + LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;); + LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;); + pbuf_cat(head->p, tail->p); + head->ptr = head->p; + memp_free(MEMP_NETBUF, tail); +} + +/** + * @ingroup netbuf + * Get the data pointer and length of the data inside a netbuf. + * + * @param buf netbuf to get the data from + * @param dataptr pointer to a void pointer where to store the data pointer + * @param len pointer to an u16_t where the length of the data is stored + * @return ERR_OK if the information was retrieved, + * ERR_BUF on error. + */ +err_t +netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len) +{ + LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;); + + if (buf->ptr == NULL) { + return ERR_BUF; + } + *dataptr = buf->ptr->payload; + *len = buf->ptr->len; + return ERR_OK; +} + +/** + * @ingroup netbuf + * Move the current data pointer of a packet buffer contained in a netbuf + * to the next part. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + * @return -1 if there is no next part + * 1 if moved to the next part but now there is no next part + * 0 if moved to the next part and there are still more parts + */ +s8_t +netbuf_next(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;); + if (buf->ptr->next == NULL) { + return -1; + } + buf->ptr = buf->ptr->next; + if (buf->ptr->next == NULL) { + return 1; + } + return 0; +} + +/** + * @ingroup netbuf + * Move the current data pointer of a packet buffer contained in a netbuf + * to the beginning of the packet. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + */ +void +netbuf_first(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;); + buf->ptr = buf->p; +} + +#endif /* LWIP_NETCONN */ diff --git a/sdk/Src/Network/lwip2.0.3/api/netdb.c b/sdk/Src/Network/lwip2.0.3/api/netdb.c new file mode 100644 index 0000000..deff495 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/netdb.c @@ -0,0 +1,413 @@ +/** + * @file + * API functions for name resolving + * + * @defgroup netdbapi NETDB API + * @ingroup socket + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/netdb.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/err.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/api.h" +#include "lwip/dns.h" + +#include /* memset */ +#include /* atoi */ + +/** helper struct for gethostbyname_r to access the char* buffer */ +struct gethostbyname_r_helper { + ip_addr_t *addr_list[2]; + ip_addr_t addr; + char *aliases; +}; + +/** h_errno is exported in netdb.h for access by applications. */ +#if LWIP_DNS_API_DECLARE_H_ERRNO +int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */ + +/** define "hostent" variables storage: 0 if we use a static (but unprotected) + * set of variables for lwip_gethostbyname, 1 if we use a local storage */ +#ifndef LWIP_DNS_API_HOSTENT_STORAGE +#define LWIP_DNS_API_HOSTENT_STORAGE 0 +#endif + +/** define "hostent" variables storage */ +#if LWIP_DNS_API_HOSTENT_STORAGE +#define HOSTENT_STORAGE +#else +#define HOSTENT_STORAGE static +#endif /* LWIP_DNS_API_STATIC_HOSTENT */ + +/** + * Returns an entry containing addresses of address family AF_INET + * for the host with name name. + * Due to dns_gethostbyname limitations, only one address is returned. + * + * @param name the hostname to resolve + * @return an entry containing addresses of address family AF_INET + * for the host with name name + */ +struct hostent* +lwip_gethostbyname(const char *name) +{ + err_t err; + ip_addr_t addr; + + /* buffer variables for lwip_gethostbyname() */ + HOSTENT_STORAGE struct hostent s_hostent; + HOSTENT_STORAGE char *s_aliases; + HOSTENT_STORAGE ip_addr_t s_hostent_addr; + HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2]; + HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1]; + + /* query host IP address */ + err = netconn_gethostbyname(name, &addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + h_errno = HOST_NOT_FOUND; + return NULL; + } + + /* fill hostent */ + s_hostent_addr = addr; + s_phostent_addr[0] = &s_hostent_addr; + s_phostent_addr[1] = NULL; + strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH); + s_hostname[DNS_MAX_NAME_LENGTH] = 0; + s_hostent.h_name = s_hostname; + s_aliases = NULL; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_INET; + s_hostent.h_length = sizeof(ip_addr_t); + s_hostent.h_addr_list = (char**)&s_phostent_addr; + +#if DNS_DEBUG + /* dump hostent */ + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", (void*)s_hostent.h_aliases)); + /* h_aliases are always empty */ + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", (void*)s_hostent.h_addr_list)); + if (s_hostent.h_addr_list != NULL) { + u8_t idx; + for (idx=0; s_hostent.h_addr_list[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx]))); + } + } +#endif /* DNS_DEBUG */ + +#if LWIP_DNS_API_HOSTENT_STORAGE + /* this function should return the "per-thread" hostent after copy from s_hostent */ + return sys_thread_hostent(&s_hostent); +#else + return &s_hostent; +#endif /* LWIP_DNS_API_HOSTENT_STORAGE */ +} + +/** + * Thread-safe variant of lwip_gethostbyname: instead of using a static + * buffer, this function takes buffer and errno pointers as arguments + * and uses these for the result. + * + * @param name the hostname to resolve + * @param ret pre-allocated struct where to store the result + * @param buf pre-allocated buffer where to store additional data + * @param buflen the size of buf + * @param result pointer to a hostent pointer that is set to ret on success + * and set to zero on error + * @param h_errnop pointer to an int where to store errors (instead of modifying + * the global h_errno) + * @return 0 on success, non-zero on error, additional error information + * is stored in *h_errnop instead of h_errno to be thread-safe + */ +int +lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop) +{ + err_t err; + struct gethostbyname_r_helper *h; + char *hostname; + size_t namelen; + int lh_errno; + + if (h_errnop == NULL) { + /* ensure h_errnop is never NULL */ + h_errnop = &lh_errno; + } + + if (result == NULL) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + /* first thing to do: set *result to nothing */ + *result = NULL; + if ((name == NULL) || (ret == NULL) || (buf == NULL)) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + + namelen = strlen(name); + if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) { + /* buf can't hold the data needed + a copy of name */ + *h_errnop = ERANGE; + return -1; + } + + h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf); + hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper); + + /* query host IP address */ + err = netconn_gethostbyname(name, &h->addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + *h_errnop = HOST_NOT_FOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addr_list[0] = &h->addr; + h->addr_list[1] = NULL; + h->aliases = NULL; + ret->h_name = hostname; + ret->h_aliases = &h->aliases; + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(ip_addr_t); + ret->h_addr_list = (char**)&h->addr_list; + + /* set result != NULL */ + *result = ret; + + /* return success */ + return 0; +} + +/** + * Frees one or more addrinfo structures returned by getaddrinfo(), along with + * any additional storage associated with those structures. If the ai_next field + * of the structure is not null, the entire list of structures is freed. + * + * @param ai struct addrinfo to free + */ +void +lwip_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + while (ai != NULL) { + next = ai->ai_next; + memp_free(MEMP_NETDB, ai); + ai = next; + } +} + +/** + * Translates the name of a service location (for example, a host name) and/or + * a service name and returns a set of socket addresses and associated + * information to be used in creating a socket with which to address the + * specified service. + * Memory for the result is allocated internally and must be freed by calling + * lwip_freeaddrinfo()! + * + * Due to a limitation in dns_gethostbyname, only the first address of a + * host is returned. + * Also, service names are not supported (only port numbers)! + * + * @param nodename descriptive name or address string of the host + * (may be NULL -> local address) + * @param servname port number as string of NULL + * @param hints structure containing input values that set socktype and protocol + * @param res pointer to a pointer where to store the result (set to NULL on failure) + * @return 0 on success, non-zero on failure + * + * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG + */ +int +lwip_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + err_t err; + ip_addr_t addr; + struct addrinfo *ai; + struct sockaddr_storage *sa = NULL; + int port_nr = 0; + size_t total_size; + size_t namelen = 0; + int ai_family; + + if (res == NULL) { + return EAI_FAIL; + } + *res = NULL; + if ((nodename == NULL) && (servname == NULL)) { + return EAI_NONAME; + } + + if (hints != NULL) { + ai_family = hints->ai_family; + if ((ai_family != AF_UNSPEC) +#if LWIP_IPV4 + && (ai_family != AF_INET) +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 + && (ai_family != AF_INET6) +#endif /* LWIP_IPV6 */ + ) { + return EAI_FAMILY; + } + } else { + ai_family = AF_UNSPEC; + } + + if (servname != NULL) { + /* service name specified: convert to port number + * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */ + port_nr = atoi(servname); + if ((port_nr <= 0) || (port_nr > 0xffff)) { + return EAI_SERVICE; + } + } + + if (nodename != NULL) { + /* service location specified, try to resolve */ + if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) { + /* no DNS lookup, just parse for an address string */ + if (!ipaddr_aton(nodename, &addr)) { + return EAI_NONAME; + } +#if LWIP_IPV4 && LWIP_IPV6 + if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) || + (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) { + return EAI_NONAME; + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + } else { +#if LWIP_IPV4 && LWIP_IPV6 + /* AF_UNSPEC: prefer IPv4 */ + u8_t type = NETCONN_DNS_IPV4_IPV6; + if (ai_family == AF_INET) { + type = NETCONN_DNS_IPV4; + } else if (ai_family == AF_INET6) { + type = NETCONN_DNS_IPV6; + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + err = netconn_gethostbyname_addrtype(nodename, &addr, type); + if (err != ERR_OK) { + return EAI_FAIL; + } + } + } else { + /* service location specified, use loopback address */ + if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) { + ip_addr_set_any(ai_family == AF_INET6, &addr); + } else { + ip_addr_set_loopback(ai_family == AF_INET6, &addr); + } + } + + total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage); + if (nodename != NULL) { + namelen = strlen(nodename); + if (namelen > DNS_MAX_NAME_LENGTH) { + /* invalid name length */ + return EAI_FAIL; + } + LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size); + total_size += namelen + 1; + } + /* If this fails, please report to lwip-devel! :-) */ + LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!", + total_size <= NETDB_ELEM_SIZE); + ai = (struct addrinfo *)memp_malloc(MEMP_NETDB); + if (ai == NULL) { + return EAI_MEMORY; + } + memset(ai, 0, total_size); + /* cast through void* to get rid of alignment warnings */ + sa = (struct sockaddr_storage *)(void*)((u8_t*)ai + sizeof(struct addrinfo)); + if (IP_IS_V6_VAL(addr)) { +#if LWIP_IPV6 + struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa; + /* set up sockaddr */ + inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr)); + sa6->sin6_family = AF_INET6; + sa6->sin6_len = sizeof(struct sockaddr_in6); + sa6->sin6_port = lwip_htons((u16_t)port_nr); + ai->ai_family = AF_INET6; +#endif /* LWIP_IPV6 */ + } else { +#if LWIP_IPV4 + struct sockaddr_in *sa4 = (struct sockaddr_in*)sa; + /* set up sockaddr */ + inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr)); + sa4->sin_family = AF_INET; + sa4->sin_len = sizeof(struct sockaddr_in); + sa4->sin_port = lwip_htons((u16_t)port_nr); + ai->ai_family = AF_INET; +#endif /* LWIP_IPV4 */ + } + + /* set up addrinfo */ + if (hints != NULL) { + /* copy socktype & protocol from hints if specified */ + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + } + if (nodename != NULL) { + /* copy nodename to canonname if specified */ + ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage)); + MEMCPY(ai->ai_canonname, nodename, namelen); + ai->ai_canonname[namelen] = 0; + } + ai->ai_addrlen = sizeof(struct sockaddr_storage); + ai->ai_addr = (struct sockaddr*)sa; + + *res = ai; + + return 0; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/sdk/Src/Network/lwip2.0.3/api/netifapi.c b/sdk/Src/Network/lwip2.0.3/api/netifapi.c new file mode 100644 index 0000000..aac0975 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/netifapi.c @@ -0,0 +1,221 @@ +/** + * @file + * Network Interface Sequential API module + * + * @defgroup netifapi NETIF API + * @ingroup sequential_api + * Thread-safe functions to be called from non-TCPIP threads + * + * @defgroup netifapi_netif NETIF related + * @ingroup netifapi + * To be called from non-TCPIP threads + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netifapi.h" +#include "lwip/memp.h" +#include "lwip/priv/tcpip_priv.h" + +#define NETIFAPI_VAR_REF(name) API_VAR_REF(name) +#define NETIFAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct netifapi_msg, name) +#define NETIFAPI_VAR_ALLOC(name) API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM) +#define NETIFAPI_VAR_FREE(name) API_VAR_FREE(MEMP_NETIFAPI_MSG, name) + +/** + * Call netif_add() inside the tcpip_thread context. + */ +static err_t +netifapi_do_netif_add(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct netifapi_msg */ + struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m; + + if (!netif_add( msg->netif, +#if LWIP_IPV4 + API_EXPR_REF(msg->msg.add.ipaddr), + API_EXPR_REF(msg->msg.add.netmask), + API_EXPR_REF(msg->msg.add.gw), +#endif /* LWIP_IPV4 */ + msg->msg.add.state, + msg->msg.add.init, + msg->msg.add.input)) { + return ERR_IF; + } else { + return ERR_OK; + } +} + +#if LWIP_IPV4 +/** + * Call netif_set_addr() inside the tcpip_thread context. + */ +static err_t +netifapi_do_netif_set_addr(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct netifapi_msg */ + struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m; + + netif_set_addr( msg->netif, + API_EXPR_REF(msg->msg.add.ipaddr), + API_EXPR_REF(msg->msg.add.netmask), + API_EXPR_REF(msg->msg.add.gw)); + return ERR_OK; +} +#endif /* LWIP_IPV4 */ + +/** + * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the + * tcpip_thread context. + */ +static err_t +netifapi_do_netif_common(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct netifapi_msg */ + struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m; + + if (msg->msg.common.errtfunc != NULL) { + return msg->msg.common.errtfunc(msg->netif); + } else { + msg->msg.common.voidfunc(msg->netif); + return ERR_OK; + } +} + +/** + * @ingroup netifapi_netif + * Call netif_add() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_add() + */ +err_t +netifapi_netif_add(struct netif *netif, +#if LWIP_IPV4 + const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw, +#endif /* LWIP_IPV4 */ + void *state, netif_init_fn init, netif_input_fn input) +{ + err_t err; + NETIFAPI_VAR_DECLARE(msg); + NETIFAPI_VAR_ALLOC(msg); + +#if LWIP_IPV4 + if (ipaddr == NULL) { + ipaddr = IP4_ADDR_ANY4; + } + if (netmask == NULL) { + netmask = IP4_ADDR_ANY4; + } + if (gw == NULL) { + gw = IP4_ADDR_ANY4; + } +#endif /* LWIP_IPV4 */ + + NETIFAPI_VAR_REF(msg).netif = netif; +#if LWIP_IPV4 + NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr); + NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask); + NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw); +#endif /* LWIP_IPV4 */ + NETIFAPI_VAR_REF(msg).msg.add.state = state; + NETIFAPI_VAR_REF(msg).msg.add.init = init; + NETIFAPI_VAR_REF(msg).msg.add.input = input; + err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call); + NETIFAPI_VAR_FREE(msg); + return err; +} + +#if LWIP_IPV4 +/** + * @ingroup netifapi_netif + * Call netif_set_addr() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_set_addr() + */ +err_t +netifapi_netif_set_addr(struct netif *netif, + const ip4_addr_t *ipaddr, + const ip4_addr_t *netmask, + const ip4_addr_t *gw) +{ + err_t err; + NETIFAPI_VAR_DECLARE(msg); + NETIFAPI_VAR_ALLOC(msg); + + if (ipaddr == NULL) { + ipaddr = IP4_ADDR_ANY4; + } + if (netmask == NULL) { + netmask = IP4_ADDR_ANY4; + } + if (gw == NULL) { + gw = IP4_ADDR_ANY4; + } + + NETIFAPI_VAR_REF(msg).netif = netif; + NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr); + NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask); + NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw); + err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call); + NETIFAPI_VAR_FREE(msg); + return err; +} +#endif /* LWIP_IPV4 */ + +/** + * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe + * way by running that function inside the tcpip_thread context. + * + * @note use only for functions where there is only "netif" parameter. + */ +err_t +netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc) +{ + err_t err; + NETIFAPI_VAR_DECLARE(msg); + NETIFAPI_VAR_ALLOC(msg); + + NETIFAPI_VAR_REF(msg).netif = netif; + NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc; + NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc; + err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call); + NETIFAPI_VAR_FREE(msg); + return err; +} + +#endif /* LWIP_NETIF_API */ diff --git a/sdk/Src/Network/lwip2.0.3/api/pppapi.c b/sdk/Src/Network/lwip2.0.3/api/pppapi.c new file mode 100644 index 0000000..f0f7b24 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/pppapi.c @@ -0,0 +1,370 @@ +/** + * @file + * Point To Point Protocol Sequential API module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pppapi.h" +#include "lwip/priv/tcpip_priv.h" +#include "netif/ppp/pppoe.h" +#include "netif/ppp/pppol2tp.h" +#include "netif/ppp/pppos.h" + +/** + * Call ppp_set_default() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_set_default(struct pppapi_msg_msg *msg) +{ + ppp_set_default(msg->ppp); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_set_default() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_set_default(ppp_pcb *pcb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_set_default; + msg.msg.ppp = pcb; + TCPIP_PPPAPI(&msg); +} + + +/** + * Call ppp_set_auth() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_set_auth(struct pppapi_msg_msg *msg) +{ + ppp_set_auth(msg->ppp, msg->msg.setauth.authtype, + msg->msg.setauth.user, msg->msg.setauth.passwd); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_set_auth() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_set_auth; + msg.msg.ppp = pcb; + msg.msg.msg.setauth.authtype = authtype; + msg.msg.msg.setauth.user = user; + msg.msg.msg.setauth.passwd = passwd; + TCPIP_PPPAPI(&msg); +} + + +#if PPP_NOTIFY_PHASE +/** + * Call ppp_set_notify_phase_callback() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_set_notify_phase_callback(struct pppapi_msg_msg *msg) +{ + ppp_set_notify_phase_callback(msg->ppp, msg->msg.setnotifyphasecb.notify_phase_cb); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_set_notify_phase_callback() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_set_notify_phase_callback; + msg.msg.ppp = pcb; + msg.msg.msg.setnotifyphasecb.notify_phase_cb = notify_phase_cb; + TCPIP_PPPAPI(&msg); +} +#endif /* PPP_NOTIFY_PHASE */ + + +#if PPPOS_SUPPORT +/** + * Call pppos_create() inside the tcpip_thread context. + */ +static void +pppapi_do_pppos_create(struct pppapi_msg_msg *msg) +{ + msg->ppp = pppos_create(msg->msg.serialcreate.pppif, msg->msg.serialcreate.output_cb, + msg->msg.serialcreate.link_status_cb, msg->msg.serialcreate.ctx_cb); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call pppos_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +ppp_pcb* +pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_pppos_create; + msg.msg.msg.serialcreate.pppif = pppif; + msg.msg.msg.serialcreate.output_cb = output_cb; + msg.msg.msg.serialcreate.link_status_cb = link_status_cb; + msg.msg.msg.serialcreate.ctx_cb = ctx_cb; + TCPIP_PPPAPI(&msg); + return msg.msg.ppp; +} +#endif /* PPPOS_SUPPORT */ + + +#if PPPOE_SUPPORT +/** + * Call pppoe_create() inside the tcpip_thread context. + */ +static void +pppapi_do_pppoe_create(struct pppapi_msg_msg *msg) +{ + + msg->ppp = pppoe_create(msg->msg.ethernetcreate.pppif, msg->msg.ethernetcreate.ethif, + msg->msg.ethernetcreate.service_name, msg->msg.ethernetcreate.concentrator_name, + msg->msg.ethernetcreate.link_status_cb, msg->msg.ethernetcreate.ctx_cb); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call pppoe_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +ppp_pcb* +pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name, + const char *concentrator_name, ppp_link_status_cb_fn link_status_cb, + void *ctx_cb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_pppoe_create; + msg.msg.msg.ethernetcreate.pppif = pppif; + msg.msg.msg.ethernetcreate.ethif = ethif; + msg.msg.msg.ethernetcreate.service_name = service_name; + msg.msg.msg.ethernetcreate.concentrator_name = concentrator_name; + msg.msg.msg.ethernetcreate.link_status_cb = link_status_cb; + msg.msg.msg.ethernetcreate.ctx_cb = ctx_cb; + TCPIP_PPPAPI(&msg); + return msg.msg.ppp; +} +#endif /* PPPOE_SUPPORT */ + + +#if PPPOL2TP_SUPPORT +/** + * Call pppol2tp_create() inside the tcpip_thread context. + */ +static void +pppapi_do_pppol2tp_create(struct pppapi_msg_msg *msg) +{ + msg->ppp = pppol2tp_create(msg->msg.l2tpcreate.pppif, + msg->msg.l2tpcreate.netif, msg->msg.l2tpcreate.ipaddr, msg->msg.l2tpcreate.port, +#if PPPOL2TP_AUTH_SUPPORT + msg->msg.l2tpcreate.secret, + msg->msg.l2tpcreate.secret_len, +#else /* PPPOL2TP_AUTH_SUPPORT */ + NULL, +#endif /* PPPOL2TP_AUTH_SUPPORT */ + msg->msg.l2tpcreate.link_status_cb, msg->msg.l2tpcreate.ctx_cb); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call pppol2tp_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +ppp_pcb* +pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port, + const u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_pppol2tp_create; + msg.msg.msg.l2tpcreate.pppif = pppif; + msg.msg.msg.l2tpcreate.netif = netif; + msg.msg.msg.l2tpcreate.ipaddr = ipaddr; + msg.msg.msg.l2tpcreate.port = port; +#if PPPOL2TP_AUTH_SUPPORT + msg.msg.msg.l2tpcreate.secret = secret; + msg.msg.msg.l2tpcreate.secret_len = secret_len; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + msg.msg.msg.l2tpcreate.link_status_cb = link_status_cb; + msg.msg.msg.l2tpcreate.ctx_cb = ctx_cb; + TCPIP_PPPAPI(&msg); + return msg.msg.ppp; +} +#endif /* PPPOL2TP_SUPPORT */ + + +/** + * Call ppp_connect() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_connect(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_connect(msg->ppp, msg->msg.connect.holdoff); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_connect() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_connect(ppp_pcb *pcb, u16_t holdoff) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_connect; + msg.msg.ppp = pcb; + msg.msg.msg.connect.holdoff = holdoff; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} + + +#if PPP_SERVER +/** + * Call ppp_listen() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_listen(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_listen(msg->ppp, msg->msg.listen.addrs); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_listen() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_listen(ppp_pcb *pcb, struct ppp_addrs *addrs) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_listen; + msg.msg.ppp = pcb; + msg.msg.msg.listen.addrs = addrs; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} +#endif /* PPP_SERVER */ + + +/** + * Call ppp_close() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_close(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_close(msg->ppp, msg->msg.close.nocarrier); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_close() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_close(ppp_pcb *pcb, u8_t nocarrier) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_close; + msg.msg.ppp = pcb; + msg.msg.msg.close.nocarrier = nocarrier; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} + + +/** + * Call ppp_free() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_free(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_free(msg->ppp); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_free() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_free(ppp_pcb *pcb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_free; + msg.msg.ppp = pcb; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} + + +/** + * Call ppp_ioctl() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_ioctl(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_ioctl(msg->ppp, msg->msg.ioctl.cmd, msg->msg.ioctl.arg); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_ioctl() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_ioctl; + msg.msg.ppp = pcb; + msg.msg.msg.ioctl.cmd = cmd; + msg.msg.msg.ioctl.arg = arg; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} + + +#endif /* LWIP_PPP_API */ diff --git a/sdk/Src/Network/lwip2.0.3/api/sockets.c b/sdk/Src/Network/lwip2.0.3/api/sockets.c new file mode 100644 index 0000000..7ab4a79 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/sockets.c @@ -0,0 +1,2860 @@ +/** + * @file + * Sockets BSD-Like API module + * + * @defgroup socket Socket API + * @ingroup sequential_api + * BSD-style socket API.\n + * Thread-safe, to be called from non-TCPIP threads only.\n + * Can be activated by defining @ref LWIP_SOCKET to 1.\n + * Header is in posix/sys/socket.h\b + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * Improved by Marc Boucher and David Haas + * + */ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sockets.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/inet.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/priv/tcpip_priv.h" +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +/* If the netconn API is not required publicly, then we include the necessary + files here to get the implementation */ +#if !LWIP_NETCONN +#undef LWIP_NETCONN +#define LWIP_NETCONN 1 +#include "api_msg.c" +#include "api_lib.c" +#include "netbuf.c" +#undef LWIP_NETCONN +#define LWIP_NETCONN 0 +#endif + +#if LWIP_IPV4 +#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \ + (sin)->sin_len = sizeof(struct sockaddr_in); \ + (sin)->sin_family = AF_INET; \ + (sin)->sin_port = lwip_htons((port)); \ + inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \ + memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0) +#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \ + inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \ + (port) = lwip_ntohs((sin)->sin_port); }while(0) +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \ + (sin6)->sin6_len = sizeof(struct sockaddr_in6); \ + (sin6)->sin6_family = AF_INET6; \ + (sin6)->sin6_port = lwip_htons((port)); \ + (sin6)->sin6_flowinfo = 0; \ + inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \ + (sin6)->sin6_scope_id = 0; }while(0) +#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \ + inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \ + (port) = lwip_ntohs((sin6)->sin6_port); }while(0) +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV4 && LWIP_IPV6 +static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port); + +#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \ + ((namelen) == sizeof(struct sockaddr_in6))) +#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \ + ((name)->sa_family == AF_INET6)) +#define SOCK_ADDR_TYPE_MATCH(name, sock) \ + ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \ + (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type)))) +#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \ + if (IP_IS_V6(ipaddr)) { \ + IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \ + } else { \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \ + } } while(0) +#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port)) +#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \ + (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6)) +#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */ +#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in6)) +#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET6) +#define SOCK_ADDR_TYPE_MATCH(name, sock) 1 +#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \ + IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port) +#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \ + SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port) +#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type) +#else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */ +#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in)) +#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET) +#define SOCK_ADDR_TYPE_MATCH(name, sock) 1 +#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port) +#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \ + SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port) +#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type) +#endif /* LWIP_IPV6 */ + +#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \ + IS_SOCK_ADDR_TYPE_VALID(name)) +#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \ + SOCK_ADDR_TYPE_MATCH(name, sock)) +#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0) + + +#define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return EINVAL; }}while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \ + if ((sock)->conn == NULL) { return EINVAL; } }while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \ + if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \ + if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0) + + +#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name) +#define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name) +#define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name) API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name) +#if LWIP_MPU_COMPATIBLE +#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \ + name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \ + if (name == NULL) { \ + sock_set_errno(sock, ENOMEM); \ + return -1; \ + } }while(0) +#else /* LWIP_MPU_COMPATIBLE */ +#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) +#endif /* LWIP_MPU_COMPATIBLE */ + +#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD +#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int +#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val)) +#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((s32_t)*(const int*)(optval)) +#else +#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval +#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \ + s32_t loc = (val); \ + ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \ + ((struct timeval *)(optval))->tv_usec = ((loc) % 1000U) * 1000U; }while(0) +#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000U) + (((const struct timeval *)(optval))->tv_usec / 1000U)) +#endif + +#define NUM_SOCKETS MEMP_NUM_NETCONN + +/** This is overridable for the rare case where more than 255 threads + * select on the same socket... + */ +#ifndef SELWAIT_T +#define SELWAIT_T u8_t +#endif + +/** Contains all internal pointers and states used for a socket */ +struct lwip_sock { + /** sockets currently are built on netconns, each socket has one netconn */ + struct netconn *conn; + /** data that was left from the previous read */ + void *lastdata; + /** offset in the data that was left from the previous read */ + u16_t lastoffset; + /** number of times data was received, set by event_callback(), + tested by the receive and select functions */ + s16_t rcvevent; + /** number of times data was ACKed (free send buffer), set by event_callback(), + tested by select */ + u16_t sendevent; + /** error happened for this socket, set by event_callback(), tested by select */ + u16_t errevent; + /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */ + u8_t err; + /** counter of how many threads are waiting for this socket using select */ + SELWAIT_T select_waiting; +}; + +#if LWIP_NETCONN_SEM_PER_THREAD +#define SELECT_SEM_T sys_sem_t* +#define SELECT_SEM_PTR(sem) (sem) +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define SELECT_SEM_T sys_sem_t +#define SELECT_SEM_PTR(sem) (&(sem)) +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + +/** Description for a task waiting in select */ +struct lwip_select_cb { + /** Pointer to the next waiting task */ + struct lwip_select_cb *next; + /** Pointer to the previous waiting task */ + struct lwip_select_cb *prev; + /** readset passed to select */ + fd_set *readset; + /** writeset passed to select */ + fd_set *writeset; + /** unimplemented: exceptset passed to select */ + fd_set *exceptset; + /** don't signal the same semaphore twice: set to 1 when signalled */ + int sem_signalled; + /** semaphore to wake up a task waiting for select */ + SELECT_SEM_T sem; +}; + +/** A struct sockaddr replacement that has the same alignment as sockaddr_in/ + * sockaddr_in6 if instantiated. + */ +union sockaddr_aligned { + struct sockaddr sa; +#if LWIP_IPV6 + struct sockaddr_in6 sin6; +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 + struct sockaddr_in sin; +#endif /* LWIP_IPV4 */ +}; + +#if LWIP_IGMP +/* Define the number of IPv4 multicast memberships, default is one per socket */ +#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS +#define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS +#endif + +/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when + a socket is closed */ +struct lwip_socket_multicast_pair { + /** the socket */ + struct lwip_sock* sock; + /** the interface address */ + ip4_addr_t if_addr; + /** the group address */ + ip4_addr_t multi_addr; +}; + +struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS]; + +static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr); +static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr); +static void lwip_socket_drop_registered_memberships(int s); +#endif /* LWIP_IGMP */ + +/** The global array of available sockets */ +static struct lwip_sock sockets[NUM_SOCKETS]; +/** The global list of tasks waiting for select */ +static struct lwip_select_cb *select_cb_list; +/** This counter is increased from lwip_select when the list is changed + and checked in event_callback to see if it has changed. */ +static volatile int select_cb_ctr; + +#if LWIP_SOCKET_SET_ERRNO +#ifndef set_errno +#define set_errno(err) do { errno = (err); } while(0) +#endif +#else /* LWIP_SOCKET_SET_ERRNO */ +#define set_errno(err) +#endif /* LWIP_SOCKET_SET_ERRNO */ + +#define sock_set_errno(sk, e) do { \ + const int sockerr = (e); \ + sk->err = (u8_t)sockerr; \ + set_errno(sockerr); \ +} while (0) + +/* Forward declaration of some functions */ +static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +#if !LWIP_TCPIP_CORE_LOCKING +static void lwip_getsockopt_callback(void *arg); +static void lwip_setsockopt_callback(void *arg); +#endif +static u8_t lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen); +static u8_t lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen); + +#if LWIP_IPV4 && LWIP_IPV6 +static void +sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port) +{ + if ((sockaddr->sa_family) == AF_INET6) { + SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, *port); + ipaddr->type = IPADDR_TYPE_V6; + } else { + SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, *port); + ipaddr->type = IPADDR_TYPE_V4; + } +} +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + +/** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */ +void +lwip_socket_thread_init(void) +{ + netconn_thread_init(); +} + +/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */ +void +lwip_socket_thread_cleanup(void) +{ + netconn_thread_cleanup(); +} + +/** + * Map a externally used socket index to the internal socket representation. + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +get_socket(int s) +{ + struct lwip_sock *sock; + + s -= LWIP_SOCKET_OFFSET; + + if ((s < 0) || (s >= NUM_SOCKETS)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s + LWIP_SOCKET_OFFSET)); + set_errno(EBADF); + return NULL; + } + + sock = &sockets[s]; + + if (!sock->conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s + LWIP_SOCKET_OFFSET)); + set_errno(EBADF); + return NULL; + } + + return sock; +} + +/** + * Same as get_socket but doesn't set errno + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +tryget_socket(int s) +{ + s -= LWIP_SOCKET_OFFSET; + if ((s < 0) || (s >= NUM_SOCKETS)) { + return NULL; + } + if (!sockets[s].conn) { + return NULL; + } + return &sockets[s]; +} + +/** + * Allocate a new socket for a given netconn. + * + * @param newconn the netconn for which to allocate a socket + * @param accepted 1 if socket has been created by accept(), + * 0 if socket has been created by socket() + * @return the index of the new socket; -1 on error + */ +static int +alloc_socket(struct netconn *newconn, int accepted) +{ + int i; + SYS_ARCH_DECL_PROTECT(lev); + + /* allocate a new socket identifier */ + for (i = 0; i < NUM_SOCKETS; ++i) { + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + if (!sockets[i].conn /*&& (sockets[i].select_waiting == 0)*/) { + sockets[i].conn = newconn; + /* The socket is not yet known to anyone, so no need to protect + after having marked it as used. */ + SYS_ARCH_UNPROTECT(lev); + sockets[i].lastdata = NULL; + sockets[i].lastoffset = 0; + sockets[i].rcvevent = 0; + /* TCP sendbuf is empty, but the socket is not yet writable until connected + * (unless it has been created by accept()). */ + sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1); + sockets[i].errevent = 0; + sockets[i].err = 0; + return i + LWIP_SOCKET_OFFSET; + } + SYS_ARCH_UNPROTECT(lev); + } + return -1; +} + +/** Free a socket. The socket's netconn must have been + * delete before! + * + * @param sock the socket to free + * @param is_tcp != 0 for TCP sockets, used to free lastdata + */ +static void +free_socket(struct lwip_sock *sock, int is_tcp) +{ + void *lastdata; + + lastdata = sock->lastdata; + sock->lastdata = NULL; + sock->lastoffset = 0; + sock->err = 0; + + /* Protect socket array */ + SYS_ARCH_SET(sock->conn, NULL); + /* don't use 'sock' after this line, as another task might have allocated it */ + + if (lastdata != NULL) { + if (is_tcp) { + pbuf_free((struct pbuf *)lastdata); + } else { + netbuf_delete((struct netbuf *)lastdata); + } + } +} + +/* Below this, the well-known socket functions are implemented. + * Use google.com or opengroup.org to get a good description :-) + * + * Exceptions are documented! + */ + +int +lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + struct lwip_sock *sock, *nsock; + struct netconn *newconn; + ip_addr_t naddr; + u16_t port = 0; + int newsock; + err_t err; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s)); + set_errno(EWOULDBLOCK); + return -1; + } + + /* wait for a new connection */ + err = netconn_accept(sock->conn, &newconn); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + } else if (err == ERR_CLSD) { + sock_set_errno(sock, EINVAL); + } else { + sock_set_errno(sock, err_to_errno(err)); + } + return -1; + } + LWIP_ASSERT("newconn != NULL", newconn != NULL); + + newsock = alloc_socket(newconn, 1); + if (newsock == -1) { + netconn_delete(newconn); + sock_set_errno(sock, ENFILE); + return -1; + } + LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET)); + LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback); + nsock = &sockets[newsock - LWIP_SOCKET_OFFSET]; + + /* See event_callback: If data comes in right away after an accept, even + * though the server task might not have created a new socket yet. + * In that case, newconn->socket is counted down (newconn->socket--), + * so nsock->rcvevent is >= 1 here! + */ + SYS_ARCH_PROTECT(lev); + nsock->rcvevent += (s16_t)(-1 - newconn->socket); + newconn->socket = newsock; + SYS_ARCH_UNPROTECT(lev); + + /* Note that POSIX only requires us to check addr is non-NULL. addrlen must + * not be NULL if addr is valid. + */ + if (addr != NULL) { + union sockaddr_aligned tempaddr; + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, &naddr, &port); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); + netconn_delete(newconn); + free_socket(nsock, 1); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); + + IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port); + if (*addrlen > tempaddr.sa.sa_len) { + *addrlen = tempaddr.sa.sa_len; + } + MEMCPY(addr, &tempaddr, *addrlen); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock)); + ip_addr_debug_print_val(SOCKETS_DEBUG, naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); + } else { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock)); + } + + sock_set_errno(sock, 0); + return newsock; +} + +int LWIP_GetMaxSockets() { + return NUM_SOCKETS; +} +int LWIP_GetActiveSockets() { + int i; + int r=0; + for (i = 0; i < NUM_SOCKETS; ++i) { + if (sockets[i].conn) { + r++; + } + } + return r; +} + + +int +lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + ip_addr_t local_addr; + u16_t local_port; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (!SOCK_ADDR_TYPE_MATCH(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* check size, family and alignment of 'name' */ + LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(namelen); + + SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); + ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port)); + +#if LWIP_IPV4 && LWIP_IPV6 + /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */ + if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) { + unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr)); + IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4); + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + err = netconn_bind(sock->conn, &local_addr, local_port); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_close(int s) +{ + struct lwip_sock *sock; + int is_tcp = 0; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn != NULL) { + is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP; + } else { + LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL); + } + +#if LWIP_IGMP + /* drop all possibly joined IGMP memberships */ + lwip_socket_drop_registered_memberships(s); +#endif /* LWIP_IGMP */ + + err = netconn_delete(sock->conn); + if (err != ERR_OK) { + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + free_socket(sock, is_tcp); + set_errno(0); + return 0; +} + +int +lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + LWIP_UNUSED_ARG(namelen); + if (name->sa_family == AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + ip_addr_t remote_addr; + u16_t remote_port; + + /* check size, family and alignment of 'name' */ + LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); + ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port)); + +#if LWIP_IPV4 && LWIP_IPV6 + /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */ + if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) { + unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr)); + IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4); + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + err = netconn_connect(sock->conn, &remote_addr, remote_port); + } + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +/** + * Set a socket into listen mode. + * The socket may not have been used for another connection previously. + * + * @param s the socket to set to listening mode + * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1) + * @return 0 on success, non-zero on failure + */ +int +lwip_listen(int s, int backlog) +{ + struct lwip_sock *sock; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* limit the "backlog" parameter to fit in an u8_t */ + backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff); + + err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return -1; + } + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + struct lwip_sock *sock; + void *buf = NULL; + struct pbuf *p; + u16_t buflen, copylen; + int off = 0; + u8_t done = 0; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + do { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata)); + /* Check if there is data left from the last recv operation. */ + if (sock->lastdata) { + buf = sock->lastdata; + } else { + /* If this is non-blocking call, then check first */ + if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) && + (sock->rcvevent <= 0)) { + if (off > 0) { + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); + set_errno(EWOULDBLOCK); + return -1; + } + + /* No data was left from the previous operation, so we try to get + some from the network. */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf); + } else { + err = netconn_recv(sock->conn, (struct netbuf **)&buf); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n", + err, buf)); + + if (err != ERR_OK) { + if (off > 0) { + if (err == ERR_CLSD) { + /* closed but already received data, ensure select gets the FIN, too */ + event_callback(sock->conn, NETCONN_EVT_RCVPLUS, 0); + } + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + /* We should really do some error checking here. */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n", + s, lwip_strerr(err))); + sock_set_errno(sock, err_to_errno(err)); + if (err == ERR_CLSD) { + return 0; + } else { + return -1; + } + } + LWIP_ASSERT("buf != NULL", buf != NULL); + sock->lastdata = buf; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + p = (struct pbuf *)buf; + } else { + p = ((struct netbuf *)buf)->p; + } + buflen = p->tot_len; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n", + buflen, len, off, sock->lastoffset)); + + buflen -= sock->lastoffset; + + if (len > buflen) { + copylen = buflen; + } else { + copylen = (u16_t)len; + } + + /* copy the contents of the received buffer into + the supplied memory pointer mem */ + pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset); + + off += copylen; + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen); + len -= copylen; + if ((len <= 0) || + (p->flags & PBUF_FLAG_PUSH) || + (sock->rcvevent <= 0) || + ((flags & MSG_PEEK) != 0)) { + done = 1; + } + } else { + done = 1; + } + + /* Check to see from where the data was.*/ + if (done) { +#if !SOCKETS_DEBUG + if (from && fromlen) +#endif /* !SOCKETS_DEBUG */ + { + u16_t port; + ip_addr_t tmpaddr; + ip_addr_t *fromaddr; + union sockaddr_aligned saddr; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + fromaddr = &tmpaddr; + netconn_getaddr(sock->conn, fromaddr, &port, 0); + } else { + port = netbuf_fromport((struct netbuf *)buf); + fromaddr = netbuf_fromaddr((struct netbuf *)buf); + } + +#if LWIP_IPV4 && LWIP_IPV6 + /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */ + if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) && IP_IS_V4(fromaddr)) { + ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr)); + IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6); + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port); + ip_addr_debug_print(SOCKETS_DEBUG, fromaddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); +#if SOCKETS_DEBUG + if (from && fromlen) +#endif /* SOCKETS_DEBUG */ + { + if (*fromlen > saddr.sa.sa_len) { + *fromlen = saddr.sa.sa_len; + } + MEMCPY(from, &saddr, *fromlen); + } + } + } + + /* If we don't peek the incoming message... */ + if ((flags & MSG_PEEK) == 0) { + /* If this is a TCP socket, check if there is data left in the + buffer. If so, it should be saved in the sock structure for next + time around. */ + if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) { + sock->lastdata = buf; + sock->lastoffset += copylen; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf)); + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + pbuf_free((struct pbuf *)buf); + } else { + netbuf_delete((struct netbuf *)buf); + } + buf = NULL; + } + } + } while (!done); + + sock_set_errno(sock, 0); + return off; +} + +int +lwip_read(int s, void *mem, size_t len) +{ + return lwip_recvfrom(s, mem, len, 0, NULL, NULL); +} + +int +lwip_recv(int s, void *mem, size_t len, int flags) +{ + return lwip_recvfrom(s, mem, len, flags, NULL, NULL); +} + +int +lwip_send(int s, const void *data, size_t size, int flags) +{ + struct lwip_sock *sock; + err_t err; + u8_t write_flags; + size_t written; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n", + s, data, size, flags)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + return lwip_sendto(s, data, size, flags, NULL, 0); +#else /* (LWIP_UDP || LWIP_RAW) */ + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + write_flags = NETCONN_COPY | + ((flags & MSG_MORE) ? NETCONN_MORE : 0) | + ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); + written = 0; + err = netconn_write_partly(sock->conn, data, size, write_flags, &written); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written)); + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? (int)written : -1); +} + +int +lwip_sendmsg(int s, const struct msghdr *msg, int flags) +{ + struct lwip_sock *sock; + int i; +#if LWIP_TCP + u8_t write_flags; + size_t written; +#endif + int size = 0; + err_t err = ERR_OK; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL, + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + LWIP_UNUSED_ARG(msg->msg_control); + LWIP_UNUSED_ARG(msg->msg_controllen); + LWIP_UNUSED_ARG(msg->msg_flags); + LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", (msg->msg_iov != NULL && msg->msg_iovlen != 0), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { +#if LWIP_TCP + write_flags = NETCONN_COPY | + ((flags & MSG_MORE) ? NETCONN_MORE : 0) | + ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); + + for (i = 0; i < msg->msg_iovlen; i++) { + u8_t apiflags = write_flags; + if (i + 1 < msg->msg_iovlen) { + apiflags |= NETCONN_MORE; + } + written = 0; + err = netconn_write_partly(sock->conn, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len, write_flags, &written); + if (err == ERR_OK) { + size += written; + /* check that the entire IO vector was accepected, if not return a partial write */ + if (written != msg->msg_iov[i].iov_len) + break; + } + /* none of this IO vector was accepted, but previous was, return partial write and conceal ERR_WOULDBLOCK */ + else if (err == ERR_WOULDBLOCK && size > 0) { + err = ERR_OK; + /* let ERR_WOULDBLOCK persist on the netconn since we are returning ERR_OK */ + break; + } else { + size = -1; + break; + } + } + sock_set_errno(sock, err_to_errno(err)); + return size; +#else /* LWIP_TCP */ + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ + } + /* else, UDP and RAW NETCONNs */ +#if LWIP_UDP || LWIP_RAW + { + struct netbuf *chain_buf; + + LWIP_UNUSED_ARG(flags); + LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) || + IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)) , + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + /* initialize chain buffer with destination */ + chain_buf = netbuf_new(); + if (!chain_buf) { + sock_set_errno(sock, err_to_errno(ERR_MEM)); + return -1; + } + if (msg->msg_name) { + u16_t remote_port; + SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf->addr, remote_port); + netbuf_fromport(chain_buf) = remote_port; + } +#if LWIP_NETIF_TX_SINGLE_PBUF + for (i = 0; i < msg->msg_iovlen; i++) { + size += msg->msg_iov[i].iov_len; + } + /* Allocate a new netbuf and copy the data into it. */ + if (netbuf_alloc(chain_buf, (u16_t)size) == NULL) { + err = ERR_MEM; + } else { + /* flatten the IO vectors */ + size_t offset = 0; + for (i = 0; i < msg->msg_iovlen; i++) { + MEMCPY(&((u8_t*)chain_buf->p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len); + offset += msg->msg_iov[i].iov_len; + } +#if LWIP_CHECKSUM_ON_COPY + { + /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */ + u16_t chksum = ~inet_chksum_pbuf(chain_buf->p); + netbuf_set_chksum(chain_buf, chksum); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + err = ERR_OK; + } +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain + manually to avoid having to allocate, chain, and delete a netbuf for each iov */ + for (i = 0; i < msg->msg_iovlen; i++) { + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (p == NULL) { + err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */ + break; + } + p->payload = msg->msg_iov[i].iov_base; + LWIP_ASSERT("iov_len < u16_t", msg->msg_iov[i].iov_len <= 0xFFFF); + p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len; + /* netbuf empty, add new pbuf */ + if (chain_buf->p == NULL) { + chain_buf->p = chain_buf->ptr = p; + /* add pbuf to existing pbuf chain */ + } else { + pbuf_cat(chain_buf->p, p); + } + } + /* save size of total chain */ + if (err == ERR_OK) { + size = netbuf_len(chain_buf); + } +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + if (err == ERR_OK) { +#if LWIP_IPV4 && LWIP_IPV6 + /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */ + if (IP_IS_V6_VAL(chain_buf->addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf->addr))) { + unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf->addr), ip_2_ip6(&chain_buf->addr)); + IP_SET_TYPE_VAL(chain_buf->addr, IPADDR_TYPE_V4); + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + /* send the data */ + err = netconn_send(sock->conn, chain_buf); + } + + /* deallocated the buffer */ + netbuf_delete(chain_buf); + + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? size : -1); + } +#else /* LWIP_UDP || LWIP_RAW */ + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_UDP || LWIP_RAW */ +} + +int +lwip_sendto(int s, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen) +{ + struct lwip_sock *sock; + err_t err; + u16_t short_size; + u16_t remote_port; + struct netbuf buf; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { +#if LWIP_TCP + return lwip_send(s, data, size, flags); +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(flags); + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ + } + + /* @todo: split into multiple sendto's? */ + LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff); + short_size = (u16_t)size; + LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || + (IS_SOCK_ADDR_LEN_VALID(tolen) && + IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(tolen); + + /* initialize a buffer */ + buf.p = buf.ptr = NULL; +#if LWIP_CHECKSUM_ON_COPY + buf.flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + if (to) { + SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port); + } else { + remote_port = 0; + ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); + } + netbuf_fromport(&buf) = remote_port; + + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=", + s, data, short_size, flags)); + ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port)); + + /* make the buffer point to the data that should be sent */ +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Allocate a new netbuf and copy the data into it. */ + if (netbuf_alloc(&buf, short_size) == NULL) { + err = ERR_MEM; + } else { +#if LWIP_CHECKSUM_ON_COPY + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) { + u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size); + netbuf_set_chksum(&buf, chksum); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + MEMCPY(buf.p->payload, data, short_size); + } + err = ERR_OK; + } +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + err = netbuf_ref(&buf, data, short_size); +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (err == ERR_OK) { +#if LWIP_IPV4 && LWIP_IPV6 + /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */ + if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) { + unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr)); + IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4); + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + /* send the data */ + err = netconn_send(sock->conn, &buf); + } + + /* deallocated the buffer */ + netbuf_free(&buf); + + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? short_size : -1); +} + +int +lwip_socket(int domain, int type, int protocol) +{ + struct netconn *conn; + int i; + + LWIP_UNUSED_ARG(domain); /* @todo: check this */ + + /* create a netconn */ + switch (type) { + case SOCK_RAW: + conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW), + (u8_t)protocol, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_DGRAM: + conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, + ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) , + event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_STREAM: + conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", + domain, type, protocol)); + set_errno(EINVAL); + return -1; + } + + if (!conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n")); + set_errno(ENOBUFS); + return -1; + } + + i = alloc_socket(conn, 0); + + if (i == -1) { + netconn_delete(conn); + set_errno(ENFILE); + return -1; + } + conn->socket = i; + LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); + set_errno(0); + return i; +} + +int +lwip_write(int s, const void *data, size_t size) +{ + return lwip_send(s, data, size, 0); +} + +int +lwip_writev(int s, const struct iovec *iov, int iovcnt) +{ + struct msghdr msg; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + /* Hack: we have to cast via number to cast from 'const' pointer to non-const. + Blame the opengroup standard for this inconsistency. */ + msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov); + msg.msg_iovlen = iovcnt; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + return lwip_sendmsg(s, &msg, 0); +} + +/** + * Go through the readset and writeset lists and see which socket of the sockets + * set in the sets has events. On return, readset, writeset and exceptset have + * the sockets enabled that had events. + * + * @param maxfdp1 the highest socket index in the sets + * @param readset_in set of sockets to check for read events + * @param writeset_in set of sockets to check for write events + * @param exceptset_in set of sockets to check for error events + * @param readset_out set of sockets that had read events + * @param writeset_out set of sockets that had write events + * @param exceptset_out set os sockets that had error events + * @return number of sockets that had events (read/write/exception) (>= 0) + */ +static int +lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in, + fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out) +{ + int i, nready = 0; + fd_set lreadset, lwriteset, lexceptset; + struct lwip_sock *sock; + SYS_ARCH_DECL_PROTECT(lev); + + FD_ZERO(&lreadset); + FD_ZERO(&lwriteset); + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) { + /* if this FD is not in the set, continue */ + if (!(readset_in && FD_ISSET(i, readset_in)) && + !(writeset_in && FD_ISSET(i, writeset_in)) && + !(exceptset_in && FD_ISSET(i, exceptset_in))) { + continue; + } + /* First get the socket's status (protected)... */ + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + void* lastdata = sock->lastdata; + s16_t rcvevent = sock->rcvevent; + u16_t sendevent = sock->sendevent; + u16_t errevent = sock->errevent; + SYS_ARCH_UNPROTECT(lev); + + /* ... then examine it: */ + /* See if netconn of this socket is ready for read */ + if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) { + FD_SET(i, &lreadset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); + nready++; + } + /* See if netconn of this socket is ready for write */ + if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) { + FD_SET(i, &lwriteset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); + nready++; + } + /* See if netconn of this socket had an error */ + if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) { + FD_SET(i, &lexceptset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i)); + nready++; + } + } else { + SYS_ARCH_UNPROTECT(lev); + /* continue on to next FD in list */ + } + } + /* copy local sets to the ones provided as arguments */ + *readset_out = lreadset; + *writeset_out = lwriteset; + *exceptset_out = lexceptset; + + LWIP_ASSERT("nready >= 0", nready >= 0); + return nready; +} + +int +lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout) +{ + u32_t waitres = 0; + int nready; + fd_set lreadset, lwriteset, lexceptset; + u32_t msectimeout; + struct lwip_select_cb select_cb; + int i; + int maxfdp2; +#if LWIP_NETCONN_SEM_PER_THREAD + int waited = 0; +#endif + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n", + maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, + timeout ? (s32_t)timeout->tv_sec : (s32_t)-1, + timeout ? (s32_t)timeout->tv_usec : (s32_t)-1)); + + /* Go through each socket in each list to count number of sockets which + currently match */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + + /* If we don't have any current events, then suspend if we are supposed to */ + if (!nready) { + if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* None ready: add our semaphore to list: + We don't actually need any dynamic memory. Our entry on the + list is only valid while we are in this function, so it's ok + to use local variables. */ + + select_cb.next = NULL; + select_cb.prev = NULL; + select_cb.readset = readset; + select_cb.writeset = writeset; + select_cb.exceptset = exceptset; + select_cb.sem_signalled = 0; +#if LWIP_NETCONN_SEM_PER_THREAD + select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else /* LWIP_NETCONN_SEM_PER_THREAD */ + if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) { + /* failed to create semaphore */ + set_errno(ENOMEM); + return -1; + } +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + /* Protect the select_cb_list */ + SYS_ARCH_PROTECT(lev); + + /* Put this select_cb on top of list */ + select_cb.next = select_cb_list; + if (select_cb_list != NULL) { + select_cb_list->prev = &select_cb; + } + select_cb_list = &select_cb; + /* Increasing this counter tells event_callback that the list has changed. */ + select_cb_ctr++; + + /* Now we can safely unprotect */ + SYS_ARCH_UNPROTECT(lev); + + /* Increase select_waiting for each socket we are interested in */ + maxfdp2 = maxfdp1; + for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock; + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + sock->select_waiting++; + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + } else { + /* Not a valid socket */ + nready = -1; + maxfdp2 = i; + SYS_ARCH_UNPROTECT(lev); + break; + } + SYS_ARCH_UNPROTECT(lev); + } + } + + if (nready >= 0) { + /* Call lwip_selscan again: there could have been events between + the last scan (without us on the list) and putting us on the list! */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + if (!nready) { + /* Still none ready, just wait to be woken */ + if (timeout == 0) { + /* Wait forever */ + msectimeout = 0; + } else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if (msectimeout == 0) { + /* Wait 1ms at least (0 means wait forever) */ + msectimeout = 1; + } + } + + waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout); +#if LWIP_NETCONN_SEM_PER_THREAD + waited = 1; +#endif + } + } + + /* Decrease select_waiting for each socket we are interested in */ + for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock; + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + /* for now, handle select_waiting==0... */ + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + if (sock->select_waiting > 0) { + sock->select_waiting--; + } + } else { + /* Not a valid socket */ + nready = -1; + } + SYS_ARCH_UNPROTECT(lev); + } + } + /* Take us off the list */ + SYS_ARCH_PROTECT(lev); + if (select_cb.next != NULL) { + select_cb.next->prev = select_cb.prev; + } + if (select_cb_list == &select_cb) { + LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL); + select_cb_list = select_cb.next; + } else { + LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL); + select_cb.prev->next = select_cb.next; + } + /* Increasing this counter tells event_callback that the list has changed. */ + select_cb_ctr++; + SYS_ARCH_UNPROTECT(lev); + +#if LWIP_NETCONN_SEM_PER_THREAD + if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) { + /* don't leave the thread-local semaphore signalled */ + sys_arch_sem_wait(select_cb.sem, 1); + } +#else /* LWIP_NETCONN_SEM_PER_THREAD */ + sys_sem_free(&select_cb.sem); +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + if (nready < 0) { + /* This happens when a socket got closed while waiting */ + set_errno(EBADF); + return -1; + } + + if (waitres == SYS_ARCH_TIMEOUT) { + /* Timeout */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* See what's set */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); +return_copy_fdsets: + set_errno(0); + if (readset) { + *readset = lreadset; + } + if (writeset) { + *writeset = lwriteset; + } + if (exceptset) { + *exceptset = lexceptset; + } + return nready; +} + +/** + * Callback registered in the netconn layer for each socket-netconn. + * Processes recvevent (data available) and wakes up tasks waiting for select. + */ +static void +event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) +{ + int s; + struct lwip_sock *sock; + struct lwip_select_cb *scb; + int last_select_cb_ctr; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_UNUSED_ARG(len); + + /* Get socket */ + if (conn) { + s = conn->socket; + if (s < 0) { + /* Data comes in right away after an accept, even though + * the server task might not have created a new socket yet. + * Just count down (or up) if that's the case and we + * will use the data later. Note that only receive events + * can happen before the new socket is set up. */ + SYS_ARCH_PROTECT(lev); + if (conn->socket < 0) { + if (evt == NETCONN_EVT_RCVPLUS) { + conn->socket--; + } + SYS_ARCH_UNPROTECT(lev); + return; + } + s = conn->socket; + SYS_ARCH_UNPROTECT(lev); + } + + sock = get_socket(s); + if (!sock) { + return; + } + } else { + return; + } + + SYS_ARCH_PROTECT(lev); + /* Set event as required */ + switch (evt) { + case NETCONN_EVT_RCVPLUS: + sock->rcvevent++; + break; + case NETCONN_EVT_RCVMINUS: + sock->rcvevent--; + break; + case NETCONN_EVT_SENDPLUS: + sock->sendevent = 1; + break; + case NETCONN_EVT_SENDMINUS: + sock->sendevent = 0; + break; + case NETCONN_EVT_ERROR: + sock->errevent = 1; + break; + default: + LWIP_ASSERT("unknown event", 0); + break; + } + + if (sock->select_waiting == 0) { + /* noone is waiting for this socket, no need to check select_cb_list */ + SYS_ARCH_UNPROTECT(lev); + return; + } + + /* Now decide if anyone is waiting for this socket */ + /* NOTE: This code goes through the select_cb_list list multiple times + ONLY IF a select was actually waiting. We go through the list the number + of waiting select calls + 1. This list is expected to be small. */ + + /* At this point, SYS_ARCH is still protected! */ +again: + for (scb = select_cb_list; scb != NULL; scb = scb->next) { + /* remember the state of select_cb_list to detect changes */ + last_select_cb_ctr = select_cb_ctr; + if (scb->sem_signalled == 0) { + /* semaphore not signalled yet */ + int do_signal = 0; + /* Test this select call for our socket */ + if (sock->rcvevent > 0) { + if (scb->readset && FD_ISSET(s, scb->readset)) { + do_signal = 1; + } + } + if (sock->sendevent != 0) { + if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) { + do_signal = 1; + } + } + if (sock->errevent != 0) { + if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) { + do_signal = 1; + } + } + if (do_signal) { + scb->sem_signalled = 1; + /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might + lead to the select thread taking itself off the list, invalidating the semaphore. */ + sys_sem_signal(SELECT_SEM_PTR(scb->sem)); + } + } + /* unlock interrupts with each step */ + SYS_ARCH_UNPROTECT(lev); + /* this makes sure interrupt protection time is short */ + SYS_ARCH_PROTECT(lev); + if (last_select_cb_ctr != select_cb_ctr) { + /* someone has changed select_cb_list, restart at the beginning */ + goto again; + } + } + SYS_ARCH_UNPROTECT(lev); +} + +/** + * Close one end of a full-duplex connection. + */ +int +lwip_shutdown(int s, int how) +{ + struct lwip_sock *sock; + err_t err; + u8_t shut_rx = 0, shut_tx = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn != NULL) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return -1; + } + } else { + sock_set_errno(sock, ENOTCONN); + return -1; + } + + if (how == SHUT_RD) { + shut_rx = 1; + } else if (how == SHUT_WR) { + shut_tx = 1; + } else if (how == SHUT_RDWR) { + shut_rx = 1; + shut_tx = 1; + } else { + sock_set_errno(sock, EINVAL); + return -1; + } + err = netconn_shutdown(sock->conn, shut_rx, shut_tx); + + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? 0 : -1); +} + +static int +lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) +{ + struct lwip_sock *sock; + union sockaddr_aligned saddr; + ip_addr_t naddr; + u16_t port; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* get the IP address and port */ + err = netconn_getaddr(sock->conn, &naddr, &port, local); + if (err != ERR_OK) { + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + +#if LWIP_IPV4 && LWIP_IPV6 + /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */ + if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) && + IP_IS_V4_VAL(naddr)) { + ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr)); + IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6); + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + ip_addr_debug_print_val(SOCKETS_DEBUG, naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port)); + + if (*namelen > saddr.sa.sa_len) { + *namelen = saddr.sa.sa_len; + } + MEMCPY(name, &saddr, *namelen); + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 0); +} + +int +lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 1); +} + +int +lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + u8_t err; + struct lwip_sock *sock = get_socket(s); +#if !LWIP_TCPIP_CORE_LOCKING + LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data); +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + + if (!sock) { + return -1; + } + + if ((NULL == optval) || (NULL == optlen)) { + sock_set_errno(sock, EFAULT); + return -1; + } + +#if LWIP_TCPIP_CORE_LOCKING + /* core-locking can just call the -impl function */ + LOCK_TCPIP_CORE(); + err = lwip_getsockopt_impl(s, level, optname, optval, optlen); + UNLOCK_TCPIP_CORE(); + +#else /* LWIP_TCPIP_CORE_LOCKING */ + +#if LWIP_MPU_COMPATIBLE + /* MPU_COMPATIBLE copies the optval data, so check for max size here */ + if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) { + sock_set_errno(sock, ENOBUFS); + return -1; + } +#endif /* LWIP_MPU_COMPATIBLE */ + + LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock); + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen; +#if !LWIP_MPU_COMPATIBLE + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval; +#endif /* !LWIP_MPU_COMPATIBLE */ + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0; +#if LWIP_NETCONN_SEM_PER_THREAD + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed; +#endif + err = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); + if (err != ERR_OK) { + LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0); + + /* write back optlen and optval */ + *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen; +#if LWIP_MPU_COMPATIBLE + MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen); +#endif /* LWIP_MPU_COMPATIBLE */ + + /* maybe lwip_getsockopt_internal has changed err */ + err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err; + LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +#if !LWIP_TCPIP_CORE_LOCKING +/** lwip_getsockopt_callback: only used without CORE_LOCKING + * to get into the tcpip_thread + */ +static void +lwip_getsockopt_callback(void *arg) +{ + struct lwip_setgetsockopt_data *data; + LWIP_ASSERT("arg != NULL", arg != NULL); + data = (struct lwip_setgetsockopt_data*)arg; + + data->err = lwip_getsockopt_impl(data->s, data->level, data->optname, +#if LWIP_MPU_COMPATIBLE + data->optval, +#else /* LWIP_MPU_COMPATIBLE */ + data->optval.p, +#endif /* LWIP_MPU_COMPATIBLE */ + &data->optlen); + + sys_sem_signal((sys_sem_t*)(data->completed_sem)); +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** lwip_getsockopt_impl: the actual implementation of getsockopt: + * same argument as lwip_getsockopt, either called directly or through callback + */ +static u8_t +lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + u8_t err = 0; + struct lwip_sock *sock = tryget_socket(s); + if (!sock) { + return EBADF; + } + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + +#if LWIP_TCP + case SO_ACCEPTCONN: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) { + return ENOPROTOOPT; + } + if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) { + *(int*)optval = 1; + } else { + *(int*)optval = 0; + } + break; +#endif /* LWIP_TCP */ + + /* The option flags */ + case SO_BROADCAST: + case SO_KEEPALIVE: +#if SO_REUSE + case SO_REUSEADDR: +#endif /* SO_REUSE */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; + + case SO_TYPE: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); + switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) { + case NETCONN_RAW: + *(int*)optval = SOCK_RAW; + break; + case NETCONN_TCP: + *(int*)optval = SOCK_STREAM; + break; + case NETCONN_UDP: + *(int*)optval = SOCK_DGRAM; + break; + default: /* unrecognized socket type */ + *(int*)optval = netconn_type(sock->conn); + LWIP_DEBUGF(SOCKETS_DEBUG, + ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", + s, *(int *)optval)); + } /* switch (netconn_type(sock->conn)) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", + s, *(int *)optval)); + break; + + case SO_ERROR: + LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int); + /* only overwrite ERR_OK or temporary errors */ + if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) { + sock_set_errno(sock, err_to_errno(sock->conn->last_err)); + } + *(int *)optval = (sock->err == 0xFF ? (int)-1 : (int)sock->err); + sock->err = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn)); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn)); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); + *(int *)optval = netconn_get_recvbufsize(sock->conn); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_SO_LINGER + case SO_LINGER: + { + s16_t conn_linger; + struct linger* linger = (struct linger*)optval; + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger); + conn_linger = sock->conn->linger; + if (conn_linger >= 0) { + linger->l_onoff = 1; + linger->l_linger = (int)conn_linger; + } else { + linger->l_onoff = 0; + linger->l_linger = 0; + } + } + break; +#endif /* LWIP_SO_LINGER */ +#if LWIP_UDP + case SO_NO_CHECK: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP); +#if LWIP_UDPLITE + if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) { + /* this flag is only available for UDP, not for UDP lite */ + return EAFNOSUPPORT; + } +#endif /* LWIP_UDPLITE */ + *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; + break; +#endif /* LWIP_UDP*/ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + *(int*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_TOS: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + *(int*)optval = sock->conn->pcb.ip->tos; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", + s, *(int *)optval)); + break; +#if LWIP_MULTICAST_TX_OPTIONS + case IP_MULTICAST_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + return ENOPROTOOPT; + } + *(u8_t*)optval = udp_get_multicast_ttl(sock->conn->pcb.udp); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_MULTICAST_IF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + return ENOPROTOOPT; + } + inet_addr_from_ip4addr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp)); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n", + s, *(u32_t *)optval)); + break; + case IP_MULTICAST_LOOP: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t); + if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) { + *(u8_t*)optval = 1; + } else { + *(u8_t*)optval = 0; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + /* Special case: all IPPROTO_TCP option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP); + if (sock->conn->pcb.tcp->state == LISTEN) { + return EINVAL; + } + switch (optname) { + case TCP_NODELAY: + *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", + s, (*(int*)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPINTVL: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPCNT: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); + *(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n", + s, *(int *)optval)); + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + /* Special case: all IPPROTO_UDPLITE option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { + return ENOPROTOOPT; + } + switch (optname) { + case UDPLITE_SEND_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_rx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + /* Level: IPPROTO_RAW */ + case IPPROTO_RAW: + switch (optname) { +#if LWIP_IPV6 && LWIP_RAW + case IPV6_CHECKSUM: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW); + if (sock->conn->pcb.raw->chksum_reqd == 0) { + *(int *)optval = -1; + } else { + *(int *)optval = sock->conn->pcb.raw->chksum_offset; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n", + s, (*(int*)optval)) ); + break; +#endif /* LWIP_IPV6 && LWIP_RAW */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + break; + } /* switch (level) */ + + return err; +} + +int +lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + u8_t err = 0; + struct lwip_sock *sock = get_socket(s); +#if !LWIP_TCPIP_CORE_LOCKING + LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data); +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + + if (!sock) { + return -1; + } + + if (NULL == optval) { + sock_set_errno(sock, EFAULT); + return -1; + } + +#if LWIP_TCPIP_CORE_LOCKING + /* core-locking can just call the -impl function */ + LOCK_TCPIP_CORE(); + err = lwip_setsockopt_impl(s, level, optname, optval, optlen); + UNLOCK_TCPIP_CORE(); + +#else /* LWIP_TCPIP_CORE_LOCKING */ + +#if LWIP_MPU_COMPATIBLE + /* MPU_COMPATIBLE copies the optval data, so check for max size here */ + if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) { + sock_set_errno(sock, ENOBUFS); + return -1; + } +#endif /* LWIP_MPU_COMPATIBLE */ + + LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock); + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen; +#if LWIP_MPU_COMPATIBLE + MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen); +#else /* LWIP_MPU_COMPATIBLE */ + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void*)optval; +#endif /* LWIP_MPU_COMPATIBLE */ + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0; +#if LWIP_NETCONN_SEM_PER_THREAD + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed; +#endif + err = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); + if (err != ERR_OK) { + LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0); + + /* maybe lwip_getsockopt_internal has changed err */ + err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err; + LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +#if !LWIP_TCPIP_CORE_LOCKING +/** lwip_setsockopt_callback: only used without CORE_LOCKING + * to get into the tcpip_thread + */ +static void +lwip_setsockopt_callback(void *arg) +{ + struct lwip_setgetsockopt_data *data; + LWIP_ASSERT("arg != NULL", arg != NULL); + data = (struct lwip_setgetsockopt_data*)arg; + + data->err = lwip_setsockopt_impl(data->s, data->level, data->optname, +#if LWIP_MPU_COMPATIBLE + data->optval, +#else /* LWIP_MPU_COMPATIBLE */ + data->optval.pc, +#endif /* LWIP_MPU_COMPATIBLE */ + data->optlen); + + sys_sem_signal((sys_sem_t*)(data->completed_sem)); +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** lwip_setsockopt_impl: the actual implementation of setsockopt: + * same argument as lwip_setsockopt, either called directly or through callback + */ +static u8_t +lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + u8_t err = 0; + struct lwip_sock *sock = tryget_socket(s); + if (!sock) { + return EBADF; + } + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* SO_ACCEPTCONN is get-only */ + + /* The option flags */ + case SO_BROADCAST: + case SO_KEEPALIVE: +#if SO_REUSE + case SO_REUSEADDR: +#endif /* SO_REUSE */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + if (*(const int*)optval) { + ip_set_option(sock->conn->pcb.ip, optname); + } else { + ip_reset_option(sock->conn->pcb.ip, optname); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", + s, optname, (*(const int*)optval?"on":"off"))); + break; + + /* SO_TYPE is get-only */ + /* SO_ERROR is get-only */ + +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval)); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval)); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int); + netconn_set_recvbufsize(sock->conn, *(const int*)optval); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_SO_LINGER + case SO_LINGER: + { + const struct linger* linger = (const struct linger*)optval; + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger); + if (linger->l_onoff) { + int lingersec = linger->l_linger; + if (lingersec < 0) { + return EINVAL; + } + if (lingersec > 0xFFFF) { + lingersec = 0xFFFF; + } + sock->conn->linger = (s16_t)lingersec; + } else { + sock->conn->linger = -1; + } + } + break; +#endif /* LWIP_SO_LINGER */ +#if LWIP_UDP + case SO_NO_CHECK: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP); +#if LWIP_UDPLITE + if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) { + /* this flag is only available for UDP, not for UDP lite */ + return EAFNOSUPPORT; + } +#endif /* LWIP_UDPLITE */ + if (*(const int*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM); + } + break; +#endif /* LWIP_UDP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + sock->conn->pcb.ip->ttl = (u8_t)(*(const int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n", + s, sock->conn->pcb.ip->ttl)); + break; + case IP_TOS: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + sock->conn->pcb.ip->tos = (u8_t)(*(const int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n", + s, sock->conn->pcb.ip->tos)); + break; +#if LWIP_MULTICAST_TX_OPTIONS + case IP_MULTICAST_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP); + udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t*)optval)); + break; + case IP_MULTICAST_IF: + { + ip4_addr_t if_addr; + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP); + inet_addr_to_ip4addr(&if_addr, (const struct in_addr*)optval); + udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr); + } + break; + case IP_MULTICAST_LOOP: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP); + if (*(const u8_t*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP); + } + break; +#endif /* LWIP_MULTICAST_TX_OPTIONS */ +#if LWIP_IGMP + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* If this is a TCP or a RAW socket, ignore these options. */ + /* @todo: assign membership to this socket so that it is dropped when closing the socket */ + err_t igmp_err; + const struct ip_mreq *imr = (const struct ip_mreq *)optval; + ip4_addr_t if_addr; + ip4_addr_t multi_addr; + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP); + inet_addr_to_ip4addr(&if_addr, &imr->imr_interface); + inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr); + if (optname == IP_ADD_MEMBERSHIP) { + if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) { + /* cannot track membership (out of memory) */ + err = ENOMEM; + igmp_err = ERR_OK; + } else { + igmp_err = igmp_joingroup(&if_addr, &multi_addr); + } + } else { + igmp_err = igmp_leavegroup(&if_addr, &multi_addr); + lwip_socket_unregister_membership(s, &if_addr, &multi_addr); + } + if (igmp_err != ERR_OK) { + err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + /* Special case: all IPPROTO_TCP option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); + if (sock->conn->pcb.tcp->state == LISTEN) { + return EINVAL; + } + switch (optname) { + case TCP_NODELAY: + if (*(const int*)optval) { + tcp_nagle_disable(sock->conn->pcb.tcp); + } else { + tcp_nagle_enable(sock->conn->pcb.tcp); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", + s, (*(const int *)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(const int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + case TCP_KEEPINTVL: + sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(const int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_intvl)); + break; + case TCP_KEEPCNT: + sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_cnt)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP*/ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); + if (*(const int*)optval) { + netconn_set_ipv6only(sock->conn, 1); + } else { + netconn_set_ipv6only(sock->conn, 0); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n", + s, (netconn_get_ipv6only(sock->conn) ? 1 : 0))); + break; +#if LWIP_IPV6_MLD + case IPV6_JOIN_GROUP: + case IPV6_LEAVE_GROUP: + { + ip_addr_t multiaddr; + ip_addr_t if_addr; + struct ipv6_mreq *imr6 = (struct ipv6_mreq *)optval; + memcpy((char *)&multiaddr.u_addr.ip6, (char *)&imr6->ipv6mr_multiaddr.s6_addr, 16); + multiaddr.type = IPADDR_TYPE_V6; + memcpy((char *)&if_addr.u_addr.ip6, (char *)&in6addr_any.s6_addr, 16); + if_addr.type = IPADDR_TYPE_V6; + if(optname == IPV6_JOIN_GROUP) + mld6_joingroup(ip_2_ip6(&if_addr), ip_2_ip6(&multiaddr)); + else + mld6_leavegroup(ip_2_ip6(&if_addr), ip_2_ip6(&multiaddr)); + } + break; +#endif //LWIP_IPV6_MLD + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + /* Special case: all IPPROTO_UDPLITE option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { + return ENOPROTOOPT; + } + switch (optname) { + case UDPLITE_SEND_CSCOV: + if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_tx = 8; + } else { + sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(const int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", + s, (*(const int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_rx = 8; + } else { + sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(const int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", + s, (*(const int*)optval)) ); + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + /* Level: IPPROTO_RAW */ + case IPPROTO_RAW: + switch (optname) { +#if LWIP_IPV6 && LWIP_RAW + case IPV6_CHECKSUM: + /* It should not be possible to disable the checksum generation with ICMPv6 + * as per RFC 3542 chapter 3.1 */ + if(sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) { + return EINVAL; + } + + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW); + if (*(const int *)optval < 0) { + sock->conn->pcb.raw->chksum_reqd = 0; + } else if (*(const int *)optval & 1) { + /* Per RFC3542, odd offsets are not allowed */ + return EINVAL; + } else { + sock->conn->pcb.raw->chksum_reqd = 1; + sock->conn->pcb.raw->chksum_offset = (u16_t)*(const int *)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n", + s, sock->conn->pcb.raw->chksum_reqd)); + break; +#endif /* LWIP_IPV6 && LWIP_RAW */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + break; + } /* switch (level) */ + + return err; +} + +int +lwip_ioctl(int s, long cmd, void *argp) +{ + struct lwip_sock *sock = get_socket(s); + u8_t val; +#if LWIP_SO_RCVBUF + u16_t buflen = 0; + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + if (!sock) { + return -1; + } + + switch (cmd) { +#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE + case FIONREAD: + if (!argp) { + sock_set_errno(sock, EINVAL); + return -1; + } +#if LWIP_FIONREAD_LINUXMODE + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + struct pbuf *p; + if (sock->lastdata) { + p = ((struct netbuf *)sock->lastdata)->p; + *((int*)argp) = p->tot_len - sock->lastoffset; + } else { + struct netbuf *rxbuf; + err_t err; + if (sock->rcvevent <= 0) { + *((int*)argp) = 0; + } else { + err = netconn_recv(sock->conn, &rxbuf); + if (err != ERR_OK) { + *((int*)argp) = 0; + } else { + sock->lastdata = rxbuf; + sock->lastoffset = 0; + *((int*)argp) = rxbuf->p->tot_len; + } + } + } + return 0; + } +#endif /* LWIP_FIONREAD_LINUXMODE */ + +#if LWIP_SO_RCVBUF + /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */ + SYS_ARCH_GET(sock->conn->recv_avail, recv_avail); + if (recv_avail < 0) { + recv_avail = 0; + } + *((int*)argp) = recv_avail; + + /* Check if there is data left from the last recv operation. /maq 041215 */ + if (sock->lastdata) { + struct pbuf *p = (struct pbuf *)sock->lastdata; + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + p = ((struct netbuf *)p)->p; + } + buflen = p->tot_len; + buflen -= sock->lastoffset; + + *((int*)argp) += buflen; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp))); + sock_set_errno(sock, 0); + return 0; +#else /* LWIP_SO_RCVBUF */ + break; +#endif /* LWIP_SO_RCVBUF */ +#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */ + + case (long)FIONBIO: + val = 0; + if (argp && *(u32_t*)argp) { + val = 1; + } + netconn_set_nonblocking(sock->conn, val); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val)); + sock_set_errno(sock, 0); + return 0; + + default: + break; + } /* switch (cmd) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + return -1; +} + +/** A minimal implementation of fcntl. + * Currently only the commands F_GETFL and F_SETFL are implemented. + * Only the flag O_NONBLOCK is implemented. + */ +int +lwip_fcntl(int s, int cmd, int val) +{ + struct lwip_sock *sock = get_socket(s); + int ret = -1; + + if (!sock) { + return -1; + } + + switch (cmd) { + case F_GETFL: + ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0; + sock_set_errno(sock, 0); + break; + case F_SETFL: + if ((val & ~O_NONBLOCK) == 0) { + /* only O_NONBLOCK, all other bits are zero */ + netconn_set_nonblocking(sock->conn, val & O_NONBLOCK); + ret = 0; + sock_set_errno(sock, 0); + } else { + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + break; + } + return ret; +} + +#if LWIP_IGMP +/** Register a new IGMP membership. On socket close, the membership is dropped automatically. + * + * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK). + * + * @return 1 on success, 0 on failure + */ +static int +lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr) +{ + struct lwip_sock *sock = get_socket(s); + int i; + + if (!sock) { + return 0; + } + + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { + if (socket_ipv4_multicast_memberships[i].sock == NULL) { + socket_ipv4_multicast_memberships[i].sock = sock; + ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr); + ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr); + return 1; + } + } + return 0; +} + +/** Unregister a previously registered membership. This prevents dropping the membership + * on socket close. + * + * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK). + */ +static void +lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr) +{ + struct lwip_sock *sock = get_socket(s); + int i; + + if (!sock) { + return; + } + + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { + if ((socket_ipv4_multicast_memberships[i].sock == sock) && + ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) && + ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) { + socket_ipv4_multicast_memberships[i].sock = NULL; + ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr); + ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr); + return; + } + } +} + +/** Drop all memberships of a socket that were not dropped explicitly via setsockopt. + * + * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK). + */ +static void +lwip_socket_drop_registered_memberships(int s) +{ + struct lwip_sock *sock = get_socket(s); + int i; + + if (!sock) { + return; + } + + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { + if (socket_ipv4_multicast_memberships[i].sock == sock) { + ip_addr_t multi_addr, if_addr; + ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr); + ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr); + socket_ipv4_multicast_memberships[i].sock = NULL; + ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr); + ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr); + + netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE); + } + } +} +#endif /* LWIP_IGMP */ +#endif /* LWIP_SOCKET */ diff --git a/sdk/Src/Network/lwip2.0.3/api/tcpip.c b/sdk/Src/Network/lwip2.0.3/api/tcpip.c new file mode 100644 index 0000000..059a480 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/api/tcpip.c @@ -0,0 +1,552 @@ +/** + * @file + * Sequential API Main thread module + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/priv/tcpip_priv.h" +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/mem.h" +#include "lwip/init.h" +#include "lwip/ip.h" +#include "lwip/pbuf.h" +#include "lwip/etharp.h" +#include "netif/ethernet.h" + +#define TCPIP_MSG_VAR_REF(name) API_VAR_REF(name) +#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name) +#define TCPIP_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM) +#define TCPIP_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_TCPIP_MSG_API, name) + +/* global variables */ +static tcpip_init_done_fn tcpip_init_done; +static void *tcpip_init_done_arg; +static sys_mbox_t mbox; + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +sys_mutex_t lock_tcpip_core; +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +#if LWIP_TIMERS +/* wait for a message, timeouts are processed while waiting */ +#define TCPIP_MBOX_FETCH(mbox, msg) sys_timeouts_mbox_fetch(mbox, msg) +#else /* LWIP_TIMERS */ +/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */ +#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg) +#endif /* LWIP_TIMERS */ + +/** + * The main lwIP thread. This thread has exclusive access to lwIP core functions + * (unless access to them is not locked). Other threads communicate with this + * thread using message boxes. + * + * It also starts all the timers to make sure they are running in the right + * thread context. + * + * @param arg unused argument + */ +static void +tcpip_thread(void *arg) +{ + struct tcpip_msg *msg; + LWIP_UNUSED_ARG(arg); + + if (tcpip_init_done != NULL) { + tcpip_init_done(tcpip_init_done_arg); + } + + LOCK_TCPIP_CORE(); + while (1) { /* MAIN Loop */ + UNLOCK_TCPIP_CORE(); + LWIP_TCPIP_THREAD_ALIVE(); + /* wait for a message, timeouts are processed while waiting */ + TCPIP_MBOX_FETCH(&mbox, (void **)&msg); + LOCK_TCPIP_CORE(); + if (msg == NULL) { + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n")); + LWIP_ASSERT("tcpip_thread: invalid message", 0); + continue; + } + switch (msg->type) { +#if !LWIP_TCPIP_CORE_LOCKING + case TCPIP_MSG_API: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); + msg->msg.api_msg.function(msg->msg.api_msg.msg); + break; + case TCPIP_MSG_API_CALL: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg)); + msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg); + sys_sem_signal(msg->msg.api_call.sem); + break; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + +#if !LWIP_TCPIP_CORE_LOCKING_INPUT + case TCPIP_MSG_INPKT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); + msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif); + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + break; +#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */ + +#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS + case TCPIP_MSG_TIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + case TCPIP_MSG_UNTIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg)); + sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; +#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */ + + case TCPIP_MSG_CALLBACK: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + + case TCPIP_MSG_CALLBACK_STATIC: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + break; + +#if TLS_CONFIG_AP_OPT_FWD + case TCPIP_MSG_OUTPKT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: alg out_put PACKET %p\n", (void *)msg)); + etharp_output(msg->msg.outp.netif, msg->msg.outp.p, (ip4_addr_t *)&(msg->msg.outp.ipaddr)); + pbuf_free(msg->msg.outp.p); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; +#endif + + default: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); + LWIP_ASSERT("tcpip_thread: invalid message", 0); + break; + } + } +} + +#if TLS_CONFIG_AP_OPT_FWD +err_t tcpip_output(struct pbuf *p, struct netif *inp, ip_addr_t ipaddr) +{ + struct tcpip_msg *msg; + + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_OUTPKT; + msg->msg.outp.p = p; + msg->msg.outp.netif = inp; + ip_addr_copy(msg->msg.outp.ipaddr, ipaddr); + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_API, msg); + return ERR_MEM; + } + return ERR_OK; +} +#endif + +/** + * Pass a received packet to tcpip_thread for input processing + * + * @param p the received packet + * @param inp the network interface on which the packet was received + * @param input_fn input function to call + */ +err_t +tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn) +{ +#if LWIP_TCPIP_CORE_LOCKING_INPUT + err_t ret; + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp)); + LOCK_TCPIP_CORE(); + ret = input_fn(p, inp); + UNLOCK_TCPIP_CORE(); + return ret; +#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + struct tcpip_msg *msg; + + LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox)); + + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_INPKT; + msg->msg.inp.p = p; + msg->msg.inp.netif = inp; + msg->msg.inp.input_fn = input_fn; + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + return ERR_MEM; + } + return ERR_OK; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ +} + +/** + * @ingroup lwip_os + * Pass a received packet to tcpip_thread for input processing with + * ethernet_input or ip_input. Don't call directly, pass to netif_add() + * and call netif->input(). + * + * @param p the received packet, p->payload pointing to the Ethernet header or + * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or + * NETIF_FLAG_ETHERNET flags) + * @param inp the network interface on which the packet was received + */ +err_t +tcpip_input(struct pbuf *p, struct netif *inp) +{ +#if LWIP_ETHERNET + if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { + return tcpip_inpkt(p, inp, ethernet_input); + } else +#endif /* LWIP_ETHERNET */ + return tcpip_inpkt(p, inp, ip_input); +} + +/** + * Call a specific function in the thread context of + * tcpip_thread for easy access synchronization. + * A function called in that way may access lwIP core code + * without fearing concurrent access. + * + * @param function the function to call + * @param ctx parameter passed to f + * @param block 1 to block until the request is posted, 0 to non-blocking mode + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block) +{ + struct tcpip_msg *msg; + + LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox)); + + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_CALLBACK; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + if (block) { + sys_mbox_post(&mbox, msg); + } else { + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_API, msg); + return ERR_MEM; + } + } + return ERR_OK; +} + +#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS +/** + * call sys_timeout in tcpip_thread + * + * @param msecs time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox)); + + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; +} + +/** + * call sys_untimeout in tcpip_thread + * + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_untimeout(sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox)); + + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_UNTIMEOUT; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; +} +#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */ + + +/** + * Sends a message to TCPIP thread to call a function. Caller thread blocks on + * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread, + * this has to be done by the user. + * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way + * with least runtime overhead. + * + * @param fn function to be called from TCPIP thread + * @param apimsg argument to API function + * @param sem semaphore to wait on + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem) +{ +#if LWIP_TCPIP_CORE_LOCKING + LWIP_UNUSED_ARG(sem); + LOCK_TCPIP_CORE(); + fn(apimsg); + UNLOCK_TCPIP_CORE(); + return ERR_OK; +#else /* LWIP_TCPIP_CORE_LOCKING */ + TCPIP_MSG_VAR_DECLARE(msg); + + LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem)); + LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox)); + + TCPIP_MSG_VAR_ALLOC(msg); + TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API; + TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn; + TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg; + sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg)); + sys_arch_sem_wait(sem, 0); + TCPIP_MSG_VAR_FREE(msg); + return ERR_OK; +#endif /* LWIP_TCPIP_CORE_LOCKING */ +} + +/** + * Synchronously calls function in TCPIP thread and waits for its completion. + * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or + * LWIP_NETCONN_SEM_PER_THREAD. + * If not, a semaphore is created and destroyed on every call which is usually + * an expensive/slow operation. + * @param fn Function to call + * @param call Call parameters + * @return Return value from tcpip_api_call_fn + */ +err_t +tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call) +{ +#if LWIP_TCPIP_CORE_LOCKING + err_t err; + LOCK_TCPIP_CORE(); + err = fn(call); + UNLOCK_TCPIP_CORE(); + return err; +#else /* LWIP_TCPIP_CORE_LOCKING */ + TCPIP_MSG_VAR_DECLARE(msg); + +#if !LWIP_NETCONN_SEM_PER_THREAD + err_t err = sys_sem_new(&call->sem, 0); + if (err != ERR_OK) { + return err; + } +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox)); + + TCPIP_MSG_VAR_ALLOC(msg); + TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL; + TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call; + TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn; +#if LWIP_NETCONN_SEM_PER_THREAD + TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else /* LWIP_NETCONN_SEM_PER_THREAD */ + TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem; +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg)); + sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0); + TCPIP_MSG_VAR_FREE(msg); + +#if !LWIP_NETCONN_SEM_PER_THREAD + sys_sem_free(&call->sem); +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + return call->err; +#endif /* LWIP_TCPIP_CORE_LOCKING */ +} + +/** + * Allocate a structure for a static callback message and initialize it. + * This is intended to be used to send "static" messages from interrupt context. + * + * @param function the function to call + * @param ctx parameter passed to function + * @return a struct pointer to pass to tcpip_trycallback(). + */ +struct tcpip_callback_msg* +tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx) +{ + struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return NULL; + } + msg->type = TCPIP_MSG_CALLBACK_STATIC; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + return (struct tcpip_callback_msg*)msg; +} + +/** + * Free a callback message allocated by tcpip_callbackmsg_new(). + * + * @param msg the message to free + */ +void +tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg) +{ + memp_free(MEMP_TCPIP_MSG_API, msg); +} + +/** + * Try to post a callback-message to the tcpip_thread mbox + * This is intended to be used to send "static" messages from interrupt context. + * + * @param msg pointer to the message to post + * @return sys_mbox_trypost() return code + */ +err_t +tcpip_trycallback(struct tcpip_callback_msg* msg) +{ + LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox)); + return sys_mbox_trypost(&mbox, msg); +} + +/** + * @ingroup lwip_os + * Initialize this module: + * - initialize all sub modules + * - start the tcpip_thread + * + * @param initfunc a function to call when tcpip_thread is running and finished initializing + * @param arg argument to pass to initfunc + */ +void +tcpip_init(tcpip_init_done_fn initfunc, void *arg) +{ + lwip_init(); + + tcpip_init_done = initfunc; + tcpip_init_done_arg = arg; + if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) { + LWIP_ASSERT("failed to create tcpip_thread mbox", 0); + } +#if LWIP_TCPIP_CORE_LOCKING + if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) { + LWIP_ASSERT("failed to create lock_tcpip_core", 0); + } +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); +} + +/** + * Simple callback function used with tcpip_callback to free a pbuf + * (pbuf_free has a wrong signature for tcpip_callback) + * + * @param p The pbuf (chain) to be dereferenced. + */ +static void +pbuf_free_int(void *p) +{ + struct pbuf *q = (struct pbuf *)p; + pbuf_free(q); +} + +/** + * A simple wrapper function that allows you to free a pbuf from interrupt context. + * + * @param p The pbuf (chain) to be dereferenced. + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +pbuf_free_callback(struct pbuf *p) +{ + return tcpip_callback_with_block(pbuf_free_int, p, 0); +} + +/** + * A simple wrapper function that allows you to free heap memory from + * interrupt context. + * + * @param m the heap memory to free + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +mem_free_callback(void *m) +{ + return tcpip_callback_with_block(mem_free, m, 0); +} + +#endif /* !NO_SYS */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/fs.c b/sdk/Src/Network/lwip2.0.3/apps/httpd/fs.c new file mode 100644 index 0000000..35b5e31 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/fs.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2001-2003 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/apps/httpd_opts.h" +#include "lwip/def.h" +#include "lwip/apps/fs.h" +#include "fsdata.h" +#include + + +#if HTTPD_USE_CUSTOM_FSDATA +#include "fsdata_custom.c" +#else /* HTTPD_USE_CUSTOM_FSDATA */ +#include "fsdata.c" +#endif /* HTTPD_USE_CUSTOM_FSDATA */ + +/*-----------------------------------------------------------------------------------*/ + +#if LWIP_HTTPD_CUSTOM_FILES +int fs_open_custom(struct fs_file *file, const char *name); +void fs_close_custom(struct fs_file *file); +#if LWIP_HTTPD_FS_ASYNC_READ +u8_t fs_canread_custom(struct fs_file *file); +u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg); +int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int fs_read_custom(struct fs_file *file, char *buffer, int count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + +/*-----------------------------------------------------------------------------------*/ +err_t +fs_open(struct fs_file *file, const char *name) +{ + const struct fsdata_file *f; + + if ((file == NULL) || (name == NULL)) { + return ERR_ARG; + } + +#if LWIP_HTTPD_CUSTOM_FILES + if (fs_open_custom(file, name)) { + file->is_custom_file = 1; + return ERR_OK; + } + file->is_custom_file = 0; +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + + for (f = FS_ROOT; f != NULL; f = f->next) { + if (!strcmp(name, (const char *)f->name)) { + file->data = (const char *)f->data; + file->len = f->len; + file->index = f->len; + file->pextension = NULL; + file->flags = f->flags; +#if HTTPD_PRECALCULATED_CHECKSUM + file->chksum_count = f->chksum_count; + file->chksum = f->chksum; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ +#if LWIP_HTTPD_FILE_STATE + file->state = fs_state_init(file, name); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + return ERR_OK; + } + } + /* file not found */ + return ERR_VAL; +} + +/*-----------------------------------------------------------------------------------*/ +void +fs_close(struct fs_file *file) +{ +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file) { + fs_close_custom(file); + } +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#if LWIP_HTTPD_FILE_STATE + fs_state_free(file, file->state); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + LWIP_UNUSED_ARG(file); +} +/*-----------------------------------------------------------------------------------*/ +#if LWIP_HTTPD_DYNAMIC_FILE_READ +#if LWIP_HTTPD_FS_ASYNC_READ +int +fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg) +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int +fs_read(struct fs_file *file, char *buffer, int count) +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +{ + int read; + if(file->index == file->len) { + return FS_READ_EOF; + } +#if LWIP_HTTPD_FS_ASYNC_READ + LWIP_UNUSED_ARG(callback_fn); + LWIP_UNUSED_ARG(callback_arg); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file) { +#if LWIP_HTTPD_FS_ASYNC_READ + return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ + return fs_read_custom(file, buffer, count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + } +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + + read = file->len - file->index; + if(read > count) { + read = count; + } + + MEMCPY(buffer, (file->data + file->index), read); + file->index += read; + + return(read); +} +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +/*-----------------------------------------------------------------------------------*/ +#if LWIP_HTTPD_FS_ASYNC_READ +int +fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg) +{ + if (file != NULL) { +#if LWIP_HTTPD_FS_ASYNC_READ +#if LWIP_HTTPD_CUSTOM_FILES + if (!fs_canread_custom(file)) { + if (fs_wait_read_custom(file, callback_fn, callback_arg)) { + return 0; + } + } +#else /* LWIP_HTTPD_CUSTOM_FILES */ + LWIP_UNUSED_ARG(callback_fn); + LWIP_UNUSED_ARG(callback_arg); +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + } + return 1; +} +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +/*-----------------------------------------------------------------------------------*/ +int +fs_bytes_left(struct fs_file *file) +{ + return file->len - file->index; +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/404.html b/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/404.html new file mode 100644 index 0000000..8a1303b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/404.html @@ -0,0 +1,21 @@ + +lwIP - A Lightweight TCP/IP Stack + + + + +
+ SICS logo + +

lwIP - A Lightweight TCP/IP Stack

+

404 - Page not found

+

+ Sorry, the page you are requesting was not found on this + server. +

+
+   +
+ + diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/img/sics.gif b/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/img/sics.gif new file mode 100644 index 0000000000000000000000000000000000000000..0a4fc7bb07050eec9226ca93bc9ad237f35502c8 GIT binary patch literal 724 zcmZ?wbhEHbbYoCrSjxa~Q`<5tD{KG${gWq8=I7_%uwlcpWy=~G8p_Jb4zT=SA;^3z!7uBu>UZ()&> z+r1;Oh0mFly?klC)5cAV$sD}98F{LBUHERS_hGGKac5`W-mvIltqUt73*&`X?UVf< z%Uqmm^}K~z9MaFS-jwgVGIUF9Z77P_s7Vc}y?@_XT+GT)AcZF$6%$qQsA+Q0bF z!Mss%ktc&>+n*qp2aIzy6-8A8zNf3ovHkU%wPnH@(O;*7g5`QX{a~M@%VFg!JaWkVUk6yFE1wL^V{({yi?^s!by^W;pXIz%mY}~L;uBR^JK|)kW|Avap zVzxyL+2N;b1q1f77^T<4c)gUoN8c;6;F^+^0^VN o#HPS7gPr3^SVM!%(d|c@c+6frXclnOc-SHmma$O4k%7S)08~d;ZvX%Q literal 0 HcmV?d00001 diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/index.html b/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/index.html new file mode 100644 index 0000000..0922eb8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/index.html @@ -0,0 +1,47 @@ + +lwIP - A Lightweight TCP/IP Stack + + + + +
+ SICS logo + +

lwIP - A Lightweight TCP/IP Stack

+

+ The web page you are watching was served by a simple web + server running on top of the lightweight TCP/IP stack lwIP. +

+

+ lwIP is an open source implementation of the TCP/IP + protocol suite that was originally written by Adam Dunkels + of the Swedish Institute of Computer Science but now is + being actively developed by a team of developers + distributed world-wide. Since it's release, lwIP has + spurred a lot of interest and has been ported to several + platforms and operating systems. lwIP can be used either + with or without an underlying OS. +

+

+ The focus of the lwIP TCP/IP implementation is to reduce + the RAM usage while still having a full scale TCP. This + makes lwIP suitable for use in embedded systems with tens + of kilobytes of free RAM and room for around 40 kilobytes + of code ROM. +

+

+ More information about lwIP can be found at the lwIP + homepage at http://savannah.nongnu.org/projects/lwip/ + or at the lwIP wiki at http://lwip.wikia.com/. +

+
+   +
+ + + diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.c b/sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.c new file mode 100644 index 0000000..4538320 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.c @@ -0,0 +1,298 @@ +#include "lwip/apps/fs.h" +#include "lwip/def.h" +#include "fsdata.h" + + +#define file_NULL (struct fsdata_file *) NULL + + +static const unsigned int dummy_align__img_sics_gif = 0; +static const unsigned char data__img_sics_gif[] = { +/* /img/sics.gif (14 chars) */ +0x2f,0x69,0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x00,0x00,0x00, + +/* HTTP header */ +/* "HTTP/1.0 200 OK +" (17 bytes) */ +0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d, +0x0a, +/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip) +" (63 bytes) */ +0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33, +0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e, +0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70, +0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a, +/* "Content-type: image/gif + +" (27 bytes) */ +0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x69,0x6d, +0x61,0x67,0x65,0x2f,0x67,0x69,0x66,0x0d,0x0a,0x0d,0x0a, +/* raw file data (724 bytes) */ +0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x22,0x00,0xa5,0x00,0x00,0xd9,0x2b,0x39, +0x6a,0x6a,0x6a,0xbf,0xbf,0xbf,0x93,0x93,0x93,0x0f,0x0f,0x0f,0xb0,0xb0,0xb0,0xa6, +0xa6,0xa6,0x80,0x80,0x80,0x76,0x76,0x76,0x1e,0x1e,0x1e,0x9d,0x9d,0x9d,0x2e,0x2e, +0x2e,0x49,0x49,0x49,0x54,0x54,0x54,0x8a,0x8a,0x8a,0x60,0x60,0x60,0xc6,0xa6,0x99, +0xbd,0xb5,0xb2,0xc2,0xab,0xa1,0xd9,0x41,0x40,0xd5,0x67,0x55,0xc0,0xb0,0xaa,0xd5, +0x5e,0x4e,0xd6,0x50,0x45,0xcc,0x93,0x7d,0xc8,0xa1,0x90,0xce,0x8b,0x76,0xd2,0x7b, +0x65,0xd1,0x84,0x6d,0xc9,0x99,0x86,0x3a,0x3a,0x3a,0x00,0x00,0x00,0xb8,0xb8,0xb8, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2c,0x00,0x00, +0x00,0x00,0x46,0x00,0x22,0x00,0x00,0x06,0xfe,0x40,0x90,0x70,0x48,0x2c,0x1a,0x8f, +0xc8,0xa4,0x72,0xc9,0x6c,0x3a,0x9f,0xd0,0xa8,0x74,0x4a,0xad,0x5a,0xaf,0xd8,0xac, +0x76,0xa9,0x40,0x04,0xbe,0x83,0xe2,0x60,0x3c,0x50,0x20,0x0d,0x8e,0x6f,0x00,0x31, +0x28,0x1c,0x0d,0x07,0xb5,0xc3,0x60,0x75,0x24,0x3e,0xf8,0xfc,0x87,0x11,0x06,0xe9, +0x3d,0x46,0x07,0x0b,0x7a,0x7a,0x7c,0x43,0x06,0x1e,0x84,0x78,0x0b,0x07,0x6e,0x51, +0x01,0x8a,0x84,0x08,0x7e,0x79,0x80,0x87,0x89,0x91,0x7a,0x93,0x0a,0x04,0x99,0x78, +0x96,0x4f,0x03,0x9e,0x79,0x01,0x94,0x9f,0x43,0x9c,0xa3,0xa4,0x05,0x77,0xa3,0xa0, +0x4e,0x98,0x79,0x0b,0x1e,0x83,0xa4,0xa6,0x1f,0x96,0x05,0x9d,0xaa,0x78,0x01,0x07, +0x84,0x04,0x1e,0x1e,0xbb,0xb8,0x51,0x84,0x0e,0x43,0x05,0x07,0x77,0xa5,0x7f,0x42, +0xb1,0xb2,0x01,0x63,0x08,0x0d,0xbb,0x01,0x0c,0x7a,0x0d,0x44,0x0e,0xd8,0xaf,0x4c, +0x05,0x7a,0x04,0x47,0x07,0x07,0xb7,0x80,0xa2,0xe1,0x7d,0x44,0x05,0x01,0x04,0x01, +0xd0,0xea,0x87,0x93,0x4f,0xe0,0x9a,0x49,0xce,0xd8,0x79,0x04,0x66,0x20,0x15,0x10, +0x10,0x11,0x92,0x29,0x80,0xb6,0xc0,0x91,0x15,0x45,0x1e,0x90,0x19,0x71,0x46,0xa8, +0x5c,0x04,0x0e,0x00,0x22,0x4e,0xe8,0x40,0x24,0x9f,0x3e,0x04,0x06,0xa7,0x58,0xd4, +0x93,0xa0,0x1c,0x91,0x3f,0xe8,0xf0,0x88,0x03,0xb1,0x21,0xa2,0x49,0x00,0x19,0x86, +0xfc,0x52,0x44,0xe0,0x01,0x9d,0x29,0x21,0x15,0x25,0x50,0xf7,0x67,0x25,0x1e,0x06, +0xfd,0x4e,0x9a,0xb4,0x90,0xac,0x15,0xfa,0xcb,0x52,0x53,0x1e,0x8c,0xf2,0xf8,0x07, +0x92,0x2d,0x08,0x3a,0x4d,0x12,0x49,0x95,0x49,0xdb,0x14,0x04,0xc4,0x14,0x85,0x29, +0xaa,0xe7,0x01,0x08,0xa4,0x49,0x01,0x14,0x51,0xe0,0x53,0x91,0xd5,0x29,0x06,0x1a, +0x64,0x02,0xf4,0xc7,0x81,0x9e,0x05,0x20,0x22,0x64,0xa5,0x30,0xae,0xab,0x9e,0x97, +0x53,0xd8,0xb9,0xfd,0x50,0xef,0x93,0x02,0x42,0x74,0x34,0xe8,0x9c,0x20,0x21,0xc9, +0x01,0x68,0x78,0xe6,0x55,0x29,0x20,0x56,0x4f,0x4c,0x40,0x51,0x71,0x82,0xc0,0x70, +0x21,0x22,0x85,0xbe,0x4b,0x1c,0x44,0x05,0xea,0xa4,0x01,0xbf,0x22,0xb5,0xf0,0x1c, +0x06,0x51,0x38,0x8f,0xe0,0x22,0xec,0x18,0xac,0x39,0x22,0xd4,0xd6,0x93,0x44,0x01, +0x32,0x82,0xc8,0xfc,0x61,0xb3,0x01,0x45,0x0c,0x2e,0x83,0x30,0xd0,0x0e,0x17,0x24, +0x0f,0x70,0x85,0x94,0xee,0x05,0x05,0x53,0x4b,0x32,0x1b,0x3f,0x98,0xd3,0x1d,0x29, +0x81,0xb0,0xae,0x1e,0x8c,0x7e,0x68,0xe0,0x60,0x5a,0x54,0x8f,0xb0,0x78,0x69,0x73, +0x06,0xa2,0x00,0x6b,0x57,0xca,0x3d,0x11,0x50,0xbd,0x04,0x30,0x4b,0x3a,0xd4,0xab, +0x5f,0x1f,0x9b,0x3d,0x13,0x74,0x27,0x88,0x3c,0x25,0xe0,0x17,0xbe,0x7a,0x79,0x45, +0x0d,0x0c,0xb0,0x8b,0xda,0x90,0xca,0x80,0x06,0x5d,0x17,0x60,0x1c,0x22,0x4c,0xd8, +0x57,0x22,0x06,0x20,0x00,0x98,0x07,0x08,0xe4,0x56,0x80,0x80,0x1c,0xc5,0xb7,0xc5, +0x82,0x0c,0x36,0xe8,0xe0,0x83,0x10,0x46,0x28,0xe1,0x84,0x14,0x56,0x68,0xa1,0x10, +0x41,0x00,0x00,0x3b,}; + +static const unsigned int dummy_align__404_html = 1; +static const unsigned char data__404_html[] = { +/* /404.html (10 chars) */ +0x2f,0x34,0x30,0x34,0x2e,0x68,0x74,0x6d,0x6c,0x00,0x00,0x00, + +/* HTTP header */ +/* "HTTP/1.0 404 File not found +" (29 bytes) */ +0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x34,0x30,0x34,0x20,0x46,0x69,0x6c, +0x65,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x0d,0x0a, +/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip) +" (63 bytes) */ +0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33, +0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e, +0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70, +0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a, +/* "Content-type: text/html + +" (27 bytes) */ +0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65, +0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a, +/* raw file data (565 bytes) */ +0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74, +0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69, +0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50, +0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f, +0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63, +0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78, +0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20, +0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22, +0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74, +0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c, +0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20, +0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68, +0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73, +0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69, +0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20, +0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d, +0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c, +0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f, +0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69, +0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09, +0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c, +0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49, +0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20, +0x20,0x3c,0x68,0x32,0x3e,0x34,0x30,0x34,0x20,0x2d,0x20,0x50,0x61,0x67,0x65,0x20, +0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x3c,0x2f,0x68,0x32,0x3e,0x0d,0x0a, +0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x53,0x6f,0x72, +0x72,0x79,0x2c,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75, +0x20,0x61,0x72,0x65,0x20,0x72,0x65,0x71,0x75,0x65,0x73,0x74,0x69,0x6e,0x67,0x20, +0x77,0x61,0x73,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x6f,0x6e, +0x20,0x74,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76, +0x65,0x72,0x2e,0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09, +0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e, +0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72, +0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65, +0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74, +0x6d,0x6c,0x3e,0x0d,0x0a,}; + +static const unsigned int dummy_align__index_html = 2; +static const unsigned char data__index_html[] = { +/* /index.html (12 chars) */ +0x2f,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x00, + +/* HTTP header */ +/* "HTTP/1.0 200 OK +" (17 bytes) */ +0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d, +0x0a, +/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip) +" (63 bytes) */ +0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33, +0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e, +0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70, +0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a, +/* "Content-type: text/html + +" (27 bytes) */ +0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65, +0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a, +/* raw file data (1751 bytes) */ +0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74, +0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69, +0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50, +0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f, +0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63, +0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78, +0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20, +0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22, +0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74, +0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c, +0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20, +0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68, +0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73, +0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69, +0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20, +0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d, +0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c, +0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f, +0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69, +0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09, +0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c, +0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49, +0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20, +0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x77, +0x65,0x62,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,0x20,0x61,0x72,0x65,0x20, +0x77,0x61,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x77,0x61,0x73,0x20,0x73,0x65,0x72, +0x76,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x73,0x69,0x6d,0x70,0x6c,0x65,0x20, +0x77,0x65,0x62,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,0x65,0x72, +0x20,0x72,0x75,0x6e,0x6e,0x69,0x6e,0x67,0x20,0x6f,0x6e,0x20,0x74,0x6f,0x70,0x20, +0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67, +0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,0x20,0x73,0x74,0x61,0x63,0x6b,0x20, +0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68, +0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73, +0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x6c, +0x77,0x49,0x50,0x3c,0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70, +0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20, +0x6c,0x77,0x49,0x50,0x20,0x69,0x73,0x20,0x61,0x6e,0x20,0x6f,0x70,0x65,0x6e,0x20, +0x73,0x6f,0x75,0x72,0x63,0x65,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74, +0x61,0x74,0x69,0x6f,0x6e,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x54,0x43,0x50, +0x2f,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x72,0x6f,0x74,0x6f,0x63, +0x6f,0x6c,0x20,0x73,0x75,0x69,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x77,0x61, +0x73,0x20,0x6f,0x72,0x69,0x67,0x69,0x6e,0x61,0x6c,0x6c,0x79,0x20,0x77,0x72,0x69, +0x74,0x74,0x65,0x6e,0x20,0x62,0x79,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20, +0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77, +0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f, +0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x41,0x64,0x61,0x6d,0x20,0x44,0x75,0x6e,0x6b, +0x65,0x6c,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x66,0x20,0x74,0x68,0x65, +0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x49,0x6e,0x73,0x74,0x69,0x74,0x75, +0x74,0x65,0x20,0x6f,0x66,0x20,0x43,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,0x20,0x53, +0x63,0x69,0x65,0x6e,0x63,0x65,0x3c,0x2f,0x61,0x3e,0x20,0x62,0x75,0x74,0x20,0x6e, +0x6f,0x77,0x20,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x62,0x65,0x69,0x6e, +0x67,0x20,0x61,0x63,0x74,0x69,0x76,0x65,0x6c,0x79,0x20,0x64,0x65,0x76,0x65,0x6c, +0x6f,0x70,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x74,0x65,0x61,0x6d,0x20,0x6f, +0x66,0x20,0x64,0x65,0x76,0x65,0x6c,0x6f,0x70,0x65,0x72,0x73,0x0d,0x0a,0x09,0x20, +0x20,0x20,0x20,0x64,0x69,0x73,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x64,0x20,0x77, +0x6f,0x72,0x6c,0x64,0x2d,0x77,0x69,0x64,0x65,0x2e,0x20,0x53,0x69,0x6e,0x63,0x65, +0x20,0x69,0x74,0x27,0x73,0x20,0x72,0x65,0x6c,0x65,0x61,0x73,0x65,0x2c,0x20,0x6c, +0x77,0x49,0x50,0x20,0x68,0x61,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x70, +0x75,0x72,0x72,0x65,0x64,0x20,0x61,0x20,0x6c,0x6f,0x74,0x20,0x6f,0x66,0x20,0x69, +0x6e,0x74,0x65,0x72,0x65,0x73,0x74,0x20,0x61,0x6e,0x64,0x20,0x68,0x61,0x73,0x20, +0x62,0x65,0x65,0x6e,0x20,0x70,0x6f,0x72,0x74,0x65,0x64,0x20,0x74,0x6f,0x20,0x73, +0x65,0x76,0x65,0x72,0x61,0x6c,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x6c,0x61, +0x74,0x66,0x6f,0x72,0x6d,0x73,0x20,0x61,0x6e,0x64,0x20,0x6f,0x70,0x65,0x72,0x61, +0x74,0x69,0x6e,0x67,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,0x73,0x2e,0x20,0x6c,0x77, +0x49,0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x65, +0x69,0x74,0x68,0x65,0x72,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x77,0x69,0x74,0x68, +0x20,0x6f,0x72,0x20,0x77,0x69,0x74,0x68,0x6f,0x75,0x74,0x20,0x61,0x6e,0x20,0x75, +0x6e,0x64,0x65,0x72,0x6c,0x79,0x69,0x6e,0x67,0x20,0x4f,0x53,0x2e,0x0d,0x0a,0x09, +0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a, +0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x66,0x6f,0x63,0x75,0x73,0x20,0x6f, +0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x54,0x43,0x50,0x2f,0x49, +0x50,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e, +0x20,0x69,0x73,0x20,0x74,0x6f,0x20,0x72,0x65,0x64,0x75,0x63,0x65,0x0d,0x0a,0x09, +0x20,0x20,0x20,0x20,0x74,0x68,0x65,0x20,0x52,0x41,0x4d,0x20,0x75,0x73,0x61,0x67, +0x65,0x20,0x77,0x68,0x69,0x6c,0x65,0x20,0x73,0x74,0x69,0x6c,0x6c,0x20,0x68,0x61, +0x76,0x69,0x6e,0x67,0x20,0x61,0x20,0x66,0x75,0x6c,0x6c,0x20,0x73,0x63,0x61,0x6c, +0x65,0x20,0x54,0x43,0x50,0x2e,0x20,0x54,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20, +0x20,0x20,0x6d,0x61,0x6b,0x65,0x73,0x20,0x6c,0x77,0x49,0x50,0x20,0x73,0x75,0x69, +0x74,0x61,0x62,0x6c,0x65,0x20,0x66,0x6f,0x72,0x20,0x75,0x73,0x65,0x20,0x69,0x6e, +0x20,0x65,0x6d,0x62,0x65,0x64,0x64,0x65,0x64,0x20,0x73,0x79,0x73,0x74,0x65,0x6d, +0x73,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x65,0x6e,0x73,0x0d,0x0a,0x09,0x20,0x20, +0x20,0x20,0x6f,0x66,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x20,0x6f, +0x66,0x20,0x66,0x72,0x65,0x65,0x20,0x52,0x41,0x4d,0x20,0x61,0x6e,0x64,0x20,0x72, +0x6f,0x6f,0x6d,0x20,0x66,0x6f,0x72,0x20,0x61,0x72,0x6f,0x75,0x6e,0x64,0x20,0x34, +0x30,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x0d,0x0a,0x09,0x20,0x20, +0x20,0x20,0x6f,0x66,0x20,0x63,0x6f,0x64,0x65,0x20,0x52,0x4f,0x4d,0x2e,0x0d,0x0a, +0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d, +0x0a,0x09,0x20,0x20,0x20,0x20,0x4d,0x6f,0x72,0x65,0x20,0x69,0x6e,0x66,0x6f,0x72, +0x6d,0x61,0x74,0x69,0x6f,0x6e,0x20,0x61,0x62,0x6f,0x75,0x74,0x20,0x6c,0x77,0x49, +0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x61, +0x74,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20, +0x20,0x68,0x6f,0x6d,0x65,0x70,0x61,0x67,0x65,0x20,0x61,0x74,0x20,0x3c,0x61,0x0d, +0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70, +0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67, +0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f, +0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61, +0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72, +0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x2f, +0x3c,0x2f,0x61,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x72,0x20,0x61,0x74, +0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x77,0x69,0x6b,0x69,0x20,0x61, +0x74,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d, +0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b, +0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f, +0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x3c, +0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09, +0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e, +0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72, +0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65, +0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74, +0x6d,0x6c,0x3e,0x0d,0x0a,0x0d,0x0a,}; + + + +const struct fsdata_file file__img_sics_gif[] = { { +file_NULL, +data__img_sics_gif, +data__img_sics_gif + 16, +sizeof(data__img_sics_gif) - 16, +1, +}}; + +const struct fsdata_file file__404_html[] = { { +file__img_sics_gif, +data__404_html, +data__404_html + 12, +sizeof(data__404_html) - 12, +1, +}}; + +const struct fsdata_file file__index_html[] = { { +file__404_html, +data__index_html, +data__index_html + 12, +sizeof(data__index_html) - 12, +1, +}}; + +#define FS_ROOT file__index_html +#define FS_NUMFILES 3 + diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.h b/sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.h new file mode 100644 index 0000000..ac4548c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/fsdata.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2001-2003 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_FSDATA_H +#define LWIP_FSDATA_H + +#include "lwip/apps/httpd_opts.h" +#include "lwip/apps/fs.h" + +struct fsdata_file { + const struct fsdata_file *next; + const unsigned char *name; + const unsigned char *data; + int len; + u8_t flags; +#if HTTPD_PRECALCULATED_CHECKSUM + u16_t chksum_count; + const struct fsdata_chksum *chksum; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ +}; + +#endif /* LWIP_FSDATA_H */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/httpd.c b/sdk/Src/Network/lwip2.0.3/apps/httpd/httpd.c new file mode 100644 index 0000000..43195d7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/httpd.c @@ -0,0 +1,2629 @@ +/** + * @file + * LWIP HTTP server implementation + */ + +/* + * Copyright (c) 2001-2003 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +/** + * @defgroup httpd HTTP server + * @ingroup apps + * + * This httpd supports for a + * rudimentary server-side-include facility which will replace tags of the form + * in any file whose extension is .shtml, .shtm or .ssi with + * strings provided by an include handler whose pointer is provided to the + * module via function http_set_ssi_handler(). + * Additionally, a simple common + * gateway interface (CGI) handling mechanism has been added to allow clients + * to hook functions to particular request URIs. + * + * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h. + * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h. + * + * By default, the server assumes that HTTP headers are already present in + * each file stored in the file system. By defining LWIP_HTTPD_DYNAMIC_HEADERS in + * lwipopts.h, this behavior can be changed such that the server inserts the + * headers automatically based on the extension of the file being served. If + * this mode is used, be careful to ensure that the file system image used + * does not already contain the header information. + * + * File system images without headers can be created using the makefsfile + * tool with the -h command line option. + * + * + * Notes about valid SSI tags + * -------------------------- + * + * The following assumptions are made about tags used in SSI markers: + * + * 1. No tag may contain '-' or whitespace characters within the tag name. + * 2. Whitespace is allowed between the tag leadin "". + * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters. + * + * Notes on CGI usage + * ------------------ + * + * The simple CGI support offered here works with GET method requests only + * and can handle up to 16 parameters encoded into the URI. The handler + * function may not write directly to the HTTP output but must return a + * filename that the HTTP server will send to the browser as a response to + * the incoming CGI request. + * + * + * + * The list of supported file types is quite short, so if makefsdata complains + * about an unknown extension, make sure to add it (and its doctype) to + * the 'g_psHTTPHeaders' list. + */ +#include "lwip/init.h" +#include "lwip/apps/httpd.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/apps/fs.h" +#include "httpd_structs.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/tcp.h" + +#include /* memset */ +#include /* atoi */ +#include + +#if LWIP_TCP && LWIP_CALLBACK_API + +/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */ +#define MIN_REQ_LEN 7 + +#define CRLF "\r\n" +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE +#define HTTP11_CONNECTIONKEEPALIVE "Connection: keep-alive" +#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive" +#endif + +/** These defines check whether tcp_write has to copy data or not */ + +/** This was TI's check whether to let TCP copy data or not + * \#define HTTP_IS_DATA_VOLATILE(hs) ((hs->file < (char *)0x20000000) ? 0 : TCP_WRITE_FLAG_COPY) + */ +#ifndef HTTP_IS_DATA_VOLATILE +#if LWIP_HTTPD_SSI +/* Copy for SSI files, no copy for non-SSI files */ +#define HTTP_IS_DATA_VOLATILE(hs) ((hs)->ssi ? TCP_WRITE_FLAG_COPY : 0) +#else /* LWIP_HTTPD_SSI */ +/** Default: don't copy if the data is sent from file-system directly */ +#define HTTP_IS_DATA_VOLATILE(hs) (((hs->file != NULL) && (hs->handle != NULL) && (hs->file == \ + (const char*)hs->handle->data + hs->handle->len - hs->left)) \ + ? 0 : TCP_WRITE_FLAG_COPY) +#endif /* LWIP_HTTPD_SSI */ +#endif + +/** Default: headers are sent from ROM */ +#ifndef HTTP_IS_HDR_VOLATILE +#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0 +#endif + +/* Return values for http_send_*() */ +#define HTTP_DATA_TO_SEND_BREAK 2 +#define HTTP_DATA_TO_SEND_CONTINUE 1 +#define HTTP_NO_DATA_TO_SEND 0 + +typedef struct +{ + const char *name; + u8_t shtml; +} default_filename; + +const default_filename g_psDefaultFilenames[] = { + {"/index.shtml", 1 }, + {"/index.ssi", 1 }, + {"/index.shtm", 1 }, + {"/index.html", 0 }, + {"/index.htm", 0 } +}; + +#define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) / \ + sizeof(default_filename)) + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST +/** HTTP request is copied here from pbufs for simple parsing */ +static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH+1]; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +#if LWIP_HTTPD_SUPPORT_POST +#if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN +#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN +#endif +#endif +#ifndef LWIP_HTTPD_URI_BUF_LEN +#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN +#endif +#if LWIP_HTTPD_URI_BUF_LEN +/* Filename for response file to send when POST is finished or + * search for default files when a directory is requested. */ +static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN+1]; +#endif + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/* The number of individual strings that comprise the headers sent before each + * requested file. + */ +#define NUM_FILE_HDR_STRINGS 5 +#define HDR_STRINGS_IDX_HTTP_STATUS 0 /* e.g. "HTTP/1.0 200 OK\r\n" */ +#define HDR_STRINGS_IDX_SERVER_NAME 1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */ +#define HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */ +#define HDR_STRINGS_IDX_CONTENT_LEN_NR 3 /* the byte count, when content-length is used */ +#define HDR_STRINGS_IDX_CONTENT_TYPE 4 /* the content type (or default answer content type including default document) */ + +/* The dynamically generated Content-Length buffer needs space for CRLF + NULL */ +#define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3 +#ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE +/* The dynamically generated Content-Length buffer shall be able to work with + ~953 MB (9 digits) */ +#define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) +#endif +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +#if LWIP_HTTPD_SSI + +#define HTTPD_LAST_TAG_PART 0xFFFF + +enum tag_check_state { + TAG_NONE, /* Not processing an SSI tag */ + TAG_LEADIN, /* Tag lead in "" being processed */ + TAG_SENDING /* Sending tag replacement string */ +}; + +struct http_ssi_state { + const char *parsed; /* Pointer to the first unparsed byte in buf. */ +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + const char *tag_started;/* Pointer to the first opening '<' of the tag. */ +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ + const char *tag_end; /* Pointer to char after the closing '>' of the tag. */ + u32_t parse_left; /* Number of unparsed bytes in buf. */ + u16_t tag_index; /* Counter used by tag parsing state machine */ + u16_t tag_insert_len; /* Length of insert in string tag_insert */ +#if LWIP_HTTPD_SSI_MULTIPART + u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */ +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + u8_t tag_name_len; /* Length of the tag name in string tag_name */ + char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */ + char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */ + enum tag_check_state tag_state; /* State of the tag processor */ +}; +#endif /* LWIP_HTTPD_SSI */ + +struct http_state { +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + struct http_state *next; +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + struct fs_file file_handle; + struct fs_file *handle; + const char *file; /* Pointer to first unsent byte in buf. */ + + struct tcp_pcb *pcb; +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + struct pbuf *req; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +#if LWIP_HTTPD_DYNAMIC_FILE_READ + char *buf; /* File read buffer. */ + int buf_len; /* Size of file read buffer, buf. */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + u32_t left; /* Number of unsent bytes in buf. */ + u8_t retries; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + u8_t keepalive; +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ +#if LWIP_HTTPD_SSI + struct http_ssi_state *ssi; +#endif /* LWIP_HTTPD_SSI */ +#if LWIP_HTTPD_CGI + char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ + char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ +#endif /* LWIP_HTTPD_CGI */ +#if LWIP_HTTPD_DYNAMIC_HEADERS + const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */ + char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE]; + u16_t hdr_pos; /* The position of the first unsent header byte in the + current string */ + u16_t hdr_index; /* The index of the hdr string currently being sent. */ +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_TIMING + u32_t time_started; +#endif /* LWIP_HTTPD_TIMING */ +#if LWIP_HTTPD_SUPPORT_POST + u32_t post_content_len_left; +#if LWIP_HTTPD_POST_MANUAL_WND + u32_t unrecved_bytes; + u8_t no_auto_wnd; + u8_t post_finished; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ +#endif /* LWIP_HTTPD_SUPPORT_POST*/ +}; + +#if HTTPD_USE_MEM_POOL +LWIP_MEMPOOL_DECLARE(HTTPD_STATE, MEMP_NUM_PARALLEL_HTTPD_CONNS, sizeof(struct http_state), "HTTPD_STATE") +#if LWIP_HTTPD_SSI +LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE") +#define HTTP_FREE_SSI_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x)) +#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE) +#endif /* LWIP_HTTPD_SSI */ +#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE) +#define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x)) +#else /* HTTPD_USE_MEM_POOL */ +#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state)) +#define HTTP_FREE_HTTP_STATE(x) mem_free(x) +#if LWIP_HTTPD_SSI +#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state)) +#define HTTP_FREE_SSI_STATE(x) mem_free(x) +#endif /* LWIP_HTTPD_SSI */ +#endif /* HTTPD_USE_MEM_POOL */ + +static err_t http_close_conn(struct tcp_pcb *pcb, struct http_state *hs); +static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn); +static err_t http_find_file(struct http_state *hs, const char *uri, int is_09); +static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char* params); +static err_t http_poll(void *arg, struct tcp_pcb *pcb); +static u8_t http_check_eof(struct tcp_pcb *pcb, struct http_state *hs); +#if LWIP_HTTPD_FS_ASYNC_READ +static void http_continue(void *connection); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +#if LWIP_HTTPD_SSI +/* SSI insert handler function pointer. */ +tSSIHandler g_pfnSSIHandler; +#if !LWIP_HTTPD_SSI_RAW +int g_iNumTags; +const char **g_ppcTags; +#endif /* !LWIP_HTTPD_SSI_RAW */ + +#define LEN_TAG_LEAD_IN 5 +const char * const g_pcTagLeadIn = ""; +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_CGI +/* CGI handler information */ +const tCGI *g_pCGIs; +int g_iNumCGIs; +int http_cgi_paramcount; +#define http_cgi_params hs->params +#define http_cgi_param_vals hs->param_vals +#elif LWIP_HTTPD_CGI_SSI +char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ +char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ +#endif /* LWIP_HTTPD_CGI */ + +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED +/** global list of active HTTP connections, use to kill the oldest when + running out of memory */ +static struct http_state *http_connections; + +static void +http_add_connection(struct http_state *hs) +{ + /* add the connection to the list */ + hs->next = http_connections; + http_connections = hs; +} + +static void +http_remove_connection(struct http_state *hs) +{ + /* take the connection off the list */ + if (http_connections) { + if (http_connections == hs) { + http_connections = hs->next; + } else { + struct http_state *last; + for(last = http_connections; last->next != NULL; last = last->next) { + if (last->next == hs) { + last->next = hs->next; + break; + } + } + } + } +} + +static void +http_kill_oldest_connection(u8_t ssi_required) +{ + struct http_state *hs = http_connections; + struct http_state *hs_free_next = NULL; + while(hs && hs->next) { +#if LWIP_HTTPD_SSI + if (ssi_required) { + if (hs->next->ssi != NULL) { + hs_free_next = hs; + } + } else +#else /* LWIP_HTTPD_SSI */ + LWIP_UNUSED_ARG(ssi_required); +#endif /* LWIP_HTTPD_SSI */ + { + hs_free_next = hs; + } + LWIP_ASSERT("broken list", hs != hs->next); + hs = hs->next; + } + if (hs_free_next != NULL) { + LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL); + LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL); + /* send RST when killing a connection because of memory shortage */ + http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */ + } +} +#else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + +#define http_add_connection(hs) +#define http_remove_connection(hs) + +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + +#if LWIP_HTTPD_SSI +/** Allocate as struct http_ssi_state. */ +static struct http_ssi_state* +http_ssi_state_alloc(void) +{ + struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE(); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + if (ret == NULL) { + http_kill_oldest_connection(1); + ret = HTTP_ALLOC_SSI_STATE(); + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + if (ret != NULL) { + memset(ret, 0, sizeof(struct http_ssi_state)); + } + return ret; +} + +/** Free a struct http_ssi_state. */ +static void +http_ssi_state_free(struct http_ssi_state *ssi) +{ + if (ssi != NULL) { + HTTP_FREE_SSI_STATE(ssi); + } +} +#endif /* LWIP_HTTPD_SSI */ + +/** Initialize a struct http_state. + */ +static void +http_state_init(struct http_state* hs) +{ + /* Initialize the structure. */ + memset(hs, 0, sizeof(struct http_state)); +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Indicate that the headers are not yet valid */ + hs->hdr_index = NUM_FILE_HDR_STRINGS; +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +} + +/** Allocate a struct http_state. */ +static struct http_state* +http_state_alloc(void) +{ + struct http_state *ret = HTTP_ALLOC_HTTP_STATE(); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + if (ret == NULL) { + http_kill_oldest_connection(0); + ret = HTTP_ALLOC_HTTP_STATE(); + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + if (ret != NULL) { + http_state_init(ret); + http_add_connection(ret); + } + return ret; +} + +/** Free a struct http_state. + * Also frees the file data if dynamic. + */ +static void +http_state_eof(struct http_state *hs) +{ + if(hs->handle) { +#if LWIP_HTTPD_TIMING + u32_t ms_needed = sys_now() - hs->time_started; + u32_t needed = LWIP_MAX(1, (ms_needed/100)); + LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n", + ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed))); +#endif /* LWIP_HTTPD_TIMING */ + fs_close(hs->handle); + hs->handle = NULL; + } +#if LWIP_HTTPD_DYNAMIC_FILE_READ + if (hs->buf != NULL) { + mem_free(hs->buf); + hs->buf = NULL; + } +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +#if LWIP_HTTPD_SSI + if (hs->ssi) { + http_ssi_state_free(hs->ssi); + hs->ssi = NULL; + } +#endif /* LWIP_HTTPD_SSI */ +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + if (hs->req) { + pbuf_free(hs->req); + hs->req = NULL; + } +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +} + +/** Free a struct http_state. + * Also frees the file data if dynamic. + */ +static void +http_state_free(struct http_state *hs) +{ + if (hs != NULL) { + http_state_eof(hs); + http_remove_connection(hs); + HTTP_FREE_HTTP_STATE(hs); + } +} + +/** Call tcp_write() in a loop trying smaller and smaller length + * + * @param pcb tcp_pcb to send + * @param ptr Data to send + * @param length Length of data to send (in/out: on return, contains the + * amount of data sent) + * @param apiflags directly passed to tcp_write + * @return the return value of tcp_write + */ +static err_t +http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags) +{ + u16_t len, max_len; + err_t err; + LWIP_ASSERT("length != NULL", length != NULL); + len = *length; + if (len == 0) { + return ERR_OK; + } + /* We cannot send more data than space available in the send buffer. */ + max_len = tcp_sndbuf(pcb); + if (max_len < len) { + len = max_len; + } +#ifdef HTTPD_MAX_WRITE_LEN + /* Additional limitation: e.g. don't enqueue more than 2*mss at once */ + max_len = HTTPD_MAX_WRITE_LEN(pcb); + if(len > max_len) { + len = max_len; + } +#endif /* HTTPD_MAX_WRITE_LEN */ + do { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len)); + err = tcp_write(pcb, ptr, len, apiflags); + if (err == ERR_MEM) { + if ((tcp_sndbuf(pcb) == 0) || + (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) { + /* no need to try smaller sizes */ + len = 1; + } else { + len /= 2; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, + ("Send failed, trying less (%d bytes)\n", len)); + } + } while ((err == ERR_MEM) && (len > 1)); + + if (err == ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len)); + *length = len; + } else { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); + *length = 0; + } + +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + /* ensure nagle is normally enabled (only disabled for persistent connections + when all data has been enqueued but the connection stays open for the next + request */ + tcp_nagle_enable(pcb); +#endif + + return err; +} + +/** + * The connection shall be actively closed (using RST to close from fault states). + * Reset the sent- and recv-callbacks. + * + * @param pcb the tcp pcb to reset callbacks + * @param hs connection state to free + */ +static err_t +http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn) +{ + err_t err; + LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb)); + +#if LWIP_HTTPD_SUPPORT_POST + if (hs != NULL) { + if ((hs->post_content_len_left != 0) +#if LWIP_HTTPD_POST_MANUAL_WND + || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0)) +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + ) { + /* make sure the post code knows that the connection is closed */ + http_uri_buf[0] = 0; + httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN); + } + } +#endif /* LWIP_HTTPD_SUPPORT_POST*/ + + + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_err(pcb, NULL); + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + if (hs != NULL) { + http_state_free(hs); + } + + if (abort_conn) { + tcp_abort(pcb); + return ERR_OK; + } + err = tcp_close(pcb); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb)); + /* error closing, try again later in poll */ + tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); + } + return err; +} + +/** + * The connection shall be actively closed. + * Reset the sent- and recv-callbacks. + * + * @param pcb the tcp pcb to reset callbacks + * @param hs connection state to free + */ +static err_t +http_close_conn(struct tcp_pcb *pcb, struct http_state *hs) +{ + return http_close_or_abort_conn(pcb, hs, 0); +} + +/** End of file: either close the connection (Connection: close) or + * close the file (Connection: keep-alive) + */ +static void +http_eof(struct tcp_pcb *pcb, struct http_state *hs) +{ + /* HTTP/1.1 persistent connection? (Not supported for SSI) */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { + http_remove_connection(hs); + + http_state_eof(hs); + http_state_init(hs); + /* restore state: */ + hs->pcb = pcb; + hs->keepalive = 1; + http_add_connection(hs); + /* ensure nagle doesn't interfere with sending all data as fast as possible: */ + tcp_nagle_disable(pcb); + } else +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + { + http_close_conn(pcb, hs); + } +} + +#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI +/** + * Extract URI parameters from the parameter-part of an URI in the form + * "test.cgi?x=y" @todo: better explanation! + * Pointers to the parameters are stored in hs->param_vals. + * + * @param hs http connection state + * @param params pointer to the NULL-terminated parameter string from the URI + * @return number of parameters extracted + */ +static int +extract_uri_parameters(struct http_state *hs, char *params) +{ + char *pair; + char *equals; + int loop; + + LWIP_UNUSED_ARG(hs); + + /* If we have no parameters at all, return immediately. */ + if(!params || (params[0] == '\0')) { + return(0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for(loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) { + + /* Save the name of the parameter */ + http_cgi_params[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, '&'); + if(pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if(pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, '='); + if(equals) { + *equals = '\0'; + http_cgi_param_vals[loop] = equals + 1; + } else { + http_cgi_param_vals[loop] = NULL; + } + } + + return loop; +} +#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */ + +#if LWIP_HTTPD_SSI +/** + * Insert a tag (found in an shtml in the form of "" into the file. + * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement + * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN). + * The amount of data written is stored to ssi->tag_insert_len. + * + * @todo: return tag_insert_len - maybe it can be removed from struct http_state? + * + * @param hs http connection state + */ +static void +get_tag_insert(struct http_state *hs) +{ +#if LWIP_HTTPD_SSI_RAW + const char* tag; +#else /* LWIP_HTTPD_SSI_RAW */ + int tag; +#endif /* LWIP_HTTPD_SSI_RAW */ + size_t len; + struct http_ssi_state *ssi; +#if LWIP_HTTPD_SSI_MULTIPART + u16_t current_tag_part; +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + + LWIP_ASSERT("hs != NULL", hs != NULL); + ssi = hs->ssi; + LWIP_ASSERT("ssi != NULL", ssi != NULL); +#if LWIP_HTTPD_SSI_MULTIPART + current_tag_part = ssi->tag_part; + ssi->tag_part = HTTPD_LAST_TAG_PART; +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if LWIP_HTTPD_SSI_RAW + tag = ssi->tag_name; +#endif + + if(g_pfnSSIHandler +#if !LWIP_HTTPD_SSI_RAW + && g_ppcTags && g_iNumTags +#endif /* !LWIP_HTTPD_SSI_RAW */ + ) { + + /* Find this tag in the list we have been provided. */ +#if LWIP_HTTPD_SSI_RAW + { +#else /* LWIP_HTTPD_SSI_RAW */ + for(tag = 0; tag < g_iNumTags; tag++) { + if(strcmp(ssi->tag_name, g_ppcTags[tag]) == 0) +#endif /* LWIP_HTTPD_SSI_RAW */ + { + ssi->tag_insert_len = g_pfnSSIHandler(tag, ssi->tag_insert, + LWIP_HTTPD_MAX_TAG_INSERT_LEN +#if LWIP_HTTPD_SSI_MULTIPART + , current_tag_part, &ssi->tag_part +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if LWIP_HTTPD_FILE_STATE + , (hs->handle ? hs->handle->state : NULL) +#endif /* LWIP_HTTPD_FILE_STATE */ + ); +#if LWIP_HTTPD_SSI_RAW + if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN) +#endif /* LWIP_HTTPD_SSI_RAW */ + { + return; + } + } + } + } + + /* If we drop out, we were asked to serve a page which contains tags that + * we don't have a handler for. Merely echo back the tags with an error + * marker. */ +#define UNKNOWN_TAG1_TEXT "***UNKNOWN TAG " +#define UNKNOWN_TAG1_LEN 18 +#define UNKNOWN_TAG2_TEXT "***" +#define UNKNOWN_TAG2_LEN 7 + len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name), + LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN))); + MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN); + MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len); + MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN); + ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0; + + len = strlen(ssi->tag_insert); + LWIP_ASSERT("len <= 0xffff", len <= 0xffff); + ssi->tag_insert_len = (u16_t)len; +} +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/** + * Generate the relevant HTTP headers for the given filename and write + * them into the supplied buffer. + */ +static void +get_http_headers(struct http_state *hs, const char *uri) +{ + size_t content_type; + char *tmp; + char *ext; + char *vars; + u8_t add_content_len; + + /* In all cases, the second header we send is the server identification + so set it here. */ + hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER]; + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = NULL; + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL; + + /* Is this a normal file or the special case we use to send back the + default "404: Page not found" response? */ + if (uri == NULL) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT]; + } else +#endif + { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML]; + } + + /* Set up to send the first header string. */ + hs->hdr_index = 0; + hs->hdr_pos = 0; + return; + } + /* We are dealing with a particular filename. Look for one other + special case. We assume that any filename with "404" in it must be + indicative of a 404 server error whereas all other files require + the 200 OK header. */ + if (strstr(uri, "404")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; + } else if (strstr(uri, "400")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST]; + } else if (strstr(uri, "501")) { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL]; + } else { + hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK]; + } + + /* Determine if the URI has any variables and, if so, temporarily remove + them. */ + vars = strchr(uri, '?'); + if(vars) { + *vars = '\0'; + } + + /* Get a pointer to the file extension. We find this by looking for the + last occurrence of "." in the filename passed. */ + ext = NULL; + tmp = strchr(uri, '.'); + while (tmp) { + ext = tmp + 1; + tmp = strchr(ext, '.'); + } + if (ext != NULL) { + /* Now determine the content type and add the relevant header for that. */ + for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) { + /* Have we found a matching extension? */ + if(!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) { + break; + } + } + } else { + content_type = NUM_HTTP_HEADERS; + } + + /* Reinstate the parameter marker if there was one in the original URI. */ + if (vars) { + *vars = '?'; + } + +#if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI + /* Does the URL passed have any file extension? If not, we assume it + is a special-case URL used for control state notification and we do + not send any HTTP headers with the response. */ + if (!ext) { + /* Force the header index to a value indicating that all headers + have already been sent. */ + hs->hdr_index = NUM_FILE_HDR_STRINGS; + return; + } +#endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */ + add_content_len = 1; + /* Did we find a matching extension? */ + if(content_type < NUM_HTTP_HEADERS) { + /* yes, store it */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type; + } else if (!ext) { + /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP; + } else { + /* No - use the default, plain text file type. */ + hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE; + } + /* Add content-length header? */ +#if LWIP_HTTPD_SSI + if (hs->ssi != NULL) { + add_content_len = 0; /* @todo: get maximum file length from SSI */ + } else +#endif /* LWIP_HTTPD_SSI */ + if ((hs->handle == NULL) || + ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) { + add_content_len = 0; + } + if (add_content_len) { + size_t len; + lwip_itoa(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE, + hs->handle->len); + len = strlen(hs->hdr_content_len); + if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) { + SMEMCPY(&hs->hdr_content_len[len], CRLF "\0", 3); + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len; + } else { + add_content_len = 0; + } + } +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (add_content_len) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN]; + } else { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE]; + } +#else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + if (add_content_len) { + hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH]; + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + + /* Set up to send the first header string. */ + hs->hdr_index = 0; + hs->hdr_pos = 0; +} + +/** Sub-function of http_send(): send dynamic headers + * + * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued + * - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body + * - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending, + * so don't send HTTP body yet + */ +static u8_t +http_send_headers(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err; + u16_t len; + u8_t data_to_send = HTTP_NO_DATA_TO_SEND; + u16_t hdrlen, sendlen; + + /* How much data can we send? */ + len = tcp_sndbuf(pcb); + sendlen = len; + + while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) { + const void *ptr; + u16_t old_sendlen; + u8_t apiflags; + /* How much do we have to send from the current header? */ + hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]); + + /* How much of this can we send? */ + sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos); + + /* Send this amount of data or as much as we can given memory + * constraints. */ + ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos); + old_sendlen = sendlen; + apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr); + if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) { + /* content-length is always volatile */ + apiflags |= TCP_WRITE_FLAG_COPY; + } + if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) { + apiflags |= TCP_WRITE_FLAG_MORE; + } + err = http_write(pcb, ptr, &sendlen, apiflags); + if ((err == ERR_OK) && (old_sendlen != sendlen)) { + /* Remember that we added some more data to be transmitted. */ + data_to_send = HTTP_DATA_TO_SEND_CONTINUE; + } else if (err != ERR_OK) { + /* special case: http_write does not try to send 1 byte */ + sendlen = 0; + } + + /* Fix up the header position for the next time round. */ + hs->hdr_pos += sendlen; + len -= sendlen; + + /* Have we finished sending this string? */ + if(hs->hdr_pos == hdrlen) { + /* Yes - move on to the next one */ + hs->hdr_index++; + /* skip headers that are NULL (not all headers are required) */ + while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) && + (hs->hdrs[hs->hdr_index] == NULL)) { + hs->hdr_index++; + } + hs->hdr_pos = 0; + } + } + + if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) { + /* When we are at the end of the headers, check for data to send + * instead of waiting for ACK from remote side to continue + * (which would happen when sending files from async read). */ + if(http_check_eof(pcb, hs)) { + data_to_send = HTTP_DATA_TO_SEND_CONTINUE; + } + } + /* If we get here and there are still header bytes to send, we send + * the header information we just wrote immediately. If there are no + * more headers to send, but we do have file data to send, drop through + * to try to send some file data too. */ + if((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) { + LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n")); + return HTTP_DATA_TO_SEND_BREAK; + } + return data_to_send; +} +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +/** Sub-function of http_send(): end-of-file (or block) is reached, + * either close the file or read the next block (if supported). + * + * @returns: 0 if the file is finished or no data has been read + * 1 if the file is not finished and data has been read + */ +static u8_t +http_check_eof(struct tcp_pcb *pcb, struct http_state *hs) +{ + int bytes_left; +#if LWIP_HTTPD_DYNAMIC_FILE_READ + int count; +#ifdef HTTPD_MAX_WRITE_LEN + int max_write_len; +#endif /* HTTPD_MAX_WRITE_LEN */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + + /* Do we have a valid file handle? */ + if (hs->handle == NULL) { + /* No - close the connection. */ + http_eof(pcb, hs); + return 0; + } + bytes_left = fs_bytes_left(hs->handle); + if (bytes_left <= 0) { + /* We reached the end of the file so this request is done. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } +#if LWIP_HTTPD_DYNAMIC_FILE_READ + /* Do we already have a send buffer allocated? */ + if(hs->buf) { + /* Yes - get the length of the buffer */ + count = LWIP_MIN(hs->buf_len, bytes_left); + } else { + /* We don't have a send buffer so allocate one now */ + count = tcp_sndbuf(pcb); + if(bytes_left < count) { + count = bytes_left; + } +#ifdef HTTPD_MAX_WRITE_LEN + /* Additional limitation: e.g. don't enqueue more than 2*mss at once */ + max_write_len = HTTPD_MAX_WRITE_LEN(pcb); + if (count > max_write_len) { + count = max_write_len; + } +#endif /* HTTPD_MAX_WRITE_LEN */ + do { + hs->buf = (char*)mem_malloc((mem_size_t)count); + if (hs->buf != NULL) { + hs->buf_len = count; + break; + } + count = count / 2; + } while (count > 100); + + /* Did we get a send buffer? If not, return immediately. */ + if (hs->buf == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n")); + return 0; + } + } + + /* Read a block of data from the file. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count)); + +#if LWIP_HTTPD_FS_ASYNC_READ + count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ + count = fs_read(hs->handle, hs->buf, count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + if (count < 0) { + if (count == FS_READ_DELAYED) { + /* Delayed read, wait for FS to unblock us */ + return 0; + } + /* We reached the end of the file so this request is done. + * @todo: close here for HTTP/1.1 when reading file fails */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } + + /* Set up to send the block of data we just read */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count)); + hs->left = count; + hs->file = hs->buf; +#if LWIP_HTTPD_SSI + if (hs->ssi) { + hs->ssi->parse_left = count; + hs->ssi->parsed = hs->buf; + } +#endif /* LWIP_HTTPD_SSI */ +#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0); +#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */ + return 1; +} + +/** Sub-function of http_send(): This is the normal send-routine for non-ssi files + * + * @returns: - 1: data has been written (so call tcp_ouput) + * - 0: no data has been written (no need to call tcp_output) + */ +static u8_t +http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err; + u16_t len; + u8_t data_to_send = 0; + + /* We are not processing an SHTML file so no tag checking is necessary. + * Just send the data as we received it from the file. */ + len = (u16_t)LWIP_MIN(hs->left, 0xffff); + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + + return data_to_send; +} + +#if LWIP_HTTPD_SSI +/** Sub-function of http_send(): This is the send-routine for ssi files + * + * @returns: - 1: data has been written (so call tcp_ouput) + * - 0: no data has been written (no need to call tcp_output) + */ +static u8_t +http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err = ERR_OK; + u16_t len; + u8_t data_to_send = 0; + + struct http_ssi_state *ssi = hs->ssi; + LWIP_ASSERT("ssi != NULL", ssi != NULL); + /* We are processing an SHTML file so need to scan for tags and replace + * them with insert strings. We need to be careful here since a tag may + * straddle the boundary of two blocks read from the file and we may also + * have to split the insert string between two tcp_write operations. */ + + /* How much data could we send? */ + len = tcp_sndbuf(pcb); + + /* Do we have remaining data to send before parsing more? */ + if(ssi->parsed > hs->file) { + len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff); + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + + /* If the send buffer is full, return now. */ + if(tcp_sndbuf(pcb) == 0) { + return data_to_send; + } + } + + LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left)); + + /* We have sent all the data that was already parsed so continue parsing + * the buffer contents looking for SSI tags. */ + while((ssi->parse_left) && (err == ERR_OK)) { + if (len == 0) { + return data_to_send; + } + switch(ssi->tag_state) { + case TAG_NONE: + /* We are not currently processing an SSI tag so scan for the + * start of the lead-in marker. */ + if(*ssi->parsed == g_pcTagLeadIn[0]) { + /* We found what could be the lead-in for a new tag so change + * state appropriately. */ + ssi->tag_state = TAG_LEADIN; + ssi->tag_index = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->tag_started = ssi->parsed; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + + case TAG_LEADIN: + /* We are processing the lead-in marker, looking for the start of + * the tag name. */ + + /* Have we reached the end of the leadin? */ + if(ssi->tag_index == LEN_TAG_LEAD_IN) { + ssi->tag_index = 0; + ssi->tag_state = TAG_FOUND; + } else { + /* Have we found the next character we expect for the tag leadin? */ + if(*ssi->parsed == g_pcTagLeadIn[ssi->tag_index]) { + /* Yes - move to the next one unless we have found the complete + * leadin, in which case we start looking for the tag itself */ + ssi->tag_index++; + } else { + /* We found an unexpected character so this is not a tag. Move + * back to idle state. */ + ssi->tag_state = TAG_NONE; + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + } + break; + + case TAG_FOUND: + /* We are reading the tag name, looking for the start of the + * lead-out marker and removing any whitespace found. */ + + /* Remove leading whitespace between the tag leading and the first + * tag name character. */ + if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || + (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || + (*ssi->parsed == '\r'))) { + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + } + + /* Have we found the end of the tag name? This is signalled by + * us finding the first leadout character or whitespace */ + if((*ssi->parsed == g_pcTagLeadOut[0]) || + (*ssi->parsed == ' ') || (*ssi->parsed == '\t') || + (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) { + + if(ssi->tag_index == 0) { + /* We read a zero length tag so ignore it. */ + ssi->tag_state = TAG_NONE; + } else { + /* We read a non-empty tag so go ahead and look for the + * leadout string. */ + ssi->tag_state = TAG_LEADOUT; + LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff); + ssi->tag_name_len = (u8_t)ssi->tag_index; + ssi->tag_name[ssi->tag_index] = '\0'; + if(*ssi->parsed == g_pcTagLeadOut[0]) { + ssi->tag_index = 1; + } else { + ssi->tag_index = 0; + } + } + } else { + /* This character is part of the tag name so save it */ + if(ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) { + ssi->tag_name[ssi->tag_index++] = *ssi->parsed; + } else { + /* The tag was too long so ignore it. */ + ssi->tag_state = TAG_NONE; + } + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + + break; + + /* We are looking for the end of the lead-out marker. */ + case TAG_LEADOUT: + /* Remove leading whitespace between the tag leading and the first + * tag leadout character. */ + if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || + (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || + (*ssi->parsed == '\r'))) { + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + } + + /* Have we found the next character we expect for the tag leadout? */ + if(*ssi->parsed == g_pcTagLeadOut[ssi->tag_index]) { + /* Yes - move to the next one unless we have found the complete + * leadout, in which case we need to call the client to process + * the tag. */ + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + + if(ssi->tag_index == (LEN_TAG_LEAD_OUT - 1)) { + /* Call the client to ask for the insert string for the + * tag we just found. */ +#if LWIP_HTTPD_SSI_MULTIPART + ssi->tag_part = 0; /* start with tag part 0 */ +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + get_tag_insert(hs); + + /* Next time through, we are going to be sending data + * immediately, either the end of the block we start + * sending here or the insert string. */ + ssi->tag_index = 0; + ssi->tag_state = TAG_SENDING; + ssi->tag_end = ssi->parsed; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->parsed = ssi->tag_started; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + + /* If there is any unsent data in the buffer prior to the + * tag, we need to send it now. */ + if (ssi->tag_end > hs->file) { + /* How much of the data can we send? */ +#if LWIP_HTTPD_SSI_INCLUDE_TAG + len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff); +#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + /* we would include the tag in sending */ + len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff); +#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + if(ssi->tag_started <= hs->file) { + /* pretend to have sent the tag, too */ + len += ssi->tag_end - ssi->tag_started; + } +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + hs->file += len; + hs->left -= len; + } + } + } else { + ssi->tag_index++; + } + } else { + /* We found an unexpected character so this is not a tag. Move + * back to idle state. */ + ssi->parse_left--; + ssi->parsed++; + ssi->tag_state = TAG_NONE; + } + break; + + /* + * We have found a valid tag and are in the process of sending + * data as a result of that discovery. We send either remaining data + * from the file prior to the insert point or the insert string itself. + */ + case TAG_SENDING: + /* Do we have any remaining file data to send from the buffer prior + * to the tag? */ + if(ssi->tag_end > hs->file) { + /* How much of the data can we send? */ +#if LWIP_HTTPD_SSI_INCLUDE_TAG + len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff); +#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file); + /* we would include the tag in sending */ + len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff); +#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + if (len != 0) { + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + } else { + err = ERR_OK; + } + if (err == ERR_OK) { + data_to_send = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + if(ssi->tag_started <= hs->file) { + /* pretend to have sent the tag, too */ + len += ssi->tag_end - ssi->tag_started; + } +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + hs->file += len; + hs->left -= len; + } + } else { +#if LWIP_HTTPD_SSI_MULTIPART + if(ssi->tag_index >= ssi->tag_insert_len) { + /* Did the last SSIHandler have more to send? */ + if (ssi->tag_part != HTTPD_LAST_TAG_PART) { + /* If so, call it again */ + ssi->tag_index = 0; + get_tag_insert(hs); + } + } +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + + /* Do we still have insert data left to send? */ + if(ssi->tag_index < ssi->tag_insert_len) { + /* We are sending the insert string itself. How much of the + * insert can we send? */ + len = (ssi->tag_insert_len - ssi->tag_index); + + /* Note that we set the copy flag here since we only have a + * single tag insert buffer per connection. If we don't do + * this, insert corruption can occur if more than one insert + * is processed before we call tcp_output. */ + err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len, + HTTP_IS_TAG_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + ssi->tag_index += len; + /* Don't return here: keep on sending data */ + } + } else { +#if LWIP_HTTPD_SSI_MULTIPART + if (ssi->tag_part == HTTPD_LAST_TAG_PART) +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + { + /* We have sent all the insert data so go back to looking for + * a new tag. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n")); + ssi->tag_index = 0; + ssi->tag_state = TAG_NONE; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->parsed = ssi->tag_end; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + } + } + break; + default: + break; + } + } + } + + /* If we drop out of the end of the for loop, this implies we must have + * file data to send so send it now. In TAG_SENDING state, we've already + * handled this so skip the send if that's the case. */ + if((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) { + len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff); + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + } + return data_to_send; +} +#endif /* LWIP_HTTPD_SSI */ + +/** + * Try to send more data on this pcb. + * + * @param pcb the pcb to send data + * @param hs connection state + */ +static u8_t +http_send(struct tcp_pcb *pcb, struct http_state *hs) +{ + u8_t data_to_send = HTTP_NO_DATA_TO_SEND; + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void*)pcb, + (void*)hs, hs != NULL ? (int)hs->left : 0)); + +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->unrecved_bytes != 0) { + return 0; + } +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + + /* If we were passed a NULL state structure pointer, ignore the call. */ + if (hs == NULL) { + return 0; + } + +#if LWIP_HTTPD_FS_ASYNC_READ + /* Check if we are allowed to read from this file. + (e.g. SSI might want to delay sending until data is available) */ + if (!fs_is_file_ready(hs->handle, http_continue, hs)) { + return 0; + } +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Do we have any more header data to send for this file? */ + if (hs->hdr_index < NUM_FILE_HDR_STRINGS) { + data_to_send = http_send_headers(pcb, hs); + if ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) && + (hs->hdr_index < NUM_FILE_HDR_STRINGS)) { + return data_to_send; + } + } +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + + /* Have we run out of file data to send? If so, we need to read the next + * block from the file. */ + if (hs->left == 0) { + if (!http_check_eof(pcb, hs)) { + return 0; + } + } + +#if LWIP_HTTPD_SSI + if(hs->ssi) { + data_to_send = http_send_data_ssi(pcb, hs); + } else +#endif /* LWIP_HTTPD_SSI */ + { + data_to_send = http_send_data_nonssi(pcb, hs); + } + + if((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) { + /* We reached the end of the file so this request is done. + * This adds the FIN flag right into the last data segment. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n")); + return data_to_send; +} + +#if LWIP_HTTPD_SUPPORT_EXTSTATUS +/** Initialize a http connection with a file to send for an error message + * + * @param hs http connection state + * @param error_nr HTTP error number + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_find_error_file(struct http_state *hs, u16_t error_nr) +{ + const char *uri1, *uri2, *uri3; + err_t err; + + if (error_nr == 501) { + uri1 = "/501.html"; + uri2 = "/501.htm"; + uri3 = "/501.shtml"; + } else { + /* 400 (bad request is the default) */ + uri1 = "/400.html"; + uri2 = "/400.htm"; + uri3 = "/400.shtml"; + } + err = fs_open(&hs->file_handle, uri1); + if (err != ERR_OK) { + err = fs_open(&hs->file_handle, uri2); + if (err != ERR_OK) { + err = fs_open(&hs->file_handle, uri3); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n", + error_nr)); + return ERR_ARG; + } + } + } + return http_init_file(hs, &hs->file_handle, 0, NULL, 0, NULL); +} +#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ +#define http_find_error_file(hs, error_nr) ERR_ARG +#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ + +/** + * Get the file struct for a 404 error page. + * Tries some file names and returns NULL if none found. + * + * @param uri pointer that receives the actual file name URI + * @return file struct for the error page or NULL no matching file was found + */ +static struct fs_file * +http_get_404_file(struct http_state *hs, const char **uri) +{ + err_t err; + + *uri = "/404.html"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.html doesn't exist. Try 404.htm instead. */ + *uri = "/404.htm"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.htm doesn't exist either. Try 404.shtml instead. */ + *uri = "/404.shtml"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.htm doesn't exist either. Indicate to the caller that it should + * send back a default 404 page. + */ + *uri = NULL; + return NULL; + } + } + } + + return &hs->file_handle; +} + +#if LWIP_HTTPD_SUPPORT_POST +static err_t +http_handle_post_finished(struct http_state *hs) +{ +#if LWIP_HTTPD_POST_MANUAL_WND + /* Prevent multiple calls to httpd_post_finished, since it might have already + been called before from httpd_post_data_recved(). */ + if (hs->post_finished) { + return ERR_OK; + } + hs->post_finished = 1; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + /* application error or POST finished */ + /* NULL-terminate the buffer */ + http_uri_buf[0] = 0; + httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN); + return http_find_file(hs, http_uri_buf, 0); +} + +/** Pass received POST body data to the application and correctly handle + * returning a response document or closing the connection. + * ATTENTION: The application is responsible for the pbuf now, so don't free it! + * + * @param hs http connection state + * @param p pbuf to pass to the application + * @return ERR_OK if passed successfully, another err_t if the response file + * hasn't been found (after POST finished) + */ +static err_t +http_post_rxpbuf(struct http_state *hs, struct pbuf *p) +{ + err_t err; + + if (p != NULL) { + /* adjust remaining Content-Length */ + if (hs->post_content_len_left < p->tot_len) { + hs->post_content_len_left = 0; + } else { + hs->post_content_len_left -= p->tot_len; + } + } +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + /* prevent connection being closed if httpd_post_data_recved() is called nested */ + hs->unrecved_bytes++; +#endif + err = httpd_post_receive_data(hs, p); +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + hs->unrecved_bytes--; +#endif + if (err != ERR_OK) { + /* Ignore remaining content in case of application error */ + hs->post_content_len_left = 0; + } + if (hs->post_content_len_left == 0) { +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->unrecved_bytes != 0) { + return ERR_OK; + } +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + /* application error or POST finished */ + return http_handle_post_finished(hs); + } + + return ERR_OK; +} + +/** Handle a post request. Called from http_parse_request when method 'POST' + * is found. + * + * @param p The input pbuf (containing the POST header and body). + * @param hs The http connection state. + * @param data HTTP request (header and part of body) from input pbuf(s). + * @param data_len Size of 'data'. + * @param uri The HTTP URI parsed from input pbuf(s). + * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP + * header starts). + * @return ERR_OK: POST correctly parsed and accepted by the application. + * ERR_INPROGRESS: POST not completely parsed (no error yet) + * another err_t: Error parsing POST or denied by the application + */ +static err_t +http_post_request(struct pbuf *inp, struct http_state *hs, + char *data, u16_t data_len, char *uri, char *uri_end) +{ + err_t err; + /* search for end-of-header (first double-CRLF) */ + char* crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data)); + + if (crlfcrlf != NULL) { + /* search for "Content-Length: " */ +#define HTTP_HDR_CONTENT_LEN "Content-Length: " +#define HTTP_HDR_CONTENT_LEN_LEN 16 +#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN 10 + char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1)); + if (scontent_len != NULL) { + char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN); + if (scontent_len_end != NULL) { + int content_len; + char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN; + content_len = atoi(content_len_num); + if (content_len == 0) { + /* if atoi returns 0 on error, fix this */ + if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) { + content_len = -1; + } + } + if (content_len >= 0) { + /* adjust length of HTTP header passed to application */ + const char *hdr_start_after_uri = uri_end + 1; + u16_t hdr_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - data); + u16_t hdr_data_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri); + u8_t post_auto_wnd = 1; + http_uri_buf[0] = 0; + /* trim http header */ + *crlfcrlf = 0; + err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len, + http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd); + if (err == ERR_OK) { + /* try to pass in data of the first pbuf(s) */ + struct pbuf *q = inp; + u16_t start_offset = hdr_len; +#if LWIP_HTTPD_POST_MANUAL_WND + hs->no_auto_wnd = !post_auto_wnd; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + /* set the Content-Length to be received for this POST */ + hs->post_content_len_left = (u32_t)content_len; + + /* get to the pbuf where the body starts */ + while((q != NULL) && (q->len <= start_offset)) { + start_offset -= q->len; + q = q->next; + } + if (q != NULL) { + /* hide the remaining HTTP header */ + pbuf_header(q, -(s16_t)start_offset); +#if LWIP_HTTPD_POST_MANUAL_WND + if (!post_auto_wnd) { + /* already tcp_recved() this data... */ + hs->unrecved_bytes = q->tot_len; + } +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + pbuf_ref(q); + return http_post_rxpbuf(hs, q); + } else if (hs->post_content_len_left == 0) { + q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF); + return http_post_rxpbuf(hs, q); + } else { + return ERR_OK; + } + } else { + /* return file passed from application */ + return http_find_file(hs, http_uri_buf, 0); + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n", + content_len_num)); + return ERR_ARG; + } + } + } + /* If we come here, headers are fully received (double-crlf), but Content-Length + was not included. Since this is currently the only supported method, we have + to fail in this case! */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n")); + return ERR_ARG; + } + /* if we come here, the POST is incomplete */ +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + return ERR_INPROGRESS; +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + return ERR_ARG; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +} + +#if LWIP_HTTPD_POST_MANUAL_WND +/** A POST implementation can call this function to update the TCP window. + * This can be used to throttle data reception (e.g. when received data is + * programmed to flash and data is received faster than programmed). + * + * @param connection A connection handle passed to httpd_post_begin for which + * httpd_post_finished has *NOT* been called yet! + * @param recved_len Length of data received (for window update) + */ +void httpd_post_data_recved(void *connection, u16_t recved_len) +{ + struct http_state *hs = (struct http_state*)connection; + if (hs != NULL) { + if (hs->no_auto_wnd) { + u16_t len = recved_len; + if (hs->unrecved_bytes >= recved_len) { + hs->unrecved_bytes -= recved_len; + } else { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n")); + len = (u16_t)hs->unrecved_bytes; + hs->unrecved_bytes = 0; + } + if (hs->pcb != NULL) { + if (len != 0) { + tcp_recved(hs->pcb, len); + } + if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) { + /* finished handling POST */ + http_handle_post_finished(hs); + http_send(hs->pcb, hs); + } + } + } + } +} +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + +#endif /* LWIP_HTTPD_SUPPORT_POST */ + +#if LWIP_HTTPD_FS_ASYNC_READ +/** Try to send more data if file has been blocked before + * This is a callback function passed to fs_read_async(). + */ +static void +http_continue(void *connection) +{ + struct http_state *hs = (struct http_state*)connection; + if (hs && (hs->pcb) && (hs->handle)) { + LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL); + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n")); + if (http_send(hs->pcb, hs)) { + /* If we wrote anything to be sent, go ahead and send it now. */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); + tcp_output(hs->pcb); + } + } +} +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +/** + * When data has been received in the correct state, try to parse it + * as a HTTP request. + * + * @param inp the received pbuf + * @param hs the connection state + * @param pcb the tcp_pcb which received this packet + * @return ERR_OK if request was OK and hs has been initialized correctly + * ERR_INPROGRESS if request was OK so far but not fully received + * another err_t otherwise + */ +static err_t +http_parse_request(struct pbuf *inp, struct http_state *hs, struct tcp_pcb *pcb) +{ + char *data; + char *crlf; + u16_t data_len; + struct pbuf *p = inp; +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + u16_t clen; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +#if LWIP_HTTPD_SUPPORT_POST + err_t err; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + + LWIP_UNUSED_ARG(pcb); /* only used for post */ + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("hs != NULL", hs != NULL); + + if ((hs->handle != NULL) || (hs->file != NULL)) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n")); + /* already sending a file */ + /* @todo: abort? */ + return ERR_USE; + } + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + + LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len)); + + /* first check allowed characters in this pbuf? */ + + /* enqueue the pbuf */ + if (hs->req == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n")); + hs->req = p; + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n")); + pbuf_cat(hs->req, p); + } + /* increase pbuf ref counter as it is freed when we return but we want to + keep it on the req list */ + pbuf_ref(p); + + if (hs->req->next != NULL) { + data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH); + pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0); + data = httpd_req_buf; + } else +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + { + data = (char *)p->payload; + data_len = p->len; + if (p->len != p->tot_len) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n")); + } + } + + /* received enough data for minimal request? */ + if (data_len >= MIN_REQ_LEN) { + /* wait for CRLF before parsing anything */ + crlf = lwip_strnstr(data, CRLF, data_len); + if (crlf != NULL) { +#if LWIP_HTTPD_SUPPORT_POST + int is_post = 0; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + int is_09 = 0; + char *sp1, *sp2; + u16_t left_len, uri_len; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n")); + /* parse method */ + if (!strncmp(data, "GET ", 4)) { + sp1 = data + 3; + /* received GET request */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n")); +#if LWIP_HTTPD_SUPPORT_POST + } else if (!strncmp(data, "POST ", 5)) { + /* store request type */ + is_post = 1; + sp1 = data + 4; + /* received GET request */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n")); +#endif /* LWIP_HTTPD_SUPPORT_POST */ + } else { + /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ + data[4] = 0; + /* unsupported method! */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n", + data)); + return http_find_error_file(hs, 501); + } + /* if we come here, method is OK, parse URI */ + left_len = (u16_t)(data_len - ((sp1 +1) - data)); + sp2 = lwip_strnstr(sp1 + 1, " ", left_len); +#if LWIP_HTTPD_SUPPORT_V09 + if (sp2 == NULL) { + /* HTTP 0.9: respond with correct protocol version */ + sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len); + is_09 = 1; +#if LWIP_HTTPD_SUPPORT_POST + if (is_post) { + /* HTTP/0.9 does not support POST */ + goto badrequest; + } +#endif /* LWIP_HTTPD_SUPPORT_POST */ + } +#endif /* LWIP_HTTPD_SUPPORT_V09 */ + uri_len = (u16_t)(sp2 - (sp1 + 1)); + if ((sp2 != 0) && (sp2 > sp1)) { + /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */ + if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) { + char *uri = sp1 + 1; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + /* This is HTTP/1.0 compatible: for strict 1.1, a connection + would always be persistent unless "close" was specified. */ + if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) || + lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) { + hs->keepalive = 1; + } else { + hs->keepalive = 0; + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ + *sp1 = 0; + uri[uri_len] = 0; + LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n", + data, uri)); +#if LWIP_HTTPD_SUPPORT_POST + if (is_post) { +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + struct pbuf *q = hs->req; +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + struct pbuf *q = inp; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + err = http_post_request(q, hs, data, data_len, uri, sp2); + if (err != ERR_OK) { + /* restore header for next try */ + *sp1 = ' '; + *sp2 = ' '; + uri[uri_len] = ' '; + } + if (err == ERR_ARG) { + goto badrequest; + } + return err; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + return http_find_file(hs, uri, is_09); + } + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n")); + } + } + } + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + clen = pbuf_clen(hs->req); + if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) && + (clen <= LWIP_HTTPD_REQ_QUEUELEN)) { + /* request not fully received (too short or CRLF is missing) */ + return ERR_INPROGRESS; + } else +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + { +#if LWIP_HTTPD_SUPPORT_POST +badrequest: +#endif /* LWIP_HTTPD_SUPPORT_POST */ + LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n")); + /* could not parse request */ + return http_find_error_file(hs, 400); + } +} + +/** Try to find the file specified by uri and, if found, initialize hs + * accordingly. + * + * @param hs the connection state + * @param uri the HTTP header URI + * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_find_file(struct http_state *hs, const char *uri, int is_09) +{ + size_t loop; + struct fs_file *file = NULL; + char *params = NULL; + err_t err; +#if LWIP_HTTPD_CGI + int i; +#endif /* LWIP_HTTPD_CGI */ +#if !LWIP_HTTPD_SSI + const +#endif /* !LWIP_HTTPD_SSI */ + /* By default, assume we will not be processing server-side-includes tags */ + u8_t tag_check = 0; + + /* Have we been asked for the default file (in root or a directory) ? */ +#if LWIP_HTTPD_MAX_REQUEST_URI_LEN + size_t uri_len = strlen(uri); + if ((uri_len > 0) && (uri[uri_len-1] == '/') && + ((uri != http_uri_buf) || (uri_len == 1))) { + size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1); + if (copy_len > 0) { + MEMCPY(http_uri_buf, uri, copy_len); + http_uri_buf[copy_len] = 0; + } +#else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ + if ((uri[0] == '/') && (uri[1] == 0)) { +#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ + /* Try each of the configured default filenames until we find one + that exists. */ + for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) { + const char* file_name; +#if LWIP_HTTPD_MAX_REQUEST_URI_LEN + if (copy_len > 0) { + size_t len_left = sizeof(http_uri_buf) - copy_len - 1; + if (len_left > 0) { + size_t name_len = strlen(g_psDefaultFilenames[loop].name); + size_t name_copy_len = LWIP_MIN(len_left, name_len); + MEMCPY(&http_uri_buf[copy_len], g_psDefaultFilenames[loop].name, name_copy_len); + } + file_name = http_uri_buf; + } else +#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ + { + file_name = g_psDefaultFilenames[loop].name; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name)); + err = fs_open(&hs->file_handle, file_name); + if(err == ERR_OK) { + uri = file_name; + file = &hs->file_handle; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n")); +#if LWIP_HTTPD_SSI + tag_check = g_psDefaultFilenames[loop].shtml; +#endif /* LWIP_HTTPD_SSI */ + break; + } + } + } + if (file == NULL) { + /* No - we've been asked for a specific file. */ + /* First, isolate the base URI (without any parameters) */ + params = (char *)strchr(uri, '?'); + if (params != NULL) { + /* URI contains parameters. NULL-terminate the base URI */ + *params = '\0'; + params++; + } + +#if LWIP_HTTPD_CGI + http_cgi_paramcount = -1; + /* Does the base URI we have isolated correspond to a CGI handler? */ + if (g_iNumCGIs && g_pCGIs) { + for (i = 0; i < g_iNumCGIs; i++) { + if (strcmp(uri, g_pCGIs[i].pcCGIName) == 0) { + /* + * We found a CGI that handles this URI so extract the + * parameters and call the handler. + */ + http_cgi_paramcount = extract_uri_parameters(hs, params); + uri = g_pCGIs[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params, + hs->param_vals); + break; + } + } + } +#endif /* LWIP_HTTPD_CGI */ + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri)); + + err = fs_open(&hs->file_handle, uri); + if (err == ERR_OK) { + file = &hs->file_handle; + } else { + file = http_get_404_file(hs, &uri); + } +#if LWIP_HTTPD_SSI + if (file != NULL) { + /* See if we have been asked for an shtml file and, if so, + enable tag checking. */ + const char* ext = NULL, *sub; + char* param = (char*)strstr(uri, "?"); + if (param != NULL) { + /* separate uri from parameters for now, set back later */ + *param = 0; + } + sub = uri; + ext = uri; + for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, ".")) + { + ext = sub; + sub++; + } + tag_check = 0; + for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { + if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) { + tag_check = 1; + break; + } + } + if (param != NULL) { + *param = '?'; + } + } +#endif /* LWIP_HTTPD_SSI */ + } + if (file == NULL) { + /* None of the default filenames exist so send back a 404 page */ + file = http_get_404_file(hs, &uri); + } + return http_init_file(hs, file, is_09, uri, tag_check, params); +} + +/** Initialize a http connection with a file to send (if found). + * Called by http_find_file and http_find_error_file. + * + * @param hs http connection state + * @param file file structure to send (or NULL if not found) + * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) + * @param uri the HTTP header URI + * @param tag_check enable SSI tag checking + * @param params != NULL if URI has parameters (separated by '?') + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, + u8_t tag_check, char* params) +{ + if (file != NULL) { + /* file opened, initialise struct http_state */ +#if LWIP_HTTPD_SSI + if (tag_check) { + struct http_ssi_state *ssi = http_ssi_state_alloc(); + if (ssi != NULL) { + ssi->tag_index = 0; + ssi->tag_state = TAG_NONE; + ssi->parsed = file->data; + ssi->parse_left = file->len; + ssi->tag_end = file->data; + hs->ssi = ssi; + } + } +#else /* LWIP_HTTPD_SSI */ + LWIP_UNUSED_ARG(tag_check); +#endif /* LWIP_HTTPD_SSI */ + hs->handle = file; + hs->file = file->data; + LWIP_ASSERT("File length must be positive!", (file->len >= 0)); +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file && (file->data == NULL)) { + /* custom file, need to read data first (via fs_read_custom) */ + hs->left = 0; + } else +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + { + hs->left = file->len; + } + hs->retries = 0; +#if LWIP_HTTPD_TIMING + hs->time_started = sys_now(); +#endif /* LWIP_HTTPD_TIMING */ +#if !LWIP_HTTPD_DYNAMIC_HEADERS + LWIP_ASSERT("HTTP headers not included in file system", + (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0); +#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_SUPPORT_V09 + if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) { + /* HTTP/0.9 responses are sent without HTTP header, + search for the end of the header. */ + char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left); + if (file_start != NULL) { + size_t diff = file_start + 4 - hs->file; + hs->file += diff; + hs->left -= (u32_t)diff; + } + } +#endif /* LWIP_HTTPD_SUPPORT_V09*/ +#if LWIP_HTTPD_CGI_SSI + if (params != NULL) { + /* URI contains parameters, call generic CGI handler */ + int count; +#if LWIP_HTTPD_CGI + if (http_cgi_paramcount >= 0) { + count = http_cgi_paramcount; + } else +#endif + { + count = extract_uri_parameters(hs, params); + } + httpd_cgi_handler(uri, count, http_cgi_params, http_cgi_param_vals +#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE + , hs->handle->state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); + } +#else /* LWIP_HTTPD_CGI_SSI */ + LWIP_UNUSED_ARG(params); +#endif /* LWIP_HTTPD_CGI_SSI */ + } else { + hs->handle = NULL; + hs->file = NULL; + hs->left = 0; + hs->retries = 0; + } +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Determine the HTTP headers to send based on the file extension of + * the requested URI. */ + if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) { + get_http_headers(hs, uri); + } +#else /* LWIP_HTTPD_DYNAMIC_HEADERS */ + LWIP_UNUSED_ARG(uri); +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive) { +#if LWIP_HTTPD_SSI + if (hs->ssi != NULL) { + hs->keepalive = 0; + } else +#endif /* LWIP_HTTPD_SSI */ + { + if ((hs->handle != NULL) && + ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) { + hs->keepalive = 0; + } + } + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + return ERR_OK; +} + +/** + * The pcb had an error and is already deallocated. + * The argument might still be valid (if != NULL). + */ +static void +http_err(void *arg, err_t err) +{ + struct http_state *hs = (struct http_state *)arg; + LWIP_UNUSED_ARG(err); + + LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err))); + + if (hs != NULL) { + http_state_free(hs); + } +} + +/** + * Data has been sent and acknowledged by the remote host. + * This means that more data can be sent. + */ +static err_t +http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct http_state *hs = (struct http_state *)arg; + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void*)pcb)); + + LWIP_UNUSED_ARG(len); + + if (hs == NULL) { + return ERR_OK; + } + + hs->retries = 0; + + http_send(pcb, hs); + + return ERR_OK; +} + +/** + * The poll function is called every 2nd second. + * If there has been no data sent (which resets the retries) in 8 seconds, close. + * If the last portion of a file has not been sent in 2 seconds, close. + * + * This could be increased, but we don't want to waste resources for bad connections. + */ +static err_t +http_poll(void *arg, struct tcp_pcb *pcb) +{ + struct http_state *hs = (struct http_state *)arg; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n", + (void*)pcb, (void*)hs, tcp_debug_state_str(pcb->state))); + + if (hs == NULL) { + err_t closed; + /* arg is null, close. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n")); + closed = http_close_conn(pcb, NULL); + LWIP_UNUSED_ARG(closed); +#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR + if (closed == ERR_MEM) { + tcp_abort(pcb); + return ERR_ABRT; + } +#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */ + return ERR_OK; + } else { + hs->retries++; + if (hs->retries == HTTPD_MAX_RETRIES) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n")); + http_close_conn(pcb, hs); + return ERR_OK; + } + + /* If this connection has a file open, try to send some more data. If + * it has not yet received a GET request, don't do this since it will + * cause the connection to close immediately. */ + if(hs && (hs->handle)) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n")); + if(http_send(pcb, hs)) { + /* If we wrote anything to be sent, go ahead and send it now. */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); + tcp_output(pcb); + } + } + } + + return ERR_OK; +} + +/** + * Data has been received on this pcb. + * For HTTP 1.0, this should normally only happen once (if the request fits in one packet). + */ +static err_t +http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct http_state *hs = (struct http_state *)arg; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb, + (void*)p, lwip_strerr(err))); + + if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) { + /* error or closed by other side? */ + if (p != NULL) { + /* Inform TCP that we have taken the data. */ + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + if (hs == NULL) { + /* this should not happen, only to be robust */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n")); + } + http_close_conn(pcb, hs); + return ERR_OK; + } + +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->no_auto_wnd) { + hs->unrecved_bytes += p->tot_len; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + { + /* Inform TCP that we have taken the data. */ + tcp_recved(pcb, p->tot_len); + } + +#if LWIP_HTTPD_SUPPORT_POST + if (hs->post_content_len_left > 0) { + /* reset idle counter when POST data is received */ + hs->retries = 0; + /* this is data for a POST, pass the complete pbuf to the application */ + http_post_rxpbuf(hs, p); + /* pbuf is passed to the application, don't free it! */ + if (hs->post_content_len_left == 0) { + /* all data received, send response or close connection */ + http_send(pcb, hs); + } + return ERR_OK; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + if (hs->handle == NULL) { + err_t parsed = http_parse_request(p, hs, pcb); + LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK + || parsed == ERR_INPROGRESS ||parsed == ERR_ARG || parsed == ERR_USE); +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + if (parsed != ERR_INPROGRESS) { + /* request fully parsed or error */ + if (hs->req != NULL) { + pbuf_free(hs->req); + hs->req = NULL; + } + } +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + pbuf_free(p); + if (parsed == ERR_OK) { +#if LWIP_HTTPD_SUPPORT_POST + if (hs->post_content_len_left == 0) +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void*)hs->file, hs->left)); + http_send(pcb, hs); + } + } else if (parsed == ERR_ARG) { + /* @todo: close on ERR_USE? */ + http_close_conn(pcb, hs); + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n")); + /* already sending but still receiving data, we might want to RST here? */ + pbuf_free(p); + } + } + return ERR_OK; +} + +/** + * A new incoming connection has been accepted. + */ +static err_t +http_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct http_state *hs; + LWIP_UNUSED_ARG(err); + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void*)pcb, arg)); + + if ((err != ERR_OK) || (pcb == NULL)) { + return ERR_VAL; + } + + /* Set priority */ + tcp_setprio(pcb, HTTPD_TCP_PRIO); + + /* Allocate memory for the structure that holds the state of the + connection - initialized by that function. */ + hs = http_state_alloc(); + if (hs == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n")); + return ERR_MEM; + } + hs->pcb = pcb; + + /* Tell TCP that this is the structure we wish to be passed for our + callbacks. */ + tcp_arg(pcb, hs); + + /* Set up the various callback functions */ + tcp_recv(pcb, http_recv); + tcp_err(pcb, http_err); + tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); + tcp_sent(pcb, http_sent); + + return ERR_OK; +} + +/** + * @ingroup httpd + * Initialize the httpd: set up a listening PCB and bind it to the defined port + */ +void +httpd_init(void) +{ + struct tcp_pcb *pcb; + err_t err; + +#if HTTPD_USE_MEM_POOL + LWIP_MEMPOOL_INIT(HTTPD_STATE); +#if LWIP_HTTPD_SSI + LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE); +#endif +#endif + LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n")); + + pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL); + tcp_setprio(pcb, HTTPD_TCP_PRIO); + /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */ + err = tcp_bind(pcb, IP_ANY_TYPE, HTTPD_SERVER_PORT); + LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK); + pcb = tcp_listen(pcb); + LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL); + tcp_accept(pcb, http_accept); +} + +#if LWIP_HTTPD_SSI +/** + * Set the SSI handler function. + * + * @param ssi_handler the SSI handler function + * @param tags an array of SSI tag strings to search for in SSI-enabled files + * @param num_tags number of tags in the 'tags' array + */ +void +http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags) +{ + LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n")); + + LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL); + g_pfnSSIHandler = ssi_handler; + +#if LWIP_HTTPD_SSI_RAW + LWIP_UNUSED_ARG(tags); + LWIP_UNUSED_ARG(num_tags); +#else /* LWIP_HTTPD_SSI_RAW */ + LWIP_ASSERT("no tags given", tags != NULL); + LWIP_ASSERT("invalid number of tags", num_tags > 0); + + g_ppcTags = tags; + g_iNumTags = num_tags; +#endif /* !LWIP_HTTPD_SSI_RAW */ +} +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_CGI +/** + * Set an array of CGI filenames/handler functions + * + * @param cgis an array of CGI filenames/handler functions + * @param num_handlers number of elements in the 'cgis' array + */ +void +http_set_cgi_handlers(const tCGI *cgis, int num_handlers) +{ + LWIP_ASSERT("no cgis given", cgis != NULL); + LWIP_ASSERT("invalid number of handlers", num_handlers > 0); + + g_pCGIs = cgis; + g_iNumCGIs = num_handlers; +} +#endif /* LWIP_HTTPD_CGI */ + +#endif /* LWIP_TCP && LWIP_CALLBACK_API */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/httpd_structs.h b/sdk/Src/Network/lwip2.0.3/apps/httpd/httpd_structs.h new file mode 100644 index 0000000..fbd135a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/httpd_structs.h @@ -0,0 +1,114 @@ +#ifndef LWIP_HTTPD_STRUCTS_H +#define LWIP_HTTPD_STRUCTS_H + +#include "lwip/apps/httpd.h" + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/** This struct is used for a list of HTTP header strings for various + * filename extensions. */ +typedef struct +{ + const char *extension; + const char *content_type; +} tHTTPHeader; + +/** A list of strings used in HTTP headers (see RFC 1945 HTTP/1.0 and + * RFC 2616 HTTP/1.1 for header field definitions) */ +static const char * const g_psHTTPHeaderStrings[] = +{ + "HTTP/1.0 200 OK\r\n", + "HTTP/1.0 404 File not found\r\n", + "HTTP/1.0 400 Bad Request\r\n", + "HTTP/1.0 501 Not Implemented\r\n", + "HTTP/1.1 200 OK\r\n", + "HTTP/1.1 404 File not found\r\n", + "HTTP/1.1 400 Bad Request\r\n", + "HTTP/1.1 501 Not Implemented\r\n", + "Content-Length: ", + "Connection: Close\r\n", + "Connection: keep-alive\r\n", + "Connection: keep-alive\r\nContent-Length: ", + "Server: "HTTPD_SERVER_AGENT"\r\n", + "\r\n

404: The requested file cannot be found.

\r\n" +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + ,"Connection: keep-alive\r\nContent-Length: 77\r\n\r\n

404: The requested file cannot be found.

\r\n" +#endif +}; + +/* Indexes into the g_psHTTPHeaderStrings array */ +#define HTTP_HDR_OK 0 /* 200 OK */ +#define HTTP_HDR_NOT_FOUND 1 /* 404 File not found */ +#define HTTP_HDR_BAD_REQUEST 2 /* 400 Bad request */ +#define HTTP_HDR_NOT_IMPL 3 /* 501 Not Implemented */ +#define HTTP_HDR_OK_11 4 /* 200 OK */ +#define HTTP_HDR_NOT_FOUND_11 5 /* 404 File not found */ +#define HTTP_HDR_BAD_REQUEST_11 6 /* 400 Bad request */ +#define HTTP_HDR_NOT_IMPL_11 7 /* 501 Not Implemented */ +#define HTTP_HDR_CONTENT_LENGTH 8 /* Content-Length: (HTTP 1.0)*/ +#define HTTP_HDR_CONN_CLOSE 9 /* Connection: Close (HTTP 1.1) */ +#define HTTP_HDR_CONN_KEEPALIVE 10 /* Connection: keep-alive (HTTP 1.1) */ +#define HTTP_HDR_KEEPALIVE_LEN 11 /* Connection: keep-alive + Content-Length: (HTTP 1.1)*/ +#define HTTP_HDR_SERVER 12 /* Server: HTTPD_SERVER_AGENT */ +#define DEFAULT_404_HTML 13 /* default 404 body */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE +#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */ +#endif + + +#define HTTP_HDR_HTML "Content-type: text/html\r\n\r\n" +#define HTTP_HDR_SSI "Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n" +#define HTTP_HDR_GIF "Content-type: image/gif\r\n\r\n" +#define HTTP_HDR_PNG "Content-type: image/png\r\n\r\n" +#define HTTP_HDR_JPG "Content-type: image/jpeg\r\n\r\n" +#define HTTP_HDR_BMP "Content-type: image/bmp\r\n\r\n" +#define HTTP_HDR_ICO "Content-type: image/x-icon\r\n\r\n" +#define HTTP_HDR_APP "Content-type: application/octet-stream\r\n\r\n" +#define HTTP_HDR_JS "Content-type: application/javascript\r\n\r\n" +#define HTTP_HDR_RA "Content-type: application/javascript\r\n\r\n" +#define HTTP_HDR_CSS "Content-type: text/css\r\n\r\n" +#define HTTP_HDR_SWF "Content-type: application/x-shockwave-flash\r\n\r\n" +#define HTTP_HDR_XML "Content-type: text/xml\r\n\r\n" +#define HTTP_HDR_PDF "Content-type: application/pdf\r\n\r\n" +#define HTTP_HDR_JSON "Content-type: application/json\r\n\r\n" + +#define HTTP_HDR_DEFAULT_TYPE "Content-type: text/plain\r\n\r\n" + +/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES + * and http://www.iana.org/assignments/media-types for registered content types + * and subtypes) */ +static const tHTTPHeader g_psHTTPHeaders[] = +{ + { "html", HTTP_HDR_HTML}, + { "htm", HTTP_HDR_HTML}, + { "shtml",HTTP_HDR_SSI}, + { "shtm", HTTP_HDR_SSI}, + { "ssi", HTTP_HDR_SSI}, + { "gif", HTTP_HDR_GIF}, + { "png", HTTP_HDR_PNG}, + { "jpg", HTTP_HDR_JPG}, + { "bmp", HTTP_HDR_BMP}, + { "ico", HTTP_HDR_ICO}, + { "class",HTTP_HDR_APP}, + { "cls", HTTP_HDR_APP}, + { "js", HTTP_HDR_JS}, + { "ram", HTTP_HDR_RA}, + { "css", HTTP_HDR_CSS}, + { "swf", HTTP_HDR_SWF}, + { "xml", HTTP_HDR_XML}, + { "xsl", HTTP_HDR_XML}, + { "pdf", HTTP_HDR_PDF}, + { "json", HTTP_HDR_JSON} +}; + +#define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader)) + +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +#if LWIP_HTTPD_SSI +static const char * const g_pcSSIExtensions[] = { + ".shtml", ".shtm", ".ssi", ".xml" +}; +#define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *)) +#endif /* LWIP_HTTPD_SSI */ + +#endif /* LWIP_HTTPD_STRUCTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/makefsdata b/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/makefsdata new file mode 100755 index 0000000..981c3d1 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/makefsdata @@ -0,0 +1,97 @@ +#!/usr/bin/perl + +open(OUTPUT, "> fsdata.c"); + +chdir("fs"); +open(FILES, "find . -type f |"); + +while($file = ) { + + # Do not include files in CVS directories nor backup files. + if($file =~ /(CVS|~)/) { + next; + } + + chop($file); + + open(HEADER, "> /tmp/header") || die $!; + if($file =~ /404/) { + print(HEADER "HTTP/1.0 404 File not found\r\n"); + } else { + print(HEADER "HTTP/1.0 200 OK\r\n"); + } + print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n"); + if($file =~ /\.html$/) { + print(HEADER "Content-type: text/html\r\n"); + } elsif($file =~ /\.gif$/) { + print(HEADER "Content-type: image/gif\r\n"); + } elsif($file =~ /\.png$/) { + print(HEADER "Content-type: image/png\r\n"); + } elsif($file =~ /\.jpg$/) { + print(HEADER "Content-type: image/jpeg\r\n"); + } elsif($file =~ /\.class$/) { + print(HEADER "Content-type: application/octet-stream\r\n"); + } elsif($file =~ /\.ram$/) { + print(HEADER "Content-type: audio/x-pn-realaudio\r\n"); + } else { + print(HEADER "Content-type: text/plain\r\n"); + } + print(HEADER "\r\n"); + close(HEADER); + + unless($file =~ /\.plain$/ || $file =~ /cgi/) { + system("cat /tmp/header $file > /tmp/file"); + } else { + system("cp $file /tmp/file"); + } + + open(FILE, "/tmp/file"); + unlink("/tmp/file"); + unlink("/tmp/header"); + + $file =~ s/\.//; + $fvar = $file; + $fvar =~ s-/-_-g; + $fvar =~ s-\.-_-g; + print(OUTPUT "static const unsigned char data".$fvar."[] = {\n"); + print(OUTPUT "\t/* $file */\n\t"); + for($j = 0; $j < length($file); $j++) { + printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1))); + } + printf(OUTPUT "0,\n"); + + + $i = 0; + while(read(FILE, $data, 1)) { + if($i == 0) { + print(OUTPUT "\t"); + } + printf(OUTPUT "%#02x, ", unpack("C", $data)); + $i++; + if($i == 10) { + print(OUTPUT "\n"); + $i = 0; + } + } + print(OUTPUT "};\n\n"); + close(FILE); + push(@fvars, $fvar); + push(@files, $file); +} + +for($i = 0; $i < @fvars; $i++) { + $file = $files[$i]; + $fvar = $fvars[$i]; + + if($i == 0) { + $prevfile = "NULL"; + } else { + $prevfile = "file" . $fvars[$i - 1]; + } + print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, "); + print(OUTPUT "data$fvar + ". (length($file) + 1) .", "); + print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n"); +} + +print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n"); +print(OUTPUT "#define FS_NUMFILES $i\n"); diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/makefsdata.c b/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/makefsdata.c new file mode 100644 index 0000000..934e721 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/makefsdata.c @@ -0,0 +1,1033 @@ +/** + * makefsdata: Converts a directory structure for use with the lwIP httpd. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jim Pettinato + * Simon Goldschmidt + * + * @todo: + * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and + * PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments + */ + +#include +#include +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#else +#include +#endif +#include +#include +#include +#include + +/** Makefsdata can generate *all* files deflate-compressed (where file size shrinks). + * Since nearly all browsers support this, this is a good way to reduce ROM size. + * To compress the files, "miniz.c" must be downloaded seperately. + */ +#ifndef MAKEFS_SUPPORT_DEFLATE +#define MAKEFS_SUPPORT_DEFLATE 0 +#endif + +#define COPY_BUFSIZE (1024*1024) /* 1 MByte */ + +#if MAKEFS_SUPPORT_DEFLATE +#include "../miniz.c" + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +/* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression. + COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */ +#define COMP_OUT_BUF_SIZE COPY_BUFSIZE + +/* OUT_BUF_SIZE is the size of the output buffer used during decompression. + OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */ +#define OUT_BUF_SIZE COPY_BUFSIZE +static uint8 s_outbuf[OUT_BUF_SIZE]; +static uint8 s_checkbuf[OUT_BUF_SIZE]; + +/* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). + This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */ +tdefl_compressor g_deflator; +tinfl_decompressor g_inflator; + +int deflate_level = 10; /* default compression level, can be changed via command line */ +#define USAGE_ARG_DEFLATE " [-defl<:compr_level>]" +#else /* MAKEFS_SUPPORT_DEFLATE */ +#define USAGE_ARG_DEFLATE "" +#endif /* MAKEFS_SUPPORT_DEFLATE */ + +/* Compatibility defines Win32 vs. DOS */ +#ifdef WIN32 + +#define FIND_T WIN32_FIND_DATAA +#define FIND_T_FILENAME(fInfo) (fInfo.cFileName) +#define FIND_T_IS_DIR(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) +#define FIND_T_IS_FILE(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) +#define FIND_RET_T HANDLE +#define FINDFIRST_FILE(path, result) FindFirstFileA(path, result) +#define FINDFIRST_DIR(path, result) FindFirstFileA(path, result) +#define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result) +#define FINDFIRST_SUCCEEDED(ret) (ret != INVALID_HANDLE_VALUE) +#define FINDNEXT_SUCCEEDED(ret) (ret == TRUE) + +#define GETCWD(path, len) GetCurrentDirectoryA(len, path) +#define CHDIR(path) SetCurrentDirectoryA(path) +#define CHDIR_SUCCEEDED(ret) (ret == TRUE) + +#else + +#define FIND_T struct ffblk +#define FIND_T_FILENAME(fInfo) (fInfo.ff_name) +#define FIND_T_IS_DIR(fInfo) ((fInfo.ff_attrib & FA_DIREC) == FA_DIREC) +#define FIND_T_IS_FILE(fInfo) (1) +#define FIND_RET_T int +#define FINDFIRST_FILE(path, result) findfirst(path, result, FA_ARCH) +#define FINDFIRST_DIR(path, result) findfirst(path, result, FA_DIREC) +#define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result) +#define FINDFIRST_SUCCEEDED(ret) (ret == 0) +#define FINDNEXT_SUCCEEDED(ret) (ret == 0) + +#define GETCWD(path, len) getcwd(path, len) +#define CHDIR(path) chdir(path) +#define CHDIR_SUCCEEDED(ret) (ret == 0) + +#endif + +#define NEWLINE "\r\n" +#define NEWLINE_LEN 2 + +/* define this to get the header variables we use to build HTTP headers */ +#define LWIP_HTTPD_DYNAMIC_HEADERS 1 +#define LWIP_HTTPD_SSI 1 +#include "lwip/init.h" +#include "../httpd_structs.h" +#include "lwip/apps/fs.h" + +#include "../core/inet_chksum.c" +#include "../core/def.c" + +/** (Your server name here) */ +const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n"; +char serverIDBuffer[1024]; + +/* change this to suit your MEM_ALIGNMENT */ +#define PAYLOAD_ALIGNMENT 4 +/* set this to 0 to prevent aligning payload */ +#define ALIGN_PAYLOAD 1 +/* define this to a type that has the required alignment */ +#define PAYLOAD_ALIGN_TYPE "unsigned int" +static int payload_alingment_dummy_counter = 0; + +#define HEX_BYTES_PER_LINE 16 + +#define MAX_PATH_LEN 256 + +struct file_entry +{ + struct file_entry* next; + const char* filename_c; +}; + +int process_sub(FILE *data_file, FILE *struct_file); +int process_file(FILE *data_file, FILE *struct_file, const char *filename); +int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len, + u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed); +int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i); +int s_put_ascii(char *buf, const char *ascii_string, int len, int *i); +void concat_files(const char *file1, const char *file2, const char *targetfile); +int check_path(char* path, size_t size); + +/* 5 bytes per char + 3 bytes per line */ +static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)]; + +char curSubdir[MAX_PATH_LEN]; +char lastFileVar[MAX_PATH_LEN]; +char hdr_buf[4096]; + +unsigned char processSubs = 1; +unsigned char includeHttpHeader = 1; +unsigned char useHttp11 = 0; +unsigned char supportSsi = 1; +unsigned char precalcChksum = 0; +unsigned char includeLastModified = 0; +#if MAKEFS_SUPPORT_DEFLATE +unsigned char deflateNonSsiFiles = 0; +size_t deflatedBytesReduced = 0; +size_t overallDataBytes = 0; +#endif + +struct file_entry* first_file = NULL; +struct file_entry* last_file = NULL; + +static void print_usage(void) +{ + printf(" Usage: htmlgen [targetdir] [-s] [-e] [-i] [-11] [-nossi] [-c] [-f:] [-m] [-svr:]" USAGE_ARG_DEFLATE NEWLINE NEWLINE); + printf(" targetdir: relative or absolute path to files to convert" NEWLINE); + printf(" switch -s: toggle processing of subdirectories (default is on)" NEWLINE); + printf(" switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE); + printf(" switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE); + printf(" switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE); + printf(" switch -c: precalculate checksums for all pages (default is off)" NEWLINE); + printf(" switch -f: target filename (default is \"fsdata.c\")" NEWLINE); + printf(" switch -m: include \"Last-Modified\" header based on file time" NEWLINE); + printf(" switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE); +#if MAKEFS_SUPPORT_DEFLATE + printf(" switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE); + printf(" ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE); +#endif + printf(" if targetdir not specified, htmlgen will attempt to" NEWLINE); + printf(" process files in subdirectory 'fs'" NEWLINE); +} + +int main(int argc, char *argv[]) +{ + char path[MAX_PATH_LEN]; + char appPath[MAX_PATH_LEN]; + FILE *data_file; + FILE *struct_file; + int filesProcessed; + int i; + char targetfile[MAX_PATH_LEN]; + strcpy(targetfile, "fsdata.c"); + + memset(path, 0, sizeof(path)); + memset(appPath, 0, sizeof(appPath)); + + printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE); + printf(" by Jim Pettinato - circa 2003 " NEWLINE); + printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE); + + strcpy(path, "fs"); + for (i = 1; i < argc; i++) { + if (argv[i] == NULL) { + continue; + } + if (argv[i][0] == '-') { + if (strstr(argv[i], "-svr:") == argv[i]) { + snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]); + serverID = serverIDBuffer; + printf("Using Server-ID: \"%s\"\n", serverID); + } else if (strstr(argv[i], "-s") == argv[i]) { + processSubs = 0; + } else if (strstr(argv[i], "-e") == argv[i]) { + includeHttpHeader = 0; + } else if (strstr(argv[i], "-11") == argv[i]) { + useHttp11 = 1; + } else if (strstr(argv[i], "-nossi") == argv[i]) { + supportSsi = 0; + } else if (strstr(argv[i], "-c") == argv[i]) { + precalcChksum = 1; + } else if (strstr(argv[i], "-f:") == argv[i]) { + strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1); + targetfile[sizeof(targetfile) - 1] = 0; + printf("Writing to file \"%s\"\n", targetfile); + } else if (strstr(argv[i], "-m") == argv[i]) { + includeLastModified = 1; + } else if (strstr(argv[i], "-defl") == argv[i]) { +#if MAKEFS_SUPPORT_DEFLATE + char* colon = strstr(argv[i], ":"); + if (colon) { + if (colon[1] != 0) { + int defl_level = atoi(&colon[1]); + if ((defl_level >= 0) && (defl_level <= 10)) { + deflate_level = defl_level; + } else { + printf("ERROR: deflate level must be [0..10]" NEWLINE); + exit(0); + } + } + } + deflateNonSsiFiles = 1; + printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level); +#else + printf("WARNING: Deflate support is disabled\n"); +#endif + } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) { + print_usage(); + exit(0); + } + } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) { + print_usage(); + exit(0); + } else { + strncpy(path, argv[i], sizeof(path)-1); + path[sizeof(path)-1] = 0; + } + } + + if (!check_path(path, sizeof(path))) { + printf("Invalid path: \"%s\"." NEWLINE, path); + exit(-1); + } + + GETCWD(appPath, MAX_PATH_LEN); + /* if command line param or subdir named 'fs' not found spout usage verbiage */ + if (!CHDIR_SUCCEEDED(CHDIR(path))) { + /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */ + printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path); + print_usage(); + exit(-1); + } + CHDIR(appPath); + + printf("HTTP %sheader will %s statically included." NEWLINE, + (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""), + (includeHttpHeader ? "be" : "not be")); + + sprintf(curSubdir, ""); /* start off in web page's root directory - relative paths */ + printf(" Processing all files in directory %s", path); + if (processSubs) { + printf(" and subdirectories..." NEWLINE NEWLINE); + } else { + printf("..." NEWLINE NEWLINE); + } + + data_file = fopen("fsdata.tmp", "wb"); + if (data_file == NULL) { + printf("Failed to create file \"fsdata.tmp\"\n"); + exit(-1); + } + struct_file = fopen("fshdr.tmp", "wb"); + if (struct_file == NULL) { + printf("Failed to create file \"fshdr.tmp\"\n"); + fclose(data_file); + exit(-1); + } + + CHDIR(path); + + fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE); + fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE); + fprintf(data_file, "#include \"fsdata.h\"" NEWLINE NEWLINE NEWLINE); + + fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE); + /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */ + fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE); + /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */ + fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE); + + /* define alignment defines */ +#if ALIGN_PAYLOAD + fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE); +#endif + fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE" NEWLINE "#define FSDATA_ALIGN_PRE" NEWLINE "#endif" NEWLINE); + fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE); +#if ALIGN_PAYLOAD + fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE); +#endif + + sprintf(lastFileVar, "NULL"); + + filesProcessed = process_sub(data_file, struct_file); + + /* data_file now contains all of the raw data.. now append linked list of + * file header structs to allow embedded app to search for a file name */ + fprintf(data_file, NEWLINE NEWLINE); + fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar); + fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed); + + fclose(data_file); + fclose(struct_file); + + CHDIR(appPath); + /* append struct_file to data_file */ + printf(NEWLINE "Creating target file..." NEWLINE NEWLINE); + concat_files("fsdata.tmp", "fshdr.tmp", targetfile); + + /* if succeeded, delete the temporary files */ + if (remove("fsdata.tmp") != 0) { + printf("Warning: failed to delete fsdata.tmp\n"); + } + if (remove("fshdr.tmp") != 0) { + printf("Warning: failed to delete fshdr.tmp\n"); + } + + printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed); +#if MAKEFS_SUPPORT_DEFLATE + if (deflateNonSsiFiles) { + printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE, + (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced*100.0)/overallDataBytes)); + } +#endif + printf(NEWLINE); + + while (first_file != NULL) { + struct file_entry* fe = first_file; + first_file = fe->next; + free(fe); + } + + return 0; +} + +int check_path(char* path, size_t size) +{ + size_t slen; + if (path[0] == 0) { + /* empty */ + return 0; + } + slen = strlen(path); + if (slen >= size) { + /* not NULL-terminated */ + return 0; + } + while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) { + /* path should not end with trailing backslash */ + path[slen] = 0; + slen--; + } + if (slen == 0) { + return 0; + } + return 1; +} + +static void copy_file(const char *filename_in, FILE *fout) +{ + FILE *fin; + size_t len; + void* buf; + fin = fopen(filename_in, "rb"); + if (fin == NULL) { + printf("Failed to open file \"%s\"\n", filename_in); + exit(-1); + } + buf = malloc(COPY_BUFSIZE); + while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) { + fwrite(buf, 1, len, fout); + } + free(buf); + fclose(fin); +} + +void concat_files(const char *file1, const char *file2, const char *targetfile) +{ + FILE *fout; + fout = fopen(targetfile, "wb"); + if (fout == NULL) { + printf("Failed to open file \"%s\"\n", targetfile); + exit(-1); + } + copy_file(file1, fout); + copy_file(file2, fout); + fclose(fout); +} + +int process_sub(FILE *data_file, FILE *struct_file) +{ + FIND_T fInfo; + FIND_RET_T fret; + int filesProcessed = 0; + + if (processSubs) { + /* process subs recursively */ + size_t sublen = strlen(curSubdir); + size_t freelen = sizeof(curSubdir) - sublen - 1; + LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir)); + fret = FINDFIRST_DIR("*", &fInfo); + if (FINDFIRST_SUCCEEDED(fret)) { + do { + const char *curName = FIND_T_FILENAME(fInfo); + if ((curName[0] == '.') || (strcmp(curName, "CVS") == 0)) { + continue; + } + if (!FIND_T_IS_DIR(fInfo)) { + continue; + } + if (freelen > 0) { + CHDIR(curName); + strncat(curSubdir, "/", freelen); + strncat(curSubdir, curName, freelen - 1); + curSubdir[sizeof(curSubdir) - 1] = 0; + printf("processing subdirectory %s/..." NEWLINE, curSubdir); + filesProcessed += process_sub(data_file, struct_file); + CHDIR(".."); + curSubdir[sublen] = 0; + } else { + printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, curName); + } + } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo))); + } + } + + fret = FINDFIRST_FILE("*.*", &fInfo); + if (FINDFIRST_SUCCEEDED(fret)) { + /* at least one file in directory */ + do { + if (FIND_T_IS_FILE(fInfo)) { + const char *curName = FIND_T_FILENAME(fInfo); + printf("processing %s/%s..." NEWLINE, curSubdir, curName); + if (process_file(data_file, struct_file, curName) < 0) { + printf(NEWLINE "Error... aborting" NEWLINE); + return -1; + } + filesProcessed++; + } + } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo))); + } + return filesProcessed; +} + +u8_t* get_file_data(const char* filename, int* file_size, int can_be_compressed, int* is_compressed) +{ + FILE *inFile; + size_t fsize = 0; + u8_t* buf; + size_t r; + int rs; + inFile = fopen(filename, "rb"); + if (inFile == NULL) { + printf("Failed to open file \"%s\"\n", filename); + exit(-1); + } + fseek(inFile, 0, SEEK_END); + rs = ftell(inFile); + if (rs < 0) { + printf("ftell failed with %d\n", errno); + exit(-1); + } + fsize = (size_t)rs; + fseek(inFile, 0, SEEK_SET); + buf = (u8_t*)malloc(fsize); + LWIP_ASSERT("buf != NULL", buf != NULL); + r = fread(buf, 1, fsize, inFile); + *file_size = fsize; + *is_compressed = 0; +#if MAKEFS_SUPPORT_DEFLATE + overallDataBytes += fsize; + if (deflateNonSsiFiles) { + if (can_be_compressed) { + if (fsize < OUT_BUF_SIZE) { + u8_t* ret_buf; + tdefl_status status; + size_t in_bytes = fsize; + size_t out_bytes = OUT_BUF_SIZE; + const void *next_in = buf; + void *next_out = s_outbuf; + /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */ + mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (!deflate_level) { + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + } + status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); + if (status != TDEFL_STATUS_OKAY) { + printf("tdefl_init() failed!\n"); + exit(-1); + } + memset(s_outbuf, 0, sizeof(s_outbuf)); + status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH); + if (status != TDEFL_STATUS_DONE) { + printf("deflate failed: %d\n", status); + exit(-1); + } + LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE); + if (out_bytes < fsize) { + ret_buf = (u8_t*)malloc(out_bytes); + LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL); + memcpy(ret_buf, s_outbuf, out_bytes); + { + /* sanity-check compression be inflating and comparing to the original */ + tinfl_status dec_status; + tinfl_decompressor inflator; + size_t dec_in_bytes = out_bytes; + size_t dec_out_bytes = OUT_BUF_SIZE; + next_out = s_checkbuf; + + tinfl_init(&inflator); + memset(s_checkbuf, 0, sizeof(s_checkbuf)); + dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0); + LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE); + LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes); + LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize)); + } + /* free original buffer, use compressed data + size */ + free(buf); + buf = ret_buf; + *file_size = out_bytes; + printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes*100.0)/fsize)); + deflatedBytesReduced += (size_t)(fsize - out_bytes); + *is_compressed = 1; + } else { + printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize)); + } + } else { + printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE); + } + } else { + printf(" - SSI file, cannot be compressed" NEWLINE); + } + } +#else + LWIP_UNUSED_ARG(can_be_compressed); +#endif + fclose(inFile); + return buf; +} + +void process_file_data(FILE* data_file, u8_t* file_data, size_t file_size) +{ + size_t written, i, src_off=0; + + size_t off = 0; + for (i = 0; i < file_size; i++) { + LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5); + sprintf(&file_buffer_c[off], "0x%02.2x,", file_data[i]); + off += 5; + if ((++src_off % HEX_BYTES_PER_LINE) == 0) { + LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN); + memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN); + off += NEWLINE_LEN; + } + if (off + 20 >= sizeof(file_buffer_c)) { + written = fwrite(file_buffer_c, 1, off, data_file); + LWIP_ASSERT("written == off", written == off); + off = 0; + } + } + written = fwrite(file_buffer_c, 1, off, data_file); + LWIP_ASSERT("written == off", written == off); +} + +int write_checksums(FILE *struct_file, const char *varname, + u16_t hdr_len, u16_t hdr_chksum, const u8_t* file_data, size_t file_size) +{ + int chunk_size = TCP_MSS; + int offset, src_offset; + size_t len; + int i = 0; +#if LWIP_TCP_TIMESTAMPS + /* when timestamps are used, usable space is 12 bytes less per segment */ + chunk_size -= 12; +#endif + + fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE); + fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname); + + if (hdr_len > 0) { + /* add checksum for HTTP header */ + fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len); + i++; + } + src_offset = 0; + for (offset = hdr_len; ; offset += len) { + unsigned short chksum; + void* data = (void*)&file_data[src_offset]; + len = LWIP_MIN(chunk_size, (int)file_size - src_offset); + if (len == 0) { + break; + } + chksum = ~inet_chksum(data, (u16_t)len); + /* add checksum for data */ + fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, offset, chksum, len); + i++; + } + fprintf(struct_file, "};" NEWLINE); + fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE); + return i; +} + +static int is_valid_char_for_c_var(char x) +{ + if (((x >= 'A') && (x <= 'Z')) || + ((x >= 'a') && (x <= 'z')) || + ((x >= '0') && (x <= '9')) || + (x == '_')) { + return 1; + } + return 0; +} + +static void fix_filename_for_c(char* qualifiedName, size_t max_len) +{ + struct file_entry* f; + size_t len = strlen(qualifiedName); + char *new_name = (char*)malloc(len + 2); + int filename_ok; + int cnt = 0; + size_t i; + if (len + 3 == max_len) { + printf("File name too long: \"%s\"\n", qualifiedName); + exit(-1); + } + strcpy(new_name, qualifiedName); + for (i = 0; i < len; i++) { + if (!is_valid_char_for_c_var(new_name[i])) { + new_name[i] = '_'; + } + } + do { + filename_ok = 1; + for (f = first_file; f != NULL; f = f->next) { + if (!strcmp(f->filename_c, new_name)) { + filename_ok = 0; + cnt++; + /* try next unique file name */ + sprintf(&new_name[len], "%d", cnt); + break; + } + } + } while (!filename_ok && (cnt < 999)); + if (!filename_ok) { + printf("Failed to get unique file name: \"%s\"\n", qualifiedName); + exit(-1); + } + strcpy(qualifiedName, new_name); + free(new_name); +} + +static void register_filename(const char* qualifiedName) +{ + struct file_entry* fe = (struct file_entry*)malloc(sizeof(struct file_entry)); + fe->filename_c = strdup(qualifiedName); + fe->next = NULL; + if (first_file == NULL) { + first_file = last_file = fe; + } else { + last_file->next = fe; + last_file = fe; + } +} + +int is_ssi_file(const char* filename) +{ + size_t loop; + for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { + if (strstr(filename, g_pcSSIExtensions[loop])) { + return 1; + } + } + return 0; +} + +int process_file(FILE *data_file, FILE *struct_file, const char *filename) +{ + char varname[MAX_PATH_LEN]; + int i = 0; + char qualifiedName[MAX_PATH_LEN]; + int file_size; + u16_t http_hdr_chksum = 0; + u16_t http_hdr_len = 0; + int chksum_count = 0; + u8_t flags = 0; + const char* flags_str; + u8_t has_content_len; + u8_t* file_data; + int is_compressed = 0; + + /* create qualified name (@todo: prepend slash or not?) */ + sprintf(qualifiedName,"%s/%s", curSubdir, filename); + /* create C variable name */ + strcpy(varname, qualifiedName); + /* convert slashes & dots to underscores */ + fix_filename_for_c(varname, MAX_PATH_LEN); + register_filename(varname); +#if ALIGN_PAYLOAD + /* to force even alignment of array, type 1 */ + fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE); + fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++); + fprintf(data_file, "#endif" NEWLINE); +#endif /* ALIGN_PAYLOAD */ + fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname); + /* encode source file name (used by file system, not returned to browser) */ + fprintf(data_file, "/* %s (%d chars) */" NEWLINE, qualifiedName, strlen(qualifiedName)+1); + file_put_ascii(data_file, qualifiedName, strlen(qualifiedName)+1, &i); +#if ALIGN_PAYLOAD + /* pad to even number of bytes to assure payload is on aligned boundary */ + while(i % PAYLOAD_ALIGNMENT != 0) { + fprintf(data_file, "0x%02.2x,", 0); + i++; + } +#endif /* ALIGN_PAYLOAD */ + fprintf(data_file, NEWLINE); + + has_content_len = !is_ssi_file(filename); + file_data = get_file_data(filename, &file_size, includeHttpHeader && has_content_len, &is_compressed); + if (includeHttpHeader) { + file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed); + flags = FS_FILE_FLAGS_HEADER_INCLUDED; + if (has_content_len) { + flags |= FS_FILE_FLAGS_HEADER_PERSISTENT; + } + } + if (precalcChksum) { + chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size); + } + + /* build declaration of struct fsdata_file in temp file */ + fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname); + fprintf(struct_file, "file_%s," NEWLINE, lastFileVar); + fprintf(struct_file, "data_%s," NEWLINE, varname); + fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i); + fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i); + switch(flags) + { + case(FS_FILE_FLAGS_HEADER_INCLUDED): + flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED"; + break; + case(FS_FILE_FLAGS_HEADER_PERSISTENT): + flags_str = "FS_FILE_FLAGS_HEADER_PERSISTENT"; + break; + case(FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT): + flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT"; + break; + default: + flags_str = "0"; + break; + } + fprintf(struct_file, "%s," NEWLINE, flags_str); + if (precalcChksum) { + fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE); + fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname); + fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE); + } + fprintf(struct_file, "}};" NEWLINE NEWLINE); + strcpy(lastFileVar, varname); + + /* write actual file contents */ + i = 0; + fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size); + process_file_data(data_file, file_data, file_size); + fprintf(data_file, "};" NEWLINE NEWLINE); + free(file_data); + return 0; +} + +int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len, + u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed) +{ + int i = 0; + int response_type = HTTP_HDR_OK; + const char* file_type; + const char *cur_string; + size_t cur_len; + int written = 0; + size_t hdr_len = 0; + u16_t acc; + const char *file_ext; + int j; + u8_t provide_last_modified = includeLastModified; + + memset(hdr_buf, 0, sizeof(hdr_buf)); + + if (useHttp11) { + response_type = HTTP_HDR_OK_11; + } + + fprintf(data_file, NEWLINE "/* HTTP header */"); + if (strstr(filename, "404") == filename) { + response_type = HTTP_HDR_NOT_FOUND; + if (useHttp11) { + response_type = HTTP_HDR_NOT_FOUND_11; + } + } else if (strstr(filename, "400") == filename) { + response_type = HTTP_HDR_BAD_REQUEST; + if (useHttp11) { + response_type = HTTP_HDR_BAD_REQUEST_11; + } + } else if (strstr(filename, "501") == filename) { + response_type = HTTP_HDR_NOT_IMPL; + if (useHttp11) { + response_type = HTTP_HDR_NOT_IMPL_11; + } + } + cur_string = g_psHTTPHeaderStrings[response_type]; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + + cur_string = serverID; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + + file_ext = filename; + if (file_ext != NULL) { + while(strstr(file_ext, ".") != NULL) { + file_ext = strstr(file_ext, "."); + file_ext++; + } + } + if ((file_ext == NULL) || (*file_ext == 0)) { + printf("failed to get extension for file \"%s\", using default.\n", filename); + file_type = HTTP_HDR_DEFAULT_TYPE; + } else { + file_type = NULL; + for (j = 0; j < NUM_HTTP_HEADERS; j++) { + if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) { + file_type = g_psHTTPHeaders[j].content_type; + break; + } + } + if (file_type == NULL) { + printf("failed to get file type for extension \"%s\", using default.\n", file_ext); + file_type = HTTP_HDR_DEFAULT_TYPE; + } + } + + /* Content-Length is used for persistent connections in HTTP/1.1 but also for + download progress in older versions + @todo: just use a big-enough buffer and let the HTTPD send spaces? */ + if (provide_content_len) { + char intbuf[MAX_PATH_LEN]; + int content_len = file_size; + memset(intbuf, 0, sizeof(intbuf)); + cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH]; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%d+ bytes) */" NEWLINE, cur_string, content_len, cur_len+2); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + + _itoa(content_len, intbuf, 10); + strcat(intbuf, "\r\n"); + cur_len = strlen(intbuf); + written += file_put_ascii(data_file, intbuf, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], intbuf, cur_len); + hdr_len += cur_len; + } + } + if (provide_last_modified) { + char modbuf[256]; + struct stat stat_data; + struct tm* t; + memset(modbuf, 0, sizeof(modbuf)); + memset(&stat_data, 0, sizeof(stat_data)); + cur_string = modbuf; + strcpy(modbuf, "Last-Modified: "); + if (stat(filename, &stat_data) != 0) { + printf("stat(%s) failed with error %d\n", filename, errno); + exit(-1); + } + t = gmtime(&stat_data.st_mtime); + if (t == NULL) { + printf("gmtime() failed with error %d\n", errno); + exit(-1); + } + strftime(&modbuf[15], sizeof(modbuf)-15, "%a, %d %b %Y %H:%M:%S GMT", t); + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%d+ bytes) */" NEWLINE, cur_string, cur_len+2); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + + modbuf[0] = 0; + strcat(modbuf, "\r\n"); + cur_len = strlen(modbuf); + written += file_put_ascii(data_file, modbuf, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], modbuf, cur_len); + hdr_len += cur_len; + } + } + + /* HTTP/1.1 implements persistent connections */ + if (useHttp11) { + if (provide_content_len) { + cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE]; + } else { + /* no Content-Length available, so a persistent connection is no possible + because the client does not know the data length */ + cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE]; + } + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + } + } + +#if MAKEFS_SUPPORT_DEFLATE + if (is_compressed) { + /* tell the client about the deflate encoding */ + LWIP_ASSERT("error", deflateNonSsiFiles); + cur_string = "Content-Encoding: deflate\r\n"; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + } +#else + LWIP_UNUSED_ARG(is_compressed); +#endif + + /* write content-type, ATTENTION: this includes the double-CRLF! */ + cur_string = file_type; + cur_len = strlen(cur_string); + fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); + written += file_put_ascii(data_file, cur_string, cur_len, &i); + i = 0; + + /* ATTENTION: headers are done now (double-CRLF has been written!) */ + + if (precalcChksum) { + memcpy(&hdr_buf[hdr_len], cur_string, cur_len); + hdr_len += cur_len; + + LWIP_ASSERT("hdr_len <= 0xffff", hdr_len <= 0xffff); + LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len); + acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len); + *http_hdr_len = (u16_t)hdr_len; + *http_hdr_chksum = acc; + } + + return written; +} + +int file_put_ascii(FILE *file, const char* ascii_string, int len, int *i) +{ + int x; + for (x = 0; x < len; x++) { + unsigned char cur = ascii_string[x]; + fprintf(file, "0x%02.2x,", cur); + if ((++(*i) % HEX_BYTES_PER_LINE) == 0) { + fprintf(file, NEWLINE); + } + } + return len; +} + +int s_put_ascii(char *buf, const char *ascii_string, int len, int *i) +{ + int x; + int idx = 0; + for (x = 0; x < len; x++) { + unsigned char cur = ascii_string[x]; + sprintf(&buf[idx], "0x%02.2x,", cur); + idx += 5; + if ((++(*i) % HEX_BYTES_PER_LINE) == 0) { + sprintf(&buf[idx], NEWLINE); + idx += NEWLINE_LEN; + } + } + return len; +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/readme.txt b/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/readme.txt new file mode 100644 index 0000000..41c7d80 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/httpd/makefsdata/readme.txt @@ -0,0 +1,13 @@ +This directory contains a script ('makefsdata') to create C code suitable for +httpd for given html pages (or other files) in a directory. + +There is also a plain C console application doing the same and extended a bit. + +Usage: htmlgen [targetdir] [-s] [-i]s + targetdir: relative or absolute path to files to convert + switch -s: toggle processing of subdirectories (default is on) + switch -e: exclude HTTP header from file (header is created at runtime, default is on) + switch -11: include HTTP 1.1 header (1.0 is default) + + if targetdir not specified, makefsdata will attempt to + process files in subdirectory 'fs'. diff --git a/sdk/Src/Network/lwip2.0.3/apps/lwiperf/lwiperf.c b/sdk/Src/Network/lwip2.0.3/apps/lwiperf/lwiperf.c new file mode 100644 index 0000000..efabe47 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/lwiperf/lwiperf.c @@ -0,0 +1,661 @@ +/** + * @file + * lwIP iPerf server implementation + */ + +/** + * @defgroup iperf Iperf server + * @ingroup apps + * + * This is a simple performance measuring server to check your bandwith using + * iPerf2 on a PC as client. + * It is currently a minimal implementation providing an IPv4 TCP server only. + * + * @todo: implement UDP mode and IPv6 + */ + +/* + * Copyright (c) 2014 Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + */ + +#include "lwip/apps/lwiperf.h" + +#include "lwip/tcp.h" +#include "lwip/sys.h" + +#include + +/* Currently, only TCP-over-IPv4 is implemented (does iperf support IPv6 anyway?) */ +#if LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API + +/** Specify the idle timeout (in seconds) after that the test fails */ +#ifndef LWIPERF_TCP_MAX_IDLE_SEC +#define LWIPERF_TCP_MAX_IDLE_SEC 10U +#endif +#if LWIPERF_TCP_MAX_IDLE_SEC > 255 +#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t +#endif + +/* File internal memory allocation (struct lwiperf_*): this defaults to + the heap */ +#ifndef LWIPERF_ALLOC +#define LWIPERF_ALLOC(type) mem_malloc(sizeof(type)) +#define LWIPERF_FREE(type, item) mem_free(item) +#endif + +/** If this is 1, check that received data has the correct format */ +#ifndef LWIPERF_CHECK_RX_DATA +#define LWIPERF_CHECK_RX_DATA 0 +#endif + +/** This is the Iperf settings struct sent from the client */ +typedef struct _lwiperf_settings { +#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000 +#define LWIPERF_FLAGS_ANSWER_NOW 0x00000001 + u32_t flags; + u32_t num_threads; /* unused for now */ + u32_t remote_port; + u32_t buffer_len; /* unused for now */ + u32_t win_band; /* TCP window / UDP rate: unused for now */ + u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */ +} lwiperf_settings_t; + +/** Basic connection handle */ +struct _lwiperf_state_base; +typedef struct _lwiperf_state_base lwiperf_state_base_t; +struct _lwiperf_state_base { + /* 1=tcp, 0=udp */ + u8_t tcp; + /* 1=server, 0=client */ + u8_t server; + lwiperf_state_base_t* next; + lwiperf_state_base_t* related_server_state; +}; + +/** Connection handle for a TCP iperf session */ +typedef struct _lwiperf_state_tcp { + lwiperf_state_base_t base; + struct tcp_pcb* server_pcb; + struct tcp_pcb* conn_pcb; + u32_t time_started; + lwiperf_report_fn report_fn; + void* report_arg; + u8_t poll_count; + u8_t next_num; + u32_t bytes_transferred; + lwiperf_settings_t settings; + u8_t have_settings_buf; +} lwiperf_state_tcp_t; + +/** List of active iperf sessions */ +static lwiperf_state_base_t* lwiperf_all_connections; +/** A const buffer to send from: we want to measure sending, not copying! */ +static const u8_t lwiperf_txbuf_const[1600] = { + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', +}; + +static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb); +static void lwiperf_tcp_err(void *arg, err_t err); + +/** Add an iperf session to the 'active' list */ +static void +lwiperf_list_add(lwiperf_state_base_t* item) +{ + if (lwiperf_all_connections == NULL) { + lwiperf_all_connections = item; + } else { + item = lwiperf_all_connections; + } +} + +/** Remove an iperf session from the 'active' list */ +static void +lwiperf_list_remove(lwiperf_state_base_t* item) +{ + lwiperf_state_base_t* prev = NULL; + lwiperf_state_base_t* iter; + for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) { + if (iter == item) { + if (prev == NULL) { + lwiperf_all_connections = iter->next; + } else { + prev->next = item; + } + /* @debug: ensure this item is listed only once */ + for (iter = iter->next; iter != NULL; iter = iter->next) { + LWIP_ASSERT("duplicate entry", iter != item); + } + break; + } + } +} + +/** Call the report function of an iperf tcp session */ +static void +lwip_tcp_conn_report(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type) +{ + if ((conn != NULL) && (conn->report_fn != NULL)) { + u32_t now, duration_ms, bandwidth_kbitpsec; + now = sys_now(); + duration_ms = now - conn->time_started; + if (duration_ms == 0) { + bandwidth_kbitpsec = 0; + } else { + bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U; + } + conn->report_fn(conn->report_arg, report_type, + &conn->conn_pcb->local_ip, conn->conn_pcb->local_port, + &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port, + conn->bytes_transferred, duration_ms, bandwidth_kbitpsec); + } +} + +/** Close an iperf tcp session */ +static void +lwiperf_tcp_close(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type) +{ + err_t err; + + lwip_tcp_conn_report(conn, report_type); + lwiperf_list_remove(&conn->base); + if (conn->conn_pcb != NULL) { + tcp_arg(conn->conn_pcb, NULL); + tcp_poll(conn->conn_pcb, NULL, 0); + tcp_sent(conn->conn_pcb, NULL); + tcp_recv(conn->conn_pcb, NULL); + tcp_err(conn->conn_pcb, NULL); + err = tcp_close(conn->conn_pcb); + if (err != ERR_OK) { + /* don't want to wait for free memory here... */ + tcp_abort(conn->conn_pcb); + } + } else { + /* no conn pcb, this is the server pcb */ + err = tcp_close(conn->server_pcb); + LWIP_ASSERT("error", err != ERR_OK); + } + LWIPERF_FREE(lwiperf_state_tcp_t, conn); +} + +/** Try to send more data on an iperf tcp session */ +static err_t +lwiperf_tcp_client_send_more(lwiperf_state_tcp_t* conn) +{ + int send_more; + err_t err; + u16_t txlen; + u16_t txlen_max; + void* txptr; + u8_t apiflags; + + LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0)); + + do { + send_more = 0; + if (conn->settings.amount & PP_HTONL(0x80000000)) { + /* this session is time-limited */ + u32_t now = sys_now(); + u32_t diff_ms = now - conn->time_started; + u32_t time = (u32_t)-(s32_t)lwip_htonl(conn->settings.amount); + u32_t time_ms = time * 10; + if (diff_ms >= time_ms) { + /* time specified by the client is over -> close the connection */ + lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT); + return ERR_OK; + } + } else { + /* this session is byte-limited */ + u32_t amount_bytes = lwip_htonl(conn->settings.amount); + /* @todo: this can send up to 1*MSS more than requested... */ + if (amount_bytes >= conn->bytes_transferred) { + /* all requested bytes transferred -> close the connection */ + lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT); + return ERR_OK; + } + } + + if (conn->bytes_transferred < 24) { + /* transmit the settings a first time */ + txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred]; + txlen_max = (u16_t)(24 - conn->bytes_transferred); + apiflags = TCP_WRITE_FLAG_COPY; + } else if (conn->bytes_transferred < 48) { + /* transmit the settings a second time */ + txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred - 24]; + txlen_max = (u16_t)(48 - conn->bytes_transferred); + apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE; + send_more = 1; + } else { + /* transmit data */ + /* @todo: every x bytes, transmit the settings again */ + txptr = LWIP_CONST_CAST(void*, &lwiperf_txbuf_const[conn->bytes_transferred % 10]); + txlen_max = TCP_MSS; + if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */ + txlen_max = TCP_MSS - 24; + } + apiflags = 0; /* no copying needed */ + send_more = 1; + } + txlen = txlen_max; + do { + err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags); + if (err == ERR_MEM) { + txlen /= 2; + } + } while ((err == ERR_MEM) && (txlen >= (TCP_MSS/2))); + + if (err == ERR_OK) { + conn->bytes_transferred += txlen; + } else { + send_more = 0; + } + } while(send_more); + + tcp_output(conn->conn_pcb); + return ERR_OK; +} + +/** TCP sent callback, try to send more data */ +static err_t +lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */ + LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb); + LWIP_UNUSED_ARG(tpcb); + LWIP_UNUSED_ARG(len); + + conn->poll_count = 0; + + return lwiperf_tcp_client_send_more(conn); +} + +/** TCP connected callback (active connection), send data now */ +static err_t +lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb); + LWIP_UNUSED_ARG(tpcb); + if (err != ERR_OK) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); + return ERR_OK; + } + conn->poll_count = 0; + conn->time_started = sys_now(); + return lwiperf_tcp_client_send_more(conn); +} + +/** Start TCP connection back to the client (either parallel or after the + * receive test has finished. + */ +static err_t +lwiperf_tx_start(lwiperf_state_tcp_t* conn) +{ + err_t err; + lwiperf_state_tcp_t* client_conn; + struct tcp_pcb* newpcb; + ip_addr_t remote_addr; + u16_t remote_port; + + client_conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); + if (client_conn == NULL) { + return ERR_MEM; + } + newpcb = tcp_new(); + if (newpcb == NULL) { + LWIPERF_FREE(lwiperf_state_tcp_t, client_conn); + return ERR_MEM; + } + + MEMCPY(client_conn, conn, sizeof(lwiperf_state_tcp_t)); + client_conn->base.server = 0; + client_conn->server_pcb = NULL; + client_conn->conn_pcb = newpcb; + client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */ + client_conn->poll_count = 0; + client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */ + client_conn->bytes_transferred = 0; + client_conn->settings.flags = 0; /* prevent the remote side starting back as client again */ + + tcp_arg(newpcb, client_conn); + tcp_sent(newpcb, lwiperf_tcp_client_sent); + tcp_poll(newpcb, lwiperf_tcp_poll, 2U); + tcp_err(newpcb, lwiperf_tcp_err); + + ip_addr_copy(remote_addr, conn->conn_pcb->remote_ip); + remote_port = (u16_t)lwip_htonl(client_conn->settings.remote_port); + + err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected); + if (err != ERR_OK) { + lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL); + return err; + } + lwiperf_list_add(&client_conn->base); + return ERR_OK; +} + +/** Receive data on an iperf tcp session */ +static err_t +lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + u8_t tmp; + u16_t tot_len; + u32_t packet_idx; + struct pbuf* q; + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + + LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb); + LWIP_UNUSED_ARG(tpcb); + + if (err != ERR_OK) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); + return ERR_OK; + } + if (p == NULL) { + /* connection closed -> test done */ + if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) == + PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) { + /* client requested transmission after end of test */ + lwiperf_tx_start(conn); + } + lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER); + return ERR_OK; + } + tot_len = p->tot_len; + + conn->poll_count = 0; + + if ((!conn->have_settings_buf) || ((conn->bytes_transferred -24) % (1024*128) == 0)) { + /* wait for 24-byte header */ + if (p->tot_len < sizeof(lwiperf_settings_t)) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); + pbuf_free(p); + return ERR_VAL; + } + if (!conn->have_settings_buf) { + if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL); + pbuf_free(p); + return ERR_VAL; + } + conn->have_settings_buf = 1; + if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) == + PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) { + /* client requested parallel transmission test */ + err_t err2 = lwiperf_tx_start(conn); + if (err2 != ERR_OK) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR); + pbuf_free(p); + return err2; + } + } + } else { + if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); + pbuf_free(p); + return ERR_VAL; + } + } + conn->bytes_transferred += sizeof(lwiperf_settings_t); + if (conn->bytes_transferred <= 24) { + conn->time_started = sys_now(); + tcp_recved(tpcb, p->tot_len); + pbuf_free(p); + return ERR_OK; + } + conn->next_num = 4; /* 24 bytes received... */ + tmp = pbuf_header(p, -24); + LWIP_ASSERT("pbuf_header failed", tmp == 0); + } + + packet_idx = 0; + for (q = p; q != NULL; q = q->next) { +#if LWIPERF_CHECK_RX_DATA + const u8_t* payload = (const u8_t*)q->payload; + u16_t i; + for (i = 0; i < q->len; i++) { + u8_t val = payload[i]; + u8_t num = val - '0'; + if (num == conn->next_num) { + conn->next_num++; + if (conn->next_num == 10) { + conn->next_num = 0; + } + } else { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); + pbuf_free(p); + return ERR_VAL; + } + } +#endif + packet_idx += q->len; + } + LWIP_ASSERT("count mismatch", packet_idx == p->tot_len); + conn->bytes_transferred += packet_idx; + tcp_recved(tpcb, tot_len); + pbuf_free(p); + return ERR_OK; +} + +/** Error callback, iperf tcp session aborted */ +static void +lwiperf_tcp_err(void *arg, err_t err) +{ + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + LWIP_UNUSED_ARG(err); + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); +} + +/** TCP poll callback, try to send more data */ +static err_t +lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb) +{ + lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; + LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb); + LWIP_UNUSED_ARG(tpcb); + if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) { + lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL); + return ERR_OK; /* lwiperf_tcp_close frees conn */ + } + + if (!conn->base.server) { + lwiperf_tcp_client_send_more(conn); + } + + return ERR_OK; +} + +/** This is called when a new client connects for an iperf tcp session */ +static err_t +lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + lwiperf_state_tcp_t *s, *conn; + if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) { + return ERR_VAL; + } + + s = (lwiperf_state_tcp_t*)arg; + conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); + if (conn == NULL) { + return ERR_MEM; + } + memset(conn, 0, sizeof(lwiperf_state_tcp_t)); + conn->base.tcp = 1; + conn->base.server = 1; + conn->base.related_server_state = &s->base; + conn->server_pcb = s->server_pcb; + conn->conn_pcb = newpcb; + conn->time_started = sys_now(); + conn->report_fn = s->report_fn; + conn->report_arg = s->report_arg; + + /* setup the tcp rx connection */ + tcp_arg(newpcb, conn); + tcp_recv(newpcb, lwiperf_tcp_recv); + tcp_poll(newpcb, lwiperf_tcp_poll, 2U); + tcp_err(conn->conn_pcb, lwiperf_tcp_err); + + lwiperf_list_add(&conn->base); + return ERR_OK; +} + +/** + * @ingroup iperf + * Start a TCP iperf server on the default TCP port (5001) and listen for + * incoming connections from iperf clients. + * + * @returns a connection handle that can be used to abort the server + * by calling @ref lwiperf_abort() + */ +void* +lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg) +{ + return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT, + report_fn, report_arg); +} + +/** + * @ingroup iperf + * Start a TCP iperf server on a specific IP address and port and listen for + * incoming connections from iperf clients. + * + * @returns a connection handle that can be used to abort the server + * by calling @ref lwiperf_abort() + */ +void* +lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port, + lwiperf_report_fn report_fn, void* report_arg) +{ + err_t err; + struct tcp_pcb* pcb; + lwiperf_state_tcp_t* s; + + if (local_addr == NULL) { + return NULL; + } + + s = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); + if (s == NULL) { + return NULL; + } + memset(s, 0, sizeof(lwiperf_state_tcp_t)); + s->base.tcp = 1; + s->base.server = 1; + s->report_fn = report_fn; + s->report_arg = report_arg; + + pcb = tcp_new(); + if (pcb != NULL) { + err = tcp_bind(pcb, local_addr, local_port); + if (err == ERR_OK) { + s->server_pcb = tcp_listen_with_backlog(pcb, 1); + } + } + if (s->server_pcb == NULL) { + if (pcb != NULL) { + tcp_close(pcb); + } + LWIPERF_FREE(lwiperf_state_tcp_t, s); + return NULL; + } + pcb = NULL; + + tcp_arg(s->server_pcb, s); + tcp_accept(s->server_pcb, lwiperf_tcp_accept); + + lwiperf_list_add(&s->base); + return s; +} + +/** + * @ingroup iperf + * Abort an iperf session (handle returned by lwiperf_start_tcp_server*()) + */ +void +lwiperf_abort(void* lwiperf_session) +{ + lwiperf_state_base_t* i, *dealloc, *last = NULL; + + for (i = lwiperf_all_connections; i != NULL; ) { + if ((i == lwiperf_session) || (i->related_server_state == lwiperf_session)) { + dealloc = i; + i = i->next; + if (last != NULL) { + last->next = i; + } + LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */ + } else { + last = i; + i = i->next; + } + } +} + +#endif /* LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/mdns/mdns.c b/sdk/Src/Network/lwip2.0.3/apps/mdns/mdns.c new file mode 100644 index 0000000..14334fc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/mdns/mdns.c @@ -0,0 +1,2028 @@ +/** + * @file + * MDNS responder implementation + * + * @defgroup mdns MDNS + * @ingroup apps + * + * RFC 6762 - Multicast DNS\n + * RFC 6763 - DNS-Based Service Discovery\n + * + * @verbinclude mdns.txt + * + * Things left to implement: + * ------------------------- + * + * - Probing/conflict resolution + * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off... + * - Checking that source address of unicast requests are on the same network + * - Limiting multicast responses to 1 per second per resource record + * - Fragmenting replies if required + * - Subscribe to netif address/link change events and act on them (currently needs to be done manually) + * - Handling multi-packet known answers + * - Individual known answer detection for all local IPv6 addresses + * - Dynamic size of outgoing packet + */ + +/* + * Copyright (c) 2015 Verisure Innovation 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Erik Ekman + * + */ + +#include "lwip/apps/mdns.h" +#include "lwip/apps/mdns_priv.h" +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/prot/dns.h" + +#include + +#if LWIP_MDNS_RESPONDER + +#if (LWIP_IPV4 && !LWIP_IGMP) + #error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h" +#endif +#if (LWIP_IPV6 && !LWIP_IPV6_MLD) +#error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP) + #error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif + +#if LWIP_IPV4 +#include "lwip/igmp.h" +/* IPv4 multicast group 224.0.0.251 */ +static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT; +#endif + +#if LWIP_IPV6 +#include "lwip/mld6.h" +/* IPv6 multicast group FF02::FB */ +static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT; +#endif + +#define MDNS_PORT 5353 +#define MDNS_TTL 255 + +/* Stored offsets to beginning of domain names + * Used for compression. + */ +#define NUM_DOMAIN_OFFSETS 10 +#define DOMAIN_JUMP_SIZE 2 +#define DOMAIN_JUMP 0xc000 + +static u8_t mdns_netif_client_id; +static struct udp_pcb *mdns_pcb; + +#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id)) + +#define TOPDOMAIN_LOCAL "local" + +#define REVERSE_PTR_TOPDOMAIN "arpa" +#define REVERSE_PTR_V4_DOMAIN "in-addr" +#define REVERSE_PTR_V6_DOMAIN "ip6" + +#define SRV_PRIORITY 0 +#define SRV_WEIGHT 0 + +/* Payload size allocated for each outgoing UDP packet */ +#define OUTPACKET_SIZE 500 + +/* Lookup from hostname -> IPv4 */ +#define REPLY_HOST_A 0x01 +/* Lookup from IPv4/v6 -> hostname */ +#define REPLY_HOST_PTR_V4 0x02 +/* Lookup from hostname -> IPv6 */ +#define REPLY_HOST_AAAA 0x04 +/* Lookup from hostname -> IPv6 */ +#define REPLY_HOST_PTR_V6 0x08 + +/* Lookup for service types */ +#define REPLY_SERVICE_TYPE_PTR 0x10 +/* Lookup for instances of service */ +#define REPLY_SERVICE_NAME_PTR 0x20 +/* Lookup for location of service instance */ +#define REPLY_SERVICE_SRV 0x40 +/* Lookup for text info on service instance */ +#define REPLY_SERVICE_TXT 0x80 + +static const char *dnssd_protos[] = { + "_udp", /* DNSSD_PROTO_UDP */ + "_tcp", /* DNSSD_PROTO_TCP */ +}; + +/** Description of a service */ +struct mdns_service { + /** TXT record to answer with */ + struct mdns_domain txtdata; + /** Name of service, like 'myweb' */ + char name[MDNS_LABEL_MAXLEN + 1]; + /** Type of service, like '_http' */ + char service[MDNS_LABEL_MAXLEN + 1]; + /** Callback function and userdata + * to update txtdata buffer */ + service_get_txt_fn_t txt_fn; + void *txt_userdata; + /** TTL in seconds of SRV/TXT replies */ + u32_t dns_ttl; + /** Protocol, TCP or UDP */ + u16_t proto; + /** Port of the service */ + u16_t port; +}; + +/** Description of a host/netif */ +struct mdns_host { + /** Hostname */ + char name[MDNS_LABEL_MAXLEN + 1]; + /** Pointer to services */ + struct mdns_service *services[MDNS_MAX_SERVICES]; + /** TTL in seconds of A/AAAA/PTR replies */ + u32_t dns_ttl; +}; + +/** Information about received packet */ +struct mdns_packet { + /** Sender IP/port */ + ip_addr_t source_addr; + u16_t source_port; + /** If packet was received unicast */ + u16_t recv_unicast; + /** Netif that received the packet */ + struct netif *netif; + /** Packet data */ + struct pbuf *pbuf; + /** Current parsing offset in packet */ + u16_t parse_offset; + /** Identifier. Used in legacy queries */ + u16_t tx_id; + /** Number of questions in packet, + * read from packet header */ + u16_t questions; + /** Number of unparsed questions */ + u16_t questions_left; + /** Number of answers in packet, + * (sum of normal, authorative and additional answers) + * read from packet header */ + u16_t answers; + /** Number of unparsed answers */ + u16_t answers_left; +}; + +/** Information about outgoing packet */ +struct mdns_outpacket { + /** Netif to send the packet on */ + struct netif *netif; + /** Packet data */ + struct pbuf *pbuf; + /** Current write offset in packet */ + u16_t write_offset; + /** Identifier. Used in legacy queries */ + u16_t tx_id; + /** Destination IP/port if sent unicast */ + ip_addr_t dest_addr; + u16_t dest_port; + /** Number of questions written */ + u16_t questions; + /** Number of normal answers written */ + u16_t answers; + /** Number of additional answers written */ + u16_t additional; + /** Offsets for written domain names in packet. + * Used for compression */ + u16_t domain_offsets[NUM_DOMAIN_OFFSETS]; + /** If all answers in packet should set cache_flush bit */ + u8_t cache_flush; + /** If reply should be sent unicast */ + u8_t unicast_reply; + /** If legacy query. (tx_id needed, and write + * question again in reply before answer) */ + u8_t legacy_query; + /* Reply bitmask for host information */ + u8_t host_replies; + /* Bitmask for which reverse IPv6 hosts to answer */ + u8_t host_reverse_v6_replies; + /* Reply bitmask per service */ + u8_t serv_replies[MDNS_MAX_SERVICES]; +}; + +/** Domain, type and class. + * Shared between questions and answers */ +struct mdns_rr_info { + struct mdns_domain domain; + u16_t type; + u16_t klass; +}; + +struct mdns_question { + struct mdns_rr_info info; + /** unicast reply requested */ + u16_t unicast; +}; + +struct mdns_answer { + struct mdns_rr_info info; + /** cache flush command bit */ + u16_t cache_flush; + /* Validity time in seconds */ + u32_t ttl; + /** Length of variable answer */ + u16_t rd_length; + /** Offset of start of variable answer in packet */ + u16_t rd_offset; +}; + +/** + * Add a label part to a domain + * @param domain The domain to add a label to + * @param label The label to add, like <hostname>, 'local', 'com' or '' + * @param len The length of the label + * @return ERR_OK on success, an err_t otherwise if label too long + */ +err_t +mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len) +{ + if (len > MDNS_LABEL_MAXLEN) { + return ERR_VAL; + } + if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) { + return ERR_VAL; + } + /* Allow only zero marker on last byte */ + if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) { + return ERR_VAL; + } + domain->name[domain->length] = len; + domain->length++; + if (len) { + MEMCPY(&domain->name[domain->length], label, len); + domain->length += len; + } + return ERR_OK; +} + +/** + * Internal readname function with max 6 levels of recursion following jumps + * while decompressing name + */ +static u16_t +mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth) +{ + u8_t c; + + do { + if (depth > 5) { + /* Too many jumps */ + return MDNS_READNAME_ERROR; + } + + c = pbuf_get_at(p, offset); + offset++; + + /* is this a compressed label? */ + if((c & 0xc0) == 0xc0) { + u16_t jumpaddr; + if (offset >= p->tot_len) { + /* Make sure both jump bytes fit in the packet */ + return MDNS_READNAME_ERROR; + } + jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff)); + offset++; + if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) { + u16_t res; + /* Recursive call, maximum depth will be checked */ + res = mdns_readname_loop(p, jumpaddr, domain, depth + 1); + /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */ + if (res == MDNS_READNAME_ERROR) { + return res; + } + } else { + return MDNS_READNAME_ERROR; + } + break; + } + + /* normal label */ + if (c <= MDNS_LABEL_MAXLEN) { + u8_t label[MDNS_LABEL_MAXLEN]; + err_t res; + + if (c + domain->length >= MDNS_DOMAIN_MAXLEN) { + return MDNS_READNAME_ERROR; + } + if (c != 0) { + if (pbuf_copy_partial(p, label, c, offset) != c) { + return MDNS_READNAME_ERROR; + } + offset += c; + } + res = mdns_domain_add_label(domain, (char *) label, c); + if (res != ERR_OK) { + return MDNS_READNAME_ERROR; + } + } else { + /* bad length byte */ + return MDNS_READNAME_ERROR; + } + } while (c != 0); + + return offset; +} + +/** + * Read possibly compressed domain name from packet buffer + * @param p The packet + * @param offset start position of domain name in packet + * @param domain The domain name destination + * @return The new offset after the domain, or MDNS_READNAME_ERROR + * if reading failed + */ +u16_t +mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain) +{ + memset(domain, 0, sizeof(struct mdns_domain)); + return mdns_readname_loop(p, offset, domain, 0); +} + +/** + * Print domain name to debug output + * @param domain The domain name + */ +static void +mdns_domain_debug_print(struct mdns_domain *domain) +{ + u8_t *src = domain->name; + u8_t i; + + while (*src) { + u8_t label_len = *src; + src++; + for (i = 0; i < label_len; i++) { + LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i])); + } + src += label_len; + LWIP_DEBUGF(MDNS_DEBUG, (".")); + } +} + +/** + * Return 1 if contents of domains match (case-insensitive) + * @param a Domain name to compare 1 + * @param b Domain name to compare 2 + * @return 1 if domains are equal ignoring case, 0 otherwise + */ +int +mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b) +{ + u8_t *ptra, *ptrb; + u8_t len; + int res; + + if (a->length != b->length) { + return 0; + } + + ptra = a->name; + ptrb = b->name; + while (*ptra && *ptrb && ptra < &a->name[a->length]) { + if (*ptra != *ptrb) { + return 0; + } + len = *ptra; + ptra++; + ptrb++; + res = lwip_strnicmp((char *) ptra, (char *) ptrb, len); + if (res != 0) { + return 0; + } + ptra += len; + ptrb += len; + } + if (*ptra != *ptrb && ptra < &a->name[a->length]) { + return 0; + } + return 1; +} + +/** + * Call user supplied function to setup TXT data + * @param service The service to build TXT record for + */ +static void +mdns_prepare_txtdata(struct mdns_service *service) +{ + memset(&service->txtdata, 0, sizeof(struct mdns_domain)); + if (service->txt_fn) { + service->txt_fn(service, service->txt_userdata); + } +} + +#if LWIP_IPV4 +/** + * Build domain for reverse lookup of IPv4 address + * like 12.0.168.192.in-addr.arpa. for 192.168.0.12 + * @param domain Where to write the domain name + * @param addr Pointer to an IPv4 address to encode + * @return ERR_OK if domain was written, an err_t otherwise + */ +static err_t +mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr) +{ + int i; + err_t res; + const u8_t *ptr; + if (!domain || !addr) { + return ERR_ARG; + } + memset(domain, 0, sizeof(struct mdns_domain)); + ptr = (const u8_t *) addr; + for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) { + char buf[4]; + u8_t val = ptr[i]; + + lwip_itoa(buf, sizeof(buf), val); + res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf)); + LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); + } + res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN)-1)); + LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN)-1)); + LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, NULL, 0); + LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res); + + return ERR_OK; +} +#endif + +#if LWIP_IPV6 +/** + * Build domain for reverse lookup of IP address + * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab + * @param domain Where to write the domain name + * @param addr Pointer to an IPv6 address to encode + * @return ERR_OK if domain was written, an err_t otherwise + */ +static err_t +mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr) +{ + int i; + err_t res; + const u8_t *ptr; + if (!domain || !addr) { + return ERR_ARG; + } + memset(domain, 0, sizeof(struct mdns_domain)); + ptr = (const u8_t *) addr; + for (i = sizeof(ip6_addr_t) - 1; i >= 0; i--) { + char buf; + u8_t byte = ptr[i]; + int j; + for (j = 0; j < 2; j++) { + if ((byte & 0x0F) < 0xA) { + buf = '0' + (byte & 0x0F); + } else { + buf = 'a' + (byte & 0x0F) - 0xA; + } + res = mdns_domain_add_label(domain, &buf, sizeof(buf)); + LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); + byte >>= 4; + } + } + res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN)-1)); + LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN)-1)); + LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, NULL, 0); + LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res); + + return ERR_OK; +} +#endif + +/* Add .local. to domain */ +static err_t +mdns_add_dotlocal(struct mdns_domain *domain) +{ + err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL)-1)); + LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res); + return mdns_domain_add_label(domain, NULL, 0); +} + +/** + * Build the .local. domain name + * @param domain Where to write the domain name + * @param mdns TMDNS netif descriptor. + * @return ERR_OK if domain .local. was written, an err_t otherwise + */ +static err_t +mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns) +{ + err_t res; + memset(domain, 0, sizeof(struct mdns_domain)); + LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL); + res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name)); + LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res); + return mdns_add_dotlocal(domain); +} + +/** + * Build the lookup-all-services special DNS-SD domain name + * @param domain Where to write the domain name + * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise + */ +static err_t +mdns_build_dnssd_domain(struct mdns_domain *domain) +{ + err_t res; + memset(domain, 0, sizeof(struct mdns_domain)); + res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services")-1)); + LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd")-1)); + LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP])); + LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res); + return mdns_add_dotlocal(domain); +} + +/** + * Build domain name for a service + * @param domain Where to write the domain name + * @param service The service struct, containing service name, type and protocol + * @param include_name Whether to include the service name in the domain + * @return ERR_OK if domain was written. If service name is included, + * ...local. will be written, otherwise ..local. + * An err_t is returned on error. + */ +static err_t +mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name) +{ + err_t res; + memset(domain, 0, sizeof(struct mdns_domain)); + if (include_name) { + res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name)); + LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); + } + res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service)); + LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); + res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto])); + LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res); + return mdns_add_dotlocal(domain); +} + +/** + * Check which replies we should send for a host/netif based on question + * @param netif The network interface that received the question + * @param rr Domain/type/class from a question + * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for + * if reply bit has REPLY_HOST_PTR_V6 set + * @return Bitmask of which replies to send + */ +static int +check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply) +{ + err_t res; + int replies = 0; + struct mdns_domain mydomain; + + LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */ + + if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) { + /* Invalid class */ + return replies; + } + + /* Handle PTR for our addresses */ + if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) { +#if LWIP_IPV6 + int i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { + res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i)); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { + replies |= REPLY_HOST_PTR_V6; + /* Mark which addresses where requested */ + if (reverse_v6_reply) { + *reverse_v6_reply |= (1 << i); + } + } + } + } +#endif +#if LWIP_IPV4 + if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) { + res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif)); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { + replies |= REPLY_HOST_PTR_V4; + } + } +#endif + } + + res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif)); + /* Handle requests for our hostname */ + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { + /* TODO return NSEC if unsupported protocol requested */ +#if LWIP_IPV4 + if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) + && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) { + replies |= REPLY_HOST_A; + } +#endif +#if LWIP_IPV6 + if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) { + replies |= REPLY_HOST_AAAA; + } +#endif + } + + return replies; +} + +/** + * Check which replies we should send for a service based on question + * @param service A registered MDNS service + * @param rr Domain/type/class from a question + * @return Bitmask of which replies to send + */ +static int +check_service(struct mdns_service *service, struct mdns_rr_info *rr) +{ + err_t res; + int replies = 0; + struct mdns_domain mydomain; + + if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) { + /* Invalid class */ + return 0; + } + + res = mdns_build_dnssd_domain(&mydomain); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) && + (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) { + /* Request for all service types */ + replies |= REPLY_SERVICE_TYPE_PTR; + } + + res = mdns_build_service_domain(&mydomain, service, 0); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) && + (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) { + /* Request for the instance of my service */ + replies |= REPLY_SERVICE_NAME_PTR; + } + + res = mdns_build_service_domain(&mydomain, service, 1); + if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) { + /* Request for info about my service */ + if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) { + replies |= REPLY_SERVICE_SRV; + } + if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) { + replies |= REPLY_SERVICE_TXT; + } + } + + return replies; +} + +/** + * Return bytes needed to write before jump for best result of compressing supplied domain + * against domain in outpacket starting at specified offset. + * If a match is found, offset is updated to where to jump to + * @param pbuf Pointer to pbuf with the partially constructed DNS packet + * @param offset Start position of a domain written earlier. If this location is suitable + * for compression, the pointer is updated to where in the domain to jump to. + * @param domain The domain to write + * @return Number of bytes to write of the new domain before writing a jump to the offset. + * If compression can not be done against this previous domain name, the full new + * domain length is returned. + */ +u16_t +mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain) +{ + struct mdns_domain target; + u16_t target_end; + u8_t target_len; + u8_t writelen = 0; + u8_t *ptr; + if (pbuf == NULL) { + return domain->length; + } + target_end = mdns_readname(pbuf, *offset, &target); + if (target_end == MDNS_READNAME_ERROR) { + return domain->length; + } + target_len = (u8_t)(target_end - *offset); + ptr = domain->name; + while (writelen < domain->length) { + u8_t domainlen = (u8_t)(domain->length - writelen); + u8_t labellen; + if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) { + /* Compare domains if target is long enough, and we have enough left of the domain */ + u8_t targetpos = (u8_t)(target.length - domainlen); + if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) { + /* We are checking at or beyond a jump in the original, stop looking */ + break; + } + if (target.length >= domainlen && + memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) { + *offset += targetpos; + return writelen; + } + } + /* Skip to next label in domain */ + labellen = *ptr; + writelen += 1 + labellen; + ptr += 1 + labellen; + } + /* Nothing found */ + return domain->length; +} + +/** + * Write domain to outpacket. Compression will be attempted, + * unless domain->skip_compression is set. + * @param outpkt The outpacket to write to + * @param domain The domain name to write + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain) +{ + int i; + err_t res; + u16_t writelen = domain->length; + u16_t jump_offset = 0; + u16_t jump; + + if (!domain->skip_compression) { + for (i = 0; i < NUM_DOMAIN_OFFSETS; ++i) { + u16_t offset = outpkt->domain_offsets[i]; + if (offset) { + u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain); + if (len < writelen) { + writelen = len; + jump_offset = offset; + } + } + } + } + + if (writelen) { + /* Write uncompressed part of name */ + res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset); + if (res != ERR_OK) { + return res; + } + + /* Store offset of this new domain */ + for (i = 0; i < NUM_DOMAIN_OFFSETS; ++i) { + if (outpkt->domain_offsets[i] == 0) { + outpkt->domain_offsets[i] = outpkt->write_offset; + break; + } + } + + outpkt->write_offset += writelen; + } + if (jump_offset) { + /* Write jump */ + jump = lwip_htons(DOMAIN_JUMP | jump_offset); + res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset); + if (res != ERR_OK) { + return res; + } + outpkt->write_offset += DOMAIN_JUMP_SIZE; + } + return ERR_OK; +} + +/** + * Write a question to an outpacket + * A question contains domain, type and class. Since an answer also starts with these fields this function is also + * called from mdns_add_answer(). + * @param outpkt The outpacket to write to + * @param domain The domain name the answer is for + * @param type The DNS type of the answer (like 'AAAA', 'SRV') + * @param klass The DNS type of the answer (like 'IN') + * @param unicast If highest bit in class should be set, to instruct the responder to + * reply with a unicast packet + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast) +{ + u16_t question_len; + u16_t field16; + err_t res; + + if (!outpkt->pbuf) { + /* If no pbuf is active, allocate one */ + outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM); + if (!outpkt->pbuf) { + return ERR_MEM; + } + outpkt->write_offset = SIZEOF_DNS_HDR; + } + + /* Worst case calculation. Domain string might be compressed */ + question_len = domain->length + sizeof(type) + sizeof(klass); + if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) { + /* No space */ + return ERR_MEM; + } + + /* Write name */ + res = mdns_write_domain(outpkt, domain); + if (res != ERR_OK) { + return res; + } + + /* Write type */ + field16 = lwip_htons(type); + res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset); + if (res != ERR_OK) { + return res; + } + outpkt->write_offset += sizeof(field16); + + /* Write class */ + if (unicast) { + klass |= 0x8000; + } + field16 = lwip_htons(klass); + res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset); + if (res != ERR_OK) { + return res; + } + outpkt->write_offset += sizeof(field16); + + return ERR_OK; +} + +/** + * Write answer to reply packet. + * buf or answer_domain can be null. The rd_length written will be buf_length + + * size of (compressed) domain. Most uses will need either buf or answer_domain, + * special case is SRV that starts with 3 u16 and then a domain name. + * @param reply The outpacket to write to + * @param domain The domain name the answer is for + * @param type The DNS type of the answer (like 'AAAA', 'SRV') + * @param klass The DNS type of the answer (like 'IN') + * @param cache_flush If highest bit in class should be set, to instruct receiver that + * this reply replaces any earlier answer for this domain/type/class + * @param ttl Validity time in seconds to send out for IP address data in DNS replies + * @param buf Pointer to buffer of answer data + * @param buf_length Length of variable data + * @param answer_domain A domain to write after any buffer data as answer + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush, + u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain) +{ + u16_t answer_len; + u16_t field16; + u16_t rdlen_offset; + u16_t answer_offset; + u32_t field32; + err_t res; + + if (!reply->pbuf) { + /* If no pbuf is active, allocate one */ + reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM); + if (!reply->pbuf) { + return ERR_MEM; + } + reply->write_offset = SIZEOF_DNS_HDR; + } + + /* Worst case calculation. Domain strings might be compressed */ + answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/; + if (buf) { + answer_len += (u16_t)buf_length; + } + if (answer_domain) { + answer_len += answer_domain->length; + } + if (reply->write_offset + answer_len > reply->pbuf->tot_len) { + /* No space */ + return ERR_MEM; + } + + /* Answer starts with same data as question, then more fields */ + mdns_add_question(reply, domain, type, klass, cache_flush); + + /* Write TTL */ + field32 = lwip_htonl(ttl); + res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset); + if (res != ERR_OK) { + return res; + } + reply->write_offset += sizeof(field32); + + /* Store offsets and skip forward to the data */ + rdlen_offset = reply->write_offset; + reply->write_offset += sizeof(field16); + answer_offset = reply->write_offset; + + if (buf) { + /* Write static data */ + res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset); + if (res != ERR_OK) { + return res; + } + reply->write_offset += (u16_t)buf_length; + } + + if (answer_domain) { + /* Write name answer (compressed if possible) */ + res = mdns_write_domain(reply, answer_domain); + if (res != ERR_OK) { + return res; + } + } + + /* Write rd_length after when we know the answer size */ + field16 = lwip_htons(reply->write_offset - answer_offset); + res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset); + + return res; +} + +/** + * Helper function for mdns_read_question/mdns_read_answer + * Reads a domain, type and class from the packet + * @param pkt The MDNS packet to read from. The parse_offset field will be + * incremented to point to the next unparsed byte. + * @param info The struct to fill with domain, type and class + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info) +{ + u16_t field16, copied; + pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain); + if (pkt->parse_offset == MDNS_READNAME_ERROR) { + return ERR_VAL; + } + + copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); + if (copied != sizeof(field16)) { + return ERR_VAL; + } + pkt->parse_offset += copied; + info->type = lwip_ntohs(field16); + + copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); + if (copied != sizeof(field16)) { + return ERR_VAL; + } + pkt->parse_offset += copied; + info->klass = lwip_ntohs(field16); + + return ERR_OK; +} + +/** + * Read a question from the packet. + * All questions have to be read before the answers. + * @param pkt The MDNS packet to read from. The questions_left field will be decremented + * and the parse_offset will be updated. + * @param question The struct to fill with question data + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question) +{ + /* Safety check */ + if (pkt->pbuf->tot_len < pkt->parse_offset) { + return ERR_VAL; + } + + if (pkt->questions_left) { + err_t res; + pkt->questions_left--; + + memset(question, 0, sizeof(struct mdns_question)); + res = mdns_read_rr_info(pkt, &question->info); + if (res != ERR_OK) { + return res; + } + + /* Extract unicast flag from class field */ + question->unicast = question->info.klass & 0x8000; + question->info.klass &= 0x7FFF; + + return ERR_OK; + } + return ERR_VAL; +} + +/** + * Read an answer from the packet + * The variable length reply is not copied, its pbuf offset and length is stored instead. + * @param pkt The MDNS packet to read. The answers_left field will be decremented and + * the parse_offset will be updated. + * @param answer The struct to fill with answer data + * @return ERR_OK on success, an err_t otherwise + */ +static err_t +mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer) +{ + /* Read questions first */ + if (pkt->questions_left) { + return ERR_VAL; + } + + /* Safety check */ + if (pkt->pbuf->tot_len < pkt->parse_offset) { + return ERR_VAL; + } + + if (pkt->answers_left) { + u16_t copied, field16; + u32_t ttl; + err_t res; + pkt->answers_left--; + + memset(answer, 0, sizeof(struct mdns_answer)); + res = mdns_read_rr_info(pkt, &answer->info); + if (res != ERR_OK) { + return res; + } + + /* Extract cache_flush flag from class field */ + answer->cache_flush = answer->info.klass & 0x8000; + answer->info.klass &= 0x7FFF; + + copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset); + if (copied != sizeof(ttl)) { + return ERR_VAL; + } + pkt->parse_offset += copied; + answer->ttl = lwip_ntohl(ttl); + + copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset); + if (copied != sizeof(field16)) { + return ERR_VAL; + } + pkt->parse_offset += copied; + answer->rd_length = lwip_ntohs(field16); + + answer->rd_offset = pkt->parse_offset; + pkt->parse_offset += answer->rd_length; + + return ERR_OK; + } + return ERR_VAL; +} + +#if LWIP_IPV4 +/** Write an IPv4 address (A) RR to outpacket */ +static err_t +mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif) +{ + struct mdns_domain host; + mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n")); + return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL); +} + +/** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */ +static err_t +mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif) +{ + struct mdns_domain host, revhost; + mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); + mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n")); + return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host); +} +#endif + +#if LWIP_IPV6 +/** Write an IPv6 address (AAAA) RR to outpacket */ +static err_t +mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex) +{ + struct mdns_domain host; + mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n")); + return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_t), NULL); +} + +/** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */ +static err_t +mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex) +{ + struct mdns_domain host, revhost; + mdns_build_host_domain(&host, NETIF_TO_HOST(netif)); + mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex)); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n")); + return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host); +} +#endif + +/** Write an all-services -> servicetype PTR RR to outpacket */ +static err_t +mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service) +{ + struct mdns_domain service_type, service_dnssd; + mdns_build_service_domain(&service_type, service, 0); + mdns_build_dnssd_domain(&service_dnssd); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n")); + return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type); +} + +/** Write a servicetype -> servicename PTR RR to outpacket */ +static err_t +mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service) +{ + struct mdns_domain service_type, service_instance; + mdns_build_service_domain(&service_type, service, 0); + mdns_build_service_domain(&service_instance, service, 1); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n")); + return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance); +} + +/** Write a SRV RR to outpacket */ +static err_t +mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service) +{ + struct mdns_domain service_instance, srvhost; + u16_t srvdata[3]; + mdns_build_service_domain(&service_instance, service, 1); + mdns_build_host_domain(&srvhost, mdns); + if (reply->legacy_query) { + /* RFC 6762 section 18.14: + * In legacy unicast responses generated to answer legacy queries, + * name compression MUST NOT be performed on SRV records. + */ + srvhost.skip_compression = 1; + } + srvdata[0] = lwip_htons(SRV_PRIORITY); + srvdata[1] = lwip_htons(SRV_WEIGHT); + srvdata[2] = lwip_htons(service->port); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n")); + return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl, + (const u8_t *) &srvdata, sizeof(srvdata), &srvhost); +} + +/** Write a TXT RR to outpacket */ +static err_t +mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service) +{ + struct mdns_domain service_instance; + mdns_build_service_domain(&service_instance, service, 1); + mdns_prepare_txtdata(service); + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n")); + return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl, + (u8_t *) &service->txtdata.name, service->txtdata.length, NULL); +} + +/** + * Setup outpacket as a reply to the incoming packet + */ +static void +mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in) +{ + memset(out, 0, sizeof(struct mdns_outpacket)); + out->cache_flush = 1; + out->netif = in->netif; + + /* Copy source IP/port to use when responding unicast, or to choose + * which pcb to use for multicast (IPv4/IPv6) + */ + SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t)); + out->dest_port = in->source_port; + + if (in->source_port != MDNS_PORT) { + out->unicast_reply = 1; + out->cache_flush = 0; + if (in->questions == 1) { + out->legacy_query = 1; + out->tx_id = in->tx_id; + } + } + + if (in->recv_unicast) { + out->unicast_reply = 1; + } +} + +/** + * Send chosen answers as a reply + * + * Add all selected answers (first write will allocate pbuf) + * Add additional answers based on the selected answers + * Send the packet + */ +static void +mdns_send_outpacket(struct mdns_outpacket *outpkt) +{ + struct mdns_service *service; + err_t res; + int i; + struct mdns_host* mdns = NETIF_TO_HOST(outpkt->netif); + + /* Write answers to host questions */ +#if LWIP_IPV4 + if (outpkt->host_replies & REPLY_HOST_A) { + res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + if (outpkt->host_replies & REPLY_HOST_PTR_V4) { + res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } +#endif +#if LWIP_IPV6 + if (outpkt->host_replies & REPLY_HOST_AAAA) { + int addrindex; + for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; ++addrindex) { + if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) { + res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + } + } + if (outpkt->host_replies & REPLY_HOST_PTR_V6) { + u8_t rev_addrs = outpkt->host_reverse_v6_replies; + int addrindex = 0; + while (rev_addrs) { + if (rev_addrs & 1) { + res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + addrindex++; + rev_addrs >>= 1; + } + } +#endif + + /* Write answers to service questions */ + for (i = 0; i < MDNS_MAX_SERVICES; ++i) { + service = mdns->services[i]; + if (!service) { + continue; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) { + res = mdns_add_servicetype_ptr_answer(outpkt, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) { + res = mdns_add_servicename_ptr_answer(outpkt, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) { + res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) { + res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->answers++; + } + } + + /* All answers written, add additional RRs */ + for (i = 0; i < MDNS_MAX_SERVICES; ++i) { + service = mdns->services[i]; + if (!service) { + continue; + } + + if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) { + /* Our service instance requested, include SRV & TXT + * if they are already not requested. */ + if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) { + res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->additional++; + } + + if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) { + res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->additional++; + } + } + + /* If service instance, SRV, record or an IP address is requested, + * supply all addresses for the host + */ + if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) || + (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) { +#if LWIP_IPV6 + if (!(outpkt->host_replies & REPLY_HOST_AAAA)) { + int addrindex; + for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; ++addrindex) { + if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) { + res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->additional++; + } + } + } +#endif +#if LWIP_IPV4 + if (!(outpkt->host_replies & REPLY_HOST_A)) { + res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif); + if (res != ERR_OK) { + goto cleanup; + } + outpkt->additional++; + } +#endif + } + } + + if (outpkt->pbuf) { + const ip_addr_t *mcast_destaddr; + struct dns_hdr hdr; + + /* Write header */ + memset(&hdr, 0, sizeof(hdr)); + hdr.flags1 = DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE; + hdr.numanswers = lwip_htons(outpkt->answers); + hdr.numextrarr = lwip_htons(outpkt->additional); + if (outpkt->legacy_query) { + hdr.numquestions = lwip_htons(1); + hdr.id = lwip_htons(outpkt->tx_id); + } + pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr)); + + /* Shrink packet */ + pbuf_realloc(outpkt->pbuf, outpkt->write_offset); + + if (IP_IS_V6_VAL(outpkt->dest_addr)) { +#if LWIP_IPV6 + mcast_destaddr = &v6group; +#endif + } else { +#if LWIP_IPV4 + mcast_destaddr = &v4group; +#endif + } + /* Send created packet */ + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply)); + if (outpkt->unicast_reply) { + udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif); + } else { + udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, MDNS_PORT, outpkt->netif); + } + } + +cleanup: + if (outpkt->pbuf) { + pbuf_free(outpkt->pbuf); + outpkt->pbuf = NULL; + } +} + +/** + * Send unsolicited answer containing all our known data + * @param netif The network interface to send on + * @param destination The target address to send to (usually multicast address) + */ +static void +mdns_announce(struct netif *netif, const ip_addr_t *destination) +{ + struct mdns_outpacket announce; + int i; + struct mdns_host* mdns = NETIF_TO_HOST(netif); + + memset(&announce, 0, sizeof(announce)); + announce.netif = netif; + announce.cache_flush = 1; +#if LWIP_IPV4 + if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) + announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4; +#endif +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { + announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6; + announce.host_reverse_v6_replies |= (1 << i); + } + } +#endif + + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + struct mdns_service *serv = mdns->services[i]; + if (serv) { + announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR | + REPLY_SERVICE_SRV | REPLY_SERVICE_TXT; + } + } + + announce.dest_port = MDNS_PORT; + SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr)); + mdns_send_outpacket(&announce); +} + +/** + * Handle question MDNS packet + * 1. Parse all questions and set bits what answers to send + * 2. Clear pending answers if known answers are supplied + * 3. Put chosen answers in new packet and send as reply + */ +static void +mdns_handle_question(struct mdns_packet *pkt) +{ + struct mdns_service *service; + struct mdns_outpacket reply; + int replies = 0; + int i; + err_t res; + struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif); + + mdns_init_outpacket(&reply, pkt); + + while (pkt->questions_left) { + struct mdns_question q; + + res = mdns_read_question(pkt, &q); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n")); + return; + } + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain ")); + mdns_domain_debug_print(&q.info.domain); + LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass)); + + if (q.unicast) { + /* Reply unicast if any question is unicast */ + reply.unicast_reply = 1; + } + + reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies); + replies |= reply.host_replies; + + for (i = 0; i < MDNS_MAX_SERVICES; ++i) { + service = mdns->services[i]; + if (!service) { + continue; + } + reply.serv_replies[i] |= check_service(service, &q.info); + replies |= reply.serv_replies[i]; + } + + if (replies && reply.legacy_query) { + /* Add question to reply packet (legacy packet only has 1 question) */ + res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0); + if (res != ERR_OK) { + goto cleanup; + } + } + } + + /* Handle known answers */ + while (pkt->answers_left) { + struct mdns_answer ans; + u8_t rev_v6; + int match; + + res = mdns_read_answer(pkt, &ans); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n")); + goto cleanup; + } + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain ")); + mdns_domain_debug_print(&ans.info.domain); + LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass)); + + + if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) { + /* Skip known answers for ANY type & class */ + continue; + } + + rev_v6 = 0; + match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6); + if (match && (ans.ttl > (mdns->dns_ttl / 2))) { + /* The RR in the known answer matches an RR we are planning to send, + * and the TTL is less than half gone. + * If the payload matches we should not send that answer. + */ + if (ans.info.type == DNS_RRTYPE_PTR) { + /* Read domain and compare */ + struct mdns_domain known_ans, my_ans; + u16_t len; + len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans); + res = mdns_build_host_domain(&my_ans, mdns); + if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { +#if LWIP_IPV4 + if (match & REPLY_HOST_PTR_V4) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n")); + reply.host_replies &= ~REPLY_HOST_PTR_V4; + } +#endif +#if LWIP_IPV6 + if (match & REPLY_HOST_PTR_V6) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n")); + reply.host_reverse_v6_replies &= ~rev_v6; + if (reply.host_reverse_v6_replies == 0) { + reply.host_replies &= ~REPLY_HOST_PTR_V6; + } + } +#endif + } + } else if (match & REPLY_HOST_A) { +#if LWIP_IPV4 + if (ans.rd_length == sizeof(ip4_addr_t) && + pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n")); + reply.host_replies &= ~REPLY_HOST_A; + } +#endif + } else if (match & REPLY_HOST_AAAA) { +#if LWIP_IPV6 + if (ans.rd_length == sizeof(ip6_addr_t) && + /* TODO this clears all AAAA responses if first addr is set as known */ + pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n")); + reply.host_replies &= ~REPLY_HOST_AAAA; + } +#endif + } + } + + for (i = 0; i < MDNS_MAX_SERVICES; ++i) { + service = mdns->services[i]; + if (!service) { + continue; + } + match = reply.serv_replies[i] & check_service(service, &ans.info); + if (match && (ans.ttl > (service->dns_ttl / 2))) { + /* The RR in the known answer matches an RR we are planning to send, + * and the TTL is less than half gone. + * If the payload matches we should not send that answer. + */ + if (ans.info.type == DNS_RRTYPE_PTR) { + /* Read domain and compare */ + struct mdns_domain known_ans, my_ans; + u16_t len; + len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans); + if (len != MDNS_READNAME_ERROR) { + if (match & REPLY_SERVICE_TYPE_PTR) { + res = mdns_build_service_domain(&my_ans, service, 0); + if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n")); + reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR; + } + } + if (match & REPLY_SERVICE_NAME_PTR) { + res = mdns_build_service_domain(&my_ans, service, 1); + if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n")); + reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR; + } + } + } + } else if (match & REPLY_SERVICE_SRV) { + /* Read and compare to my SRV record */ + u16_t field16, len, read_pos; + struct mdns_domain known_ans, my_ans; + read_pos = ans.rd_offset; + do { + /* Check priority field */ + len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); + if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) { + break; + } + read_pos += len; + /* Check weight field */ + len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); + if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) { + break; + } + read_pos += len; + /* Check port field */ + len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos); + if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) { + break; + } + read_pos += len; + /* Check host field */ + len = mdns_readname(pkt->pbuf, read_pos, &known_ans); + mdns_build_host_domain(&my_ans, mdns); + if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) { + break; + } + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n")); + reply.serv_replies[i] &= ~REPLY_SERVICE_SRV; + } while (0); + } else if (match & REPLY_SERVICE_TXT) { + mdns_prepare_txtdata(service); + if (service->txtdata.length == ans.rd_length && + pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n")); + reply.serv_replies[i] &= ~REPLY_SERVICE_TXT; + } + } + } + } + } + + mdns_send_outpacket(&reply); + +cleanup: + if (reply.pbuf) { + /* This should only happen if we fail to alloc/write question for legacy query */ + pbuf_free(reply.pbuf); + reply.pbuf = NULL; + } +} + +/** + * Handle response MDNS packet + * Only prints debug for now. Will need more code to do conflict resolution. + */ +static void +mdns_handle_response(struct mdns_packet *pkt) +{ + /* Ignore all questions */ + while (pkt->questions_left) { + struct mdns_question q; + err_t res; + + res = mdns_read_question(pkt, &q); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n")); + return; + } + } + + while (pkt->answers_left) { + struct mdns_answer ans; + err_t res; + + res = mdns_read_answer(pkt, &ans); + if (res != ERR_OK) { + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n")); + return; + } + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain ")); + mdns_domain_debug_print(&ans.info.domain); + LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass)); + } +} + +/** + * Receive input function for MDNS packets. + * Handles both IPv4 and IPv6 UDP pcbs. + */ +static void +mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + struct dns_hdr hdr; + struct mdns_packet packet; + struct netif *recv_netif = ip_current_input_netif(); + u16_t offset = 0; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + + LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr)? 6 : 4, p->tot_len)); + + if (NETIF_TO_HOST(recv_netif) == NULL) { + /* From netif not configured for MDNS */ + goto dealloc; + } + + if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) { + /* Too small */ + goto dealloc; + } + offset += SIZEOF_DNS_HDR; + + if (DNS_HDR_GET_OPCODE(&hdr)) { + /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */ + goto dealloc; + } + + memset(&packet, 0, sizeof(packet)); + SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr)); + packet.source_port = port; + packet.netif = recv_netif; + packet.pbuf = p; + packet.parse_offset = offset; + packet.tx_id = lwip_ntohs(hdr.id); + packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions); + packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr); + +#if LWIP_IPV6 + if (IP_IS_V6(ip_current_dest_addr())) { + if (!ip_addr_cmp(ip_current_dest_addr(), &v6group)) { + packet.recv_unicast = 1; + } + } +#endif +#if LWIP_IPV4 + if (!IP_IS_V6(ip_current_dest_addr())) { + if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) { + packet.recv_unicast = 1; + } + } +#endif + + if (hdr.flags1 & DNS_FLAG1_RESPONSE) { + mdns_handle_response(&packet); + } else { + mdns_handle_question(&packet); + } + +dealloc: + pbuf_free(p); +} + +/** + * @ingroup mdns + * Initiate MDNS responder. Will open UDP sockets on port 5353 + */ +void +mdns_resp_init(void) +{ + err_t res; + + mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL); +#if LWIP_MULTICAST_TX_OPTIONS + udp_set_multicast_ttl(mdns_pcb, MDNS_TTL); +#else + mdns_pcb->ttl = MDNS_TTL; +#endif + res = udp_bind(mdns_pcb, IP_ANY_TYPE, MDNS_PORT); + LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("Failed to bind pcb", res == ERR_OK); + udp_recv(mdns_pcb, mdns_recv, NULL); + + mdns_netif_client_id = netif_alloc_client_data_id(); +} + +/** + * @ingroup mdns + * Announce IP settings have changed on netif. + * Call this in your callback registered by netif_set_status_callback(). + * This function may go away in the future when netif supports registering + * multiple callback functions. + * @param netif The network interface where settings have changed. + */ +void +mdns_resp_netif_settings_changed(struct netif *netif) +{ + LWIP_ERROR("mdns_resp_netif_ip_changed: netif != NULL", (netif != NULL), return); + + if (NETIF_TO_HOST(netif) == NULL) { + return; + } + + /* Announce on IPv6 and IPv4 */ +#if LWIP_IPV6 + mdns_announce(netif, IP6_ADDR_ANY); +#endif +#if LWIP_IPV4 + mdns_announce(netif, IP4_ADDR_ANY); +#endif +} + +/** + * @ingroup mdns + * Activate MDNS responder for a network interface and send announce packets. + * @param netif The network interface to activate. + * @param hostname Name to use. Queries for <hostname>.local will be answered + * with the IP addresses of the netif. The hostname will be copied, the + * given pointer can be on the stack. + * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies + * @return ERR_OK if netif was added, an err_t otherwise + */ +err_t +mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl) +{ + err_t res; + struct mdns_host* mdns; + + LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL); + LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL); + + LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL); + mdns = (struct mdns_host *) mem_malloc(sizeof(struct mdns_host)); + LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM); + + netif_set_client_data(netif, mdns_netif_client_id, mdns); + + memset(mdns, 0, sizeof(struct mdns_host)); + MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname))); + mdns->dns_ttl = dns_ttl; + + /* Join multicast groups */ +#if LWIP_IPV4 + res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group)); + if (res != ERR_OK) { + goto cleanup; + } +#endif +#if LWIP_IPV6 + res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group)); + if (res != ERR_OK) { + goto cleanup; + } +#endif + + mdns_resp_netif_settings_changed(netif); + return ERR_OK; + +cleanup: + mem_free(mdns); + netif_set_client_data(netif, mdns_netif_client_id, NULL); + return res; +} + +/** + * @ingroup mdns + * Stop responding to MDNS queries on this interface, leave multicast groups, + * and free the helper structure and any of its services. + * @param netif The network interface to remove. + * @return ERR_OK if netif was removed, an err_t otherwise + */ +err_t +mdns_resp_remove_netif(struct netif *netif) +{ + int i; + struct mdns_host* mdns; + + LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif); + mdns = NETIF_TO_HOST(netif); + LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL); + + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + struct mdns_service *service = mdns->services[i]; + if (service) { + mem_free(service); + } + } + + /* Leave multicast groups */ +#if LWIP_IPV4 + igmp_leavegroup_netif(netif, ip_2_ip4(&v4group)); +#endif +#if LWIP_IPV6 + mld6_leavegroup_netif(netif, ip_2_ip6(&v6group)); +#endif + + mem_free(mdns); + netif_set_client_data(netif, mdns_netif_client_id, NULL); + return ERR_OK; +} + +/** + * @ingroup mdns + * Add a service to the selected network interface. + * @param netif The network interface to publish this service on + * @param name The name of the service + * @param service The service type, like "_http" + * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP + * for others ("_udp") + * @param port The port the service listens to + * @param dns_ttl Validity time in seconds to send out for service data in DNS replies + * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to + * allow dynamic replies. + * @param txt_data Userdata pointer for txt_fn + * @return ERR_OK if the service was added to the netif, an err_t otherwise + */ +err_t +mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data) +{ + int i; + int slot = -1; + struct mdns_service *srv; + struct mdns_host* mdns; + + LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif); + mdns = NETIF_TO_HOST(netif); + LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL); + + LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL); + LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL); + LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL); + + for (i = 0; i < MDNS_MAX_SERVICES; i++) { + if (mdns->services[i] == NULL) { + slot = i; + break; + } + } + LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM); + + srv = (struct mdns_service*)mem_malloc(sizeof(struct mdns_service)); + LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM); + + memset(srv, 0, sizeof(struct mdns_service)); + + MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name))); + MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service))); + srv->txt_fn = txt_fn; + srv->txt_userdata = txt_data; + srv->proto = (u16_t)proto; + srv->port = port; + srv->dns_ttl = dns_ttl; + + mdns->services[slot] = srv; + + /* Announce on IPv6 and IPv4 */ +#if LWIP_IPV6 + mdns_announce(netif, IP6_ADDR_ANY); +#endif +#if LWIP_IPV4 + mdns_announce(netif, IP4_ADDR_ANY); +#endif + + return ERR_OK; +} + +/** + * @ingroup mdns + * Call this function from inside the service_get_txt_fn_t callback to add text data. + * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte. + * @param service The service provided to the get_txt callback + * @param txt String to add to the TXT field. + * @param txt_len Length of string + * @return ERR_OK if the string was added to the reply, an err_t otherwise + */ +err_t +mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len) +{ + LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service); + + /* Use a mdns_domain struct to store txt chunks since it is the same encoding */ + return mdns_domain_add_label(&service->txtdata, txt, txt_len); +} + +#endif /* LWIP_MDNS_RESPONDER */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/mqtt/mqtt.c b/sdk/Src/Network/lwip2.0.3/apps/mqtt/mqtt.c new file mode 100644 index 0000000..81ef06c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/mqtt/mqtt.c @@ -0,0 +1,1376 @@ +/** + * @file + * MQTT client + * + * @defgroup mqtt MQTT client + * @ingroup apps + * @verbinclude mqtt_client.txt + */ + +/* + * Copyright (c) 2016 Erik Andersson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack + * + * Author: Erik Andersson + * + * + * @todo: + * - Handle large outgoing payloads for PUBLISH messages + * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics) + * - Add support for legacy MQTT protocol version + * + * Please coordinate changes and requests with Erik Andersson + * Erik Andersson + * + */ +#include "lwip/apps/mqtt.h" +#include "lwip/timeouts.h" +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/tcp.h" +#include + +#ifdef WINDOWS +#define LWIP_TCP 1 +#define LWIP_CALLBACK_API 1 +#endif +#if LWIP_TCP && LWIP_CALLBACK_API + + +#define MQTT_DEBUG 1 + +/** + * MQTT_DEBUG: Default is off. + */ +#if !defined MQTT_DEBUG || defined __DOXYGEN__ +#define MQTT_DEBUG LWIP_DBG_OFF +#endif + +#define MQTT_DEBUG_TRACE (MQTT_DEBUG | LWIP_DBG_TRACE) +#define MQTT_DEBUG_STATE (MQTT_DEBUG | LWIP_DBG_STATE) +#define MQTT_DEBUG_WARN (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define MQTT_DEBUG_WARN_STATE (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) +#define MQTT_DEBUG_SERIOUS (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS) + +static void mqtt_cyclic_timer(void *arg); + +/** + * MQTT client connection states + */ +enum { + TCP_DISCONNECTED, + TCP_CONNECTING, + MQTT_CONNECTING, + MQTT_CONNECTED +}; + +/** + * MQTT control message types + */ +enum mqtt_message_type { + MQTT_MSG_TYPE_CONNECT = 1, + MQTT_MSG_TYPE_CONNACK = 2, + MQTT_MSG_TYPE_PUBLISH = 3, + MQTT_MSG_TYPE_PUBACK = 4, + MQTT_MSG_TYPE_PUBREC = 5, + MQTT_MSG_TYPE_PUBREL = 6, + MQTT_MSG_TYPE_PUBCOMP = 7, + MQTT_MSG_TYPE_SUBSCRIBE = 8, + MQTT_MSG_TYPE_SUBACK = 9, + MQTT_MSG_TYPE_UNSUBSCRIBE = 10, + MQTT_MSG_TYPE_UNSUBACK = 11, + MQTT_MSG_TYPE_PINGREQ = 12, + MQTT_MSG_TYPE_PINGRESP = 13, + MQTT_MSG_TYPE_DISCONNECT = 14 +}; + +/** Helpers to extract control packet type and qos from first byte in fixed header */ +#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4) +#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1) + +/** + * MQTT connect flags, only used in CONNECT message + */ +enum mqtt_connect_flag { + MQTT_CONNECT_FLAG_USERNAME = 1 << 7, + MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, + MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, + MQTT_CONNECT_FLAG_WILL = 1 << 2, + MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 +}; + + +#if defined(LWIP_DEBUG) +static const char * const mqtt_message_type_str[15] = +{ + "UNDEFINED", + "CONNECT", + "CONNACK", + "PUBLISH", + "PUBACK", + "PUBREC", + "PUBREL", + "PUBCOMP", + "SUBSCRIBE", + "SUBACK", + "UNSUBSCRIBE", + "UNSUBACK", + "PINGREQ", + "PINGRESP", + "DISCONNECT" +}; + +/** + * Message type value to string + * @param msg_type see enum mqtt_message_type + * + * @return Control message type text string + */ +static const char * +mqtt_msg_type_to_str(u8_t msg_type) +{ + if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) { + msg_type = 0; + } + return mqtt_message_type_str[msg_type]; +} + +#endif + + +/** + * Generate MQTT packet identifier + * @param client MQTT client + * @return New packet identifier, range 1 to 65535 + */ +static u16_t +msg_generate_packet_id(mqtt_client_t *client) +{ + client->pkt_id_seq++; + if (client->pkt_id_seq == 0) { + client->pkt_id_seq++; + } + return client->pkt_id_seq; +} + +/*--------------------------------------------------------------------------------------------------------------------- */ +/* Output ring buffer */ + + +#define MQTT_RINGBUF_IDX_MASK ((MQTT_OUTPUT_RINGBUF_SIZE) - 1) + +/** Add single item to ring buffer */ +#define mqtt_ringbuf_put(rb, item) ((rb)->buf)[(rb)->put++ & MQTT_RINGBUF_IDX_MASK] = (item) + +/** Return number of bytes in ring buffer */ +#define mqtt_ringbuf_len(rb) ((u16_t)((rb)->put - (rb)->get)) + +/** Return number of bytes free in ring buffer */ +#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb)) + +/** Return number of bytes possible to read without wrapping around */ +#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - ((rb)->get & MQTT_RINGBUF_IDX_MASK))) + +/** Return pointer to ring buffer get position */ +#define mqtt_ringbuf_get_ptr(rb) (&(rb)->buf[(rb)->get & MQTT_RINGBUF_IDX_MASK]) + +#define mqtt_ringbuf_advance_get_idx(rb, len) ((rb)->get += (len)) + + +/** + * Try send as many bytes as possible from output ring buffer + * @param rb Output ring buffer + * @param tpcb TCP connection handle + */ +static void +mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_pcb *tpcb) +{ + err_t err; + u8_t wrap = 0; + u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb); + u16_t send_len = tcp_sndbuf(tpcb); + LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL); + + if (send_len == 0 || ringbuf_lin_len == 0) { + return; + } + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n", + send_len, ringbuf_lin_len, ((rb)->get & MQTT_RINGBUF_IDX_MASK), ((rb)->put & MQTT_RINGBUF_IDX_MASK))); + + if (send_len > ringbuf_lin_len) { + /* Space in TCP output buffer is larger than available in ring buffer linear portion */ + send_len = ringbuf_lin_len; + /* Wrap around if more data in ring buffer after linear portion */ + wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len); + } + err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0)); + if ((err == ERR_OK) && wrap) { + mqtt_ringbuf_advance_get_idx(rb, send_len); + /* Use the lesser one of ring buffer linear length and TCP send buffer size */ + send_len = LWIP_MIN(tcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb)); + err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY); + } + + if (err == ERR_OK) { + mqtt_ringbuf_advance_get_idx(rb, send_len); + /* Flush */ + tcp_output(tpcb); + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); + } +} + + + +/*--------------------------------------------------------------------------------------------------------------------- */ +/* Request queue */ + +/** + * Create request item + * @param r_objs Pointer to request objects + * @param pkt_id Packet identifier of request + * @param cb Packet callback to call when requests lifetime ends + * @param arg Parameter following callback + * @return Request or NULL if failed to create + */ +static struct mqtt_request_t * +mqtt_create_request(struct mqtt_request_t *r_objs, u16_t pkt_id, mqtt_request_cb_t cb, void *arg) +{ + struct mqtt_request_t *r = NULL; + u8_t n; + LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL); + for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) { + /* Item point to itself if not in use */ + if (r_objs[n].next == &r_objs[n]) { + r = &r_objs[n]; + r->next = NULL; + r->cb = cb; + r->arg = arg; + r->pkt_id = pkt_id; + break; + } + } + return r; +} + + +/** + * Append request to pending request queue + * @param tail Pointer to request queue tail pointer + * @param r Request to append + */ +static void +mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r) +{ + struct mqtt_request_t *head = NULL; + s16_t time_before = 0; + struct mqtt_request_t *iter; + + LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL); + + /* Iterate trough queue to find head, and count total timeout time */ + for (iter = *tail; iter != NULL; iter = iter->next) { + time_before += iter->timeout_diff; + head = iter; + } + + LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT); + r->timeout_diff = MQTT_REQ_TIMEOUT - time_before; + if (head == NULL) { + *tail = r; + } else { + head->next = r; + } +} + + +/** + * Delete request item + * @param r Request item to delete + */ +static void +mqtt_delete_request(struct mqtt_request_t *r) +{ + if (r != NULL) { + r->next = r; + } +} + +/** + * Remove a request item with a specific packet identifier from request queue + * @param tail Pointer to request queue tail pointer + * @param pkt_id Packet identifier of request to take + * @return Request item if found, NULL if not + */ +static struct mqtt_request_t * +mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id) +{ + struct mqtt_request_t *iter = NULL, *prev = NULL; + LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL); + /* Search all request for pkt_id */ + for (iter = *tail; iter != NULL; iter = iter->next) { + if (iter->pkt_id == pkt_id) { + break; + } + prev = iter; + } + + /* If request was found */ + if (iter != NULL) { + /* unchain */ + if (prev == NULL) { + *tail= iter->next; + } else { + prev->next = iter->next; + } + /* If exists, add remaining timeout time for the request to next */ + if (iter->next != NULL) { + iter->next->timeout_diff += iter->timeout_diff; + } + iter->next = NULL; + } + return iter; +} + +/** + * Handle requests timeout + * @param tail Pointer to request queue tail pointer + * @param t Time since last call in seconds + */ +static void +mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t) +{ + struct mqtt_request_t *r = *tail; + LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL); + r = *tail; + while (t > 0 && r != NULL) { + if (t >= r->timeout_diff) { + t -= (u8_t)r->timeout_diff; + /* Unchain */ + *tail = r->next; + /* Notify upper layer about timeout */ + if (r->cb != NULL) { + r->cb(r->arg, ERR_TIMEOUT); + } + mqtt_delete_request(r); + /* Tail might be be modified in callback, so re-read it in every iteration */ + r = *(struct mqtt_request_t * const volatile *)tail; + } else { + r->timeout_diff -= t; + t = 0; + } + } +} + +/** + * Free all request items + * @param tail Pointer to request queue tail pointer + */ +static void +mqtt_clear_requests(struct mqtt_request_t **tail) +{ + struct mqtt_request_t *iter, *next; + LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL); + for (iter = *tail; iter != NULL; iter = next) { + next = iter->next; + mqtt_delete_request(iter); + } + *tail = NULL; +} +/** + * Initialize all request items + * @param r_objs Pointer to request objects + */ +static void +mqtt_init_requests(struct mqtt_request_t *r_objs) +{ + u8_t n; + LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL); + for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) { + /* Item pointing to itself indicates unused */ + r_objs[n].next = &r_objs[n]; + } +} + +/*--------------------------------------------------------------------------------------------------------------------- */ +/* Output message build helpers */ + + +static void +mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value) +{ + mqtt_ringbuf_put(rb, value); +} + +static +void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value) +{ + mqtt_ringbuf_put(rb, value >> 8); + mqtt_ringbuf_put(rb, value & 0xff); +} + +static void +mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length) +{ + u16_t n; + for (n = 0; n < length; n++) { + mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]); + } +} + +static void +mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length) +{ + u16_t n; + mqtt_ringbuf_put(rb, length >> 8); + mqtt_ringbuf_put(rb, length & 0xff); + for (n = 0; n < length; n++) { + mqtt_ringbuf_put(rb, str[n]); + } +} + +/** + * Append fixed header + * @param rb Output ring buffer + * @param msg_type see enum mqtt_message_type + * @param dup MQTT DUP flag + * @param qos MQTT QoS field + * @param retain MQTT retain flag + * @param r_length Remaining length after fixed header + */ + +static void +mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t dup, + u8_t qos, u8_t retain, u16_t r_length) +{ + /* Start with control byte */ + mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1))); + /* Encode remaining length field */ + do { + mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0)); + r_length >>= 7; + } while (r_length > 0); +} + + +/** + * Check output buffer space + * @param rb Output ring buffer + * @param r_length Remaining length after fixed header + * @return 1 if message will fit, 0 if not enough buffer space + */ +static u8_t +mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length) +{ + /* Start with length of type byte + remaining length */ + u16_t total_len = 1 + r_length; + + LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL); + + /* Calculate number of required bytes to contain the remaining bytes field and add to total*/ + do { + total_len++; + r_length >>= 7; + } while (r_length > 0); + + return (total_len <= mqtt_ringbuf_free(rb)); +} + + +/** + * Close connection to server + * @param client MQTT client + * @param reason Reason for disconnection + */ +static void +mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason) +{ + LWIP_ASSERT("mqtt_close: client != NULL", client != NULL); + + /* Bring down TCP connection if not already done */ + if (client->conn != NULL) { + err_t res; + tcp_recv(client->conn, NULL); + tcp_err(client->conn, NULL); + tcp_sent(client->conn, NULL); + res = tcp_close(client->conn); + if (res != ERR_OK) { + tcp_abort(client->conn); + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_close: Close err=%s\n", lwip_strerr(res))); + } + client->conn = NULL; + } + + /* Remove all pending requests */ + mqtt_clear_requests(&client->pend_req_queue); + /* Stop cyclic timer */ + sys_untimeout(mqtt_cyclic_timer, client); + + /* Notify upper layer of disconnection if changed state */ + if (client->conn_state != TCP_DISCONNECTED) { + + client->conn_state = TCP_DISCONNECTED; + if (client->connect_cb != NULL) { + client->connect_cb(client, client->connect_arg, reason); + } + } +} + + +/** + * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states + * @param arg MQTT client + */ +static void +mqtt_cyclic_timer(void *arg) +{ + u8_t restart_timer = 1; + mqtt_client_t *client = (mqtt_client_t *)arg; + LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL); + + if (client->conn_state == MQTT_CONNECTING) { + client->cyclic_tick++; + if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: CONNECT attempt to server timed out\n")); + /* Disconnect TCP */ + mqtt_close(client, MQTT_CONNECT_TIMEOUT); + restart_timer = 0; + } + } else if (client->conn_state == MQTT_CONNECTED) { + /* Handle timeout for pending requests */ + mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL); + + /* keep_alive > 0 means keep alive functionality shall be used */ + if (client->keep_alive > 0) { + + client->server_watchdog++; + /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */ + if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive/2)) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Server incoming keep-alive timeout\n")); + mqtt_close(client, MQTT_CONNECT_TIMEOUT); + restart_timer = 0; + } + + /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */ + if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: Sending keep-alive message to server\n")); + if (mqtt_output_check_space(&client->output, 0) != 0) { + mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0); + client->cyclic_tick = 0; + } + } else { + client->cyclic_tick++; + } + } + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state)); + restart_timer = 0; + } + if (restart_timer) { + sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, arg); + } +} + + +/** + * Send PUBACK, PUBREC or PUBREL response message + * @param client MQTT client + * @param msg PUBACK, PUBREC or PUBREL + * @param pkt_id Packet identifier + * @param qos QoS value + * @return ERR_OK if successful, ERR_MEM if out of memory + */ +static err_t +pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos) +{ + err_t err = ERR_OK; + if (mqtt_output_check_space(&client->output, 2)) { + mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2); + mqtt_output_append_u16(&client->output, pkt_id); + mqtt_output_send(&client->output, client->conn); + } else { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n", + mqtt_msg_type_to_str(msg), pkt_id)); + err = ERR_MEM; + } + return err; +} + +/** + * Subscribe response from server + * @param r Matching request + * @param result Result code from server + */ +static void +mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result) +{ + if (r->cb != NULL) { + r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT); + } +} + + +/** + * Complete MQTT message received or buffer full + * @param client MQTT client + * @param fixed_hdr_idx header index + * @param length length received part + * @param remaining_length Remaining length of complete message + */ +static mqtt_connection_status_t + mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length) +{ + mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED; + + u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx; + + /* Control packet type */ + u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]); + u16_t pkt_id = 0; + + if (pkt_type == MQTT_MSG_TYPE_CONNACK) { + if (client->conn_state == MQTT_CONNECTING) { + /* Get result code from CONNACK */ + res = (mqtt_connection_status_t)var_hdr_payload[1]; + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: Connect response code %d\n", res)); + if (res == MQTT_CONNECT_ACCEPTED) { + /* Reset cyclic_tick when changing to connected state */ + client->cyclic_tick = 0; + client->conn_state = MQTT_CONNECTED; + /* Notify upper layer */ + if (client->connect_cb != 0) { + client->connect_cb(client, client->connect_arg, res); + } + } + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Received CONNACK in connected state\n")); + } + } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,( "mqtt_message_received: Received PINGRESP from server\n")); + + } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) { + u16_t payload_offset = 0; + u16_t payload_length = length; + u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]); + + if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) { + /* Should have topic and pkt id*/ + uint8_t *topic; + uint16_t after_topic; + u8_t bkp; + u16_t topic_len = var_hdr_payload[0]; + topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]); + + topic = var_hdr_payload + 2; + after_topic = 2 + topic_len; + /* Check length, add one byte even for QoS 0 so that zero termination will fit */ + if ((after_topic + (qos? 2 : 1)) > length) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n")); + goto out_disconnect; + } + + /* id for QoS 1 and 2 */ + if (qos > 0) { + client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1]; + after_topic += 2; + } else { + client->inpub_pkt_id = 0; + } + /* Take backup of byte after topic */ + bkp = topic[topic_len]; + /* Zero terminate string */ + topic[topic_len] = 0; + /* Payload data remaining in receive buffer */ + payload_length = length - after_topic; + payload_offset = after_topic; + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %d\n", + qos, topic, remaining_length + payload_length)); + if (client->pub_cb != NULL) { + client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length); + } + /* Restore byte after topic */ + topic[topic_len] = bkp; + } + if (payload_length > 0 || remaining_length == 0) { + client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0); + /* Reply if QoS > 0 */ + if (remaining_length == 0 && qos > 0) { + /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */ + u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC; + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n", + mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id)); + pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0); + } + } + } else { + /* Get packet identifier */ + pkt_id = (u16_t)var_hdr_payload[0] << 8; + pkt_id |= (u16_t)var_hdr_payload[1]; + if (pkt_id == 0) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Got message with illegal packet identifier: 0\n")); + goto out_disconnect; + } + if (pkt_type == MQTT_MSG_TYPE_PUBREC) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n",pkt_id)); + pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1); + + } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n",pkt_id)); + pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0); + + } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK || + pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) { + struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id); + if (r != NULL) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id)); + if (pkt_type == MQTT_MSG_TYPE_SUBACK) { + if (length < 3) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: To small SUBACK packet\n")); + goto out_disconnect; + } else { + mqtt_incomming_suback(r, var_hdr_payload[2]); + } + } else if (r->cb != NULL) { + r->cb(r->arg, ERR_OK); + } + mqtt_delete_request(r); + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id)); + } + } else { + LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received unknown message type: %d\n", pkt_type)); + goto out_disconnect; + } + } + return res; +out_disconnect: + return MQTT_CONNECT_DISCONNECTED; +} + + +/** + * MQTT incoming message parser + * @param client MQTT client + * @param p PBUF chain of received data + * @return Connection status + */ +static mqtt_connection_status_t +mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p) +{ + u16_t in_offset = 0; + u32_t msg_rem_len = 0; + u8_t fixed_hdr_idx = 0; + u8_t b = 0; + + while (p->tot_len > in_offset) { + if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) { + + if (fixed_hdr_idx < client->msg_idx) { + b = client->rx_buffer[fixed_hdr_idx]; + } else { + b = pbuf_get_at(p, in_offset++); + client->rx_buffer[client->msg_idx++] = b; + } + fixed_hdr_idx++; + + if (fixed_hdr_idx >= 2) { + msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7); + if ((b & 0x80) == 0) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: Remaining length after fixed header: %d\n", msg_rem_len)); + if (msg_rem_len == 0) { + /* Complete message with no extra headers of payload received */ + mqtt_message_received(client, fixed_hdr_idx, 0, 0); + client->msg_idx = 0; + fixed_hdr_idx = 0; + } else { + /* Bytes remaining in message */ + msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx; + } + } + } + } else { + u16_t cpy_len, cpy_start, buffer_space; + + cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx; + + /* Allow to copy the lesser one of available length in input data or bytes remaining in message */ + cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len); + + /* Limit to available space in buffer */ + buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start; + if (cpy_len > buffer_space) { + cpy_len = buffer_space; + } + pbuf_copy_partial(p, client->rx_buffer+cpy_start, cpy_len, in_offset); + + /* Advance get and put indexes */ + client->msg_idx += cpy_len; + in_offset += cpy_len; + msg_rem_len -= cpy_len; + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: msg_idx: %d, cpy_len: %d, remaining %d\n", client->msg_idx, cpy_len, msg_rem_len)); + if (msg_rem_len == 0 || cpy_len == buffer_space) { + /* Whole message received or buffer is full */ + mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len); + if (res != MQTT_CONNECT_ACCEPTED) { + return res; + } + if (msg_rem_len == 0) { + /* Reset parser state */ + client->msg_idx = 0; + /* msg_tot_len = 0; */ + fixed_hdr_idx = 0; + } + } + } + } + return MQTT_CONNECT_ACCEPTED; +} + + +/** + * TCP received callback function. @see tcp_recv_fn + * @param arg MQTT client + * @param p PBUF chain of received data + * @param err Passed as return value if not ERR_OK + * @return ERR_OK or err passed into callback + */ +static err_t +mqtt_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + mqtt_client_t *client = (mqtt_client_t *)arg; + LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL); + LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb); + + if (p == NULL) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n")); + mqtt_close(client, MQTT_CONNECT_DISCONNECTED); + } else { + mqtt_connection_status_t res; + if (err != ERR_OK) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_recv_cb: Recv err=%d\n", err)); + pbuf_free(p); + return err; + } + + /* Tell remote that data has been received */ + tcp_recved(pcb, p->tot_len); + res = mqtt_parse_incoming(client, p); + pbuf_free(p); + + if (res != MQTT_CONNECT_ACCEPTED) { + mqtt_close(client, res); + } + /* If keep alive functionality is used */ + if (client->keep_alive != 0) { + /* Reset server alive watchdog */ + client->server_watchdog = 0; + } + + } + return ERR_OK; +} + + +/** + * TCP data sent callback function. @see tcp_sent_fn + * @param arg MQTT client + * @param tpcb TCP connection handle + * @param len Number of bytes sent + * @return ERR_OK + */ +static err_t +mqtt_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + mqtt_client_t *client = (mqtt_client_t *)arg; + + LWIP_UNUSED_ARG(tpcb); + LWIP_UNUSED_ARG(len); + + if (client->conn_state == MQTT_CONNECTED) { + struct mqtt_request_t *r; + + /* Reset keep-alive send timer and server watchdog */ + client->cyclic_tick = 0; + client->server_watchdog = 0; + /* QoS 0 publish has no response from server, so call its callbacks here */ + while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n")); + if (r->cb != NULL) { + r->cb(r->arg, ERR_OK); + } + mqtt_delete_request(r); + } + /* Try send any remaining buffers from output queue */ + mqtt_output_send(&client->output, client->conn); + } + return ERR_OK; +} + +/** + * TCP error callback function. @see tcp_err_fn + * @param arg MQTT client + * @param err Error encountered + */ +static void +mqtt_tcp_err_cb(void *arg, err_t err) +{ + mqtt_client_t *client = (mqtt_client_t *)arg; + LWIP_UNUSED_ARG(err); /* only used for debug output */ + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg)); + LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL); + /* Set conn to null before calling close as pcb is already deallocated*/ + client->conn = 0; + mqtt_close(client, MQTT_CONNECT_DISCONNECTED); +} + +/** + * TCP poll callback function. @see tcp_poll_fn + * @param arg MQTT client + * @param tpcb TCP connection handle + * @return err ERR_OK + */ +static err_t +mqtt_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb) +{ + mqtt_client_t *client = (mqtt_client_t *)arg; + if (client->conn_state == MQTT_CONNECTED) { + /* Try send any remaining buffers from output queue */ + mqtt_output_send(&client->output, tpcb); + } + return ERR_OK; +} + +/** + * TCP connect callback function. @see tcp_connected_fn + * @param arg MQTT client + * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error + * @return ERR_OK + */ +static err_t +mqtt_tcp_connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + mqtt_client_t* client = (mqtt_client_t *)arg; + + if (err != ERR_OK) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_connect_cb: TCP connect error %d\n", err)); + return err; + } + + /* Initiate receiver state */ + client->msg_idx = 0; + + /* Setup TCP callbacks */ + tcp_recv(tpcb, mqtt_tcp_recv_cb); + tcp_sent(tpcb, mqtt_tcp_sent_cb); + tcp_poll(tpcb, mqtt_tcp_poll_cb, 2); + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_connect_cb: TCP connection established to server\n")); + /* Enter MQTT connect state */ + client->conn_state = MQTT_CONNECTING; + + /* Start cyclic timer */ + sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, client); + client->cyclic_tick = 0; + + /* Start transmission from output queue, connect message is the first one out*/ + mqtt_output_send(&client->output, client->conn); + + return ERR_OK; +} + + + +/*---------------------------------------------------------------------------------------------------- */ +/* Public API */ + + +/** + * @ingroup mqtt + * MQTT publish function. + * @param client MQTT client + * @param topic Publish topic string + * @param payload Data to publish (NULL is allowed) + * @param payload_length: Length of payload (0 is allowed) + * @param qos Quality of service, 0 1 or 2 + * @param retain MQTT retain flag + * @param cb Callback to call when publish is complete or has timed out + * @param arg User supplied argument to publish callback + * @return ERR_OK if successful + * ERR_CONN if client is disconnected + * ERR_MEM if short on memory + */ +err_t +mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain, + mqtt_request_cb_t cb, void *arg) +{ + struct mqtt_request_t *r; + u16_t pkt_id; + size_t topic_strlen; + size_t total_len; + u16_t topic_len; + u16_t remaining_length; + + LWIP_ASSERT("mqtt_publish: client != NULL", client); + LWIP_ASSERT("mqtt_publish: topic != NULL", topic); + LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN); + + topic_strlen = strlen(topic); + LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG); + topic_len = (u16_t)topic_strlen; + total_len = 2 + topic_len + payload_length; + LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG); + remaining_length = (u16_t)total_len; + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic)); + + if (qos > 0) { + remaining_length += 2; + /* Generate pkt_id id for QoS1 and 2 */ + pkt_id = msg_generate_packet_id(client); + } else { + /* Use reserved value pkt_id 0 for QoS 0 in request handle */ + pkt_id = 0; + } + + r = mqtt_create_request(client->req_list, pkt_id, cb, arg); + if (r == NULL) { + return ERR_MEM; + } + + if (mqtt_output_check_space(&client->output, remaining_length) == 0) { + mqtt_delete_request(r); + return ERR_MEM; + } + /* Append fixed header */ + mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length); + + /* Append Topic */ + mqtt_output_append_string(&client->output, topic, topic_len); + + /* Append packet if for QoS 1 and 2*/ + if (qos > 0) { + mqtt_output_append_u16(&client->output, pkt_id); + } + + /* Append optional publish payload */ + if ((payload != NULL) && (payload_length > 0)) { + mqtt_output_append_buf(&client->output, payload, payload_length); + } + + mqtt_append_request(&client->pend_req_queue, r); + mqtt_output_send(&client->output, client->conn); + return ERR_OK; +} + + +/** + * @ingroup mqtt + * MQTT subscribe/unsubscribe function. + * @param client MQTT client + * @param topic topic to subscribe to + * @param qos Quality of service, 0 1 or 2 (only used for subscribe) + * @param cb Callback to call when subscribe/unsubscribe reponse is received + * @param arg User supplied argument to publish callback + * @param sub 1 for subscribe, 0 for unsubscribe + * @return ERR_OK if successful, @see err_t enum for other results + */ +err_t +mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub) +{ + size_t topic_strlen; + size_t total_len; + u16_t topic_len; + u16_t remaining_length; + u16_t pkt_id; + struct mqtt_request_t *r; + + LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client); + LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic); + + topic_strlen = strlen(topic); + LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG); + topic_len = (u16_t)topic_strlen; + /* Topic string, pkt_id, qos for subscribe */ + total_len = topic_len + 2 + 2 + (sub != 0); + LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG); + remaining_length = (u16_t)total_len; + + LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3); + if (client->conn_state == TCP_DISCONNECTED) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n")); + return ERR_CONN; + } + + pkt_id = msg_generate_packet_id(client); + r = mqtt_create_request(client->req_list, pkt_id, cb, arg); + if (r == NULL) { + return ERR_MEM; + } + + if (mqtt_output_check_space(&client->output, remaining_length) == 0) { + mqtt_delete_request(r); + return ERR_MEM; + } + + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id)); + + mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length); + /* Packet id */ + mqtt_output_append_u16(&client->output, pkt_id); + /* Topic */ + mqtt_output_append_string(&client->output, topic, topic_len); + /* QoS */ + if (sub != 0) { + mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2)); + } + + mqtt_append_request(&client->pend_req_queue, r); + mqtt_output_send(&client->output, client->conn); + return ERR_OK; +} + + +/** + * @ingroup mqtt + * Set callback to handle incoming publish requests from server + * @param client MQTT client + * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload + * @param data_cb Callback for each fragment of payload that arrives + * @param arg User supplied argument to both callbacks + */ +void +mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb, + mqtt_incoming_data_cb_t data_cb, void *arg) +{ + LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL); + client->data_cb = data_cb; + client->pub_cb = pub_cb; + client->inpub_arg = arg; +} + +/** + * @ingroup mqtt + * Create a new MQTT client instance + * @return Pointer to instance on success, NULL otherwise + */ +mqtt_client_t * +mqtt_client_new(void) +{ + mqtt_client_t *client = (mqtt_client_t *)mem_malloc(sizeof(mqtt_client_t)); + if (client != NULL) { + memset(client, 0, sizeof(mqtt_client_t)); + } + return client; +} + + +/** + * @ingroup mqtt + * Connect to MQTT server + * @param client MQTT client + * @param ip_addr Server IP + * @param port Server port + * @param cb Connection state change callback + * @param arg User supplied argument to connection callback + * @param client_info Client identification and connection options + * @return ERR_OK if successful, @see err_t enum for other results + */ +err_t +mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg, + const struct mqtt_connect_client_info_t *client_info) +{ + err_t err; + size_t len; + u16_t client_id_length; + /* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */ + u16_t remaining_length = 2 + 4 + 1 + 1 + 2; + u8_t flags = 0, will_topic_len = 0, will_msg_len = 0, client_user_len = 0, client_pass_len = 0; + + LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL); + LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL); + LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL); + LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL); + + if (client->conn_state != TCP_DISCONNECTED) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Already connected\n")); + return ERR_ISCONN; + } + + /* Wipe clean */ + memset(client, 0, sizeof(mqtt_client_t)); + client->connect_arg = arg; + client->connect_cb = cb; + client->keep_alive = client_info->keep_alive; + mqtt_init_requests(client->req_list); + + /* Build connect message */ + if (client_info->will_topic != NULL && client_info->will_msg != NULL) { + flags |= MQTT_CONNECT_FLAG_WILL; + flags |= (client_info->will_qos & 3) << 3; + if (client_info->will_retain) { + flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; + } + len = strlen(client_info->will_topic); + LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL); + LWIP_ERROR("mqtt_client_connect: client_info->will_topic length must be > 0", len > 0, return ERR_VAL); + will_topic_len = (u8_t)len; + len = strlen(client_info->will_msg); + LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL); + will_msg_len = (u8_t)len; + len = remaining_length + 2 + will_topic_len + 2 + will_msg_len; + LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL); + remaining_length = (u16_t)len; + } + + if (client_info->client_user != NULL && client_info->client_user[0] != NULL) { + flags |= MQTT_CONNECT_FLAG_USERNAME; + len = strlen(client_info->client_user); + LWIP_ERROR("mqtt_client_connect: client_info->client_user length overflow", len <= 0xFF, return ERR_VAL); + LWIP_ERROR("mqtt_client_connect: client_info->client_user length must be > 0", len > 0, return ERR_VAL); + client_user_len = (u8_t)len; + len = remaining_length + 2 + client_user_len; + LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL); + remaining_length = (u16_t)len; + } + if (client_info->client_pass != NULL && client_info->client_pass[0] != NULL) { + flags |= MQTT_CONNECT_FLAG_PASSWORD; + len = strlen(client_info->client_pass); + LWIP_ERROR("mqtt_client_connect: client_info->client_pass length overflow", len <= 0xFF, return ERR_VAL); + LWIP_ERROR("mqtt_client_connect: client_info->client_pass length must be > 0", len > 0, return ERR_VAL); + client_pass_len = (u8_t)len; + len = remaining_length + 2 + client_pass_len; + LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL); + remaining_length = (u16_t)len; + } + + /* Don't complicate things, always connect using clean session */ + flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; + + len = strlen(client_info->client_id); + LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL); + client_id_length = (u16_t)len; + len = remaining_length + 2 + client_id_length; + LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL); + remaining_length = (u16_t)len; + + if (mqtt_output_check_space(&client->output, remaining_length) == 0) { + return ERR_MEM; + } + + client->conn = tcp_new(); + if (client->conn == NULL) { + return ERR_MEM; + } + + /* Set arg pointer for callbacks */ + tcp_arg(client->conn, client); + /* Any local address, pick random local port number */ + err = tcp_bind(client->conn, IP_ADDR_ANY, 0); + if (err != ERR_OK) { + LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Error binding to local ip/port, %d\n", err)); + goto tcp_fail; + } + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port)); + + /* Connect to server */ + err = tcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb); + if (err != ERR_OK) { + LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err)); + goto tcp_fail; + } + /* Set error callback */ + tcp_err(client->conn, mqtt_tcp_err_cb); + client->conn_state = TCP_CONNECTING; + + /* Append fixed header */ + mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length); + /* Append Protocol string */ + mqtt_output_append_string(&client->output, "MQTT", 4); + /* Append Protocol level */ + mqtt_output_append_u8(&client->output, 4); + /* Append connect flags */ + mqtt_output_append_u8(&client->output, flags); + /* Append keep-alive */ + mqtt_output_append_u16(&client->output, client_info->keep_alive); + /* Append client id */ + mqtt_output_append_string(&client->output, client_info->client_id, client_id_length); + /* Append will message if used */ + if ((flags & MQTT_CONNECT_FLAG_WILL) != 0) { + mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len); + mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len); + } + if ((flags & MQTT_CONNECT_FLAG_USERNAME) != 0) { + mqtt_output_append_string(&client->output, client_info->client_user, client_user_len); + } + if ((flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) { + mqtt_output_append_string(&client->output, client_info->client_pass, client_pass_len); + } + return ERR_OK; + +tcp_fail: + tcp_abort(client->conn); + client->conn = NULL; + return err; +} + + +/** + * @ingroup mqtt + * Disconnect from MQTT server + * @param client MQTT client + */ +void +mqtt_disconnect(mqtt_client_t *client) +{ + LWIP_ASSERT("mqtt_disconnect: client != NULL", client); + /* If connection in not already closed */ + if (client->conn_state != TCP_DISCONNECTED) { + /* Set conn_state before calling mqtt_close to prevent callback from being called */ + client->conn_state = TCP_DISCONNECTED; + mqtt_close(client, (mqtt_connection_status_t)0); + } +} + +/** + * @ingroup mqtt + * Check connection with server + * @param client MQTT client + * @return 1 if connected to server, 0 otherwise + */ +u8_t +mqtt_client_is_connected(mqtt_client_t *client) +{ + LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client); + return client->conn_state == MQTT_CONNECTED; +} + +#endif /* LWIP_TCP && LWIP_CALLBACK_API */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/netbiosns/netbiosns.c b/sdk/Src/Network/lwip2.0.3/apps/netbiosns/netbiosns.c new file mode 100644 index 0000000..2dfbe65 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/netbiosns/netbiosns.c @@ -0,0 +1,367 @@ +/** + * @file + * NetBIOS name service responder + */ + +/** + * @defgroup netbiosns NETBIOS responder + * @ingroup apps + * + * This is an example implementation of a NetBIOS name server. + * It responds to name queries for a configurable name. + * Name resolving is not supported. + * + * Note that the device doesn't broadcast it's own name so can't + * detect duplicate names! + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/apps/netbiosns.h" + +#if LWIP_IPV4 && LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/udp.h" +#include "lwip/netif.h" + +#include + +/** default port number for "NetBIOS Name service */ +#define NETBIOS_PORT 137 + +/** size of a NetBIOS name */ +#define NETBIOS_NAME_LEN 16 + +/** The Time-To-Live for NetBIOS name responds (in seconds) + * Default is 300000 seconds (3 days, 11 hours, 20 minutes) */ +#define NETBIOS_NAME_TTL 300000u + +/** NetBIOS header flags */ +#define NETB_HFLAG_RESPONSE 0x8000U +#define NETB_HFLAG_OPCODE 0x7800U +#define NETB_HFLAG_OPCODE_NAME_QUERY 0x0000U +#define NETB_HFLAG_AUTHORATIVE 0x0400U +#define NETB_HFLAG_TRUNCATED 0x0200U +#define NETB_HFLAG_RECURS_DESIRED 0x0100U +#define NETB_HFLAG_RECURS_AVAILABLE 0x0080U +#define NETB_HFLAG_BROADCAST 0x0010U +#define NETB_HFLAG_REPLYCODE 0x0008U +#define NETB_HFLAG_REPLYCODE_NOERROR 0x0000U + +/** NetBIOS name flags */ +#define NETB_NFLAG_UNIQUE 0x8000U +#define NETB_NFLAG_NODETYPE 0x6000U +#define NETB_NFLAG_NODETYPE_HNODE 0x6000U +#define NETB_NFLAG_NODETYPE_MNODE 0x4000U +#define NETB_NFLAG_NODETYPE_PNODE 0x2000U +#define NETB_NFLAG_NODETYPE_BNODE 0x0000U + +/** NetBIOS message header */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct netbios_hdr { + PACK_STRUCT_FIELD(u16_t trans_id); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FIELD(u16_t questions); + PACK_STRUCT_FIELD(u16_t answerRRs); + PACK_STRUCT_FIELD(u16_t authorityRRs); + PACK_STRUCT_FIELD(u16_t additionalRRs); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** NetBIOS message name part */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct netbios_name_hdr { + PACK_STRUCT_FLD_8(u8_t nametype); + PACK_STRUCT_FLD_8(u8_t encname[(NETBIOS_NAME_LEN*2)+1]); + PACK_STRUCT_FIELD(u16_t type); + PACK_STRUCT_FIELD(u16_t cls); + PACK_STRUCT_FIELD(u32_t ttl); + PACK_STRUCT_FIELD(u16_t datalen); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FLD_S(ip4_addr_p_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** NetBIOS message */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct netbios_resp +{ + struct netbios_hdr resp_hdr; + struct netbios_name_hdr resp_name; +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef NETBIOS_LWIP_NAME +#define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME +#else +static char netbiosns_local_name[NETBIOS_NAME_LEN]; +#define NETBIOS_LOCAL_NAME netbiosns_local_name +#endif + +struct udp_pcb *netbiosns_pcb; + +/** Decode a NetBIOS name (from packet to string) */ +static int +netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len) +{ + char *pname; + char cname; + char cnbname; + int idx = 0; + + LWIP_UNUSED_ARG(name_dec_len); + + /* Start decoding netbios name. */ + pname = name_enc; + for (;;) { + /* Every two characters of the first level-encoded name + * turn into one character in the decoded name. */ + cname = *pname; + if (cname == '\0') + break; /* no more characters */ + if (cname == '.') + break; /* scope ID follows */ + if (cname < 'A' || cname > 'Z') { + /* Not legal. */ + return -1; + } + cname -= 'A'; + cnbname = cname << 4; + pname++; + + cname = *pname; + if (cname == '\0' || cname == '.') { + /* No more characters in the name - but we're in + * the middle of a pair. Not legal. */ + return -1; + } + if (cname < 'A' || cname > 'Z') { + /* Not legal. */ + return -1; + } + cname -= 'A'; + cnbname |= cname; + pname++; + + /* Do we have room to store the character? */ + if (idx < NETBIOS_NAME_LEN) { + /* Yes - store the character. */ + name_dec[idx++] = (cnbname!=' '?cnbname:'\0'); + } + } + + return 0; +} + +#if 0 /* function currently unused */ +/** Encode a NetBIOS name (from string to packet) - currently unused because + we don't ask for names. */ +static int +netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len) +{ + char *pname; + char cname; + unsigned char ucname; + int idx = 0; + + /* Start encoding netbios name. */ + pname = name_enc; + + for (;;) { + /* Every two characters of the first level-encoded name + * turn into one character in the decoded name. */ + cname = *pname; + if (cname == '\0') + break; /* no more characters */ + if (cname == '.') + break; /* scope ID follows */ + if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) { + /* Not legal. */ + return -1; + } + + /* Do we have room to store the character? */ + if (idx >= name_dec_len) { + return -1; + } + + /* Yes - store the character. */ + ucname = cname; + name_dec[idx++] = ('A'+((ucname>>4) & 0x0F)); + name_dec[idx++] = ('A'+( ucname & 0x0F)); + pname++; + } + + /* Fill with "space" coding */ + for (;idx < name_dec_len - 1;) { + name_dec[idx++] = 'C'; + name_dec[idx++] = 'A'; + } + + /* Terminate string */ + name_dec[idx] = '\0'; + + return 0; +} +#endif /* 0 */ + +/** NetBIOS Name service recv callback */ +static void +netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + LWIP_UNUSED_ARG(arg); + + /* if packet is valid */ + if (p != NULL) { + char netbios_name[NETBIOS_NAME_LEN+1]; + struct netbios_hdr* netbios_hdr = (struct netbios_hdr*)p->payload; + struct netbios_name_hdr* netbios_name_hdr = (struct netbios_name_hdr*)(netbios_hdr+1); + + /* we only answer if we got a default interface */ + if (netif_default != NULL) { + /* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */ + /* if the packet is a NetBIOS name query question */ + if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) && + ((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) && + (netbios_hdr->questions == PP_NTOHS(1))) { + /* decode the NetBIOS name */ + netbiosns_name_decode((char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name)); + /* if the packet is for us */ + if (lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) == 0) { + struct pbuf *q; + struct netbios_resp *resp; + + q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM); + if (q != NULL) { + resp = (struct netbios_resp*)q->payload; + + /* prepare NetBIOS header response */ + resp->resp_hdr.trans_id = netbios_hdr->trans_id; + resp->resp_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE | + NETB_HFLAG_OPCODE_NAME_QUERY | + NETB_HFLAG_AUTHORATIVE | + NETB_HFLAG_RECURS_DESIRED); + resp->resp_hdr.questions = 0; + resp->resp_hdr.answerRRs = PP_HTONS(1); + resp->resp_hdr.authorityRRs = 0; + resp->resp_hdr.additionalRRs = 0; + + /* prepare NetBIOS header datas */ + MEMCPY( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname)); + resp->resp_name.nametype = netbios_name_hdr->nametype; + resp->resp_name.type = netbios_name_hdr->type; + resp->resp_name.cls = netbios_name_hdr->cls; + resp->resp_name.ttl = PP_HTONL(NETBIOS_NAME_TTL); + resp->resp_name.datalen = PP_HTONS(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr)); + resp->resp_name.flags = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE); + ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default)); + + /* send the NetBIOS response */ + udp_sendto(upcb, q, addr, port); + + /* free the "reference" pbuf */ + pbuf_free(q); + } + } + } + } + /* free the pbuf */ + pbuf_free(p); + } +} + +/** + * @ingroup netbiosns + * Init netbios responder + */ +void +netbiosns_init(void) +{ +#ifdef NETBIOS_LWIP_NAME + LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN); +#endif + + netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + if (netbiosns_pcb != NULL) { + /* we have to be allowed to send broadcast packets! */ + ip_set_option(netbiosns_pcb, SOF_BROADCAST); + udp_bind(netbiosns_pcb, IP_ANY_TYPE, NETBIOS_PORT); + udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb); + } +} + +#ifndef NETBIOS_LWIP_NAME +/** + * @ingroup netbiosns + * Set netbios name. ATTENTION: the hostname must be less than 15 characters! + */ +void +netbiosns_set_name(const char* hostname) +{ + size_t copy_len = strlen(hostname); + LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN); + if (copy_len >= NETBIOS_NAME_LEN) { + copy_len = NETBIOS_NAME_LEN - 1; + } + MEMCPY(netbiosns_local_name, hostname, copy_len + 1); +} +#endif + +/** + * @ingroup netbiosns + * Stop netbios responder + */ +void +netbiosns_stop(void) +{ + if (netbiosns_pcb != NULL) { + udp_remove(netbiosns_pcb); + netbiosns_pcb = NULL; + } +} + +#endif /* LWIP_IPV4 && LWIP_UDP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CCodeGeneration.csproj b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CCodeGeneration.csproj new file mode 100644 index 0000000..ddeae90 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CCodeGeneration.csproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {7DA7C0AB-0982-4BF5-9324-F59A7A08D65B} + Library + Properties + CCodeGeneration + CCodeGeneration + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CFile.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CFile.cs new file mode 100644 index 0000000..ba826c5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CFile.cs @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; + +namespace CCodeGeneration +{ + public class CFile: CodeContainerBase + { + public CFile() + { + base.IncreaseLevel = false; + } + + public void Save(CGenerator generator) + { + if (generator == null) + { + throw new ArgumentNullException("generator"); + } + + this.GenerateCode(0, generator); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CGenerator.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CGenerator.cs new file mode 100644 index 0000000..5e41ece --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CGenerator.cs @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.IO; + +namespace CCodeGeneration +{ + public class CGenerator + { + public TextWriter OutputStream { get; private set; } + public string File { get; private set; } + public uint IndentCount { get; private set; } + public string IndentChar { get; private set; } + public string NewLine { get; private set; } + + public CGenerator(System.IO.TextWriter outputStream, string file, uint indentCount, string indentChar, string newLine) + { + this.OutputStream = outputStream; + this.File = file; + this.IndentCount = indentCount; + this.IndentChar = indentChar; + this.NewLine = newLine; + } + + public string FileName + { + get + { + if (!String.IsNullOrWhiteSpace(this.File)) + { + return Path.GetFileName(this.File); + } + + return null; + } + } + + public void WriteSequence(string value, uint repetitions) + { + while (repetitions > 0) + { + this.OutputStream.Write(value); + repetitions--; + } + } + + public void IndentLine(int level) + { + while (level > 0) + { + WriteSequence(this.IndentChar, this.IndentCount); + level--; + } + } + + public void WriteNewLine() + { + this.OutputStream.Write(this.NewLine); + } + + public void WriteMultilineString(string value, int level = 0) + { + if (String.IsNullOrEmpty(value)) + { + return; + } + + // only \n and \r\n are recognized as linebreaks + string[] lines = value.Split(new char[] { '\n' }, StringSplitOptions.None); + + for (int l = 0; l < (lines.Length - 1); l++) + { + if (lines[l].EndsWith("\r")) + { + this.OutputStream.Write(lines[l].Substring(0, lines[l].Length-1)); + } + else + { + this.OutputStream.Write(lines[l]); + } + + this.WriteNewLine(); + this.IndentLine(level); + } + + this.OutputStream.Write(lines[lines.Length - 1]); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Code.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Code.cs new file mode 100644 index 0000000..98d508e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Code.cs @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +namespace CCodeGeneration +{ + public class Code: CodeElement + { + public string Code_ { get; set; } + + public Code() + { + } + + public Code(string code) + { + this.Code_ = code; + } + + public override void GenerateCode(int level, CGenerator generator) + { + generator.IndentLine(level); + generator.WriteMultilineString(this.Code_, level); + generator.WriteNewLine(); + } + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CodeContainerBase.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CodeContainerBase.cs new file mode 100644 index 0000000..39c7f39 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CodeContainerBase.cs @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System.Collections.Generic; +using System; + +namespace CCodeGeneration +{ + public class CodeContainerBase: CodeElement + { + private readonly List declarations = new List(); + private readonly List innerElements = new List(); + private bool increaseLevel = true; + + public List Declarations + { + get { return this.declarations; } + } + + public List InnerElements + { + get { return this.innerElements; } + } + + protected bool IncreaseLevel + { + get { return this.increaseLevel; } + set { this.increaseLevel = value; } + } + + public void AddElements(IList elements, params CodeElement[] spacerElements) + { + if (elements != null) + { + if ((spacerElements == null) || (spacerElements.Length == 0)) + { + this.innerElements.AddRange(elements); + } + else + { + bool spacerAdded = false; + + foreach (CodeElement element in elements) + { + this.innerElements.Add(element); + this.innerElements.AddRange(spacerElements); + spacerAdded = true; + } + + if (spacerAdded) + { + // remove last spacer again + this.innerElements.RemoveRange(this.innerElements.Count - spacerElements.Length, spacerElements.Length); + } + } + } + } + + public CodeElement AddElement(CodeElement element) + { + if (element != null) + { + this.innerElements.Add(element); + } + + return element; + } + + public Code AddCode(string code) + { + return this.AddElement(new Code(code)) as Code; + } + + public Code AddCodeFormat(string codeFormat, params object[] args) + { + return this.AddElement(new Code(String.Format(codeFormat, args))) as Code; + } + + public CodeElement AddDeclaration(CodeElement declaration) + { + if (declaration != null) + { + this.declarations.Add(declaration); + } + + return declaration; + } + + public override void GenerateCode(int level, CGenerator generator) + { + if (this.increaseLevel) + level++; + + if (this.declarations.Count > 0) + { + foreach (CodeElement element in this.declarations) + { + element.GenerateCode(level, generator); + } + + EmptyLine.SingleLine.GenerateCode(level, generator); + } + + foreach (CodeElement element in this.innerElements) + { + element.GenerateCode(level, generator); + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CodeElement.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CodeElement.cs new file mode 100644 index 0000000..9998d19 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/CodeElement.cs @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +namespace CCodeGeneration +{ + public class CodeElement + { + public virtual void GenerateCode(int level, CGenerator generator) + { + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Comment.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Comment.cs new file mode 100644 index 0000000..5f80acc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Comment.cs @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +namespace CCodeGeneration +{ + public class Comment: CodeElement + { + public const string CommentStart = "/*"; + public const string CommentEnd = "*/"; + + public string Comment_ { get; set; } + public bool SingleLine { get; set; } + + public Comment() + { + } + + public Comment(string comment, bool singleLine = false) + { + this.Comment_ = comment; + this.SingleLine = singleLine; + } + + public override void GenerateCode(int level, CGenerator generator) + { + generator.IndentLine(level); + generator.OutputStream.Write(CommentStart); + + if (!this.SingleLine) + { + generator.WriteNewLine(); + generator.IndentLine(level); + generator.WriteMultilineString(this.Comment_, level); + generator.WriteNewLine(); + generator.IndentLine(level); + } + else + { + generator.OutputStream.Write(" " + Comment_ + " "); + } + + generator.OutputStream.Write(CommentEnd); + generator.WriteNewLine(); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/EmptyLine.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/EmptyLine.cs new file mode 100644 index 0000000..d93ab39 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/EmptyLine.cs @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +namespace CCodeGeneration +{ + public class EmptyLine : CodeElement + { + public static readonly EmptyLine SingleLine = new EmptyLine(); + public static readonly EmptyLine TwoLines = new EmptyLine(2); + public static readonly EmptyLine ThreeLines = new EmptyLine(3); + + public uint Count { get; set; } + + public EmptyLine() + { + this.Count = 1; + } + + public EmptyLine(uint count) + { + this.Count = count; + } + + public override void GenerateCode(int level, CGenerator generator) + { + uint c = this.Count; + + while (c > 0) + { + generator.WriteNewLine(); + c--; + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Function.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Function.cs new file mode 100644 index 0000000..cf22b98 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Function.cs @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; + +namespace CCodeGeneration +{ + public class Function: CodeContainerBase + { + public string Name { get; set; } + public bool IsStatic { get; set; } + + private readonly List parameter = new List(); + private VariableType returnType = VariableType.Void; + + public Function() + { + } + + public Function(string name, bool isStatic = false) + { + this.Name = name; + this.IsStatic = isStatic; + } + + public List Parameter + { + get { return this.parameter; } + } + + public VariableType ReturnType + { + get { return this.returnType; } + set + { + if (value == null) + { + throw new ArgumentNullException("ReturnValue"); + } + this.returnType = value; + } + } + + public static Function FromDeclaration(FunctionDeclaration decl) + { + Function result = new Function(decl.Name, decl.IsStatic); + result.ReturnType = decl.ReturnType.Clone() as VariableType; + + foreach (VariableType param in decl.Parameter) + { + result.parameter.Add(param.Clone() as VariableType); + } + + return result; + } + + public override void GenerateCode(int level, CGenerator generator) + { + generator.IndentLine(level); + + if (this.IsStatic) + { + generator.OutputStream.Write("static "); + } + + this.returnType.GenerateCode(generator); + generator.OutputStream.Write(" " + this.Name + "("); + + if (this.Parameter.Count > 0) + { + for (int i = 0; i < this.parameter.Count; i++) + { + this.parameter[i].GenerateCode(generator); + + if (i < (this.parameter.Count - 1)) + { + generator.OutputStream.Write(", "); + } + } + } + else + { + generator.OutputStream.Write("void"); + } + + generator.OutputStream.Write(")"); + generator.WriteNewLine(); + generator.IndentLine(level); + generator.OutputStream.Write("{"); + generator.WriteNewLine(); + + base.GenerateCode(level, generator); + + generator.IndentLine(level); + generator.OutputStream.Write("}"); + generator.WriteNewLine(); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/FunctionDeclaration.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/FunctionDeclaration.cs new file mode 100644 index 0000000..e3bde2e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/FunctionDeclaration.cs @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; + +namespace CCodeGeneration +{ + public class FunctionDeclaration: CodeElement + { + public string Name { get; set; } + public bool IsStatic { get; set; } + public bool IsExtern { get; set; } + + private readonly List parameter = new List(); + private VariableType returnType = VariableType.Void; + + public FunctionDeclaration() + { + } + + public FunctionDeclaration(string name, bool isStatic = false, bool isExtern = false) + { + this.Name = name; + this.IsStatic = isStatic; + this.IsExtern = isExtern; + } + + public List Parameter + { + get { return this.parameter; } + } + + public VariableType ReturnType + { + get { return this.returnType; } + set + { + if (value == null) + { + throw new ArgumentNullException("ReturnValue"); + } + this.returnType = value; + } + } + + public override void GenerateCode(int level, CGenerator generator) + { + generator.IndentLine(level); + + if (this.IsExtern) + { + generator.OutputStream.Write("extern "); + } + + if (this.IsStatic) + { + generator.OutputStream.Write("static "); + } + + this.returnType.GenerateCode(generator); + generator.OutputStream.Write(" " + this.Name + "("); + + if (this.Parameter.Count > 0) + { + for (int i = 0; i < this.parameter.Count; i++) + { + this.parameter[i].GenerateCode(generator); + + if (i < (this.parameter.Count - 1)) + { + generator.OutputStream.Write(", "); + } + } + } + else + { + generator.OutputStream.Write("void"); + } + + generator.OutputStream.Write(");"); + generator.WriteNewLine(); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/IfThenElse.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/IfThenElse.cs new file mode 100644 index 0000000..205583f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/IfThenElse.cs @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; + +namespace CCodeGeneration +{ + public class ElseIf : CodeContainerBase + { + public string Condition { get; set; } + + public ElseIf() + { + } + + public ElseIf(string condition) + { + this.Condition = condition; + } + + public override void GenerateCode(int level, CGenerator generator) + { + if (!String.IsNullOrWhiteSpace(this.Condition)) + { + generator.IndentLine(level); + generator.OutputStream.Write(String.Format("else if ({0})", this.Condition)); + generator.WriteNewLine(); + generator.IndentLine(level); + generator.OutputStream.Write("{"); + generator.WriteNewLine(); + + base.GenerateCode(level, generator); + + generator.IndentLine(level); + generator.OutputStream.Write("}"); + generator.WriteNewLine(); + } + } + } + + public class IfThenElse: CodeContainerBase + { + public string Condition { get; set; } + + private List elseIf = new List(); + private CodeContainerBase else_ = new CodeContainerBase(); + + public IfThenElse() + { + } + + public IfThenElse(string condition) + { + this.Condition = condition; + } + + public List ElseIf + { + get { return this.elseIf; } + } + + public CodeContainerBase Else + { + get { return this.else_; } + } + + public override void GenerateCode(int level, CGenerator generator) + { + if (!String.IsNullOrWhiteSpace(this.Condition)) + { + generator.IndentLine(level); + generator.OutputStream.Write(String.Format("if ({0})", this.Condition)); + generator.WriteNewLine(); + generator.IndentLine(level); + generator.OutputStream.Write("{"); + generator.WriteNewLine(); + + base.GenerateCode(level, generator); + + generator.IndentLine(level); + generator.OutputStream.Write("}"); + generator.WriteNewLine(); + + foreach (ElseIf elif in this.elseIf) + { + elif.GenerateCode(level, generator); + } + + if (this.else_.InnerElements.Count > 0) + { + generator.IndentLine(level); + generator.OutputStream.Write("else"); + generator.WriteNewLine(); + generator.IndentLine(level); + generator.OutputStream.Write("{"); + generator.WriteNewLine(); + + this.else_.GenerateCode(level, generator); + + generator.IndentLine(level); + generator.OutputStream.Write("}"); + generator.WriteNewLine(); + } + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_If.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_If.cs new file mode 100644 index 0000000..9451a9a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_If.cs @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; + +namespace CCodeGeneration +{ + public class PP_If: CodeContainerBase + { + public string Condition { get; set; } + + public PP_If() + { + base.IncreaseLevel = false; + } + + public PP_If(string condition) + : this() + { + this.Condition = condition; + } + + + public override void GenerateCode(int level, CGenerator generator) + { + if (!String.IsNullOrWhiteSpace(this.Condition)) + { + generator.OutputStream.Write("#if " + this.Condition); + generator.WriteNewLine(); + + base.GenerateCode(level, generator); + + generator.OutputStream.Write("#endif /* " + this.Condition + " */"); + generator.WriteNewLine(); + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Ifdef.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Ifdef.cs new file mode 100644 index 0000000..8e867fb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Ifdef.cs @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; + +namespace CCodeGeneration +{ + public class PP_Ifdef: CodeContainerBase + { + public string Macro { get; set; } + public bool Inverted { get; set; } + + public PP_Ifdef() + { + base.IncreaseLevel = false; + } + + public PP_Ifdef(string macro, bool inverted = false) + : this() + { + this.Macro = macro; + this.Inverted = inverted; + } + + + public override void GenerateCode(int level, CGenerator generator) + { + if (!String.IsNullOrWhiteSpace(this.Macro)) + { + if (this.Inverted) + { + generator.OutputStream.Write("#ifndef " + this.Macro); + } + else + { + generator.OutputStream.Write("#ifdef " + this.Macro); + } + generator.WriteNewLine(); + + base.GenerateCode(level, generator); + + generator.OutputStream.Write("#endif /* " + this.Macro + " */"); + generator.WriteNewLine(); + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Include.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Include.cs new file mode 100644 index 0000000..36ca4bd --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Include.cs @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; + +namespace CCodeGeneration +{ + public class PP_Include : CodeElement + { + public string File { get; set; } + public bool IsLocal { get; set; } + + public PP_Include() + { + this.IsLocal = true; + } + + public PP_Include(string file, bool isLocal = true) + { + this.File = file; + this.IsLocal = isLocal; + } + + public override void GenerateCode(int level, CGenerator generator) + { + if (!String.IsNullOrWhiteSpace(this.File)) + { + // includes are never indented + if (this.IsLocal) + { + generator.OutputStream.Write("#include \"" + this.File + "\""); + } + else + { + generator.OutputStream.Write("#include <" + this.File + ">"); + } + + generator.WriteNewLine(); + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Macro.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Macro.cs new file mode 100644 index 0000000..0a12de8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PP_Macro.cs @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +namespace CCodeGeneration +{ + public class PP_Macro: CodeElement + { + public string Name { get; set; } + public string Value { get; set; } + + public PP_Macro() + { + } + + public PP_Macro(string name, string value) + { + this.Name = name; + this.Value = value; + } + + + public override void GenerateCode(int level, CGenerator generator) + { + // macros are not indented at all + generator.OutputStream.Write("#define " + this.Name + " "); + generator.WriteMultilineString(this.Value); + generator.WriteNewLine(); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PlainText.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PlainText.cs new file mode 100644 index 0000000..f8d55af --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/PlainText.cs @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +namespace CCodeGeneration +{ + public class PlainText : CodeElement + { + public string Value { get; set; } + + public PlainText(string value) + { + this.Value = value; + } + + public override void GenerateCode(int level, CGenerator generator) + { + generator.WriteMultilineString(this.Value); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Properties/AssemblyInfo.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c9c3a5a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("CCodeGeneration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CCodeGeneration")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("8f07a0fa-86f4-48a0-97c7-f94fc5c3f103")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Switch.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Switch.cs new file mode 100644 index 0000000..c3f4d4d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/Switch.cs @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; + +namespace CCodeGeneration +{ + public class SwitchCase : CodeContainerBase + { + public string Value { get; set; } + + public SwitchCase() + { + } + + public SwitchCase(string value) + { + this.Value = value; + } + + public bool IsDefault + { + get { return (this.Value.ToLowerInvariant() == "default"); } + } + + public static SwitchCase GenerateDefault() + { + return new SwitchCase("default"); + } + + public override void GenerateCode(int level, CGenerator generator) + { + if (!String.IsNullOrWhiteSpace(this.Value)) + { + generator.IndentLine(level); + if (this.IsDefault) + { + generator.OutputStream.Write("default:"); + } + else + { + generator.OutputStream.Write(String.Format("case {0}:", this.Value)); + } + generator.WriteNewLine(); + generator.IndentLine(level + 1); + generator.OutputStream.Write("{"); + generator.WriteNewLine(); + + base.GenerateCode(level + 1, generator); + + generator.IndentLine(level + 1); + generator.OutputStream.Write("}"); + generator.WriteNewLine(); + + generator.IndentLine(level + 1); + generator.OutputStream.Write("break;"); + generator.WriteNewLine(); + } + } + } + + public class Switch: CodeElement + { + public string SwitchVar { get; set; } + + private List switches = new List(); + + public Switch() + { + } + + public Switch(string switchVar) + { + this.SwitchVar = switchVar; + } + + public List Switches + { + get { return this.switches; } + } + + public override void GenerateCode(int level, CGenerator generator) + { + if (!String.IsNullOrWhiteSpace(this.SwitchVar)) + { + generator.IndentLine(level); + generator.OutputStream.Write(String.Format("switch ({0})", this.SwitchVar)); + generator.WriteNewLine(); + generator.IndentLine(level); + generator.OutputStream.Write("{"); + generator.WriteNewLine(); + + SwitchCase defaultCase = null; // generate 'default' always as last case + foreach (SwitchCase switchCase in this.switches) + { + if (switchCase.IsDefault) + { + defaultCase = switchCase; + } + else + { + switchCase.GenerateCode(level + 1, generator); + } + } + if (defaultCase != null) + { + defaultCase.GenerateCode(level + 1, generator); + } + + generator.IndentLine(level); + generator.OutputStream.Write("}"); + generator.WriteNewLine(); + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariableDeclaration.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariableDeclaration.cs new file mode 100644 index 0000000..6bb8639 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariableDeclaration.cs @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; + +namespace CCodeGeneration +{ + public class VariableDeclaration : CodeElement + { + public VariableType Type { get; set; } + public string InitialValue { get; set; } + public bool IsStatic { get; set; } + + public VariableDeclaration() + : base() + { + } + + public VariableDeclaration(VariableType type, string initialValue = null, bool isStatic = false) : + base() + { + this.Type = type; + this.InitialValue = initialValue; + this.IsStatic = isStatic; + } + + public override void GenerateCode(int level, CGenerator generator) + { + if (this.Type != null) + { + generator.IndentLine(level); + + if (this.IsStatic) + { + generator.OutputStream.Write("static "); + } + + // declare the variable + this.Type.GenerateCode(generator); + + if (!String.IsNullOrWhiteSpace(this.InitialValue)) + { + // add initialization value + generator.OutputStream.Write(" = "); + generator.WriteMultilineString(this.InitialValue, level); + } + + generator.OutputStream.Write(";"); + generator.WriteNewLine(); + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariablePrototype.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariablePrototype.cs new file mode 100644 index 0000000..b5301c5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariablePrototype.cs @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +namespace CCodeGeneration +{ + public class VariablePrototype : CodeElement + { + public VariableType Type { get; set; } + + public VariablePrototype() + : base() + { + } + + public VariablePrototype(VariableType type) : + base() + { + Type = type; + } + + public static VariablePrototype FromVariableDeclaration(VariableDeclaration declaration) + { + return new VariablePrototype(declaration.Type); + } + + + public override void GenerateCode(int level, CGenerator generator) + { + if (this.Type != null) + { + generator.IndentLine(level); + + generator.OutputStream.Write("extern "); + + // declare the variable + this.Type.GenerateCode(generator); + + generator.OutputStream.Write(";"); + generator.WriteNewLine(); + } + } + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariableType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariableType.cs new file mode 100644 index 0000000..a7a9f66 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/CCodeGeneration/VariableType.cs @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Text; + +namespace CCodeGeneration +{ + public enum ConstType + { + None, + Value, + Indirection, + Both + } + + public class VariableType : ICloneable + { + public const string VoidString = "void"; + public static readonly VariableType Void = new VariableType(null, "void"); + + public string Name { get; set; } + public string Type { get; set; } + public string Indirection { get; set; } + public ConstType Const { get; set; } + public string ArraySpecifier { get; set; } + + public VariableType() + { + } + + public VariableType(string name, string type, string indirection = null, ConstType const_ = ConstType.None, string arraySpecifier = null) + { + this.Name = name; + this.Type = type; + this.Indirection = indirection; + this.Const = const_; + this.ArraySpecifier = arraySpecifier; + } + + public void GenerateCode(CGenerator generator) + { + if (!String.IsNullOrWhiteSpace(this.Type)) + { + generator.OutputStream.Write(this.ToString().Trim()); + } + } + + public override string ToString() + { + if (!String.IsNullOrWhiteSpace(this.Type)) + { + StringBuilder vt = new StringBuilder(); + + if ((this.Const == ConstType.Value) || (this.Const == ConstType.Both)) + { + vt.Append("const "); + } + + vt.Append(this.Type); + vt.Append(" "); + + if (!String.IsNullOrWhiteSpace(this.Indirection)) + { + vt.Append(this.Indirection); + } + + if ((this.Const == ConstType.Indirection) || (this.Const == ConstType.Both)) + { + vt.Append("const "); + } + + if (!String.IsNullOrWhiteSpace(this.Name)) + { + vt.Append(this.Name); + } + + if (this.ArraySpecifier != null) + { + vt.Append("["); + vt.Append(this.ArraySpecifier); + vt.Append("]"); + } + + return vt.ToString().Trim(); + } + + return base.ToString(); + } + + #region ICloneable Member + + public object Clone() + { + // we only have value types as members -> simply use .net base function + return this.MemberwiseClone(); + } + + #endregion + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler.sln b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler.sln new file mode 100644 index 0000000..232ac90 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler.sln @@ -0,0 +1,47 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LwipMibCompiler", "LwipMibCompiler\LwipMibCompiler.csproj", "{C25D5640-D999-49BD-82E0-A1975296A91E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LwipSnmpCodeGeneration", "LwipSnmpCodeGeneration\LwipSnmpCodeGeneration.csproj", "{AABCAB90-1540-45D4-A159-14831A54E9A3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CCodeGeneration", "CCodeGeneration\CCodeGeneration.csproj", "{7DA7C0AB-0982-4BF5-9324-F59A7A08D65B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpSnmpLib.Mib", "SharpSnmpLib\SharpSnmpLib.Mib.csproj", "{CBE20411-5DB7-487D-825D-7694267BB6F5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LwipMibViewer", "LwipMibViewer\LwipMibViewer.csproj", "{86CC0B65-7985-4017-A252-0A7A18DCAEF3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7DA7C0AB-0982-4BF5-9324-F59A7A08D65B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DA7C0AB-0982-4BF5-9324-F59A7A08D65B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DA7C0AB-0982-4BF5-9324-F59A7A08D65B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DA7C0AB-0982-4BF5-9324-F59A7A08D65B}.Release|Any CPU.Build.0 = Release|Any CPU + {86CC0B65-7985-4017-A252-0A7A18DCAEF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86CC0B65-7985-4017-A252-0A7A18DCAEF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86CC0B65-7985-4017-A252-0A7A18DCAEF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86CC0B65-7985-4017-A252-0A7A18DCAEF3}.Release|Any CPU.Build.0 = Release|Any CPU + {AABCAB90-1540-45D4-A159-14831A54E9A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AABCAB90-1540-45D4-A159-14831A54E9A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AABCAB90-1540-45D4-A159-14831A54E9A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AABCAB90-1540-45D4-A159-14831A54E9A3}.Release|Any CPU.Build.0 = Release|Any CPU + {C25D5640-D999-49BD-82E0-A1975296A91E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C25D5640-D999-49BD-82E0-A1975296A91E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C25D5640-D999-49BD-82E0-A1975296A91E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C25D5640-D999-49BD-82E0-A1975296A91E}.Release|Any CPU.Build.0 = Release|Any CPU + {CBE20411-5DB7-487D-825D-7694267BB6F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBE20411-5DB7-487D-825D-7694267BB6F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBE20411-5DB7-487D-825D-7694267BB6F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBE20411-5DB7-487D-825D-7694267BB6F5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = LwipMibViewer\LwipMibViewer.csproj + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/LwipMibCompiler.csproj b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/LwipMibCompiler.csproj new file mode 100644 index 0000000..fa4108e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/LwipMibCompiler.csproj @@ -0,0 +1,73 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {C25D5640-D999-49BD-82E0-A1975296A91E} + Exe + Properties + LwipMibCompiler + LwipMibCompiler + v4.0 + 512 + + + + true + bin\Debug\ + DEBUG;TRACE + full + AnyCPU + prompt + false + false + 4 + false + false + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + 4 + false + + + + + + + + + + + + + + {7DA7C0AB-0982-4BF5-9324-F59A7A08D65B} + CCodeGeneration + + + {AABCAB90-1540-45D4-A159-14831A54E9A3} + LwipSnmpCodeGeneration + + + {CBE20411-5DB7-487D-825D-7694267BB6F5} + SharpSnmpLib.Mib + + + + + + \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/Program.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/Program.cs new file mode 100644 index 0000000..515a260 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/Program.cs @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using CCodeGeneration; +using Lextm.SharpSnmpLib.Mib; +using Lextm.SharpSnmpLib.Mib.Elements.Entities; +using Lextm.SharpSnmpLib.Mib.Elements.Types; +using LwipSnmpCodeGeneration; + +namespace LwipMibCompiler +{ + class Program + { + private static readonly Regex _alphaNumericRegex = new Regex("[^a-zA-Z0-9]"); + + static void Main(string[] args) + { + Console.WriteLine("lwIP MIB Compiler"); + Console.WriteLine(""); + + // check args + if ((args.Length < 2) || String.IsNullOrWhiteSpace(args[0]) || String.IsNullOrWhiteSpace(args[1])) + { + PrintUsage(); + return; + } + + string mibFile = args[0]; + if (!File.Exists(mibFile)) + { + Console.WriteLine(String.Format("Unable to find file '{0}'!", mibFile)); + } + + string destFile = args[1]; + string destHeaderFile; + + if (Directory.Exists(destFile)) + { + // only directory passed -> create dest filename from mib filename + string mibFileName = Path.GetFileNameWithoutExtension(mibFile).ToLowerInvariant(); + destFile = Path.Combine(destFile, mibFileName + ".c"); + } + + string destFileExt = Path.GetExtension(destFile); + if (!String.IsNullOrEmpty(destFileExt)) + { + destHeaderFile = destFile.Substring(0, destFile.Length - destFileExt.Length); + } + else + { + destHeaderFile = destFile; + } + destHeaderFile += ".h"; + + for (int i=2; i [ ...]", appName)); + Console.WriteLine(""); + Console.WriteLine(" "); + Console.WriteLine(" Path and filename of MIB file to convert."); + Console.WriteLine(""); + Console.WriteLine(" "); + Console.WriteLine(" Destination path and file. If a path is passed only, filename is auto"); + Console.WriteLine(" generated from MIB file name."); + Console.WriteLine(""); + Console.WriteLine(" "); + Console.WriteLine(" It's important to provide all referred MIB's in order to correctly "); + Console.WriteLine(" resolve all used types."); + Console.WriteLine(""); + } + + + #region Generation of LWIP Object Tree + + private static void ProcessMibTreeNode(MibTreeNode mibTreeNode, SnmpTreeNode assignedSnmpNode) + { + foreach (MibTreeNode mtn in mibTreeNode.ChildNodes) + { + // in theory container nodes may also be scalars or tables at the same time (for now only process real containers) + if (mtn.NodeType == MibTreeNodeType.Container) + { + SnmpTreeNode snmpTreeNode = GenerateSnmpTreeNode(mtn, assignedSnmpNode); + assignedSnmpNode.ChildNodes.Add(snmpTreeNode); + + ProcessMibTreeNode(mtn, snmpTreeNode); + } + else if ((mtn.NodeType & MibTreeNodeType.Scalar) != 0) + { + SnmpScalarNode snmpScalarNode = GenerateSnmpScalarNode(mtn, assignedSnmpNode); + if (snmpScalarNode != null) + { + assignedSnmpNode.ChildNodes.Add(snmpScalarNode); + } + } + else if ((mtn.NodeType & MibTreeNodeType.Table) != 0) + { + SnmpTableNode snmpTableNode = GenerateSnmpTableNode(mtn, assignedSnmpNode); + if (snmpTableNode != null) + { + assignedSnmpNode.ChildNodes.Add(snmpTableNode); + } + } + } + } + + private static SnmpTreeNode GenerateSnmpTreeNode(MibTreeNode mibTreeNode, SnmpTreeNode parentNode) + { + SnmpTreeNode result = new SnmpTreeNode(parentNode); + result.Name = _alphaNumericRegex.Replace(mibTreeNode.Entity.Name, "").ToLowerInvariant(); + result.Oid = mibTreeNode.Entity.Value; + result.FullOid = MibTypesResolver.ResolveOid(mibTreeNode.Entity).GetOidString(); + + return result; + } + + private static SnmpScalarNode GenerateSnmpScalarNode(MibTreeNode mibTreeNode, SnmpTreeNode parentNode, bool ignoreAccessibleFlag = false) + { + ObjectType ote = mibTreeNode.Entity as ObjectType; + if (ote != null) + { + return GenerateSnmpScalarNode(ote, parentNode, ignoreAccessibleFlag); + } + + return null; + } + + private static SnmpScalarNode GenerateSnmpScalarNode(ObjectType ote, SnmpTreeNode parentNode, bool ignoreAccessibleFlag = false) + { + SnmpScalarNode result; + + ITypeAssignment mibType = ote.BaseType; + IntegerType it = (mibType as IntegerType); + if (it != null) + { + if (ote.ReferredType.Name == Symbol.TruthValue.ToString()) + { + result = new SnmpScalarNodeTruthValue(parentNode); + } + else if ((it.Type == IntegerType.Types.Integer) || (it.Type == IntegerType.Types.Integer32)) + { + result = new SnmpScalarNodeInt(parentNode); + } + else + { + Console.WriteLine(String.Format("Unsupported IntegerType '{0}'!", it.Type)); + return null; + } + if (it.IsEnumeration) + { + result.Restrictions.AddRange(CreateRestrictions(it.Enumeration)); + } + else + { + result.Restrictions.AddRange(CreateRestrictions(it.Ranges)); + } + } + else + { + UnsignedType ut = (mibType as UnsignedType); + if (ut != null) + { + if ((ut.Type == UnsignedType.Types.Unsigned32) || + (ut.Type == UnsignedType.Types.Gauge32)) + { + result = new SnmpScalarNodeUint(SnmpDataType.Gauge, parentNode); + } + else if (ut.Type == UnsignedType.Types.Counter32) + { + result = new SnmpScalarNodeUint(SnmpDataType.Counter, parentNode); + } + else if (ut.Type == UnsignedType.Types.TimeTicks) + { + result = new SnmpScalarNodeUint(SnmpDataType.TimeTicks, parentNode); + } + else if (ut.Type == UnsignedType.Types.Counter64) + { + result = new SnmpScalarNodeCounter64(parentNode); + if ((ut.Ranges != null) && (ut.Ranges.Count > 0)) + { + Console.WriteLine(String.Format("Generation of ranges is not supported for Counter64 type!")); + return null; + } + } + else + { + Console.WriteLine(String.Format("Unsupported UnsignedType '{0}'!", ut.Type)); + return null; + } + result.Restrictions.AddRange(CreateRestrictions(ut.Ranges)); + } + else if (mibType is IpAddressType) + { + result = new SnmpScalarNodeOctetString(SnmpDataType.IpAddress, parentNode); + result.Restrictions.AddRange(CreateRestrictions((mibType as OctetStringType).Size)); + } + else if (mibType is OpaqueType) + { + result = new SnmpScalarNodeOctetString(SnmpDataType.Opaque, parentNode); + result.Restrictions.AddRange(CreateRestrictions((mibType as OctetStringType).Size)); + } + else if (mibType is OctetStringType) + { + result = new SnmpScalarNodeOctetString(SnmpDataType.OctetString, parentNode); + result.Restrictions.AddRange(CreateRestrictions((mibType as OctetStringType).Size)); + } + else if (mibType is ObjectIdentifierType) + { + result = new SnmpScalarNodeObjectIdentifier(parentNode); + } + else if (mibType is BitsType) + { + result = new SnmpScalarNodeBits(parentNode, (uint)((mibType as BitsType).Map.GetHighestValue() + 1)); + result.Restrictions.AddRange(CreateRestrictions(mibType as BitsType)); + } + else + { + TypeAssignment ta = mibType as TypeAssignment; + if (ta != null) + { + Console.WriteLine(String.Format("Unsupported BaseType: Module='{0}', Name='{1}', Type='{2}'!", ta.Module.Name, ta.Name, ta.Type)); + } + else + { + Console.WriteLine(String.Format("Unsupported BaseType: Module='{0}', Name='{1}'!", mibType.Module, mibType.Name)); + } + + return null; + } + } + + result.Name = _alphaNumericRegex.Replace(ote.Name, "").ToLowerInvariant(); + result.Oid = ote.Value; + + if (ote.Access == MaxAccess.readWrite) + { + result.AccessMode = SnmpAccessMode.ReadWrite; + } + else if (ote.Access == MaxAccess.readOnly) + { + result.AccessMode = SnmpAccessMode.ReadOnly; + } + else if (ote.Access == MaxAccess.readCreate) + { + result.AccessMode = SnmpAccessMode.ReadOnly; + } + else if (ignoreAccessibleFlag && (ote.Access == MaxAccess.notAccessible)) + { + result.AccessMode = SnmpAccessMode.NotAccessible; + } + else + { + // not accessible or unsupported accress type + return null; + } + + return result; + } + + private static IEnumerable CreateRestrictions(ValueRanges ranges) + { + List result = new List(); + + if (ranges != null) + { + foreach (ValueRange range in ranges) + { + if (!range.End.HasValue) + { + result.Add(new IsEqualRestriction(range.Start)); + } + else + { + result.Add(new IsInRangeRestriction(range.Start, range.End.Value)); + } + } + } + + return result; + } + + private static IEnumerable CreateRestrictions(ValueMap map) + { + if ((map != null) && (map.Count > 0)) + { + return CreateRestrictions(map.GetContinousRanges()); + } + + return new List(); + } + + private static IEnumerable CreateRestrictions(BitsType bt) + { + List result = new List(); + + if ((bt != null) && (bt.Map != null)) + { + result.Add(new BitMaskRestriction(bt.Map.GetBitMask())); + } + + return result; + } + + private static SnmpTableNode GenerateSnmpTableNode(MibTreeNode mibTreeNode, SnmpTreeNode parentNode) + { + SnmpTableNode result = new SnmpTableNode(parentNode); + result.Name = mibTreeNode.Entity.Name; + result.Oid = mibTreeNode.Entity.Value; + + // expect exactly one row entry + if ((mibTreeNode.ChildNodes.Count != 1) || ((mibTreeNode.ChildNodes[0].NodeType & MibTreeNodeType.TableRow) == 0) || (mibTreeNode.ChildNodes[0].Entity.Value != 1)) + { + Console.WriteLine("Found table with unsupported properties! Table needs exactly one (fixed) TableRow with OID=1 ! (" + mibTreeNode.Entity.Name + ")"); + return null; + } + + MibTreeNode rowNode = mibTreeNode.ChildNodes[0]; + + ObjectType rot = rowNode.Entity as ObjectType; + if (rot != null) + { + if (!String.IsNullOrWhiteSpace(rot.Augments)) + { + result.AugmentedTableRow = rot.Augments; + + // the indeces from another table shall be used because this table is only an extension of it + rot = MibTypesResolver.ResolveDeclaration(rot.Module, rot.Augments) as ObjectType; + } + + if (rot.Indices != null) + { + foreach (string index in rot.Indices) + { + ObjectType indexEntity = MibTypesResolver.ResolveDeclaration(rot.Module, index) as ObjectType; + if (indexEntity == null) + { + Console.WriteLine(String.Format("Could not resolve index '{0}' for table '{1}'! Table omitted!", index, result.Name)); + return null; + } + + result.IndexNodes.Add(GenerateSnmpScalarNode(indexEntity, parentNode, ignoreAccessibleFlag: true)); + } + } + } + + if (result.IndexNodes.Count == 0) + { + // a table cannot be used without index + Console.WriteLine("Found table without any index column ! (" + mibTreeNode.Entity.Name + ")"); + return null; + } + + // add child nodes + foreach (MibTreeNode cellNode in rowNode.ChildNodes) + { + SnmpScalarNode ssn = GenerateSnmpScalarNode(cellNode, parentNode); + if (ssn != null) + { + result.CellNodes.Add(ssn); + } + } + + return result; + } + + #endregion + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/Properties/AssemblyInfo.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9bbeaf6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("ConsoleApplication28")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ConsoleApplication28")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("0abf7541-6a96-43cd-9e24-462e074b2c96")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/app.config b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/app.config new file mode 100644 index 0000000..cb2586b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibCompiler/app.config @@ -0,0 +1,3 @@ + + + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.Designer.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.Designer.cs new file mode 100644 index 0000000..1a5dbed --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.Designer.cs @@ -0,0 +1,166 @@ +namespace LwipMibViewer +{ + partial class FormMain + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormMain)); + this.treeMib = new System.Windows.Forms.TreeView(); + this.imagelistTreeNodeImages = new System.Windows.Forms.ImageList(this.components); + this.splitContainerMain = new System.Windows.Forms.SplitContainer(); + this.listviewNodeDetails = new System.Windows.Forms.ListView(); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStripMain = new System.Windows.Forms.ToolStrip(); + this.toolbuttonOpenMib = new System.Windows.Forms.ToolStripButton(); + this.dialogOpenMib = new System.Windows.Forms.OpenFileDialog(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainerMain)).BeginInit(); + this.splitContainerMain.Panel1.SuspendLayout(); + this.splitContainerMain.Panel2.SuspendLayout(); + this.splitContainerMain.SuspendLayout(); + this.toolStripMain.SuspendLayout(); + this.SuspendLayout(); + // + // treeMib + // + this.treeMib.Dock = System.Windows.Forms.DockStyle.Fill; + this.treeMib.ImageIndex = 0; + this.treeMib.ImageList = this.imagelistTreeNodeImages; + this.treeMib.Location = new System.Drawing.Point(0, 0); + this.treeMib.Name = "treeMib"; + this.treeMib.SelectedImageIndex = 0; + this.treeMib.Size = new System.Drawing.Size(1028, 418); + this.treeMib.TabIndex = 0; + this.treeMib.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeMib_AfterSelect); + // + // imagelistTreeNodeImages + // + this.imagelistTreeNodeImages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imagelistTreeNodeImages.ImageStream"))); + this.imagelistTreeNodeImages.TransparentColor = System.Drawing.Color.Transparent; + this.imagelistTreeNodeImages.Images.SetKeyName(0, "ntimgContainer"); + this.imagelistTreeNodeImages.Images.SetKeyName(1, "ntimgTable"); + this.imagelistTreeNodeImages.Images.SetKeyName(2, "ntimgRow"); + this.imagelistTreeNodeImages.Images.SetKeyName(3, "ntimgColumn"); + this.imagelistTreeNodeImages.Images.SetKeyName(4, "ntimgScalar"); + this.imagelistTreeNodeImages.Images.SetKeyName(5, "ntimgUnknown"); + // + // splitContainerMain + // + this.splitContainerMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainerMain.Location = new System.Drawing.Point(0, 25); + this.splitContainerMain.Name = "splitContainerMain"; + this.splitContainerMain.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainerMain.Panel1 + // + this.splitContainerMain.Panel1.Controls.Add(this.treeMib); + // + // splitContainerMain.Panel2 + // + this.splitContainerMain.Panel2.Controls.Add(this.listviewNodeDetails); + this.splitContainerMain.Size = new System.Drawing.Size(1028, 625); + this.splitContainerMain.SplitterDistance = 418; + this.splitContainerMain.TabIndex = 1; + // + // listviewNodeDetails + // + this.listviewNodeDetails.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader2}); + this.listviewNodeDetails.Dock = System.Windows.Forms.DockStyle.Fill; + this.listviewNodeDetails.FullRowSelect = true; + this.listviewNodeDetails.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.listviewNodeDetails.Location = new System.Drawing.Point(0, 0); + this.listviewNodeDetails.Name = "listviewNodeDetails"; + this.listviewNodeDetails.Size = new System.Drawing.Size(1028, 203); + this.listviewNodeDetails.TabIndex = 0; + this.listviewNodeDetails.UseCompatibleStateImageBehavior = false; + this.listviewNodeDetails.View = System.Windows.Forms.View.Details; + // + // columnHeader1 + // + this.columnHeader1.Text = ""; + this.columnHeader1.Width = 150; + // + // columnHeader2 + // + this.columnHeader2.Text = ""; + this.columnHeader2.Width = 777; + // + // toolStripMain + // + this.toolStripMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolbuttonOpenMib}); + this.toolStripMain.Location = new System.Drawing.Point(0, 0); + this.toolStripMain.Name = "toolStripMain"; + this.toolStripMain.Size = new System.Drawing.Size(1028, 25); + this.toolStripMain.TabIndex = 2; + // + // toolbuttonOpenMib + // + this.toolbuttonOpenMib.Image = ((System.Drawing.Image)(resources.GetObject("toolbuttonOpenMib.Image"))); + this.toolbuttonOpenMib.Name = "toolbuttonOpenMib"; + this.toolbuttonOpenMib.Size = new System.Drawing.Size(65, 22); + this.toolbuttonOpenMib.Text = "Open..."; + this.toolbuttonOpenMib.Click += new System.EventHandler(this.toolbuttonOpenMib_Click); + // + // FormMain + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1028, 650); + this.Controls.Add(this.splitContainerMain); + this.Controls.Add(this.toolStripMain); + this.Name = "FormMain"; + this.Text = "MIB Viewer"; + this.WindowState = System.Windows.Forms.FormWindowState.Maximized; + this.splitContainerMain.Panel1.ResumeLayout(false); + this.splitContainerMain.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainerMain)).EndInit(); + this.splitContainerMain.ResumeLayout(false); + this.toolStripMain.ResumeLayout(false); + this.toolStripMain.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TreeView treeMib; + private System.Windows.Forms.SplitContainer splitContainerMain; + private System.Windows.Forms.ListView listviewNodeDetails; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.ImageList imagelistTreeNodeImages; + private System.Windows.Forms.ToolStrip toolStripMain; + private System.Windows.Forms.ToolStripButton toolbuttonOpenMib; + private System.Windows.Forms.OpenFileDialog dialogOpenMib; + } +} + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.cs new file mode 100644 index 0000000..2da6586 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.cs @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System.Windows.Forms; +using Lextm.SharpSnmpLib.Mib; +using Lextm.SharpSnmpLib.Mib.Elements; +using Lextm.SharpSnmpLib.Mib.Elements.Types; +using Lextm.SharpSnmpLib.Mib.Elements.Entities; +using System.IO; + +namespace LwipMibViewer +{ + public partial class FormMain : Form + { + readonly ListViewGroup listviewgroupAbstract; + readonly ListViewGroup listviewgroupElement; + readonly ListViewGroup listviewgroupBaseType; + readonly ListViewGroup listviewgroupTypeChain; + + public FormMain() + { + this.Font = SystemInformation.MenuFont; + InitializeComponent(); + + this.listviewgroupAbstract = new ListViewGroup("Abstract", System.Windows.Forms.HorizontalAlignment.Left); + this.listviewgroupElement = new ListViewGroup("Element Properties", System.Windows.Forms.HorizontalAlignment.Left); + this.listviewgroupBaseType = new ListViewGroup("Element Base Type", System.Windows.Forms.HorizontalAlignment.Left); + this.listviewgroupTypeChain = new ListViewGroup("Element Type Chain", System.Windows.Forms.HorizontalAlignment.Left); + this.listviewNodeDetails.Groups.AddRange(new System.Windows.Forms.ListViewGroup[] { + listviewgroupAbstract, + listviewgroupElement, + listviewgroupBaseType, + listviewgroupTypeChain}); + + try + { + DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(Application.ExecutablePath)); + if (dirInfo != null) + { + dirInfo = dirInfo.Parent; + if (dirInfo != null) + { + dirInfo = dirInfo.Parent; + if (dirInfo != null) + { + dirInfo = new DirectoryInfo(Path.Combine(dirInfo.FullName, "Mibs")); + if (dirInfo.Exists) + { + MibTypesResolver.RegisterResolver(new FileSystemMibResolver(dirInfo.FullName, true)); + } + } + } + } + } + catch + { } + } + + #region GUI Event Handler + + private void toolbuttonOpenMib_Click(object sender, System.EventArgs e) + { + if (this.dialogOpenMib.ShowDialog() == DialogResult.OK) + { + OpenMib(this.dialogOpenMib.FileName); + } + } + + private void treeMib_AfterSelect(object sender, TreeViewEventArgs e) + { + listviewNodeDetails.Items.Clear(); + + if (e.Node != null) + { + MibTreeNode mtn = e.Node.Tag as MibTreeNode; + if (mtn != null) + { + listviewNodeDetails.Items.Add(new ListViewItem(new string[] { "Abstract", mtn.NodeType.ToString() }, this.listviewgroupAbstract)); + + listviewNodeDetails.Items.Add(new ListViewItem(new string[] { "Module", (mtn.Entity.Module != null) ? mtn.Entity.Module.Name : "" }, this.listviewgroupElement)); + listviewNodeDetails.Items.Add(new ListViewItem(new string[] { "Type", mtn.Entity.GetType().Name }, this.listviewgroupElement)); + listviewNodeDetails.Items.Add(new ListViewItem(new string[] { "Name", mtn.Entity.Name }, this.listviewgroupElement)); + listviewNodeDetails.Items.Add(new ListViewItem(new string[] { "Description", mtn.Entity.Description }, this.listviewgroupElement)); + listviewNodeDetails.Items.Add(new ListViewItem(new string[] { "OID", mtn.Entity.Value.ToString() }, this.listviewgroupElement)); + listviewNodeDetails.Items.Add(new ListViewItem(new string[] { "Full OID", MibTypesResolver.ResolveOid(mtn.Entity).GetOidString() }, this.listviewgroupElement)); + if (mtn.Entity is ObjectType) + { + listviewNodeDetails.Items.Add(new ListViewItem(new string[] { "Access", (mtn.Entity as ObjectType).Access.ToString() }, this.listviewgroupElement)); + } + + ITypeReferrer tr = mtn.Entity as ITypeReferrer; + if (tr != null) + { + ShowTypeDetails(listviewNodeDetails, this.listviewgroupBaseType, tr.BaseType); + ShowTypeChain(listviewNodeDetails, tr.ReferredType); + } + } + } + } + + #endregion + + #region Methods + + private void OpenMib(string file) + { + try + { + MibDocument md = new MibDocument(file); + MibTypesResolver.ResolveTypes(md.Modules[0]); + + this.treeMib.Nodes.Clear(); + this.listviewNodeDetails.Items.Clear(); + + MibTree mt = new MibTree(md.Modules[0] as MibModule); + foreach (MibTreeNode mibTreeNode in mt.Root) + { + AddNode(mibTreeNode, this.treeMib.Nodes); + + foreach (TreeNode node in this.treeMib.Nodes) + { + node.Expand(); + } + } + } + catch + { + } + } + + private void AddNode(MibTreeNode mibNode, TreeNodeCollection parentNodes) + { + int imgIndex = 5; //unknown + if ((mibNode.NodeType & MibTreeNodeType.Table) != 0) + { + imgIndex = 1; + } + else if ((mibNode.NodeType & MibTreeNodeType.TableRow) != 0) + { + imgIndex = 2; + } + else if ((mibNode.NodeType & MibTreeNodeType.TableCell) != 0) + { + imgIndex = 3; + } + else if ((mibNode.NodeType & MibTreeNodeType.Scalar) != 0) + { + imgIndex = 4; + } + else if ((mibNode.NodeType & MibTreeNodeType.Container) != 0) + { + imgIndex = 0; + } + + TreeNode newNode = new TreeNode(mibNode.Entity.Name, imgIndex, imgIndex); + newNode.Tag = mibNode; + + parentNodes.Add(newNode); + + foreach (MibTreeNode child in mibNode.ChildNodes) + { + AddNode(child, newNode.Nodes); + } + } + + private void ShowTypeChain(ListView lv, ITypeAssignment type) + { + ShowTypeDetails(lv, this.listviewgroupTypeChain, type); + + ITypeReferrer tr = type as ITypeReferrer; + if ((tr != null) && (tr.ReferredType != null)) + { + lv.Items.Add(new ListViewItem(new string[] { " >>>", "" }, this.listviewgroupTypeChain)); + ShowTypeChain(listviewNodeDetails, tr.ReferredType); + } + } + + private void ShowTypeDetails(ListView lv, ListViewGroup lvg, ITypeAssignment type) + { + lv.Items.Add(new ListViewItem(new string[] { "Module", (type.Module != null) ? type.Module.Name : "" }, lvg)); + lv.Items.Add(new ListViewItem(new string[] { "Type", type.GetType().Name }, lvg)); + lv.Items.Add(new ListViewItem(new string[] { "Name", type.Name }, lvg)); + } + + #endregion + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.resx b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.resx new file mode 100644 index 0000000..bad46b3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/FormMain.resx @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABo + IQAAAk1TRnQBSQFMAgEBBgEAARABAAEQAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABIAMAAQEBAAEgBgABIBIAAwQBBQMWAR4DIgEyAzEBTwJGAUQBhwMvAUsDHgErAxsBJgMYASIDFQEd + AxIBGAMNARIDCgENAwcBCQMEAQUDAQECAwQBBQMWAR4DIgEyAzEBTgJGAUQBhwMvAUsDHgErAxsBJgMb + ASYDIQExAyEBMAMdASoDGwEmAxgBIQMLAQ8DAQECgAADAgEDAwwBEAMrAUMCRgFEAYIC/wHwAf8CRgFE + AYIDKgFAAw8BFAMNAREDCwEPAwkBDAMHAQoDBQEHAwQBBQMCAQMDAAEBAwIBAwMLAQ8DKwFDAkYBRAGC + Av8B8AH/AkYBRAGCAyoBQAMOARMDEgEZAT0COwFpAVwBRQFCAawBZwE+AToBxAFaAUUBQwGqATwBOwE6 + AWYDEAEWAwABAYQAAx4BKwJEAUIBewL/AfAB/wLpAdoD/wHxAf8CRAFCAXsDHgErJAADHgErAkQBQgF7 + Av8B8AH/AukB2gP/AfEB/wJEAUIBewMeASsBLgItAUcBdwFHATwByQG7AVQBPQHxA+4B/wG7AVMBPAHx + AXcBRgE8AckBLgItAUeEAAMdASoCRAFCAXcC/wHwAf8B6wHdAbEB/wH3AcEBNwH/Ae0B3wGzA/8B8gH/ + AkQBQgF3Ax0BKhwAAx0BKgJEAUIBdwL/AfAB/wLpAdoB/wLqAdwB/wLrAd4D/wHyAf8CRAFCAXcBZAFJ + AUIBrwG2AVkBQQHxAc0BVAEyAf8BvQF5AWIB/wHFAVABLgH/AbEBUQE1AfEBXAFIAUQBn4QAAkMBQQF2 + Av8B8AH/AukB2gH/AecBqwEhAf8B5wGrASEB/wHnAasBIQH/AeoB2wGwA/8B9AH/AkMBQQF2Ax0BKhgA + AkMBQQF2Av8B8AH/AukB2gH/AuoB3AH/AusB3gH/AuwB3wH/Au0B4QP/AfQB/wGAAUQBMQHaAc4BcAFN + AfwBugFMASoB/wPSAf8BvgGLAXgB/wG7AVIBMgH8AW8BSQE/AbqEAAMdASkCQwFBAXQC/wHxAf8B5wHX + AasB/wHXAZYBDAH/AdcBlgEMAf8B1wGWAQwB/wHoAdgBrgP/AfUB/wJDAUEBdAMdASkUAAMdASkCQwFB + AXQC/wHxAf8C6wHeAf8C7AHfAf8C7QHhAf8C7gHjAf8C7wHlAf8BzQF5AV4B/wHOAXcBWAH3AbwBVAEy + Af8BtAFMASoB/wPmAf8BtwFlAUsB8AFdAUkBRAGdiAADHQEpAkIBQQFyAv8B8gH/AeUB1AGpAf8BzQGJ + AQAB/wHNAYkBAAH/Ac0BiQEAAf8B6AHXAa8D/wH3Af8CQgFBAXIDHAEoFAADHQEpAkIBQQFyAv8B8gH/ + Au0B4QH/Au4B4wH/Au8B5QH/AvAB5wH/AeABuwGqAf8BzgFpAUgB/wHjAcsBwQH5BP8B3gHHAb0B9QF+ + AU8BQgHEAi0BLAFFjAADHAEoAkEBQAFxAv8B9AH/AecB1gGsAf8B0QGOAQQB/wHRAY4BBAH/AdEBjgEE + Af8B7AHbAbMD/wH4Af8CQQFAAXEDHAEoFAADHAEoAkEBQAFxAv8B9AH/Au8B5QH/AvAB5wH/AvEB6QH/ + AvMB6gH/AeQBvgGsAf8B1AGBAWIB/wGGAUoBNAHXAWYBTQFEAaoCLQEsAUWUAAMcAScCQQFAAW8C/wH1 + Af8B7AHcAbMB/wHfAaEBFwH/Ad8BoQEXAf8B3wGhARcB/wHxAeIBuwP/AfoB/wJBAUABbwMcAScUAAMc + AScCQQFAAW8C/wH1Af8C8QHpAf8C8wHqAf8C9AHsAf8C9QHuAf8C9gHwA/8B+gH/AkEBQAFvAxwBJ5gA + AxwBJwJAAT8BbQL/AfcB/wHyAeMBuwH/AfABuAEuAf8B8AG4AS4B/wHwAbgBLgH/AvgB9AP/AfsB/wJA + AT8BbQMcAScUAAMcAScCQAE/AW0C/wH3Af8C9AHsAf8C9QHuAf8C9gHwAf8C9wHyAf8C+AH0A/8B+wH/ + AkABPwFtAxwBJ5gAAxsBJgJAAT8BbAL/AfgB/wH3AeoBwwH/Af0ByQE/Af8B+QHsAccB/wL7AfcB/wL8 + AfkD/wH8Af8CQAE/AWwDGwEmFAADGwEmAkABPwFsAv8B+AH/AvYB8AH/AvcB8gH/AvgB9AH/AvsB9wH/ + AvwB+QP/AfwB/wJAAT8BbAMbASaYAAMbASYCPwE+AWsC/wH6Af8C+AH0Af8C+wH3Af8C3wHVAf8CyQG5 + Af8C4AHWA/8B/gH/Aj8BPgFrGAADGwEmAj8BPgFrAv8B+gH/AvgB9AH/AvsB9wH/At8B1QH/AskBuQH/ + AuAB1gP/Af4B/wI/AT4Ba5wAAxoBJQI/AT0BaQL/AfsB/wL8AfkB/wK8AawB/wQAArwBrAP/Af4B/wI/ + AT0BaRwAAxoBJQI/AT0BaQL/AfsB/wL8AfkB/wK8AawB/wQAArwBrAP/Af4B/wI/AT0BaaAAAxoBJQI+ + AT0BaAL/AfwB/wLLAcEB/wKgAZAB/wLLAcED/wH+Af8CPgE9AWggAAMaASUCPgE9AWgC/wH8Af8CywHB + Af8CoAGQAf8CywHBA/8B/gH/Aj4BPQFopAADGgElAj4BPQFnAv8B/gP/Af4D/wH+Bf8CPgE9AWckAAMa + ASUCPgE9AWcC/wH+A/8B/gP/Af4F/wI+AT0BZ6gAAxoBJAI+AT0BZgI+AT0BZgI+AT0BZgI+AT0BZgMx + AU0oAAMaASQCPgE9AWYCPgE9AWYCPgE9AWYCPgE9AWYDMQFNlAADIQEwAUABRgFIAXwBQwFOAVIBkgMF + AQccAAMHAQkDEAEWAxMBGgMTARoDEwEaAxMBGgMTARoDEwEaAxMBGgMTARoDEwEaAxMBGgMTARoDEwEa + AxABFgMHAQkDBwEJAxABFgMTARoDEwEaAxMBGgMTARoDEwEaAxMBGgMTARoDEwEaAxMBGgMTARoDEwEa + AxMBGgMQARYDBwEJAwcBCQMQARYDEwEaAxMBGgMTARoDEwEaAxMBGgMTARoDEwEaAxMBGgMTARoDEwEa + AxMBGgMTARoDEAEWAwcBCQwAAjIBMwFQAUMBUQFXAZkBRQFkAXQBwAFYAYsBogHgATwBWAFqAcEDEwEa + AwUBBxgAAjwBOwFpAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGH + AkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAjwBOwFpAjkBNAFpAkABNwGHAkABNwGH + AkABNwGHAkABNwGHAkABNwGHAkABNwGHAkABNwGHAkABNwGHAkABNwGHAkABNwGHAkABNwGHAkABNwGH + AkABNwGHAkABNwGHAjkBNAFpAjwBOwFpAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGH + AkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAkYBRAGHAjwBOwFpAw0BEQMa + ASQBRAFNAVEBmAE8AYkBrAHyAWcBrwHTAfoBggHLAewB/wGFAc4B7gH/ARUBWwGCAe8BOgFXAWYBxAE6 + AVcBZgHEAT4BWgFqAb4BPgFaAWoBvgE+AVoBagG+AUQBTQFRAZgDGgEkAw0BEQJGAUMBgQL5AekB/wLz + AeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLz + AeIB/wLzAeIB/wL5AekB/wJGAUMBgQJDAToBgQL5AekB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLz + AeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wL5AekB/wJDAToBgQJG + AUMBgQL5AekB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLzAeIB/wLz + AeIB/wLzAeIB/wLzAeIB/wLzAeIB/wL5AekB/wJGAUMBgQMHAQkDDQESAUIBWwFmAbIBiAHQAe8B/wF9 + AcoB6QH/AX0BygHpAf8BhwHQAe8B/wEkAXsBqQH/AX0BvAHbAf8BfQG8AdsB/wGNAdEB8wH/AY0B0QHz + Af8BkAHUAfUB/wFCAVsBZgGyATACMQFNAwcBCQJEAUMBegL0AeQC/wHMAUIB/wH+AcsBQQH/AewB0gGG + Af8C2gHJAf8C2AHHAf8C1gHFAf8C1AHDAf8C0wHCAf8C0QHAAf8CzwG+Af8CzgG9Af8CzQG8Af8C9AHk + Af8CRAFDAXoCRgE+AXoC9AHkAv8BzAFDAf8B/gHLAUIB/wHsAdIBhgH/AtoByQH/AtgBxwH/AtYBxQH/ + AtQBwwH/AtMBwgH/AtEBwAH/As8BvgH/As4BvQH/As0BvAH/AvQB5AH/AkYBPgF6AkQBQwF6AvQB5AL/ + AcwBQgH/Af4BywFBAf8B7AHSAYYB/wLaAckB/wLYAccB/wHnAWEBPwH/AecBYQE/Af8B5wFhAT8B/wHn + AWEBPwH/As8BvgH/As4BvQH/As0BvAH/AvQB5AH/AkQBQwF6CAABQwFXAWABpAGKAdMB8AH/AYIBzQHr + Af8BggHNAesB/wGKAdMB8AH/ASQBfAGrAf8BegG5AdgB/wF6AbkB2AH/AYoBzgHwAf8BigHOAfAB/wGP + AdMB9AH/AfQBtgEsAf8BQwFXAWABpAQAAkQBQgF3AvUB5gL/AcwBQgL/Ae4BiAH/AewB0gGGAf8C9QHu + Af8C9QHuAf8C1gHFAf8C9QHuAf8C9QHuAf8C0QHAAf8C9QHuAf8C9QHuAf8CzQG8Af8C9QHmAf8CRAFC + AXcCRwE/AXcC9QHmAv8BzAFDAv8B7gGIAf8B7AHSAYYB/wL1Ae4B/wL1Ae4B/wLWAcUB/wL1Ae4B/wL1 + Ae4B/wLRAcAB/wL1Ae4B/wL1Ae4B/wLNAbwB/wL1AeYB/wJHAT8BdwJEAUIBdwL1AeYC/wHMAUIC/wHu + AYgB/wHsAdIBhgH/AvUB7gH/AvUB7gH/AdkBWAE2Af8B8gHJAbgB/wHyAckBuAH/AdkBWAE2Af8C9QHu + Af8C9QHuAf8CzQG8Af8C9QHmAf8CRAFCAXcIAAFDAVUBXgGeAY4B1gHyAf8BhwHQAe0B/wGHAdAB7QH/ + AY4B1gHyAf8BJgGCAa8B/wF7AboB2AH/AXsBugHYAf8BiwHPAfEB/wGLAc8B8QH/AZEB1QH1Af8B/gHJ + AT8B/wFDAVUBXgGeBAACQwFBAXUC9gHpAv8BzAFCAf8B/gHLAUEB/wHsAdIBhgH/AtoByQH/AtgBxwH/ + AtwBzAH/AtQBwwH/AtMBwgH/AtgByAH/As8BvgH/As4BvQH/As0BvAH/AvYB6QH/AkMBQQF1AkcBPwF1 + AvYB6QL/AcwBQwH/Af4BywFCAf8B7AHSAYYB/wLaAckB/wLYAccB/wLcAcwB/wLUAcMB/wLTAcIB/wLY + AcgB/wLPAb4B/wLOAb0B/wLNAbwB/wL2AekB/wJHAT8BdQJDAUEBdQL2AekC/wHMAUIB/wH+AcsBQQH/ + AewB0gGGAf8C2gHJAf8C2AHHAf8ByAFPAS0B/wHeAbYBngH/Ad4BtQGdAf8ByAFPAS0B/wLPAb4B/wLO + Ab0B/wLNAbwB/wL2AekB/wJDAUEBdQgAAUQBVQFdAZsBkgHaAfQB/wGLAdQB8AH/AYsB1AHwAf8BkgHa + AfQB/wEpAYUBswH/AX0BvAHaAf8BfQG8AdoB/wGNAdEB8wH/AY0B0QHzAf8BkwHXAfYB/wLrAd0B/wFE + AVUBXQGbBAACQgFBAXMC9wHrAv8BzAFCAv8B7gGIAf8B7AHSAYYB/wL3AfEB/wL3AfEB/wLWAcUB/wL3 + AfEB/wL3AfEB/wLRAcAB/wL3AfEB/wL3AfEB/wLNAbwB/wL3AesB/wJCAUEBcwJHAT8BcwL3AesC/wHM + AUMC/wHuAYgB/wHsAdIBhgH/AvcB8QH/AvcB8QH/AtYBxQH/AvcB8QH/AvcB8QH/AtEBwAH/AvcB8QH/ + AvcB8QH/As0BvAH/AvcB6wH/AkcBPwFzAkIBQQFzAvcB6wL/AcwBQgL/Ae4BiAH/AewB0gGGAf8C9wHx + Af8C9wHxAf8BuAFHASUB/wHzAcsBuQH/AfMBywG5Af8BuAFHASUB/wL3AfEB/wL3AfEB/wLNAbwB/wL3 + AesB/wJCAUEBcwgAAUQBUwFbApcB3gH2Af8BkAHYAfIB/wGQAdgB8gH/AZcB3gH2Af8BKwGJAbcB/wGA + Ab0B3AH/AYABvQHcAf8BjwHTAfUB/wGPAdMB9QH/AZUB2QH4Af8C9QHuAf8BRAFTAVsBlwQAAkIBQQFy + AvgB7gL/AcwBQgH/Af4BywFBAf8B7AHSAYYB/wLaAckB/wLYAccB/wLdAc4B/wLUAcMB/wLTAcIB/wLZ + AcoB/wLPAb4B/wLOAb0B/wLNAbwB/wL4Ae4B/wJCAUEBcgJIAUABcgL4Ae4B/wHsAYYBYwH/AeIBewFZ + Af8B1AFuAUwB/wHEAWABPgH/AbYBUgEwAf8BrQFHASUB/wGrAUMBIQH/AbEBRAEiAf8BvQFKASgB/wHM + AVIBMAH/AdsBWgE4Af8B6AFiAUAB/wL4Ae4B/wJIAUABcgJCAUEBcgL4Ae4C/wHMAUIB/wH+AcsBQQH/ + AewB0gGGAf8C2gHJAf8C2AHHAf8BrQFCASAB/wHeAbYBngH/Ad4BtQGdAf8BrQFCASAB/wLPAb4B/wLO + Ab0B/wLNAbwB/wL4Ae4B/wJCAUEBcggAAUQBUwFaAZQBmwHhAfcB/wGUAdsB9AH/AZQB2wH0Af8BmwHh + AfcB/wEuAY0BvAH/AYEBvgHdAf8BgQG+Ad0B/wGQAdQB9gH/AZAB1AH2Af8BlwHbAfkB/wL+Af0B/wFE + AVMBWgGUBAACQQFAAXAC+QHxAv8BzAFCAv8B7gGIAf8B7AHSAYYB/wL5AfUB/wL5AfUB/wLWAcUB/wL5 + AfUB/wL5AfUB/wLRAcAB/wL5AfUB/wL5AfUB/wLNAbwB/wL5AfEB/wJBAUABcAJHAUABcAL5AfEB/wHs + AYYBYwH/AfgBxQF5Af8B7QG1AXgB/wH1AcwBvAH/AfUBzAG8Af8B4AG3AZ8B/wH1AcwBvAH/AfUBzAG8 + Af8B3QG0AZwB/wH1AcwBvAH/AfUBzAG8Af8B6AFiAUAB/wL5AfEB/wJHAUABcAJBAUABcAL5AfEC/wHM + AUIC/wHuAYgB/wHsAdIBhgH/AvkB9QH/AvkB9QH/AasBRAEiAf8B9QHMAbwB/wH1AcwBvAH/AasBRAEi + Af8C+QH1Af8C+QH1Af8CzQG8Af8C+QHxAf8CQQFAAXAIAAFEAVEBVwGQAZ4B5QH5Af8BmAHfAfYB/wGY + Ad8B9gH/AZ4B5QH5Af8BMAGQAcAB/wGDAcAB3wH/AYMBwAHfAf8BkgHWAfgB/wGSAdYB+AH/AZkB3QH6 + Af8BRAFRAVcBkAMjATMEAAJBAUABbgL7AfQC/wHMAUIB/wH+AcsBQQH/AewB0gGGAf8C2gHJAf8C2AHH + Af8C3gHQAf8C1AHDAf8C0wHCAf8C2gHMAf8CzwG+Af8CzgG9Af8CzQG8Af8C+wH0Af8CQQFAAW4CRwFA + AW4C+wH0Af8B7AGGAWMB/wHiAXsBWQH/AdQBbgFMAf8BxAFgAT4B/wG2AVIBMAH/Aa0BRwElAf8BqwFD + ASEB/wGxAUQBIgH/Ab0BSgEoAf8BzAFSATAB/wHbAVoBOAH/AegBYgFAAf8C+wH0Af8CRwFAAW4CQQFA + AW4C+wH0Av8BzAFCAf8B/gHLAUEB/wHsAdIBhgH/AtoByQH/AtgBxwH/AbIBTAEqAf8B3gG2AZ4B/wHe + AbUBnQH/AbIBTAEqAf8CzwG+Af8CzgG9Af8CzQG8Af8C+wH0Af8CQQFAAW4IAAFDAU8BVQGNAaMB6AH7 + Af8BnQHjAfkB/wGdAeMB+QH/AaMB6AH7Af8BMwGUAcUB/wGFAcIB4QH/AYUBwgHhAf8BlAHYAfoB/wGU + AdgB+gH/AZsB3wH8Af8BQwFPAVUBjQgAAkABPwFtAvwB9wL/AcwBQgL/Ae4BiAH/AewB0gGGAf8C/AH6 + Af8C/AH6Af8C1gHFAf8C/AH6Af8C/AH6Af8C0QHAAf8C/AH6Af8C/AH6Af8CzQG8Af8C/AH3Af8CQAE/ + AW0CRwFAAW0C/AH3Av8BzAFDAv8B7gGIAf8B7AHSAYYB/wL8AfoB/wL8AfoB/wLWAcUB/wL8AfoB/wL8 + AfoB/wLRAcAB/wL8AfoB/wL8AfoB/wLNAbwB/wL8AfcB/wJHAUABbQJAAT8BbQL8AfcC/wHMAUIC/wHu + AYgB/wHsAdIBhgH/AvwB+gH/AvwB+gH/AcABWgE4Af8B9gHOAb8B/wH2Ac4BvwH/AcABWgE4Af8C/AH6 + Af8C/AH6Af8CzQG8Af8C/AH3Af8CQAE/AW0IAAFDAU8BVAGKAaYB6wH8Af8BoQHmAfsB/wGhAeYB+wH/ + AaYB6wH8Af8BOgGdAc8B/wGHAcQB4gH/AYcBxAHiAf8BlgHaAfwB/wGWAdoB/AH/AZ4B4gH9Af8BQwFP + AVQBiggAAj8BPgFrAv0B+QL/AcwBQgH/Af4BywFBAf8B9QHOAWIB/wHrAdIBhQH/AekB0AGDAf8B5wHO + AYEB/wHlAcwBgAH/AeQBywF8Af8B4gHJAXoB/wHgAccBeAH/Ad8BxgF3Af8B3gHFAXYB/wL9AfkB/wI/ + AT4BawJHAUABawL9AfkC/wHMAUMB/wH+AcsBQgH/AfUBzgFjAf8B6wHSAYUB/wHpAdABgwH/AecBzgGB + Af8B5QHMAYAB/wHkAcsBfQH/AeIByQF7Af8B4AHHAXkB/wHfAcYBeAH/Ad4BxQF3Af8C/QH5Af8CRwFA + AWsCPwE+AWsC/QH5Av8BzAFCAf8B/gHLAUEB/wH1Ac4BYgH/AesB0gGFAf8B6QHQAYMB/wHRAWoBSAH/ + AekBsQF0Af8B6AGwAXMB/wHRAWoBSAH/AeABxwF4Af8B3wHGAXcB/wHeAcUBdgH/Av0B+QH/Aj8BPgFr + CAABQgFNAVIBhwGpAe4B/QH/AaQB6QH8Af8BpAHpAfwB/wGqAe8B/QH/AUABoQHRAf8BkAHRAfEB/wGW + AdoB+wH/AZcB2wH9Af8BlwHbAf0B/wGfAeMB/gH/AUIBTQFSAYcIAAI/AT4BagL+AfwC/wHMAUIC/wHu + AYgB/wH9AcoBQAH/AfwB6wGFAf8B+wHqAYQB/wH4AcUBOwH/AfYB5QF9Af8B9AHjAXsB/wHzAcABNgH/ + AfEB4AF4Af8B7wHeAXYB/wHvAbwBMgH/Av4B/AH/Aj8BPgFqAkcBQAFqAv4B/AL/AcwBQwL/Ae4BiAH/ + Af0BygFBAf8B/AHrAYUB/wH7AeoBhAH/AfgBxQE8Af8B9gHlAX4B/wH0AeMBfAH/AfMBwAE3Af8B8QHg + AXkB/wHvAd4BdwH/Ae8BvAEzAf8C/gH8Af8CRwFAAWoCPwE+AWoC/gH8Av8BzAFCAv8B7gGIAf8B/QHK + AUAB/wH8AesBhQH/AfsB6gGEAf8B4QF5AVcB/wHzAcABcwH/AfIBvwFyAf8B4QF5AVcB/wHxAeABeAH/ + Ae8B3gF2Af8B7wG8ATIB/wL+AfwB/wI/AT4BaggAAUMBTAFSAYUBrQHxAv8BqwHvAf4B/wGVAeIB+AH/ + AWwByQHtAf8BRgGpAdkB/wGYAdwB/gH/AZgB3AH+Af8BmAHcAf4B/wGYAdwB/gH/AaEB5QL/AUMBTAFS + AYUIAAI+AT0BaAL/Af4C/wHMAUIB/wH+AcsBQQH/Af0BygFAAf8B/AHJAT8B/wH6AccBPQH/AfgBxQE7 + Af8B9gHDAToB/wH1AcIBOAH/AfMBwAE2Af8B8QG+ATQB/wHwAb0BMwH/Ae8BvAEyA/8B/gH/Aj4BPQFo + AkcBQAFoAv8B/gL/AcwBQwH/Af4BywFCAf8B/QHKAUEB/wH8AckBQAH/AfoBxwE+Af8B+AHFATwB/wH2 + AcMBOwH/AfUBwgE5Af8B8wHAATcB/wHxAb4BNQH/AfABvQE0Af8B7wG8ATMD/wH+Af8CRwFAAWgCPgE9 + AWgC/wH+Av8BzAFCAf8B/gHLAUEB/wH9AcoBQAH/AfwByQE/Af8B+gHHAT0B/wHsAYYBYgH/AewBhgFi + Af8B7AGGAWIB/wHsAYYBYgH/AfEBvgE0Af8B8AG9ATMB/wHvAbwBMgP/Af4B/wI+AT0BaAgAAUMBTAFQ + AYMBiAHcAfQB/wFeAcAB6QH/AV0BvwHqAf8BgAHTAfQB/wGcAeMB/QH/AaIB5gL/AaIB5gL/AaIB5gL/ + AaIB5gL/AaYB6gL/AUMBTAFQAYMIAAI+AT0BZzj/Aj4BPQFnAkcBQAFnOP8CRwFAAWcCPgE9AWc4/wI+ + AT0BZwgAATkBOwE9AWEBQgFLAU8BgQFCAUsBTwGBAUIBSwFPAYEBQgFLAU8BgQFCAUsBTwGBAUIBSwFP + AYEBQgFLAU8BgQFCAUsBTwGBAUIBSwFPAYEBQgFLAU8BgQE5ATsBPQFhCAADMQFNAj4BPQFmAj4BPQFm + Aj4BPQFmAj4BPQFmAj4BPQFmAj4BPQFmAj4BPQFmAj4BPQFmAj4BPQFmAj4BPQFmAj4BPQFmAj4BPQFm + Aj4BPQFmAj4BPQFmAzEBTQI3ATQBTQJHAUABZgJHAUABZgJHAUABZgJHAUABZgJHAUABZgJHAUABZgJH + AUABZgJHAUABZgJHAUABZgJHAUABZgJHAUABZgJHAUABZgJHAUABZgJHAUABZgI3ATQBTQMxAU0CPgE9 + AWYCPgE9AWYCPgE9AWYCPgE9AWYCPgE9AWYCPgE9AWYCPgE9AWYCPgE9AWYCPgE9AWYCPgE9AWYCPgE9 + AWYCPgE9AWYCPgE9AWYCPgE9AWYDMQFNAUIBTQE+BwABPgMAASgDAAFAAwABIAMAAQEBAAEBBgABARYA + A/8RAAGAAf8BgAEBBQABfwEAAQEFAAE/AQABAQUAAR8BAAEBBAABgAEPAYABAQQAAcABBwHAAQMEAAHg + AQMB4AEDBAAB8AEBAfABAQQAAfgBAAH4BQAB/AEAAfwFAAH+AQgB/gEIBAAB/wEAAf8FAAH/AYAB/wGA + BAAB/wHAAf8BwAQAAfgBfwYAAeABPxYAAcABAQYAAcABAQYAAcABAQYAAcABAQYAAcABAQYAAcABAQYA + AcABAwYAAcABAwYAAcABAwYAAcABAwYAAcABAwYAAcABAwYACw== + + + + 211, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAK6SURBVDhPjZNbSNNRHMfPU9DtwR71oZf5IgkF0YMEEYb2 + EGgG5YNgGQmGSUoYimbel5KKmlM0u3jJSpv3ad7WnGkzb2yO4bByF3WuuTnnnLv47Zw/9YclQQc+/L// + P+d8/r/f+Z8/Ib/HjJDcmhaS3P+Bzf2zjr8qiki+QyuE6dNNbIzFw6F8DJ++AVh97c9GK9jcA4LJAlKI + rQ7sW9/DpauGZSoFg6JwfJSU+TE0XIXOgqCaAwJ5ASn2bb6F19TM4bO+w4z4HgwWC9YcDugpK3Y7umQy + FOZEDMRkZQX7SWS5pMRrboVn9RUHy1/aEqDSajGn0WDZbIZ6bQ0t/f1gIzojI8lPMvaIPPWsN2FP/5yD + ZdmLWLwUi/FhZASSqSlOUtXczBMcGZnFVzGUTSr2jI1wfq/lYHms4Tqkc3MYnZ2F0mDAqs3GV8LaiUhN + TeYFA5mkenelHg5tNQfLw3UxaOrpQZdUiu7xca5/Mc0do6PQb28jPDk5hRf0PiQi5zcR7JoKDpYHaqIg + VyohW1jg+lcZjVwlCzod1p1OXEhMvM8LOtNJvWOpEjZVKQfL/ZVX0NrXh165HP2Tk5hQqzGuUmFQocCm + y4XzCQlpvKA9jTTa1WWwzBdzsNxdfhmfFxcxQRct0Q3UmEzY2t2FdWcHdrcb5+LiHvCC1hTSbFOWwDyT + x8GyuDQCbRIJ3tBPp6CfU0pbcdA3M4mDCs7ExqYzwWHKibo7pNs6T4+yIofDSqtof3IJBtqzTq+Hx+uF + y+OBky5kkh2aT0VFZTNBAEWQFEFqhyvO2pclSe6f03nYnC1EW9FFGOnGGSi+/X14KW6fD3tUtkdzYFiY + 0O801iWSaNFtUteWGST92nL1R/q1Q7ojAkHV0ZCQkuOhocV/c0wguHvgn2APyuPJ6dI4kpV/gzyjtycp + gf8g4Begs1B6Kbj3cQAAAABJRU5ErkJggg== + + + + 337, 17 + + \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/LwipMibViewer.csproj b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/LwipMibViewer.csproj new file mode 100644 index 0000000..20c1b31 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/LwipMibViewer.csproj @@ -0,0 +1,94 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {86CC0B65-7985-4017-A252-0A7A18DCAEF3} + WinExe + Properties + LwipMibViewer + LwipMibViewer + v4.0 + 512 + + + + true + bin\Debug\ + DEBUG;TRACE + full + prompt + true + true + 4 + false + false + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + true + true + 4 + false + + + + + + + + + Form + + + FormMain.cs + + + + + FormMain.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {CBE20411-5DB7-487D-825D-7694267BB6F5} + SharpSnmpLib.Mib + + + + + + \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Program.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Program.cs new file mode 100644 index 0000000..c3b3cd5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Program.cs @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Windows.Forms; + +namespace LwipMibViewer +{ + static class Program + { + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new FormMain()); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/AssemblyInfo.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..49d837c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("LwipMibViewer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LwipMibViewer")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("7ffbd1c1-1c64-45bb-b243-2400446c649d")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Resources.Designer.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Resources.Designer.cs new file mode 100644 index 0000000..e6ffe0c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.225 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace LwipMibViewer.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LwipMibViewer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Resources.resx b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Settings.Designer.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Settings.Designer.cs new file mode 100644 index 0000000..a34fe2f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.225 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace LwipMibViewer.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Settings.settings b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/app.config b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/app.config new file mode 100644 index 0000000..cb2586b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipMibViewer/app.config @@ -0,0 +1,3 @@ + + + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/IRestriction.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/IRestriction.cs new file mode 100644 index 0000000..2a022ab --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/IRestriction.cs @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; + +namespace LwipSnmpCodeGeneration +{ + public interface IRestriction + { + string GetCheckCodeValid(string varNameToCheck); + string GetCheckCodeInvalid(string varNameToCheck); + } + + public class BitMaskRestriction : IRestriction + { + UInt32 mask; + + public BitMaskRestriction(UInt32 mask) + { + this.mask = mask; + } + + public string GetCheckCodeValid(string varNameToCheck) + { + return String.Format("(({0} & {1}) == {0})", varNameToCheck, this.mask); + } + + public string GetCheckCodeInvalid(string varNameToCheck) + { + return String.Format("(({0} & {1}) != {0})", varNameToCheck, this.mask); + } + } + + public class IsEqualRestriction : IRestriction + { + private Int64 value; + + public IsEqualRestriction(Int64 value) + { + this.value = value; + } + + public long Value + { + get { return value; } + } + + public string GetCheckCodeValid(string varNameToCheck) + { + return String.Format("({0} == {1})", varNameToCheck, this.value); + } + + public string GetCheckCodeInvalid(string varNameToCheck) + { + return String.Format("({0} != {1})", varNameToCheck, this.value); + } + } + + public class IsInRangeRestriction : IRestriction + { + private Int64 rangeStart; + private Int64 rangeEnd; + + public IsInRangeRestriction(Int64 rangeStart, Int64 rangeEnd) + { + this.rangeStart = rangeStart; + this.rangeEnd = rangeEnd; + } + + public long RangeStart + { + get { return this.rangeStart; } + } + + public long RangeEnd + { + get { return this.rangeEnd; } + } + + public string GetCheckCodeValid(string varNameToCheck) + { + return String.Format("(({0} >= {1}) && ({0} <= {2}))", varNameToCheck, this.rangeStart, this.rangeEnd); + } + + public string GetCheckCodeInvalid(string varNameToCheck) + { + return String.Format("(({0} < {1}) || ({0} > {2}))", varNameToCheck, this.rangeStart, this.rangeEnd); + } + } + +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/LwipSnmp.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/LwipSnmp.cs new file mode 100644 index 0000000..39439a8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/LwipSnmp.cs @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; + +namespace LwipSnmpCodeGeneration +{ + public static class LwipOpts + { + public static bool GenerateEmptyFolders = false; + /// + /// If a tree node only has scalar nodes as child nodes, it is replaced by + /// a single scalar array node in order to save memory and have only one single get/test/set method for all scalars. + /// + public static bool GenerateScalarArrays = true; + /// + /// If a tree node has multiple scalars as subnodes as well as other treenodes it + /// defines a single get/test/set method for all scalar child node. + /// (without other treenodes as child it would have been converted to scalar array node). + /// + public static bool GenerateSingleAccessMethodsForTreeNodeScalars = GenerateScalarArrays; + } + + public static class LwipDefs + { + public const string Null = "NULL"; + public const string Vt_U8 = "u8_t"; + public const string Vt_U16 = "u16_t"; + public const string Vt_U32 = "u32_t"; + public const string Vt_S8 = "s8_t"; + public const string Vt_S16 = "s16_t"; + public const string Vt_S32 = "s32_t"; + public const string Vt_Snmp_err = "snmp_err_t"; + + public const string Incl_SnmpOpts = "lwip/apps/snmp_opts.h"; + public const string Opt_SnmpEnabled = "LWIP_SNMP"; + + public const string Vt_StMib = "struct snmp_mib"; + public const string Vt_StObjectId = "struct snmp_obj_id"; + public const string Vt_StNode = "struct snmp_node"; + public const string Vt_StNodeInstance = "struct snmp_node_instance"; + public const string Vt_StTreeNode = "struct snmp_tree_node"; + public const string Vt_StScalarNode = "struct snmp_scalar_node"; + public const string Vt_StScalarArrayNode = "struct snmp_scalar_array_node"; + public const string Vt_StScalarArrayNodeDef = "struct snmp_scalar_array_node_def"; + public const string Vt_StTableNode = "struct snmp_table_node"; + public const string Vt_StTableColumnDef = "struct snmp_table_col_def"; + public const string Vt_StNextOidState = "struct snmp_next_oid_state"; + + public const string Def_NodeAccessReadOnly = "SNMP_NODE_INSTANCE_READ_ONLY"; + public const string Def_NodeAccessReadWrite = "SNMP_NODE_INSTANCE_READ_WRITE"; + public const string Def_NodeAccessWriteOnly = "SNMP_NODE_INSTANCE_WRITE_ONLY"; + public const string Def_NodeAccessNotAccessible = "SNMP_NODE_INSTANCE_NOT_ACCESSIBLE"; + + public const string Def_ErrorCode_Ok = "SNMP_ERR_NOERROR"; + public const string Def_ErrorCode_WrongValue = "SNMP_ERR_WRONGVALUE"; + public const string Def_ErrorCode_NoSuchInstance = "SNMP_ERR_NOSUCHINSTANCE"; + + public const string FnctSuffix_GetValue = "_get_value"; + public const string FnctSuffix_SetTest = "_set_test"; + public const string FnctSuffix_SetValue = "_set_value"; + public const string FnctSuffix_GetInstance = "_get_instance"; + public const string FnctSuffix_GetNextInstance = "_get_next_instance"; + + public const string FnctName_SetTest_Ok = "snmp_set_test_ok"; + + public static string GetLwipDefForSnmpAccessMode(SnmpAccessMode am) + { + switch (am) + { + case SnmpAccessMode.ReadOnly: return Def_NodeAccessReadOnly; + case SnmpAccessMode.ReadWrite: return Def_NodeAccessReadWrite; + case SnmpAccessMode.NotAccessible: return Def_NodeAccessNotAccessible; + case SnmpAccessMode.WriteOnly: return Def_NodeAccessWriteOnly; + default: throw new NotSupportedException("Unknown SnmpAccessMode!"); + } + } + + public static string GetAsn1DefForSnmpDataType(SnmpDataType dt) + { + switch (dt) + { + // primitive + case SnmpDataType.Null: + return "SNMP_ASN1_TYPE_NULL"; + case SnmpDataType.Bits: + case SnmpDataType.OctetString: + return "SNMP_ASN1_TYPE_OCTET_STRING"; + case SnmpDataType.ObjectIdentifier: + return "SNMP_ASN1_TYPE_OBJECT_ID"; + case SnmpDataType.Integer: + return "SNMP_ASN1_TYPE_INTEGER"; + + // application + case SnmpDataType.IpAddress: + return "SNMP_ASN1_TYPE_IPADDR"; + case SnmpDataType.Counter: + return "SNMP_ASN1_TYPE_COUNTER"; + case SnmpDataType.Gauge: + return "SNMP_ASN1_TYPE_GAUGE"; + case SnmpDataType.TimeTicks: + return "SNMP_ASN1_TYPE_TIMETICKS"; + case SnmpDataType.Opaque: + return "SNMP_ASN1_TYPE_OPAQUE"; + case SnmpDataType.Counter64: + return "SNMP_ASN1_TYPE_COUNTER64"; + default: + throw new NotSupportedException("Unknown SnmpDataType!"); + } + } + + public static string GetLengthForSnmpDataType(SnmpDataType dt) + { + switch (dt) + { + case SnmpDataType.Null: + return "0"; + + case SnmpDataType.Integer: + case SnmpDataType.Counter: + case SnmpDataType.IpAddress: + case SnmpDataType.Gauge: + case SnmpDataType.TimeTicks: + return "4"; + + case SnmpDataType.Counter64: + return "8"; + + case SnmpDataType.OctetString: + case SnmpDataType.ObjectIdentifier: + case SnmpDataType.Bits: + case SnmpDataType.Opaque: + return null; + + default: + throw new NotSupportedException("Unknown SnmpDataType!"); + } + } + } + + public enum SnmpDataType + { + Null, + + Integer, // INTEGER, Integer32 + + Counter, // Counter, Counter32 + Gauge, // Gauge, Gauge32, Unsigned32 + TimeTicks, + + Counter64, + + OctetString, + Opaque, + Bits, + + ObjectIdentifier, + + IpAddress, + } + + public enum SnmpAccessMode + { + ReadOnly, + ReadWrite, + WriteOnly, + NotAccessible + } + +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/LwipSnmpCodeGeneration.csproj b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/LwipSnmpCodeGeneration.csproj new file mode 100644 index 0000000..c55c0e6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/LwipSnmpCodeGeneration.csproj @@ -0,0 +1,72 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {AABCAB90-1540-45D4-A159-14831A54E9A3} + Library + Properties + LwipSnmpCodeGeneration + LwipSnmpCodeGeneration + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + {7DA7C0AB-0982-4BF5-9324-F59A7A08D65B} + CCodeGeneration + + + + + \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/MibCFile.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/MibCFile.cs new file mode 100644 index 0000000..08ac711 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/MibCFile.cs @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System.Collections.Generic; +using CCodeGeneration; +using System; +using System.IO; + +namespace LwipSnmpCodeGeneration +{ + public class MibCFile + { + #region Fields + + private const string PreservedSectionMarker = "LWIP MIB generator - preserved section begin"; + private const string PreservedSectionHeader = + "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" + + PreservedSectionMarker + "\n" + + "Code below is preserved on regeneration. Remove these comment lines to regenerate code.\n" + + "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; + + private readonly List includes = new List(); + private readonly List defines = new List(); + private readonly List declarations = new List(); + private readonly List implementation = new List(); + private readonly List preservedCode = new List(); + + #endregion + + public MibCFile() + { + } + + #region Accessors + + public List Includes + { + get { return this.includes; } + } + + public List Defines + { + get { return this.defines; } + } + + public List Declarations + { + get { return this.declarations; } + } + + public List Implementation + { + get { return this.implementation; } + } + + public List PreservedCode + { + get { return this.preservedCode; } + } + + #endregion + + #region Methods + + public void Save(CGenerator cGenerator) + { + CFile cFile = new CFile(); + + cFile.AddElement(new Comment("Generated by LwipMibCompiler")); + cFile.AddElement(EmptyLine.SingleLine); + + cFile.AddElement(new PP_Include(LwipDefs.Incl_SnmpOpts)); + CodeContainerBase e = cFile.AddElement(new PP_If(LwipDefs.Opt_SnmpEnabled)) as CodeContainerBase; + e.AddElement(EmptyLine.SingleLine); + + // include header file + string file = cGenerator.FileName; + if (!String.IsNullOrWhiteSpace(file)) + { + string ext = System.IO.Path.GetExtension(file); + + string headerFile = !String.IsNullOrEmpty(ext) ? file.Substring(0, file.Length - ext.Length) : file; + headerFile += ".h"; + + e.AddElement(new PP_Include(headerFile)); + } + + // include common snmp files + e.AddElement(new PP_Include("lwip/apps/snmp.h")); + e.AddElement(new PP_Include("lwip/apps/snmp_core.h")); + e.AddElement(new PP_Include("lwip/apps/snmp_scalar.h")); + e.AddElement(new PP_Include("lwip/apps/snmp_table.h")); + + if (this.includes.Count > 0) + { + e.AddElement(EmptyLine.SingleLine); + e.AddElements(this.includes); + } + + if (this.defines.Count > 0) + { + e.AddElement(EmptyLine.SingleLine); + e.AddElements(this.defines); + } + + if (this.declarations.Count > 0) + { + e.AddElement(EmptyLine.TwoLines); + e.AddElements(this.declarations); + } + + if (this.implementation.Count > 0) + { + e.AddElement(EmptyLine.TwoLines); + e.AddElements(this.implementation); + } + + if (this.preservedCode.Count > 0) + { + e.AddElement(EmptyLine.TwoLines); + e.AddElement(new Comment(PreservedSectionHeader)); + e.AddElement(EmptyLine.SingleLine); + e.AddElements(this.preservedCode); + } + + cFile.Save(cGenerator); + } + + public static string GetPreservedCode(string file) + { + if (File.Exists(file)) + { + using (StreamReader fileStream = new StreamReader(file)) + { + while (!fileStream.EndOfStream) + { + string line = fileStream.ReadLine(); + if (line == PreservedSectionMarker) + { + break; + } + } + + if (!fileStream.EndOfStream) + { + // skip the rest of the comment + spacer line + fileStream.ReadLine(); // "Code below is preserved... + fileStream.ReadLine(); // "+++++++++++++++++++++++... + fileStream.ReadLine(); // */ + fileStream.ReadLine(); // + + string preservedCode = fileStream.ReadToEnd(); + + int lastEndif = preservedCode.LastIndexOf("#endif", StringComparison.Ordinal); + preservedCode = preservedCode.Remove(lastEndif); + + return preservedCode; + } + } + } + + return null; + } + + #endregion + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/MibHeaderFile.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/MibHeaderFile.cs new file mode 100644 index 0000000..2cf8c77 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/MibHeaderFile.cs @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System.Collections.Generic; +using System.Text.RegularExpressions; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class MibHeaderFile + { + + #region Fields + + private readonly List defines = new List(); + private readonly List includes = new List(); + private readonly List functionDeclarations = new List(); + private readonly List variableDeclarations = new List(); + + #endregion + + public MibHeaderFile() + { + } + + #region Accessors + + public List Defines + { + get { return this.defines; } + } + + public List Includes + { + get { return this.includes; } + } + + public List FunctionDeclarations + { + get { return this.functionDeclarations; } + } + + public List VariableDeclarations + { + get { return this.variableDeclarations; } + } + + #endregion + + #region Methods + + public void Save(CGenerator cGenerator) + { + CFile cFile = new CFile(); + + cFile.AddElement(new Comment("Generated by LwipMibCompiler")); + cFile.AddElement(EmptyLine.SingleLine); + + string headerDefine = cGenerator.FileName; + headerDefine = new Regex("[^a-zA-Z0-9]").Replace(headerDefine, "_"); + headerDefine = headerDefine.ToUpperInvariant(); + + CodeContainerBase e = cFile.AddElement(new PP_Ifdef(headerDefine, inverted: true)) as CodeContainerBase; + e.AddElement(new PP_Macro(headerDefine, headerDefine)); + e.AddElement(EmptyLine.SingleLine); + + e.AddElement(new PP_Include(LwipDefs.Incl_SnmpOpts)); + e = e.AddElement(new PP_If(LwipDefs.Opt_SnmpEnabled)) as CodeContainerBase; + e.AddElement(EmptyLine.SingleLine); + + CodeContainerBase cplusplusopen = e.AddElement(new PP_Ifdef("__cplusplus")) as CodeContainerBase; + cplusplusopen.AddElement(new Code("extern \"C\" {")); + e.AddElement(EmptyLine.SingleLine); + + if (this.includes.Count > 0) + { + e.AddElements(this.includes); + e.AddElement(EmptyLine.SingleLine); + } + + if (this.defines.Count > 0) + { + e.AddElements(this.defines); + e.AddElement(EmptyLine.SingleLine); + } + + e.AddElements(this.functionDeclarations, EmptyLine.SingleLine); + e.AddElements(this.variableDeclarations, EmptyLine.SingleLine); + + e.AddElement(EmptyLine.SingleLine); + CodeContainerBase cplusplusclose = e.AddElement(new PP_Ifdef("__cplusplus")) as CodeContainerBase; + cplusplusclose.AddElement(new Code("}")); + + e.AddElement(EmptyLine.SingleLine); + cFile.Save(cGenerator); + } + + #endregion + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/Properties/AssemblyInfo.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0e56898 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("LwipSnmpCodeGeneration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LwipSnmpCodeGeneration")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("8cfbbb8b-dfbb-4dd5-80c9-e07845dd58c9")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpMib.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpMib.cs new file mode 100644 index 0000000..915cebb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpMib.cs @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpMib : SnmpTreeNode + { + public uint[] BaseOid { get; set; } + + public SnmpMib() + : base(null) + { + } + + public SnmpMib(uint[] baseOid) + : base(null) + { + this.BaseOid = baseOid; + } + + public override string FullNodeName + { + get { return this.Name + "_root"; } + } + + public override void GenerateCode(MibCFile mibFile) + { + base.GenerateCode(mibFile); + + System.Diagnostics.Debug.Assert((this.BaseOid != null) && (this.BaseOid.Length > 0)); + + // create and add BaseOID declarations + StringBuilder boidInitialization = new StringBuilder("{"); + foreach (uint t in this.BaseOid) + { + boidInitialization.Append(t); + boidInitialization.Append(","); + } + boidInitialization.Length -= 1; + boidInitialization.Append("}"); + + VariableDeclaration boidDecl = new VariableDeclaration( + new VariableType(this.Name + "_base_oid", LwipDefs.Vt_U32, null, ConstType.Value, String.Empty), + boidInitialization.ToString(), true); + + mibFile.Declarations.Add(boidDecl); + mibFile.Declarations.Add(GetExportDeclaration()); + } + + public override void GenerateHeaderCode(MibHeaderFile mibHeaderFile) + { + mibHeaderFile.Includes.Add(new PP_Include("lwip/apps/snmp_core.h")); + + mibHeaderFile.VariableDeclarations.Add(VariablePrototype.FromVariableDeclaration(GetExportDeclaration())); + } + + VariableDeclaration GetExportDeclaration() + { + return new VariableDeclaration( + new VariableType(this.Name, LwipDefs.Vt_StMib, null, ConstType.Value), + String.Format("{{{0}_base_oid, LWIP_ARRAYSIZE({0}_base_oid), &{1}.node}}", this.Name, this.FullNodeName)); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpNode.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpNode.cs new file mode 100644 index 0000000..d0a27d6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpNode.cs @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Text.RegularExpressions; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public abstract class SnmpNode + { + public static readonly Regex NameValidationRegex = new Regex(@"^\w+$"); + + private string name; + private readonly SnmpTreeNode parentNode; + + protected SnmpNode(SnmpTreeNode parentNode) + { + this.parentNode = parentNode; + } + + public SnmpTreeNode ParentNode + { + get { return this.parentNode; } + } + + public virtual uint Oid { get; set; } + + public abstract string FullNodeName + { + get; + } + + public virtual string Name + { + get { return this.name; } + set + { + if (value != this.name) + { + // check for valid name + if (!NameValidationRegex.IsMatch(value)) + { + throw new ArgumentOutOfRangeException("Name"); + } + + this.name = value; + } + } + } + + public virtual void Generate(MibCFile generatedFile, MibHeaderFile generatedHeaderFile) + { + int declCount = generatedFile.Declarations.Count; + int implCount = generatedFile.Implementation.Count; + + this.GenerateHeaderCode(generatedHeaderFile); + this.GenerateCode(generatedFile); + + if (generatedFile.Declarations.Count != declCount) + { + generatedFile.Declarations.Add(EmptyLine.SingleLine); + } + if (generatedFile.Implementation.Count != implCount) + { + generatedFile.Implementation.Add(EmptyLine.SingleLine); + } + } + + public abstract void GenerateCode(MibCFile mibFile); + + public virtual void GenerateHeaderCode(MibHeaderFile mibHeaderFile) + { + } + + /// + /// Called after node structure creation is completed and before code is created. + /// Offers the possibility to perform operations depending on properties/subnodes. + /// If the node shall be transformed to another node(-type) than the own instance + /// may be replaced on parent node by the transformed instance. + /// Calling sequence is always from leafs up to root. So a tree node can assume + /// that the analyze method was already called on all child nodes. + /// E.g. a tree node only has scalar sub nodes -> it transforms itself to a scalar array node + /// + /// The transformed node or null if nothing shall be changed in parent structure. + public virtual void Analyze() + { + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarAggregationNode.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarAggregationNode.cs new file mode 100644 index 0000000..cb42ede --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarAggregationNode.cs @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System.Collections.Generic; +using System.Globalization; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public abstract class SnmpScalarAggregationNode: SnmpNode + { + private bool getMethodRequired = false; + private bool testMethodRequired = false; + private bool setMethodRequired = false; + + protected SnmpScalarAggregationNode(SnmpTreeNode parentNode) + : base(parentNode) + { + } + + protected virtual string GetMethodName + { + get { return this.FullNodeName + LwipDefs.FnctSuffix_GetValue; } + } + + protected bool GetMethodRequired + { + get { return this.getMethodRequired; } + } + + protected virtual string TestMethodName + { + get { return this.FullNodeName + LwipDefs.FnctSuffix_SetTest; } + } + + protected bool TestMethodRequired + { + get { return this.testMethodRequired; } + } + + protected virtual string SetMethodName + { + get { return this.FullNodeName + LwipDefs.FnctSuffix_SetValue; } + } + + protected bool SetMethodRequired + { + get { return this.setMethodRequired; } + } + + protected abstract IEnumerable AggregatedScalarNodes + { + get; + } + + public override void Analyze() + { + base.Analyze(); + + this.getMethodRequired = false; + this.testMethodRequired = false; + this.setMethodRequired = false; + + foreach (SnmpScalarNode scalarNode in this.AggregatedScalarNodes) + { + if ((scalarNode.AccessMode == SnmpAccessMode.ReadOnly) || (scalarNode.AccessMode == SnmpAccessMode.ReadWrite)) + { + this.getMethodRequired = true; + } + if ((scalarNode.AccessMode == SnmpAccessMode.WriteOnly) || (scalarNode.AccessMode == SnmpAccessMode.ReadWrite)) + { + this.testMethodRequired = true; + this.setMethodRequired = true; + } + + if (this.getMethodRequired && this.setMethodRequired) + { + break; + } + } + } + + protected void GenerateAggregatedCode(MibCFile mibFile, VariableType instanceType, string switchSelector, bool generateDeclarations = true, bool generateImplementations = true) + { + if (this.getMethodRequired) + { + FunctionDeclaration getMethodDecl = new FunctionDeclaration(this.GetMethodName, isStatic: true); + getMethodDecl.Parameter.Add(instanceType); + getMethodDecl.Parameter.Add(new VariableType("value", VariableType.VoidString, "*")); + getMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_U16); + + if (generateDeclarations) + { + mibFile.Declarations.Add(getMethodDecl); + } + if (generateImplementations) + { + Function getMethod = Function.FromDeclaration(getMethodDecl); + GenerateGetMethodCode(getMethod, switchSelector); + mibFile.Implementation.Add(getMethod); + } + } + + if (this.testMethodRequired) + { + FunctionDeclaration testMethodDecl = new FunctionDeclaration(this.TestMethodName, isStatic: true); + testMethodDecl.Parameter.Add(instanceType); + testMethodDecl.Parameter.Add(new VariableType("len", LwipDefs.Vt_U16)); + testMethodDecl.Parameter.Add(new VariableType("value", VariableType.VoidString, "*")); + testMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_Snmp_err); + + if (generateDeclarations) + { + mibFile.Declarations.Add(testMethodDecl); + } + if (generateImplementations) + { + Function testMethod = Function.FromDeclaration(testMethodDecl); + GenerateTestMethodCode(testMethod, switchSelector); + mibFile.Implementation.Add(testMethod); + } + } + + if (this.setMethodRequired) + { + FunctionDeclaration setMethodDecl = new FunctionDeclaration(this.SetMethodName, isStatic: true); + setMethodDecl.Parameter.Add(instanceType); + setMethodDecl.Parameter.Add(new VariableType("len", LwipDefs.Vt_U16)); + setMethodDecl.Parameter.Add(new VariableType("value", VariableType.VoidString, "*")); + setMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_Snmp_err); + + if (generateDeclarations) + { + mibFile.Declarations.Add(setMethodDecl); + } + if (generateImplementations) + { + Function setMethod = Function.FromDeclaration(setMethodDecl); + GenerateSetMethodCode(setMethod, switchSelector); + mibFile.Implementation.Add(setMethod); + } + } + } + + protected virtual void GenerateGetMethodCode(Function getMethod, string switchSelector) + { + VariableDeclaration returnValue = new VariableDeclaration((VariableType)getMethod.ReturnType.Clone()); + returnValue.Type.Name = "value_len"; + getMethod.Declarations.Add(returnValue); + Switch sw = new Switch(switchSelector); + + bool valueVarUsed = false; + + foreach (SnmpScalarNode scalarNode in this.AggregatedScalarNodes) + { + if ((scalarNode.AccessMode == SnmpAccessMode.ReadOnly) || (scalarNode.AccessMode == SnmpAccessMode.ReadWrite)) + { + SwitchCase sc = new SwitchCase(scalarNode.Oid.ToString(CultureInfo.InvariantCulture)); + sc.Declarations.Add(new Comment(scalarNode.Name, singleLine: true)); + + scalarNode.GenerateGetMethodCode(sc, getMethod.Parameter[1].Name, ref valueVarUsed, returnValue.Type.Name); + + sw.Switches.Add(sc); + } + } + + SwitchCase scd = SwitchCase.GenerateDefault(); + scd.AddCodeFormat("LWIP_DEBUGF(SNMP_MIB_DEBUG,(\"{0}(): unknown id: %\"S32_F\"\\n\", {1}));", getMethod.Name, switchSelector); + scd.AddCodeFormat("{0} = 0;", returnValue.Type.Name); + sw.Switches.Add(scd); + + if (!valueVarUsed) + { + getMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getMethod.Parameter[1].Name); + } + + getMethod.AddElement(sw); + + getMethod.AddCodeFormat("return {0};", returnValue.Type.Name); + } + + protected virtual void GenerateTestMethodCode(Function testMethod, string switchSelector) + { + VariableDeclaration returnValue = new VariableDeclaration((VariableType)testMethod.ReturnType.Clone(), LwipDefs.Def_ErrorCode_WrongValue); + returnValue.Type.Name = "err"; + testMethod.Declarations.Add(returnValue); + Switch sw = new Switch(switchSelector); + + bool valueVarUsed = false; + bool lenVarUsed = false; + + foreach (SnmpScalarNode scalarNode in this.AggregatedScalarNodes) + { + if ((scalarNode.AccessMode == SnmpAccessMode.WriteOnly) || (scalarNode.AccessMode == SnmpAccessMode.ReadWrite)) + { + SwitchCase sc = new SwitchCase(scalarNode.Oid.ToString(CultureInfo.InvariantCulture)); + sc.Declarations.Add(new Comment(scalarNode.Name, singleLine: true)); + + scalarNode.GenerateTestMethodCode(sc, testMethod.Parameter[2].Name, ref valueVarUsed, testMethod.Parameter[1].Name, ref lenVarUsed, returnValue.Type.Name); + + sw.Switches.Add(sc); + } + } + + SwitchCase scd = SwitchCase.GenerateDefault(); + scd.AddCodeFormat("LWIP_DEBUGF(SNMP_MIB_DEBUG,(\"{0}(): unknown id: %\"S32_F\"\\n\", {1}));", testMethod.Name, switchSelector); + sw.Switches.Add(scd); + + if (!valueVarUsed) + { + testMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", testMethod.Parameter[2].Name); + } + if (!lenVarUsed) + { + testMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", testMethod.Parameter[1].Name); + } + + testMethod.AddElement(sw); + + testMethod.AddCodeFormat("return {0};", returnValue.Type.Name); + } + + protected virtual void GenerateSetMethodCode(Function setMethod, string switchSelector) + { + VariableDeclaration returnValue = new VariableDeclaration((VariableType)setMethod.ReturnType.Clone(), LwipDefs.Def_ErrorCode_Ok); + returnValue.Type.Name = "err"; + setMethod.Declarations.Add(returnValue); + Switch sw = new Switch(switchSelector); + + bool valueVarUsed = false; + bool lenVarUsed = false; + + foreach (SnmpScalarNode scalarNode in this.AggregatedScalarNodes) + { + if ((scalarNode.AccessMode == SnmpAccessMode.WriteOnly) || (scalarNode.AccessMode == SnmpAccessMode.ReadWrite)) + { + SwitchCase sc = new SwitchCase(scalarNode.Oid.ToString(CultureInfo.InvariantCulture)); + sc.Declarations.Add(new Comment(scalarNode.Name, singleLine: true)); + + scalarNode.GenerateSetMethodCode(sc, setMethod.Parameter[2].Name, ref valueVarUsed, setMethod.Parameter[1].Name, ref lenVarUsed, returnValue.Type.Name); + + sw.Switches.Add(sc); + } + } + + SwitchCase scd = SwitchCase.GenerateDefault(); + scd.AddCodeFormat("LWIP_DEBUGF(SNMP_MIB_DEBUG,(\"{0}(): unknown id: %\"S32_F\"\\n\", {1}));", setMethod.Name, switchSelector); + sw.Switches.Add(scd); + + if (!valueVarUsed) + { + setMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", setMethod.Parameter[2].Name); + } + if (!lenVarUsed) + { + setMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", setMethod.Parameter[1].Name); + } + + setMethod.AddElement(sw); + + setMethod.AddCodeFormat("return {0};", returnValue.Type.Name); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarArrayNode.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarArrayNode.cs new file mode 100644 index 0000000..bf6a1b1 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarArrayNode.cs @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarArrayNode : SnmpScalarAggregationNode + { + private readonly List scalarNodes; + + public SnmpScalarArrayNode(List scalarNodes, SnmpTreeNode parentNode) + : base(parentNode) + { + this.scalarNodes = scalarNodes; + } + + public override string FullNodeName + { + get { return this.Name + "_scalars"; } + } + + protected override IEnumerable AggregatedScalarNodes + { + get { return this.scalarNodes; } + } + + public override void GenerateCode(MibCFile mibFile) + { + VariableType instanceType = new VariableType("node", LwipDefs.Vt_StScalarArrayNodeDef, "*", ConstType.Value); + GenerateAggregatedCode( + mibFile, + instanceType, + instanceType.Name + "->oid"); + + + // create and add node definitions + StringBuilder nodeDefs = new StringBuilder(); + foreach (SnmpScalarNode scalarNode in this.scalarNodes) + { + nodeDefs.AppendFormat(" {{{0}, {1}, {2}}}, /* {3} */ \n", + scalarNode.Oid, + LwipDefs.GetAsn1DefForSnmpDataType(scalarNode.DataType), + LwipDefs.GetLwipDefForSnmpAccessMode(scalarNode.AccessMode), + scalarNode.Name); + } + if (nodeDefs.Length > 0) + nodeDefs.Length--; + + VariableDeclaration nodeDefsDecl = new VariableDeclaration( + new VariableType(this.FullNodeName + "_nodes", LwipDefs.Vt_StScalarArrayNodeDef, null, ConstType.Value, String.Empty), + "{\n" + nodeDefs + "\n}" , + isStatic: true); + + mibFile.Declarations.Add(nodeDefsDecl); + + + // create and add node declaration + string nodeInitialization = String.Format("SNMP_SCALAR_CREATE_ARRAY_NODE({0}, {1}, {2}, {3}, {4})", + this.Oid, + nodeDefsDecl.Type.Name, + (this.GetMethodRequired) ? this.GetMethodName : LwipDefs.Null, + (this.TestMethodRequired) ? this.TestMethodName : LwipDefs.Null, + (this.SetMethodRequired) ? this.SetMethodName : LwipDefs.Null + ); + + mibFile.Declarations.Add(new VariableDeclaration( + new VariableType(this.FullNodeName, LwipDefs.Vt_StScalarArrayNode, null, ConstType.Value), + nodeInitialization, + isStatic: true)); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNode.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNode.cs new file mode 100644 index 0000000..9f1b341 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNode.cs @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarNode: SnmpNode + { + protected const string LocalValueName = "v"; // name of (casted) local value variable + + private SnmpDataType dataType; + private SnmpAccessMode accessMode; + private readonly List restrictions = new List(); + + private bool useExternalMethods = false; + private string externalGetMethod; + private string externalTestMethod; + private string externalSetMethod; + + + public SnmpScalarNode(SnmpTreeNode parentNode) + : base(parentNode) + { + } + + public override string FullNodeName + { + get { return this.Name + "_scalar"; } + } + + public SnmpDataType DataType + { + get { return this.dataType; } + set { this.dataType = value; } + } + + public List Restrictions + { + get { return this.restrictions; } + } + + public SnmpAccessMode AccessMode + { + get { return this.accessMode; } + set { this.accessMode = value; } + } + + public virtual string FixedValueLength + { + get { return null; } + } + + /// + /// If scalar is used as a table index its value becomes part of the OID. This value returns how many OID parts are required to represent this value. + /// + public virtual int OidRepresentationLen + { + get { return -1; } + } + + public bool UseExternalMethods + { + get { return this.useExternalMethods; } + set { this.useExternalMethods = value; } + } + + public string ExternalGetMethod + { + get { return this.externalGetMethod; } + set { this.externalGetMethod = value; } + } + public string ExternalTestMethod + { + get { return this.externalTestMethod; } + set { this.externalTestMethod = value; } + } + public string ExternalSetMethod + { + get { return this.externalSetMethod; } + set { this.externalSetMethod = value; } + } + + public override void GenerateCode(MibCFile mibFile) + { + string getMethodName; + string testMethodName; + string setMethodName; + + if (this.useExternalMethods) + { + getMethodName = this.externalGetMethod; + testMethodName = this.externalTestMethod; + setMethodName = this.externalSetMethod; + } + else + { + getMethodName = LwipDefs.Null; + testMethodName = LwipDefs.Null; + setMethodName = LwipDefs.Null; + + if ((this.accessMode == SnmpAccessMode.ReadWrite) || (this.accessMode == SnmpAccessMode.ReadOnly)) + { + FunctionDeclaration getMethodDecl = new FunctionDeclaration(this.Name + LwipDefs.FnctSuffix_GetValue, isStatic: true); + getMethodDecl.Parameter.Add(new VariableType("instance", LwipDefs.Vt_StNodeInstance, "*")); + getMethodDecl.Parameter.Add(new VariableType("value", VariableType.VoidString, "*")); + getMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_U16); + mibFile.Declarations.Add(getMethodDecl); + + Function getMethod = Function.FromDeclaration(getMethodDecl); + getMethodName = getMethod.Name; + + VariableDeclaration returnValue = new VariableDeclaration((VariableType)getMethod.ReturnType.Clone()); + returnValue.Type.Name = "value_len"; + getMethod.Declarations.Add(returnValue); + getMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getMethod.Parameter[0].Name); + + bool valueVarUsed = false; + GenerateGetMethodCode(getMethod, getMethod.Parameter[1].Name, ref valueVarUsed, returnValue.Type.Name); + if (!valueVarUsed) + { + getMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getMethod.Parameter[1].Name); + } + + getMethod.AddCodeFormat("return {0};", returnValue.Type.Name); + + mibFile.Implementation.Add(getMethod); + } + + if ((this.accessMode == SnmpAccessMode.ReadWrite) || (this.accessMode == SnmpAccessMode.WriteOnly)) + { + bool valueVarUsed; + bool lenVarUsed; + VariableDeclaration returnValue; + + if (this.restrictions.Count > 0) + { + FunctionDeclaration testMethodDecl = new FunctionDeclaration(this.Name + LwipDefs.FnctSuffix_SetTest, isStatic: true); + testMethodDecl.Parameter.Add(new VariableType("instance", LwipDefs.Vt_StNodeInstance, "*")); + testMethodDecl.Parameter.Add(new VariableType("len", LwipDefs.Vt_U16)); + testMethodDecl.Parameter.Add(new VariableType("value", VariableType.VoidString, "*")); + testMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_Snmp_err); + mibFile.Declarations.Add(testMethodDecl); + + Function testMethod = Function.FromDeclaration(testMethodDecl); + testMethodName = testMethod.Name; + + returnValue = new VariableDeclaration((VariableType)testMethod.ReturnType.Clone(), LwipDefs.Def_ErrorCode_WrongValue); + returnValue.Type.Name = "err"; + testMethod.Declarations.Add(returnValue); + testMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", testMethod.Parameter[0].Name); + + valueVarUsed = false; + lenVarUsed = false; + + GenerateTestMethodCode(testMethod, testMethod.Parameter[2].Name, ref valueVarUsed, testMethod.Parameter[1].Name, ref lenVarUsed, returnValue.Type.Name); + + if (!valueVarUsed) + { + testMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", testMethod.Parameter[2].Name); + } + if (!lenVarUsed) + { + testMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", testMethod.Parameter[1].Name); + } + + testMethod.AddCodeFormat("return {0};", returnValue.Type.Name); + + mibFile.Implementation.Add(testMethod); + + } + else + { + testMethodName = LwipDefs.FnctName_SetTest_Ok; + } + + FunctionDeclaration setMethodDecl = null; + setMethodDecl = new FunctionDeclaration(this.Name + LwipDefs.FnctSuffix_SetValue, isStatic: true); + setMethodDecl.Parameter.Add(new VariableType("instance", LwipDefs.Vt_StNodeInstance, "*")); + setMethodDecl.Parameter.Add(new VariableType("len", LwipDefs.Vt_U16)); + setMethodDecl.Parameter.Add(new VariableType("value", VariableType.VoidString, "*")); + setMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_Snmp_err); + mibFile.Declarations.Add(setMethodDecl); + + Function setMethod = Function.FromDeclaration(setMethodDecl); + setMethodName = setMethod.Name; + + returnValue = new VariableDeclaration((VariableType)setMethod.ReturnType.Clone(), LwipDefs.Def_ErrorCode_Ok); + returnValue.Type.Name = "err"; + setMethod.Declarations.Add(returnValue); + setMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", setMethod.Parameter[0].Name); + + valueVarUsed = false; + lenVarUsed = false; + + GenerateSetMethodCode(setMethod, setMethod.Parameter[2].Name, ref valueVarUsed, setMethod.Parameter[1].Name, ref lenVarUsed, returnValue.Type.Name); + + if (!valueVarUsed) + { + setMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", setMethod.Parameter[2].Name); + } + if (!lenVarUsed) + { + setMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", setMethod.Parameter[1].Name); + } + + setMethod.AddCodeFormat("return {0};", returnValue.Type.Name); + + mibFile.Implementation.Add(setMethod); + } + } + + // create and add node declaration + string nodeInitialization; + if (this.accessMode == SnmpAccessMode.ReadOnly) + { + nodeInitialization = String.Format("SNMP_SCALAR_CREATE_NODE_READONLY({0}, {1}, {2})", + this.Oid, + LwipDefs.GetAsn1DefForSnmpDataType(this.dataType), + getMethodName); + } + else + { + nodeInitialization = String.Format("SNMP_SCALAR_CREATE_NODE({0}, {1}, {2}, {3}, {4}, {5})", + this.Oid, + LwipDefs.GetLwipDefForSnmpAccessMode(this.accessMode), + LwipDefs.GetAsn1DefForSnmpDataType(this.dataType), + getMethodName, + testMethodName, + setMethodName); + } + + mibFile.Declarations.Add(new VariableDeclaration( + new VariableType(this.FullNodeName, LwipDefs.Vt_StScalarNode, null, ConstType.Value), + nodeInitialization, isStatic: true)); + } + + public virtual void GenerateGetMethodCode(CodeContainerBase container, string valueVarName, ref bool valueVarUsed, string retLenVarName) + { + bool localValueVarUsed; + if (GenerateValueDeclaration(container, LocalValueName, valueVarName)) + { + valueVarUsed = true; + localValueVarUsed = false; + } + else + { + localValueVarUsed = true; // do not generate UNUSED_ARG code + } + + if (this.FixedValueLength == null) + { + // check that value with variable length fits into buffer + container.AddElement(new Comment(String.Format("TODO: take care that value with variable length fits into buffer: ({0} <= SNMP_MAX_VALUE_SIZE)", retLenVarName), singleLine: true)); + } + + GenerateGetMethodCodeCore(container, LocalValueName, ref localValueVarUsed, retLenVarName); + if (!localValueVarUsed) + { + container.AddCode(String.Format("LWIP_UNUSED_ARG({0});", LocalValueName)); + } + } + + protected virtual void GenerateGetMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string retLenVarName) + { + container.AddElement(new Comment(String.Format("TODO: put requested value to '*{0}' here", localValueVarName), singleLine: true)); + container.AddCodeFormat("{0} = {1};", + retLenVarName, + (!String.IsNullOrWhiteSpace(this.FixedValueLength)) ? this.FixedValueLength : "0"); + } + + public virtual void GenerateTestMethodCode(CodeContainerBase container, string valueVarName, ref bool valueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + if (this.Restrictions.Count > 0) + { + bool localVarUsed; + if (GenerateValueDeclaration(container, LocalValueName, valueVarName)) + { + valueVarUsed = true; + localVarUsed = false; + } + else + { + localVarUsed = true; // do not generate UNUSED_ARG code + } + + if (!String.IsNullOrWhiteSpace(this.FixedValueLength)) + { + // check for fixed value + container.AddCodeFormat("LWIP_ASSERT(\"Invalid length for datatype\", ({0} == {1}));", lenVarName, this.FixedValueLength); + lenVarUsed = true; + } + + GenerateTestMethodCodeCore(container, LocalValueName, ref localVarUsed, lenVarName, ref lenVarUsed, retErrVarName); + + if (!localVarUsed) + { + container.AddCode(String.Format("LWIP_UNUSED_ARG({0});", LocalValueName)); + } + } + else + { + container.AddCodeFormat("{0} == {1};", retErrVarName, LwipDefs.Def_ErrorCode_Ok); + } + } + + protected virtual void GenerateTestMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + container.AddElement(new Comment(String.Format("TODO: test new value here:\nif (*{0} == ) {1} = {2};", localValueVarName, retErrVarName, LwipDefs.Def_ErrorCode_Ok))); + } + + public virtual void GenerateSetMethodCode(CodeContainerBase container, string valueVarName, ref bool valueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + bool localVarUsed; + if (GenerateValueDeclaration(container, LocalValueName, valueVarName)) + { + valueVarUsed = true; + localVarUsed = false; + } + else + { + localVarUsed = true; // do not generate UNUSED_ARG code + } + + GenerateSetMethodCodeCore(container, LocalValueName, ref localVarUsed, lenVarName, ref lenVarUsed, retErrVarName); + + if (!localVarUsed) + { + container.AddCode(String.Format("LWIP_UNUSED_ARG({0});", LocalValueName)); + } + } + + protected virtual void GenerateSetMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + container.AddElement(new Comment(String.Format("TODO: store new value contained in '*{0}' here", localValueVarName), singleLine: true)); + } + + + protected virtual bool GenerateValueDeclaration(CodeContainerBase container, string variableName, string sourceName) + { + container.AddDeclaration(new VariableDeclaration( + new VariableType(variableName, LwipDefs.Vt_U8, "*"), + "(" + new VariableType(null, LwipDefs.Vt_U8, "*") + ")" + sourceName)); + + return true; + } + + public static SnmpScalarNode CreateFromDatatype(SnmpDataType dataType, SnmpTreeNode parentNode) + { + switch (dataType) + { + case SnmpDataType.Integer: + return new SnmpScalarNodeInt(parentNode); + + case SnmpDataType.Gauge: + case SnmpDataType.Counter: + case SnmpDataType.TimeTicks: + return new SnmpScalarNodeUint(dataType, parentNode); + } + + return new SnmpScalarNode(parentNode); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeBits.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeBits.cs new file mode 100644 index 0000000..fbaa388 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeBits.cs @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarNodeBits : SnmpScalarNode + { + private readonly uint bitCount; + + public SnmpScalarNodeBits(SnmpTreeNode parentNode, uint bitCount) + : base(parentNode) + { + this.DataType = SnmpDataType.Bits; + this.bitCount = bitCount; + } + + public override void GenerateGetMethodCode(CodeContainerBase container, string valueVarName, ref bool valueVarUsed, string retLenVarName) + { + container.AddCode(String.Format( + "{0} = snmp_encode_bits(({1} *){2}, SNMP_MAX_VALUE_SIZE, 0 /* TODO: pass real value here */, {3});", + retLenVarName, + LwipDefs.Vt_U8, + valueVarName, + this.bitCount)); + + valueVarUsed = true; + } + + public override void GenerateTestMethodCode(CodeContainerBase container, string valueVarName, ref bool valueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + if (this.Restrictions.Count > 0) + { + const string bitVarName = "bits"; + + container.Declarations.Add(new VariableDeclaration(new VariableType(bitVarName, LwipDefs.Vt_U32))); + + IfThenElse ite = new IfThenElse(String.Format( + "snmp_decode_bits(({0} *){1}, {2}, &{3}) == ERR_OK", + LwipDefs.Vt_U8, + valueVarName, + lenVarName, + bitVarName)); + + valueVarUsed = true; + lenVarUsed = true; + + StringBuilder innerIfCond = new StringBuilder(); + foreach (IRestriction restriction in this.Restrictions) + { + innerIfCond.Append(restriction.GetCheckCodeValid(bitVarName)); + innerIfCond.Append(" || "); + } + + innerIfCond.Length -= 4; + + IfThenElse innerIte = new IfThenElse(innerIfCond.ToString()); + innerIte.AddCode(String.Format("{0} = {1};", retErrVarName, LwipDefs.Def_ErrorCode_Ok)); + ite.AddElement(innerIte); + container.AddElement(ite); + } + else + { + base.GenerateTestMethodCode(container, valueVarName, ref valueVarUsed, lenVarName, ref lenVarUsed, retErrVarName); + } + } + + public override void GenerateSetMethodCode(CodeContainerBase container, string valueVarName, ref bool valueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + const string bitVarName = "bits"; + + container.Declarations.Add(new VariableDeclaration(new VariableType(bitVarName, LwipDefs.Vt_U32))); + + IfThenElse ite = new IfThenElse(String.Format( + "snmp_decode_bits(({0} *){1}, {2}, &{3}) == ERR_OK", + LwipDefs.Vt_U8, + valueVarName, + lenVarName, + bitVarName)); + + valueVarUsed = true; + lenVarUsed = true; + + ite.AddElement(new Comment(String.Format("TODO: store new value contained in '{0}' here", bitVarName), singleLine: true)); + + container.AddElement(ite); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeCounter64.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeCounter64.cs new file mode 100644 index 0000000..7533939 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeCounter64.cs @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarNodeCounter64 : SnmpScalarNode + { + public SnmpScalarNodeCounter64(SnmpTreeNode parentNode) + : base(parentNode) + { + this.DataType = SnmpDataType.Counter64; + } + + protected override bool GenerateValueDeclaration(CodeContainerBase container, string variableName, string sourceName) + { + container.AddDeclaration(new VariableDeclaration( + new VariableType(variableName + "_high", LwipDefs.Vt_U32, "*"), + "(" + new VariableType(null, LwipDefs.Vt_U32, "*").ToString() + ")" + sourceName)); + container.AddDeclaration(new VariableDeclaration( + new VariableType(variableName + "_low", LwipDefs.Vt_U32, "*"), + variableName + "_high + 1")); + + container.AddCode(String.Format("LWIP_UNUSED_ARG({0}_high);", variableName)); + container.AddCode(String.Format("LWIP_UNUSED_ARG({0}_low);", variableName)); + + return false; + } + + public override string FixedValueLength + { + get { return String.Format("(2 * sizeof({0}))", LwipDefs.Vt_U32); } + } + + public override int OidRepresentationLen + { + get { return 1; } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeInt.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeInt.cs new file mode 100644 index 0000000..7093bbe --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeInt.cs @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarNodeInt : SnmpScalarNode + { + public SnmpScalarNodeInt(SnmpTreeNode parentNode) + : base(parentNode) + { + this.DataType = SnmpDataType.Integer; + } + + protected override void GenerateTestMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + System.Diagnostics.Trace.Assert(this.Restrictions.Count > 0); + + StringBuilder ifCond = new StringBuilder(); + foreach (IRestriction restriction in this.Restrictions) + { + ifCond.Append(restriction.GetCheckCodeValid("*" + localValueVarName)); + ifCond.Append(" || "); + + localValueVarUsed = true; + } + + ifCond.Length -= 4; + + IfThenElse ite = new IfThenElse(ifCond.ToString()); + ite.AddCode(String.Format("{0} = {1};", retErrVarName, LwipDefs.Def_ErrorCode_Ok)); + container.AddElement(ite); + } + + protected override bool GenerateValueDeclaration(CodeContainerBase container, string variableName, string sourceName) + { + container.AddDeclaration(new VariableDeclaration( + new VariableType(variableName, LwipDefs.Vt_S32, "*"), + "(" + new VariableType(null, LwipDefs.Vt_S32, "*") + ")" + sourceName)); + + return true; + } + + public override string FixedValueLength + { + get { return String.Format("sizeof({0})", LwipDefs.Vt_S32); } + } + + public override int OidRepresentationLen + { + get { return 1; } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeObjectIdentifier.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeObjectIdentifier.cs new file mode 100644 index 0000000..c2c8bb5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeObjectIdentifier.cs @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarNodeObjectIdentifier: SnmpScalarNode + { + public SnmpScalarNodeObjectIdentifier(SnmpTreeNode parentNode) + : base(parentNode) + { + this.DataType = SnmpDataType.ObjectIdentifier; + } + + protected override bool GenerateValueDeclaration(CodeContainerBase container, string variableName, string sourceName) + { + container.AddDeclaration(new VariableDeclaration( + new VariableType(variableName, LwipDefs.Vt_U32, "*"), + "(" + new VariableType(null, LwipDefs.Vt_U32, "*") + ")" + sourceName)); + + return true; + } + + protected override void GenerateGetMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string retLenVarName) + { + container.AddElement(new Comment(String.Format("TODO: put requested value to '*{0}' here. '{0}' has to be interpreted as {1}[]", localValueVarName, LwipDefs.Vt_U32), singleLine: true)); + container.AddElement(EmptyLine.SingleLine); + container.AddCode(String.Format("{0} = 0; // TODO: return real value length here (should be 'numOfElements * sizeof({1})')", retLenVarName, LwipDefs.Vt_U32)); + } + + protected override void GenerateTestMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + VariableDeclaration objIdLenVar = new VariableDeclaration( + new VariableType(localValueVarName + "_len", LwipDefs.Vt_U8), + String.Format("{0} / sizeof({1})", lenVarName, LwipDefs.Vt_U32)); + lenVarUsed = true; + + container.Declarations.Add(objIdLenVar); + + base.GenerateTestMethodCodeCore(container, localValueVarName, ref localValueVarUsed, lenVarName, ref lenVarUsed, retErrVarName); + + container.AddCode(String.Format("LWIP_UNUSED_ARG({0});", objIdLenVar.Type.Name)); + } + + protected override void GenerateSetMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + VariableDeclaration objIdLenVar = new VariableDeclaration( + new VariableType(localValueVarName + "_len", LwipDefs.Vt_U8), + String.Format("{0} / sizeof({1})", lenVarName, LwipDefs.Vt_U32)); + lenVarUsed = true; + + container.Declarations.Add(objIdLenVar); + + base.GenerateSetMethodCodeCore(container, localValueVarName, ref localValueVarUsed, lenVarName, ref lenVarUsed, retErrVarName); + + container.AddCode(String.Format("LWIP_UNUSED_ARG({0});", objIdLenVar.Type.Name)); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeOctetString.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeOctetString.cs new file mode 100644 index 0000000..c773208 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeOctetString.cs @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarNodeOctetString : SnmpScalarNode + { + public SnmpScalarNodeOctetString(SnmpDataType dataType, SnmpTreeNode parentNode) + : base(parentNode) + { + System.Diagnostics.Debug.Assert( + (dataType == SnmpDataType.OctetString) || + (dataType == SnmpDataType.Opaque) || + (dataType == SnmpDataType.IpAddress)); + + this.DataType = dataType; + } + + protected override void GenerateGetMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string retLenVarName) + { + if (this.Restrictions.Count > 0) + { + StringBuilder ifCond = new StringBuilder(); + foreach (IRestriction restriction in this.Restrictions) + { + ifCond.Append(restriction.GetCheckCodeValid(retLenVarName)); + ifCond.Append(" || "); + } + + ifCond.Length -= 4; + container.AddElement(new Comment("TODO: take care of len restrictions defined in MIB: " + ifCond, singleLine: true)); + } + base.GenerateGetMethodCodeCore(container, localValueVarName, ref localValueVarUsed, retLenVarName); + } + + protected override void GenerateTestMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + System.Diagnostics.Trace.Assert(this.Restrictions.Count > 0); + + // checks refer to length of octet string + StringBuilder ifCond = new StringBuilder(); + foreach (IRestriction restriction in this.Restrictions) + { + ifCond.Append(restriction.GetCheckCodeValid(lenVarName)); + ifCond.Append(" || "); + + lenVarUsed = true; + } + + ifCond.Length -= 4; + + IfThenElse ite = new IfThenElse(ifCond.ToString()); + ite.AddCode(String.Format("{0} = {1};", retErrVarName, LwipDefs.Def_ErrorCode_Ok)); + container.AddElement(ite); + } + + public override int OidRepresentationLen + { + get + { + // check restrictions if we are set to one fixed length + if ((this.Restrictions != null) && (this.Restrictions.Count > 0)) + { + foreach (IRestriction restriction in this.Restrictions) + { + if (restriction is IsInRangeRestriction) + { + if ((restriction as IsInRangeRestriction).RangeStart == (restriction as IsInRangeRestriction).RangeEnd) + { + return (int)(restriction as IsInRangeRestriction).RangeStart; + } + } + else if (restriction is IsEqualRestriction) + { + return (int)(restriction as IsEqualRestriction).Value; + } + } + } + + return -1; // variable length + } + } + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeTruthValue.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeTruthValue.cs new file mode 100644 index 0000000..7649da0 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeTruthValue.cs @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarNodeTruthValue : SnmpScalarNodeInt + { + public SnmpScalarNodeTruthValue(SnmpTreeNode parentNode) + : base(parentNode) + { + } + + protected override void GenerateGetMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string retLenVarName) + { + container.AddCodeFormat("snmp_encode_truthvalue({0}, /* TODO: put requested bool value here */ 0);", localValueVarName); + localValueVarUsed = true; + + container.AddCode(String.Format("{0} = {1};", + retLenVarName, + (!String.IsNullOrWhiteSpace(this.FixedValueLength)) ? this.FixedValueLength : "0")); + } + + protected override void GenerateSetMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + VariableType truthVar = new VariableType("bool_value", LwipDefs.Vt_U8); + container.Declarations.Add(new VariableDeclaration(truthVar)); + + container.AddCodeFormat("snmp_decode_truthvalue({0}, &{1});", localValueVarName, truthVar.Name); + localValueVarUsed = true; + + container.AddElement(new Comment(String.Format("TODO: store new value contained in '{0}' here", truthVar.Name), singleLine: true)); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeUint.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeUint.cs new file mode 100644 index 0000000..d3b1209 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpScalarNodeUint.cs @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpScalarNodeUint : SnmpScalarNode + { + public SnmpScalarNodeUint(SnmpDataType dataType, SnmpTreeNode parentNode) + : base(parentNode) + { + System.Diagnostics.Debug.Assert( + (dataType == SnmpDataType.Counter) || + (dataType == SnmpDataType.Gauge) || + (dataType == SnmpDataType.TimeTicks)); + + this.DataType = dataType; + } + + protected override void GenerateTestMethodCodeCore(CodeContainerBase container, string localValueVarName, ref bool localValueVarUsed, string lenVarName, ref bool lenVarUsed, string retErrVarName) + { + System.Diagnostics.Trace.Assert(this.Restrictions.Count > 0); + + StringBuilder ifCond = new StringBuilder(); + foreach (IRestriction restriction in this.Restrictions) + { + ifCond.Append(restriction.GetCheckCodeValid("*" + localValueVarName)); + ifCond.Append(" || "); + + localValueVarUsed = true; + } + + ifCond.Length -= 4; + + IfThenElse ite = new IfThenElse(ifCond.ToString()); + ite.AddCode(String.Format("{0} = {1};", retErrVarName, LwipDefs.Def_ErrorCode_Ok)); + container.AddElement(ite); + } + + protected override bool GenerateValueDeclaration(CodeContainerBase container, string variableName, string sourceName) + { + container.AddDeclaration(new VariableDeclaration( + new VariableType(variableName, LwipDefs.Vt_U32, "*"), + "(" + new VariableType(null, LwipDefs.Vt_U32, "*") + ")" + sourceName)); + + return true; + } + + public override string FixedValueLength + { + get { return String.Format("sizeof({0})", LwipDefs.Vt_U32); } + } + + public override int OidRepresentationLen + { + get { return 1; } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpTableNode.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpTableNode.cs new file mode 100644 index 0000000..ee3cddb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpTableNode.cs @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpTableNode: SnmpScalarAggregationNode + { + private readonly List cellNodes = new List(); + private readonly List indexNodes = new List(); + private string augmentedTableRow = null; + + + public SnmpTableNode(SnmpTreeNode parentNode) + : base(parentNode) + { + } + + public List CellNodes + { + get { return cellNodes; } + } + + public List IndexNodes + { + get { return indexNodes; } + } + + public string AugmentedTableRow + { + get { return this.augmentedTableRow; } + set { this.augmentedTableRow = value; } + } + + public override string FullNodeName + { + get + { + string result = this.Name.ToLowerInvariant(); + if (!result.Contains("table")) + { + result += "_table"; + } + + return result; + } + } + + protected override IEnumerable AggregatedScalarNodes + { + get { return this.cellNodes; } + } + + public override void GenerateCode(MibCFile mibFile) + { + FunctionDeclaration getInstanceMethodDecl = new FunctionDeclaration(this.FullNodeName + LwipDefs.FnctSuffix_GetInstance, isStatic: true); + getInstanceMethodDecl.Parameter.Add(new VariableType("column", LwipDefs.Vt_U32, "*", ConstType.Value)); + getInstanceMethodDecl.Parameter.Add(new VariableType("row_oid", LwipDefs.Vt_U32, "*", ConstType.Value)); + getInstanceMethodDecl.Parameter.Add(new VariableType("row_oid_len", LwipDefs.Vt_U8, "")); + getInstanceMethodDecl.Parameter.Add(new VariableType("cell_instance", LwipDefs.Vt_StNodeInstance, "*")); + getInstanceMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_Snmp_err); + mibFile.Declarations.Add(getInstanceMethodDecl); + + Function getInstanceMethod = Function.FromDeclaration(getInstanceMethodDecl); + GenerateGetInstanceMethodCode(getInstanceMethod); + mibFile.Implementation.Add(getInstanceMethod); + + + FunctionDeclaration getNextInstanceMethodDecl = new FunctionDeclaration(this.FullNodeName + LwipDefs.FnctSuffix_GetNextInstance, isStatic: true); + getNextInstanceMethodDecl.Parameter.Add(new VariableType("column", LwipDefs.Vt_U32, "*", ConstType.Value)); + getNextInstanceMethodDecl.Parameter.Add(new VariableType("row_oid", LwipDefs.Vt_StObjectId, "*")); + getNextInstanceMethodDecl.Parameter.Add(new VariableType("cell_instance", LwipDefs.Vt_StNodeInstance, "*")); + getNextInstanceMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_Snmp_err); + mibFile.Declarations.Add(getNextInstanceMethodDecl); + + Function getNextInstanceMethod = Function.FromDeclaration(getNextInstanceMethodDecl); + GenerateGetNextInstanceMethodCode(getNextInstanceMethod); + mibFile.Implementation.Add(getNextInstanceMethod); + + + VariableType instanceType = new VariableType("cell_instance", LwipDefs.Vt_StNodeInstance, "*"); + GenerateAggregatedCode( + mibFile, + instanceType, + String.Format("SNMP_TABLE_GET_COLUMN_FROM_OID({0}->instance_oid.id)", instanceType.Name)); + + + #region create and add column/table definitions + + StringBuilder colDefs = new StringBuilder(); + foreach (SnmpScalarNode colNode in this.cellNodes) + { + colDefs.AppendFormat(" {{{0}, {1}, {2}}}, /* {3} */ \n", + colNode.Oid, + LwipDefs.GetAsn1DefForSnmpDataType(colNode.DataType), + LwipDefs.GetLwipDefForSnmpAccessMode(colNode.AccessMode), + colNode.Name); + } + if (colDefs.Length > 0) + { + colDefs.Length--; + } + + VariableDeclaration colDefsDecl = new VariableDeclaration( + new VariableType(this.FullNodeName + "_columns", LwipDefs.Vt_StTableColumnDef, null, ConstType.Value, String.Empty), + "{\n" + colDefs + "\n}", + isStatic: true); + + mibFile.Declarations.Add(colDefsDecl); + + string nodeInitialization = String.Format("SNMP_TABLE_CREATE({0}, {1}, {2}, {3}, {4}, {5}, {6})", + this.Oid, + colDefsDecl.Type.Name, + getInstanceMethodDecl.Name, getNextInstanceMethodDecl.Name, + (this.GetMethodRequired) ? this.GetMethodName : LwipDefs.Null, + (this.TestMethodRequired) ? this.TestMethodName : LwipDefs.Null, + (this.SetMethodRequired) ? this.SetMethodName : LwipDefs.Null + ); + + mibFile.Declarations.Add(new VariableDeclaration( + new VariableType(this.FullNodeName, LwipDefs.Vt_StTableNode, null, ConstType.Value), + nodeInitialization, + isStatic: true)); + + #endregion + } + + protected virtual void GenerateGetInstanceMethodCode(Function getInstanceMethod) + { + VariableDeclaration returnValue = new VariableDeclaration((VariableType)getInstanceMethod.ReturnType.Clone(), LwipDefs.Def_ErrorCode_NoSuchInstance); + returnValue.Type.Name = "err"; + getInstanceMethod.Declarations.Add(returnValue); + + int instanceOidLength = 0; + StringBuilder indexColumns = new StringBuilder(); + foreach (SnmpScalarNode indexNode in this.indexNodes) + { + if (instanceOidLength >= 0) + { + if (indexNode.OidRepresentationLen >= 0) + { + instanceOidLength += indexNode.OidRepresentationLen; + } + else + { + // at least one index column has a variable length -> we cannot perform a static check + instanceOidLength = -1; + } + } + + indexColumns.AppendFormat( + " {0} ({1}, OID length = {2})\n", + indexNode.Name, + indexNode.DataType, + (indexNode.OidRepresentationLen >= 0) ? indexNode.OidRepresentationLen.ToString() : "variable"); + } + if (indexColumns.Length > 0) + { + indexColumns.Length--; + + getInstanceMethod.Declarations.Insert(0, new Comment(String.Format( + "The instance OID of this table consists of following (index) column(s):\n{0}", + indexColumns))); + } + + string augmentsHint = ""; + if (!String.IsNullOrWhiteSpace(this.augmentedTableRow)) + { + augmentsHint = String.Format( + "This table augments table '{0}'! Index columns therefore belong to table '{0}'!\n" + + "You may simply call the '*{1}' method of this table.\n\n", + (this.augmentedTableRow.ToLowerInvariant().EndsWith("entry")) ? this.augmentedTableRow.Substring(0, this.augmentedTableRow.Length-5) : this.augmentedTableRow, + LwipDefs.FnctSuffix_GetInstance); + } + + CodeContainerBase ccb = getInstanceMethod; + if (instanceOidLength > 0) + { + IfThenElse ite = new IfThenElse(String.Format("{0} == {1}", getInstanceMethod.Parameter[2].Name, instanceOidLength)); + getInstanceMethod.AddElement(ite); + ccb = ite; + } + + ccb.AddCodeFormat("LWIP_UNUSED_ARG({0});", getInstanceMethod.Parameter[0].Name); + ccb.AddCodeFormat("LWIP_UNUSED_ARG({0});", getInstanceMethod.Parameter[1].Name); + if (instanceOidLength <= 0) + { + ccb.AddCodeFormat("LWIP_UNUSED_ARG({0});", getInstanceMethod.Parameter[2].Name); + } + ccb.AddCodeFormat("LWIP_UNUSED_ARG({0});", getInstanceMethod.Parameter[3].Name); + + ccb.AddElement(new Comment(String.Format( + "TODO: check if '{0}'/'{1}' params contain a valid instance oid for a row\n" + + "If so, set '{2} = {3};'\n\n" + + "snmp_oid_* methods may be used for easier processing of oid\n\n" + + "{4}" + + "In order to avoid decoding OID a second time in subsequent get_value/set_test/set_value methods,\n" + + "you may store an arbitrary value (like a pointer to target value object) in '{5}->reference'/'{5}->reference_len'.\n" + + "But be aware that not always a subsequent method is called -> Do NOT allocate memory here and try to release it in subsequent methods!\n\n" + + "You also may replace function pointers in '{5}' param for get/test/set methods which contain the default values from table definition,\n" + + "in order to provide special methods, for the currently processed cell. Changed pointers are only valid for current request.", + getInstanceMethod.Parameter[1].Name, + getInstanceMethod.Parameter[2].Name, + returnValue.Type.Name, + LwipDefs.Def_ErrorCode_Ok, + augmentsHint, + getInstanceMethod.Parameter[3].Name + ))); + + getInstanceMethod.AddCodeFormat("return {0};", returnValue.Type.Name); + } + + protected virtual void GenerateGetNextInstanceMethodCode(Function getNextInstanceMethod) + { + getNextInstanceMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getNextInstanceMethod.Parameter[0].Name); + getNextInstanceMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getNextInstanceMethod.Parameter[1].Name); + getNextInstanceMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getNextInstanceMethod.Parameter[2].Name); + + VariableDeclaration returnValue = new VariableDeclaration((VariableType)getNextInstanceMethod.ReturnType.Clone(), LwipDefs.Def_ErrorCode_NoSuchInstance); + returnValue.Type.Name = "err"; + getNextInstanceMethod.Declarations.Add(returnValue); + + StringBuilder indexColumns = new StringBuilder(); + foreach (SnmpScalarNode indexNode in this.indexNodes) + { + indexColumns.AppendFormat( + " {0} ({1}, OID length = {2})\n", + indexNode.Name, + indexNode.DataType, + (indexNode.OidRepresentationLen >= 0) ? indexNode.OidRepresentationLen.ToString() : "variable"); + } + if (indexColumns.Length > 0) + { + indexColumns.Length--; + + getNextInstanceMethod.Declarations.Insert(0, new Comment(String.Format( + "The instance OID of this table consists of following (index) column(s):\n{0}", + indexColumns))); + } + + string augmentsHint = ""; + if (!String.IsNullOrWhiteSpace(this.augmentedTableRow)) + { + augmentsHint = String.Format( + "This table augments table '{0}'! Index columns therefore belong to table '{0}'!\n" + + "You may simply call the '*{1}' method of this table.\n\n", + (this.augmentedTableRow.ToLowerInvariant().EndsWith("entry")) ? this.augmentedTableRow.Substring(0, this.augmentedTableRow.Length-5) : this.augmentedTableRow, + LwipDefs.FnctSuffix_GetNextInstance); + } + + getNextInstanceMethod.AddElement(new Comment(String.Format( + "TODO: analyze '{0}->id'/'{0}->len' and return the subsequent row instance\n" + + "Be aware that '{0}->id'/'{0}->len' must not point to a valid instance or have correct instance length.\n" + + "If '{0}->len' is 0, return the first instance. If '{0}->len' is longer than expected, cut superfluous OID parts.\n" + + "If a valid next instance is found, store it in '{0}->id'/'{0}->len' and set '{1} = {2};'\n\n" + + "snmp_oid_* methods may be used for easier processing of oid\n\n" + + "{3}" + + "In order to avoid decoding OID a second time in subsequent get_value/set_test/set_value methods,\n" + + "you may store an arbitrary value (like a pointer to target value object) in '{4}->reference'/'{4}->reference_len'.\n" + + "But be aware that not always a subsequent method is called -> Do NOT allocate memory here and try to release it in subsequent methods!\n\n" + + "You also may replace function pointers in '{4}' param for get/test/set methods which contain the default values from table definition,\n" + + "in order to provide special methods, for the currently processed cell. Changed pointers are only valid for current request.", + getNextInstanceMethod.Parameter[1].Name, + returnValue.Type.Name, + LwipDefs.Def_ErrorCode_Ok, + augmentsHint, + getNextInstanceMethod.Parameter[2].Name + ))); + + getNextInstanceMethod.AddElement(new Comment(String.Format( + "For easier processing and getting the next instance, you may use the 'snmp_next_oid_*' enumerator.\n" + + "Simply pass all known instance OID's to it and it returns the next valid one:\n\n" + + "{0} state;\n" + + "{1} result_buf;\n" + + "snmp_next_oid_init(&state, {2}->id, {2}->len, result_buf, LWIP_SNMP_OBJ_ID_LEN);\n" + + "while ({{not all instances passed}}) {{\n" + + " {1} test_oid;\n" + + " {{fill test_oid to create instance oid for next instance}}\n" + + " snmp_next_oid_check(&state, test_oid->id, test_oid->len, {{target_data_ptr}});\n" + + "}}\n" + + "if(state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {{\n" + + " snmp_oid_assign(row_oid, result_buf->oid, result_buf->len);\n" + + " {3}->reference.ptr = state.reference; //==target_data_ptr, for usage in subsequent get/test/set\n" + + " {4} = {5};\n" + + "}}" + , + LwipDefs.Vt_StNextOidState, + LwipDefs.Vt_StObjectId, + getNextInstanceMethod.Parameter[1].Name, + getNextInstanceMethod.Parameter[2].Name, + returnValue.Type.Name, + LwipDefs.Def_ErrorCode_Ok + ))); + + getNextInstanceMethod.AddCodeFormat("return {0};", returnValue.Type.Name); + } + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpTreeNode.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpTreeNode.cs new file mode 100644 index 0000000..0a75e7f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/SnmpTreeNode.cs @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +using System; +using System.Collections.Generic; +using System.Text; +using CCodeGeneration; + +namespace LwipSnmpCodeGeneration +{ + public class SnmpTreeNode: SnmpScalarAggregationNode + { + private readonly List childNodes = new List(); + private readonly List childScalarNodes = new List(); + private string fullOid = ""; + + public SnmpTreeNode(SnmpTreeNode parentNode) + : base(parentNode) + { + } + + public override string FullNodeName + { + get { return this.Name + "_treenode"; } + } + + public string FullOid + { + get { return this.fullOid; } + set { this.fullOid = value; } + } + + public List ChildNodes + { + get { return this.childNodes; } + } + + protected override IEnumerable AggregatedScalarNodes + { + get { return this.childScalarNodes; } + } + + private void GenerateAggregatedCode(MibCFile mibFile, bool generateDeclarations, bool generateImplementations) + { + VariableType instanceType = new VariableType("instance", LwipDefs.Vt_StNodeInstance, "*"); + base.GenerateAggregatedCode( + mibFile, + instanceType, + String.Format("{0}->node->oid", instanceType.Name), + generateDeclarations, + generateImplementations); + } + + private void GenerateAggregateMethodDeclarations(MibCFile mibFile) + { + if (LwipOpts.GenerateSingleAccessMethodsForTreeNodeScalars && (this.childScalarNodes.Count > 1)) + { + GenerateAggregatedCode(mibFile, true, false); + } + } + + public override void GenerateCode(MibCFile mibFile) + { + string nodeInitialization; + + if (LwipOpts.GenerateSingleAccessMethodsForTreeNodeScalars && (this.childScalarNodes.Count > 1)) + { + GenerateAggregatedCode(mibFile, false, true); + } + + // create and add node declaration + if (this.childNodes.Count > 0) + { + StringBuilder subnodeArrayInitialization = new StringBuilder(); + + for (int i=0; i=0; i--) + { + this.ChildNodes[i].Analyze(); + } + + // collect scalar nodes + foreach (SnmpNode childNode in this.childNodes) + { + SnmpScalarNode scalarNode = childNode as SnmpScalarNode; + if (scalarNode != null) + { + this.childScalarNodes.Add(scalarNode); + } + } + + base.Analyze(); + + // check if we can merge this node to a scalar array node (all childs need to be scalars) + if (this.childNodes.Count > 0) + { + if (LwipOpts.GenerateScalarArrays && (this.childScalarNodes.Count == this.childNodes.Count) && (this.ParentNode != null)) + { + SnmpScalarArrayNode scalarArrayNode = new SnmpScalarArrayNode(this.childScalarNodes, this.ParentNode); + scalarArrayNode.Oid = this.Oid; + scalarArrayNode.Name = this.Name; + + for (int i=0; i 1)) + { + foreach (SnmpScalarNode scalarNode in this.childScalarNodes) + { + scalarNode.UseExternalMethods = true; + scalarNode.ExternalGetMethod = this.GetMethodName; + scalarNode.ExternalTestMethod = this.TestMethodName; + scalarNode.ExternalSetMethod = this.SetMethodName; + } + } + } + else // if (this.childNodes.Count == 0) + { + if (!LwipOpts.GenerateEmptyFolders && (this.ParentNode != null)) + { + // do not generate this empty folder because it only waste (static) memory + for (int i=0; i + prtAlertTime the value of sysUpTime at + the time of the removal of the + binary change event from the + alert table. + + Optionally, the agent may generate a trap coincident with + + removing the binary change event and placing the unary change + event alertRemovalOfBinaryChangeEntry(1801) in the alert + table. For such a trap, the prtAlertIndex sent with the above + trap parameters shall be the index of the + alertRemovalOfBinaryChangeEvent row that was added to the + prtAlertTable; not the index of the row that was removed from + the prtAlertTable." + SYNTAX INTEGER { + other(1), + -- an event that is not represented + -- by one of the alert codes + -- specified below. + unknown(2), + -- The following generic codes are common to + -- multiple groups. The NMS may examine the + -- prtAlertGroup object to determine what group + -- to query for further information. + coverOpen(3), + coverClosed(4), + interlockOpen(5), + interlockClosed(6), + configurationChange(7), + jam(8), + subunitMissing(9), -- Not in RFC 1759 + -- The subunit tray, bin, etc. + -- has been removed. + subunitLifeAlmostOver(10), -- Not in RFC 1759 + subunitLifeOver(11), -- Not in RFC 1759 + subunitAlmostEmpty(12), -- Not in RFC 1759 + subunitEmpty(13), -- Not in RFC 1759 + subunitAlmostFull(14), -- Not in RFC 1759 + subunitFull(15), -- Not in RFC 1759 + subunitNearLimit(16), -- Not in RFC 1759 + subunitAtLimit(17), -- Not in RFC 1759 + subunitOpened(18), -- Not in RFC 1759 + subunitClosed(19), -- Not in RFC 1759 + subunitTurnedOn(20), -- Not in RFC 1759 + subunitTurnedOff(21), -- Not in RFC 1759 + subunitOffline(22), -- Not in RFC 1759 + subunitPowerSaver(23), -- Not in RFC 1759 + subunitWarmingUp(24), -- Not in RFC 1759 + subunitAdded(25), -- Not in RFC 1759 + subunitRemoved(26), -- Not in RFC 1759 + subunitResourceAdded(27), -- Not in RFC 1759 + subunitResourceRemoved(28), -- Not in RFC 1759 + subunitRecoverableFailure(29), + -- Not in RFC 1759 + subunitUnrecoverableFailure(30), + + -- Not in RFC 1759 + subunitRecoverableStorageError(31), + -- Not in RFC 1759 + subunitUnrecoverableStorageError(32), + -- Not in RFC 1759 + subunitMotorFailure(33), -- Not in RFC 1759 + subunitMemoryExhausted(34), -- Not in RFC 1759 + subunitUnderTemperature(35), -- Not in RFC 1759 + subunitOverTemperature(36), -- Not in RFC 1759 + subunitTimingFailure(37), -- Not in RFC 1759 + subunitThermistorFailure(38), -- Not in RFC 1759 + + -- General Printer group + doorOpen(501), -- DEPRECATED + -- Use coverOpened(3) + doorClosed(502), -- DEPRECATED + -- Use coverClosed(4) + powerUp(503), + powerDown(504), + printerNMSReset(505), -- Not in RFC 1759 + -- The printer has been reset by some + -- network management station(NMS) + -- writing into 'prtGeneralReset'. + printerManualReset(506), -- Not in RFC 1759 + -- The printer has been reset manually. + printerReadyToPrint(507), -- Not in RFC 1759 + -- The printer is ready to print. (i.e., + -- not warming up, not in power save + -- state, not adjusting print quality, + -- etc.). + + -- Input Group + inputMediaTrayMissing(801), + inputMediaSizeChange(802), + inputMediaWeightChange(803), + inputMediaTypeChange(804), + inputMediaColorChange(805), + inputMediaFormPartsChange(806), + inputMediaSupplyLow(807), + inputMediaSupplyEmpty(808), + inputMediaChangeRequest(809), -- Not in RFC 1759 + -- An interpreter has detected that a + -- different medium is need in this input + -- tray subunit. The prtAlertDescription may + -- be used to convey a human readable + -- description of the medium required to + -- satisfy the request. + inputManualInputRequest(810), -- Not in RFC 1759 + + -- An interpreter has detected that manual + -- input is required in this subunit. The + -- prtAlertDescription may be used to convey + -- a human readable description of the medium + -- required to satisfy the request. + inputTrayPositionFailure(811), -- Not in RFC 1759 + -- The input tray failed to position correctly. + inputTrayElevationFailure(812), + -- Not in RFC 1759 + inputCannotFeedSizeSelected(813), + -- Not in RFC 1759 + -- Output Group + outputMediaTrayMissing(901), + outputMediaTrayAlmostFull(902), + outputMediaTrayFull(903), + outputMailboxSelectFailure(904), + -- Not in RFC 1759 + -- Marker group + markerFuserUnderTemperature(1001), + markerFuserOverTemperature(1002), + markerFuserTimingFailure(1003), + -- Not in RFC 1759 + markerFuserThermistorFailure(1004), + -- Not in RFC 1759 + markerAdjustingPrintQuality(1005), + -- Not in RFC 1759 + -- Marker Supplies group + markerTonerEmpty(1101), + markerInkEmpty(1102), + markerPrintRibbonEmpty(1103), + markerTonerAlmostEmpty(1104), + markerInkAlmostEmpty(1105), + markerPrintRibbonAlmostEmpty(1106), + markerWasteTonerReceptacleAlmostFull(1107), + markerWasteInkReceptacleAlmostFull(1108), + markerWasteTonerReceptacleFull(1109), + markerWasteInkReceptacleFull(1110), + markerOpcLifeAlmostOver(1111), + markerOpcLifeOver(1112), + markerDeveloperAlmostEmpty(1113), + markerDeveloperEmpty(1114), + markerTonerCartridgeMissing(1115), + -- Not in RFC 1759 + -- Media Path Device Group + mediaPathMediaTrayMissing(1301), + mediaPathMediaTrayAlmostFull(1302), + mediaPathMediaTrayFull(1303), + mediaPathCannotDuplexMediaSelected(1304), + + -- Not in RFC 1759 + -- Interpreter Group + interpreterMemoryIncrease(1501), + interpreterMemoryDecrease(1502), + interpreterCartridgeAdded(1503), + interpreterCartridgeDeleted(1504), + interpreterResourceAdded(1505), + interpreterResourceDeleted(1506), + interpreterResourceUnavailable(1507), + interpreterComplexPageEncountered(1509), + -- Not in RFC 1759 + -- The interpreter has encountered a page + -- that is too complex for the resources that + -- are available. + -- Alert Group + alertRemovalOfBinaryChangeEntry(1801) + -- Not in RFC 1759 + -- A binary change event entry has been + -- removed from the alert table. This unary + -- change alert table entry is added to the + -- end of the alert table. + } +END + + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-RTPROTO-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-RTPROTO-MIB new file mode 100644 index 0000000..caec2f2 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANA-RTPROTO-MIB @@ -0,0 +1,92 @@ + +IANA-RTPROTO-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, mib-2 FROM SNMPv2-SMI + TEXTUAL-CONVENTION FROM SNMPv2-TC; + +ianaRtProtoMIB MODULE-IDENTITY + LAST-UPDATED "200009260000Z" -- September 26, 2000 + ORGANIZATION "IANA" + CONTACT-INFO + " Internet Assigned Numbers Authority + Internet Corporation for Assigned Names and Numbers + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292-6601 + + Phone: +1 310 823 9358 + EMail: iana&iana.org" + DESCRIPTION + "This MIB module defines the IANAipRouteProtocol and + IANAipMRouteProtocol textual conventions for use in MIBs + which need to identify unicast or multicast routing + mechanisms. + + Any additions or changes to the contents of this MIB module + require either publication of an RFC, or Designated Expert + Review as defined in RFC 2434, Guidelines for Writing an + IANA Considerations Section in RFCs. The Designated Expert + will be selected by the IESG Area Director(s) of the Routing + Area." + + REVISION "200009260000Z" -- September 26, 2000 + DESCRIPTION "Original version, published in coordination + with RFC 2932." + + ::= { mib-2 84 } + +IANAipRouteProtocol ::= TEXTUAL-CONVENTION + STATUS current + + DESCRIPTION + "A mechanism for learning routes. Inclusion of values for + routing protocols is not intended to imply that those + protocols need be supported." + SYNTAX INTEGER { + other (1), -- not specified + local (2), -- local interface + netmgmt (3), -- static route + icmp (4), -- result of ICMP Redirect + + -- the following are all dynamic + -- routing protocols + + egp (5), -- Exterior Gateway Protocol + ggp (6), -- Gateway-Gateway Protocol + hello (7), -- FuzzBall HelloSpeak + rip (8), -- Berkeley RIP or RIP-II + isIs (9), -- Dual IS-IS + esIs (10), -- ISO 9542 + ciscoIgrp (11), -- Cisco IGRP + bbnSpfIgp (12), -- BBN SPF IGP + ospf (13), -- Open Shortest Path First + bgp (14), -- Border Gateway Protocol + idpr (15), -- InterDomain Policy Routing + ciscoEigrp (16), -- Cisco EIGRP + dvmrp (17) -- DVMRP + } + +IANAipMRouteProtocol ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The multicast routing protocol. Inclusion of values for + multicast routing protocols is not intended to imply that + those protocols need be supported." + SYNTAX INTEGER { + other(1), -- none of the following + local(2), -- e.g., manually configured + netmgmt(3), -- set via net.mgmt protocol + dvmrp(4), + mospf(5), + pimSparseDense(6), -- PIMv1, both DM and SM + cbt(7), + pimSparseMode(8), -- PIM-SM + pimDenseMode(9), -- PIM-DM + igmpOnly(10), + bgmp(11), + msdp(12) + } + +END + + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANATn3270eTC-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANATn3270eTC-MIB new file mode 100644 index 0000000..be7b053 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANATn3270eTC-MIB @@ -0,0 +1,306 @@ + + IANATn3270eTC-MIB DEFINITIONS ::= BEGIN + + IMPORTS + MODULE-IDENTITY, mib-2 + FROM SNMPv2-SMI + TEXTUAL-CONVENTION + FROM SNMPv2-TC; + + ianaTn3270eTcMib MODULE-IDENTITY + LAST-UPDATED "200005100000Z" -- May 10, 2000 + ORGANIZATION "IANA" + CONTACT-INFO + "Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 x20 + E-Mail: iana&iana.org" + DESCRIPTION + "This module defines a set of textual conventions + for use by the TN3270E-MIB and the TN3270E-RT-MIB. + + Any additions or changes to the contents of this + MIB module must first be discussed on the tn3270e + working group list at: tn3270e&list.nih.gov + and approved by one of the following TN3270E + working group contacts: + + Ed Bailey (co-chair) - elbailey&us.ibm.com + Michael Boe (co-chair) - mboe&cisco.com + Ken White - kennethw&vnet.ibm.com + Robert Moore - remoore&us.ibm.com + + The above list of contacts can be altered with + the approval of the two co-chairs. + + The Textual Conventions defined within this MIB have + no security issues associated with them unless + explicitly stated in their corresponding + DESCRIPTION clause." + + -- revision log, in reverse chronological order + + REVISION "200005100000Z" -- May 10, 2000 + DESCRIPTION "Fix to import mib-2 instead of experimental." + + REVISION "199909011000Z" -- September 1, 1999 + DESCRIPTION + "Initial version transferred from the TN3270E + working group to IANA." + + ::= { mib-2 61 } + + + -- Textual Conventions + + IANATn3270eAddrType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The textual convention for defining the type of a + client address. The enumeration value unknown(0) is + also used to indicate that no actual address is present." + SYNTAX INTEGER { + unknown(0), + ipv4(1), + ipv6(2) + } + + IANATn3270eAddress ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Denotes a client address. The type of client address is + determined by use of the IANATn3270eAddrType textual +convention. + The length in octets of a IANATn3270eAddress object is: + + IANATn3270eAddrType Address Length + +++++++++++++++++++ ++++++++++++++ + unknown(0) not specified or unknown; the + actual length of the + IANATn3270eAddress octet string + indicates if an address + is present + ipv4(1) 4 OCTETS + ipv6(2) 16 OCTETS + + This textual convention is similar to the TAddress + TC defined by RFC1903 except that it allows a + zero-length octet string and is not a full transport + layer address." + SYNTAX OCTET STRING (SIZE (0..255)) + + IANATn3270eClientType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The textual convention for defining the set of + enumerations used by tn3270eTcpConnClientIdFormat + in the TN3270E-MIB: + + ENUMERATION OCTETs DESCRIPTION + + none(1) 0 Not specified + other(2) 1..512 Implementation specific + ipv4(3) 6 4-octet IP Address plus + 2-octet TCP Port + ipv6(4) 18 16-octet IPv6 Address + plus 2-octet TCP Port + domainName(5) 1..512 The DNS name of a + client. + truncDomainName(6) 1..512 The (truncated) DNS name + of a client. + string(7) 1..512 Unknown Utf8String + certificate(8) 1..512 certificate + userId(9) 1..8 Client's userid + x509dn(10) 1..512 X.509 Distinguished Name + + Representation of a certificate(8) may be lead to + a security exposure and is NOT RECOMMENDED without + adequate security." + SYNTAX INTEGER { + none(1), + other(2), + ipv4(3), + ipv6(4), + domainName(5), + truncDomainName(6), + string(7), + certificate(8), + userId(9), + x509dn(10) + } + + IANATn3270Functions ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "This textual convention reflects the current set of + TN3270 and TN3270E functions that can be negotiated + between a server and its client: + + RFC856 + transmitBinary The sender of this command REQUESTS + permission to begin transmitting, or + confirms that it will now begin + transmitting characters which are to + be interpreted as 8 bits of binary + data by the receiver of the data. + RFC860 + timingMark The sender of this command REQUESTS + that the receiver of this command + return a WILL TIMING-MARK in the data + stream at the 'appropriate place'. + RFC885 + endOfRecord The sender of this command requests + permission to begin transmission of + the Telnet END-OF-RECORD (EOR) code + when transmitting data characters, or + the sender of this command confirms it + will now begin transmission of EORs + with transmitted data characters. + RFC1091 + terminalType Sender is willing to send terminal + type information in a subsequent + sub-negotiation. + + RFC1041 + tn3270Regime Sender is willing to send list of + supported 3270 Regimes in a + subsequent sub-negotiation. + RFC2355 + scsCtlCodes (Printer sessions only). Allows the + use of the SNA Character Stream (SCS) + and SCS control codes on the session. + SCS is used with LU type 1 SNA sessions. + dataStreamCtl (Printer sessions only). Allows the use + of the standard 3270 data stream. This + corresponds to LU type 3 SNA sessions. + responses Provides support for positive and + negative response handling. Allows the + server to reflect to the client any and + all definite, exception, and no response + requests sent by the host application. + bindImage Allows the server to send the SNA Bind + image and Unbind notification to the + client. + sysreq Allows the client and server to emulate + some (or all, depending on the server) of + the functions of the SYSREQ key in an SNA + environment." + SYNTAX BITS { + transmitBinary(0),-- rfc856 + timemark(1), -- rfc860 + endOfRecord(2), -- rfc885 + terminalType(3), -- rfc1091 + tn3270Regime(4), -- rfc1041 + scsCtlCodes(5), -- rfc2355 + dataStreamCtl(6), -- rfc2355 + responses(7), -- rfc2355 + bindImage(8), -- rfc2355 + sysreq(9) -- rfc2355 + } + + IANATn3270ResourceType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The type of resource defined by a resource pool. Refer + to tn3270eResPoolTable." + SYNTAX INTEGER { + other(1), + terminal(2), + printer(3), + terminalOrPrinter(4) + } + + IANATn3270DeviceType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "This textual convention defines the list of device + types that can be set, as defined by RFC 2355." + SYNTAX INTEGER { + -- terminals + ibm3278d2(1), -- (24 row x 80 col display) + ibm3278d2E(2), -- (24 row x 80 col display) + ibm3278d3(3), -- (32 row x 80 col display) + ibm3278d3E(4), -- (32 row x 80 col display) + ibm3278d4(5), -- (43 row x 80 col display) + ibm3278d4E(6), -- (43 row x 80 col display) + ibm3278d5(7), -- (27 row x 132 col display) + ibm3278d5E(8), -- (27 row x 132 col display) + ibmDynamic(9), -- (no pre-defined display size) + + -- printers + ibm3287d1(10), + + unknown(100) + } + + IANATn3270eLogData ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "An octet string representing log data as pertaining to + either a TN3270 or TN3270E Session as reported from a + TN3270E Server. Log data is stored in an octet string + in time order (from earliest to latest). + + Each log element has the following form: + + +------+----+---------+------------+ + !length!type!TimeStamp! data ! + +------+----+---------+------------+ + + where + + length = one-octet length of the data portion of the + trace element, not including the length, + type, and TimeStamp fields + type = one-octet code point characterizing the data. + TimeStamp = A 4-octet field representing the number of + TimeTicks since the TN3270E server was last + activated. The server's last activation time + is available in the tn3270eSrvrConfLastActTime + object in the TN3270E MIB, which has the + syntax DateAndTime. + data = initial part of a PDU. + + length type + + 0-255 x'00' - unknown + 0 x'01' - inactivity timer expired + 0 x'02' - dynamic timer expired + 0 x'03' - actlu req + 0 x'04' - bind req + 0 x'05' - clear req + 0 x'06' - dactlu req + 0 x'07' - warm actpu req + 0 x'08' - sdt req + 0 x'09' - unbind req + 0 x'0A' - notify resp + 0 x'0B' - reply PSID neg rsp + 0 x'0C' - reply PSID pos rsp + 0 x'0D' - unbind rsp + 0 x'0E' - hierarchical reset + 0 x'0F' - client connect req + 0 x'10' - client disconnect req + 0 x'11' - timingmark received + 0 x'12' - flowControl timer expired + 0 x'13' - neg rsp to host + 0 x'14' - neg rsp from host + 0 x'15' - data contention + 0 x'16' - no buffer to send SNA data + 0 x'17' - receive response while inbound + 0 x'18' - client protocol error + 0 x'19' - badClientSequenceReceived + 1-255 x'1A' - utf8String + 2 x'1B' - hexCode, implementation dependent + + Log element entries have a minimum length of 6 octets. + The zero-length string indicates that no log data is + available." + SYNTAX OCTET STRING (SIZE (0 | 6..2048)) + + END + + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANAifType-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANAifType-MIB new file mode 100644 index 0000000..15edda9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IANA/IANAifType-MIB @@ -0,0 +1,572 @@ + IANAifType-MIB DEFINITIONS ::= BEGIN + + IMPORTS + MODULE-IDENTITY, mib-2 FROM SNMPv2-SMI + TEXTUAL-CONVENTION FROM SNMPv2-TC; + + ianaifType MODULE-IDENTITY + LAST-UPDATED "200709130000Z" -- September 13, 2007 + ORGANIZATION "IANA" + CONTACT-INFO " Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 + E-Mail: iana&iana.org" + + DESCRIPTION "This MIB module defines the IANAifType Textual + Convention, and thus the enumerated values of + the ifType object defined in MIB-II's ifTable." + + REVISION "200709130000Z" -- September 13, 2007 + DESCRIPTION "Registration of new IANAifTypes 243 and 244." + + REVISION "200705290000Z" -- May 29, 2007 + DESCRIPTION "Changed the description for IANAifType 228." + + REVISION "200703080000Z" -- March 08, 2007 + DESCRIPTION "Registration of new IANAifType 242." + + REVISION "200701230000Z" -- January 23, 2007 + DESCRIPTION "Registration of new IANAifTypes 239, 240, and 241." + + REVISION "200610170000Z" -- October 17, 2006 + DESCRIPTION "Deprecated/Obsoleted IANAifType 230. Registration of + IANAifType 238." + + REVISION "200609250000Z" -- September 25, 2006 + DESCRIPTION "Changed the description for IANA ifType + 184 and added new IANA ifType 237." + + REVISION "200608170000Z" -- August 17, 2006 + DESCRIPTION "Changed the descriptions for IANAifTypes + 20 and 21." + + REVISION "200608110000Z" -- August 11, 2006 + DESCRIPTION "Changed the descriptions for IANAifTypes + 7, 11, 62, 69, and 117." + + REVISION "200607250000Z" -- July 25, 2006 + DESCRIPTION "Registration of new IANA ifType 236." + + REVISION "200606140000Z" -- June 14, 2006 + DESCRIPTION "Registration of new IANA ifType 235." + + REVISION "200603310000Z" -- March 31, 2006 + DESCRIPTION "Registration of new IANA ifType 234." + + REVISION "200603300000Z" -- March 30, 2006 + DESCRIPTION "Registration of new IANA ifType 233." + + REVISION "200512220000Z" -- December 22, 2005 + DESCRIPTION "Registration of new IANA ifTypes 231 and 232." + + REVISION "200510100000Z" -- October 10, 2005 + DESCRIPTION "Registration of new IANA ifType 230." + + REVISION "200509090000Z" -- September 09, 2005 + DESCRIPTION "Registration of new IANA ifType 229." + + REVISION "200505270000Z" -- May 27, 2005 + DESCRIPTION "Registration of new IANA ifType 228." + + REVISION "200503030000Z" -- March 3, 2005 + DESCRIPTION "Added the IANAtunnelType TC and deprecated + IANAifType sixToFour (215) per RFC4087." + + REVISION "200411220000Z" -- November 22, 2004 + DESCRIPTION "Registration of new IANA ifType 227 per RFC4631." + + REVISION "200406170000Z" -- June 17, 2004 + DESCRIPTION "Registration of new IANA ifType 226." + + REVISION "200405120000Z" -- May 12, 2004 + DESCRIPTION "Added description for IANAifType 6, and + changed the descriptions for IANAifTypes + 180, 181, and 182." + + REVISION "200405070000Z" -- May 7, 2004 + DESCRIPTION "Registration of new IANAifType 225." + + REVISION "200308250000Z" -- Aug 25, 2003 + DESCRIPTION "Deprecated IANAifTypes 7 and 11. Obsoleted + IANAifTypes 62, 69, and 117. ethernetCsmacd (6) + should be used instead of these values" + + REVISION "200308180000Z" -- Aug 18, 2003 + DESCRIPTION "Registration of new IANAifType + 224." + + REVISION "200308070000Z" -- Aug 7, 2003 + DESCRIPTION "Registration of new IANAifTypes + 222 and 223." + + REVISION "200303180000Z" -- Mar 18, 2003 + DESCRIPTION "Registration of new IANAifType + 221." + + REVISION "200301130000Z" -- Jan 13, 2003 + DESCRIPTION "Registration of new IANAifType + 220." + + REVISION "200210170000Z" -- Oct 17, 2002 + DESCRIPTION "Registration of new IANAifType + 219." + + REVISION "200207160000Z" -- Jul 16, 2002 + DESCRIPTION "Registration of new IANAifTypes + 217 and 218." + + REVISION "200207100000Z" -- Jul 10, 2002 + DESCRIPTION "Registration of new IANAifTypes + 215 and 216." + + REVISION "200206190000Z" -- Jun 19, 2002 + DESCRIPTION "Registration of new IANAifType + 214." + + REVISION "200201040000Z" -- Jan 4, 2002 + DESCRIPTION "Registration of new IANAifTypes + 211, 212 and 213." + + REVISION "200112200000Z" -- Dec 20, 2001 + DESCRIPTION "Registration of new IANAifTypes + 209 and 210." + + REVISION "200111150000Z" -- Nov 15, 2001 + DESCRIPTION "Registration of new IANAifTypes + 207 and 208." + + + REVISION "200111060000Z" -- Nov 6, 2001 + DESCRIPTION "Registration of new IANAifType + 206." + + + REVISION "200111020000Z" -- Nov 2, 2001 + DESCRIPTION "Registration of new IANAifType + 205." + + + REVISION "200110160000Z" -- Oct 16, 2001 + DESCRIPTION "Registration of new IANAifTypes + 199, 200, 201, 202, 203, and 204." + + + REVISION "200109190000Z" -- Sept 19, 2001 + DESCRIPTION "Registration of new IANAifType + 198." + + REVISION "200105110000Z" -- May 11, 2001 + DESCRIPTION "Registration of new IANAifType + 197." + + + REVISION "200101120000Z" -- Jan 12, 2001 + DESCRIPTION "Registration of new IANAifTypes + 195 and 196." + + REVISION "200012190000Z" -- Dec 19, 2000 + DESCRIPTION "Registration of new IANAifTypes + 193 and 194." + + REVISION "200012070000Z" -- Dec 07, 2000 + DESCRIPTION "Registration of new IANAifTypes + 191 and 192." + + REVISION "200012040000Z" -- Dec 04, 2000 + DESCRIPTION "Registration of new IANAifType + 190." + + REVISION "200010170000Z" -- Oct 17, 2000 + DESCRIPTION "Registration of new IANAifTypes + 188 and 189." + + REVISION "200010020000Z" -- Oct 02, 2000 + DESCRIPTION "Registration of new IANAifType 187." + + REVISION "200009010000Z" -- Sept 01, 2000 + DESCRIPTION "Registration of new IANAifTypes + 184, 185, and 186." + + REVISION "200008240000Z" -- Aug 24, 2000 + DESCRIPTION "Registration of new IANAifType 183." + + REVISION "200008230000Z" -- Aug 23, 2000 + DESCRIPTION "Registration of new IANAifTypes + 174-182." + + REVISION "200008220000Z" -- Aug 22, 2000 + DESCRIPTION "Registration of new IANAifTypes 170, + 171, 172 and 173." + + REVISION "200004250000Z" -- Apr 25, 2000 + DESCRIPTION "Registration of new IANAifTypes 168 and 169." + + REVISION "200003060000Z" -- Mar 6, 2000 + DESCRIPTION "Fixed a missing semi-colon in the IMPORT. + Also cleaned up the REVISION log a bit. + It is not complete, but from now on it will + be maintained and kept up to date with each + change to this MIB module." + + REVISION "199910081430Z" -- Oct 08, 1999 + DESCRIPTION "Include new name assignments up to cnr(85). + This is the first version available via the WWW + at: ftp://ftp.isi.edu/mib/ianaiftype.mib" + + REVISION "199401310000Z" -- Jan 31, 1994 + DESCRIPTION "Initial version of this MIB as published in + RFC 1573." + + ::= { mib-2 30 } + + + IANAifType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "This data type is used as the syntax of the ifType + object in the (updated) definition of MIB-II's + ifTable. + + The definition of this textual convention with the + addition of newly assigned values is published + periodically by the IANA, in either the Assigned + Numbers RFC, or some derivative of it specific to + Internet Network Management number assignments. (The + latest arrangements can be obtained by contacting the + IANA.) + + Requests for new values should be made to IANA via + email (iana&iana.org). + + The relationship between the assignment of ifType + values and of OIDs to particular media-specific MIBs + is solely the purview of IANA and is subject to change + without notice. Quite often, a media-specific MIB's + OID-subtree assignment within MIB-II's 'transmission' + subtree will be the same as its ifType value. + However, in some circumstances this will not be the + case, and implementors must not pre-assume any + specific relationship between ifType values and + transmission subtree OIDs." + SYNTAX INTEGER { + other(1), -- none of the following + regular1822(2), + hdh1822(3), + ddnX25(4), + rfc877x25(5), + ethernetCsmacd(6), -- for all ethernet-like interfaces, + -- regardless of speed, as per RFC3635 + iso88023Csmacd(7), -- Deprecated via RFC3635 + -- ethernetCsmacd (6) should be used instead + iso88024TokenBus(8), + iso88025TokenRing(9), + iso88026Man(10), + starLan(11), -- Deprecated via RFC3635 + -- ethernetCsmacd (6) should be used instead + proteon10Mbit(12), + proteon80Mbit(13), + hyperchannel(14), + fddi(15), + lapb(16), + sdlc(17), + ds1(18), -- DS1-MIB + e1(19), -- Obsolete see DS1-MIB + basicISDN(20), -- no longer used + -- see also RFC2127 + primaryISDN(21), -- no longer used + -- see also RFC2127 + propPointToPointSerial(22), -- proprietary serial + ppp(23), + softwareLoopback(24), + eon(25), -- CLNP over IP + ethernet3Mbit(26), + nsip(27), -- XNS over IP + slip(28), -- generic SLIP + ultra(29), -- ULTRA technologies + ds3(30), -- DS3-MIB + sip(31), -- SMDS, coffee + frameRelay(32), -- DTE only. + rs232(33), + para(34), -- parallel-port + arcnet(35), -- arcnet + arcnetPlus(36), -- arcnet plus + atm(37), -- ATM cells + miox25(38), + sonet(39), -- SONET or SDH + x25ple(40), + iso88022llc(41), + localTalk(42), + smdsDxi(43), + frameRelayService(44), -- FRNETSERV-MIB + v35(45), + hssi(46), + hippi(47), + modem(48), -- Generic modem + aal5(49), -- AAL5 over ATM + sonetPath(50), + sonetVT(51), + smdsIcip(52), -- SMDS InterCarrier Interface + propVirtual(53), -- proprietary virtual/internal + propMultiplexor(54),-- proprietary multiplexing + ieee80212(55), -- 100BaseVG + fibreChannel(56), -- Fibre Channel + hippiInterface(57), -- HIPPI interfaces + frameRelayInterconnect(58), -- Obsolete use either + -- frameRelay(32) or + -- frameRelayService(44). + aflane8023(59), -- ATM Emulated LAN for 802.3 + aflane8025(60), -- ATM Emulated LAN for 802.5 + cctEmul(61), -- ATM Emulated circuit + fastEther(62), -- Obsoleted via RFC3635 + -- ethernetCsmacd (6) should be used instead + isdn(63), -- ISDN and X.25 + v11(64), -- CCITT V.11/X.21 + v36(65), -- CCITT V.36 + g703at64k(66), -- CCITT G703 at 64Kbps + g703at2mb(67), -- Obsolete see DS1-MIB + qllc(68), -- SNA QLLC + fastEtherFX(69), -- Obsoleted via RFC3635 + -- ethernetCsmacd (6) should be used instead + channel(70), -- channel + ieee80211(71), -- radio spread spectrum + ibm370parChan(72), -- IBM System 360/370 OEMI Channel + escon(73), -- IBM Enterprise Systems Connection + dlsw(74), -- Data Link Switching + isdns(75), -- ISDN S/T interface + isdnu(76), -- ISDN U interface + lapd(77), -- Link Access Protocol D + ipSwitch(78), -- IP Switching Objects + rsrb(79), -- Remote Source Route Bridging + atmLogical(80), -- ATM Logical Port + ds0(81), -- Digital Signal Level 0 + ds0Bundle(82), -- group of ds0s on the same ds1 + bsc(83), -- Bisynchronous Protocol + async(84), -- Asynchronous Protocol + cnr(85), -- Combat Net Radio + iso88025Dtr(86), -- ISO 802.5r DTR + eplrs(87), -- Ext Pos Loc Report Sys + arap(88), -- Appletalk Remote Access Protocol + propCnls(89), -- Proprietary Connectionless Protocol + hostPad(90), -- CCITT-ITU X.29 PAD Protocol + termPad(91), -- CCITT-ITU X.3 PAD Facility + frameRelayMPI(92), -- Multiproto Interconnect over FR + x213(93), -- CCITT-ITU X213 + adsl(94), -- Asymmetric Digital Subscriber Loop + radsl(95), -- Rate-Adapt. Digital Subscriber Loop + sdsl(96), -- Symmetric Digital Subscriber Loop + vdsl(97), -- Very H-Speed Digital Subscrib. Loop + iso88025CRFPInt(98), -- ISO 802.5 CRFP + myrinet(99), -- Myricom Myrinet + voiceEM(100), -- voice recEive and transMit + voiceFXO(101), -- voice Foreign Exchange Office + voiceFXS(102), -- voice Foreign Exchange Station + voiceEncap(103), -- voice encapsulation + voiceOverIp(104), -- voice over IP encapsulation + atmDxi(105), -- ATM DXI + atmFuni(106), -- ATM FUNI + atmIma (107), -- ATM IMA + pppMultilinkBundle(108), -- PPP Multilink Bundle + ipOverCdlc (109), -- IBM ipOverCdlc + ipOverClaw (110), -- IBM Common Link Access to Workstn + stackToStack (111), -- IBM stackToStack + virtualIpAddress (112), -- IBM VIPA + mpc (113), -- IBM multi-protocol channel support + ipOverAtm (114), -- IBM ipOverAtm + iso88025Fiber (115), -- ISO 802.5j Fiber Token Ring + tdlc (116), -- IBM twinaxial data link control + gigabitEthernet (117), -- Obsoleted via RFC3635 + -- ethernetCsmacd (6) should be used instead + hdlc (118), -- HDLC + lapf (119), -- LAP F + v37 (120), -- V.37 + x25mlp (121), -- Multi-Link Protocol + x25huntGroup (122), -- X25 Hunt Group + trasnpHdlc (123), -- Transp HDLC + interleave (124), -- Interleave channel + fast (125), -- Fast channel + ip (126), -- IP (for APPN HPR in IP networks) + docsCableMaclayer (127), -- CATV Mac Layer + docsCableDownstream (128), -- CATV Downstream interface + docsCableUpstream (129), -- CATV Upstream interface + a12MppSwitch (130), -- Avalon Parallel Processor + tunnel (131), -- Encapsulation interface + coffee (132), -- coffee pot + ces (133), -- Circuit Emulation Service + atmSubInterface (134), -- ATM Sub Interface + l2vlan (135), -- Layer 2 Virtual LAN using 802.1Q + l3ipvlan (136), -- Layer 3 Virtual LAN using IP + l3ipxvlan (137), -- Layer 3 Virtual LAN using IPX + digitalPowerline (138), -- IP over Power Lines + mediaMailOverIp (139), -- Multimedia Mail over IP + dtm (140), -- Dynamic syncronous Transfer Mode + dcn (141), -- Data Communications Network + ipForward (142), -- IP Forwarding Interface + msdsl (143), -- Multi-rate Symmetric DSL + ieee1394 (144), -- IEEE1394 High Performance Serial Bus + if-gsn (145), -- HIPPI-6400 + dvbRccMacLayer (146), -- DVB-RCC MAC Layer + dvbRccDownstream (147), -- DVB-RCC Downstream Channel + dvbRccUpstream (148), -- DVB-RCC Upstream Channel + atmVirtual (149), -- ATM Virtual Interface + mplsTunnel (150), -- MPLS Tunnel Virtual Interface + srp (151), -- Spatial Reuse Protocol + voiceOverAtm (152), -- Voice Over ATM + voiceOverFrameRelay (153), -- Voice Over Frame Relay + idsl (154), -- Digital Subscriber Loop over ISDN + compositeLink (155), -- Avici Composite Link Interface + ss7SigLink (156), -- SS7 Signaling Link + propWirelessP2P (157), -- Prop. P2P wireless interface + frForward (158), -- Frame Forward Interface + rfc1483 (159), -- Multiprotocol over ATM AAL5 + usb (160), -- USB Interface + ieee8023adLag (161), -- IEEE 802.3ad Link Aggregate + bgppolicyaccounting (162), -- BGP Policy Accounting + frf16MfrBundle (163), -- FRF .16 Multilink Frame Relay + h323Gatekeeper (164), -- H323 Gatekeeper + h323Proxy (165), -- H323 Voice and Video Proxy + mpls (166), -- MPLS + mfSigLink (167), -- Multi-frequency signaling link + hdsl2 (168), -- High Bit-Rate DSL - 2nd generation + shdsl (169), -- Multirate HDSL2 + ds1FDL (170), -- Facility Data Link 4Kbps on a DS1 + pos (171), -- Packet over SONET/SDH Interface + dvbAsiIn (172), -- DVB-ASI Input + dvbAsiOut (173), -- DVB-ASI Output + plc (174), -- Power Line Communtications + nfas (175), -- Non Facility Associated Signaling + tr008 (176), -- TR008 + gr303RDT (177), -- Remote Digital Terminal + gr303IDT (178), -- Integrated Digital Terminal + isup (179), -- ISUP + propDocsWirelessMaclayer (180), -- Cisco proprietary Maclayer + propDocsWirelessDownstream (181), -- Cisco proprietary Downstream + propDocsWirelessUpstream (182), -- Cisco proprietary Upstream + hiperlan2 (183), -- HIPERLAN Type 2 Radio Interface + propBWAp2Mp (184), -- PropBroadbandWirelessAccesspt2multipt + -- use of this iftype for IEEE 802.16 WMAN + -- interfaces as per IEEE Std 802.16f is + -- deprecated and ifType 237 should be used instead. + sonetOverheadChannel (185), -- SONET Overhead Channel + digitalWrapperOverheadChannel (186), -- Digital Wrapper + aal2 (187), -- ATM adaptation layer 2 + radioMAC (188), -- MAC layer over radio links + atmRadio (189), -- ATM over radio links + imt (190), -- Inter Machine Trunks + mvl (191), -- Multiple Virtual Lines DSL + reachDSL (192), -- Long Reach DSL + frDlciEndPt (193), -- Frame Relay DLCI End Point + atmVciEndPt (194), -- ATM VCI End Point + opticalChannel (195), -- Optical Channel + opticalTransport (196), -- Optical Transport + propAtm (197), -- Proprietary ATM + voiceOverCable (198), -- Voice Over Cable Interface + infiniband (199), -- Infiniband + teLink (200), -- TE Link + q2931 (201), -- Q.2931 + virtualTg (202), -- Virtual Trunk Group + sipTg (203), -- SIP Trunk Group + sipSig (204), -- SIP Signaling + docsCableUpstreamChannel (205), -- CATV Upstream Channel + econet (206), -- Acorn Econet + pon155 (207), -- FSAN 155Mb Symetrical PON interface + pon622 (208), -- FSAN622Mb Symetrical PON interface + bridge (209), -- Transparent bridge interface + linegroup (210), -- Interface common to multiple lines + voiceEMFGD (211), -- voice E&M Feature Group D + voiceFGDEANA (212), -- voice FGD Exchange Access North American + voiceDID (213), -- voice Direct Inward Dialing + mpegTransport (214), -- MPEG transport interface + sixToFour (215), -- 6to4 interface (DEPRECATED) + gtp (216), -- GTP (GPRS Tunneling Protocol) + pdnEtherLoop1 (217), -- Paradyne EtherLoop 1 + pdnEtherLoop2 (218), -- Paradyne EtherLoop 2 + opticalChannelGroup (219), -- Optical Channel Group + homepna (220), -- HomePNA ITU-T G.989 + gfp (221), -- Generic Framing Procedure (GFP) + ciscoISLvlan (222), -- Layer 2 Virtual LAN using Cisco ISL + actelisMetaLOOP (223), -- Acteleis proprietary MetaLOOP High Speed Link + fcipLink (224), -- FCIP Link + rpr (225), -- Resilient Packet Ring Interface Type + qam (226), -- RF Qam Interface + lmp (227), -- Link Management Protocol + cblVectaStar (228), -- Cambridge Broadband Networks Limited VectaStar + docsCableMCmtsDownstream (229), -- CATV Modular CMTS Downstream Interface + adsl2 (230), -- Asymmetric Digital Subscriber Loop Version 2 + -- (DEPRECATED/OBSOLETED - please use adsl2plus 238 instead) + macSecControlledIF (231), -- MACSecControlled + macSecUncontrolledIF (232), -- MACSecUncontrolled + aviciOpticalEther (233), -- Avici Optical Ethernet Aggregate + atmbond (234), -- atmbond + voiceFGDOS (235), -- voice FGD Operator Services + mocaVersion1 (236), -- MultiMedia over Coax Alliance (MoCA) Interface + -- as documented in information provided privately to IANA + ieee80216WMAN (237), -- IEEE 802.16 WMAN interface + adsl2plus (238), -- Asymmetric Digital Subscriber Loop Version 2, + -- Version 2 Plus and all variants + dvbRcsMacLayer (239), -- DVB-RCS MAC Layer + dvbTdm (240), -- DVB Satellite TDM + dvbRcsTdma (241), -- DVB-RCS TDMA + x86Laps (242), -- LAPS based on ITU-T X.86/Y.1323 + wwanPP (243), -- 3GPP WWAN + wwanPP2 (244) -- 3GPP2 WWAN + } + +IANAtunnelType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The encapsulation method used by a tunnel. The value + direct indicates that a packet is encapsulated + directly within a normal IP header, with no + intermediate header, and unicast to the remote tunnel + endpoint (e.g., an RFC 2003 IP-in-IP tunnel, or an RFC + 1933 IPv6-in-IPv4 tunnel). The value minimal indicates + that a Minimal Forwarding Header (RFC 2004) is + inserted between the outer header and the payload + packet. The value UDP indicates that the payload + packet is encapsulated within a normal UDP packet + (e.g., RFC 1234). + + The values sixToFour, sixOverFour, and isatap + indicates that an IPv6 packet is encapsulated directly + within an IPv4 header, with no intermediate header, + and unicast to the destination determined by the 6to4, + 6over4, or ISATAP protocol. + + The remaining protocol-specific values indicate that a + header of the protocol of that name is inserted + between the outer header and the payload header. + + The assignment policy for IANAtunnelType values is + identical to the policy for assigning IANAifType + values." + SYNTAX INTEGER { + other(1), -- none of the following + direct(2), -- no intermediate header + gre(3), -- GRE encapsulation + minimal(4), -- Minimal encapsulation + l2tp(5), -- L2TP encapsulation + pptp(6), -- PPTP encapsulation + l2f(7), -- L2F encapsulation + udp(8), -- UDP encapsulation + atmp(9), -- ATMP encapsulation + msdp(10), -- MSDP encapsulation + sixToFour(11), -- 6to4 encapsulation + sixOverFour(12), -- 6over4 encapsulation + isatap(13), -- ISATAP encapsulation + teredo(14) -- Teredo encapsulation + } + + END + + + + + + + + + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IF-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IF-MIB new file mode 100644 index 0000000..6723ab0 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IF-MIB @@ -0,0 +1,1899 @@ +IF-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Counter32, Gauge32, Counter64, + Integer32, TimeTicks, mib-2, + NOTIFICATION-TYPE FROM SNMPv2-SMI + TEXTUAL-CONVENTION, DisplayString, + PhysAddress, TruthValue, RowStatus, + TimeStamp, AutonomousType, TestAndIncr FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP, + NOTIFICATION-GROUP FROM SNMPv2-CONF + snmpTraps FROM SNMPv2-MIB + IANAifType FROM IANAifType-MIB; + + +ifMIB MODULE-IDENTITY + LAST-UPDATED "200006140000Z" + ORGANIZATION "IETF Interfaces MIB Working Group" + CONTACT-INFO + " Keith McCloghrie + Cisco Systems, Inc. + 170 West Tasman Drive + San Jose, CA 95134-1706 + US + + 408-526-5260 + kzm@cisco.com" + DESCRIPTION + "The MIB module to describe generic objects for network + interface sub-layers. This MIB is an updated version of + MIB-II's ifTable, and incorporates the extensions defined in + RFC 1229." + + + REVISION "200006140000Z" + DESCRIPTION + "Clarifications agreed upon by the Interfaces MIB WG, and + published as RFC 2863." + REVISION "199602282155Z" + DESCRIPTION + "Revisions made by the Interfaces MIB WG, and published in + RFC 2233." + REVISION "199311082155Z" + DESCRIPTION + "Initial revision, published as part of RFC 1573." + ::= { mib-2 31 } + + +ifMIBObjects OBJECT IDENTIFIER ::= { ifMIB 1 } + +interfaces OBJECT IDENTIFIER ::= { mib-2 2 } + +-- +-- Textual Conventions +-- + + +-- OwnerString has the same semantics as used in RFC 1271 + +OwnerString ::= TEXTUAL-CONVENTION + DISPLAY-HINT "255a" + STATUS deprecated + DESCRIPTION + "This data type is used to model an administratively + assigned name of the owner of a resource. This information + is taken from the NVT ASCII character set. It is suggested + that this name contain one or more of the following: ASCII + form of the manager station's transport address, management + station name (e.g., domain name), network management + personnel's name, location, or phone number. In some cases + the agent itself will be the owner of an entry. In these + cases, this string shall be set to a string starting with + 'agent'." + SYNTAX OCTET STRING (SIZE(0..255)) + +-- InterfaceIndex contains the semantics of ifIndex and should be used +-- for any objects defined in other MIB modules that need these semantics. + +InterfaceIndex ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + + + "A unique value, greater than zero, for each interface or + interface sub-layer in the managed system. It is + recommended that values are assigned contiguously starting + from 1. The value for each interface sub-layer must remain + constant at least from one re-initialization of the entity's + network management system to the next re-initialization." + SYNTAX Integer32 (1..2147483647) + +InterfaceIndexOrZero ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "This textual convention is an extension of the + InterfaceIndex convention. The latter defines a greater + than zero value used to identify an interface or interface + sub-layer in the managed system. This extension permits the + additional value of zero. the value zero is object-specific + and must therefore be defined as part of the description of + any object which uses this syntax. Examples of the usage of + zero might include situations where interface was unknown, + or when none or all interfaces need to be referenced." + SYNTAX Integer32 (0..2147483647) + +ifNumber OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of network interfaces (regardless of their + current state) present on this system." + ::= { interfaces 1 } + +ifTableLastChange OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the time of the last creation or + deletion of an entry in the ifTable. If the number of + entries has been unchanged since the last re-initialization + of the local network management subsystem, then this object + contains a zero value." + ::= { ifMIBObjects 5 } + + +-- the Interfaces table + +-- The Interfaces table contains information on the entity's + + +-- interfaces. Each sub-layer below the internetwork-layer +-- of a network interface is considered to be an interface. + +ifTable OBJECT-TYPE + SYNTAX SEQUENCE OF IfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of interface entries. The number of entries is + given by the value of ifNumber." + ::= { interfaces 2 } + +ifEntry OBJECT-TYPE + SYNTAX IfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry containing management information applicable to a + particular interface." + INDEX { ifIndex } + ::= { ifTable 1 } + +IfEntry ::= + SEQUENCE { + ifIndex InterfaceIndex, + ifDescr DisplayString, + ifType IANAifType, + ifMtu Integer32, + ifSpeed Gauge32, + ifPhysAddress PhysAddress, + ifAdminStatus INTEGER, + ifOperStatus INTEGER, + ifLastChange TimeTicks, + ifInOctets Counter32, + ifInUcastPkts Counter32, + ifInNUcastPkts Counter32, -- deprecated + ifInDiscards Counter32, + ifInErrors Counter32, + ifInUnknownProtos Counter32, + ifOutOctets Counter32, + ifOutUcastPkts Counter32, + ifOutNUcastPkts Counter32, -- deprecated + ifOutDiscards Counter32, + ifOutErrors Counter32, + ifOutQLen Gauge32, -- deprecated + ifSpecific OBJECT IDENTIFIER -- deprecated + } + + + +ifIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A unique value, greater than zero, for each interface. It + is recommended that values are assigned contiguously + starting from 1. The value for each interface sub-layer + must remain constant at least from one re-initialization of + the entity's network management system to the next re- + initialization." + ::= { ifEntry 1 } + +ifDescr OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A textual string containing information about the + interface. This string should include the name of the + manufacturer, the product name and the version of the + interface hardware/software." + ::= { ifEntry 2 } + +ifType OBJECT-TYPE + SYNTAX IANAifType + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The type of interface. Additional values for ifType are + assigned by the Internet Assigned Numbers Authority (IANA), + through updating the syntax of the IANAifType textual + convention." + ::= { ifEntry 3 } + +ifMtu OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The size of the largest packet which can be sent/received + on the interface, specified in octets. For interfaces that + are used for transmitting network datagrams, this is the + size of the largest network datagram that can be sent on the + interface." + ::= { ifEntry 4 } + +ifSpeed OBJECT-TYPE + + + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An estimate of the interface's current bandwidth in bits + per second. For interfaces which do not vary in bandwidth + or for those where no accurate estimation can be made, this + object should contain the nominal bandwidth. If the + bandwidth of the interface is greater than the maximum value + reportable by this object then this object should report its + maximum value (4,294,967,295) and ifHighSpeed must be used + to report the interace's speed. For a sub-layer which has + no concept of bandwidth, this object should be zero." + ::= { ifEntry 5 } + +ifPhysAddress OBJECT-TYPE + SYNTAX PhysAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a MAC address. The interface's media-specific MIB + must define the bit and byte ordering and the format of the + value of this object. For interfaces which do not have such + an address (e.g., a serial line), this object should contain + an octet string of zero length." + ::= { ifEntry 6 } + +ifAdminStatus OBJECT-TYPE + SYNTAX INTEGER { + up(1), -- ready to pass packets + down(2), + testing(3) -- in some test mode + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The desired state of the interface. The testing(3) state + indicates that no operational packets can be passed. When a + managed system initializes, all interfaces start with + ifAdminStatus in the down(2) state. As a result of either + explicit management action or per configuration information + retained by the managed system, ifAdminStatus is then + changed to either the up(1) or testing(3) states (or remains + in the down(2) state)." + ::= { ifEntry 7 } + + + +ifOperStatus OBJECT-TYPE + SYNTAX INTEGER { + up(1), -- ready to pass packets + down(2), + testing(3), -- in some test mode + unknown(4), -- status can not be determined + -- for some reason. + dormant(5), + notPresent(6), -- some component is missing + lowerLayerDown(7) -- down due to state of + -- lower-layer interface(s) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current operational state of the interface. The + testing(3) state indicates that no operational packets can + be passed. If ifAdminStatus is down(2) then ifOperStatus + should be down(2). If ifAdminStatus is changed to up(1) + then ifOperStatus should change to up(1) if the interface is + ready to transmit and receive network traffic; it should + change to dormant(5) if the interface is waiting for + external actions (such as a serial line waiting for an + incoming connection); it should remain in the down(2) state + if and only if there is a fault that prevents it from going + to the up(1) state; it should remain in the notPresent(6) + state if the interface has missing (typically, hardware) + components." + ::= { ifEntry 8 } + +ifLastChange OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the time the interface entered + its current operational state. If the current state was + entered prior to the last re-initialization of the local + network management subsystem, then this object contains a + zero value." + ::= { ifEntry 9 } + +ifInOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received on the interface, + + + including framing characters. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 10 } + +ifInUcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were not addressed to a multicast + or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 11 } + +ifInNUcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a multicast or + broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime. + + This object is deprecated in favour of ifInMulticastPkts and + ifInBroadcastPkts." + ::= { ifEntry 12 } + +ifInDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of inbound packets which were chosen to be + discarded even though no errors had been detected to prevent + + + their being deliverable to a higher-layer protocol. One + possible reason for discarding such a packet could be to + free up buffer space. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 13 } + +ifInErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of inbound + transmission units that contained errors preventing them + from being deliverable to a higher-layer protocol. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 14 } + +ifInUnknownProtos OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "For packet-oriented interfaces, the number of packets + received via the interface which were discarded because of + an unknown or unsupported protocol. For character-oriented + or fixed-length interfaces that support protocol + multiplexing the number of transmission units received via + the interface which were discarded because of an unknown or + unsupported protocol. For any interface that does not + support protocol multiplexing, this counter will always be + 0. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 15 } + + +ifOutOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 16 } + +ifOutUcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets that higher-level protocols + requested be transmitted, and which were not addressed to a + multicast or broadcast address at this sub-layer, including + those that were discarded or not sent. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 17 } + +ifOutNUcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + multicast or broadcast address at this sub-layer, including + those that were discarded or not sent. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime. + + This object is deprecated in favour of ifOutMulticastPkts + and ifOutBroadcastPkts." + ::= { ifEntry 18 } + + +ifOutDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outbound packets which were chosen to be + discarded even though no errors had been detected to prevent + their being transmitted. One possible reason for discarding + such a packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 19 } + +ifOutErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifEntry 20 } + +ifOutQLen OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The length of the output packet queue (in packets)." + ::= { ifEntry 21 } + +ifSpecific OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "A reference to MIB definitions specific to the particular + media being used to realize the interface. It is + + + recommended that this value point to an instance of a MIB + object in the media-specific MIB, i.e., that this object + have the semantics associated with the InstancePointer + textual convention defined in RFC 2579. In fact, it is + recommended that the media-specific MIB specify what value + ifSpecific should/can take for values of ifType. If no MIB + definitions specific to the particular media are available, + the value should be set to the OBJECT IDENTIFIER { 0 0 }." + ::= { ifEntry 22 } + + + +-- +-- Extension to the interface table +-- +-- This table replaces the ifExtnsTable table. +-- + +ifXTable OBJECT-TYPE + SYNTAX SEQUENCE OF IfXEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of interface entries. The number of entries is + given by the value of ifNumber. This table contains + additional objects for the interface table." + ::= { ifMIBObjects 1 } + +ifXEntry OBJECT-TYPE + SYNTAX IfXEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry containing additional management information + applicable to a particular interface." + AUGMENTS { ifEntry } + ::= { ifXTable 1 } + +IfXEntry ::= + SEQUENCE { + ifName DisplayString, + ifInMulticastPkts Counter32, + ifInBroadcastPkts Counter32, + ifOutMulticastPkts Counter32, + ifOutBroadcastPkts Counter32, + ifHCInOctets Counter64, + ifHCInUcastPkts Counter64, + ifHCInMulticastPkts Counter64, + + + ifHCInBroadcastPkts Counter64, + ifHCOutOctets Counter64, + ifHCOutUcastPkts Counter64, + ifHCOutMulticastPkts Counter64, + ifHCOutBroadcastPkts Counter64, + ifLinkUpDownTrapEnable INTEGER, + ifHighSpeed Gauge32, + ifPromiscuousMode TruthValue, + ifConnectorPresent TruthValue, + ifAlias DisplayString, + ifCounterDiscontinuityTime TimeStamp + } + + +ifName OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The textual name of the interface. The value of this + object should be the name of the interface as assigned by + the local device and should be suitable for use in commands + entered at the device's `console'. This might be a text + name, such as `le0' or a simple port number, such as `1', + depending on the interface naming syntax of the device. If + several entries in the ifTable together represent a single + interface as named by the device, then each will have the + same value of ifName. Note that for an agent which responds + to SNMP queries concerning an interface on some other + (proxied) device, then the value of ifName for such an + interface is the proxied device's local name for it. + + If there is no local name, or this object is otherwise not + applicable, then this object contains a zero-length string." + ::= { ifXEntry 1 } + +ifInMulticastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a multicast + address at this sub-layer. For a MAC layer protocol, this + includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + + + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 2 } + +ifInBroadcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 3 } + +ifOutMulticastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + multicast address at this sub-layer, including those that + were discarded or not sent. For a MAC layer protocol, this + includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 4 } + +ifOutBroadcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + broadcast address at this sub-layer, including those that + were discarded or not sent. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + + + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 5 } + +-- +-- High Capacity Counter objects. These objects are all +-- 64 bit versions of the "basic" ifTable counters. These +-- objects all have the same basic semantics as their 32-bit +-- counterparts, however, their syntax has been extended +-- to 64 bits. +-- + +ifHCInOctets OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received on the interface, + including framing characters. This object is a 64-bit + version of ifInOctets. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 6 } + +ifHCInUcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were not addressed to a multicast + or broadcast address at this sub-layer. This object is a + 64-bit version of ifInUcastPkts. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 7 } + +ifHCInMulticastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + + + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a multicast + address at this sub-layer. For a MAC layer protocol, this + includes both Group and Functional addresses. This object + is a 64-bit version of ifInMulticastPkts. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 8 } + +ifHCInBroadcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a broadcast + address at this sub-layer. This object is a 64-bit version + of ifInBroadcastPkts. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 9 } + +ifHCOutOctets OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets transmitted out of the + interface, including framing characters. This object is a + 64-bit version of ifOutOctets. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 10 } + +ifHCOutUcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + + + "The total number of packets that higher-level protocols + requested be transmitted, and which were not addressed to a + multicast or broadcast address at this sub-layer, including + those that were discarded or not sent. This object is a + 64-bit version of ifOutUcastPkts. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 11 } + +ifHCOutMulticastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + multicast address at this sub-layer, including those that + were discarded or not sent. For a MAC layer protocol, this + includes both Group and Functional addresses. This object + is a 64-bit version of ifOutMulticastPkts. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 12 } + +ifHCOutBroadcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + broadcast address at this sub-layer, including those that + were discarded or not sent. This object is a 64-bit version + of ifOutBroadcastPkts. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ifCounterDiscontinuityTime." + ::= { ifXEntry 13 } + +ifLinkUpDownTrapEnable OBJECT-TYPE + + + SYNTAX INTEGER { enabled(1), disabled(2) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Indicates whether linkUp/linkDown traps should be generated + for this interface. + + By default, this object should have the value enabled(1) for + interfaces which do not operate on 'top' of any other + interface (as defined in the ifStackTable), and disabled(2) + otherwise." + ::= { ifXEntry 14 } + +ifHighSpeed OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An estimate of the interface's current bandwidth in units + of 1,000,000 bits per second. If this object reports a + value of `n' then the speed of the interface is somewhere in + the range of `n-500,000' to `n+499,999'. For interfaces + which do not vary in bandwidth or for those where no + accurate estimation can be made, this object should contain + the nominal bandwidth. For a sub-layer which has no concept + of bandwidth, this object should be zero." + ::= { ifXEntry 15 } + +ifPromiscuousMode OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object has a value of false(2) if this interface only + accepts packets/frames that are addressed to this station. + This object has a value of true(1) when the station accepts + all packets/frames transmitted on the media. The value + true(1) is only legal on certain types of media. If legal, + setting this object to a value of true(1) may require the + interface to be reset before becoming effective. + + The value of ifPromiscuousMode does not affect the reception + of broadcast and multicast packets/frames by the interface." + ::= { ifXEntry 16 } + +ifConnectorPresent OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + + + STATUS current + DESCRIPTION + "This object has the value 'true(1)' if the interface + sublayer has a physical connector and the value 'false(2)' + otherwise." + ::= { ifXEntry 17 } + +ifAlias OBJECT-TYPE + SYNTAX DisplayString (SIZE(0..64)) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object is an 'alias' name for the interface as + specified by a network manager, and provides a non-volatile + 'handle' for the interface. + + On the first instantiation of an interface, the value of + ifAlias associated with that interface is the zero-length + string. As and when a value is written into an instance of + ifAlias through a network management set operation, then the + agent must retain the supplied value in the ifAlias instance + associated with the same interface for as long as that + interface remains instantiated, including across all re- + initializations/reboots of the network management system, + including those which result in a change of the interface's + ifIndex value. + + An example of the value which a network manager might store + in this object for a WAN interface is the (Telco's) circuit + number/identifier of the interface. + + Some agents may support write-access only for interfaces + having particular values of ifType. An agent which supports + write access to this object is required to keep the value in + non-volatile storage, but it may limit the length of new + values depending on how much storage is already occupied by + the current values for other interfaces." + ::= { ifXEntry 18 } + +ifCounterDiscontinuityTime OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime on the most recent occasion at which + any one or more of this interface's counters suffered a + discontinuity. The relevant counters are the specific + instances associated with this interface of any Counter32 or + + + Counter64 object contained in the ifTable or ifXTable. If + no such discontinuities have occurred since the last re- + initialization of the local management subsystem, then this + object contains a zero value." + ::= { ifXEntry 19 } + +-- The Interface Stack Group +-- +-- Implementation of this group is optional, but strongly recommended +-- for all systems +-- + +ifStackTable OBJECT-TYPE + SYNTAX SEQUENCE OF IfStackEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table containing information on the relationships + between the multiple sub-layers of network interfaces. In + particular, it contains information on which sub-layers run + 'on top of' which other sub-layers, where each sub-layer + corresponds to a conceptual row in the ifTable. For + example, when the sub-layer with ifIndex value x runs over + the sub-layer with ifIndex value y, then this table + contains: + + ifStackStatus.x.y=active + + For each ifIndex value, I, which identifies an active + interface, there are always at least two instantiated rows + in this table associated with I. For one of these rows, I + is the value of ifStackHigherLayer; for the other, I is the + value of ifStackLowerLayer. (If I is not involved in + multiplexing, then these are the only two rows associated + with I.) + + For example, two rows exist even for an interface which has + no others stacked on top or below it: + + ifStackStatus.0.x=active + ifStackStatus.x.0=active " + ::= { ifMIBObjects 2 } + + +ifStackEntry OBJECT-TYPE + SYNTAX IfStackEntry + MAX-ACCESS not-accessible + STATUS current + + + DESCRIPTION + "Information on a particular relationship between two sub- + layers, specifying that one sub-layer runs on 'top' of the + other sub-layer. Each sub-layer corresponds to a conceptual + row in the ifTable." + INDEX { ifStackHigherLayer, ifStackLowerLayer } + ::= { ifStackTable 1 } + + +IfStackEntry ::= + SEQUENCE { + ifStackHigherLayer InterfaceIndexOrZero, + ifStackLowerLayer InterfaceIndexOrZero, + ifStackStatus RowStatus + } + + +ifStackHigherLayer OBJECT-TYPE + SYNTAX InterfaceIndexOrZero + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The value of ifIndex corresponding to the higher sub-layer + of the relationship, i.e., the sub-layer which runs on 'top' + of the sub-layer identified by the corresponding instance of + ifStackLowerLayer. If there is no higher sub-layer (below + the internetwork layer), then this object has the value 0." + ::= { ifStackEntry 1 } + + +ifStackLowerLayer OBJECT-TYPE + SYNTAX InterfaceIndexOrZero + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The value of ifIndex corresponding to the lower sub-layer + of the relationship, i.e., the sub-layer which runs 'below' + the sub-layer identified by the corresponding instance of + ifStackHigherLayer. If there is no lower sub-layer, then + this object has the value 0." + ::= { ifStackEntry 2 } + + +ifStackStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + + + "The status of the relationship between two sub-layers. + + Changing the value of this object from 'active' to + 'notInService' or 'destroy' will likely have consequences up + and down the interface stack. Thus, write access to this + object is likely to be inappropriate for some types of + interfaces, and many implementations will choose not to + support write-access for any type of interface." + ::= { ifStackEntry 3 } + +ifStackLastChange OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the time of the last change of + the (whole) interface stack. A change of the interface + stack is defined to be any creation, deletion, or change in + value of any instance of ifStackStatus. If the interface + stack has been unchanged since the last re-initialization of + the local network management subsystem, then this object + contains a zero value." + ::= { ifMIBObjects 6 } + + +-- Generic Receive Address Table +-- +-- This group of objects is mandatory for all types of +-- interfaces which can receive packets/frames addressed to +-- more than one address. +-- +-- This table replaces the ifExtnsRcvAddr table. The main +-- difference is that this table makes use of the RowStatus +-- textual convention, while ifExtnsRcvAddr did not. + +ifRcvAddressTable OBJECT-TYPE + SYNTAX SEQUENCE OF IfRcvAddressEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "This table contains an entry for each address (broadcast, + multicast, or uni-cast) for which the system will receive + packets/frames on a particular interface, except as follows: + + - for an interface operating in promiscuous mode, entries + are only required for those addresses for which the system + would receive frames were it not operating in promiscuous + mode. + + + - for 802.5 functional addresses, only one entry is + required, for the address which has the functional address + bit ANDed with the bit mask of all functional addresses for + which the interface will accept frames. + + A system is normally able to use any unicast address which + corresponds to an entry in this table as a source address." + ::= { ifMIBObjects 4 } + +ifRcvAddressEntry OBJECT-TYPE + SYNTAX IfRcvAddressEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of objects identifying an address for which the + system will accept packets/frames on the particular + interface identified by the index value ifIndex." + INDEX { ifIndex, ifRcvAddressAddress } + ::= { ifRcvAddressTable 1 } + +IfRcvAddressEntry ::= + SEQUENCE { + ifRcvAddressAddress PhysAddress, + ifRcvAddressStatus RowStatus, + ifRcvAddressType INTEGER + } + +ifRcvAddressAddress OBJECT-TYPE + SYNTAX PhysAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An address for which the system will accept packets/frames + on this entry's interface." + ::= { ifRcvAddressEntry 1 } + +ifRcvAddressStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object is used to create and delete rows in the + ifRcvAddressTable." + + ::= { ifRcvAddressEntry 2 } + +ifRcvAddressType OBJECT-TYPE + SYNTAX INTEGER { + + + other(1), + volatile(2), + nonVolatile(3) + } + + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object has the value nonVolatile(3) for those entries + in the table which are valid and will not be deleted by the + next restart of the managed system. Entries having the + value volatile(2) are valid and exist, but have not been + saved, so that will not exist after the next restart of the + managed system. Entries having the value other(1) are valid + and exist but are not classified as to whether they will + continue to exist after the next restart." + + DEFVAL { volatile } + ::= { ifRcvAddressEntry 3 } + +-- definition of interface-related traps. + +linkDown NOTIFICATION-TYPE + OBJECTS { ifIndex, ifAdminStatus, ifOperStatus } + STATUS current + DESCRIPTION + "A linkDown trap signifies that the SNMP entity, acting in + an agent role, has detected that the ifOperStatus object for + one of its communication links is about to enter the down + state from some other state (but not from the notPresent + state). This other state is indicated by the included value + of ifOperStatus." + ::= { snmpTraps 3 } + +linkUp NOTIFICATION-TYPE + OBJECTS { ifIndex, ifAdminStatus, ifOperStatus } + STATUS current + DESCRIPTION + "A linkUp trap signifies that the SNMP entity, acting in an + agent role, has detected that the ifOperStatus object for + one of its communication links left the down state and + transitioned into some other state (but not into the + notPresent state). This other state is indicated by the + included value of ifOperStatus." + ::= { snmpTraps 4 } + +-- conformance information + + + +ifConformance OBJECT IDENTIFIER ::= { ifMIB 2 } + +ifGroups OBJECT IDENTIFIER ::= { ifConformance 1 } +ifCompliances OBJECT IDENTIFIER ::= { ifConformance 2 } + + +-- compliance statements + +ifCompliance3 MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for SNMP entities which have + network interfaces." + + MODULE -- this module + MANDATORY-GROUPS { ifGeneralInformationGroup, + linkUpDownNotificationsGroup } + +-- The groups: +-- ifFixedLengthGroup +-- ifHCFixedLengthGroup +-- ifPacketGroup +-- ifHCPacketGroup +-- ifVHCPacketGroup +-- are mutually exclusive; at most one of these groups is implemented +-- for a particular interface. When any of these groups is implemented +-- for a particular interface, then ifCounterDiscontinuityGroup must +-- also be implemented for that interface. + + + GROUP ifFixedLengthGroup + DESCRIPTION + "This group is mandatory for those network interfaces which + are character-oriented or transmit data in fixed-length + transmission units, and for which the value of the + corresponding instance of ifSpeed is less than or equal to + 20,000,000 bits/second." + + GROUP ifHCFixedLengthGroup + DESCRIPTION + "This group is mandatory for those network interfaces which + are character-oriented or transmit data in fixed-length + transmission units, and for which the value of the + corresponding instance of ifSpeed is greater than 20,000,000 + bits/second." + + GROUP ifPacketGroup + DESCRIPTION + + + "This group is mandatory for those network interfaces which + are packet-oriented, and for which the value of the + corresponding instance of ifSpeed is less than or equal to + 20,000,000 bits/second." + + GROUP ifHCPacketGroup + DESCRIPTION + "This group is mandatory only for those network interfaces + which are packet-oriented and for which the value of the + corresponding instance of ifSpeed is greater than 20,000,000 + bits/second but less than or equal to 650,000,000 + bits/second." + + GROUP ifVHCPacketGroup + DESCRIPTION + "This group is mandatory only for those network interfaces + which are packet-oriented and for which the value of the + corresponding instance of ifSpeed is greater than + 650,000,000 bits/second." + + + GROUP ifCounterDiscontinuityGroup + DESCRIPTION + "This group is mandatory for those network interfaces that + are required to maintain counters (i.e., those for which one + of the ifFixedLengthGroup, ifHCFixedLengthGroup, + ifPacketGroup, ifHCPacketGroup, or ifVHCPacketGroup is + mandatory)." + + + GROUP ifRcvAddressGroup + DESCRIPTION + "The applicability of this group MUST be defined by the + media-specific MIBs. Media-specific MIBs must define the + exact meaning, use, and semantics of the addresses in this + group." + + OBJECT ifLinkUpDownTrapEnable + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ifPromiscuousMode + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ifAdminStatus + + + SYNTAX INTEGER { up(1), down(2) } + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required, nor is support for the value + testing(3)." + + OBJECT ifAlias + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + ::= { ifCompliances 3 } + +-- units of conformance + +ifGeneralInformationGroup OBJECT-GROUP + OBJECTS { ifIndex, ifDescr, ifType, ifSpeed, ifPhysAddress, + ifAdminStatus, ifOperStatus, ifLastChange, + ifLinkUpDownTrapEnable, ifConnectorPresent, + ifHighSpeed, ifName, ifNumber, ifAlias, + ifTableLastChange } + STATUS current + DESCRIPTION + "A collection of objects providing information applicable to + all network interfaces." + ::= { ifGroups 10 } + +-- the following five groups are mutually exclusive; at most +-- one of these groups is implemented for any interface + +ifFixedLengthGroup OBJECT-GROUP + OBJECTS { ifInOctets, ifOutOctets, ifInUnknownProtos, + ifInErrors, ifOutErrors } + STATUS current + DESCRIPTION + "A collection of objects providing information specific to + non-high speed (non-high speed interfaces transmit and + receive at speeds less than or equal to 20,000,000 + bits/second) character-oriented or fixed-length-transmission + network interfaces." + ::= { ifGroups 2 } + +ifHCFixedLengthGroup OBJECT-GROUP + OBJECTS { ifHCInOctets, ifHCOutOctets, + ifInOctets, ifOutOctets, ifInUnknownProtos, + ifInErrors, ifOutErrors } + STATUS current + DESCRIPTION + + + "A collection of objects providing information specific to + high speed (greater than 20,000,000 bits/second) character- + oriented or fixed-length-transmission network interfaces." + ::= { ifGroups 3 } + +ifPacketGroup OBJECT-GROUP + OBJECTS { ifInOctets, ifOutOctets, ifInUnknownProtos, + ifInErrors, ifOutErrors, + ifMtu, ifInUcastPkts, ifInMulticastPkts, + ifInBroadcastPkts, ifInDiscards, + ifOutUcastPkts, ifOutMulticastPkts, + ifOutBroadcastPkts, ifOutDiscards, + ifPromiscuousMode } + STATUS current + DESCRIPTION + "A collection of objects providing information specific to + non-high speed (non-high speed interfaces transmit and + receive at speeds less than or equal to 20,000,000 + bits/second) packet-oriented network interfaces." + ::= { ifGroups 4 } + +ifHCPacketGroup OBJECT-GROUP + OBJECTS { ifHCInOctets, ifHCOutOctets, + ifInOctets, ifOutOctets, ifInUnknownProtos, + ifInErrors, ifOutErrors, + ifMtu, ifInUcastPkts, ifInMulticastPkts, + ifInBroadcastPkts, ifInDiscards, + ifOutUcastPkts, ifOutMulticastPkts, + ifOutBroadcastPkts, ifOutDiscards, + ifPromiscuousMode } + STATUS current + DESCRIPTION + "A collection of objects providing information specific to + high speed (greater than 20,000,000 bits/second but less + than or equal to 650,000,000 bits/second) packet-oriented + network interfaces." + ::= { ifGroups 5 } + +ifVHCPacketGroup OBJECT-GROUP + OBJECTS { ifHCInUcastPkts, ifHCInMulticastPkts, + ifHCInBroadcastPkts, ifHCOutUcastPkts, + ifHCOutMulticastPkts, ifHCOutBroadcastPkts, + ifHCInOctets, ifHCOutOctets, + ifInOctets, ifOutOctets, ifInUnknownProtos, + ifInErrors, ifOutErrors, + ifMtu, ifInUcastPkts, ifInMulticastPkts, + ifInBroadcastPkts, ifInDiscards, + ifOutUcastPkts, ifOutMulticastPkts, + + + ifOutBroadcastPkts, ifOutDiscards, + ifPromiscuousMode } + STATUS current + DESCRIPTION + "A collection of objects providing information specific to + higher speed (greater than 650,000,000 bits/second) packet- + oriented network interfaces." + ::= { ifGroups 6 } + +ifRcvAddressGroup OBJECT-GROUP + OBJECTS { ifRcvAddressStatus, ifRcvAddressType } + STATUS current + DESCRIPTION + "A collection of objects providing information on the + multiple addresses which an interface receives." + ::= { ifGroups 7 } + +ifStackGroup2 OBJECT-GROUP + OBJECTS { ifStackStatus, ifStackLastChange } + STATUS current + DESCRIPTION + "A collection of objects providing information on the + layering of MIB-II interfaces." + ::= { ifGroups 11 } + +ifCounterDiscontinuityGroup OBJECT-GROUP + OBJECTS { ifCounterDiscontinuityTime } + STATUS current + DESCRIPTION + "A collection of objects providing information specific to + interface counter discontinuities." + ::= { ifGroups 13 } + +linkUpDownNotificationsGroup NOTIFICATION-GROUP + NOTIFICATIONS { linkUp, linkDown } + STATUS current + DESCRIPTION + "The notifications which indicate specific changes in the + value of ifOperStatus." + ::= { ifGroups 14 } + +-- Deprecated Definitions - Objects + + +-- +-- The Interface Test Table +-- +-- This group of objects is optional. However, a media-specific + + +-- MIB may make implementation of this group mandatory. +-- +-- This table replaces the ifExtnsTestTable +-- + +ifTestTable OBJECT-TYPE + SYNTAX SEQUENCE OF IfTestEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "This table contains one entry per interface. It defines + objects which allow a network manager to instruct an agent + to test an interface for various faults. Tests for an + interface are defined in the media-specific MIB for that + interface. After invoking a test, the object ifTestResult + can be read to determine the outcome. If an agent can not + perform the test, ifTestResult is set to so indicate. The + object ifTestCode can be used to provide further test- + specific or interface-specific (or even enterprise-specific) + information concerning the outcome of the test. Only one + test can be in progress on each interface at any one time. + If one test is in progress when another test is invoked, the + second test is rejected. Some agents may reject a test when + a prior test is active on another interface. + + Before starting a test, a manager-station must first obtain + 'ownership' of the entry in the ifTestTable for the + interface to be tested. This is accomplished with the + ifTestId and ifTestStatus objects as follows: + + try_again: + get (ifTestId, ifTestStatus) + while (ifTestStatus != notInUse) + /* + * Loop while a test is running or some other + * manager is configuring a test. + */ + short delay + get (ifTestId, ifTestStatus) + } + + /* + * Is not being used right now -- let's compete + * to see who gets it. + */ + lock_value = ifTestId + + if ( set(ifTestId = lock_value, ifTestStatus = inUse, + + + ifTestOwner = 'my-IP-address') == FAILURE) + /* + * Another manager got the ifTestEntry -- go + * try again + */ + goto try_again; + + /* + * I have the lock + */ + set up any test parameters. + + /* + * This starts the test + */ + set(ifTestType = test_to_run); + + wait for test completion by polling ifTestResult + + when test completes, agent sets ifTestResult + agent also sets ifTestStatus = 'notInUse' + + retrieve any additional test results, and ifTestId + + if (ifTestId == lock_value+1) results are valid + + A manager station first retrieves the value of the + appropriate ifTestId and ifTestStatus objects, periodically + repeating the retrieval if necessary, until the value of + ifTestStatus is 'notInUse'. The manager station then tries + to set the same ifTestId object to the value it just + retrieved, the same ifTestStatus object to 'inUse', and the + corresponding ifTestOwner object to a value indicating + itself. If the set operation succeeds then the manager has + obtained ownership of the ifTestEntry, and the value of the + ifTestId object is incremented by the agent (per the + semantics of TestAndIncr). Failure of the set operation + indicates that some other manager has obtained ownership of + the ifTestEntry. + + Once ownership is obtained, any test parameters can be + setup, and then the test is initiated by setting ifTestType. + On completion of the test, the agent sets ifTestStatus to + 'notInUse'. Once this occurs, the manager can retrieve the + results. In the (rare) event that the invocation of tests + by two network managers were to overlap, then there would be + a possibility that the first test's results might be + overwritten by the second test's results prior to the first + + + results being read. This unlikely circumstance can be + detected by a network manager retrieving ifTestId at the + same time as retrieving the test results, and ensuring that + the results are for the desired request. + + If ifTestType is not set within an abnormally long period of + time after ownership is obtained, the agent should time-out + the manager, and reset the value of the ifTestStatus object + back to 'notInUse'. It is suggested that this time-out + period be 5 minutes. + + In general, a management station must not retransmit a + request to invoke a test for which it does not receive a + response; instead, it properly inspects an agent's MIB to + determine if the invocation was successful. Only if the + invocation was unsuccessful, is the invocation request + retransmitted. + + Some tests may require the interface to be taken off-line in + order to execute them, or may even require the agent to + reboot after completion of the test. In these + circumstances, communication with the management station + invoking the test may be lost until after completion of the + test. An agent is not required to support such tests. + However, if such tests are supported, then the agent should + make every effort to transmit a response to the request + which invoked the test prior to losing communication. When + the agent is restored to normal service, the results of the + test are properly made available in the appropriate objects. + Note that this requires that the ifIndex value assigned to + an interface must be unchanged even if the test causes a + reboot. An agent must reject any test for which it cannot, + perhaps due to resource constraints, make available at least + the minimum amount of information after that test + completes." + ::= { ifMIBObjects 3 } + +ifTestEntry OBJECT-TYPE + SYNTAX IfTestEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "An entry containing objects for invoking tests on an + interface." + AUGMENTS { ifEntry } + ::= { ifTestTable 1 } + +IfTestEntry ::= + + + SEQUENCE { + ifTestId TestAndIncr, + ifTestStatus INTEGER, + ifTestType AutonomousType, + ifTestResult INTEGER, + ifTestCode OBJECT IDENTIFIER, + ifTestOwner OwnerString + } + +ifTestId OBJECT-TYPE + SYNTAX TestAndIncr + MAX-ACCESS read-write + STATUS deprecated + DESCRIPTION + "This object identifies the current invocation of the + interface's test." + ::= { ifTestEntry 1 } + +ifTestStatus OBJECT-TYPE + SYNTAX INTEGER { notInUse(1), inUse(2) } + MAX-ACCESS read-write + STATUS deprecated + DESCRIPTION + "This object indicates whether or not some manager currently + has the necessary 'ownership' required to invoke a test on + this interface. A write to this object is only successful + when it changes its value from 'notInUse(1)' to 'inUse(2)'. + After completion of a test, the agent resets the value back + to 'notInUse(1)'." + ::= { ifTestEntry 2 } + +ifTestType OBJECT-TYPE + SYNTAX AutonomousType + MAX-ACCESS read-write + STATUS deprecated + DESCRIPTION + "A control variable used to start and stop operator- + initiated interface tests. Most OBJECT IDENTIFIER values + assigned to tests are defined elsewhere, in association with + specific types of interface. However, this document assigns + a value for a full-duplex loopback test, and defines the + special meanings of the subject identifier: + + noTest OBJECT IDENTIFIER ::= { 0 0 } + + When the value noTest is written to this object, no action + is taken unless a test is in progress, in which case the + test is aborted. Writing any other value to this object is + + + only valid when no test is currently in progress, in which + case the indicated test is initiated. + + When read, this object always returns the most recent value + that ifTestType was set to. If it has not been set since + the last initialization of the network management subsystem + on the agent, a value of noTest is returned." + ::= { ifTestEntry 3 } + +ifTestResult OBJECT-TYPE + SYNTAX INTEGER { + none(1), -- no test yet requested + success(2), + inProgress(3), + notSupported(4), + unAbleToRun(5), -- due to state of system + aborted(6), + failed(7) + } + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "This object contains the result of the most recently + requested test, or the value none(1) if no tests have been + requested since the last reset. Note that this facility + provides no provision for saving the results of one test + when starting another, as could be required if used by + multiple managers concurrently." + ::= { ifTestEntry 4 } + +ifTestCode OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "This object contains a code which contains more specific + information on the test result, for example an error-code + after a failed test. Error codes and other values this + object may take are specific to the type of interface and/or + test. The value may have the semantics of either the + AutonomousType or InstancePointer textual conventions as + defined in RFC 2579. The identifier: + + testCodeUnknown OBJECT IDENTIFIER ::= { 0 0 } + + is defined for use if no additional result code is + available." + ::= { ifTestEntry 5 } + + +ifTestOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-write + STATUS deprecated + DESCRIPTION + "The entity which currently has the 'ownership' required to + invoke a test on this interface." + ::= { ifTestEntry 6 } + +-- Deprecated Definitions - Groups + + +ifGeneralGroup OBJECT-GROUP + OBJECTS { ifDescr, ifType, ifSpeed, ifPhysAddress, + ifAdminStatus, ifOperStatus, ifLastChange, + ifLinkUpDownTrapEnable, ifConnectorPresent, + ifHighSpeed, ifName } + STATUS deprecated + DESCRIPTION + "A collection of objects deprecated in favour of + ifGeneralInformationGroup." + ::= { ifGroups 1 } + + +ifTestGroup OBJECT-GROUP + OBJECTS { ifTestId, ifTestStatus, ifTestType, + ifTestResult, ifTestCode, ifTestOwner } + STATUS deprecated + DESCRIPTION + "A collection of objects providing the ability to invoke + tests on an interface." + ::= { ifGroups 8 } + + +ifStackGroup OBJECT-GROUP + OBJECTS { ifStackStatus } + STATUS deprecated + DESCRIPTION + "The previous collection of objects providing information on + the layering of MIB-II interfaces." + ::= { ifGroups 9 } + + +ifOldObjectsGroup OBJECT-GROUP + OBJECTS { ifInNUcastPkts, ifOutNUcastPkts, + ifOutQLen, ifSpecific } + STATUS deprecated + DESCRIPTION + + + "The collection of objects deprecated from the original MIB- + II interfaces group." + ::= { ifGroups 12 } + +-- Deprecated Definitions - Compliance + +ifCompliance MODULE-COMPLIANCE + STATUS deprecated + DESCRIPTION + "A compliance statement defined in a previous version of + this MIB module, for SNMP entities which have network + interfaces." + + MODULE -- this module + MANDATORY-GROUPS { ifGeneralGroup, ifStackGroup } + + GROUP ifFixedLengthGroup + DESCRIPTION + "This group is mandatory for all network interfaces which + are character-oriented or transmit data in fixed-length + transmission units." + + GROUP ifHCFixedLengthGroup + DESCRIPTION + "This group is mandatory only for those network interfaces + which are character-oriented or transmit data in fixed- + length transmission units, and for which the value of the + corresponding instance of ifSpeed is greater than 20,000,000 + bits/second." + + GROUP ifPacketGroup + DESCRIPTION + "This group is mandatory for all network interfaces which + are packet-oriented." + + GROUP ifHCPacketGroup + DESCRIPTION + "This group is mandatory only for those network interfaces + which are packet-oriented and for which the value of the + corresponding instance of ifSpeed is greater than + 650,000,000 bits/second." + + GROUP ifTestGroup + DESCRIPTION + "This group is optional. Media-specific MIBs which require + interface tests are strongly encouraged to use this group + for invoking tests and reporting results. A medium specific + MIB which has mandatory tests may make implementation of + + + this group mandatory." + + GROUP ifRcvAddressGroup + DESCRIPTION + "The applicability of this group MUST be defined by the + media-specific MIBs. Media-specific MIBs must define the + exact meaning, use, and semantics of the addresses in this + group." + + OBJECT ifLinkUpDownTrapEnable + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ifPromiscuousMode + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ifStackStatus + SYNTAX INTEGER { active(1) } -- subset of RowStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required, and only one of the six + enumerated values for the RowStatus textual convention need + be supported, specifically: active(1)." + + OBJECT ifAdminStatus + SYNTAX INTEGER { up(1), down(2) } + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required, nor is support for the value + testing(3)." + ::= { ifCompliances 1 } + +ifCompliance2 MODULE-COMPLIANCE + STATUS deprecated + DESCRIPTION + "A compliance statement defined in a previous version of + this MIB module, for SNMP entities which have network + interfaces." + + MODULE -- this module + MANDATORY-GROUPS { ifGeneralInformationGroup, ifStackGroup2, + ifCounterDiscontinuityGroup } + + GROUP ifFixedLengthGroup + DESCRIPTION + + + "This group is mandatory for all network interfaces which + are character-oriented or transmit data in fixed-length + transmission units." + + GROUP ifHCFixedLengthGroup + DESCRIPTION + "This group is mandatory only for those network interfaces + which are character-oriented or transmit data in fixed- + length transmission units, and for which the value of the + corresponding instance of ifSpeed is greater than 20,000,000 + bits/second." + + GROUP ifPacketGroup + DESCRIPTION + "This group is mandatory for all network interfaces which + are packet-oriented." + + GROUP ifHCPacketGroup + DESCRIPTION + "This group is mandatory only for those network interfaces + which are packet-oriented and for which the value of the + corresponding instance of ifSpeed is greater than + 650,000,000 bits/second." + + GROUP ifRcvAddressGroup + DESCRIPTION + "The applicability of this group MUST be defined by the + media-specific MIBs. Media-specific MIBs must define the + exact meaning, use, and semantics of the addresses in this + group." + + OBJECT ifLinkUpDownTrapEnable + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ifPromiscuousMode + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ifStackStatus + SYNTAX INTEGER { active(1) } -- subset of RowStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required, and only one of the six + enumerated values for the RowStatus textual convention need + be supported, specifically: active(1)." + + + OBJECT ifAdminStatus + SYNTAX INTEGER { up(1), down(2) } + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required, nor is support for the value + testing(3)." + + OBJECT ifAlias + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + ::= { ifCompliances 2 } + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/INET-ADDRESS-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/INET-ADDRESS-MIB new file mode 100644 index 0000000..a943c9f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/INET-ADDRESS-MIB @@ -0,0 +1,421 @@ +INET-ADDRESS-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, mib-2, Unsigned32 FROM SNMPv2-SMI + TEXTUAL-CONVENTION FROM SNMPv2-TC; + +inetAddressMIB MODULE-IDENTITY + LAST-UPDATED "200502040000Z" + ORGANIZATION + "IETF Operations and Management Area" + CONTACT-INFO + "Juergen Schoenwaelder (Editor) + International University Bremen + P.O. Box 750 561 + 28725 Bremen, Germany + + Phone: +49 421 200-3587 + EMail: j.schoenwaelder@iu-bremen.de + + Send comments to ." + DESCRIPTION + "This MIB module defines textual conventions for + representing Internet addresses. An Internet + address can be an IPv4 address, an IPv6 address, + or a DNS domain name. This module also defines + textual conventions for Internet port numbers, + autonomous system numbers, and the length of an + Internet address prefix. + + Copyright (C) The Internet Society (2005). This version + of this MIB module is part of RFC 4001, see the RFC + itself for full legal notices." + REVISION "200502040000Z" + DESCRIPTION + "Third version, published as RFC 4001. This revision + introduces the InetZoneIndex, InetScopeType, and + InetVersion textual conventions." + REVISION "200205090000Z" + DESCRIPTION + "Second version, published as RFC 3291. This + revision contains several clarifications and + introduces several new textual conventions: + InetAddressPrefixLength, InetPortNumber, + InetAutonomousSystemNumber, InetAddressIPv4z, + and InetAddressIPv6z." + REVISION "200006080000Z" + + + + DESCRIPTION + "Initial version, published as RFC 2851." + ::= { mib-2 76 } + +InetAddressType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A value that represents a type of Internet address. + + unknown(0) An unknown address type. This value MUST + be used if the value of the corresponding + InetAddress object is a zero-length string. + It may also be used to indicate an IP address + that is not in one of the formats defined + below. + + ipv4(1) An IPv4 address as defined by the + InetAddressIPv4 textual convention. + + ipv6(2) An IPv6 address as defined by the + InetAddressIPv6 textual convention. + + ipv4z(3) A non-global IPv4 address including a zone + index as defined by the InetAddressIPv4z + textual convention. + + ipv6z(4) A non-global IPv6 address including a zone + index as defined by the InetAddressIPv6z + textual convention. + + dns(16) A DNS domain name as defined by the + InetAddressDNS textual convention. + + Each definition of a concrete InetAddressType value must be + accompanied by a definition of a textual convention for use + with that InetAddressType. + + To support future extensions, the InetAddressType textual + convention SHOULD NOT be sub-typed in object type definitions. + It MAY be sub-typed in compliance statements in order to + require only a subset of these address types for a compliant + implementation. + + Implementations must ensure that InetAddressType objects + and any dependent objects (e.g., InetAddress objects) are + consistent. An inconsistentValue error must be generated + if an attempt to change an InetAddressType object would, + for example, lead to an undefined InetAddress value. In + + + + particular, InetAddressType/InetAddress pairs must be + changed together if the address type changes (e.g., from + ipv6(2) to ipv4(1))." + SYNTAX INTEGER { + unknown(0), + ipv4(1), + ipv6(2), + ipv4z(3), + ipv6z(4), + dns(16) + } + +InetAddress ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Denotes a generic Internet address. + + An InetAddress value is always interpreted within the context + of an InetAddressType value. Every usage of the InetAddress + textual convention is required to specify the InetAddressType + object that provides the context. It is suggested that the + InetAddressType object be logically registered before the + object(s) that use the InetAddress textual convention, if + they appear in the same logical row. + + The value of an InetAddress object must always be + consistent with the value of the associated InetAddressType + object. Attempts to set an InetAddress object to a value + inconsistent with the associated InetAddressType + must fail with an inconsistentValue error. + + When this textual convention is used as the syntax of an + index object, there may be issues with the limit of 128 + sub-identifiers specified in SMIv2, STD 58. In this case, + the object definition MUST include a 'SIZE' clause to + limit the number of potential instance sub-identifiers; + otherwise the applicable constraints MUST be stated in + the appropriate conceptual row DESCRIPTION clauses, or + in the surrounding documentation if there is no single + DESCRIPTION clause that is appropriate." + SYNTAX OCTET STRING (SIZE (0..255)) + +InetAddressIPv4 ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1d.1d.1d.1d" + STATUS current + DESCRIPTION + "Represents an IPv4 network address: + + + + + Octets Contents Encoding + 1-4 IPv4 address network-byte order + + The corresponding InetAddressType value is ipv4(1). + + This textual convention SHOULD NOT be used directly in object + definitions, as it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or in + conjunction with InetAddressType, as a pair." + SYNTAX OCTET STRING (SIZE (4)) + +InetAddressIPv6 ::= TEXTUAL-CONVENTION + DISPLAY-HINT "2x:2x:2x:2x:2x:2x:2x:2x" + STATUS current + DESCRIPTION + "Represents an IPv6 network address: + + Octets Contents Encoding + 1-16 IPv6 address network-byte order + + The corresponding InetAddressType value is ipv6(2). + + This textual convention SHOULD NOT be used directly in object + definitions, as it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or in + conjunction with InetAddressType, as a pair." + SYNTAX OCTET STRING (SIZE (16)) + +InetAddressIPv4z ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1d.1d.1d.1d%4d" + STATUS current + DESCRIPTION + "Represents a non-global IPv4 network address, together + with its zone index: + + Octets Contents Encoding + 1-4 IPv4 address network-byte order + 5-8 zone index network-byte order + + The corresponding InetAddressType value is ipv4z(3). + + The zone index (bytes 5-8) is used to disambiguate identical + address values on nodes that have interfaces attached to + different zones of the same scope. The zone index may contain + the special value 0, which refers to the default zone for each + scope. + + This textual convention SHOULD NOT be used directly in object + + + + definitions, as it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or in + conjunction with InetAddressType, as a pair." + SYNTAX OCTET STRING (SIZE (8)) + +InetAddressIPv6z ::= TEXTUAL-CONVENTION + DISPLAY-HINT "2x:2x:2x:2x:2x:2x:2x:2x%4d" + STATUS current + DESCRIPTION + "Represents a non-global IPv6 network address, together + with its zone index: + + Octets Contents Encoding + 1-16 IPv6 address network-byte order + 17-20 zone index network-byte order + + The corresponding InetAddressType value is ipv6z(4). + + The zone index (bytes 17-20) is used to disambiguate + identical address values on nodes that have interfaces + attached to different zones of the same scope. The zone index + may contain the special value 0, which refers to the default + zone for each scope. + + This textual convention SHOULD NOT be used directly in object + definitions, as it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or in + conjunction with InetAddressType, as a pair." + SYNTAX OCTET STRING (SIZE (20)) + +InetAddressDNS ::= TEXTUAL-CONVENTION + DISPLAY-HINT "255a" + STATUS current + DESCRIPTION + "Represents a DNS domain name. The name SHOULD be fully + qualified whenever possible. + + The corresponding InetAddressType is dns(16). + + The DESCRIPTION clause of InetAddress objects that may have + InetAddressDNS values MUST fully describe how (and when) + these names are to be resolved to IP addresses. + + The resolution of an InetAddressDNS value may require to + query multiple DNS records (e.g., A for IPv4 and AAAA for + IPv6). The order of the resolution process and which DNS + record takes precedence depends on the configuration of the + resolver. + + + + This textual convention SHOULD NOT be used directly in object + definitions, as it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or in + conjunction with InetAddressType, as a pair." + SYNTAX OCTET STRING (SIZE (1..255)) + +InetAddressPrefixLength ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "Denotes the length of a generic Internet network address + prefix. A value of n corresponds to an IP address mask + that has n contiguous 1-bits from the most significant + bit (MSB), with all other bits set to 0. + + An InetAddressPrefixLength value is always interpreted within + the context of an InetAddressType value. Every usage of the + InetAddressPrefixLength textual convention is required to + specify the InetAddressType object that provides the + context. It is suggested that the InetAddressType object be + logically registered before the object(s) that use the + InetAddressPrefixLength textual convention, if they appear + in the same logical row. + + InetAddressPrefixLength values larger than + the maximum length of an IP address for a specific + InetAddressType are treated as the maximum significant + value applicable for the InetAddressType. The maximum + significant value is 32 for the InetAddressType + 'ipv4(1)' and 'ipv4z(3)' and 128 for the InetAddressType + 'ipv6(2)' and 'ipv6z(4)'. The maximum significant value + for the InetAddressType 'dns(16)' is 0. + + The value zero is object-specific and must be defined as + part of the description of any object that uses this + syntax. Examples of the usage of zero might include + situations where the Internet network address prefix + is unknown or does not apply. + + The upper bound of the prefix length has been chosen to + be consistent with the maximum size of an InetAddress." + SYNTAX Unsigned32 (0..2040) + +InetPortNumber ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "Represents a 16 bit port number of an Internet transport + + + + layer protocol. Port numbers are assigned by IANA. A + current list of all assignments is available from + . + + The value zero is object-specific and must be defined as + part of the description of any object that uses this + syntax. Examples of the usage of zero might include + situations where a port number is unknown, or when the + value zero is used as a wildcard in a filter." + REFERENCE "STD 6 (RFC 768), STD 7 (RFC 793) and RFC 2960" + SYNTAX Unsigned32 (0..65535) + +InetAutonomousSystemNumber ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "Represents an autonomous system number that identifies an + Autonomous System (AS). An AS is a set of routers under a + single technical administration, using an interior gateway + protocol and common metrics to route packets within the AS, + and using an exterior gateway protocol to route packets to + other ASes'. IANA maintains the AS number space and has + delegated large parts to the regional registries. + + Autonomous system numbers are currently limited to 16 bits + (0..65535). There is, however, work in progress to enlarge the + autonomous system number space to 32 bits. Therefore, this + textual convention uses an Unsigned32 value without a + range restriction in order to support a larger autonomous + system number space." + REFERENCE "RFC 1771, RFC 1930" + SYNTAX Unsigned32 + +InetScopeType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents a scope type. This textual convention can be used + in cases where a MIB has to represent different scope types + and there is no context information, such as an InetAddress + object, that implicitly defines the scope type. + + Note that not all possible values have been assigned yet, but + they may be assigned in future revisions of this specification. + Applications should therefore be able to deal with values + not yet assigned." + REFERENCE "RFC 3513" + SYNTAX INTEGER { + -- reserved(0), + + + + interfaceLocal(1), + linkLocal(2), + subnetLocal(3), + adminLocal(4), + siteLocal(5), -- site-local unicast addresses + -- have been deprecated by RFC 3879 + -- unassigned(6), + -- unassigned(7), + organizationLocal(8), + -- unassigned(9), + -- unassigned(10), + -- unassigned(11), + -- unassigned(12), + -- unassigned(13), + global(14) + -- reserved(15) + } + +InetZoneIndex ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "A zone index identifies an instance of a zone of a + specific scope. + + The zone index MUST disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index (ifIndex as defined in the + IF-MIB) of the interface on which the address is configured. + + The zone index may contain the special value 0, which refers + to the default zone. The default zone may be used in cases + where the valid zone index is not known (e.g., when a + management application has to write a link-local IPv6 + address without knowing the interface index value). The + default zone SHOULD NOT be used as an easy way out in + cases where the zone index for a non-global IPv6 address + is known." + REFERENCE "RFC4007" + SYNTAX Unsigned32 + +InetVersion ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A value representing a version of the IP protocol. + + unknown(0) An unknown or unspecified version of the IP + protocol. + + + + ipv4(1) The IPv4 protocol as defined in RFC 791 (STD 5). + + ipv6(2) The IPv6 protocol as defined in RFC 2460. + + Note that this textual convention SHOULD NOT be used to + distinguish different address types associated with IP + protocols. The InetAddressType has been designed for this + purpose." + REFERENCE "RFC 791, RFC 2460" + SYNTAX INTEGER { + unknown(0), + ipv4(1), + ipv6(2) + } +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IP-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IP-MIB new file mode 100644 index 0000000..6bee0c0 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/IP-MIB @@ -0,0 +1,5254 @@ +IP-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, + Integer32, Counter32, IpAddress, + mib-2, Unsigned32, Counter64, + zeroDotZero FROM SNMPv2-SMI + PhysAddress, TruthValue, + TimeStamp, RowPointer, + TEXTUAL-CONVENTION, TestAndIncr, + RowStatus, StorageType FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF + InetAddress, InetAddressType, + InetAddressPrefixLength, + InetVersion, InetZoneIndex FROM INET-ADDRESS-MIB + InterfaceIndex FROM IF-MIB; + +ipMIB MODULE-IDENTITY + LAST-UPDATED "200602020000Z" + ORGANIZATION "IETF IPv6 MIB Revision Team" + CONTACT-INFO + "Editor: + + + + Shawn A. Routhier + Interworking Labs + 108 Whispering Pines Dr. Suite 235 + Scotts Valley, CA 95066 + USA + EMail: " + DESCRIPTION + "The MIB module for managing IP and ICMP implementations, but + excluding their management of IP routes. + + Copyright (C) The Internet Society (2006). This version of + this MIB module is part of RFC 4293; see the RFC itself for + full legal notices." + + REVISION "200602020000Z" + DESCRIPTION + "The IP version neutral revision with added IPv6 objects for + ND, default routers, and router advertisements. As well as + being the successor to RFC 2011, this MIB is also the + successor to RFCs 2465 and 2466. Published as RFC 4293." + + REVISION "199411010000Z" + DESCRIPTION + "A separate MIB module (IP-MIB) for IP and ICMP management + objects. Published as RFC 2011." + + REVISION "199103310000Z" + DESCRIPTION + "The initial revision of this MIB module was part of MIB-II, + which was published as RFC 1213." + ::= { mib-2 48} + +-- +-- The textual conventions we define and use in this MIB. +-- + +IpAddressOriginTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The origin of the address. + + manual(2) indicates that the address was manually configured + to a specified address, e.g., by user configuration. + + dhcp(4) indicates an address that was assigned to this + system by a DHCP server. + + linklayer(5) indicates an address created by IPv6 stateless + + + + auto-configuration. + + random(6) indicates an address chosen by the system at + random, e.g., an IPv4 address within 169.254/16, or an RFC + 3041 privacy address." + SYNTAX INTEGER { + other(1), + manual(2), + dhcp(4), + linklayer(5), + random(6) + } + +IpAddressStatusTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The status of an address. Most of the states correspond to + states from the IPv6 Stateless Address Autoconfiguration + protocol. + + The preferred(1) state indicates that this is a valid + address that can appear as the destination or source address + of a packet. + + The deprecated(2) state indicates that this is a valid but + deprecated address that should no longer be used as a source + address in new communications, but packets addressed to such + an address are processed as expected. + + The invalid(3) state indicates that this isn't a valid + address and it shouldn't appear as the destination or source + address of a packet. + + The inaccessible(4) state indicates that the address is not + accessible because the interface to which this address is + assigned is not operational. + + The unknown(5) state indicates that the status cannot be + determined for some reason. + + The tentative(6) state indicates that the uniqueness of the + address on the link is being verified. Addresses in this + state should not be used for general communication and + should only be used to determine the uniqueness of the + address. + + The duplicate(7) state indicates the address has been + determined to be non-unique on the link and so must not be + + + + used. + + The optimistic(8) state indicates the address is available + for use, subject to restrictions, while its uniqueness on + a link is being verified. + + In the absence of other information, an IPv4 address is + always preferred(1)." + REFERENCE "RFC 2462" + SYNTAX INTEGER { + preferred(1), + deprecated(2), + invalid(3), + inaccessible(4), + unknown(5), + tentative(6), + duplicate(7), + optimistic(8) + } + +IpAddressPrefixOriginTC ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The origin of this prefix. + + manual(2) indicates a prefix that was manually configured. + + wellknown(3) indicates a well-known prefix, e.g., 169.254/16 + for IPv4 auto-configuration or fe80::/10 for IPv6 link-local + addresses. Well known prefixes may be assigned by IANA, + the address registries, or by specification in a standards + track RFC. + + dhcp(4) indicates a prefix that was assigned by a DHCP + server. + + routeradv(5) indicates a prefix learned from a router + advertisement. + + Note: while IpAddressOriginTC and IpAddressPrefixOriginTC + are similar, they are not identical. The first defines how + an address was created, while the second defines how a + prefix was found." + SYNTAX INTEGER { + other(1), + manual(2), + wellknown(3), + dhcp(4), + + + + routeradv(5) + } + +Ipv6AddressIfIdentifierTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "2x:" + STATUS current + DESCRIPTION + "This data type is used to model IPv6 address + interface identifiers. This is a binary string + of up to 8 octets in network byte-order." + SYNTAX OCTET STRING (SIZE (0..8)) + +-- +-- the IP general group +-- some objects that affect all of IPv4 +-- + +ip OBJECT IDENTIFIER ::= { mib-2 4 } + +ipForwarding OBJECT-TYPE + SYNTAX INTEGER { + forwarding(1), -- acting as a router + notForwarding(2) -- NOT acting as a router + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The indication of whether this entity is acting as an IPv4 + router in respect to the forwarding of datagrams received + by, but not addressed to, this entity. IPv4 routers forward + datagrams. IPv4 hosts do not (except those source-routed + via the host). + + When this object is written, the entity should save the + change to non-volatile storage and restore the object from + non-volatile storage upon re-initialization of the system. + Note: a stronger requirement is not used because this object + was previously defined." + ::= { ip 1 } + +ipDefaultTTL OBJECT-TYPE + SYNTAX Integer32 (1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The default value inserted into the Time-To-Live field of + the IPv4 header of datagrams originated at this entity, + whenever a TTL value is not supplied by the transport layer + + + + protocol. + + When this object is written, the entity should save the + change to non-volatile storage and restore the object from + non-volatile storage upon re-initialization of the system. + Note: a stronger requirement is not used because this object + was previously defined." + ::= { ip 2 } + +ipReasmTimeout OBJECT-TYPE + SYNTAX Integer32 + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum number of seconds that received fragments are + held while they are awaiting reassembly at this entity." + ::= { ip 13 } + +-- +-- the IPv6 general group +-- Some objects that affect all of IPv6 +-- + +ipv6IpForwarding OBJECT-TYPE + SYNTAX INTEGER { + forwarding(1), -- acting as a router + notForwarding(2) -- NOT acting as a router + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The indication of whether this entity is acting as an IPv6 + router on any interface in respect to the forwarding of + datagrams received by, but not addressed to, this entity. + IPv6 routers forward datagrams. IPv6 hosts do not (except + those source-routed via the host). + + When this object is written, the entity SHOULD save the + change to non-volatile storage and restore the object from + non-volatile storage upon re-initialization of the system." + ::= { ip 25 } + +ipv6IpDefaultHopLimit OBJECT-TYPE + SYNTAX Integer32 (0..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + + + + "The default value inserted into the Hop Limit field of the + IPv6 header of datagrams originated at this entity whenever + a Hop Limit value is not supplied by the transport layer + protocol. + + When this object is written, the entity SHOULD save the + change to non-volatile storage and restore the object from + non-volatile storage upon re-initialization of the system." + REFERENCE "RFC 2461 Section 6.3.2" + ::= { ip 26 } + +-- +-- IPv4 Interface Table +-- + +ipv4InterfaceTableLastChange OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime on the most recent occasion at which + a row in the ipv4InterfaceTable was added or deleted, or + when an ipv4InterfaceReasmMaxSize or an + ipv4InterfaceEnableStatus object was modified. + + If new objects are added to the ipv4InterfaceTable that + require the ipv4InterfaceTableLastChange to be updated when + they are modified, they must specify that requirement in + their description clause." + ::= { ip 27 } + +ipv4InterfaceTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ipv4InterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table containing per-interface IPv4-specific + information." + ::= { ip 28 } + +ipv4InterfaceEntry OBJECT-TYPE + SYNTAX Ipv4InterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry containing IPv4-specific information for a specific + interface." + INDEX { ipv4InterfaceIfIndex } + + + + ::= { ipv4InterfaceTable 1 } + +Ipv4InterfaceEntry ::= SEQUENCE { + ipv4InterfaceIfIndex InterfaceIndex, + ipv4InterfaceReasmMaxSize Integer32, + ipv4InterfaceEnableStatus INTEGER, + ipv4InterfaceRetransmitTime Unsigned32 + } + +ipv4InterfaceIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface to + which this entry is applicable. The interface identified by + a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipv4InterfaceEntry 1 } + +ipv4InterfaceReasmMaxSize OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The size of the largest IPv4 datagram that this entity can + re-assemble from incoming IPv4 fragmented datagrams received + on this interface." + ::= { ipv4InterfaceEntry 2 } + +ipv4InterfaceEnableStatus OBJECT-TYPE + SYNTAX INTEGER { + up(1), + down(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The indication of whether IPv4 is enabled (up) or disabled + (down) on this interface. This object does not affect the + state of the interface itself, only its connection to an + IPv4 stack. The IF-MIB should be used to control the state + of the interface." + ::= { ipv4InterfaceEntry 3 } + +ipv4InterfaceRetransmitTime OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + + + + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The time between retransmissions of ARP requests to a + neighbor when resolving the address or when probing the + reachability of a neighbor." + REFERENCE "RFC 1122" + DEFVAL { 1000 } + ::= { ipv4InterfaceEntry 4 } + +-- +-- v6 interface table +-- + +ipv6InterfaceTableLastChange OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime on the most recent occasion at which + a row in the ipv6InterfaceTable was added or deleted or when + an ipv6InterfaceReasmMaxSize, ipv6InterfaceIdentifier, + ipv6InterfaceEnableStatus, ipv6InterfaceReachableTime, + ipv6InterfaceRetransmitTime, or ipv6InterfaceForwarding + object was modified. + + If new objects are added to the ipv6InterfaceTable that + require the ipv6InterfaceTableLastChange to be updated when + they are modified, they must specify that requirement in + their description clause." + ::= { ip 29 } + +ipv6InterfaceTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ipv6InterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table containing per-interface IPv6-specific + information." + ::= { ip 30 } + +ipv6InterfaceEntry OBJECT-TYPE + SYNTAX Ipv6InterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry containing IPv6-specific information for a given + interface." + + + + INDEX { ipv6InterfaceIfIndex } + ::= { ipv6InterfaceTable 1 } + +Ipv6InterfaceEntry ::= SEQUENCE { + ipv6InterfaceIfIndex InterfaceIndex, + ipv6InterfaceReasmMaxSize Unsigned32, + ipv6InterfaceIdentifier Ipv6AddressIfIdentifierTC, + ipv6InterfaceEnableStatus INTEGER, + ipv6InterfaceReachableTime Unsigned32, + ipv6InterfaceRetransmitTime Unsigned32, + ipv6InterfaceForwarding INTEGER + } + +ipv6InterfaceIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface to + which this entry is applicable. The interface identified by + a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipv6InterfaceEntry 1 } + +ipv6InterfaceReasmMaxSize OBJECT-TYPE + SYNTAX Unsigned32 (1500..65535) + UNITS "octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The size of the largest IPv6 datagram that this entity can + re-assemble from incoming IPv6 fragmented datagrams received + on this interface." + ::= { ipv6InterfaceEntry 2 } + +ipv6InterfaceIdentifier OBJECT-TYPE + SYNTAX Ipv6AddressIfIdentifierTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Interface Identifier for this interface. The Interface + Identifier is combined with an address prefix to form an + interface address. + + By default, the Interface Identifier is auto-configured + according to the rules of the link type to which this + interface is attached. + + + + + A zero length identifier may be used where appropriate. One + possible example is a loopback interface." + ::= { ipv6InterfaceEntry 3 } + +-- This object ID is reserved as it was used in earlier versions of +-- the MIB module. In theory, OIDs are not assigned until the +-- specification is released as an RFC; however, as some companies +-- may have shipped code based on earlier versions of the MIB, it +-- seems best to reserve this OID. This OID had been +-- ipv6InterfacePhysicalAddress. +-- ::= { ipv6InterfaceEntry 4} + +ipv6InterfaceEnableStatus OBJECT-TYPE + SYNTAX INTEGER { + up(1), + down(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The indication of whether IPv6 is enabled (up) or disabled + (down) on this interface. This object does not affect the + state of the interface itself, only its connection to an + IPv6 stack. The IF-MIB should be used to control the state + of the interface. + + When this object is written, the entity SHOULD save the + change to non-volatile storage and restore the object from + non-volatile storage upon re-initialization of the system." + ::= { ipv6InterfaceEntry 5 } + +ipv6InterfaceReachableTime OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The time a neighbor is considered reachable after receiving + a reachability confirmation." + REFERENCE "RFC 2461, Section 6.3.2" + ::= { ipv6InterfaceEntry 6 } + +ipv6InterfaceRetransmitTime OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + + + + "The time between retransmissions of Neighbor Solicitation + messages to a neighbor when resolving the address or when + probing the reachability of a neighbor." + REFERENCE "RFC 2461, Section 6.3.2" + ::= { ipv6InterfaceEntry 7 } + +ipv6InterfaceForwarding OBJECT-TYPE + SYNTAX INTEGER { + forwarding(1), -- acting as a router + notForwarding(2) -- NOT acting as a router + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The indication of whether this entity is acting as an IPv6 + router on this interface with respect to the forwarding of + datagrams received by, but not addressed to, this entity. + IPv6 routers forward datagrams. IPv6 hosts do not (except + those source-routed via the host). + + This object is constrained by ipv6IpForwarding and is + ignored if ipv6IpForwarding is set to notForwarding. Those + systems that do not provide per-interface control of the + forwarding function should set this object to forwarding for + all interfaces and allow the ipv6IpForwarding object to + control the forwarding capability. + + When this object is written, the entity SHOULD save the + change to non-volatile storage and restore the object from + non-volatile storage upon re-initialization of the system." + ::= { ipv6InterfaceEntry 8 } + +-- +-- Per-Interface or System-Wide IP statistics. +-- +-- The following two tables, ipSystemStatsTable and ipIfStatsTable, +-- are intended to provide the same counters at different granularities. +-- The ipSystemStatsTable provides system wide counters aggregating +-- the traffic counters for all interfaces for a given address type. +-- The ipIfStatsTable provides the same counters but for specific +-- interfaces rather than as an aggregate. +-- +-- Note well: If a system provides both system-wide and interface- +-- specific values, the system-wide value may not be equal to the sum +-- of the interface-specific values across all interfaces due to e.g., +-- dynamic interface creation/deletion. +-- +-- Note well: Both of these tables contain some items that are + + + +-- represented by two objects, representing the value in either 32 +-- or 64 bits. For those objects, the 32-bit value MUST be the low +-- order 32 bits of the 64-bit value. Also note that the 32-bit +-- counters must be included when the 64-bit counters are included. + +ipTrafficStats OBJECT IDENTIFIER ::= { ip 31 } + +ipSystemStatsTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpSystemStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table containing system wide, IP version specific + traffic statistics. This table and the ipIfStatsTable + contain similar objects whose difference is in their + granularity. Where this table contains system wide traffic + statistics, the ipIfStatsTable contains the same statistics + but counted on a per-interface basis." + ::= { ipTrafficStats 1 } + +ipSystemStatsEntry OBJECT-TYPE + SYNTAX IpSystemStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A statistics entry containing system-wide objects for a + particular IP version." + INDEX { ipSystemStatsIPVersion } + ::= { ipSystemStatsTable 1 } + +IpSystemStatsEntry ::= SEQUENCE { + ipSystemStatsIPVersion InetVersion, + ipSystemStatsInReceives Counter32, + ipSystemStatsHCInReceives Counter64, + ipSystemStatsInOctets Counter32, + ipSystemStatsHCInOctets Counter64, + ipSystemStatsInHdrErrors Counter32, + ipSystemStatsInNoRoutes Counter32, + ipSystemStatsInAddrErrors Counter32, + ipSystemStatsInUnknownProtos Counter32, + ipSystemStatsInTruncatedPkts Counter32, + ipSystemStatsInForwDatagrams Counter32, + ipSystemStatsHCInForwDatagrams Counter64, + ipSystemStatsReasmReqds Counter32, + ipSystemStatsReasmOKs Counter32, + ipSystemStatsReasmFails Counter32, + ipSystemStatsInDiscards Counter32, + ipSystemStatsInDelivers Counter32, + + + + ipSystemStatsHCInDelivers Counter64, + ipSystemStatsOutRequests Counter32, + ipSystemStatsHCOutRequests Counter64, + ipSystemStatsOutNoRoutes Counter32, + ipSystemStatsOutForwDatagrams Counter32, + ipSystemStatsHCOutForwDatagrams Counter64, + ipSystemStatsOutDiscards Counter32, + ipSystemStatsOutFragReqds Counter32, + ipSystemStatsOutFragOKs Counter32, + ipSystemStatsOutFragFails Counter32, + ipSystemStatsOutFragCreates Counter32, + ipSystemStatsOutTransmits Counter32, + ipSystemStatsHCOutTransmits Counter64, + ipSystemStatsOutOctets Counter32, + ipSystemStatsHCOutOctets Counter64, + ipSystemStatsInMcastPkts Counter32, + ipSystemStatsHCInMcastPkts Counter64, + ipSystemStatsInMcastOctets Counter32, + ipSystemStatsHCInMcastOctets Counter64, + ipSystemStatsOutMcastPkts Counter32, + ipSystemStatsHCOutMcastPkts Counter64, + ipSystemStatsOutMcastOctets Counter32, + ipSystemStatsHCOutMcastOctets Counter64, + ipSystemStatsInBcastPkts Counter32, + ipSystemStatsHCInBcastPkts Counter64, + ipSystemStatsOutBcastPkts Counter32, + ipSystemStatsHCOutBcastPkts Counter64, + ipSystemStatsDiscontinuityTime TimeStamp, + ipSystemStatsRefreshRate Unsigned32 + } + +ipSystemStatsIPVersion OBJECT-TYPE + SYNTAX InetVersion + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IP version of this row." + ::= { ipSystemStatsEntry 1 } + +-- This object ID is reserved to allow the IDs for this table's objects +-- to align with the objects in the ipIfStatsTable. +-- ::= { ipSystemStatsEntry 2 } + +ipSystemStatsInReceives OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + + + + "The total number of input IP datagrams received, including + those received in error. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 3 } + +ipSystemStatsHCInReceives OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of input IP datagrams received, including + those received in error. This object counts the same + datagrams as ipSystemStatsInReceives, but allows for larger + values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 4 } + +ipSystemStatsInOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received in input IP datagrams, + including those received in error. Octets from datagrams + counted in ipSystemStatsInReceives MUST be counted here. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 5 } + +ipSystemStatsHCInOctets OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received in input IP datagrams, + including those received in error. This object counts the + same octets as ipSystemStatsInOctets, but allows for larger + + + + values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 6 } + +ipSystemStatsInHdrErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams discarded due to errors in + their IP headers, including version number mismatch, other + format errors, hop count exceeded, errors discovered in + processing their IP options, etc. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 7 } + +ipSystemStatsInNoRoutes OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams discarded because no route + could be found to transmit them to their destination. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 8 } + +ipSystemStatsInAddrErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams discarded because the IP + address in their IP header's destination field was not a + valid address to be received at this entity. This count + includes invalid addresses (e.g., ::0). For entities + that are not IP routers and therefore do not forward + + + + datagrams, this counter includes datagrams discarded + because the destination address was not a local address. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 9 } + +ipSystemStatsInUnknownProtos OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of locally-addressed IP datagrams received + successfully but discarded because of an unknown or + unsupported protocol. + + When tracking interface statistics, the counter of the + interface to which these datagrams were addressed is + incremented. This interface might not be the same as the + input interface for some of the datagrams. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 10 } + +ipSystemStatsInTruncatedPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams discarded because the + datagram frame didn't carry enough data. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 11 } + +ipSystemStatsInForwDatagrams OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + + + + "The number of input datagrams for which this entity was not + their final IP destination and for which this entity + attempted to find a route to forward them to that final + destination. In entities that do not act as IP routers, + this counter will include only those datagrams that were + Source-Routed via this entity, and the Source-Route + processing was successful. + + When tracking interface statistics, the counter of the + incoming interface is incremented for each datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 12 } + +ipSystemStatsHCInForwDatagrams OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input datagrams for which this entity was not + their final IP destination and for which this entity + attempted to find a route to forward them to that final + destination. This object counts the same packets as + ipSystemStatsInForwDatagrams, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 13 } + +ipSystemStatsReasmReqds OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP fragments received that needed to be + reassembled at this interface. + + When tracking interface statistics, the counter of the + interface to which these fragments were addressed is + incremented. This interface might not be the same as the + input interface for some of the fragments. + + Discontinuities in the value of this counter can occur at + + + + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 14 } + +ipSystemStatsReasmOKs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP datagrams successfully reassembled. + + When tracking interface statistics, the counter of the + interface to which these datagrams were addressed is + incremented. This interface might not be the same as the + input interface for some of the datagrams. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 15 } + +ipSystemStatsReasmFails OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of failures detected by the IP re-assembly + algorithm (for whatever reason: timed out, errors, etc.). + Note that this is not necessarily a count of discarded IP + fragments since some algorithms (notably the algorithm in + RFC 815) can lose track of the number of fragments by + combining them as they are received. + + When tracking interface statistics, the counter of the + interface to which these fragments were addressed is + incremented. This interface might not be the same as the + input interface for some of the fragments. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 16 } + +ipSystemStatsInDiscards OBJECT-TYPE + SYNTAX Counter32 + + + + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams for which no problems were + encountered to prevent their continued processing, but + were discarded (e.g., for lack of buffer space). Note that + this counter does not include any datagrams discarded while + awaiting re-assembly. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 17 } + +ipSystemStatsInDelivers OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of datagrams successfully delivered to IP + user-protocols (including ICMP). + + When tracking interface statistics, the counter of the + interface to which these datagrams were addressed is + incremented. This interface might not be the same as the + input interface for some of the datagrams. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 18 } + +ipSystemStatsHCInDelivers OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of datagrams successfully delivered to IP + user-protocols (including ICMP). This object counts the + same packets as ipSystemStatsInDelivers, but allows for + larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + + + + ::= { ipSystemStatsEntry 19 } + +ipSystemStatsOutRequests OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of IP datagrams that local IP user- + protocols (including ICMP) supplied to IP in requests for + transmission. Note that this counter does not include any + datagrams counted in ipSystemStatsOutForwDatagrams. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 20 } + +ipSystemStatsHCOutRequests OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of IP datagrams that local IP user- + protocols (including ICMP) supplied to IP in requests for + transmission. This object counts the same packets as + ipSystemStatsOutRequests, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 21 } + +ipSystemStatsOutNoRoutes OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of locally generated IP datagrams discarded + because no route could be found to transmit them to their + destination. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 22 } + + + +ipSystemStatsOutForwDatagrams OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of datagrams for which this entity was not their + final IP destination and for which it was successful in + finding a path to their final destination. In entities + that do not act as IP routers, this counter will include + only those datagrams that were Source-Routed via this + entity, and the Source-Route processing was successful. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for a successfully + forwarded datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 23 } + +ipSystemStatsHCOutForwDatagrams OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of datagrams for which this entity was not their + final IP destination and for which it was successful in + finding a path to their final destination. This object + counts the same packets as ipSystemStatsOutForwDatagrams, + but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 24 } + +ipSystemStatsOutDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of output IP datagrams for which no problem was + encountered to prevent their transmission to their + destination, but were discarded (e.g., for lack of + buffer space). Note that this counter would include + + + + datagrams counted in ipSystemStatsOutForwDatagrams if any + such datagrams met this (discretionary) discard criterion. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 25 } + +ipSystemStatsOutFragReqds OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP datagrams that would require fragmentation + in order to be transmitted. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for a successfully + fragmented datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 26 } + +ipSystemStatsOutFragOKs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP datagrams that have been successfully + fragmented. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for a successfully + fragmented datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 27 } + +ipSystemStatsOutFragFails OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + + + + STATUS current + DESCRIPTION + "The number of IP datagrams that have been discarded because + they needed to be fragmented but could not be. This + includes IPv4 packets that have the DF bit set and IPv6 + packets that are being forwarded and exceed the outgoing + link MTU. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for an unsuccessfully + fragmented datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 28 } + +ipSystemStatsOutFragCreates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of output datagram fragments that have been + generated as a result of IP fragmentation. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for a successfully + fragmented datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 29 } + +ipSystemStatsOutTransmits OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of IP datagrams that this entity supplied + to the lower layers for transmission. This includes + datagrams generated locally and those forwarded by this + entity. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + + + + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 30 } + +ipSystemStatsHCOutTransmits OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of IP datagrams that this entity supplied + to the lower layers for transmission. This object counts + the same datagrams as ipSystemStatsOutTransmits, but allows + for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 31 } + +ipSystemStatsOutOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets in IP datagrams delivered to the + lower layers for transmission. Octets from datagrams + counted in ipSystemStatsOutTransmits MUST be counted here. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 32 } + +ipSystemStatsHCOutOctets OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets in IP datagrams delivered to the + lower layers for transmission. This objects counts the same + octets as ipSystemStatsOutOctets, but allows for larger + values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + + + + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 33 } + +ipSystemStatsInMcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP multicast datagrams received. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 34 } + +ipSystemStatsHCInMcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP multicast datagrams received. This object + counts the same datagrams as ipSystemStatsInMcastPkts but + allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 35 } + +ipSystemStatsInMcastOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received in IP multicast + datagrams. Octets from datagrams counted in + ipSystemStatsInMcastPkts MUST be counted here. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 36 } + +ipSystemStatsHCInMcastOctets OBJECT-TYPE + SYNTAX Counter64 + + + + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received in IP multicast + datagrams. This object counts the same octets as + ipSystemStatsInMcastOctets, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 37 } + +ipSystemStatsOutMcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP multicast datagrams transmitted. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 38 } + +ipSystemStatsHCOutMcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP multicast datagrams transmitted. This + object counts the same datagrams as + ipSystemStatsOutMcastPkts, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 39 } + +ipSystemStatsOutMcastOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets transmitted in IP multicast + datagrams. Octets from datagrams counted in + + + + ipSystemStatsOutMcastPkts MUST be counted here. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 40 } + +ipSystemStatsHCOutMcastOctets OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets transmitted in IP multicast + datagrams. This object counts the same octets as + ipSystemStatsOutMcastOctets, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 41 } + +ipSystemStatsInBcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP broadcast datagrams received. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 42 } + +ipSystemStatsHCInBcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP broadcast datagrams received. This object + counts the same datagrams as ipSystemStatsInBcastPkts but + allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + + + + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 43 } + +ipSystemStatsOutBcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP broadcast datagrams transmitted. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 44 } + +ipSystemStatsHCOutBcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP broadcast datagrams transmitted. This + object counts the same datagrams as + ipSystemStatsOutBcastPkts, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipSystemStatsDiscontinuityTime." + ::= { ipSystemStatsEntry 45 } + +ipSystemStatsDiscontinuityTime OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime on the most recent occasion at which + any one or more of this entry's counters suffered a + discontinuity. + + If no such discontinuities have occurred since the last re- + initialization of the local management subsystem, then this + object contains a zero value." + ::= { ipSystemStatsEntry 46 } + +ipSystemStatsRefreshRate OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milli-seconds" + + + + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The minimum reasonable polling interval for this entry. + This object provides an indication of the minimum amount of + time required to update the counters in this entry." + ::= { ipSystemStatsEntry 47 } + +ipIfStatsTableLastChange OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime on the most recent occasion at which + a row in the ipIfStatsTable was added or deleted. + + If new objects are added to the ipIfStatsTable that require + the ipIfStatsTableLastChange to be updated when they are + modified, they must specify that requirement in their + description clause." + ::= { ipTrafficStats 2 } + +ipIfStatsTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpIfStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table containing per-interface traffic statistics. This + table and the ipSystemStatsTable contain similar objects + whose difference is in their granularity. Where this table + contains per-interface statistics, the ipSystemStatsTable + contains the same statistics, but counted on a system wide + basis." + ::= { ipTrafficStats 3 } + +ipIfStatsEntry OBJECT-TYPE + SYNTAX IpIfStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An interface statistics entry containing objects for a + particular interface and version of IP." + INDEX { ipIfStatsIPVersion, ipIfStatsIfIndex } + ::= { ipIfStatsTable 1 } + +IpIfStatsEntry ::= SEQUENCE { + ipIfStatsIPVersion InetVersion, + ipIfStatsIfIndex InterfaceIndex, + + + + ipIfStatsInReceives Counter32, + ipIfStatsHCInReceives Counter64, + ipIfStatsInOctets Counter32, + ipIfStatsHCInOctets Counter64, + ipIfStatsInHdrErrors Counter32, + ipIfStatsInNoRoutes Counter32, + ipIfStatsInAddrErrors Counter32, + ipIfStatsInUnknownProtos Counter32, + ipIfStatsInTruncatedPkts Counter32, + ipIfStatsInForwDatagrams Counter32, + ipIfStatsHCInForwDatagrams Counter64, + ipIfStatsReasmReqds Counter32, + ipIfStatsReasmOKs Counter32, + ipIfStatsReasmFails Counter32, + ipIfStatsInDiscards Counter32, + ipIfStatsInDelivers Counter32, + ipIfStatsHCInDelivers Counter64, + ipIfStatsOutRequests Counter32, + ipIfStatsHCOutRequests Counter64, + ipIfStatsOutForwDatagrams Counter32, + ipIfStatsHCOutForwDatagrams Counter64, + ipIfStatsOutDiscards Counter32, + ipIfStatsOutFragReqds Counter32, + ipIfStatsOutFragOKs Counter32, + ipIfStatsOutFragFails Counter32, + ipIfStatsOutFragCreates Counter32, + ipIfStatsOutTransmits Counter32, + ipIfStatsHCOutTransmits Counter64, + ipIfStatsOutOctets Counter32, + ipIfStatsHCOutOctets Counter64, + ipIfStatsInMcastPkts Counter32, + ipIfStatsHCInMcastPkts Counter64, + ipIfStatsInMcastOctets Counter32, + ipIfStatsHCInMcastOctets Counter64, + ipIfStatsOutMcastPkts Counter32, + ipIfStatsHCOutMcastPkts Counter64, + ipIfStatsOutMcastOctets Counter32, + ipIfStatsHCOutMcastOctets Counter64, + ipIfStatsInBcastPkts Counter32, + ipIfStatsHCInBcastPkts Counter64, + ipIfStatsOutBcastPkts Counter32, + ipIfStatsHCOutBcastPkts Counter64, + ipIfStatsDiscontinuityTime TimeStamp, + ipIfStatsRefreshRate Unsigned32 + } + +ipIfStatsIPVersion OBJECT-TYPE + SYNTAX InetVersion + + + + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IP version of this row." + ::= { ipIfStatsEntry 1 } + +ipIfStatsIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface to + which this entry is applicable. The interface identified by + a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipIfStatsEntry 2 } + +ipIfStatsInReceives OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of input IP datagrams received, including + those received in error. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 3 } + +ipIfStatsHCInReceives OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of input IP datagrams received, including + those received in error. This object counts the same + datagrams as ipIfStatsInReceives, but allows for larger + values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 4 } + +ipIfStatsInOctets OBJECT-TYPE + + + + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received in input IP datagrams, + including those received in error. Octets from datagrams + counted in ipIfStatsInReceives MUST be counted here. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 5 } + +ipIfStatsHCInOctets OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received in input IP datagrams, + including those received in error. This object counts the + same octets as ipIfStatsInOctets, but allows for larger + values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 6 } + +ipIfStatsInHdrErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams discarded due to errors in + their IP headers, including version number mismatch, other + format errors, hop count exceeded, errors discovered in + processing their IP options, etc. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 7 } + +ipIfStatsInNoRoutes OBJECT-TYPE + SYNTAX Counter32 + + + + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams discarded because no route + could be found to transmit them to their destination. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 8 } + +ipIfStatsInAddrErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams discarded because the IP + address in their IP header's destination field was not a + valid address to be received at this entity. This count + includes invalid addresses (e.g., ::0). For entities that + are not IP routers and therefore do not forward datagrams, + this counter includes datagrams discarded because the + destination address was not a local address. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 9 } + +ipIfStatsInUnknownProtos OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of locally-addressed IP datagrams received + successfully but discarded because of an unknown or + unsupported protocol. + + When tracking interface statistics, the counter of the + interface to which these datagrams were addressed is + incremented. This interface might not be the same as the + input interface for some of the datagrams. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + + + + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 10 } + +ipIfStatsInTruncatedPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams discarded because the + datagram frame didn't carry enough data. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 11 } + +ipIfStatsInForwDatagrams OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input datagrams for which this entity was not + their final IP destination and for which this entity + attempted to find a route to forward them to that final + destination. In entities that do not act as IP routers, + this counter will include only those datagrams that were + Source-Routed via this entity, and the Source-Route + processing was successful. + + When tracking interface statistics, the counter of the + incoming interface is incremented for each datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 12 } + +ipIfStatsHCInForwDatagrams OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input datagrams for which this entity was not + their final IP destination and for which this entity + attempted to find a route to forward them to that final + destination. This object counts the same packets as + + + + ipIfStatsInForwDatagrams, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 13 } + +ipIfStatsReasmReqds OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP fragments received that needed to be + reassembled at this interface. + + When tracking interface statistics, the counter of the + interface to which these fragments were addressed is + incremented. This interface might not be the same as the + input interface for some of the fragments. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 14 } + +ipIfStatsReasmOKs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP datagrams successfully reassembled. + + When tracking interface statistics, the counter of the + interface to which these datagrams were addressed is + incremented. This interface might not be the same as the + input interface for some of the datagrams. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 15 } + +ipIfStatsReasmFails OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + + + + STATUS current + DESCRIPTION + "The number of failures detected by the IP re-assembly + algorithm (for whatever reason: timed out, errors, etc.). + Note that this is not necessarily a count of discarded IP + fragments since some algorithms (notably the algorithm in + RFC 815) can lose track of the number of fragments by + combining them as they are received. + + When tracking interface statistics, the counter of the + interface to which these fragments were addressed is + incremented. This interface might not be the same as the + input interface for some of the fragments. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 16 } + +ipIfStatsInDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input IP datagrams for which no problems were + encountered to prevent their continued processing, but + were discarded (e.g., for lack of buffer space). Note that + this counter does not include any datagrams discarded while + awaiting re-assembly. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 17 } + +ipIfStatsInDelivers OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of datagrams successfully delivered to IP + user-protocols (including ICMP). + + When tracking interface statistics, the counter of the + interface to which these datagrams were addressed is + incremented. This interface might not be the same as the + + + + input interface for some of the datagrams. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 18 } + +ipIfStatsHCInDelivers OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of datagrams successfully delivered to IP + user-protocols (including ICMP). This object counts the + same packets as ipIfStatsInDelivers, but allows for larger + values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 19 } + +ipIfStatsOutRequests OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of IP datagrams that local IP user- + protocols (including ICMP) supplied to IP in requests for + transmission. Note that this counter does not include any + datagrams counted in ipIfStatsOutForwDatagrams. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 20 } + +ipIfStatsHCOutRequests OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of IP datagrams that local IP user- + protocols (including ICMP) supplied to IP in requests for + transmission. This object counts the same packets as + + + + ipIfStatsOutRequests, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 21 } + +-- This object ID is reserved to allow the IDs for this table's objects +-- to align with the objects in the ipSystemStatsTable. +-- ::= {ipIfStatsEntry 22} + +ipIfStatsOutForwDatagrams OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of datagrams for which this entity was not their + final IP destination and for which it was successful in + finding a path to their final destination. In entities + that do not act as IP routers, this counter will include + only those datagrams that were Source-Routed via this + entity, and the Source-Route processing was successful. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for a successfully + forwarded datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 23 } + +ipIfStatsHCOutForwDatagrams OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of datagrams for which this entity was not their + final IP destination and for which it was successful in + finding a path to their final destination. This object + counts the same packets as ipIfStatsOutForwDatagrams, but + allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + + + + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 24 } + +ipIfStatsOutDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of output IP datagrams for which no problem was + encountered to prevent their transmission to their + destination, but were discarded (e.g., for lack of + buffer space). Note that this counter would include + datagrams counted in ipIfStatsOutForwDatagrams if any such + datagrams met this (discretionary) discard criterion. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 25 } + +ipIfStatsOutFragReqds OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP datagrams that would require fragmentation + in order to be transmitted. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for a successfully + fragmented datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 26 } + +ipIfStatsOutFragOKs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP datagrams that have been successfully + fragmented. + + When tracking interface statistics, the counter of the + + + + outgoing interface is incremented for a successfully + fragmented datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 27 } + +ipIfStatsOutFragFails OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP datagrams that have been discarded because + they needed to be fragmented but could not be. This + includes IPv4 packets that have the DF bit set and IPv6 + packets that are being forwarded and exceed the outgoing + link MTU. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for an unsuccessfully + fragmented datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 28 } + +ipIfStatsOutFragCreates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of output datagram fragments that have been + generated as a result of IP fragmentation. + + When tracking interface statistics, the counter of the + outgoing interface is incremented for a successfully + fragmented datagram. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 29 } + + + + +ipIfStatsOutTransmits OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of IP datagrams that this entity supplied + to the lower layers for transmission. This includes + datagrams generated locally and those forwarded by this + entity. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 30 } + +ipIfStatsHCOutTransmits OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of IP datagrams that this entity supplied + to the lower layers for transmission. This object counts + the same datagrams as ipIfStatsOutTransmits, but allows for + larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 31 } + +ipIfStatsOutOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets in IP datagrams delivered to the + lower layers for transmission. Octets from datagrams + counted in ipIfStatsOutTransmits MUST be counted here. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 32 } + +ipIfStatsHCOutOctets OBJECT-TYPE + + + + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets in IP datagrams delivered to the + lower layers for transmission. This objects counts the same + octets as ipIfStatsOutOctets, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 33 } + +ipIfStatsInMcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP multicast datagrams received. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 34 } + +ipIfStatsHCInMcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP multicast datagrams received. This object + counts the same datagrams as ipIfStatsInMcastPkts, but + allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 35 } + +ipIfStatsInMcastOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received in IP multicast + + + + datagrams. Octets from datagrams counted in + ipIfStatsInMcastPkts MUST be counted here. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 36 } + +ipIfStatsHCInMcastOctets OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets received in IP multicast + datagrams. This object counts the same octets as + ipIfStatsInMcastOctets, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 37 } + +ipIfStatsOutMcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP multicast datagrams transmitted. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 38 } + +ipIfStatsHCOutMcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP multicast datagrams transmitted. This + object counts the same datagrams as ipIfStatsOutMcastPkts, + but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + + + + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 39 } + +ipIfStatsOutMcastOctets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets transmitted in IP multicast + datagrams. Octets from datagrams counted in + ipIfStatsOutMcastPkts MUST be counted here. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 40 } + +ipIfStatsHCOutMcastOctets OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets transmitted in IP multicast + datagrams. This object counts the same octets as + ipIfStatsOutMcastOctets, but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 41 } + +ipIfStatsInBcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP broadcast datagrams received. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 42 } + +ipIfStatsHCInBcastPkts OBJECT-TYPE + + + + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP broadcast datagrams received. This object + counts the same datagrams as ipIfStatsInBcastPkts, but + allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 43 } + +ipIfStatsOutBcastPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP broadcast datagrams transmitted. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 44 } + +ipIfStatsHCOutBcastPkts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IP broadcast datagrams transmitted. This + object counts the same datagrams as ipIfStatsOutBcastPkts, + but allows for larger values. + + Discontinuities in the value of this counter can occur at + re-initialization of the management system, and at other + times as indicated by the value of + ipIfStatsDiscontinuityTime." + ::= { ipIfStatsEntry 45 } + +ipIfStatsDiscontinuityTime OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime on the most recent occasion at which + + + + any one or more of this entry's counters suffered a + discontinuity. + + If no such discontinuities have occurred since the last re- + initialization of the local management subsystem, then this + object contains a zero value." + ::= { ipIfStatsEntry 46 } + +ipIfStatsRefreshRate OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milli-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The minimum reasonable polling interval for this entry. + This object provides an indication of the minimum amount of + time required to update the counters in this entry." + ::= { ipIfStatsEntry 47 } + +-- +-- Internet Address Prefix table +-- + +ipAddressPrefixTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpAddressPrefixEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "This table allows the user to determine the source of an IP + address or set of IP addresses, and allows other tables to + share the information via pointer rather than by copying. + + For example, when the node configures both a unicast and + anycast address for a prefix, the ipAddressPrefix objects + for those addresses will point to a single row in this + table. + + This table primarily provides support for IPv6 prefixes, and + several of the objects are less meaningful for IPv4. The + table continues to allow IPv4 addresses to allow future + flexibility. In order to promote a common configuration, + this document includes suggestions for default values for + IPv4 prefixes. Each of these values may be overridden if an + object is meaningful to the node. + + All prefixes used by this entity should be included in this + table independent of how the entity learned the prefix. + (This table isn't limited to prefixes learned from router + + + + advertisements.)" + ::= { ip 32 } + +ipAddressPrefixEntry OBJECT-TYPE + SYNTAX IpAddressPrefixEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry in the ipAddressPrefixTable." + INDEX { ipAddressPrefixIfIndex, ipAddressPrefixType, + ipAddressPrefixPrefix, ipAddressPrefixLength } + ::= { ipAddressPrefixTable 1 } + +IpAddressPrefixEntry ::= SEQUENCE { + ipAddressPrefixIfIndex InterfaceIndex, + ipAddressPrefixType InetAddressType, + ipAddressPrefixPrefix InetAddress, + ipAddressPrefixLength InetAddressPrefixLength, + ipAddressPrefixOrigin IpAddressPrefixOriginTC, + ipAddressPrefixOnLinkFlag TruthValue, + ipAddressPrefixAutonomousFlag TruthValue, + ipAddressPrefixAdvPreferredLifetime Unsigned32, + ipAddressPrefixAdvValidLifetime Unsigned32 + } + +ipAddressPrefixIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface on + which this prefix is configured. The interface identified + by a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipAddressPrefixEntry 1 } + +ipAddressPrefixType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type of ipAddressPrefix." + ::= { ipAddressPrefixEntry 2 } + +ipAddressPrefixPrefix OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + + + + DESCRIPTION + "The address prefix. The address type of this object is + specified in ipAddressPrefixType. The length of this object + is the standard length for objects of that type (4 or 16 + bytes). Any bits after ipAddressPrefixLength must be zero. + + Implementors need to be aware that, if the size of + ipAddressPrefixPrefix exceeds 114 octets, then OIDS of + instances of columns in this row will have more than 128 + sub-identifiers and cannot be accessed using SNMPv1, + SNMPv2c, or SNMPv3." + ::= { ipAddressPrefixEntry 3 } + +ipAddressPrefixLength OBJECT-TYPE + SYNTAX InetAddressPrefixLength + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The prefix length associated with this prefix. + + The value 0 has no special meaning for this object. It + simply refers to address '::/0'." + ::= { ipAddressPrefixEntry 4 } + +ipAddressPrefixOrigin OBJECT-TYPE + SYNTAX IpAddressPrefixOriginTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The origin of this prefix." + ::= { ipAddressPrefixEntry 5 } + +ipAddressPrefixOnLinkFlag OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This object has the value 'true(1)', if this prefix can be + used for on-link determination; otherwise, the value is + 'false(2)'. + + The default for IPv4 prefixes is 'true(1)'." + REFERENCE "For IPv6 RFC 2461, especially sections 2 and 4.6.2 and + RFC 2462" + ::= { ipAddressPrefixEntry 6 } + +ipAddressPrefixAutonomousFlag OBJECT-TYPE + SYNTAX TruthValue + + + + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Autonomous address configuration flag. When true(1), + indicates that this prefix can be used for autonomous + address configuration (i.e., can be used to form a local + interface address). If false(2), it is not used to auto- + configure a local interface address. + + The default for IPv4 prefixes is 'false(2)'." + REFERENCE "For IPv6 RFC 2461, especially sections 2 and 4.6.2 and + RFC 2462" + ::= { ipAddressPrefixEntry 7 } + +ipAddressPrefixAdvPreferredLifetime OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The remaining length of time, in seconds, that this prefix + will continue to be preferred, i.e., time until deprecation. + + A value of 4,294,967,295 represents infinity. + + The address generated from a deprecated prefix should no + longer be used as a source address in new communications, + but packets received on such an interface are processed as + expected. + + The default for IPv4 prefixes is 4,294,967,295 (infinity)." + REFERENCE "For IPv6 RFC 2461, especially sections 2 and 4.6.2 and + RFC 2462" + ::= { ipAddressPrefixEntry 8 } + +ipAddressPrefixAdvValidLifetime OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The remaining length of time, in seconds, that this prefix + will continue to be valid, i.e., time until invalidation. A + value of 4,294,967,295 represents infinity. + + The address generated from an invalidated prefix should not + appear as the destination or source address of a packet. + + + + + The default for IPv4 prefixes is 4,294,967,295 (infinity)." + REFERENCE "For IPv6 RFC 2461, especially sections 2 and 4.6.2 and + RFC 2462" + ::= { ipAddressPrefixEntry 9 } + +-- +-- Internet Address Table +-- + +ipAddressSpinLock OBJECT-TYPE + SYNTAX TestAndIncr + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "An advisory lock used to allow cooperating SNMP managers to + coordinate their use of the set operation in creating or + modifying rows within this table. + + In order to use this lock to coordinate the use of set + operations, managers should first retrieve + ipAddressTableSpinLock. They should then determine the + appropriate row to create or modify. Finally, they should + issue the appropriate set command, including the retrieved + value of ipAddressSpinLock. If another manager has altered + the table in the meantime, then the value of + ipAddressSpinLock will have changed, and the creation will + fail as it will be specifying an incorrect value for + ipAddressSpinLock. It is suggested, but not required, that + the ipAddressSpinLock be the first var bind for each set of + objects representing a 'row' in a PDU." + ::= { ip 33 } + +ipAddressTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpAddressEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "This table contains addressing information relevant to the + entity's interfaces. + + This table does not contain multicast address information. + Tables for such information should be contained in multicast + specific MIBs, such as RFC 3019. + + While this table is writable, the user will note that + several objects, such as ipAddressOrigin, are not. The + intention in allowing a user to write to this table is to + allow them to add or remove any entry that isn't + + + + permanent. The user should be allowed to modify objects + and entries when that would not cause inconsistencies + within the table. Allowing write access to objects, such + as ipAddressOrigin, could allow a user to insert an entry + and then label it incorrectly. + + Note well: When including IPv6 link-local addresses in this + table, the entry must use an InetAddressType of 'ipv6z' in + order to differentiate between the possible interfaces." + ::= { ip 34 } + +ipAddressEntry OBJECT-TYPE + SYNTAX IpAddressEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An address mapping for a particular interface." + INDEX { ipAddressAddrType, ipAddressAddr } + ::= { ipAddressTable 1 } + +IpAddressEntry ::= SEQUENCE { + ipAddressAddrType InetAddressType, + ipAddressAddr InetAddress, + ipAddressIfIndex InterfaceIndex, + ipAddressType INTEGER, + ipAddressPrefix RowPointer, + ipAddressOrigin IpAddressOriginTC, + ipAddressStatus IpAddressStatusTC, + ipAddressCreated TimeStamp, + ipAddressLastChanged TimeStamp, + ipAddressRowStatus RowStatus, + ipAddressStorageType StorageType + } + +ipAddressAddrType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type of ipAddressAddr." + ::= { ipAddressEntry 1 } + +ipAddressAddr OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IP address to which this entry's addressing information + + + + pertains. The address type of this object is specified in + ipAddressAddrType. + + Implementors need to be aware that if the size of + ipAddressAddr exceeds 116 octets, then OIDS of instances of + columns in this row will have more than 128 sub-identifiers + and cannot be accessed using SNMPv1, SNMPv2c, or SNMPv3." + ::= { ipAddressEntry 2 } + +ipAddressIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface to + which this entry is applicable. The interface identified by + a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipAddressEntry 3 } + +ipAddressType OBJECT-TYPE + SYNTAX INTEGER { + unicast(1), + anycast(2), + broadcast(3) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The type of address. broadcast(3) is not a valid value for + IPv6 addresses (RFC 3513)." + DEFVAL { unicast } + ::= { ipAddressEntry 4 } + +ipAddressPrefix OBJECT-TYPE + SYNTAX RowPointer + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A pointer to the row in the prefix table to which this + address belongs. May be { 0 0 } if there is no such row." + DEFVAL { zeroDotZero } + ::= { ipAddressEntry 5 } + +ipAddressOrigin OBJECT-TYPE + SYNTAX IpAddressOriginTC + MAX-ACCESS read-only + STATUS current + + + + DESCRIPTION + "The origin of the address." + ::= { ipAddressEntry 6 } + +ipAddressStatus OBJECT-TYPE + SYNTAX IpAddressStatusTC + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of the address, describing if the address can be + used for communication. + + In the absence of other information, an IPv4 address is + always preferred(1)." + DEFVAL { preferred } + ::= { ipAddressEntry 7 } + +ipAddressCreated OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the time this entry was created. + If this entry was created prior to the last re- + initialization of the local network management subsystem, + then this object contains a zero value." + ::= { ipAddressEntry 8 } + +ipAddressLastChanged OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the time this entry was last + updated. If this entry was updated prior to the last re- + initialization of the local network management subsystem, + then this object contains a zero value." + ::= { ipAddressEntry 9 } + +ipAddressRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this conceptual row. + + The RowStatus TC requires that this DESCRIPTION clause + states under which circumstances other objects in this row + + + + can be modified. The value of this object has no effect on + whether other objects in this conceptual row can be + modified. + + A conceptual row can not be made active until the + ipAddressIfIndex has been set to a valid index." + ::= { ipAddressEntry 10 } + +ipAddressStorageType OBJECT-TYPE + SYNTAX StorageType + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The storage type for this conceptual row. If this object + has a value of 'permanent', then no other objects are + required to be able to be modified." + DEFVAL { volatile } + ::= { ipAddressEntry 11 } + +-- +-- the Internet Address Translation table +-- + +ipNetToPhysicalTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpNetToPhysicalEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IP Address Translation table used for mapping from IP + addresses to physical addresses. + + The Address Translation tables contain the IP address to + 'physical' address equivalences. Some interfaces do not use + translation tables for determining address equivalences + (e.g., DDN-X.25 has an algorithmic method); if all + interfaces are of this type, then the Address Translation + table is empty, i.e., has zero entries. + + While many protocols may be used to populate this table, ARP + and Neighbor Discovery are the most likely + options." + REFERENCE "RFC 826 and RFC 2461" + ::= { ip 35 } + +ipNetToPhysicalEntry OBJECT-TYPE + SYNTAX IpNetToPhysicalEntry + MAX-ACCESS not-accessible + STATUS current + + + + DESCRIPTION + "Each entry contains one IP address to `physical' address + equivalence." + INDEX { ipNetToPhysicalIfIndex, + ipNetToPhysicalNetAddressType, + ipNetToPhysicalNetAddress } + ::= { ipNetToPhysicalTable 1 } + +IpNetToPhysicalEntry ::= SEQUENCE { + ipNetToPhysicalIfIndex InterfaceIndex, + ipNetToPhysicalNetAddressType InetAddressType, + ipNetToPhysicalNetAddress InetAddress, + ipNetToPhysicalPhysAddress PhysAddress, + ipNetToPhysicalLastUpdated TimeStamp, + ipNetToPhysicalType INTEGER, + ipNetToPhysicalState INTEGER, + ipNetToPhysicalRowStatus RowStatus + } + +ipNetToPhysicalIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface to + which this entry is applicable. The interface identified by + a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipNetToPhysicalEntry 1 } + +ipNetToPhysicalNetAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The type of ipNetToPhysicalNetAddress." + ::= { ipNetToPhysicalEntry 2 } + +ipNetToPhysicalNetAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IP Address corresponding to the media-dependent + `physical' address. The address type of this object is + specified in ipNetToPhysicalAddressType. + + Implementors need to be aware that if the size of + + + + ipNetToPhysicalNetAddress exceeds 115 octets, then OIDS of + instances of columns in this row will have more than 128 + sub-identifiers and cannot be accessed using SNMPv1, + SNMPv2c, or SNMPv3." + ::= { ipNetToPhysicalEntry 3 } + +ipNetToPhysicalPhysAddress OBJECT-TYPE + SYNTAX PhysAddress (SIZE(0..65535)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The media-dependent `physical' address. + + As the entries in this table are typically not persistent + when this object is written the entity SHOULD NOT save the + change to non-volatile storage." + ::= { ipNetToPhysicalEntry 4 } + +ipNetToPhysicalLastUpdated OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the time this entry was last + updated. If this entry was updated prior to the last re- + initialization of the local network management subsystem, + then this object contains a zero value." + ::= { ipNetToPhysicalEntry 5 } + +ipNetToPhysicalType OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + invalid(2), -- an invalidated mapping + dynamic(3), + static(4), + local(5) -- local interface + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The type of mapping. + + Setting this object to the value invalid(2) has the effect + of invalidating the corresponding entry in the + ipNetToPhysicalTable. That is, it effectively dis- + associates the interface identified with said entry from the + mapping identified with said entry. It is an + implementation-specific matter as to whether the agent + + + + removes an invalidated entry from the table. Accordingly, + management stations must be prepared to receive tabular + information from agents that corresponds to entries not + currently in use. Proper interpretation of such entries + requires examination of the relevant ipNetToPhysicalType + object. + + The 'dynamic(3)' type indicates that the IP address to + physical addresses mapping has been dynamically resolved + using e.g., IPv4 ARP or the IPv6 Neighbor Discovery + protocol. + + The 'static(4)' type indicates that the mapping has been + statically configured. Both of these refer to entries that + provide mappings for other entities addresses. + + The 'local(5)' type indicates that the mapping is provided + for an entity's own interface address. + + As the entries in this table are typically not persistent + when this object is written the entity SHOULD NOT save the + change to non-volatile storage." + DEFVAL { static } + ::= { ipNetToPhysicalEntry 6 } + +ipNetToPhysicalState OBJECT-TYPE + SYNTAX INTEGER { + reachable(1), -- confirmed reachability + + stale(2), -- unconfirmed reachability + + delay(3), -- waiting for reachability + -- confirmation before entering + -- the probe state + + probe(4), -- actively probing + + invalid(5), -- an invalidated mapping + + unknown(6), -- state can not be determined + -- for some reason. + + incomplete(7) -- address resolution is being + -- performed. + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + + + + "The Neighbor Unreachability Detection state for the + interface when the address mapping in this entry is used. + If Neighbor Unreachability Detection is not in use (e.g. for + IPv4), this object is always unknown(6)." + REFERENCE "RFC 2461" + ::= { ipNetToPhysicalEntry 7 } + +ipNetToPhysicalRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this conceptual row. + + The RowStatus TC requires that this DESCRIPTION clause + states under which circumstances other objects in this row + can be modified. The value of this object has no effect on + whether other objects in this conceptual row can be + modified. + + A conceptual row can not be made active until the + ipNetToPhysicalPhysAddress object has been set. + + Note that if the ipNetToPhysicalType is set to 'invalid', + the managed node may delete the entry independent of the + state of this object." + ::= { ipNetToPhysicalEntry 8 } + +-- +-- The IPv6 Scope Zone Index Table. +-- + +ipv6ScopeZoneIndexTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ipv6ScopeZoneIndexEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table used to describe IPv6 unicast and multicast scope + zones. + + For those objects that have names rather than numbers, the + names were chosen to coincide with the names used in the + IPv6 address architecture document. " + REFERENCE "Section 2.7 of RFC 4291" + ::= { ip 36 } + +ipv6ScopeZoneIndexEntry OBJECT-TYPE + SYNTAX Ipv6ScopeZoneIndexEntry + + + + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Each entry contains the list of scope identifiers on a given + interface." + INDEX { ipv6ScopeZoneIndexIfIndex } + ::= { ipv6ScopeZoneIndexTable 1 } + +Ipv6ScopeZoneIndexEntry ::= SEQUENCE { + ipv6ScopeZoneIndexIfIndex InterfaceIndex, + ipv6ScopeZoneIndexLinkLocal InetZoneIndex, + ipv6ScopeZoneIndex3 InetZoneIndex, + ipv6ScopeZoneIndexAdminLocal InetZoneIndex, + ipv6ScopeZoneIndexSiteLocal InetZoneIndex, + ipv6ScopeZoneIndex6 InetZoneIndex, + ipv6ScopeZoneIndex7 InetZoneIndex, + ipv6ScopeZoneIndexOrganizationLocal InetZoneIndex, + ipv6ScopeZoneIndex9 InetZoneIndex, + ipv6ScopeZoneIndexA InetZoneIndex, + ipv6ScopeZoneIndexB InetZoneIndex, + ipv6ScopeZoneIndexC InetZoneIndex, + ipv6ScopeZoneIndexD InetZoneIndex + } + +ipv6ScopeZoneIndexIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface to + which these scopes belong. The interface identified by a + particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipv6ScopeZoneIndexEntry 1 } + +ipv6ScopeZoneIndexLinkLocal OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for the link-local scope on this interface." + ::= { ipv6ScopeZoneIndexEntry 2 } + +ipv6ScopeZoneIndex3 OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + + + + "The zone index for scope 3 on this interface." + ::= { ipv6ScopeZoneIndexEntry 3 } + +ipv6ScopeZoneIndexAdminLocal OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for the admin-local scope on this interface." + ::= { ipv6ScopeZoneIndexEntry 4 } + +ipv6ScopeZoneIndexSiteLocal OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for the site-local scope on this interface." + ::= { ipv6ScopeZoneIndexEntry 5 } + +ipv6ScopeZoneIndex6 OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for scope 6 on this interface." + ::= { ipv6ScopeZoneIndexEntry 6 } + +ipv6ScopeZoneIndex7 OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for scope 7 on this interface." + ::= { ipv6ScopeZoneIndexEntry 7 } + +ipv6ScopeZoneIndexOrganizationLocal OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for the organization-local scope on this + interface." + ::= { ipv6ScopeZoneIndexEntry 8 } + +ipv6ScopeZoneIndex9 OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + + + + DESCRIPTION + "The zone index for scope 9 on this interface." + ::= { ipv6ScopeZoneIndexEntry 9 } + +ipv6ScopeZoneIndexA OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for scope A on this interface." + ::= { ipv6ScopeZoneIndexEntry 10 } + +ipv6ScopeZoneIndexB OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for scope B on this interface." + ::= { ipv6ScopeZoneIndexEntry 11 } + +ipv6ScopeZoneIndexC OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for scope C on this interface." + ::= { ipv6ScopeZoneIndexEntry 12 } + +ipv6ScopeZoneIndexD OBJECT-TYPE + SYNTAX InetZoneIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The zone index for scope D on this interface." + ::= { ipv6ScopeZoneIndexEntry 13 } + +-- +-- The Default Router Table +-- This table simply lists the default routers; for more information +-- about routing tables, see the routing MIBs +-- + +ipDefaultRouterTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpDefaultRouterEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table used to describe the default routers known to this + + + + entity." + ::= { ip 37 } + +ipDefaultRouterEntry OBJECT-TYPE + SYNTAX IpDefaultRouterEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Each entry contains information about a default router known + to this entity." + INDEX {ipDefaultRouterAddressType, ipDefaultRouterAddress, + ipDefaultRouterIfIndex} + ::= { ipDefaultRouterTable 1 } + +IpDefaultRouterEntry ::= SEQUENCE { + ipDefaultRouterAddressType InetAddressType, + ipDefaultRouterAddress InetAddress, + ipDefaultRouterIfIndex InterfaceIndex, + ipDefaultRouterLifetime Unsigned32, + ipDefaultRouterPreference INTEGER + } + +ipDefaultRouterAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type for this row." + ::= { ipDefaultRouterEntry 1 } + +ipDefaultRouterAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IP address of the default router represented by this + row. The address type of this object is specified in + ipDefaultRouterAddressType. + + Implementers need to be aware that if the size of + ipDefaultRouterAddress exceeds 115 octets, then OIDS of + instances of columns in this row will have more than 128 + sub-identifiers and cannot be accessed using SNMPv1, + SNMPv2c, or SNMPv3." + ::= { ipDefaultRouterEntry 2 } + +ipDefaultRouterIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + + + + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface by + which the router can be reached. The interface identified + by a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipDefaultRouterEntry 3 } + +ipDefaultRouterLifetime OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The remaining length of time, in seconds, that this router + will continue to be useful as a default router. A value of + zero indicates that it is no longer useful as a default + router. It is left to the implementer of the MIB as to + whether a router with a lifetime of zero is removed from the + list. + + For IPv6, this value should be extracted from the router + advertisement messages." + REFERENCE "For IPv6 RFC 2462 sections 4.2 and 6.3.4" + ::= { ipDefaultRouterEntry 4 } + +ipDefaultRouterPreference OBJECT-TYPE + SYNTAX INTEGER { + reserved (-2), + low (-1), + medium (0), + high (1) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An indication of preference given to this router as a + default router as described in he Default Router + Preferences document. Treating the value as a + 2 bit signed integer allows for simple arithmetic + comparisons. + + For IPv4 routers or IPv6 routers that are not using the + updated router advertisement format, this object is set to + medium (0)." + REFERENCE "RFC 4291, section 2.1" + ::= { ipDefaultRouterEntry 5 } + + + +-- +-- Configuration information for constructing router advertisements +-- + +ipv6RouterAdvertSpinLock OBJECT-TYPE + SYNTAX TestAndIncr + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "An advisory lock used to allow cooperating SNMP managers to + coordinate their use of the set operation in creating or + modifying rows within this table. + + In order to use this lock to coordinate the use of set + operations, managers should first retrieve + ipv6RouterAdvertSpinLock. They should then determine the + appropriate row to create or modify. Finally, they should + issue the appropriate set command including the retrieved + value of ipv6RouterAdvertSpinLock. If another manager has + altered the table in the meantime, then the value of + ipv6RouterAdvertSpinLock will have changed and the creation + will fail as it will be specifying an incorrect value for + ipv6RouterAdvertSpinLock. It is suggested, but not + required, that the ipv6RouterAdvertSpinLock be the first var + bind for each set of objects representing a 'row' in a PDU." + ::= { ip 38 } + +ipv6RouterAdvertTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ipv6RouterAdvertEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table containing information used to construct router + advertisements." + ::= { ip 39 } + +ipv6RouterAdvertEntry OBJECT-TYPE + SYNTAX Ipv6RouterAdvertEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry containing information used to construct router + advertisements. + + Information in this table is persistent, and when this + object is written, the entity SHOULD save the change to + non-volatile storage." + INDEX { ipv6RouterAdvertIfIndex } + + + + ::= { ipv6RouterAdvertTable 1 } + +Ipv6RouterAdvertEntry ::= SEQUENCE { + ipv6RouterAdvertIfIndex InterfaceIndex, + ipv6RouterAdvertSendAdverts TruthValue, + ipv6RouterAdvertMaxInterval Unsigned32, + ipv6RouterAdvertMinInterval Unsigned32, + ipv6RouterAdvertManagedFlag TruthValue, + ipv6RouterAdvertOtherConfigFlag TruthValue, + ipv6RouterAdvertLinkMTU Unsigned32, + ipv6RouterAdvertReachableTime Unsigned32, + ipv6RouterAdvertRetransmitTime Unsigned32, + ipv6RouterAdvertCurHopLimit Unsigned32, + ipv6RouterAdvertDefaultLifetime Unsigned32, + ipv6RouterAdvertRowStatus RowStatus + } + +ipv6RouterAdvertIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index value that uniquely identifies the interface on + which router advertisements constructed with this + information will be transmitted. The interface identified + by a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipv6RouterAdvertEntry 1 } + +ipv6RouterAdvertSendAdverts OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "A flag indicating whether the router sends periodic + router advertisements and responds to router solicitations + on this interface." + REFERENCE "RFC 2461 Section 6.2.1" + DEFVAL { false } + ::= { ipv6RouterAdvertEntry 2 } + +ipv6RouterAdvertMaxInterval OBJECT-TYPE + SYNTAX Unsigned32 (4..1800) + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The maximum time allowed between sending unsolicited router + + + + advertisements from this interface." + REFERENCE "RFC 2461 Section 6.2.1" + DEFVAL { 600 } + ::= { ipv6RouterAdvertEntry 3 } + +ipv6RouterAdvertMinInterval OBJECT-TYPE + SYNTAX Unsigned32 (3..1350) + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The minimum time allowed between sending unsolicited router + advertisements from this interface. + + The default is 0.33 * ipv6RouterAdvertMaxInterval, however, + in the case of a low value for ipv6RouterAdvertMaxInterval, + the minimum value for this object is restricted to 3." + REFERENCE "RFC 2461 Section 6.2.1" + ::= { ipv6RouterAdvertEntry 4 } + +ipv6RouterAdvertManagedFlag OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The true/false value to be placed into the 'managed address + configuration' flag field in router advertisements sent from + this interface." + REFERENCE "RFC 2461 Section 6.2.1" + DEFVAL { false } + ::= { ipv6RouterAdvertEntry 5 } + +ipv6RouterAdvertOtherConfigFlag OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The true/false value to be placed into the 'other stateful + configuration' flag field in router advertisements sent from + this interface." + REFERENCE "RFC 2461 Section 6.2.1" + DEFVAL { false } + ::= { ipv6RouterAdvertEntry 6 } + +ipv6RouterAdvertLinkMTU OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-create + STATUS current + + + + DESCRIPTION + "The value to be placed in MTU options sent by the router on + this interface. + + A value of zero indicates that no MTU options are sent." + REFERENCE "RFC 2461 Section 6.2.1" + DEFVAL { 0 } + ::= { ipv6RouterAdvertEntry 7 } + +ipv6RouterAdvertReachableTime OBJECT-TYPE + SYNTAX Unsigned32 (0..3600000) + UNITS "milliseconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The value to be placed in the reachable time field in router + advertisement messages sent from this interface. + + A value of zero in the router advertisement indicates that + the advertisement isn't specifying a value for reachable + time." + REFERENCE "RFC 2461 Section 6.2.1" + DEFVAL { 0 } + ::= { ipv6RouterAdvertEntry 8 } + +ipv6RouterAdvertRetransmitTime OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The value to be placed in the retransmit timer field in + router advertisements sent from this interface. + + A value of zero in the router advertisement indicates that + the advertisement isn't specifying a value for retrans + time." + REFERENCE "RFC 2461 Section 6.2.1" + DEFVAL { 0 } + ::= { ipv6RouterAdvertEntry 9 } + +ipv6RouterAdvertCurHopLimit OBJECT-TYPE + SYNTAX Unsigned32 (0..255) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The default value to be placed in the current hop limit + field in router advertisements sent from this interface. + + + + The value should be set to the current diameter of the + Internet. + + A value of zero in the router advertisement indicates that + the advertisement isn't specifying a value for curHopLimit. + + The default should be set to the value specified in the IANA + web pages (www.iana.org) at the time of implementation." + REFERENCE "RFC 2461 Section 6.2.1" + ::= { ipv6RouterAdvertEntry 10 } + +ipv6RouterAdvertDefaultLifetime OBJECT-TYPE + SYNTAX Unsigned32 (0|4..9000) + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The value to be placed in the router lifetime field of + router advertisements sent from this interface. This value + MUST be either 0 or between ipv6RouterAdvertMaxInterval and + 9000 seconds. + + A value of zero indicates that the router is not to be used + as a default router. + + The default is 3 * ipv6RouterAdvertMaxInterval." + REFERENCE "RFC 2461 Section 6.2.1" + ::= { ipv6RouterAdvertEntry 11 } + +ipv6RouterAdvertRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this conceptual row. + + As all objects in this conceptual row have default values, a + row can be created and made active by setting this object + appropriately. + + The RowStatus TC requires that this DESCRIPTION clause + states under which circumstances other objects in this row + can be modified. The value of this object has no effect on + whether other objects in this conceptual row can be + modified." + ::= { ipv6RouterAdvertEntry 12 } + +-- + + + +-- ICMP section +-- + +icmp OBJECT IDENTIFIER ::= { mib-2 5 } + +-- +-- ICMP non-message-specific counters +-- + +-- These object IDs are reserved, as they were used in earlier +-- versions of the MIB module. In theory, OIDs are not assigned +-- until the specification is released as an RFC; however, as some +-- companies may have shipped code based on earlier versions of +-- the MIB, it seems best to reserve these OIDs. +-- ::= { icmp 27 } +-- ::= { icmp 28 } + +icmpStatsTable OBJECT-TYPE + SYNTAX SEQUENCE OF IcmpStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table of generic system-wide ICMP counters." + ::= { icmp 29 } + +icmpStatsEntry OBJECT-TYPE + SYNTAX IcmpStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A conceptual row in the icmpStatsTable." + INDEX { icmpStatsIPVersion } + ::= { icmpStatsTable 1 } + +IcmpStatsEntry ::= SEQUENCE { + icmpStatsIPVersion InetVersion, + icmpStatsInMsgs Counter32, + icmpStatsInErrors Counter32, + icmpStatsOutMsgs Counter32, + icmpStatsOutErrors Counter32 + } + +icmpStatsIPVersion OBJECT-TYPE + SYNTAX InetVersion + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IP version of the statistics." + + + + ::= { icmpStatsEntry 1 } + +icmpStatsInMsgs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of ICMP messages that the entity received. + Note that this counter includes all those counted by + icmpStatsInErrors." + ::= { icmpStatsEntry 2 } + +icmpStatsInErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of ICMP messages that the entity received but + determined as having ICMP-specific errors (bad ICMP + checksums, bad length, etc.)." + ::= { icmpStatsEntry 3 } + +icmpStatsOutMsgs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of ICMP messages that the entity attempted + to send. Note that this counter includes all those counted + by icmpStatsOutErrors." + ::= { icmpStatsEntry 4 } + +icmpStatsOutErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of ICMP messages that this entity did not send + due to problems discovered within ICMP, such as a lack of + buffers. This value should not include errors discovered + outside the ICMP layer, such as the inability of IP to route + the resultant datagram. In some implementations, there may + be no types of error that contribute to this counter's + value." + ::= { icmpStatsEntry 5 } + +-- +-- per-version, per-message type ICMP counters + + + +-- + +icmpMsgStatsTable OBJECT-TYPE + SYNTAX SEQUENCE OF IcmpMsgStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table of system-wide per-version, per-message type ICMP + counters." + ::= { icmp 30 } + +icmpMsgStatsEntry OBJECT-TYPE + SYNTAX IcmpMsgStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A conceptual row in the icmpMsgStatsTable. + + The system should track each ICMP type value, even if that + ICMP type is not supported by the system. However, a + given row need not be instantiated unless a message of that + type has been processed, i.e., the row for + icmpMsgStatsType=X MAY be instantiated before but MUST be + instantiated after the first message with Type=X is + received or transmitted. After receiving or transmitting + any succeeding messages with Type=X, the relevant counter + must be incremented." + INDEX { icmpMsgStatsIPVersion, icmpMsgStatsType } + ::= { icmpMsgStatsTable 1 } + +IcmpMsgStatsEntry ::= SEQUENCE { + icmpMsgStatsIPVersion InetVersion, + icmpMsgStatsType Integer32, + icmpMsgStatsInPkts Counter32, + icmpMsgStatsOutPkts Counter32 + } + +icmpMsgStatsIPVersion OBJECT-TYPE + SYNTAX InetVersion + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IP version of the statistics." + ::= { icmpMsgStatsEntry 1 } + +icmpMsgStatsType OBJECT-TYPE + SYNTAX Integer32 (0..255) + MAX-ACCESS not-accessible + + + + STATUS current + DESCRIPTION + "The ICMP type field of the message type being counted by + this row. + + Note that ICMP message types are scoped by the address type + in use." + REFERENCE "http://www.iana.org/assignments/icmp-parameters and + http://www.iana.org/assignments/icmpv6-parameters" + ::= { icmpMsgStatsEntry 2 } + +icmpMsgStatsInPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of input packets for this AF and type." + ::= { icmpMsgStatsEntry 3 } + +icmpMsgStatsOutPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of output packets for this AF and type." + ::= { icmpMsgStatsEntry 4 } +-- +-- conformance information +-- + +ipMIBConformance OBJECT IDENTIFIER ::= { ipMIB 2 } + +ipMIBCompliances OBJECT IDENTIFIER ::= { ipMIBConformance 1 } +ipMIBGroups OBJECT IDENTIFIER ::= { ipMIBConformance 2 } + +-- compliance statements +ipMIBCompliance2 MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for systems that implement IP - + either IPv4 or IPv6. + + There are a number of INDEX objects that cannot be + represented in the form of OBJECT clauses in SMIv2, but + for which we have the following compliance requirements, + expressed in OBJECT clause form in this description + clause: + + + + + -- OBJECT ipSystemStatsIPVersion + -- SYNTAX InetVersion {ipv4(1), ipv6(2)} + -- DESCRIPTION + -- This MIB requires support for only IPv4 and IPv6 + -- versions. + -- + -- OBJECT ipIfStatsIPVersion + -- SYNTAX InetVersion {ipv4(1), ipv6(2)} + -- DESCRIPTION + -- This MIB requires support for only IPv4 and IPv6 + -- versions. + -- + -- OBJECT icmpStatsIPVersion + -- SYNTAX InetVersion {ipv4(1), ipv6(2)} + -- DESCRIPTION + -- This MIB requires support for only IPv4 and IPv6 + -- versions. + -- + -- OBJECT icmpMsgStatsIPVersion + -- SYNTAX InetVersion {ipv4(1), ipv6(2)} + -- DESCRIPTION + -- This MIB requires support for only IPv4 and IPv6 + -- versions. + -- + -- OBJECT ipAddressPrefixType + -- SYNTAX InetAddressType {ipv4(1), ipv6(2)} + -- DESCRIPTION + -- This MIB requires support for only global IPv4 and + -- IPv6 address types. + -- + -- OBJECT ipAddressPrefixPrefix + -- SYNTAX InetAddress (Size(4 | 16)) + -- DESCRIPTION + -- This MIB requires support for only global IPv4 and + -- IPv6 addresses and so the size can be either 4 or + -- 16 bytes. + -- + -- OBJECT ipAddressAddrType + -- SYNTAX InetAddressType {ipv4(1), ipv6(2), + -- ipv4z(3), ipv6z(4)} + -- DESCRIPTION + -- This MIB requires support for only global and + -- non-global IPv4 and IPv6 address types. + -- + -- OBJECT ipAddressAddr + -- SYNTAX InetAddress (Size(4 | 8 | 16 | 20)) + -- DESCRIPTION + -- This MIB requires support for only global and + + + + -- non-global IPv4 and IPv6 addresses and so the size + -- can be 4, 8, 16, or 20 bytes. + -- + -- OBJECT ipNetToPhysicalNetAddressType + -- SYNTAX InetAddressType {ipv4(1), ipv6(2), + -- ipv4z(3), ipv6z(4)} + -- DESCRIPTION + -- This MIB requires support for only global and + -- non-global IPv4 and IPv6 address types. + -- + -- OBJECT ipNetToPhysicalNetAddress + -- SYNTAX InetAddress (Size(4 | 8 | 16 | 20)) + -- DESCRIPTION + -- This MIB requires support for only global and + -- non-global IPv4 and IPv6 addresses and so the size + -- can be 4, 8, 16, or 20 bytes. + -- + -- OBJECT ipDefaultRouterAddressType + -- SYNTAX InetAddressType {ipv4(1), ipv6(2), + -- ipv4z(3), ipv6z(4)} + -- DESCRIPTION + -- This MIB requires support for only global and + -- non-global IPv4 and IPv6 address types. + -- + -- OBJECT ipDefaultRouterAddress + -- SYNTAX InetAddress (Size(4 | 8 | 16 | 20)) + -- DESCRIPTION + -- This MIB requires support for only global and + -- non-global IPv4 and IPv6 addresses and so the size + -- can be 4, 8, 16, or 20 bytes." + + MODULE -- this module + + MANDATORY-GROUPS { ipSystemStatsGroup, ipAddressGroup, + ipNetToPhysicalGroup, ipDefaultRouterGroup, + icmpStatsGroup } + + GROUP ipSystemStatsHCOctetGroup + DESCRIPTION + "This group is mandatory for systems that have an aggregate + bandwidth of greater than 20MB. Including this group does + not allow an entity to neglect the 32 bit versions of these + objects." + + GROUP ipSystemStatsHCPacketGroup + DESCRIPTION + "This group is mandatory for systems that have an aggregate + bandwidth of greater than 650MB. Including this group + + + + does not allow an entity to neglect the 32 bit versions of + these objects." + + GROUP ipIfStatsGroup + DESCRIPTION + "This group is optional for all systems." + + GROUP ipIfStatsHCOctetGroup + DESCRIPTION + "This group is mandatory for systems that include the + ipIfStatsGroup and include links with bandwidths of greater + than 20MB. Including this group does not allow an entity to + neglect the 32 bit versions of these objects." + + GROUP ipIfStatsHCPacketGroup + DESCRIPTION + "This group is mandatory for systems that include the + ipIfStatsGroup and include links with bandwidths of greater + than 650MB. Including this group does not allow an entity + to neglect the 32 bit versions of these objects." + + GROUP ipv4GeneralGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv4." + + GROUP ipv4IfGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv4." + + GROUP ipv4SystemStatsGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv4." + + GROUP ipv4SystemStatsHCPacketGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv4 and + that have an aggregate bandwidth of greater than 650MB. + Including this group does not allow an entity to neglect the + 32 bit versions of these objects." + + GROUP ipv4IfStatsGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv4 and + including the ipIfStatsGroup." + + GROUP ipv4IfStatsHCPacketGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv4 and + + + + including the ipIfStatsHCPacketGroup. Including this group + does not allow an entity to neglect the 32 bit versions of + these objects." + + GROUP ipv6GeneralGroup2 + DESCRIPTION + "This group is mandatory for all systems supporting IPv6." + + GROUP ipv6IfGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv6." + + GROUP ipAddressPrefixGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv6." + + GROUP ipv6ScopeGroup + DESCRIPTION + "This group is mandatory for all systems supporting IPv6." + + GROUP ipv6RouterAdvertGroup + DESCRIPTION + "This group is mandatory for all IPv6 routers." + + GROUP ipLastChangeGroup + DESCRIPTION + "This group is optional for all agents." + + OBJECT ipv6IpForwarding + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6IpDefaultHopLimit + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv4InterfaceEnableStatus + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6InterfaceEnableStatus + MIN-ACCESS read-only + + + + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6InterfaceForwarding + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipAddressSpinLock + MIN-ACCESS not-accessible + DESCRIPTION + "An agent is not required to provide write access to this + object. However, if an agent provides write access to any + of the other objects in the ipAddressGroup, it SHOULD + provide write access to this object as well." + + OBJECT ipAddressIfIndex + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write or create access + to this object." + + OBJECT ipAddressType + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write or create access + to this object." + + OBJECT ipAddressStatus + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write or create access + to this object." + + OBJECT ipAddressRowStatus + SYNTAX RowStatus { active(1) } + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write or create access + to this object." + + OBJECT ipAddressStorageType + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write or create access + to this object. + + + + If an agent allows this object to be written or created, it + is not required to allow this object to be set to readOnly, + permanent, or nonVolatile." + + OBJECT ipNetToPhysicalPhysAddress + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write or create access + to this object." + + OBJECT ipNetToPhysicalType + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write or create access + to this object." + + OBJECT ipv6RouterAdvertSpinLock + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object. However, if an agent provides write access to + any of the other objects in the ipv6RouterAdvertGroup, it + SHOULD provide write access to this object as well." + + OBJECT ipv6RouterAdvertSendAdverts + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertMaxInterval + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertMinInterval + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertManagedFlag + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + + + + OBJECT ipv6RouterAdvertOtherConfigFlag + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertLinkMTU + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertReachableTime + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertRetransmitTime + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertCurHopLimit + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertDefaultLifetime + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write access to this + object." + + OBJECT ipv6RouterAdvertRowStatus + MIN-ACCESS read-only + DESCRIPTION + "An agent is not required to provide write or create access + to this object." + + ::= { ipMIBCompliances 2 } + +-- units of conformance + +ipv4GeneralGroup OBJECT-GROUP + OBJECTS { ipForwarding, ipDefaultTTL, ipReasmTimeout } + + + + STATUS current + DESCRIPTION + "The group of IPv4-specific objects for basic management of + IPv4 entities." + ::= { ipMIBGroups 3 } + +ipv4IfGroup OBJECT-GROUP + OBJECTS { ipv4InterfaceReasmMaxSize, ipv4InterfaceEnableStatus, + ipv4InterfaceRetransmitTime } + STATUS current + DESCRIPTION + "The group of IPv4-specific objects for basic management of + IPv4 interfaces." + ::= { ipMIBGroups 4 } + +ipv6GeneralGroup2 OBJECT-GROUP + OBJECTS { ipv6IpForwarding, ipv6IpDefaultHopLimit } + STATUS current + DESCRIPTION + "The IPv6 group of objects providing for basic management of + IPv6 entities." + ::= { ipMIBGroups 5 } + +ipv6IfGroup OBJECT-GROUP + OBJECTS { ipv6InterfaceReasmMaxSize, ipv6InterfaceIdentifier, + ipv6InterfaceEnableStatus, ipv6InterfaceReachableTime, + ipv6InterfaceRetransmitTime, ipv6InterfaceForwarding } + STATUS current + DESCRIPTION + "The group of IPv6-specific objects for basic management of + IPv6 interfaces." + ::= { ipMIBGroups 6 } + +ipLastChangeGroup OBJECT-GROUP + OBJECTS { ipv4InterfaceTableLastChange, + ipv6InterfaceTableLastChange, + ipIfStatsTableLastChange } + STATUS current + DESCRIPTION + "The last change objects associated with this MIB. These + objects are optional for all agents. They SHOULD be + implemented on agents where it is possible to determine the + proper values. Where it is not possible to determine the + proper values, for example when the tables are split amongst + several sub-agents using AgentX, the agent MUST NOT + implement these objects to return an incorrect or static + value." + ::= { ipMIBGroups 7 } + + + +ipSystemStatsGroup OBJECT-GROUP + OBJECTS { ipSystemStatsInReceives, + ipSystemStatsInOctets, + ipSystemStatsInHdrErrors, + ipSystemStatsInNoRoutes, + ipSystemStatsInAddrErrors, + ipSystemStatsInUnknownProtos, + ipSystemStatsInTruncatedPkts, + ipSystemStatsInForwDatagrams, + ipSystemStatsReasmReqds, + ipSystemStatsReasmOKs, + ipSystemStatsReasmFails, + ipSystemStatsInDiscards, + ipSystemStatsInDelivers, + ipSystemStatsOutRequests, + ipSystemStatsOutNoRoutes, + ipSystemStatsOutForwDatagrams, + ipSystemStatsOutDiscards, + ipSystemStatsOutFragReqds, + ipSystemStatsOutFragOKs, + ipSystemStatsOutFragFails, + ipSystemStatsOutFragCreates, + ipSystemStatsOutTransmits, + ipSystemStatsOutOctets, + ipSystemStatsInMcastPkts, + ipSystemStatsInMcastOctets, + ipSystemStatsOutMcastPkts, + ipSystemStatsOutMcastOctets, + ipSystemStatsDiscontinuityTime, + ipSystemStatsRefreshRate } + STATUS current + DESCRIPTION + "IP system wide statistics." + ::= { ipMIBGroups 8 } + +ipv4SystemStatsGroup OBJECT-GROUP + OBJECTS { ipSystemStatsInBcastPkts, ipSystemStatsOutBcastPkts } + STATUS current + DESCRIPTION + "IPv4 only system wide statistics." + ::= { ipMIBGroups 9 } + +ipSystemStatsHCOctetGroup OBJECT-GROUP + OBJECTS { ipSystemStatsHCInOctets, + ipSystemStatsHCOutOctets, + ipSystemStatsHCInMcastOctets, + ipSystemStatsHCOutMcastOctets +} + + + + STATUS current + DESCRIPTION + "IP system wide statistics for systems that may overflow the + standard octet counters within 1 hour." + ::= { ipMIBGroups 10 } + +ipSystemStatsHCPacketGroup OBJECT-GROUP + OBJECTS { ipSystemStatsHCInReceives, + ipSystemStatsHCInForwDatagrams, + ipSystemStatsHCInDelivers, + ipSystemStatsHCOutRequests, + ipSystemStatsHCOutForwDatagrams, + ipSystemStatsHCOutTransmits, + ipSystemStatsHCInMcastPkts, + ipSystemStatsHCOutMcastPkts +} + STATUS current + DESCRIPTION + "IP system wide statistics for systems that may overflow the + standard packet counters within 1 hour." + ::= { ipMIBGroups 11 } + +ipv4SystemStatsHCPacketGroup OBJECT-GROUP + OBJECTS { ipSystemStatsHCInBcastPkts, + ipSystemStatsHCOutBcastPkts } + STATUS current + DESCRIPTION + "IPv4 only system wide statistics for systems that may + overflow the standard packet counters within 1 hour." + ::= { ipMIBGroups 12 } + +ipIfStatsGroup OBJECT-GROUP + OBJECTS { ipIfStatsInReceives, ipIfStatsInOctets, + ipIfStatsInHdrErrors, ipIfStatsInNoRoutes, + ipIfStatsInAddrErrors, ipIfStatsInUnknownProtos, + ipIfStatsInTruncatedPkts, ipIfStatsInForwDatagrams, + ipIfStatsReasmReqds, ipIfStatsReasmOKs, + ipIfStatsReasmFails, ipIfStatsInDiscards, + ipIfStatsInDelivers, ipIfStatsOutRequests, + ipIfStatsOutForwDatagrams, ipIfStatsOutDiscards, + ipIfStatsOutFragReqds, ipIfStatsOutFragOKs, + ipIfStatsOutFragFails, ipIfStatsOutFragCreates, + ipIfStatsOutTransmits, ipIfStatsOutOctets, + ipIfStatsInMcastPkts, ipIfStatsInMcastOctets, + ipIfStatsOutMcastPkts, ipIfStatsOutMcastOctets, + ipIfStatsDiscontinuityTime, ipIfStatsRefreshRate } + STATUS current + DESCRIPTION + + + + "IP per-interface statistics." + ::= { ipMIBGroups 13 } + +ipv4IfStatsGroup OBJECT-GROUP + OBJECTS { ipIfStatsInBcastPkts, ipIfStatsOutBcastPkts } + STATUS current + DESCRIPTION + "IPv4 only per-interface statistics." + ::= { ipMIBGroups 14 } + +ipIfStatsHCOctetGroup OBJECT-GROUP + OBJECTS { ipIfStatsHCInOctets, ipIfStatsHCOutOctets, + ipIfStatsHCInMcastOctets, ipIfStatsHCOutMcastOctets } + STATUS current + DESCRIPTION + "IP per-interfaces statistics for systems that include + interfaces that may overflow the standard octet + counters within 1 hour." + ::= { ipMIBGroups 15 } + +ipIfStatsHCPacketGroup OBJECT-GROUP + OBJECTS { ipIfStatsHCInReceives, ipIfStatsHCInForwDatagrams, + ipIfStatsHCInDelivers, ipIfStatsHCOutRequests, + ipIfStatsHCOutForwDatagrams, ipIfStatsHCOutTransmits, + ipIfStatsHCInMcastPkts, ipIfStatsHCOutMcastPkts } + STATUS current + DESCRIPTION + "IP per-interfaces statistics for systems that include + interfaces that may overflow the standard packet counters + within 1 hour." + ::= { ipMIBGroups 16 } + +ipv4IfStatsHCPacketGroup OBJECT-GROUP + OBJECTS { ipIfStatsHCInBcastPkts, ipIfStatsHCOutBcastPkts } + STATUS current + DESCRIPTION + "IPv4 only per-interface statistics for systems that include + interfaces that may overflow the standard packet counters + within 1 hour." + ::= { ipMIBGroups 17 } + +ipAddressPrefixGroup OBJECT-GROUP + OBJECTS { ipAddressPrefixOrigin, + ipAddressPrefixOnLinkFlag, + ipAddressPrefixAutonomousFlag, + ipAddressPrefixAdvPreferredLifetime, + ipAddressPrefixAdvValidLifetime } + STATUS current + + + + DESCRIPTION + "The group of objects for providing information about address + prefixes used by this node." + ::= { ipMIBGroups 18 } + +ipAddressGroup OBJECT-GROUP + OBJECTS { ipAddressSpinLock, ipAddressIfIndex, + ipAddressType, ipAddressPrefix, + ipAddressOrigin, ipAddressStatus, + ipAddressCreated, ipAddressLastChanged, + ipAddressRowStatus, ipAddressStorageType } + STATUS current + DESCRIPTION + "The group of objects for providing information about the + addresses relevant to this entity's interfaces." + ::= { ipMIBGroups 19 } + +ipNetToPhysicalGroup OBJECT-GROUP + OBJECTS { ipNetToPhysicalPhysAddress, ipNetToPhysicalLastUpdated, + ipNetToPhysicalType, ipNetToPhysicalState, + ipNetToPhysicalRowStatus } + STATUS current + DESCRIPTION + "The group of objects for providing information about the + mappings of network address to physical address known to + this node." + ::= { ipMIBGroups 20 } + +ipv6ScopeGroup OBJECT-GROUP + OBJECTS { ipv6ScopeZoneIndexLinkLocal, + ipv6ScopeZoneIndex3, + ipv6ScopeZoneIndexAdminLocal, + ipv6ScopeZoneIndexSiteLocal, + ipv6ScopeZoneIndex6, + ipv6ScopeZoneIndex7, + ipv6ScopeZoneIndexOrganizationLocal, + ipv6ScopeZoneIndex9, + ipv6ScopeZoneIndexA, + ipv6ScopeZoneIndexB, + ipv6ScopeZoneIndexC, + ipv6ScopeZoneIndexD } + STATUS current + DESCRIPTION + "The group of objects for managing IPv6 scope zones." + ::= { ipMIBGroups 21 } + +ipDefaultRouterGroup OBJECT-GROUP + OBJECTS { ipDefaultRouterLifetime, ipDefaultRouterPreference } + + + + STATUS current + DESCRIPTION + "The group of objects for providing information about default + routers known to this node." + ::= { ipMIBGroups 22 } + +ipv6RouterAdvertGroup OBJECT-GROUP + OBJECTS { ipv6RouterAdvertSpinLock, + ipv6RouterAdvertSendAdverts, + ipv6RouterAdvertMaxInterval, + ipv6RouterAdvertMinInterval, + ipv6RouterAdvertManagedFlag, + ipv6RouterAdvertOtherConfigFlag, + ipv6RouterAdvertLinkMTU, + ipv6RouterAdvertReachableTime, + ipv6RouterAdvertRetransmitTime, + ipv6RouterAdvertCurHopLimit, + ipv6RouterAdvertDefaultLifetime, + ipv6RouterAdvertRowStatus +} + STATUS current + DESCRIPTION + "The group of objects for controlling information advertised + by IPv6 routers." + ::= { ipMIBGroups 23 } + +icmpStatsGroup OBJECT-GROUP + OBJECTS {icmpStatsInMsgs, icmpStatsInErrors, + icmpStatsOutMsgs, icmpStatsOutErrors, + icmpMsgStatsInPkts, icmpMsgStatsOutPkts } + STATUS current + DESCRIPTION + "The group of objects providing ICMP statistics." + ::= { ipMIBGroups 24 } + +-- +-- Deprecated objects +-- + +ipInReceives OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The total number of input datagrams received from + interfaces, including those received in error. + + This object has been deprecated, as a new IP version-neutral + + + + table has been added. It is loosely replaced by + ipSystemStatsInRecieves." + ::= { ip 3 } + +ipInHdrErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of input datagrams discarded due to errors in + their IPv4 headers, including bad checksums, version number + mismatch, other format errors, time-to-live exceeded, errors + discovered in processing their IPv4 options, etc. + + This object has been deprecated as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsInHdrErrors." + ::= { ip 4 } + +ipInAddrErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of input datagrams discarded because the IPv4 + address in their IPv4 header's destination field was not a + valid address to be received at this entity. This count + includes invalid addresses (e.g., 0.0.0.0) and addresses of + unsupported Classes (e.g., Class E). For entities which are + not IPv4 routers, and therefore do not forward datagrams, + this counter includes datagrams discarded because the + destination address was not a local address. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsInAddrErrors." + ::= { ip 5 } + +ipForwDatagrams OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of input datagrams for which this entity was not + their final IPv4 destination, as a result of which an + attempt was made to find a route to forward them to that + final destination. In entities which do not act as IPv4 + routers, this counter will include only those packets which + + + + were Source-Routed via this entity, and the Source-Route + option processing was successful. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsInForwDatagrams." + ::= { ip 6 } + +ipInUnknownProtos OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of locally-addressed datagrams received + successfully but discarded because of an unknown or + unsupported protocol. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsInUnknownProtos." + ::= { ip 7 } + +ipInDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of input IPv4 datagrams for which no problems + were encountered to prevent their continued processing, but + which were discarded (e.g., for lack of buffer space). Note + that this counter does not include any datagrams discarded + while awaiting re-assembly. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsInDiscards." + ::= { ip 8 } + +ipInDelivers OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The total number of input datagrams successfully delivered + to IPv4 user-protocols (including ICMP). + + This object has been deprecated as a new IP version neutral + table has been added. It is loosely replaced by + + + + ipSystemStatsIndelivers." + ::= { ip 9 } + +ipOutRequests OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The total number of IPv4 datagrams which local IPv4 user + protocols (including ICMP) supplied to IPv4 in requests for + transmission. Note that this counter does not include any + datagrams counted in ipForwDatagrams. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsOutRequests." + ::= { ip 10 } + +ipOutDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of output IPv4 datagrams for which no problem was + encountered to prevent their transmission to their + destination, but which were discarded (e.g., for lack of + buffer space). Note that this counter would include + datagrams counted in ipForwDatagrams if any such packets met + this (discretionary) discard criterion. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsOutDiscards." + ::= { ip 11 } + +ipOutNoRoutes OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of IPv4 datagrams discarded because no route + could be found to transmit them to their destination. Note + that this counter includes any packets counted in + ipForwDatagrams which meet this `no-route' criterion. Note + that this includes any datagrams which a host cannot route + because all of its default routers are down. + + This object has been deprecated, as a new IP version-neutral + + + + table has been added. It is loosely replaced by + ipSystemStatsOutNoRoutes." + ::= { ip 12 } + +ipReasmReqds OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of IPv4 fragments received which needed to be + reassembled at this entity. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsReasmReqds." + ::= { ip 14 } + +ipReasmOKs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of IPv4 datagrams successfully re-assembled. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsReasmOKs." + ::= { ip 15 } + +ipReasmFails OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of failures detected by the IPv4 re-assembly + algorithm (for whatever reason: timed out, errors, etc). + Note that this is not necessarily a count of discarded IPv4 + fragments since some algorithms (notably the algorithm in + RFC 815) can lose track of the number of fragments by + combining them as they are received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsReasmFails." + ::= { ip 16 } + +ipFragOKs OBJECT-TYPE + SYNTAX Counter32 + + + + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of IPv4 datagrams that have been successfully + fragmented at this entity. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsOutFragOKs." + ::= { ip 17 } + +ipFragFails OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of IPv4 datagrams that have been discarded + because they needed to be fragmented at this entity but + could not be, e.g., because their Don't Fragment flag was + set. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + ipSystemStatsOutFragFails." + ::= { ip 18 } + +ipFragCreates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of IPv4 datagram fragments that have been + generated as a result of fragmentation at this entity. + + This object has been deprecated as a new IP version neutral + table has been added. It is loosely replaced by + ipSystemStatsOutFragCreates." + ::= { ip 19 } + +ipRoutingDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of routing entries which were chosen to be + discarded even though they are valid. One possible reason + for discarding such an entry could be to free-up buffer + space for other routing entries. + + + + This object was defined in pre-IPv6 versions of the IP MIB. + It was implicitly IPv4 only, but the original specifications + did not indicate this protocol restriction. In order to + clarify the specifications, this object has been deprecated + and a similar, but more thoroughly clarified, object has + been added to the IP-FORWARD-MIB." + ::= { ip 23 } + +-- the deprecated IPv4 address table + +ipAddrTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpAddrEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "The table of addressing information relevant to this + entity's IPv4 addresses. + + This table has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by the + ipAddressTable although several objects that weren't deemed + useful weren't carried forward while another + (ipAdEntReasmMaxSize) was moved to the ipv4InterfaceTable." + ::= { ip 20 } + +ipAddrEntry OBJECT-TYPE + SYNTAX IpAddrEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "The addressing information for one of this entity's IPv4 + addresses." + INDEX { ipAdEntAddr } + ::= { ipAddrTable 1 } + +IpAddrEntry ::= SEQUENCE { + ipAdEntAddr IpAddress, + ipAdEntIfIndex INTEGER, + ipAdEntNetMask IpAddress, + ipAdEntBcastAddr INTEGER, + ipAdEntReasmMaxSize INTEGER + } + +ipAdEntAddr OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + + + + "The IPv4 address to which this entry's addressing + information pertains." + ::= { ipAddrEntry 1 } + +ipAdEntIfIndex OBJECT-TYPE + SYNTAX INTEGER (1..2147483647) + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The index value which uniquely identifies the interface to + which this entry is applicable. The interface identified by + a particular value of this index is the same interface as + identified by the same value of the IF-MIB's ifIndex." + ::= { ipAddrEntry 2 } + +ipAdEntNetMask OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The subnet mask associated with the IPv4 address of this + entry. The value of the mask is an IPv4 address with all + the network bits set to 1 and all the hosts bits set to 0." + ::= { ipAddrEntry 3 } + +ipAdEntBcastAddr OBJECT-TYPE + SYNTAX INTEGER (0..1) + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The value of the least-significant bit in the IPv4 broadcast + address used for sending datagrams on the (logical) + interface associated with the IPv4 address of this entry. + For example, when the Internet standard all-ones broadcast + address is used, the value will be 1. This value applies to + both the subnet and network broadcast addresses used by the + entity on this (logical) interface." + ::= { ipAddrEntry 4 } + +ipAdEntReasmMaxSize OBJECT-TYPE + SYNTAX INTEGER (0..65535) + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The size of the largest IPv4 datagram which this entity can + re-assemble from incoming IPv4 fragmented datagrams received + on this interface." + ::= { ipAddrEntry 5 } + + + +-- the deprecated IPv4 Address Translation table + +-- The Address Translation tables contain the IpAddress to +-- "physical" address equivalences. Some interfaces do not +-- use translation tables for determining address +-- equivalences (e.g., DDN-X.25 has an algorithmic method); +-- if all interfaces are of this type, then the Address +-- Translation table is empty, i.e., has zero entries. + +ipNetToMediaTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpNetToMediaEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "The IPv4 Address Translation table used for mapping from + IPv4 addresses to physical addresses. + + This table has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by the + ipNetToPhysicalTable." + ::= { ip 22 } + +ipNetToMediaEntry OBJECT-TYPE + SYNTAX IpNetToMediaEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "Each entry contains one IpAddress to `physical' address + equivalence." + INDEX { ipNetToMediaIfIndex, + ipNetToMediaNetAddress } + ::= { ipNetToMediaTable 1 } + +IpNetToMediaEntry ::= SEQUENCE { + ipNetToMediaIfIndex INTEGER, + ipNetToMediaPhysAddress PhysAddress, + ipNetToMediaNetAddress IpAddress, + ipNetToMediaType INTEGER + } + +ipNetToMediaIfIndex OBJECT-TYPE + SYNTAX INTEGER (1..2147483647) + MAX-ACCESS read-create + STATUS deprecated + DESCRIPTION + "The interface on which this entry's equivalence is + effective. The interface identified by a particular value + of this index is the same interface as identified by the + + + + same value of the IF-MIB's ifIndex. + + This object predates the rule limiting index objects to a + max access value of 'not-accessible' and so continues to use + a value of 'read-create'." + ::= { ipNetToMediaEntry 1 } + +ipNetToMediaPhysAddress OBJECT-TYPE + SYNTAX PhysAddress (SIZE(0..65535)) + MAX-ACCESS read-create + STATUS deprecated + DESCRIPTION + "The media-dependent `physical' address. This object should + return 0 when this entry is in the 'incomplete' state. + + As the entries in this table are typically not persistent + when this object is written the entity should not save the + change to non-volatile storage. Note: a stronger + requirement is not used because this object was previously + defined." + ::= { ipNetToMediaEntry 2 } + +ipNetToMediaNetAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-create + STATUS deprecated + DESCRIPTION + "The IpAddress corresponding to the media-dependent + `physical' address. + + This object predates the rule limiting index objects to a + max access value of 'not-accessible' and so continues to use + a value of 'read-create'." + ::= { ipNetToMediaEntry 3 } + +ipNetToMediaType OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + invalid(2), -- an invalidated mapping + dynamic(3), + static(4) + } + MAX-ACCESS read-create + STATUS deprecated + DESCRIPTION + "The type of mapping. + + Setting this object to the value invalid(2) has the effect + + + + of invalidating the corresponding entry in the + ipNetToMediaTable. That is, it effectively dis-associates + the interface identified with said entry from the mapping + identified with said entry. It is an implementation- + specific matter as to whether the agent removes an + invalidated entry from the table. Accordingly, management + stations must be prepared to receive tabular information + from agents that corresponds to entries not currently in + use. Proper interpretation of such entries requires + examination of the relevant ipNetToMediaType object. + + As the entries in this table are typically not persistent + when this object is written the entity should not save the + change to non-volatile storage. Note: a stronger + requirement is not used because this object was previously + defined." + ::= { ipNetToMediaEntry 4 } + +-- the deprecated ICMP group + +icmpInMsgs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The total number of ICMP messages which the entity received. + Note that this counter includes all those counted by + icmpInErrors. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + icmpStatsInMsgs." + ::= { icmp 1 } + +icmpInErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP messages which the entity received but + determined as having ICMP-specific errors (bad ICMP + checksums, bad length, etc.). + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + icmpStatsInErrors." + ::= { icmp 2 } + + + + +icmpInDestUnreachs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Destination Unreachable messages + received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 3 } + +icmpInTimeExcds OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Time Exceeded messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 4 } + +icmpInParmProbs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Parameter Problem messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 5 } + +icmpInSrcQuenchs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Source Quench messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 6 } + + + +icmpInRedirects OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Redirect messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 7 } + +icmpInEchos OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Echo (request) messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 8 } + +icmpInEchoReps OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Echo Reply messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 9 } + +icmpInTimestamps OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Timestamp (request) messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 10 } + + + + +icmpInTimestampReps OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Timestamp Reply messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 11 } + +icmpInAddrMasks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Address Mask Request messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 12 } + +icmpInAddrMaskReps OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Address Mask Reply messages received. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 13 } + +icmpOutMsgs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The total number of ICMP messages which this entity + attempted to send. Note that this counter includes all + those counted by icmpOutErrors. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + icmpStatsOutMsgs." + + + + ::= { icmp 14 } + +icmpOutErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP messages which this entity did not send + due to problems discovered within ICMP, such as a lack of + buffers. This value should not include errors discovered + outside the ICMP layer, such as the inability of IP to route + the resultant datagram. In some implementations, there may + be no types of error which contribute to this counter's + value. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by + icmpStatsOutErrors." + ::= { icmp 15 } + +icmpOutDestUnreachs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Destination Unreachable messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 16 } + +icmpOutTimeExcds OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Time Exceeded messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 17 } + +icmpOutParmProbs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + + + + DESCRIPTION + "The number of ICMP Parameter Problem messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 18 } + +icmpOutSrcQuenchs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Source Quench messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 19 } + +icmpOutRedirects OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Redirect messages sent. For a host, this + object will always be zero, since hosts do not send + redirects. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 20 } + +icmpOutEchos OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Echo (request) messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 21 } + +icmpOutEchoReps OBJECT-TYPE + SYNTAX Counter32 + + + + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Echo Reply messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 22 } + +icmpOutTimestamps OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Timestamp (request) messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 23 } + +icmpOutTimestampReps OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Timestamp Reply messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 24 } + +icmpOutAddrMasks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Address Mask Request messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 25 } + +icmpOutAddrMaskReps OBJECT-TYPE + SYNTAX Counter32 + + + + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The number of ICMP Address Mask Reply messages sent. + + This object has been deprecated, as a new IP version-neutral + table has been added. It is loosely replaced by a column in + the icmpMsgStatsTable." + ::= { icmp 26 } + +-- deprecated conformance information +-- deprecated compliance statements + +ipMIBCompliance MODULE-COMPLIANCE + STATUS deprecated + DESCRIPTION + "The compliance statement for systems that implement only + IPv4. For version-independence, this compliance statement + is deprecated in favor of ipMIBCompliance2." + MODULE -- this module + MANDATORY-GROUPS { ipGroup, + icmpGroup } + ::= { ipMIBCompliances 1 } + +-- deprecated units of conformance + +ipGroup OBJECT-GROUP + OBJECTS { ipForwarding, ipDefaultTTL, + ipInReceives, ipInHdrErrors, + ipInAddrErrors, ipForwDatagrams, + ipInUnknownProtos, ipInDiscards, + ipInDelivers, ipOutRequests, + ipOutDiscards, ipOutNoRoutes, + ipReasmTimeout, ipReasmReqds, + ipReasmOKs, ipReasmFails, + ipFragOKs, ipFragFails, + ipFragCreates, ipAdEntAddr, + ipAdEntIfIndex, ipAdEntNetMask, + ipAdEntBcastAddr, ipAdEntReasmMaxSize, + ipNetToMediaIfIndex, ipNetToMediaPhysAddress, + ipNetToMediaNetAddress, ipNetToMediaType, + ipRoutingDiscards +} + STATUS deprecated + DESCRIPTION + "The ip group of objects providing for basic management of IP + entities, exclusive of the management of IP routes. + + + + + As part of the version independence, this group has been + deprecated. " + ::= { ipMIBGroups 1 } + +icmpGroup OBJECT-GROUP + OBJECTS { icmpInMsgs, icmpInErrors, + icmpInDestUnreachs, icmpInTimeExcds, + icmpInParmProbs, icmpInSrcQuenchs, + icmpInRedirects, icmpInEchos, + icmpInEchoReps, icmpInTimestamps, + icmpInTimestampReps, icmpInAddrMasks, + icmpInAddrMaskReps, icmpOutMsgs, + icmpOutErrors, icmpOutDestUnreachs, + icmpOutTimeExcds, icmpOutParmProbs, + icmpOutSrcQuenchs, icmpOutRedirects, + icmpOutEchos, icmpOutEchoReps, + icmpOutTimestamps, icmpOutTimestampReps, + icmpOutAddrMasks, icmpOutAddrMaskReps } + STATUS deprecated + DESCRIPTION + "The icmp group of objects providing ICMP statistics. + + As part of the version independence, this group has been + deprecated. " + ::= { ipMIBGroups 2 } + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC-1212 b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC-1212 new file mode 100644 index 0000000..fbb2a7a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC-1212 @@ -0,0 +1,75 @@ +RFC-1212 DEFINITIONS ::= BEGIN + + IMPORTS + ObjectName + FROM RFC1155-SMI; +-- DisplayString +-- FROM RFC1158-MIB; + + OBJECT-TYPE MACRO ::= + BEGIN + TYPE NOTATION ::= + -- must conform to + -- RFC1155's ObjectSyntax + "SYNTAX" type(ObjectSyntax) + "ACCESS" Access + "STATUS" Status + DescrPart + ReferPart + IndexPart + DefValPart + VALUE NOTATION ::= value (VALUE ObjectName) + + Access ::= "read-only" + | "read-write" + | "write-only" + | "not-accessible" + Status ::= "mandatory" + | "optional" + | "obsolete" + | "deprecated" + + DescrPart ::= + "DESCRIPTION" value (description DisplayString) + | empty + + ReferPart ::= + "REFERENCE" value (reference DisplayString) + | empty + + IndexPart ::= + "INDEX" "{" IndexTypes "}" + | empty + IndexTypes ::= + IndexType | IndexTypes "," IndexType + IndexType ::= + -- if indexobject, use the SYNTAX + -- value of the correspondent + -- OBJECT-TYPE invocation + value (indexobject ObjectName) + -- otherwise use named SMI type + -- must conform to IndexSyntax below + | type (indextype) + + DefValPart ::= + "DEFVAL" "{" value (defvalue ObjectSyntax) "}" + | empty + + END + + IndexSyntax ::= + CHOICE { + number + INTEGER (0..MAX), + string + OCTET STRING, + object + OBJECT IDENTIFIER, + address + NetworkAddress, + ipAddress + IpAddress + } + +END + diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC-1215 b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC-1215 new file mode 100644 index 0000000..8d4b730 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC-1215 @@ -0,0 +1,34 @@ +RFC-1215 DEFINITIONS ::= BEGIN + + IMPORTS + ObjectName + FROM RFC1155-SMI; + + TRAP-TYPE MACRO ::= + BEGIN + TYPE NOTATION ::= "ENTERPRISE" value + (enterprise OBJECT IDENTIFIER) + VarPart + DescrPart + ReferPart + VALUE NOTATION ::= value (VALUE INTEGER) + + VarPart ::= + "VARIABLES" "{" VarTypes "}" + | empty + VarTypes ::= + VarType | VarTypes "," VarType + VarType ::= + value (vartype ObjectName) + + DescrPart ::= + "DESCRIPTION" value (description DisplayString) + | empty + + ReferPart ::= + "REFERENCE" value (reference DisplayString) + | empty + + END + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1065-SMI b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1065-SMI new file mode 100644 index 0000000..9cdb52c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1065-SMI @@ -0,0 +1,132 @@ +RFC1065-SMI DEFINITIONS ::= BEGIN + +EXPORTS -- EVERYTHING + internet, directory, mgmt, + experimental, private, enterprises, + OBJECT-TYPE, ObjectName, ObjectSyntax, SimpleSyntax, + ApplicationSyntax, NetworkAddress, IpAddress, + Counter, Gauge, TimeTicks, Opaque; + + -- the path to the root + + internet OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 } + + directory OBJECT IDENTIFIER ::= { internet 1 } + + mgmt OBJECT IDENTIFIER ::= { internet 2 } + + experimental OBJECT IDENTIFIER ::= { internet 3 } + + private OBJECT IDENTIFIER ::= { internet 4 } + enterprises OBJECT IDENTIFIER ::= { private 1 } + + + -- definition of object types + + OBJECT-TYPE MACRO ::= + BEGIN + TYPE NOTATION ::= "SYNTAX" type (TYPE ObjectSyntax) + "ACCESS" Access + "STATUS" Status + VALUE NOTATION ::= value (VALUE ObjectName) + + Access ::= "read-only" + | "read-write" + | "write-only" + | "not-accessible" + Status ::= "mandatory" + | "optional" + | "obsolete" + END + + -- names of objects in the MIB + + ObjectName ::= + OBJECT IDENTIFIER + + + + -- syntax of objects in the MIB + + ObjectSyntax ::= + CHOICE { + simple + SimpleSyntax, + + -- note that simple SEQUENCEs are not directly + -- mentioned here to keep things simple (i.e., + -- prevent mis-use). However, application-wide + -- types which are IMPLICITly encoded simple + -- SEQUENCEs may appear in the following CHOICE + + application-wide + ApplicationSyntax + } + + SimpleSyntax ::= + CHOICE { + number + INTEGER, + + string + OCTET STRING, + + object + OBJECT IDENTIFIER, + + empty + NULL + } + + ApplicationSyntax ::= + CHOICE { + address + NetworkAddress, + + counter + Counter, + + gauge + Gauge, + + ticks + TimeTicks, + + arbitrary + Opaque + + + -- other application-wide types, as they are + -- defined, will be added here + } + + + -- application-wide types + + NetworkAddress ::= + CHOICE { + internet + IpAddress + } + + IpAddress ::= + [APPLICATION 0] -- in network-byte order + IMPLICIT OCTET STRING (SIZE (4)) + + Counter ::= + [APPLICATION 1] + IMPLICIT INTEGER (0..4294967295) + + Gauge ::= + [APPLICATION 2] + IMPLICIT INTEGER (0..4294967295) + + TimeTicks ::= + [APPLICATION 3] + IMPLICIT INTEGER + + Opaque ::= + [APPLICATION 4] -- arbitrary ASN.1 value, + IMPLICIT OCTET STRING -- "double-wrapped" + + END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1155-SMI b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1155-SMI new file mode 100644 index 0000000..89bd9de --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1155-SMI @@ -0,0 +1,129 @@ +RFC1155-SMI DEFINITIONS ::= BEGIN + +EXPORTS -- EVERYTHING + internet, directory, mgmt, + experimental, private, enterprises, + OBJECT-TYPE, ObjectName, ObjectSyntax, SimpleSyntax, + ApplicationSyntax, NetworkAddress, IpAddress, + Counter, Gauge, TimeTicks, Opaque; + + -- the path to the root + + internet OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 } + + directory OBJECT IDENTIFIER ::= { internet 1 } + + mgmt OBJECT IDENTIFIER ::= { internet 2 } + + experimental OBJECT IDENTIFIER ::= { internet 3 } + + private OBJECT IDENTIFIER ::= { internet 4 } + enterprises OBJECT IDENTIFIER ::= { private 1 } + + + -- definition of object types + + OBJECT-TYPE MACRO ::= + BEGIN + TYPE NOTATION ::= "SYNTAX" type (TYPE ObjectSyntax) + "ACCESS" Access + "STATUS" Status + VALUE NOTATION ::= value (VALUE ObjectName) + + Access ::= "read-only" + | "read-write" + | "write-only" + | "not-accessible" + Status ::= "mandatory" + | "optional" + | "obsolete" + END + + -- names of objects in the MIB + + ObjectName ::= + OBJECT IDENTIFIER + + -- syntax of objects in the MIB + + ObjectSyntax ::= + CHOICE { + simple + SimpleSyntax, + + -- note that simple SEQUENCEs are not directly + -- mentioned here to keep things simple (i.e., + -- prevent mis-use). However, application-wide + -- types which are IMPLICITly encoded simple + -- SEQUENCEs may appear in the following CHOICE + + application-wide + ApplicationSyntax + } + + SimpleSyntax ::= + CHOICE { + number + INTEGER, + + string + OCTET STRING, + + object + OBJECT IDENTIFIER, + + empty + NULL + } + + ApplicationSyntax ::= + CHOICE { + address + NetworkAddress, + + counter + Counter, + + gauge + Gauge, + + ticks + TimeTicks, + + arbitrary + Opaque + + -- other application-wide types, as they are + -- defined, will be added here + } + + + -- application-wide types + + NetworkAddress ::= + CHOICE { + internet + IpAddress + } + + IpAddress ::= + [APPLICATION 0] -- in network-byte order + IMPLICIT OCTET STRING (SIZE (4)) + + Counter ::= + [APPLICATION 1] + IMPLICIT INTEGER (0..4294967295) + + Gauge ::= + [APPLICATION 2] + IMPLICIT INTEGER (0..4294967295) + + TimeTicks ::= + [APPLICATION 3] + IMPLICIT INTEGER (0..4294967295) + + Opaque ::= + [APPLICATION 4] -- arbitrary ASN.1 value, + IMPLICIT OCTET STRING -- "double-wrapped" + + END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1158-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1158-MIB new file mode 100644 index 0000000..efdd6a9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1158-MIB @@ -0,0 +1,1493 @@ +RFC1158-MIB DEFINITIONS ::= BEGIN + +IMPORTS + mgmt, OBJECT-TYPE, NetworkAddress, IpAddress, + Counter, Gauge, TimeTicks + FROM RFC1155-SMI; + +DisplayString ::= + OCTET STRING + +mib-2 OBJECT IDENTIFIER ::= { mgmt 1 } -- MIB-II + -- (same prefix as MIB-I) + +system OBJECT IDENTIFIER ::= { mib-2 1 } +interfaces OBJECT IDENTIFIER ::= { mib-2 2 } +at OBJECT IDENTIFIER ::= { mib-2 3 } +ip OBJECT IDENTIFIER ::= { mib-2 4 } +icmp OBJECT IDENTIFIER ::= { mib-2 5 } +tcp OBJECT IDENTIFIER ::= { mib-2 6 } +udp OBJECT IDENTIFIER ::= { mib-2 7 } +egp OBJECT IDENTIFIER ::= { mib-2 8 } +-- cmot OBJECT IDENTIFIER ::= { mib-2 9 } +transmission OBJECT IDENTIFIER ::= { mib-2 10 } +snmp OBJECT IDENTIFIER ::= { mib-2 11 } + + +-- object types + +-- the System group + +sysDescr OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-only + STATUS mandatory + ::= { system 1 } + +sysObjectID OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + ACCESS read-only + STATUS mandatory + ::= { system 2 } + +sysUpTime OBJECT-TYPE + SYNTAX TimeTicks + ACCESS read-only + STATUS mandatory + ::= { system 3 } + +sysContact OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-write + STATUS mandatory + ::= { system 4 } + +sysName OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-write + STATUS mandatory + ::= { system 5 } + +sysLocation OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-only + STATUS mandatory + ::= { system 6 } + +sysServices OBJECT-TYPE + SYNTAX INTEGER (0..127) + ACCESS read-only + STATUS mandatory + ::= { system 7 } + + +-- the Interfaces group + +ifNumber OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { interfaces 1 } + +-- the Interfaces table + +ifTable OBJECT-TYPE + SYNTAX SEQUENCE OF IfEntry + ACCESS read-only + STATUS mandatory + ::= { interfaces 2 } + +ifEntry OBJECT-TYPE + SYNTAX IfEntry + ACCESS read-only + STATUS mandatory + ::= { ifTable 1 } + +IfEntry ::= SEQUENCE { + ifIndex + INTEGER, + ifDescr + DisplayString, + ifType + INTEGER, + ifMtu + INTEGER, + ifSpeed + Gauge, + ifPhysAddress + OCTET STRING, + ifAdminStatus + INTEGER, + ifOperStatus + INTEGER, + ifLastChange + TimeTicks, + ifInOctets + Counter, + ifInUcastPkts + Counter, + ifInNUcastPkts + Counter, + ifInDiscards + Counter, + ifInErrors + Counter, + ifInUnknownProtos + Counter, + ifOutOctets + Counter, + ifOutUcastPkts + Counter, + ifOutNUcastPkts + Counter, + ifOutDiscards + Counter, + ifOutErrors + Counter, + ifOutQLen + Gauge, + ifSpecific + OBJECT IDENTIFIER +} + +ifIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { ifEntry 1 } + +ifDescr OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-only + STATUS mandatory + ::= { ifEntry 2 } + +ifType OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the + -- following + regular1822(2), + hdh1822(3), + ddn-x25(4), + rfc877-x25(5), + ethernet-csmacd(6), + iso88023-csmacd(7), + iso88024-tokenBus(8), + iso88025-tokenRing(9), + iso88026-man(10), + starLan(11), + proteon-10Mbit(12), + proteon-80Mbit(13), + hyperchannel(14), + fddi(15), + lapb(16), + sdlc(17), + t1-carrier(18), + cept(19), -- european + --equivalent of T-1 + basicISDN(20), + primaryISDN(21), + -- proprietary + -- serial + propPointToPointSerial(22), + terminalServer-asyncPort(23), + softwareLoopback(24), + eon(25), -- CLNP over IP + ethernet-3Mbit(26), + nsip(27), -- XNS over IP + slip(28) -- generic SLIP + } + ACCESS read-only + STATUS mandatory + ::= { ifEntry 3 } + +ifMtu OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { ifEntry 4 } + +ifSpeed OBJECT-TYPE + SYNTAX Gauge + ACCESS read-only + STATUS mandatory + ::= { ifEntry 5 } + +ifPhysAddress OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-only + STATUS mandatory + ::= { ifEntry 6 } + +ifAdminStatus OBJECT-TYPE + SYNTAX INTEGER { + up(1), -- ready to pass packets + down(2), + testing(3) -- in some test mode + } + ACCESS read-write + STATUS mandatory + ::= { ifEntry 7 } + +ifOperStatus OBJECT-TYPE + SYNTAX INTEGER { + up(1), -- ready to pass packets + down(2), + testing(3) -- in some test mode + } + ACCESS read-only + STATUS mandatory + ::= { ifEntry 8 } + +ifLastChange OBJECT-TYPE + SYNTAX TimeTicks + ACCESS read-only + STATUS mandatory + ::= { ifEntry 9 } + +ifInOctets OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 10 } + +ifInUcastPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 11 } + +ifInNUcastPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 12 } + +ifInDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 13 } + +ifInErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 14 } + +ifInUnknownProtos OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 15 } + +ifOutOctets OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 16 } + +ifOutUcastPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 17 } + +ifOutNUcastPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 18 } + +ifOutDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 19 } + +ifOutErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ifEntry 20 } + +ifOutQLen OBJECT-TYPE + SYNTAX Gauge + ACCESS read-only + STATUS mandatory + ::= { ifEntry 21 } + +ifSpecific OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + ACCESS read-only + STATUS mandatory + ::= { ifEntry 22 } + +nullSpecific OBJECT IDENTIFIER ::= { 0 0 } + +-- the Address Translation group (deprecated) + +atTable OBJECT-TYPE + SYNTAX SEQUENCE OF AtEntry + ACCESS read-write + STATUS deprecated + ::= { at 1 } + +atEntry OBJECT-TYPE + SYNTAX AtEntry + ACCESS read-write + STATUS deprecated + ::= { atTable 1 } + +AtEntry ::= SEQUENCE { + atIfIndex + INTEGER, + atPhysAddress + OCTET STRING, + atNetAddress + NetworkAddress +} + +atIfIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS deprecated + ::= { atEntry 1 } + +atPhysAddress OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-write + STATUS deprecated + ::= { atEntry 2 } + +atNetAddress OBJECT-TYPE + SYNTAX NetworkAddress + ACCESS read-write + STATUS deprecated + ::= { atEntry 3 } + + +-- the IP group + +ipForwarding OBJECT-TYPE + SYNTAX INTEGER { + gateway(1), -- entity forwards + -- datagrams + host(2) -- entity does NOT + -- forward datagrams + } + ACCESS read-write + STATUS mandatory + ::= { ip 1 } + +ipDefaultTTL OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + ::= { ip 2 } + +ipInReceives OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 3 } + +ipInHdrErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 4 } + +ipInAddrErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 5 } + +ipForwDatagrams OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 6 } + +ipInUnknownProtos OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 7 } + +ipInDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 8 } + +ipInDelivers OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 9 } + +ipOutRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 10 } + +ipOutDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 11 } + +ipOutNoRoutes OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 12 } + +ipReasmTimeout OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { ip 13 } + +ipReasmReqds OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 14 } + +ipReasmOKs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 15 } + +ipReasmFails OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 16 } + +ipFragOKs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 17 } + +ipFragFails OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 18 } + +ipFragCreates OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { ip 19 } + +-- the IP Interface table + +ipAddrTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpAddrEntry + ACCESS read-only + STATUS mandatory + ::= { ip 20 } + +ipAddrEntry OBJECT-TYPE + SYNTAX IpAddrEntry + ACCESS read-only + STATUS mandatory + ::= { ipAddrTable 1 } + +IpAddrEntry ::= SEQUENCE { + ipAdEntAddr + IpAddress, + ipAdEntIfIndex + INTEGER, + ipAdEntNetMask + IpAddress, + ipAdEntBcastAddr + INTEGER, + ipAdEntReasmMaxSize + INTEGER (0..65535) +} + +ipAdEntAddr OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + ::= { ipAddrEntry 1 } + +ipAdEntIfIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { ipAddrEntry 2 } + +ipAdEntNetMask OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + ::= { ipAddrEntry 3 } + +ipAdEntBcastAddr OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { ipAddrEntry 4 } + +ipAdEntReasmMaxSize OBJECT-TYPE + SYNTAX INTEGER (0..65535) + ACCESS read-only + STATUS mandatory + ::= { ipAddrEntry 5 } + +-- the IP Routing table + +ipRoutingTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpRouteEntry + ACCESS read-write + STATUS mandatory + ::= { ip 21 } + +ipRouteEntry OBJECT-TYPE + SYNTAX IpRouteEntry + ACCESS read-write + STATUS mandatory + ::= { ipRoutingTable 1 } + +IpRouteEntry ::= SEQUENCE { + ipRouteDest + IpAddress, + ipRouteIfIndex + INTEGER, + ipRouteMetric1 + INTEGER, + ipRouteMetric2 + INTEGER, + ipRouteMetric3 + INTEGER, + ipRouteMetric4 + INTEGER, + ipRouteNextHop + IpAddress, + ipRouteType + INTEGER, + ipRouteProto + INTEGER, + ipRouteAge + INTEGER, + ipRouteMask + IpAddress +} + +ipRouteDest OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 1 } + +ipRouteIfIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 2 } + +ipRouteMetric1 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 3 } + +ipRouteMetric2 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 4 } + +ipRouteMetric3 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 5 } + +ipRouteMetric4 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 6 } + +ipRouteNextHop OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 7 } + +ipRouteType OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + + invalid(2), -- an invalidated route + + -- route to directly + direct(3), -- connected + -- (sub-)network + + -- route to a non-local + remote(4) -- host/network/ + -- sub-network + } + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 8 } + +ipRouteProto OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + + -- non-protocol + -- information + + -- e.g., manually + local(2), -- configured entries + + -- set via a network + netmgmt(3), -- management protocol + + -- obtained via ICMP, + icmp(4), -- e.g., Redirect + + -- the following are + -- gateway routing + -- protocols + egp(5), + ggp(6), + hello(7), + rip(8), + is-is(9), + es-is(10), + ciscoIgrp(11), + bbnSpfIgp(12), + ospf(13), + bgp(14) + } + ACCESS read-only + STATUS mandatory + ::= { ipRouteEntry 9 } + +ipRouteAge OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 10 } + +ipRouteMask OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-write + STATUS mandatory + ::= { ipRouteEntry 11 } + +-- the IP Address Translation tables + +ipNetToMediaTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpNetToMediaEntry + ACCESS read-write + STATUS mandatory + ::= { ip 22 } + +ipNetToMediaEntry OBJECT-TYPE + SYNTAX IpNetToMediaEntry + ACCESS read-write + STATUS mandatory + ::= { ipNetToMediaTable 1 } + +IpNetToMediaEntry ::= SEQUENCE { + ipNetToMediaIfIndex + INTEGER, + ipNetToMediaPhysAddress + OCTET STRING, + ipNetToMediaNetAddress + IpAddress, + ipNetToMediaType + INTEGER +} + +ipNetToMediaIfIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + ::= { ipNetToMediaEntry 1 } + +ipNetToMediaPhysAddress OBJECT-TYPE + SYNTAX OCTET STRING + ACCESS read-write + STATUS mandatory + ::= { ipNetToMediaEntry 2 } + +ipNetToMediaNetAddress OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-write + STATUS mandatory + ::= { ipNetToMediaEntry 3 } + +ipNetToMediaType OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + + invalid(2), -- an invalidated mapping + dynamic(3), -- connected (sub-)network + + static(4) + } + ACCESS read-write + STATUS mandatory + ::= { ipNetToMediaEntry 4 } + +-- the ICMP group + +icmpInMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 1 } + +icmpInErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 2 } + +icmpInDestUnreachs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 3 } + +icmpInTimeExcds OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 4 } + +icmpInParmProbs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 5 } + +icmpInSrcQuenchs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 6 } + +icmpInRedirects OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 7 } + +icmpInEchos OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 8 } + +icmpInEchoReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 9 } + +icmpInTimestamps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 10 } + +icmpInTimestampReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 11 } + +icmpInAddrMasks OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 12 } + +icmpInAddrMaskReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 13 } + +icmpOutMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 14 } + +icmpOutErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 15 } + +icmpOutDestUnreachs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 16 } + +icmpOutTimeExcds OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 17 } + +icmpOutParmProbs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 18 } + +icmpOutSrcQuenchs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 19 } + +icmpOutRedirects OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 20 } + +icmpOutEchos OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 21 } + +icmpOutEchoReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 22 } + +icmpOutTimestamps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 23 } + +icmpOutTimestampReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 24 } + +icmpOutAddrMasks OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 25 } + +icmpOutAddrMaskReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { icmp 26 } + + +-- the TCP group + +tcpRtoAlgorithm OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + constant(2), -- a constant rto + rsre(3), -- MIL-STD-1778, + -- Appendix B + vanj(4) -- Van Jacobson's + -- algorithm + } + ACCESS read-only + STATUS mandatory + ::= { tcp 1 } + +tcpRtoMin OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { tcp 2 } + +tcpRtoMax OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { tcp 3 } + +tcpMaxConn OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { tcp 4 } + +tcpActiveOpens OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 5 } + +tcpPassiveOpens OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 6 } + +tcpAttemptFails OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 7 } + +tcpEstabResets OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 8 } + +tcpCurrEstab OBJECT-TYPE + SYNTAX Gauge + ACCESS read-only + STATUS mandatory + ::= { tcp 9 } + +tcpInSegs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 10 } + +tcpOutSegs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 11 } + +tcpRetransSegs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 12 } + +-- the TCP connections table + +tcpConnTable OBJECT-TYPE + SYNTAX SEQUENCE OF TcpConnEntry + ACCESS read-only + STATUS mandatory + ::= { tcp 13 } + +tcpConnEntry OBJECT-TYPE + SYNTAX TcpConnEntry + ACCESS read-only + STATUS mandatory + ::= { tcpConnTable 1 } + +TcpConnEntry ::= SEQUENCE { + tcpConnState + INTEGER, + tcpConnLocalAddress + IpAddress, + tcpConnLocalPort + INTEGER (0..65535), + tcpConnRemAddress + IpAddress, + tcpConnRemPort + INTEGER (0..65535) +} + +tcpConnState OBJECT-TYPE + SYNTAX INTEGER { + closed(1), + listen(2), + synSent(3), + synReceived(4), + established(5), + finWait1(6), + finWait2(7), + closeWait(8), + lastAck(9), + closing(10), + timeWait(11) + } + ACCESS read-only + STATUS mandatory + ::= { tcpConnEntry 1 } + +tcpConnLocalAddress OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + ::= { tcpConnEntry 2 } + +tcpConnLocalPort OBJECT-TYPE + SYNTAX INTEGER (0..65535) + ACCESS read-only + STATUS mandatory + ::= { tcpConnEntry 3 } + +tcpConnRemAddress OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + ::= { tcpConnEntry 4 } + +tcpConnRemPort OBJECT-TYPE + SYNTAX INTEGER (0..65535) + ACCESS read-only + STATUS mandatory + ::= { tcpConnEntry 5 } + +-- additional TCP variables + +tcpInErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 14 } + +tcpOutRsts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { tcp 15 } + + +-- the UDP group + +udpInDatagrams OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { udp 1 } + +udpNoPorts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { udp 2 } + +udpInErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { udp 3 } + +udpOutDatagrams OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { udp 4 } + +-- the UDP listener table + +udpTable OBJECT-TYPE + SYNTAX SEQUENCE OF UdpEntry + ACCESS read-only + STATUS mandatory + ::= { udp 5 } + +udpEntry OBJECT-TYPE + SYNTAX UdpEntry + ACCESS read-only + STATUS mandatory + ::= { udpTable 1 } + +UdpEntry ::= SEQUENCE { + udpLocalAddress + IpAddress, + udpLocalPort + INTEGER (0..65535) +} + +udpLocalAddress OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + ::= { udpEntry 1 } + +udpLocalPort OBJECT-TYPE + SYNTAX INTEGER (0..65535) + ACCESS read-only + STATUS mandatory + ::= { udpEntry 2 } + +-- the EGP group + +egpInMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egp 1 } + +egpInErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egp 2 } + +egpOutMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egp 3 } + +egpOutErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egp 4 } + +-- the EGP Neighbor table + +egpNeighTable OBJECT-TYPE + SYNTAX SEQUENCE OF EgpNeighEntry + ACCESS read-only + STATUS mandatory + ::= { egp 5 } + +egpNeighEntry OBJECT-TYPE + SYNTAX EgpNeighEntry + ACCESS read-only + STATUS mandatory + ::= { egpNeighTable 1 } + +EgpNeighEntry ::= SEQUENCE { + egpNeighState + INTEGER, + egpNeighAddr + IpAddress, + egpNeighAs + INTEGER, + egpNeighInMsgs + Counter, + egpNeighInErrs + Counter, + egpNeighOutMsgs + Counter, + egpNeighOutErrs + Counter, + egpNeighInErrMsgs + Counter, + egpNeighOutErrMsgs + Counter, + egpNeighStateUps + Counter, + egpNeighStateDowns + Counter, + egpNeighIntervalHello + INTEGER, + egpNeighIntervalPoll + INTEGER, + egpNeighMode + INTEGER, + egpNeighEventTrigger + INTEGER +} + +egpNeighState OBJECT-TYPE + SYNTAX INTEGER { + idle(1), + acquisition(2), + down(3), + up(4), + cease(5) + } + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 1 } + +egpNeighAddr OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 2 } + +egpNeighAs OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 3 } + +egpNeighInMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 4 } + +egpNeighInErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 5 } + +egpNeighOutMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 6 } + +egpNeighOutErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 7 } + +egpNeighInErrMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 8 } + +egpNeighOutErrMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 9 } + +egpNeighStateUps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 10 } + +egpNeighStateDowns OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 11 } + +egpNeighIntervalHello OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 12 } + +egpNeighIntervalPoll OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 13 } + +egpNeighMode OBJECT-TYPE + SYNTAX INTEGER { + active(1), + passive(2) + } + ACCESS read-only + STATUS mandatory + ::= { egpNeighEntry 14 } + +egpNeighEventTrigger OBJECT-TYPE + SYNTAX INTEGER { + start(1), + stop(2) + } + ACCESS read-write + STATUS mandatory + ::= { egpNeighEntry 15 } + +-- additional EGP variables + +egpAs OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + ::= { egp 6 } + + +-- the Transmission group (empty at present) + +-- the SNMP group + +snmpInPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 1 } + +snmpOutPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 2 } + +snmpInBadVersions OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 3 } + +snmpInBadCommunityNames OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 4 } + +snmpInBadCommunityUses OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 5 } + +snmpInASNParseErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 6 } + +snmpInBadTypes OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 7 } + +snmpInTooBigs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 8 } + +snmpInNoSuchNames OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 9 } + +snmpInBadValues OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 10 } + +snmpInReadOnlys OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 11 } + +snmpInGenErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 12 } + +snmpInTotalReqVars OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 13 } + +snmpInTotalSetVars OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 14 } + +snmpInGetRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 15 } + +snmpInGetNexts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 16 } + +snmpInSetRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 17 } + +snmpInGetResponses OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 18 } + +snmpInTraps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 19 } + +snmpOutTooBigs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 20 } + +snmpOutNoSuchNames OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 21 } + +snmpOutBadValues OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 22 } + +snmpOutReadOnlys OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 23 } + +snmpOutGenErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 24 } + +snmpOutGetRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 25 } + +snmpOutGetNexts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 26 } + +snmpOutSetRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 27 } + +snmpOutGetResponses OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 28 } + +snmpOutTraps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + ::= { snmp 29 } + +snmpEnableAuthTraps OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + ACCESS read-write + STATUS mandatory + ::= { snmp 30 } + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1213-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1213-MIB new file mode 100644 index 0000000..c430f6b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/RFC1213-MIB @@ -0,0 +1,2621 @@ +RFC1213-MIB DEFINITIONS ::= BEGIN + +IMPORTS + mgmt, NetworkAddress, IpAddress, Counter, Gauge, + TimeTicks + FROM RFC1155-SMI + OBJECT-TYPE + FROM RFC-1212; + +-- This MIB module uses the extended OBJECT-TYPE macro as +-- defined in [14]; + + +-- MIB-II (same prefix as MIB-I) + +mib-2 OBJECT IDENTIFIER ::= { mgmt 1 } + +-- textual conventions + +DisplayString ::= + OCTET STRING +-- This data type is used to model textual information taken +-- from the NVT ASCII character set. By convention, objects +-- with this syntax are declared as having + +-- +-- SIZE (0..255) + +PhysAddress ::= + OCTET STRING +-- This data type is used to model media addresses. For many +-- types of media, this will be in a binary representation. +-- For example, an ethernet address would be represented as +-- a string of 6 octets. + + +-- groups in MIB-II + +system OBJECT IDENTIFIER ::= { mib-2 1 } + +interfaces OBJECT IDENTIFIER ::= { mib-2 2 } + +at OBJECT IDENTIFIER ::= { mib-2 3 } + +ip OBJECT IDENTIFIER ::= { mib-2 4 } + +icmp OBJECT IDENTIFIER ::= { mib-2 5 } + +tcp OBJECT IDENTIFIER ::= { mib-2 6 } + +udp OBJECT IDENTIFIER ::= { mib-2 7 } + +egp OBJECT IDENTIFIER ::= { mib-2 8 } + +-- historical (some say hysterical) +-- cmot OBJECT IDENTIFIER ::= { mib-2 9 } + +transmission OBJECT IDENTIFIER ::= { mib-2 10 } + +snmp OBJECT IDENTIFIER ::= { mib-2 11 } + + +-- the System group + +-- Implementation of the System group is mandatory for all +-- systems. If an agent is not configured to have a value +-- for any of these variables, a string of length 0 is +-- returned. + +sysDescr OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-only + STATUS mandatory + DESCRIPTION + "A textual description of the entity. This value + should include the full name and version + identification of the system's hardware type, + software operating-system, and networking + software. It is mandatory that this only contain + printable ASCII characters." + ::= { system 1 } + +sysObjectID OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The vendor's authoritative identification of the + network management subsystem contained in the + entity. This value is allocated within the SMI + enterprises subtree (1.3.6.1.4.1) and provides an + easy and unambiguous means for determining `what + kind of box' is being managed. For example, if + vendor `Flintstones, Inc.' was assigned the + subtree 1.3.6.1.4.1.4242, it could assign the + identifier 1.3.6.1.4.1.4242.1.1 to its `Fred + Router'." + ::= { system 2 } + +sysUpTime OBJECT-TYPE + SYNTAX TimeTicks + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The time (in hundredths of a second) since the + network management portion of the system was last + re-initialized." + ::= { system 3 } + +sysContact OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The textual identification of the contact person + for this managed node, together with information + on how to contact this person." + ::= { system 4 } + +sysName OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-write + STATUS mandatory + DESCRIPTION + "An administratively-assigned name for this + managed node. By convention, this is the node's + fully-qualified domain name." + ::= { system 5 } + +sysLocation OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The physical location of this node (e.g., + `telephone closet, 3rd floor')." + ::= { system 6 } + +sysServices OBJECT-TYPE + SYNTAX INTEGER (0..127) + ACCESS read-only + STATUS mandatory + DESCRIPTION + "A value which indicates the set of services that + this entity primarily offers. + + The value is a sum. This sum initially takes the + value zero, Then, for each layer, L, in the range + 1 through 7, that this node performs transactions + for, 2 raised to (L - 1) is added to the sum. For + example, a node which performs primarily routing + functions would have a value of 4 (2^(3-1)). In + contrast, a node which is a host offering + application services would have a value of 72 + (2^(4-1) + 2^(7-1)). Note that in the context of + the Internet suite of protocols, values should be + calculated accordingly: + + layer functionality + 1 physical (e.g., repeaters) + 2 datalink/subnetwork (e.g., bridges) + 3 internet (e.g., IP gateways) + 4 end-to-end (e.g., IP hosts) + 7 applications (e.g., mail relays) + + For systems including OSI protocols, layers 5 and + 6 may also be counted." + ::= { system 7 } + +-- the Interfaces group + +-- Implementation of the Interfaces group is mandatory for +-- all systems. + +ifNumber OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of network interfaces (regardless of + their current state) present on this system." + ::= { interfaces 1 } + + +-- the Interfaces table + +-- The Interfaces table contains information on the entity's +-- interfaces. Each interface is thought of as being +-- attached to a `subnetwork'. Note that this term should +-- not be confused with `subnet' which refers to an +-- addressing partitioning scheme used in the Internet suite +-- of protocols. + +ifTable OBJECT-TYPE + SYNTAX SEQUENCE OF IfEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "A list of interface entries. The number of + entries is given by the value of ifNumber." + ::= { interfaces 2 } + +ifEntry OBJECT-TYPE + SYNTAX IfEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "An interface entry containing objects at the + subnetwork layer and below for a particular + interface." + INDEX { ifIndex } + ::= { ifTable 1 } + +IfEntry ::= + SEQUENCE { + ifIndex + INTEGER, + ifDescr + DisplayString, + ifType + INTEGER, + ifMtu + INTEGER, + ifSpeed + Gauge, + ifPhysAddress + PhysAddress, + ifAdminStatus + INTEGER, + ifOperStatus + INTEGER, + ifLastChange + TimeTicks, + ifInOctets + Counter, + ifInUcastPkts + Counter, + ifInNUcastPkts + Counter, + ifInDiscards + Counter, + ifInErrors + Counter, + ifInUnknownProtos + Counter, + ifOutOctets + Counter, + ifOutUcastPkts + Counter, + ifOutNUcastPkts + Counter, + ifOutDiscards + Counter, + ifOutErrors + Counter, + ifOutQLen + Gauge, + ifSpecific + OBJECT IDENTIFIER + } + +ifIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "A unique value for each interface. Its value + ranges between 1 and the value of ifNumber. The + value for each interface must remain constant at + least from one re-initialization of the entity's + network management system to the next re- + initialization." + ::= { ifEntry 1 } + +ifDescr OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + ACCESS read-only + STATUS mandatory + DESCRIPTION + "A textual string containing information about the + interface. This string should include the name of + the manufacturer, the product name and the version + of the hardware interface." + ::= { ifEntry 2 } + +ifType OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + regular1822(2), + hdh1822(3), + ddn-x25(4), + rfc877-x25(5), + ethernet-csmacd(6), + iso88023-csmacd(7), + iso88024-tokenBus(8), + iso88025-tokenRing(9), + iso88026-man(10), + starLan(11), + proteon-10Mbit(12), + proteon-80Mbit(13), + hyperchannel(14), + fddi(15), + lapb(16), + sdlc(17), + ds1(18), -- T-1 + e1(19), -- european equiv. of T-1 + basicISDN(20), + primaryISDN(21), -- proprietary serial + propPointToPointSerial(22), + ppp(23), + softwareLoopback(24), + eon(25), -- CLNP over IP [11] + ethernet-3Mbit(26), + nsip(27), -- XNS over IP + slip(28), -- generic SLIP + ultra(29), -- ULTRA technologies + ds3(30), -- T-3 + sip(31), -- SMDS + frame-relay(32) + } + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The type of interface, distinguished according to + the physical/link protocol(s) immediately `below' + the network layer in the protocol stack." + ::= { ifEntry 3 } + +ifMtu OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The size of the largest datagram which can be + sent/received on the interface, specified in + octets. For interfaces that are used for + transmitting network datagrams, this is the size + of the largest network datagram that can be sent + on the interface." + ::= { ifEntry 4 } + +ifSpeed OBJECT-TYPE + SYNTAX Gauge + ACCESS read-only + STATUS mandatory + DESCRIPTION + "An estimate of the interface's current bandwidth + in bits per second. For interfaces which do not + vary in bandwidth or for those where no accurate + estimation can be made, this object should contain + the nominal bandwidth." + ::= { ifEntry 5 } + +ifPhysAddress OBJECT-TYPE + SYNTAX PhysAddress + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The interface's address at the protocol layer + immediately `below' the network layer in the + protocol stack. For interfaces which do not have + such an address (e.g., a serial line), this object + should contain an octet string of zero length." + ::= { ifEntry 6 } + +ifAdminStatus OBJECT-TYPE + SYNTAX INTEGER { + up(1), -- ready to pass packets + down(2), + testing(3) -- in some test mode + } + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The desired state of the interface. The + testing(3) state indicates that no operational + packets can be passed." + ::= { ifEntry 7 } + +ifOperStatus OBJECT-TYPE + SYNTAX INTEGER { + up(1), -- ready to pass packets + down(2), + testing(3) -- in some test mode + } + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The current operational state of the interface. + The testing(3) state indicates that no operational + packets can be passed." + ::= { ifEntry 8 } + +ifLastChange OBJECT-TYPE + SYNTAX TimeTicks + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The value of sysUpTime at the time the interface + entered its current operational state. If the + current state was entered prior to the last re- + initialization of the local network management + subsystem, then this object contains a zero + value." + ::= { ifEntry 9 } + +ifInOctets OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of octets received on the + interface, including framing characters." + ::= { ifEntry 10 } + +ifInUcastPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of subnetwork-unicast packets + delivered to a higher-layer protocol." + ::= { ifEntry 11 } + +ifInNUcastPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of non-unicast (i.e., subnetwork- + broadcast or subnetwork-multicast) packets + delivered to a higher-layer protocol." + ::= { ifEntry 12 } + +ifInDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of inbound packets which were chosen + to be discarded even though no errors had been + detected to prevent their being deliverable to a + higher-layer protocol. One possible reason for + discarding such a packet could be to free up + buffer space." + ::= { ifEntry 13 } + +ifInErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of inbound packets that contained + errors preventing them from being deliverable to a + higher-layer protocol." + ::= { ifEntry 14 } + +ifInUnknownProtos OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of packets received via the interface + which were discarded because of an unknown or + unsupported protocol." + ::= { ifEntry 15 } + +ifOutOctets OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of octets transmitted out of the + interface, including framing characters." + ::= { ifEntry 16 } + +ifOutUcastPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of packets that higher-level + protocols requested be transmitted to a + subnetwork-unicast address, including those that + were discarded or not sent." + ::= { ifEntry 17 } + +ifOutNUcastPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of packets that higher-level + protocols requested be transmitted to a non- + unicast (i.e., a subnetwork-broadcast or + subnetwork-multicast) address, including those + that were discarded or not sent." + ::= { ifEntry 18 } + +ifOutDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of outbound packets which were chosen + to be discarded even though no errors had been + detected to prevent their being transmitted. One + possible reason for discarding such a packet could + be to free up buffer space." + ::= { ifEntry 19 } + +ifOutErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of outbound packets that could not be + transmitted because of errors." + ::= { ifEntry 20 } + +ifOutQLen OBJECT-TYPE + SYNTAX Gauge + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The length of the output packet queue (in + packets)." + ::= { ifEntry 21 } + +ifSpecific OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "A reference to MIB definitions specific to the + particular media being used to realize the + interface. For example, if the interface is + realized by an ethernet, then the value of this + object refers to a document defining objects + specific to ethernet. If this information is not + present, its value should be set to the OBJECT + IDENTIFIER { 0 0 }, which is a syntatically valid + object identifier, and any conformant + implementation of ASN.1 and BER must be able to + generate and recognize this value." + ::= { ifEntry 22 } + + +-- the Address Translation group + +-- Implementation of the Address Translation group is +-- mandatory for all systems. Note however that this group +-- is deprecated by MIB-II. That is, it is being included + +-- solely for compatibility with MIB-I nodes, and will most +-- likely be excluded from MIB-III nodes. From MIB-II and +-- onwards, each network protocol group contains its own +-- address translation tables. + +-- The Address Translation group contains one table which is +-- the union across all interfaces of the translation tables +-- for converting a NetworkAddress (e.g., an IP address) into +-- a subnetwork-specific address. For lack of a better term, +-- this document refers to such a subnetwork-specific address +-- as a `physical' address. + +-- Examples of such translation tables are: for broadcast +-- media where ARP is in use, the translation table is +-- equivalent to the ARP cache; or, on an X.25 network where +-- non-algorithmic translation to X.121 addresses is +-- required, the translation table contains the +-- NetworkAddress to X.121 address equivalences. + +atTable OBJECT-TYPE + SYNTAX SEQUENCE OF AtEntry + ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "The Address Translation tables contain the + NetworkAddress to `physical' address equivalences. + Some interfaces do not use translation tables for + determining address equivalences (e.g., DDN-X.25 + has an algorithmic method); if all interfaces are + of this type, then the Address Translation table + is empty, i.e., has zero entries." + ::= { at 1 } + +atEntry OBJECT-TYPE + SYNTAX AtEntry + ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "Each entry contains one NetworkAddress to + `physical' address equivalence." + INDEX { atIfIndex, + atNetAddress } + ::= { atTable 1 } + +AtEntry ::= + SEQUENCE { + atIfIndex + INTEGER, + atPhysAddress + PhysAddress, + atNetAddress + NetworkAddress + } + +atIfIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS deprecated + DESCRIPTION + "The interface on which this entry's equivalence + is effective. The interface identified by a + particular value of this index is the same + interface as identified by the same value of + ifIndex." + ::= { atEntry 1 } + +atPhysAddress OBJECT-TYPE + SYNTAX PhysAddress + ACCESS read-write + STATUS deprecated + DESCRIPTION + "The media-dependent `physical' address. + + Setting this object to a null string (one of zero + length) has the effect of invaliding the + corresponding entry in the atTable object. That + is, it effectively dissasociates the interface + identified with said entry from the mapping + identified with said entry. It is an + implementation-specific matter as to whether the + agent removes an invalidated entry from the table. + Accordingly, management stations must be prepared + to receive tabular information from agents that + corresponds to entries not currently in use. + Proper interpretation of such entries requires + examination of the relevant atPhysAddress object." + ::= { atEntry 2 } + +atNetAddress OBJECT-TYPE + SYNTAX NetworkAddress + ACCESS read-write + STATUS deprecated + DESCRIPTION + "The NetworkAddress (e.g., the IP address) + corresponding to the media-dependent `physical' + address." + ::= { atEntry 3 } + + +-- the IP group + +-- Implementation of the IP group is mandatory for all +-- systems. + +ipForwarding OBJECT-TYPE + SYNTAX INTEGER { + forwarding(1), -- acting as a gateway + not-forwarding(2) -- NOT acting as a gateway + } + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The indication of whether this entity is acting + as an IP gateway in respect to the forwarding of + datagrams received by, but not addressed to, this + entity. IP gateways forward datagrams. IP hosts + do not (except those source-routed via the host). + + Note that for some managed nodes, this object may + take on only a subset of the values possible. + Accordingly, it is appropriate for an agent to + return a `badValue' response if a management + station attempts to change this object to an + inappropriate value." + ::= { ip 1 } + +ipDefaultTTL OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The default value inserted into the Time-To-Live + field of the IP header of datagrams originated at + this entity, whenever a TTL value is not supplied + by the transport layer protocol." + ::= { ip 2 } + +ipInReceives OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of input datagrams received from + interfaces, including those received in error." + ::= { ip 3 } + +ipInHdrErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of input datagrams discarded due to + errors in their IP headers, including bad + checksums, version number mismatch, other format + errors, time-to-live exceeded, errors discovered + in processing their IP options, etc." + ::= { ip 4 } + +ipInAddrErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of input datagrams discarded because + the IP address in their IP header's destination + field was not a valid address to be received at + this entity. This count includes invalid + addresses (e.g., 0.0.0.0) and addresses of + unsupported Classes (e.g., Class E). For entities + which are not IP Gateways and therefore do not + forward datagrams, this counter includes datagrams + discarded because the destination address was not + a local address." + ::= { ip 5 } + +ipForwDatagrams OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of input datagrams for which this + entity was not their final IP destination, as a + result of which an attempt was made to find a + route to forward them to that final destination. + In entities which do not act as IP Gateways, this + counter will include only those packets which were + Source-Routed via this entity, and the Source- + Route option processing was successful." + ::= { ip 6 } + +ipInUnknownProtos OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of locally-addressed datagrams + received successfully but discarded because of an + unknown or unsupported protocol." + ::= { ip 7 } + +ipInDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of input IP datagrams for which no + problems were encountered to prevent their + continued processing, but which were discarded + (e.g., for lack of buffer space). Note that this + counter does not include any datagrams discarded + while awaiting re-assembly." + ::= { ip 8 } + +ipInDelivers OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of input datagrams successfully + delivered to IP user-protocols (including ICMP)." + ::= { ip 9 } + +ipOutRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of IP datagrams which local IP + user-protocols (including ICMP) supplied to IP in + requests for transmission. Note that this counter + does not include any datagrams counted in + ipForwDatagrams." + ::= { ip 10 } + +ipOutDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of output IP datagrams for which no + problem was encountered to prevent their + transmission to their destination, but which were + discarded (e.g., for lack of buffer space). Note + that this counter would include datagrams counted + in ipForwDatagrams if any such packets met this + (discretionary) discard criterion." + ::= { ip 11 } + +ipOutNoRoutes OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of IP datagrams discarded because no + route could be found to transmit them to their + destination. Note that this counter includes any + packets counted in ipForwDatagrams which meet this + `no-route' criterion. Note that this includes any + datagarms which a host cannot route because all of + its default gateways are down." + ::= { ip 12 } + +ipReasmTimeout OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The maximum number of seconds which received + fragments are held while they are awaiting + reassembly at this entity." + ::= { ip 13 } + +ipReasmReqds OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of IP fragments received which needed + to be reassembled at this entity." + ::= { ip 14 } + +ipReasmOKs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of IP datagrams successfully re- + assembled." + ::= { ip 15 } + +ipReasmFails OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of failures detected by the IP re- + assembly algorithm (for whatever reason: timed + out, errors, etc). Note that this is not + necessarily a count of discarded IP fragments + since some algorithms (notably the algorithm in + RFC 815) can lose track of the number of fragments + by combining them as they are received." + ::= { ip 16 } + +ipFragOKs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of IP datagrams that have been + successfully fragmented at this entity." + ::= { ip 17 } + +ipFragFails OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of IP datagrams that have been + discarded because they needed to be fragmented at + this entity but could not be, e.g., because their + Don't Fragment flag was set." + ::= { ip 18 } + +ipFragCreates OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of IP datagram fragments that have + been generated as a result of fragmentation at + this entity." + ::= { ip 19 } + +-- the IP address table + +-- The IP address table contains this entity's IP addressing +-- information. + +ipAddrTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpAddrEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "The table of addressing information relevant to + this entity's IP addresses." + ::= { ip 20 } + +ipAddrEntry OBJECT-TYPE + SYNTAX IpAddrEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "The addressing information for one of this + entity's IP addresses." + INDEX { ipAdEntAddr } + ::= { ipAddrTable 1 } + +IpAddrEntry ::= + SEQUENCE { + ipAdEntAddr + IpAddress, + ipAdEntIfIndex + INTEGER, + ipAdEntNetMask + IpAddress, + ipAdEntBcastAddr + INTEGER, + ipAdEntReasmMaxSize + INTEGER (0..65535) + } + +ipAdEntAddr OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The IP address to which this entry's addressing + information pertains." + ::= { ipAddrEntry 1 } + +ipAdEntIfIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The index value which uniquely identifies the + interface to which this entry is applicable. The + interface identified by a particular value of this + index is the same interface as identified by the + same value of ifIndex." + ::= { ipAddrEntry 2 } + +ipAdEntNetMask OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The subnet mask associated with the IP address of + this entry. The value of the mask is an IP + address with all the network bits set to 1 and all + the hosts bits set to 0." + ::= { ipAddrEntry 3 } + +ipAdEntBcastAddr OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The value of the least-significant bit in the IP + broadcast address used for sending datagrams on + the (logical) interface associated with the IP + address of this entry. For example, when the + Internet standard all-ones broadcast address is + used, the value will be 1. This value applies to + both the subnet and network broadcasts addresses + used by the entity on this (logical) interface." + ::= { ipAddrEntry 4 } + +ipAdEntReasmMaxSize OBJECT-TYPE + SYNTAX INTEGER (0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The size of the largest IP datagram which this + entity can re-assemble from incoming IP fragmented + datagrams received on this interface." + ::= { ipAddrEntry 5 } + +-- the IP routing table + +-- The IP routing table contains an entry for each route +-- presently known to this entity. + +ipRouteTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpRouteEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "This entity's IP Routing table." + ::= { ip 21 } + +ipRouteEntry OBJECT-TYPE + SYNTAX IpRouteEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "A route to a particular destination." + INDEX { ipRouteDest } + ::= { ipRouteTable 1 } + +IpRouteEntry ::= + SEQUENCE { + ipRouteDest + IpAddress, + ipRouteIfIndex + INTEGER, + ipRouteMetric1 + INTEGER, + ipRouteMetric2 + INTEGER, + ipRouteMetric3 + INTEGER, + ipRouteMetric4 + INTEGER, + ipRouteNextHop + IpAddress, + ipRouteType + INTEGER, + ipRouteProto + INTEGER, + ipRouteAge + INTEGER, + ipRouteMask + IpAddress, + ipRouteMetric5 + INTEGER, + ipRouteInfo + OBJECT IDENTIFIER + } + +ipRouteDest OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The destination IP address of this route. An + entry with a value of 0.0.0.0 is considered a + default route. Multiple routes to a single + destination can appear in the table, but access to + such multiple entries is dependent on the table- + access mechanisms defined by the network + management protocol in use." + ::= { ipRouteEntry 1 } + +ipRouteIfIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The index value which uniquely identifies the + local interface through which the next hop of this + route should be reached. The interface identified + by a particular value of this index is the same + interface as identified by the same value of + ifIndex." + ::= { ipRouteEntry 2 } + +ipRouteMetric1 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The primary routing metric for this route. The + semantics of this metric are determined by the + routing-protocol specified in the route's + ipRouteProto value. If this metric is not used, + its value should be set to -1." + ::= { ipRouteEntry 3 } + +ipRouteMetric2 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "An alternate routing metric for this route. The + semantics of this metric are determined by the + routing-protocol specified in the route's + ipRouteProto value. If this metric is not used, + its value should be set to -1." + ::= { ipRouteEntry 4 } + +ipRouteMetric3 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "An alternate routing metric for this route. The + semantics of this metric are determined by the + routing-protocol specified in the route's + ipRouteProto value. If this metric is not used, + its value should be set to -1." + ::= { ipRouteEntry 5 } + +ipRouteMetric4 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "An alternate routing metric for this route. The + semantics of this metric are determined by the + routing-protocol specified in the route's + ipRouteProto value. If this metric is not used, + its value should be set to -1." + ::= { ipRouteEntry 6 } + +ipRouteNextHop OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The IP address of the next hop of this route. + (In the case of a route bound to an interface + which is realized via a broadcast media, the value + of this field is the agent's IP address on that + interface.)" + ::= { ipRouteEntry 7 } + +ipRouteType OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + + invalid(2), -- an invalidated route + + -- route to directly + direct(3), -- connected (sub-)network + + -- route to a non-local + indirect(4) -- host/network/sub-network + } + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The type of route. Note that the values + direct(3) and indirect(4) refer to the notion of + direct and indirect routing in the IP + architecture. + + Setting this object to the value invalid(2) has + the effect of invalidating the corresponding entry + in the ipRouteTable object. That is, it + effectively dissasociates the destination + identified with said entry from the route + identified with said entry. It is an + implementation-specific matter as to whether the + agent removes an invalidated entry from the table. + Accordingly, management stations must be prepared + to receive tabular information from agents that + corresponds to entries not currently in use. + Proper interpretation of such entries requires + examination of the relevant ipRouteType object." + ::= { ipRouteEntry 8 } + +ipRouteProto OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + + -- non-protocol information, + -- e.g., manually configured + local(2), -- entries + + -- set via a network + netmgmt(3), -- management protocol + + -- obtained via ICMP, + icmp(4), -- e.g., Redirect + + -- the remaining values are + -- all gateway routing + -- protocols + egp(5), + ggp(6), + hello(7), + rip(8), + is-is(9), + es-is(10), + ciscoIgrp(11), + bbnSpfIgp(12), + ospf(13), + bgp(14) + } + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The routing mechanism via which this route was + learned. Inclusion of values for gateway routing + protocols is not intended to imply that hosts + should support those protocols." + ::= { ipRouteEntry 9 } + +ipRouteAge OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The number of seconds since this route was last + updated or otherwise determined to be correct. + Note that no semantics of `too old' can be implied + except through knowledge of the routing protocol + by which the route was learned." + ::= { ipRouteEntry 10 } + +ipRouteMask OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-write + STATUS mandatory + DESCRIPTION + "Indicate the mask to be logical-ANDed with the + destination address before being compared to the + value in the ipRouteDest field. For those systems + that do not support arbitrary subnet masks, an + agent constructs the value of the ipRouteMask by + determining whether the value of the correspondent + ipRouteDest field belong to a class-A, B, or C + network, and then using one of: + + mask network + 255.0.0.0 class-A + 255.255.0.0 class-B + 255.255.255.0 class-C + If the value of the ipRouteDest is 0.0.0.0 (a + default route), then the mask value is also + 0.0.0.0. It should be noted that all IP routing + subsystems implicitly use this mechanism." + ::= { ipRouteEntry 11 } + +ipRouteMetric5 OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "An alternate routing metric for this route. The + semantics of this metric are determined by the + routing-protocol specified in the route's + ipRouteProto value. If this metric is not used, + its value should be set to -1." + ::= { ipRouteEntry 12 } + +ipRouteInfo OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "A reference to MIB definitions specific to the + particular routing protocol which is responsible + for this route, as determined by the value + specified in the route's ipRouteProto value. If + this information is not present, its value should + be set to the OBJECT IDENTIFIER { 0 0 }, which is + a syntatically valid object identifier, and any + conformant implementation of ASN.1 and BER must be + able to generate and recognize this value." + ::= { ipRouteEntry 13 } + + +-- the IP Address Translation table + +-- The IP address translation table contain the IpAddress to +-- `physical' address equivalences. Some interfaces do not +-- use translation tables for determining address +-- equivalences (e.g., DDN-X.25 has an algorithmic method); +-- if all interfaces are of this type, then the Address +-- Translation table is empty, i.e., has zero entries. + +ipNetToMediaTable OBJECT-TYPE + SYNTAX SEQUENCE OF IpNetToMediaEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "The IP Address Translation table used for mapping + from IP addresses to physical addresses." + ::= { ip 22 } + +ipNetToMediaEntry OBJECT-TYPE + SYNTAX IpNetToMediaEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "Each entry contains one IpAddress to `physical' + address equivalence." + INDEX { ipNetToMediaIfIndex, + ipNetToMediaNetAddress } + ::= { ipNetToMediaTable 1 } + +IpNetToMediaEntry ::= + SEQUENCE { + ipNetToMediaIfIndex + INTEGER, + ipNetToMediaPhysAddress + PhysAddress, + ipNetToMediaNetAddress + IpAddress, + ipNetToMediaType + INTEGER + } + +ipNetToMediaIfIndex OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The interface on which this entry's equivalence + is effective. The interface identified by a + particular value of this index is the same + interface as identified by the same value of + ifIndex." + ::= { ipNetToMediaEntry 1 } + +ipNetToMediaPhysAddress OBJECT-TYPE + SYNTAX PhysAddress + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The media-dependent `physical' address." + ::= { ipNetToMediaEntry 2 } + +ipNetToMediaNetAddress OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The IpAddress corresponding to the media- + dependent `physical' address." + ::= { ipNetToMediaEntry 3 } + +ipNetToMediaType OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + invalid(2), -- an invalidated mapping + dynamic(3), + static(4) + } + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The type of mapping. + + Setting this object to the value invalid(2) has + the effect of invalidating the corresponding entry + in the ipNetToMediaTable. That is, it effectively + dissasociates the interface identified with said + entry from the mapping identified with said entry. + It is an implementation-specific matter as to + whether the agent removes an invalidated entry + from the table. Accordingly, management stations + must be prepared to receive tabular information + from agents that corresponds to entries not + currently in use. Proper interpretation of such + entries requires examination of the relevant + ipNetToMediaType object." + ::= { ipNetToMediaEntry 4 } + + +-- additional IP objects + +ipRoutingDiscards OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of routing entries which were chosen + to be discarded even though they are valid. One + possible reason for discarding such an entry could + be to free-up buffer space for other routing + entries." + ::= { ip 23 } + + +-- the ICMP group + +-- Implementation of the ICMP group is mandatory for all +-- systems. + +icmpInMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of ICMP messages which the + entity received. Note that this counter includes + all those counted by icmpInErrors." + ::= { icmp 1 } + +icmpInErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP messages which the entity + received but determined as having ICMP-specific + errors (bad ICMP checksums, bad length, etc.)." + ::= { icmp 2 } + +icmpInDestUnreachs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Destination Unreachable + messages received." + ::= { icmp 3 } + +icmpInTimeExcds OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Time Exceeded messages + received." + ::= { icmp 4 } + +icmpInParmProbs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Parameter Problem messages + received." + ::= { icmp 5 } + +icmpInSrcQuenchs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Source Quench messages + received." + ::= { icmp 6 } + +icmpInRedirects OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Redirect messages received." + ::= { icmp 7 } + +icmpInEchos OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Echo (request) messages + received." + ::= { icmp 8 } + +icmpInEchoReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Echo Reply messages received." + ::= { icmp 9 } + +icmpInTimestamps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Timestamp (request) messages + received." + ::= { icmp 10 } + +icmpInTimestampReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Timestamp Reply messages + received." + ::= { icmp 11 } + +icmpInAddrMasks OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Address Mask Request messages + received." + ::= { icmp 12 } + +icmpInAddrMaskReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Address Mask Reply messages + received." + ::= { icmp 13 } + +icmpOutMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of ICMP messages which this + entity attempted to send. Note that this counter + includes all those counted by icmpOutErrors." + ::= { icmp 14 } + +icmpOutErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP messages which this entity did + not send due to problems discovered within ICMP + such as a lack of buffers. This value should not + include errors discovered outside the ICMP layer + such as the inability of IP to route the resultant + datagram. In some implementations there may be no + types of error which contribute to this counter's + value." + ::= { icmp 15 } + +icmpOutDestUnreachs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Destination Unreachable + messages sent." + ::= { icmp 16 } + +icmpOutTimeExcds OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Time Exceeded messages sent." + ::= { icmp 17 } + +icmpOutParmProbs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Parameter Problem messages + sent." + ::= { icmp 18 } + +icmpOutSrcQuenchs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Source Quench messages sent." + ::= { icmp 19 } + +icmpOutRedirects OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Redirect messages sent. For a + host, this object will always be zero, since hosts + do not send redirects." + ::= { icmp 20 } + +icmpOutEchos OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Echo (request) messages sent." + ::= { icmp 21 } + +icmpOutEchoReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Echo Reply messages sent." + ::= { icmp 22 } + +icmpOutTimestamps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Timestamp (request) messages + sent." + ::= { icmp 23 } + +icmpOutTimestampReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Timestamp Reply messages + sent." + ::= { icmp 24 } + +icmpOutAddrMasks OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Address Mask Request messages + sent." + ::= { icmp 25 } + +icmpOutAddrMaskReps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of ICMP Address Mask Reply messages + sent." + ::= { icmp 26 } + + +-- the TCP group + +-- Implementation of the TCP group is mandatory for all +-- systems that implement the TCP. + +-- Note that instances of object types that represent +-- information about a particular TCP connection are +-- transient; they persist only as long as the connection +-- in question. + +tcpRtoAlgorithm OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + + constant(2), -- a constant rto + rsre(3), -- MIL-STD-1778, Appendix B + vanj(4) -- Van Jacobson's algorithm [10] + } + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The algorithm used to determine the timeout value + used for retransmitting unacknowledged octets." + ::= { tcp 1 } + +tcpRtoMin OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The minimum value permitted by a TCP + implementation for the retransmission timeout, + measured in milliseconds. More refined semantics + for objects of this type depend upon the algorithm + used to determine the retransmission timeout. In + particular, when the timeout algorithm is rsre(3), + an object of this type has the semantics of the + LBOUND quantity described in RFC 793." + ::= { tcp 2 } + + +tcpRtoMax OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The maximum value permitted by a TCP + implementation for the retransmission timeout, + measured in milliseconds. More refined semantics + for objects of this type depend upon the algorithm + used to determine the retransmission timeout. In + particular, when the timeout algorithm is rsre(3), + an object of this type has the semantics of the + UBOUND quantity described in RFC 793." + ::= { tcp 3 } + +tcpMaxConn OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The limit on the total number of TCP connections + the entity can support. In entities where the + maximum number of connections is dynamic, this + object should contain the value -1." + ::= { tcp 4 } + +tcpActiveOpens OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of times TCP connections have made a + direct transition to the SYN-SENT state from the + CLOSED state." + ::= { tcp 5 } + +tcpPassiveOpens OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of times TCP connections have made a + direct transition to the SYN-RCVD state from the + LISTEN state." + ::= { tcp 6 } + +tcpAttemptFails OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of times TCP connections have made a + direct transition to the CLOSED state from either + the SYN-SENT state or the SYN-RCVD state, plus the + number of times TCP connections have made a direct + transition to the LISTEN state from the SYN-RCVD + state." + ::= { tcp 7 } + +tcpEstabResets OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of times TCP connections have made a + direct transition to the CLOSED state from either + the ESTABLISHED state or the CLOSE-WAIT state." + ::= { tcp 8 } + +tcpCurrEstab OBJECT-TYPE + SYNTAX Gauge + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of TCP connections for which the + current state is either ESTABLISHED or CLOSE- + WAIT." + ::= { tcp 9 } + +tcpInSegs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of segments received, including + those received in error. This count includes + segments received on currently established + connections." + ::= { tcp 10 } + +tcpOutSegs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of segments sent, including + those on current connections but excluding those + containing only retransmitted octets." + ::= { tcp 11 } + +tcpRetransSegs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of segments retransmitted - that + is, the number of TCP segments transmitted + containing one or more previously transmitted + octets." + ::= { tcp 12 } + + +-- the TCP Connection table + +-- The TCP connection table contains information about this +-- entity's existing TCP connections. + +tcpConnTable OBJECT-TYPE + SYNTAX SEQUENCE OF TcpConnEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "A table containing TCP connection-specific + information." + ::= { tcp 13 } + +tcpConnEntry OBJECT-TYPE + SYNTAX TcpConnEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "Information about a particular current TCP + connection. An object of this type is transient, + in that it ceases to exist when (or soon after) + the connection makes the transition to the CLOSED + state." + INDEX { tcpConnLocalAddress, + tcpConnLocalPort, + tcpConnRemAddress, + tcpConnRemPort } + ::= { tcpConnTable 1 } + +TcpConnEntry ::= + SEQUENCE { + tcpConnState + INTEGER, + tcpConnLocalAddress + IpAddress, + tcpConnLocalPort + INTEGER (0..65535), + tcpConnRemAddress + IpAddress, + tcpConnRemPort + INTEGER (0..65535) + } + +tcpConnState OBJECT-TYPE + SYNTAX INTEGER { + closed(1), + listen(2), + synSent(3), + synReceived(4), + established(5), + finWait1(6), + finWait2(7), + closeWait(8), + lastAck(9), + closing(10), + timeWait(11), + deleteTCB(12) + } + ACCESS read-write + STATUS mandatory + DESCRIPTION + "The state of this TCP connection. + + The only value which may be set by a management + station is deleteTCB(12). Accordingly, it is + appropriate for an agent to return a `badValue' + response if a management station attempts to set + this object to any other value. + + If a management station sets this object to the + value deleteTCB(12), then this has the effect of + deleting the TCB (as defined in RFC 793) of the + corresponding connection on the managed node, + resulting in immediate termination of the + connection. + + As an implementation-specific option, a RST + segment may be sent from the managed node to the + other TCP endpoint (note however that RST segments + are not sent reliably)." + ::= { tcpConnEntry 1 } + +tcpConnLocalAddress OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The local IP address for this TCP connection. In + the case of a connection in the listen state which + is willing to accept connections for any IP + interface associated with the node, the value + 0.0.0.0 is used." + ::= { tcpConnEntry 2 } + +tcpConnLocalPort OBJECT-TYPE + SYNTAX INTEGER (0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The local port number for this TCP connection." + ::= { tcpConnEntry 3 } + +tcpConnRemAddress OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The remote IP address for this TCP connection." + ::= { tcpConnEntry 4 } + +tcpConnRemPort OBJECT-TYPE + SYNTAX INTEGER (0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The remote port number for this TCP connection." + ::= { tcpConnEntry 5 } + + +-- additional TCP objects + +tcpInErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of segments received in error + (e.g., bad TCP checksums)." + ::= { tcp 14 } + +tcpOutRsts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of TCP segments sent containing the + RST flag." + ::= { tcp 15 } + + +-- the UDP group + +-- Implementation of the UDP group is mandatory for all +-- systems which implement the UDP. + +udpInDatagrams OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of UDP datagrams delivered to + UDP users." + ::= { udp 1 } + +udpNoPorts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of received UDP datagrams for + which there was no application at the destination + port." + ::= { udp 2 } + +udpInErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of received UDP datagrams that could + not be delivered for reasons other than the lack + of an application at the destination port." + ::= { udp 3 } + +udpOutDatagrams OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of UDP datagrams sent from this + entity." + ::= { udp 4 } + + +-- the UDP Listener table + +-- The UDP listener table contains information about this +-- entity's UDP end-points on which a local application is +-- currently accepting datagrams. + +udpTable OBJECT-TYPE + SYNTAX SEQUENCE OF UdpEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "A table containing UDP listener information." + ::= { udp 5 } + +udpEntry OBJECT-TYPE + SYNTAX UdpEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "Information about a particular current UDP + listener." + INDEX { udpLocalAddress, udpLocalPort } + ::= { udpTable 1 } + +UdpEntry ::= + SEQUENCE { + udpLocalAddress + IpAddress, + udpLocalPort + INTEGER (0..65535) + } + +udpLocalAddress OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The local IP address for this UDP listener. In + the case of a UDP listener which is willing to + accept datagrams for any IP interface associated + with the node, the value 0.0.0.0 is used." + ::= { udpEntry 1 } + +udpLocalPort OBJECT-TYPE + SYNTAX INTEGER (0..65535) + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The local port number for this UDP listener." + ::= { udpEntry 2 } + + +-- the EGP group + +-- Implementation of the EGP group is mandatory for all +-- systems which implement the EGP. + +egpInMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of EGP messages received without + error." + ::= { egp 1 } + +egpInErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of EGP messages received that proved + to be in error." + ::= { egp 2 } + +egpOutMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of locally generated EGP + messages." + ::= { egp 3 } + +egpOutErrors OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of locally generated EGP messages not + sent due to resource limitations within an EGP + entity." + ::= { egp 4 } + + +-- the EGP Neighbor table + +-- The EGP neighbor table contains information about this +-- entity's EGP neighbors. + +egpNeighTable OBJECT-TYPE + SYNTAX SEQUENCE OF EgpNeighEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "The EGP neighbor table." + ::= { egp 5 } + +egpNeighEntry OBJECT-TYPE + SYNTAX EgpNeighEntry + ACCESS not-accessible + STATUS mandatory + DESCRIPTION + "Information about this entity's relationship with + a particular EGP neighbor." + INDEX { egpNeighAddr } + ::= { egpNeighTable 1 } + +EgpNeighEntry ::= + SEQUENCE { + egpNeighState + INTEGER, + egpNeighAddr + IpAddress, + egpNeighAs + INTEGER, + egpNeighInMsgs + Counter, + egpNeighInErrs + Counter, + egpNeighOutMsgs + Counter, + egpNeighOutErrs + Counter, + egpNeighInErrMsgs + Counter, + egpNeighOutErrMsgs + Counter, + egpNeighStateUps + Counter, + egpNeighStateDowns + Counter, + egpNeighIntervalHello + INTEGER, + egpNeighIntervalPoll + INTEGER, + egpNeighMode + INTEGER, + egpNeighEventTrigger + INTEGER + } + +egpNeighState OBJECT-TYPE + SYNTAX INTEGER { + idle(1), + acquisition(2), + down(3), + up(4), + cease(5) + } + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The EGP state of the local system with respect to + this entry's EGP neighbor. Each EGP state is + represented by a value that is one greater than + the numerical value associated with said state in + RFC 904." + ::= { egpNeighEntry 1 } + +egpNeighAddr OBJECT-TYPE + SYNTAX IpAddress + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The IP address of this entry's EGP neighbor." + ::= { egpNeighEntry 2 } + +egpNeighAs OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The autonomous system of this EGP peer. Zero + should be specified if the autonomous system + number of the neighbor is not yet known." + ::= { egpNeighEntry 3 } + +egpNeighInMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of EGP messages received without error + from this EGP peer." + ::= { egpNeighEntry 4 } + +egpNeighInErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of EGP messages received from this EGP + peer that proved to be in error (e.g., bad EGP + checksum)." + ::= { egpNeighEntry 5 } + +egpNeighOutMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of locally generated EGP messages to + this EGP peer." + ::= { egpNeighEntry 6 } + +egpNeighOutErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of locally generated EGP messages not + sent to this EGP peer due to resource limitations + within an EGP entity." + ::= { egpNeighEntry 7 } + +egpNeighInErrMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of EGP-defined error messages received + from this EGP peer." + ::= { egpNeighEntry 8 } + +egpNeighOutErrMsgs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of EGP-defined error messages sent to + this EGP peer." + ::= { egpNeighEntry 9 } + +egpNeighStateUps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of EGP state transitions to the UP + state with this EGP peer." + ::= { egpNeighEntry 10 } + +egpNeighStateDowns OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The number of EGP state transitions from the UP + state to any other state with this EGP peer." + ::= { egpNeighEntry 11 } + +egpNeighIntervalHello OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The interval between EGP Hello command + retransmissions (in hundredths of a second). This + represents the t1 timer as defined in RFC 904." + ::= { egpNeighEntry 12 } + +egpNeighIntervalPoll OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The interval between EGP poll command + retransmissions (in hundredths of a second). This + represents the t3 timer as defined in RFC 904." + ::= { egpNeighEntry 13 } + +egpNeighMode OBJECT-TYPE + SYNTAX INTEGER { active(1), passive(2) } + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The polling mode of this EGP entity, either + passive or active." + ::= { egpNeighEntry 14 } + +egpNeighEventTrigger OBJECT-TYPE + SYNTAX INTEGER { start(1), stop(2) } + ACCESS read-write + STATUS mandatory + DESCRIPTION + "A control variable used to trigger operator- + initiated Start and Stop events. When read, this + variable always returns the most recent value that + egpNeighEventTrigger was set to. If it has not + been set since the last initialization of the + network management subsystem on the node, it + returns a value of `stop'. + + When set, this variable causes a Start or Stop + event on the specified neighbor, as specified on + pages 8-10 of RFC 904. Briefly, a Start event + causes an Idle peer to begin neighbor acquisition + and a non-Idle peer to reinitiate neighbor + acquisition. A stop event causes a non-Idle peer + to return to the Idle state until a Start event + occurs, either via egpNeighEventTrigger or + otherwise." + ::= { egpNeighEntry 15 } + + +-- additional EGP objects + +egpAs OBJECT-TYPE + SYNTAX INTEGER + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The autonomous system number of this EGP entity." + ::= { egp 6 } + +-- the Transmission group + +-- Based on the transmission media underlying each interface +-- on a system, the corresponding portion of the Transmission +-- group is mandatory for that system. + +-- When Internet-standard definitions for managing +-- transmission media are defined, the transmission group is +-- used to provide a prefix for the names of those objects. + +-- Typically, such definitions reside in the experimental +-- portion of the MIB until they are "proven", then as a +-- part of the Internet standardization process, the +-- definitions are accordingly elevated and a new object +-- identifier, under the transmission group is defined. By +-- convention, the name assigned is: +-- +-- type OBJECT IDENTIFIER ::= { transmission number } +-- +-- where "type" is the symbolic value used for the media in +-- the ifType column of the ifTable object, and "number" is +-- the actual integer value corresponding to the symbol. + + +-- the SNMP group + +-- Implementation of the SNMP group is mandatory for all +-- systems which support an SNMP protocol entity. Some of +-- the objects defined below will be zero-valued in those +-- SNMP implementations that are optimized to support only +-- those functions specific to either a management agent or +-- a management station. In particular, it should be +-- observed that the objects below refer to an SNMP entity, +-- and there may be several SNMP entities residing on a +-- managed node (e.g., if the node is hosting acting as +-- a management station). + +snmpInPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of Messages delivered to the + SNMP entity from the transport service." + ::= { snmp 1 } + +snmpOutPkts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Messages which were + passed from the SNMP protocol entity to the + transport service." + ::= { snmp 2 } + +snmpInBadVersions OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Messages which were + delivered to the SNMP protocol entity and were for + an unsupported SNMP version." + ::= { snmp 3 } + +snmpInBadCommunityNames OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Messages delivered to + the SNMP protocol entity which used a SNMP + community name not known to said entity." + ::= { snmp 4 } + +snmpInBadCommunityUses OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Messages delivered to + the SNMP protocol entity which represented an SNMP + operation which was not allowed by the SNMP + community named in the Message." + ::= { snmp 5 } + +snmpInASNParseErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of ASN.1 or BER errors + encountered by the SNMP protocol entity when + decoding received SNMP Messages." + ::= { snmp 6 } + +-- { snmp 7 } is not used + +snmpInTooBigs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP PDUs which were + delivered to the SNMP protocol entity and for + which the value of the error-status field is + `tooBig'." + ::= { snmp 8 } + +snmpInNoSuchNames OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP PDUs which were + delivered to the SNMP protocol entity and for + which the value of the error-status field is + `noSuchName'." + ::= { snmp 9 } + +snmpInBadValues OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP PDUs which were + delivered to the SNMP protocol entity and for + which the value of the error-status field is + `badValue'." + ::= { snmp 10 } + +snmpInReadOnlys OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number valid SNMP PDUs which were + delivered to the SNMP protocol entity and for + which the value of the error-status field is + `readOnly'. It should be noted that it is a + protocol error to generate an SNMP PDU which + contains the value `readOnly' in the error-status + field, as such this object is provided as a means + of detecting incorrect implementations of the + SNMP." + ::= { snmp 11 } + +snmpInGenErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP PDUs which were + delivered to the SNMP protocol entity and for + which the value of the error-status field is + `genErr'." + ::= { snmp 12 } + +snmpInTotalReqVars OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of MIB objects which have been + retrieved successfully by the SNMP protocol entity + as the result of receiving valid SNMP Get-Request + and Get-Next PDUs." + ::= { snmp 13 } + +snmpInTotalSetVars OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of MIB objects which have been + altered successfully by the SNMP protocol entity + as the result of receiving valid SNMP Set-Request + PDUs." + ::= { snmp 14 } + +snmpInGetRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Get-Request PDUs which + have been accepted and processed by the SNMP + protocol entity." + ::= { snmp 15 } + +snmpInGetNexts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Get-Next PDUs which have + been accepted and processed by the SNMP protocol + entity." + ::= { snmp 16 } + +snmpInSetRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Set-Request PDUs which + have been accepted and processed by the SNMP + protocol entity." + ::= { snmp 17 } + +snmpInGetResponses OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Get-Response PDUs which + have been accepted and processed by the SNMP + protocol entity." + ::= { snmp 18 } + +snmpInTraps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Trap PDUs which have + been accepted and processed by the SNMP protocol + entity." + ::= { snmp 19 } + +snmpOutTooBigs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP PDUs which were + generated by the SNMP protocol entity and for + which the value of the error-status field is + `tooBig.'" + ::= { snmp 20 } + +snmpOutNoSuchNames OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP PDUs which were + generated by the SNMP protocol entity and for + which the value of the error-status is + `noSuchName'." + ::= { snmp 21 } + +snmpOutBadValues OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP PDUs which were + generated by the SNMP protocol entity and for + which the value of the error-status field is + `badValue'." + ::= { snmp 22 } + +-- { snmp 23 } is not used + +snmpOutGenErrs OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP PDUs which were + generated by the SNMP protocol entity and for + which the value of the error-status field is + `genErr'." + ::= { snmp 24 } + +snmpOutGetRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Get-Request PDUs which + have been generated by the SNMP protocol entity." + ::= { snmp 25 } + +snmpOutGetNexts OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Get-Next PDUs which have + been generated by the SNMP protocol entity." + ::= { snmp 26 } + +snmpOutSetRequests OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Set-Request PDUs which + have been generated by the SNMP protocol entity." + ::= { snmp 27 } + +snmpOutGetResponses OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Get-Response PDUs which + have been generated by the SNMP protocol entity." + ::= { snmp 28 } + +snmpOutTraps OBJECT-TYPE + SYNTAX Counter + ACCESS read-only + STATUS mandatory + DESCRIPTION + "The total number of SNMP Trap PDUs which have + been generated by the SNMP protocol entity." + ::= { snmp 29 } + +snmpEnableAuthenTraps OBJECT-TYPE + SYNTAX INTEGER { enabled(1), disabled(2) } + ACCESS read-write + STATUS mandatory + DESCRIPTION + "Indicates whether the SNMP agent process is + permitted to generate authentication-failure + traps. The value of this object overrides any + configuration information; as such, it provides a + means whereby all authentication-failure traps may + be disabled. + + Note that it is strongly recommended that this + object be stored in non-volatile memory so that it + remains constant between re-initializations of the + network management system." + ::= { snmp 30 } + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-CONF b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-CONF new file mode 100644 index 0000000..782710b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-CONF @@ -0,0 +1,318 @@ +SNMPv2-CONF DEFINITIONS ::= BEGIN + +IMPORTS ObjectName, NotificationName, ObjectSyntax + FROM SNMPv2-SMI; + +-- definitions for conformance groups + +OBJECT-GROUP MACRO ::= +BEGIN + TYPE NOTATION ::= + ObjectsPart + "STATUS" Status + "DESCRIPTION" Text + ReferPart + + VALUE NOTATION ::= + value(VALUE OBJECT IDENTIFIER) + + ObjectsPart ::= + "OBJECTS" "{" Objects "}" + Objects ::= + Object + | Objects "," Object + Object ::= + value(ObjectName) + + Status ::= + "current" + | "deprecated" + | "obsolete" + + ReferPart ::= + "REFERENCE" Text + | empty + + -- a character string as defined in [2] + Text ::= value(IA5String) +END + +-- more definitions for conformance groups + +NOTIFICATION-GROUP MACRO ::= +BEGIN + TYPE NOTATION ::= + NotificationsPart + "STATUS" Status + "DESCRIPTION" Text + ReferPart + + VALUE NOTATION ::= + value(VALUE OBJECT IDENTIFIER) + + NotificationsPart ::= + "NOTIFICATIONS" "{" Notifications "}" + Notifications ::= + Notification + | Notifications "," Notification + Notification ::= + value(NotificationName) + + Status ::= + "current" + | "deprecated" + | "obsolete" + + ReferPart ::= + "REFERENCE" Text + | empty + + -- a character string as defined in [2] + Text ::= value(IA5String) +END + +-- definitions for compliance statements + +MODULE-COMPLIANCE MACRO ::= +BEGIN + TYPE NOTATION ::= + "STATUS" Status + "DESCRIPTION" Text + ReferPart + ModulePart + + VALUE NOTATION ::= + value(VALUE OBJECT IDENTIFIER) + + Status ::= + "current" + | "deprecated" + | "obsolete" + + ReferPart ::= + "REFERENCE" Text + | empty + + ModulePart ::= + Modules + Modules ::= + Module + | Modules Module + Module ::= + -- name of module -- + "MODULE" ModuleName + MandatoryPart + CompliancePart + + ModuleName ::= + -- identifier must start with uppercase letter + identifier ModuleIdentifier + -- must not be empty unless contained + -- in MIB Module + | empty + ModuleIdentifier ::= + value(OBJECT IDENTIFIER) + | empty + + MandatoryPart ::= + "MANDATORY-GROUPS" "{" Groups "}" + | empty + + Groups ::= + Group + | Groups "," Group + Group ::= + value(OBJECT IDENTIFIER) + + CompliancePart ::= + Compliances + | empty + + Compliances ::= + Compliance + | Compliances Compliance + Compliance ::= + ComplianceGroup + | Object + + ComplianceGroup ::= + "GROUP" value(OBJECT IDENTIFIER) + "DESCRIPTION" Text + + Object ::= + "OBJECT" value(ObjectName) + SyntaxPart + WriteSyntaxPart + AccessPart + "DESCRIPTION" Text + + -- must be a refinement for object's SYNTAX clause + SyntaxPart ::= "SYNTAX" Syntax + | empty + + -- must be a refinement for object's SYNTAX clause + WriteSyntaxPart ::= "WRITE-SYNTAX" Syntax + | empty + + Syntax ::= -- Must be one of the following: + -- a base type (or its refinement), + -- a textual convention (or its refinement), or + -- a BITS pseudo-type + type + | "BITS" "{" NamedBits "}" + + NamedBits ::= NamedBit + | NamedBits "," NamedBit + + NamedBit ::= identifier "(" number ")" -- number is nonnegative + + AccessPart ::= + "MIN-ACCESS" Access + | empty + Access ::= + "not-accessible" + | "accessible-for-notify" + | "read-only" + | "read-write" + | "read-create" + + -- a character string as defined in [2] + Text ::= value(IA5String) +END + +-- definitions for capabilities statements + +AGENT-CAPABILITIES MACRO ::= +BEGIN + TYPE NOTATION ::= + "PRODUCT-RELEASE" Text + "STATUS" Status + "DESCRIPTION" Text + ReferPart + ModulePart + + VALUE NOTATION ::= + value(VALUE OBJECT IDENTIFIER) + + Status ::= + "current" + | "obsolete" + + ReferPart ::= + "REFERENCE" Text + | empty + + ModulePart ::= + Modules + | empty + Modules ::= + Module + | Modules Module + Module ::= + -- name of module -- + "SUPPORTS" ModuleName + "INCLUDES" "{" Groups "}" + VariationPart + + ModuleName ::= + -- identifier must start with uppercase letter + identifier ModuleIdentifier + ModuleIdentifier ::= + value(OBJECT IDENTIFIER) + | empty + + Groups ::= + Group + | Groups "," Group + Group ::= + value(OBJECT IDENTIFIER) + + VariationPart ::= + Variations + | empty + Variations ::= + Variation + | Variations Variation + + Variation ::= + ObjectVariation + | NotificationVariation + + NotificationVariation ::= + "VARIATION" value(NotificationName) + AccessPart + "DESCRIPTION" Text + + ObjectVariation ::= + "VARIATION" value(ObjectName) + SyntaxPart + WriteSyntaxPart + AccessPart + CreationPart + DefValPart + "DESCRIPTION" Text + + -- must be a refinement for object's SYNTAX clause + SyntaxPart ::= "SYNTAX" Syntax + | empty + + WriteSyntaxPart ::= "WRITE-SYNTAX" Syntax + | empty + + Syntax ::= -- Must be one of the following: + -- a base type (or its refinement), + -- a textual convention (or its refinement), or + -- a BITS pseudo-type + type + | "BITS" "{" NamedBits "}" + + NamedBits ::= NamedBit + | NamedBits "," NamedBit + + NamedBit ::= identifier "(" number ")" -- number is nonnegative + + AccessPart ::= + "ACCESS" Access + | empty + + Access ::= + "not-implemented" + -- only "not-implemented" for notifications + | "accessible-for-notify" + | "read-only" + | "read-write" + | "read-create" + -- following is for backward-compatibility only + | "write-only" + + CreationPart ::= + "CREATION-REQUIRES" "{" Cells "}" + | empty + Cells ::= + Cell + | Cells "," Cell + Cell ::= + value(ObjectName) + + DefValPart ::= "DEFVAL" "{" Defvalue "}" + | empty + + Defvalue ::= -- must be valid for the object's syntax + -- in this macro's SYNTAX clause, if present, + -- or if not, in object's OBJECT-TYPE macro + value(ObjectSyntax) + | "{" BitsValue "}" + + BitsValue ::= BitNames + | empty + + BitNames ::= BitName + | BitNames "," BitName + + BitName ::= identifier + + -- a character string as defined in [2] + Text ::= value(IA5String) +END + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-MIB new file mode 100644 index 0000000..e93b607 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-MIB @@ -0,0 +1,903 @@ +SNMPv2-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, + TimeTicks, Counter32, snmpModules, mib-2 + FROM SNMPv2-SMI + DisplayString, TestAndIncr, TimeStamp + + + + FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP + FROM SNMPv2-CONF; + +snmpMIB MODULE-IDENTITY + LAST-UPDATED "200210160000Z" + ORGANIZATION "IETF SNMPv3 Working Group" + CONTACT-INFO + "WG-EMail: snmpv3@lists.tislabs.com + Subscribe: snmpv3-request@lists.tislabs.com + + Co-Chair: Russ Mundy + Network Associates Laboratories + postal: 15204 Omega Drive, Suite 300 + Rockville, MD 20850-4601 + USA + EMail: mundy@tislabs.com + phone: +1 301 947-7107 + + Co-Chair: David Harrington + Enterasys Networks + postal: 35 Industrial Way + P. O. Box 5005 + Rochester, NH 03866-5005 + USA + EMail: dbh@enterasys.com + phone: +1 603 337-2614 + + Editor: Randy Presuhn + BMC Software, Inc. + postal: 2141 North First Street + San Jose, CA 95131 + USA + EMail: randy_presuhn@bmc.com + phone: +1 408 546-1006" + DESCRIPTION + "The MIB module for SNMP entities. + + Copyright (C) The Internet Society (2002). This + version of this MIB module is part of RFC 3418; + see the RFC itself for full legal notices. + " + REVISION "200210160000Z" + DESCRIPTION + "This revision of this MIB module was published as + RFC 3418." + REVISION "199511090000Z" + DESCRIPTION + + + + "This revision of this MIB module was published as + RFC 1907." + REVISION "199304010000Z" + DESCRIPTION + "The initial revision of this MIB module was published + as RFC 1450." + ::= { snmpModules 1 } + +snmpMIBObjects OBJECT IDENTIFIER ::= { snmpMIB 1 } + +-- ::= { snmpMIBObjects 1 } this OID is obsolete +-- ::= { snmpMIBObjects 2 } this OID is obsolete +-- ::= { snmpMIBObjects 3 } this OID is obsolete + +-- the System group +-- +-- a collection of objects common to all managed systems. + +system OBJECT IDENTIFIER ::= { mib-2 1 } + +sysDescr OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A textual description of the entity. This value should + include the full name and version identification of + the system's hardware type, software operating-system, + and networking software." + ::= { system 1 } + +sysObjectID OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The vendor's authoritative identification of the + network management subsystem contained in the entity. + This value is allocated within the SMI enterprises + subtree (1.3.6.1.4.1) and provides an easy and + unambiguous means for determining `what kind of box' is + being managed. For example, if vendor `Flintstones, + Inc.' was assigned the subtree 1.3.6.1.4.1.424242, + it could assign the identifier 1.3.6.1.4.1.424242.1.1 + to its `Fred Router'." + ::= { system 2 } + +sysUpTime OBJECT-TYPE + + + + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The time (in hundredths of a second) since the + network management portion of the system was last + re-initialized." + ::= { system 3 } + +sysContact OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The textual identification of the contact person for + this managed node, together with information on how + to contact this person. If no contact information is + known, the value is the zero-length string." + ::= { system 4 } + +sysName OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "An administratively-assigned name for this managed + node. By convention, this is the node's fully-qualified + domain name. If the name is unknown, the value is + the zero-length string." + ::= { system 5 } + +sysLocation OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The physical location of this node (e.g., 'telephone + closet, 3rd floor'). If the location is unknown, the + value is the zero-length string." + ::= { system 6 } + +sysServices OBJECT-TYPE + SYNTAX INTEGER (0..127) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A value which indicates the set of services that this + entity may potentially offer. The value is a sum. + + + + This sum initially takes the value zero. Then, for + each layer, L, in the range 1 through 7, that this node + performs transactions for, 2 raised to (L - 1) is added + to the sum. For example, a node which performs only + routing functions would have a value of 4 (2^(3-1)). + In contrast, a node which is a host offering application + services would have a value of 72 (2^(4-1) + 2^(7-1)). + Note that in the context of the Internet suite of + protocols, values should be calculated accordingly: + + layer functionality + 1 physical (e.g., repeaters) + 2 datalink/subnetwork (e.g., bridges) + 3 internet (e.g., supports the IP) + 4 end-to-end (e.g., supports the TCP) + 7 applications (e.g., supports the SMTP) + + For systems including OSI protocols, layers 5 and 6 + may also be counted." + ::= { system 7 } + +-- object resource information +-- +-- a collection of objects which describe the SNMP entity's +-- (statically and dynamically configurable) support of +-- various MIB modules. + +sysORLastChange OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the time of the most recent + change in state or value of any instance of sysORID." + ::= { system 8 } + +sysORTable OBJECT-TYPE + SYNTAX SEQUENCE OF SysOREntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The (conceptual) table listing the capabilities of + the local SNMP application acting as a command + responder with respect to various MIB modules. + SNMP entities having dynamically-configurable support + of MIB modules will have a dynamically-varying number + of conceptual rows." + ::= { system 9 } + + + +sysOREntry OBJECT-TYPE + SYNTAX SysOREntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry (conceptual row) in the sysORTable." + INDEX { sysORIndex } + ::= { sysORTable 1 } + +SysOREntry ::= SEQUENCE { + sysORIndex INTEGER, + sysORID OBJECT IDENTIFIER, + sysORDescr DisplayString, + sysORUpTime TimeStamp +} + +sysORIndex OBJECT-TYPE + SYNTAX INTEGER (1..2147483647) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The auxiliary variable used for identifying instances + of the columnar objects in the sysORTable." + ::= { sysOREntry 1 } + +sysORID OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An authoritative identification of a capabilities + statement with respect to various MIB modules supported + by the local SNMP application acting as a command + responder." + ::= { sysOREntry 2 } + +sysORDescr OBJECT-TYPE + SYNTAX DisplayString + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A textual description of the capabilities identified + by the corresponding instance of sysORID." + ::= { sysOREntry 3 } + +sysORUpTime OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + + + + STATUS current + DESCRIPTION + "The value of sysUpTime at the time this conceptual + row was last instantiated." + ::= { sysOREntry 4 } + + +-- the SNMP group +-- +-- a collection of objects providing basic instrumentation and +-- control of an SNMP entity. + +snmp OBJECT IDENTIFIER ::= { mib-2 11 } + +snmpInPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of messages delivered to the SNMP + entity from the transport service." + ::= { snmp 1 } + +snmpInBadVersions OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SNMP messages which were delivered + to the SNMP entity and were for an unsupported SNMP + version." + ::= { snmp 3 } + +snmpInBadCommunityNames OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of community-based SNMP messages (for + example, SNMPv1) delivered to the SNMP entity which + used an SNMP community name not known to said entity. + Also, implementations which authenticate community-based + SNMP messages using check(s) in addition to matching + the community name (for example, by also checking + whether the message originated from a transport address + allowed to use a specified community name) MAY include + in this value the number of messages which failed the + additional check(s). It is strongly RECOMMENDED that + + + + the documentation for any security model which is used + to authenticate community-based SNMP messages specify + the precise conditions that contribute to this value." + ::= { snmp 4 } + +snmpInBadCommunityUses OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of community-based SNMP messages (for + example, SNMPv1) delivered to the SNMP entity which + represented an SNMP operation that was not allowed for + the SNMP community named in the message. The precise + conditions under which this counter is incremented + (if at all) depend on how the SNMP entity implements + its access control mechanism and how its applications + interact with that access control mechanism. It is + strongly RECOMMENDED that the documentation for any + access control mechanism which is used to control access + to and visibility of MIB instrumentation specify the + precise conditions that contribute to this value." + ::= { snmp 5 } + +snmpInASNParseErrs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of ASN.1 or BER errors encountered by + the SNMP entity when decoding received SNMP messages." + ::= { snmp 6 } + +snmpEnableAuthenTraps OBJECT-TYPE + SYNTAX INTEGER { enabled(1), disabled(2) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Indicates whether the SNMP entity is permitted to + generate authenticationFailure traps. The value of this + object overrides any configuration information; as such, + it provides a means whereby all authenticationFailure + traps may be disabled. + + Note that it is strongly recommended that this object + be stored in non-volatile memory so that it remains + constant across re-initializations of the network + management system." + + + + ::= { snmp 30 } + +snmpSilentDrops OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Confirmed Class PDUs (such as + GetRequest-PDUs, GetNextRequest-PDUs, + GetBulkRequest-PDUs, SetRequest-PDUs, and + InformRequest-PDUs) delivered to the SNMP entity which + were silently dropped because the size of a reply + containing an alternate Response Class PDU (such as a + Response-PDU) with an empty variable-bindings field + was greater than either a local constraint or the + maximum message size associated with the originator of + the request." + ::= { snmp 31 } + +snmpProxyDrops OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Confirmed Class PDUs + (such as GetRequest-PDUs, GetNextRequest-PDUs, + GetBulkRequest-PDUs, SetRequest-PDUs, and + InformRequest-PDUs) delivered to the SNMP entity which + were silently dropped because the transmission of + the (possibly translated) message to a proxy target + failed in a manner (other than a time-out) such that + no Response Class PDU (such as a Response-PDU) could + be returned." + ::= { snmp 32 } + +-- information for notifications +-- +-- a collection of objects which allow the SNMP entity, when +-- supporting a notification originator application, +-- to be configured to generate SNMPv2-Trap-PDUs. + +snmpTrap OBJECT IDENTIFIER ::= { snmpMIBObjects 4 } + +snmpTrapOID OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + + + + "The authoritative identification of the notification + currently being sent. This variable occurs as + the second varbind in every SNMPv2-Trap-PDU and + InformRequest-PDU." + ::= { snmpTrap 1 } + +-- ::= { snmpTrap 2 } this OID is obsolete + +snmpTrapEnterprise OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The authoritative identification of the enterprise + associated with the trap currently being sent. When an + SNMP proxy agent is mapping an RFC1157 Trap-PDU + into a SNMPv2-Trap-PDU, this variable occurs as the + last varbind." + ::= { snmpTrap 3 } + +-- ::= { snmpTrap 4 } this OID is obsolete + + +-- well-known traps + +snmpTraps OBJECT IDENTIFIER ::= { snmpMIBObjects 5 } + +coldStart NOTIFICATION-TYPE + STATUS current + DESCRIPTION + "A coldStart trap signifies that the SNMP entity, + supporting a notification originator application, is + reinitializing itself and that its configuration may + have been altered." + ::= { snmpTraps 1 } + +warmStart NOTIFICATION-TYPE + STATUS current + DESCRIPTION + "A warmStart trap signifies that the SNMP entity, + supporting a notification originator application, + is reinitializing itself such that its configuration + is unaltered." + ::= { snmpTraps 2 } + +-- Note the linkDown NOTIFICATION-TYPE ::= { snmpTraps 3 } +-- and the linkUp NOTIFICATION-TYPE ::= { snmpTraps 4 } +-- are defined in RFC 2863 [RFC2863] + + + +authenticationFailure NOTIFICATION-TYPE + STATUS current + DESCRIPTION + "An authenticationFailure trap signifies that the SNMP + entity has received a protocol message that is not + properly authenticated. While all implementations + of SNMP entities MAY be capable of generating this + trap, the snmpEnableAuthenTraps object indicates + whether this trap will be generated." + ::= { snmpTraps 5 } + +-- Note the egpNeighborLoss notification is defined +-- as { snmpTraps 6 } in RFC 1213 + +-- the set group +-- +-- a collection of objects which allow several cooperating +-- command generator applications to coordinate their use of the +-- set operation. + +snmpSet OBJECT IDENTIFIER ::= { snmpMIBObjects 6 } + +snmpSetSerialNo OBJECT-TYPE + SYNTAX TestAndIncr + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "An advisory lock used to allow several cooperating + command generator applications to coordinate their + use of the SNMP set operation. + + This object is used for coarse-grain coordination. + To achieve fine-grain coordination, one or more similar + objects might be defined within each MIB group, as + appropriate." + ::= { snmpSet 1 } + +-- conformance information + +snmpMIBConformance + OBJECT IDENTIFIER ::= { snmpMIB 2 } + +snmpMIBCompliances + OBJECT IDENTIFIER ::= { snmpMIBConformance 1 } +snmpMIBGroups OBJECT IDENTIFIER ::= { snmpMIBConformance 2 } + +-- compliance statements + + + + +-- ::= { snmpMIBCompliances 1 } this OID is obsolete +snmpBasicCompliance MODULE-COMPLIANCE + STATUS deprecated + DESCRIPTION + "The compliance statement for SNMPv2 entities which + implement the SNMPv2 MIB. + + This compliance statement is replaced by + snmpBasicComplianceRev2." + MODULE -- this module + MANDATORY-GROUPS { snmpGroup, snmpSetGroup, systemGroup, + snmpBasicNotificationsGroup } + + GROUP snmpCommunityGroup + DESCRIPTION + "This group is mandatory for SNMPv2 entities which + support community-based authentication." + + ::= { snmpMIBCompliances 2 } + +snmpBasicComplianceRev2 MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for SNMP entities which + implement this MIB module." + MODULE -- this module + MANDATORY-GROUPS { snmpGroup, snmpSetGroup, systemGroup, + snmpBasicNotificationsGroup } + + GROUP snmpCommunityGroup + DESCRIPTION + "This group is mandatory for SNMP entities which + support community-based authentication." + + GROUP snmpWarmStartNotificationGroup + DESCRIPTION + "This group is mandatory for an SNMP entity which + supports command responder applications, and is + able to reinitialize itself such that its + configuration is unaltered." + + ::= { snmpMIBCompliances 3 } + +-- units of conformance + +-- ::= { snmpMIBGroups 1 } this OID is obsolete +-- ::= { snmpMIBGroups 2 } this OID is obsolete +-- ::= { snmpMIBGroups 3 } this OID is obsolete + + + +-- ::= { snmpMIBGroups 4 } this OID is obsolete + +snmpGroup OBJECT-GROUP + OBJECTS { snmpInPkts, + snmpInBadVersions, + snmpInASNParseErrs, + snmpSilentDrops, + snmpProxyDrops, + snmpEnableAuthenTraps } + STATUS current + DESCRIPTION + "A collection of objects providing basic instrumentation + and control of an SNMP entity." + ::= { snmpMIBGroups 8 } + +snmpCommunityGroup OBJECT-GROUP + OBJECTS { snmpInBadCommunityNames, + snmpInBadCommunityUses } + STATUS current + DESCRIPTION + "A collection of objects providing basic instrumentation + of a SNMP entity which supports community-based + authentication." + ::= { snmpMIBGroups 9 } + +snmpSetGroup OBJECT-GROUP + OBJECTS { snmpSetSerialNo } + STATUS current + DESCRIPTION + "A collection of objects which allow several cooperating + command generator applications to coordinate their + use of the set operation." + ::= { snmpMIBGroups 5 } + +systemGroup OBJECT-GROUP + OBJECTS { sysDescr, sysObjectID, sysUpTime, + sysContact, sysName, sysLocation, + sysServices, + sysORLastChange, sysORID, + sysORUpTime, sysORDescr } + STATUS current + DESCRIPTION + "The system group defines objects which are common to all + managed systems." + ::= { snmpMIBGroups 6 } + +snmpBasicNotificationsGroup NOTIFICATION-GROUP + NOTIFICATIONS { coldStart, authenticationFailure } + + + + STATUS current + DESCRIPTION + "The basic notifications implemented by an SNMP entity + supporting command responder applications." + ::= { snmpMIBGroups 7 } + +snmpWarmStartNotificationGroup NOTIFICATION-GROUP + NOTIFICATIONS { warmStart } + STATUS current + DESCRIPTION + "An additional notification for an SNMP entity supporting + command responder applications, if it is able to reinitialize + itself such that its configuration is unaltered." + ::= { snmpMIBGroups 11 } + +snmpNotificationGroup OBJECT-GROUP + OBJECTS { snmpTrapOID, snmpTrapEnterprise } + STATUS current + DESCRIPTION + "These objects are required for entities + which support notification originator applications." + ::= { snmpMIBGroups 12 } + +-- definitions in RFC 1213 made obsolete by the inclusion of a +-- subset of the snmp group in this MIB + +snmpOutPkts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Messages which were + passed from the SNMP protocol entity to the + transport service." + ::= { snmp 2 } + +-- { snmp 7 } is not used + +snmpInTooBigs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP PDUs which were + delivered to the SNMP protocol entity and for + which the value of the error-status field was + `tooBig'." + ::= { snmp 8 } + + + +snmpInNoSuchNames OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP PDUs which were + delivered to the SNMP protocol entity and for + which the value of the error-status field was + `noSuchName'." + ::= { snmp 9 } + +snmpInBadValues OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP PDUs which were + delivered to the SNMP protocol entity and for + which the value of the error-status field was + `badValue'." + ::= { snmp 10 } + +snmpInReadOnlys OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number valid SNMP PDUs which were delivered + to the SNMP protocol entity and for which the value + of the error-status field was `readOnly'. It should + be noted that it is a protocol error to generate an + SNMP PDU which contains the value `readOnly' in the + error-status field, as such this object is provided + as a means of detecting incorrect implementations of + the SNMP." + ::= { snmp 11 } + +snmpInGenErrs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP PDUs which were delivered + to the SNMP protocol entity and for which the value + of the error-status field was `genErr'." + ::= { snmp 12 } + +snmpInTotalReqVars OBJECT-TYPE + + + + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of MIB objects which have been + retrieved successfully by the SNMP protocol entity + as the result of receiving valid SNMP Get-Request + and Get-Next PDUs." + ::= { snmp 13 } + +snmpInTotalSetVars OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of MIB objects which have been + altered successfully by the SNMP protocol entity as + the result of receiving valid SNMP Set-Request PDUs." + ::= { snmp 14 } + +snmpInGetRequests OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Get-Request PDUs which + have been accepted and processed by the SNMP + protocol entity." + ::= { snmp 15 } + +snmpInGetNexts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Get-Next PDUs which have been + accepted and processed by the SNMP protocol entity." + ::= { snmp 16 } + +snmpInSetRequests OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Set-Request PDUs which + have been accepted and processed by the SNMP protocol + entity." + ::= { snmp 17 } + + + +snmpInGetResponses OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Get-Response PDUs which + have been accepted and processed by the SNMP protocol + entity." + ::= { snmp 18 } + +snmpInTraps OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Trap PDUs which have been + accepted and processed by the SNMP protocol entity." + ::= { snmp 19 } + +snmpOutTooBigs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP PDUs which were generated + by the SNMP protocol entity and for which the value + of the error-status field was `tooBig.'" + ::= { snmp 20 } + +snmpOutNoSuchNames OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP PDUs which were generated + by the SNMP protocol entity and for which the value + of the error-status was `noSuchName'." + ::= { snmp 21 } + +snmpOutBadValues OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP PDUs which were generated + by the SNMP protocol entity and for which the value + of the error-status field was `badValue'." + ::= { snmp 22 } + + + +-- { snmp 23 } is not used + +snmpOutGenErrs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP PDUs which were generated + by the SNMP protocol entity and for which the value + of the error-status field was `genErr'." + ::= { snmp 24 } + +snmpOutGetRequests OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Get-Request PDUs which + have been generated by the SNMP protocol entity." + ::= { snmp 25 } + +snmpOutGetNexts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Get-Next PDUs which have + been generated by the SNMP protocol entity." + ::= { snmp 26 } + +snmpOutSetRequests OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Set-Request PDUs which + have been generated by the SNMP protocol entity." + ::= { snmp 27 } + +snmpOutGetResponses OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Get-Response PDUs which + have been generated by the SNMP protocol entity." + ::= { snmp 28 } + + + + +snmpOutTraps OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The total number of SNMP Trap PDUs which have + been generated by the SNMP protocol entity." + ::= { snmp 29 } + +snmpObsoleteGroup OBJECT-GROUP + OBJECTS { snmpOutPkts, snmpInTooBigs, snmpInNoSuchNames, + snmpInBadValues, snmpInReadOnlys, snmpInGenErrs, + snmpInTotalReqVars, snmpInTotalSetVars, + snmpInGetRequests, snmpInGetNexts, snmpInSetRequests, + snmpInGetResponses, snmpInTraps, snmpOutTooBigs, + snmpOutNoSuchNames, snmpOutBadValues, + snmpOutGenErrs, snmpOutGetRequests, snmpOutGetNexts, + snmpOutSetRequests, snmpOutGetResponses, snmpOutTraps + } + STATUS obsolete + DESCRIPTION + "A collection of objects from RFC 1213 made obsolete + by this MIB module." + ::= { snmpMIBGroups 10 } + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-SMI b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-SMI new file mode 100644 index 0000000..19ed716 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-SMI @@ -0,0 +1,352 @@ +SNMPv2-SMI DEFINITIONS ::= BEGIN + + +-- the path to the root + +org OBJECT IDENTIFIER ::= { iso 3 } -- "iso" = 1 +dod OBJECT IDENTIFIER ::= { org 6 } +internet OBJECT IDENTIFIER ::= { dod 1 } + +directory OBJECT IDENTIFIER ::= { internet 1 } + +mgmt OBJECT IDENTIFIER ::= { internet 2 } +mib-2 OBJECT IDENTIFIER ::= { mgmt 1 } +transmission OBJECT IDENTIFIER ::= { mib-2 10 } + +experimental OBJECT IDENTIFIER ::= { internet 3 } + +private OBJECT IDENTIFIER ::= { internet 4 } +enterprises OBJECT IDENTIFIER ::= { private 1 } + +security OBJECT IDENTIFIER ::= { internet 5 } + +snmpV2 OBJECT IDENTIFIER ::= { internet 6 } + +-- transport domains +snmpDomains OBJECT IDENTIFIER ::= { snmpV2 1 } + +-- transport proxies +snmpProxys OBJECT IDENTIFIER ::= { snmpV2 2 } + +-- module identities +snmpModules OBJECT IDENTIFIER ::= { snmpV2 3 } + +-- Extended UTCTime, to allow dates with four-digit years +-- (Note that this definition of ExtUTCTime is not to be IMPORTed +-- by MIB modules.) +ExtUTCTime ::= OCTET STRING(SIZE(11 | 13)) + -- format is YYMMDDHHMMZ or YYYYMMDDHHMMZ + -- where: YY - last two digits of year (only years + -- between 1900-1999) + -- YYYY - last four digits of the year (any year) + -- MM - month (01 through 12) + -- DD - day of month (01 through 31) + -- HH - hours (00 through 23) + -- MM - minutes (00 through 59) + -- Z - denotes GMT (the ASCII character Z) + -- + -- For example, "9502192015Z" and "199502192015Z" represent + -- 8:15pm GMT on 19 February 1995. Years after 1999 must use + -- the four digit year format. Years 1900-1999 may use the + -- two or four digit format. + +-- definitions for information modules + +MODULE-IDENTITY MACRO ::= +BEGIN + TYPE NOTATION ::= + "LAST-UPDATED" value(Update ExtUTCTime) + "ORGANIZATION" Text + "CONTACT-INFO" Text + "DESCRIPTION" Text + RevisionPart + + VALUE NOTATION ::= + value(VALUE OBJECT IDENTIFIER) + + RevisionPart ::= + Revisions + | empty + Revisions ::= + Revision + | Revisions Revision + Revision ::= + "REVISION" value(Update ExtUTCTime) + "DESCRIPTION" Text + + -- a character string as defined in section 3.1.1 + Text ::= value(IA5String) +END + + +OBJECT-IDENTITY MACRO ::= +BEGIN + TYPE NOTATION ::= + "STATUS" Status + "DESCRIPTION" Text + ReferPart + + VALUE NOTATION ::= + value(VALUE OBJECT IDENTIFIER) + + Status ::= + "current" + | "deprecated" + | "obsolete" + + ReferPart ::= + "REFERENCE" Text + | empty + + -- a character string as defined in section 3.1.1 + Text ::= value(IA5String) +END + + +-- names of objects +-- (Note that these definitions of ObjectName and NotificationName +-- are not to be IMPORTed by MIB modules.) + +ObjectName ::= + OBJECT IDENTIFIER + +NotificationName ::= + OBJECT IDENTIFIER + +-- syntax of objects + +-- the "base types" defined here are: +-- 3 built-in ASN.1 types: INTEGER, OCTET STRING, OBJECT IDENTIFIER +-- 8 application-defined types: Integer32, IpAddress, Counter32, +-- Gauge32, Unsigned32, TimeTicks, Opaque, and Counter64 + +ObjectSyntax ::= + CHOICE { + simple + SimpleSyntax, + + -- note that SEQUENCEs for conceptual tables and + -- rows are not mentioned here... + + application-wide + ApplicationSyntax + } + +-- built-in ASN.1 types + +SimpleSyntax ::= + CHOICE { + -- INTEGERs with a more restrictive range + -- may also be used + integer-value -- includes Integer32 + INTEGER (-2147483648..2147483647), + + -- OCTET STRINGs with a more restrictive size + -- may also be used + string-value + OCTET STRING (SIZE (0..65535)), + + objectID-value + OBJECT IDENTIFIER + } + +-- indistinguishable from INTEGER, but never needs more than +-- 32-bits for a two's complement representation +Integer32 ::= + INTEGER (-2147483648..2147483647) + + +-- application-wide types + +ApplicationSyntax ::= + CHOICE { + ipAddress-value + IpAddress, + + counter-value + Counter32, + + timeticks-value + TimeTicks, + + arbitrary-value + Opaque, + + big-counter-value + Counter64, + + unsigned-integer-value -- includes Gauge32 + Unsigned32 + } + +-- in network-byte order +-- (this is a tagged type for historical reasons) +IpAddress ::= + [APPLICATION 0] + IMPLICIT OCTET STRING (SIZE (4)) + +-- this wraps +Counter32 ::= + [APPLICATION 1] + IMPLICIT INTEGER (0..4294967295) + +-- this doesn't wrap +Gauge32 ::= + [APPLICATION 2] + IMPLICIT INTEGER (0..4294967295) + +-- an unsigned 32-bit quantity +-- indistinguishable from Gauge32 +Unsigned32 ::= + [APPLICATION 2] + IMPLICIT INTEGER (0..4294967295) + +-- hundredths of seconds since an epoch +TimeTicks ::= + [APPLICATION 3] + IMPLICIT INTEGER (0..4294967295) + +-- for backward-compatibility only +Opaque ::= + [APPLICATION 4] + IMPLICIT OCTET STRING + +-- for counters that wrap in less than one hour with only 32 bits +Counter64 ::= + [APPLICATION 6] + IMPLICIT INTEGER (0..18446744073709551615) + + +-- definition for objects + +OBJECT-TYPE MACRO ::= +BEGIN + TYPE NOTATION ::= + "SYNTAX" Syntax + UnitsPart + "MAX-ACCESS" Access + "STATUS" Status + "DESCRIPTION" Text + ReferPart + IndexPart + DefValPart + + VALUE NOTATION ::= + value(VALUE ObjectName) + + Syntax ::= -- Must be one of the following: + -- a base type (or its refinement), + -- a textual convention (or its refinement), or + -- a BITS pseudo-type + type + | "BITS" "{" NamedBits "}" + + NamedBits ::= NamedBit + | NamedBits "," NamedBit + + NamedBit ::= identifier "(" number ")" -- number is nonnegative + + UnitsPart ::= + "UNITS" Text + | empty + + Access ::= + "not-accessible" + | "accessible-for-notify" + | "read-only" + | "read-write" + | "read-create" + + Status ::= + "current" + | "deprecated" + | "obsolete" + + ReferPart ::= + "REFERENCE" Text + | empty + + IndexPart ::= + "INDEX" "{" IndexTypes "}" + | "AUGMENTS" "{" Entry "}" + | empty + IndexTypes ::= + IndexType + | IndexTypes "," IndexType + IndexType ::= + "IMPLIED" Index + | Index + Index ::= + -- use the SYNTAX value of the + -- correspondent OBJECT-TYPE invocation + value(ObjectName) + Entry ::= + -- use the INDEX value of the + -- correspondent OBJECT-TYPE invocation + value(ObjectName) + + DefValPart ::= "DEFVAL" "{" Defvalue "}" + | empty + + Defvalue ::= -- must be valid for the type specified in + -- SYNTAX clause of same OBJECT-TYPE macro + value(ObjectSyntax) + | "{" BitsValue "}" + + BitsValue ::= BitNames + | empty + + BitNames ::= BitName + | BitNames "," BitName + + BitName ::= identifier + + -- a character string as defined in section 3.1.1 + Text ::= value(IA5String) +END + + +-- definitions for notifications + +NOTIFICATION-TYPE MACRO ::= +BEGIN + TYPE NOTATION ::= + ObjectsPart + "STATUS" Status + "DESCRIPTION" Text + ReferPart + + VALUE NOTATION ::= + value(VALUE NotificationName) + + ObjectsPart ::= + "OBJECTS" "{" Objects "}" + | empty + Objects ::= + Object + | Objects "," Object + Object ::= + value(ObjectName) + + Status ::= + "current" + | "deprecated" + | "obsolete" + + ReferPart ::= + "REFERENCE" Text + | empty + + -- a character string as defined in section 3.1.1 + Text ::= value(IA5String) +END + +-- definitions of administrative identifiers + +zeroDotZero OBJECT-IDENTITY + STATUS current + DESCRIPTION + "A value used for null identifiers." + ::= { 0 0 } + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TC b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TC new file mode 100644 index 0000000..cce1e7c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TC @@ -0,0 +1,786 @@ +SNMPv2-TC DEFINITIONS ::= BEGIN + +IMPORTS + TimeTicks FROM SNMPv2-SMI; + + +-- definition of textual conventions + +TEXTUAL-CONVENTION MACRO ::= +BEGIN + TYPE NOTATION ::= + DisplayPart + "STATUS" Status + "DESCRIPTION" Text + ReferPart + "SYNTAX" Syntax + + VALUE NOTATION ::= + value(VALUE Syntax) -- adapted ASN.1 + + DisplayPart ::= + "DISPLAY-HINT" Text + | empty + + Status ::= + "current" + | "deprecated" + | "obsolete" + + ReferPart ::= + "REFERENCE" Text + | empty + + -- a character string as defined in [2] + Text ::= value(IA5String) + + Syntax ::= -- Must be one of the following: + -- a base type (or its refinement), or + -- a BITS pseudo-type + type + | "BITS" "{" NamedBits "}" + + NamedBits ::= NamedBit + | NamedBits "," NamedBit + + NamedBit ::= identifier "(" number ")" -- number is nonnegative + +END + + + + +DisplayString ::= TEXTUAL-CONVENTION + DISPLAY-HINT "255a" + STATUS current + DESCRIPTION + "Represents textual information taken from the NVT ASCII + character set, as defined in pages 4, 10-11 of RFC 854. + + To summarize RFC 854, the NVT ASCII repertoire specifies: + + - the use of character codes 0-127 (decimal) + + - the graphics characters (32-126) are interpreted as + US ASCII + + - NUL, LF, CR, BEL, BS, HT, VT and FF have the special + meanings specified in RFC 854 + + - the other 25 codes have no standard interpretation + + - the sequence 'CR LF' means newline + + - the sequence 'CR NUL' means carriage-return + + - an 'LF' not preceded by a 'CR' means moving to the + same column on the next line. + + - the sequence 'CR x' for any x other than LF or NUL is + illegal. (Note that this also means that a string may + end with either 'CR LF' or 'CR NUL', but not with CR.) + + Any object defined using this syntax may not exceed 255 + characters in length." + SYNTAX OCTET STRING (SIZE (0..255)) + +PhysAddress ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1x:" + STATUS current + DESCRIPTION + "Represents media- or physical-level addresses." + SYNTAX OCTET STRING + + +MacAddress ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1x:" + STATUS current + DESCRIPTION + "Represents an 802 MAC address represented in the + `canonical' order defined by IEEE 802.1a, i.e., as if it + were transmitted least significant bit first, even though + 802.5 (in contrast to other 802.x protocols) requires MAC + addresses to be transmitted most significant bit first." + SYNTAX OCTET STRING (SIZE (6)) + +TruthValue ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents a boolean value." + SYNTAX INTEGER { true(1), false(2) } + +TestAndIncr ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents integer-valued information used for atomic + operations. When the management protocol is used to specify + that an object instance having this syntax is to be + modified, the new value supplied via the management protocol + must precisely match the value presently held by the + instance. If not, the management protocol set operation + fails with an error of `inconsistentValue'. Otherwise, if + the current value is the maximum value of 2^31-1 (2147483647 + decimal), then the value held by the instance is wrapped to + zero; otherwise, the value held by the instance is + incremented by one. (Note that regardless of whether the + management protocol set operation succeeds, the variable- + binding in the request and response PDUs are identical.) + + The value of the ACCESS clause for objects having this + syntax is either `read-write' or `read-create'. When an + instance of a columnar object having this syntax is created, + any value may be supplied via the management protocol. + + When the network management portion of the system is re- + initialized, the value of every object instance having this + syntax must either be incremented from its value prior to + the re-initialization, or (if the value prior to the re- + initialization is unknown) be set to a pseudo-randomly + generated value." + SYNTAX INTEGER (0..2147483647) + +AutonomousType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents an independently extensible type identification + value. It may, for example, indicate a particular sub-tree + with further MIB definitions, or define a particular type of + protocol or hardware." + SYNTAX OBJECT IDENTIFIER + + +InstancePointer ::= TEXTUAL-CONVENTION + STATUS obsolete + DESCRIPTION + "A pointer to either a specific instance of a MIB object or + a conceptual row of a MIB table in the managed device. In + the latter case, by convention, it is the name of the + particular instance of the first accessible columnar object + in the conceptual row. + + The two uses of this textual convention are replaced by + VariablePointer and RowPointer, respectively." + SYNTAX OBJECT IDENTIFIER + + +VariablePointer ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A pointer to a specific object instance. For example, + sysContact.0 or ifInOctets.3." + SYNTAX OBJECT IDENTIFIER + + +RowPointer ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents a pointer to a conceptual row. The value is the + name of the instance of the first accessible columnar object + in the conceptual row. + + For example, ifIndex.3 would point to the 3rd row in the + ifTable (note that if ifIndex were not-accessible, then + ifDescr.3 would be used instead)." + SYNTAX OBJECT IDENTIFIER + +RowStatus ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The RowStatus textual convention is used to manage the + creation and deletion of conceptual rows, and is used as the + value of the SYNTAX clause for the status column of a + conceptual row (as described in Section 7.7.1 of [2].) + The status column has six defined values: + + - `active', which indicates that the conceptual row is + available for use by the managed device; + + - `notInService', which indicates that the conceptual + row exists in the agent, but is unavailable for use by + the managed device (see NOTE below); 'notInService' has + no implication regarding the internal consistency of + the row, availability of resources, or consistency with + the current state of the managed device; + + - `notReady', which indicates that the conceptual row + exists in the agent, but is missing information + necessary in order to be available for use by the + managed device (i.e., one or more required columns in + the conceptual row have not been instanciated); + + - `createAndGo', which is supplied by a management + station wishing to create a new instance of a + conceptual row and to have its status automatically set + to active, making it available for use by the managed + device; + + - `createAndWait', which is supplied by a management + station wishing to create a new instance of a + conceptual row (but not make it available for use by + the managed device); and, + + - `destroy', which is supplied by a management station + wishing to delete all of the instances associated with + an existing conceptual row. + + Whereas five of the six values (all except `notReady') may + be specified in a management protocol set operation, only + three values will be returned in response to a management + protocol retrieval operation: `notReady', `notInService' or + `active'. That is, when queried, an existing conceptual row + has only three states: it is either available for use by + the managed device (the status column has value `active'); + it is not available for use by the managed device, though + the agent has sufficient information to attempt to make it + so (the status column has value `notInService'); or, it is + not available for use by the managed device, and an attempt + to make it so would fail because the agent has insufficient + information (the state column has value `notReady'). + + NOTE WELL + + This textual convention may be used for a MIB table, + irrespective of whether the values of that table's + conceptual rows are able to be modified while it is + active, or whether its conceptual rows must be taken + out of service in order to be modified. That is, it is + the responsibility of the DESCRIPTION clause of the + status column to specify whether the status column must + not be `active' in order for the value of some other + column of the same conceptual row to be modified. If + such a specification is made, affected columns may be + changed by an SNMP set PDU if the RowStatus would not + be equal to `active' either immediately before or after + processing the PDU. In other words, if the PDU also + contained a varbind that would change the RowStatus + value, the column in question may be changed if the + RowStatus was not equal to `active' as the PDU was + received, or if the varbind sets the status to a value + other than 'active'. + + + Also note that whenever any elements of a row exist, the + RowStatus column must also exist. + + To summarize the effect of having a conceptual row with a + status column having a SYNTAX clause value of RowStatus, + consider the following state diagram: + + + STATE + +--------------+-----------+-------------+------------- + | A | B | C | D + | |status col.|status column| + |status column | is | is |status column + ACTION |does not exist| notReady | notInService| is active +--------------+--------------+-----------+-------------+------------- +set status |noError ->D|inconsist- |inconsistent-|inconsistent- +column to | or | entValue| Value| Value +createAndGo |inconsistent- | | | + | Value| | | +--------------+--------------+-----------+-------------+------------- +set status |noError see 1|inconsist- |inconsistent-|inconsistent- +column to | or | entValue| Value| Value +createAndWait |wrongValue | | | +--------------+--------------+-----------+-------------+------------- +set status |inconsistent- |inconsist- |noError |noError +column to | Value| entValue| | +active | | | | + | | or | | + | | | | + | |see 2 ->D|see 8 ->D| ->D +--------------+--------------+-----------+-------------+------------- +set status |inconsistent- |inconsist- |noError |noError ->C +column to | Value| entValue| | +notInService | | | | + | | or | | or + | | | | + | |see 3 ->C| ->C|see 6 +--------------+--------------+-----------+-------------+------------- +set status |noError |noError |noError |noError ->A +column to | | | | or +destroy | ->A| ->A| ->A|see 7 +--------------+--------------+-----------+-------------+------------- +set any other |see 4 |noError |noError |see 5 +column to some| | | | +value | | see 1| ->C| ->D +--------------+--------------+-----------+-------------+------------- + + (1) goto B or C, depending on information available to the + agent. + + (2) if other variable bindings included in the same PDU, + provide values for all columns which are missing but + required, and all columns have acceptable values, then + return noError and goto D. + + (3) if other variable bindings included in the same PDU, + provide legal values for all columns which are missing but + required, then return noError and goto C. + + (4) at the discretion of the agent, the return value may be + either: + + inconsistentName: because the agent does not choose to + create such an instance when the corresponding + RowStatus instance does not exist, or + + inconsistentValue: if the supplied value is + inconsistent with the state of some other MIB object's + value, or + + noError: because the agent chooses to create the + instance. + + If noError is returned, then the instance of the status + column must also be created, and the new state is B or C, + depending on the information available to the agent. If + inconsistentName or inconsistentValue is returned, the row + remains in state A. + + (5) depending on the MIB definition for the column/table, + either noError or inconsistentValue may be returned. + + (6) the return value can indicate one of the following + errors: + + wrongValue: because the agent does not support + notInService (e.g., an agent which does not support + createAndWait), or + + inconsistentValue: because the agent is unable to take + the row out of service at this time, perhaps because it + is in use and cannot be de-activated. + + (7) the return value can indicate the following error: + + inconsistentValue: because the agent is unable to + remove the row at this time, perhaps because it is in + use and cannot be de-activated. + + (8) the transition to D can fail, e.g., if the values of the + conceptual row are inconsistent, then the error code would + be inconsistentValue. + + NOTE: Other processing of (this and other varbinds of) the + set request may result in a response other than noError + being returned, e.g., wrongValue, noCreation, etc. + + + Conceptual Row Creation + + There are four potential interactions when creating a + conceptual row: selecting an instance-identifier which is + not in use; creating the conceptual row; initializing any + objects for which the agent does not supply a default; and, + making the conceptual row available for use by the managed + device. + + Interaction 1: Selecting an Instance-Identifier + + The algorithm used to select an instance-identifier varies + for each conceptual row. In some cases, the instance- + identifier is semantically significant, e.g., the + destination address of a route, and a management station + selects the instance-identifier according to the semantics. + + In other cases, the instance-identifier is used solely to + distinguish conceptual rows, and a management station + without specific knowledge of the conceptual row might + examine the instances present in order to determine an + unused instance-identifier. (This approach may be used, but + it is often highly sub-optimal; however, it is also a + questionable practice for a naive management station to + attempt conceptual row creation.) + + Alternately, the MIB module which defines the conceptual row + might provide one or more objects which provide assistance + in determining an unused instance-identifier. For example, + if the conceptual row is indexed by an integer-value, then + an object having an integer-valued SYNTAX clause might be + defined for such a purpose, allowing a management station to + issue a management protocol retrieval operation. In order + to avoid unnecessary collisions between competing management + stations, `adjacent' retrievals of this object should be + different. + + Finally, the management station could select a pseudo-random + number to use as the index. In the event that this index + was already in use and an inconsistentValue was returned in + response to the management protocol set operation, the + management station should simply select a new pseudo-random + number and retry the operation. + + A MIB designer should choose between the two latter + algorithms based on the size of the table (and therefore the + efficiency of each algorithm). For tables in which a large + number of entries are expected, it is recommended that a MIB + object be defined that returns an acceptable index for + creation. For tables with small numbers of entries, it is + recommended that the latter pseudo-random index mechanism be + used. + + Interaction 2: Creating the Conceptual Row + + Once an unused instance-identifier has been selected, the + management station determines if it wishes to create and + activate the conceptual row in one transaction or in a + negotiated set of interactions. + + Interaction 2a: Creating and Activating the Conceptual Row + + The management station must first determine the column + requirements, i.e., it must determine those columns for + which it must or must not provide values. Depending on the + complexity of the table and the management station's + knowledge of the agent's capabilities, this determination + can be made locally by the management station. Alternately, + the management station issues a management protocol get + operation to examine all columns in the conceptual row that + it wishes to create. In response, for each column, there + are three possible outcomes: + + - a value is returned, indicating that some other + management station has already created this conceptual + row. We return to interaction 1. + + - the exception `noSuchInstance' is returned, + indicating that the agent implements the object-type + associated with this column, and that this column in at + least one conceptual row would be accessible in the MIB + view used by the retrieval were it to exist. For those + columns to which the agent provides read-create access, + the `noSuchInstance' exception tells the management + station that it should supply a value for this column + when the conceptual row is to be created. + + - the exception `noSuchObject' is returned, indicating + that the agent does not implement the object-type + associated with this column or that there is no + conceptual row for which this column would be + accessible in the MIB view used by the retrieval. As + such, the management station can not issue any + management protocol set operations to create an + instance of this column. + + Once the column requirements have been determined, a + management protocol set operation is accordingly issued. + This operation also sets the new instance of the status + column to `createAndGo'. + + When the agent processes the set operation, it verifies that + it has sufficient information to make the conceptual row + available for use by the managed device. The information + available to the agent is provided by two sources: the + management protocol set operation which creates the + conceptual row, and, implementation-specific defaults + supplied by the agent (note that an agent must provide + implementation-specific defaults for at least those objects + which it implements as read-only). If there is sufficient + information available, then the conceptual row is created, a + `noError' response is returned, the status column is set to + `active', and no further interactions are necessary (i.e., + interactions 3 and 4 are skipped). If there is insufficient + information, then the conceptual row is not created, and the + set operation fails with an error of `inconsistentValue'. + On this error, the management station can issue a management + protocol retrieval operation to determine if this was + because it failed to specify a value for a required column, + or, because the selected instance of the status column + already existed. In the latter case, we return to + interaction 1. In the former case, the management station + can re-issue the set operation with the additional + information, or begin interaction 2 again using + `createAndWait' in order to negotiate creation of the + conceptual row. + + NOTE WELL + + Regardless of the method used to determine the column + requirements, it is possible that the management + station might deem a column necessary when, in fact, + the agent will not allow that particular columnar + instance to be created or written. In this case, the + management protocol set operation will fail with an + error such as `noCreation' or `notWritable'. In this + case, the management station decides whether it needs + to be able to set a value for that particular columnar + instance. If not, the management station re-issues the + management protocol set operation, but without setting + a value for that particular columnar instance; + otherwise, the management station aborts the row + creation algorithm. + + Interaction 2b: Negotiating the Creation of the Conceptual + Row + + The management station issues a management protocol set + operation which sets the desired instance of the status + column to `createAndWait'. If the agent is unwilling to + process a request of this sort, the set operation fails with + an error of `wrongValue'. (As a consequence, such an agent + must be prepared to accept a single management protocol set + operation, i.e., interaction 2a above, containing all of the + columns indicated by its column requirements.) Otherwise, + the conceptual row is created, a `noError' response is + returned, and the status column is immediately set to either + `notInService' or `notReady', depending on whether it has + sufficient information to (attempt to) make the conceptual + row available for use by the managed device. If there is + sufficient information available, then the status column is + set to `notInService'; otherwise, if there is insufficient + information, then the status column is set to `notReady'. + Regardless, we proceed to interaction 3. + + Interaction 3: Initializing non-defaulted Objects + + The management station must now determine the column + requirements. It issues a management protocol get operation + to examine all columns in the created conceptual row. In + the response, for each column, there are three possible + outcomes: + + - a value is returned, indicating that the agent + implements the object-type associated with this column + and had sufficient information to provide a value. For + those columns to which the agent provides read-create + access (and for which the agent allows their values to + be changed after their creation), a value return tells + the management station that it may issue additional + management protocol set operations, if it desires, in + order to change the value associated with this column. + + - the exception `noSuchInstance' is returned, + indicating that the agent implements the object-type + associated with this column, and that this column in at + least one conceptual row would be accessible in the MIB + view used by the retrieval were it to exist. However, + the agent does not have sufficient information to + provide a value, and until a value is provided, the + conceptual row may not be made available for use by the + managed device. For those columns to which the agent + provides read-create access, the `noSuchInstance' + exception tells the management station that it must + issue additional management protocol set operations, in + order to provide a value associated with this column. + + - the exception `noSuchObject' is returned, indicating + that the agent does not implement the object-type + associated with this column or that there is no + conceptual row for which this column would be + accessible in the MIB view used by the retrieval. As + such, the management station can not issue any + management protocol set operations to create an + instance of this column. + + If the value associated with the status column is + `notReady', then the management station must first deal with + all `noSuchInstance' columns, if any. Having done so, the + value of the status column becomes `notInService', and we + proceed to interaction 4. + + Interaction 4: Making the Conceptual Row Available + + Once the management station is satisfied with the values + associated with the columns of the conceptual row, it issues + a management protocol set operation to set the status column + to `active'. If the agent has sufficient information to + make the conceptual row available for use by the managed + device, the management protocol set operation succeeds (a + `noError' response is returned). Otherwise, the management + protocol set operation fails with an error of + `inconsistentValue'. + + NOTE WELL + + A conceptual row having a status column with value + `notInService' or `notReady' is unavailable to the + managed device. As such, it is possible for the + managed device to create its own instances during the + time between the management protocol set operation + which sets the status column to `createAndWait' and the + management protocol set operation which sets the status + column to `active'. In this case, when the management + protocol set operation is issued to set the status + column to `active', the values held in the agent + supersede those used by the managed device. + + If the management station is prevented from setting the + status column to `active' (e.g., due to management station + or network failure) the conceptual row will be left in the + `notInService' or `notReady' state, consuming resources + indefinitely. The agent must detect conceptual rows that + have been in either state for an abnormally long period of + time and remove them. It is the responsibility of the + DESCRIPTION clause of the status column to indicate what an + abnormally long period of time would be. This period of + time should be long enough to allow for human response time + (including `think time') between the creation of the + conceptual row and the setting of the status to `active'. + In the absence of such information in the DESCRIPTION + clause, it is suggested that this period be approximately 5 + minutes in length. This removal action applies not only to + newly-created rows, but also to previously active rows which + are set to, and left in, the notInService state for a + prolonged period exceeding that which is considered normal + for such a conceptual row. + + Conceptual Row Suspension + + When a conceptual row is `active', the management station + may issue a management protocol set operation which sets the + instance of the status column to `notInService'. If the + agent is unwilling to do so, the set operation fails with an + error of `wrongValue' or `inconsistentValue'. Otherwise, + the conceptual row is taken out of service, and a `noError' + response is returned. It is the responsibility of the + DESCRIPTION clause of the status column to indicate under + what circumstances the status column should be taken out of + service (e.g., in order for the value of some other column + of the same conceptual row to be modified). + + + Conceptual Row Deletion + + For deletion of conceptual rows, a management protocol set + operation is issued which sets the instance of the status + column to `destroy'. This request may be made regardless of + the current value of the status column (e.g., it is possible + to delete conceptual rows which are either `notReady', + `notInService' or `active'.) If the operation succeeds, + then all instances associated with the conceptual row are + immediately removed." + SYNTAX INTEGER { + -- the following two values are states: + -- these values may be read or written + active(1), + notInService(2), + + -- the following value is a state: + -- this value may be read, but not written + notReady(3), + + -- the following three values are + -- actions: these values may be written, + -- but are never read + createAndGo(4), + createAndWait(5), + destroy(6) + } + +TimeStamp ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The value of the sysUpTime object at which a specific + occurrence happened. The specific occurrence must be + defined in the description of any object defined using this + type. + + If sysUpTime is reset to zero as a result of a re- + initialization of the network management (sub)system, then + the values of all TimeStamp objects are also reset. + However, after approximately 497 days without a re- + initialization, the sysUpTime object will reach 2^^32-1 and + then increment around to zero; in this case, existing values + of TimeStamp objects do not change. This can lead to + ambiguities in the value of TimeStamp objects." + SYNTAX TimeTicks + + +TimeInterval ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A period of time, measured in units of 0.01 seconds." + SYNTAX INTEGER (0..2147483647) + +DateAndTime ::= TEXTUAL-CONVENTION + DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d" + STATUS current + DESCRIPTION + "A date-time specification. + + field octets contents range + ----- ------ -------- ----- + 1 1-2 year* 0..65536 + 2 3 month 1..12 + 3 4 day 1..31 + 4 5 hour 0..23 + 5 6 minutes 0..59 + 6 7 seconds 0..60 + (use 60 for leap-second) + 7 8 deci-seconds 0..9 + 8 9 direction from UTC '+' / '-' + 9 10 hours from UTC* 0..13 + 10 11 minutes from UTC 0..59 + + * Notes: + - the value of year is in network-byte order + - daylight saving time in New Zealand is +13 + + For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be + displayed as: + + 1992-5-26,13:30:15.0,-4:0 + + Note that if only local time is known, then timezone + information (fields 8-10) is not present." + SYNTAX OCTET STRING (SIZE (8 | 11)) + + +StorageType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Describes the memory realization of a conceptual row. A + row which is volatile(2) is lost upon reboot. A row which + is either nonVolatile(3), permanent(4) or readOnly(5), is + backed up by stable storage. A row which is permanent(4) + can be changed but not deleted. A row which is readOnly(5) + cannot be changed nor deleted. + + If the value of an object with this syntax is either + permanent(4) or readOnly(5), it cannot be written. + Conversely, if the value is either other(1), volatile(2) or + nonVolatile(3), it cannot be modified to be permanent(4) or + readOnly(5). (All illegal modifications result in a + 'wrongValue' error.) + + Every usage of this textual convention is required to + specify the columnar objects which a permanent(4) row must + at a minimum allow to be writable." + SYNTAX INTEGER { + other(1), -- eh? + volatile(2), -- e.g., in RAM + nonVolatile(3), -- e.g., in NVRAM + permanent(4), -- e.g., partially in ROM + readOnly(5) -- e.g., completely in ROM + } + +TDomain ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Denotes a kind of transport service. + + Some possible values, such as snmpUDPDomain, are defined in + the SNMPv2-TM MIB module. Other possible values are defined + in other MIB modules." + REFERENCE "The SNMPv2-TM MIB module is defined in RFC 1906." + SYNTAX OBJECT IDENTIFIER + + +TAddress ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Denotes a transport service address. + + A TAddress value is always interpreted within the context of a + TDomain value. Thus, each definition of a TDomain value must + be accompanied by a definition of a textual convention for use + with that TDomain. Some possible textual conventions, such as + SnmpUDPAddress for snmpUDPDomain, are defined in the SNMPv2-TM + MIB module. Other possible textual conventions are defined in + other MIB modules." + REFERENCE "The SNMPv2-TM MIB module is defined in RFC 1906." + SYNTAX OCTET STRING (SIZE (1..255)) + + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TM b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TM new file mode 100644 index 0000000..401e430 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/SNMPv2-TM @@ -0,0 +1,194 @@ +SNMPv2-TM DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-IDENTITY, + snmpModules, snmpDomains, snmpProxys + FROM SNMPv2-SMI + TEXTUAL-CONVENTION + FROM SNMPv2-TC; + +snmpv2tm MODULE-IDENTITY + LAST-UPDATED "200210160000Z" + ORGANIZATION "IETF SNMPv3 Working Group" + CONTACT-INFO + "WG-EMail: snmpv3@lists.tislabs.com + Subscribe: snmpv3-request@lists.tislabs.com + + Co-Chair: Russ Mundy + Network Associates Laboratories + postal: 15204 Omega Drive, Suite 300 + Rockville, MD 20850-4601 + USA + EMail: mundy@tislabs.com + phone: +1 301 947-7107 + + + + Co-Chair: David Harrington + Enterasys Networks + postal: 35 Industrial Way + P. O. Box 5005 + Rochester, NH 03866-5005 + USA + EMail: dbh@enterasys.com + phone: +1 603 337-2614 + + Editor: Randy Presuhn + BMC Software, Inc. + postal: 2141 North First Street + San Jose, CA 95131 + USA + EMail: randy_presuhn@bmc.com + phone: +1 408 546-1006" + DESCRIPTION + "The MIB module for SNMP transport mappings. + + Copyright (C) The Internet Society (2002). This + version of this MIB module is part of RFC 3417; + see the RFC itself for full legal notices. + " + REVISION "200210160000Z" + DESCRIPTION + "Clarifications, published as RFC 3417." + REVISION "199601010000Z" + DESCRIPTION + "Clarifications, published as RFC 1906." + REVISION "199304010000Z" + DESCRIPTION + "The initial version, published as RFC 1449." + ::= { snmpModules 19 } + +-- SNMP over UDP over IPv4 + +snmpUDPDomain OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SNMP over UDP over IPv4 transport domain. + The corresponding transport address is of type + SnmpUDPAddress." + ::= { snmpDomains 1 } + + + + + + + + +SnmpUDPAddress ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1d.1d.1d.1d/2d" + STATUS current + DESCRIPTION + "Represents a UDP over IPv4 address: + + octets contents encoding + 1-4 IP-address network-byte order + 5-6 UDP-port network-byte order + " + SYNTAX OCTET STRING (SIZE (6)) + +-- SNMP over OSI + +snmpCLNSDomain OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SNMP over CLNS transport domain. + The corresponding transport address is of type + SnmpOSIAddress." + ::= { snmpDomains 2 } + +snmpCONSDomain OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SNMP over CONS transport domain. + The corresponding transport address is of type + SnmpOSIAddress." + ::= { snmpDomains 3 } + +SnmpOSIAddress ::= TEXTUAL-CONVENTION + DISPLAY-HINT "*1x:/1x:" + STATUS current + DESCRIPTION + "Represents an OSI transport-address: + + octets contents encoding + 1 length of NSAP 'n' as an unsigned-integer + (either 0 or from 3 to 20) + 2..(n+1) NSAP concrete binary representation + (n+2)..m TSEL string of (up to 64) octets + " + SYNTAX OCTET STRING (SIZE (1 | 4..85)) + + + + + + + + +-- SNMP over DDP + +snmpDDPDomain OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SNMP over DDP transport domain. The corresponding + transport address is of type SnmpNBPAddress." + ::= { snmpDomains 4 } + +SnmpNBPAddress ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents an NBP name: + + octets contents encoding + 1 length of object 'n' as an unsigned integer + 2..(n+1) object string of (up to 32) octets + n+2 length of type 'p' as an unsigned integer + (n+3)..(n+2+p) type string of (up to 32) octets + n+3+p length of zone 'q' as an unsigned integer + (n+4+p)..(n+3+p+q) zone string of (up to 32) octets + + For comparison purposes, strings are + case-insensitive. All strings may contain any octet + other than 255 (hex ff)." + SYNTAX OCTET STRING (SIZE (3..99)) + +-- SNMP over IPX + +snmpIPXDomain OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SNMP over IPX transport domain. The corresponding + transport address is of type SnmpIPXAddress." + ::= { snmpDomains 5 } + +SnmpIPXAddress ::= TEXTUAL-CONVENTION + DISPLAY-HINT "4x.1x:1x:1x:1x:1x:1x.2d" + STATUS current + DESCRIPTION + "Represents an IPX address: + + octets contents encoding + 1-4 network-number network-byte order + 5-10 physical-address network-byte order + 11-12 socket-number network-byte order + " + SYNTAX OCTET STRING (SIZE (12)) + + + +-- for proxy to SNMPv1 (RFC 1157) + +rfc1157Proxy OBJECT IDENTIFIER ::= { snmpProxys 1 } + +rfc1157Domain OBJECT-IDENTITY + STATUS deprecated + DESCRIPTION + "The transport domain for SNMPv1 over UDP over IPv4. + The corresponding transport address is of type + SnmpUDPAddress." + ::= { rfc1157Proxy 1 } + +-- ::= { rfc1157Proxy 2 } this OID is obsolete + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/TCP-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/TCP-MIB new file mode 100644 index 0000000..3033148 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/TCP-MIB @@ -0,0 +1,829 @@ +TCP-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Integer32, Unsigned32, + Gauge32, Counter32, Counter64, IpAddress, mib-2 + FROM SNMPv2-SMI + MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF + InetAddress, InetAddressType, + InetPortNumber FROM INET-ADDRESS-MIB; + +tcpMIB MODULE-IDENTITY + LAST-UPDATED "200502180000Z" -- 18 February 2005 + ORGANIZATION + "IETF IPv6 MIB Revision Team + http://www.ietf.org/html.charters/ipv6-charter.html" + CONTACT-INFO + "Rajiv Raghunarayan (editor) + + Cisco Systems Inc. + 170 West Tasman Drive + San Jose, CA 95134 + + Phone: +1 408 853 9612 + Email: + + Send comments to " + DESCRIPTION + "The MIB module for managing TCP implementations. + + Copyright (C) The Internet Society (2005). This version + of this MIB module is a part of RFC 4022; see the RFC + itself for full legal notices." + REVISION "200502180000Z" -- 18 February 2005 + DESCRIPTION + "IP version neutral revision, published as RFC 4022." + REVISION "9411010000Z" + DESCRIPTION + "Initial SMIv2 version, published as RFC 2012." + REVISION "9103310000Z" + DESCRIPTION + "The initial revision of this MIB module was part of + MIB-II." + ::= { mib-2 49 } + +-- the TCP base variables group + + + + +tcp OBJECT IDENTIFIER ::= { mib-2 6 } + +-- Scalars + +tcpRtoAlgorithm OBJECT-TYPE + SYNTAX INTEGER { + other(1), -- none of the following + constant(2), -- a constant rto + rsre(3), -- MIL-STD-1778, Appendix B + vanj(4), -- Van Jacobson's algorithm + rfc2988(5) -- RFC 2988 + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The algorithm used to determine the timeout value used for + retransmitting unacknowledged octets." + ::= { tcp 1 } + +tcpRtoMin OBJECT-TYPE + SYNTAX Integer32 (0..2147483647) + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The minimum value permitted by a TCP implementation for + the retransmission timeout, measured in milliseconds. + More refined semantics for objects of this type depend + on the algorithm used to determine the retransmission + timeout; in particular, the IETF standard algorithm + rfc2988(5) provides a minimum value." + ::= { tcp 2 } + +tcpRtoMax OBJECT-TYPE + SYNTAX Integer32 (0..2147483647) + UNITS "milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum value permitted by a TCP implementation for + the retransmission timeout, measured in milliseconds. + More refined semantics for objects of this type depend + on the algorithm used to determine the retransmission + timeout; in particular, the IETF standard algorithm + rfc2988(5) provides an upper bound (as part of an + adaptive backoff algorithm)." + ::= { tcp 3 } + + + + +tcpMaxConn OBJECT-TYPE + SYNTAX Integer32 (-1 | 0..2147483647) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The limit on the total number of TCP connections the entity + can support. In entities where the maximum number of + connections is dynamic, this object should contain the + value -1." + ::= { tcp 4 } + +tcpActiveOpens OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times that TCP connections have made a direct + transition to the SYN-SENT state from the CLOSED state. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 5 } + +tcpPassiveOpens OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times TCP connections have made a direct + transition to the SYN-RCVD state from the LISTEN state. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 6 } + +tcpAttemptFails OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times that TCP connections have made a direct + transition to the CLOSED state from either the SYN-SENT + state or the SYN-RCVD state, plus the number of times that + TCP connections have made a direct transition to the + LISTEN state from the SYN-RCVD state. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + + + + ::= { tcp 7 } + +tcpEstabResets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times that TCP connections have made a direct + transition to the CLOSED state from either the ESTABLISHED + state or the CLOSE-WAIT state. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 8 } + +tcpCurrEstab OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of TCP connections for which the current state + is either ESTABLISHED or CLOSE-WAIT." + ::= { tcp 9 } + +tcpInSegs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of segments received, including those + received in error. This count includes segments received + on currently established connections. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 10 } + +tcpOutSegs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of segments sent, including those on + current connections but excluding those containing only + retransmitted octets. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + + + + ::= { tcp 11 } + +tcpRetransSegs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of segments retransmitted; that is, the + number of TCP segments transmitted containing one or more + previously transmitted octets. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 12 } + +tcpInErrs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of segments received in error (e.g., bad + TCP checksums). + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 14 } + +tcpOutRsts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of TCP segments sent containing the RST flag. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 15 } + +-- { tcp 16 } was used to represent the ipv6TcpConnTable in RFC 2452, +-- which has since been obsoleted. It MUST not be used. + +tcpHCInSegs OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of segments received, including those + received in error. This count includes segments received + + + + on currently established connections. This object is + the 64-bit equivalent of tcpInSegs. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 17 } + +tcpHCOutSegs OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of segments sent, including those on + current connections but excluding those containing only + retransmitted octets. This object is the 64-bit + equivalent of tcpOutSegs. + + Discontinuities in the value of this counter are + indicated via discontinuities in the value of sysUpTime." + ::= { tcp 18 } + + +-- The TCP Connection table + +tcpConnectionTable OBJECT-TYPE + SYNTAX SEQUENCE OF TcpConnectionEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing information about existing TCP + connections. Note that unlike earlier TCP MIBs, there + is a separate table for connections in the LISTEN state." + ::= { tcp 19 } + +tcpConnectionEntry OBJECT-TYPE + SYNTAX TcpConnectionEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A conceptual row of the tcpConnectionTable containing + information about a particular current TCP connection. + Each row of this table is transient in that it ceases to + exist when (or soon after) the connection makes the + transition to the CLOSED state." + INDEX { tcpConnectionLocalAddressType, + tcpConnectionLocalAddress, + tcpConnectionLocalPort, + tcpConnectionRemAddressType, + + + + tcpConnectionRemAddress, + tcpConnectionRemPort } + ::= { tcpConnectionTable 1 } + +TcpConnectionEntry ::= SEQUENCE { + tcpConnectionLocalAddressType InetAddressType, + tcpConnectionLocalAddress InetAddress, + tcpConnectionLocalPort InetPortNumber, + tcpConnectionRemAddressType InetAddressType, + tcpConnectionRemAddress InetAddress, + tcpConnectionRemPort InetPortNumber, + tcpConnectionState INTEGER, + tcpConnectionProcess Unsigned32 + } + +tcpConnectionLocalAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type of tcpConnectionLocalAddress." + ::= { tcpConnectionEntry 1 } + +tcpConnectionLocalAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The local IP address for this TCP connection. The type + of this address is determined by the value of + tcpConnectionLocalAddressType. + + As this object is used in the index for the + tcpConnectionTable, implementors should be + careful not to create entries that would result in OIDs + with more than 128 subidentifiers; otherwise the information + cannot be accessed by using SNMPv1, SNMPv2c, or SNMPv3." + ::= { tcpConnectionEntry 2 } + +tcpConnectionLocalPort OBJECT-TYPE + SYNTAX InetPortNumber + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The local port number for this TCP connection." + ::= { tcpConnectionEntry 3 } + +tcpConnectionRemAddressType OBJECT-TYPE + + + + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type of tcpConnectionRemAddress." + ::= { tcpConnectionEntry 4 } + +tcpConnectionRemAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The remote IP address for this TCP connection. The type + of this address is determined by the value of + tcpConnectionRemAddressType. + + As this object is used in the index for the + tcpConnectionTable, implementors should be + careful not to create entries that would result in OIDs + with more than 128 subidentifiers; otherwise the information + cannot be accessed by using SNMPv1, SNMPv2c, or SNMPv3." + ::= { tcpConnectionEntry 5 } + +tcpConnectionRemPort OBJECT-TYPE + SYNTAX InetPortNumber + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The remote port number for this TCP connection." + ::= { tcpConnectionEntry 6 } + +tcpConnectionState OBJECT-TYPE + SYNTAX INTEGER { + closed(1), + listen(2), + synSent(3), + synReceived(4), + established(5), + finWait1(6), + finWait2(7), + closeWait(8), + lastAck(9), + closing(10), + timeWait(11), + deleteTCB(12) + } + MAX-ACCESS read-write + STATUS current + + + + DESCRIPTION + "The state of this TCP connection. + + The value listen(2) is included only for parallelism to the + old tcpConnTable and should not be used. A connection in + LISTEN state should be present in the tcpListenerTable. + + The only value that may be set by a management station is + deleteTCB(12). Accordingly, it is appropriate for an agent + to return a `badValue' response if a management station + attempts to set this object to any other value. + + If a management station sets this object to the value + deleteTCB(12), then the TCB (as defined in [RFC793]) of + the corresponding connection on the managed node is + deleted, resulting in immediate termination of the + connection. + + As an implementation-specific option, a RST segment may be + sent from the managed node to the other TCP endpoint (note, + however, that RST segments are not sent reliably)." + ::= { tcpConnectionEntry 7 } + +tcpConnectionProcess OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The system's process ID for the process associated with + this connection, or zero if there is no such process. This + value is expected to be the same as HOST-RESOURCES-MIB:: + hrSWRunIndex or SYSAPPL-MIB::sysApplElmtRunIndex for some + row in the appropriate tables." + ::= { tcpConnectionEntry 8 } + +-- The TCP Listener table + +tcpListenerTable OBJECT-TYPE + SYNTAX SEQUENCE OF TcpListenerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing information about TCP listeners. A + listening application can be represented in three + possible ways: + + 1. An application that is willing to accept both IPv4 and + IPv6 datagrams is represented by + + + + a tcpListenerLocalAddressType of unknown (0) and + a tcpListenerLocalAddress of ''h (a zero-length + octet-string). + + 2. An application that is willing to accept only IPv4 or + IPv6 datagrams is represented by a + tcpListenerLocalAddressType of the appropriate address + type and a tcpListenerLocalAddress of '0.0.0.0' or '::' + respectively. + + 3. An application that is listening for data destined + only to a specific IP address, but from any remote + system, is represented by a tcpListenerLocalAddressType + of an appropriate address type, with + tcpListenerLocalAddress as the specific local address. + + NOTE: The address type in this table represents the + address type used for the communication, irrespective + of the higher-layer abstraction. For example, an + application using IPv6 'sockets' to communicate via + IPv4 between ::ffff:10.0.0.1 and ::ffff:10.0.0.2 would + use InetAddressType ipv4(1))." + ::= { tcp 20 } + +tcpListenerEntry OBJECT-TYPE + SYNTAX TcpListenerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A conceptual row of the tcpListenerTable containing + information about a particular TCP listener." + INDEX { tcpListenerLocalAddressType, + tcpListenerLocalAddress, + tcpListenerLocalPort } + ::= { tcpListenerTable 1 } + +TcpListenerEntry ::= SEQUENCE { + tcpListenerLocalAddressType InetAddressType, + tcpListenerLocalAddress InetAddress, + tcpListenerLocalPort InetPortNumber, + tcpListenerProcess Unsigned32 + } + +tcpListenerLocalAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + + + + "The address type of tcpListenerLocalAddress. The value + should be unknown (0) if connection initiations to all + local IP addresses are accepted." + ::= { tcpListenerEntry 1 } + +tcpListenerLocalAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The local IP address for this TCP connection. + + The value of this object can be represented in three + possible ways, depending on the characteristics of the + listening application: + + 1. For an application willing to accept both IPv4 and + IPv6 datagrams, the value of this object must be + ''h (a zero-length octet-string), with the value + of the corresponding tcpListenerLocalAddressType + object being unknown (0). + + 2. For an application willing to accept only IPv4 or + IPv6 datagrams, the value of this object must be + '0.0.0.0' or '::' respectively, with + tcpListenerLocalAddressType representing the + appropriate address type. + + 3. For an application which is listening for data + destined only to a specific IP address, the value + of this object is the specific local address, with + tcpListenerLocalAddressType representing the + appropriate address type. + + As this object is used in the index for the + tcpListenerTable, implementors should be + careful not to create entries that would result in OIDs + with more than 128 subidentifiers; otherwise the information + cannot be accessed, using SNMPv1, SNMPv2c, or SNMPv3." + ::= { tcpListenerEntry 2 } + +tcpListenerLocalPort OBJECT-TYPE + SYNTAX InetPortNumber + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The local port number for this TCP connection." + ::= { tcpListenerEntry 3 } + + + +tcpListenerProcess OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The system's process ID for the process associated with + this listener, or zero if there is no such process. This + value is expected to be the same as HOST-RESOURCES-MIB:: + hrSWRunIndex or SYSAPPL-MIB::sysApplElmtRunIndex for some + row in the appropriate tables." + ::= { tcpListenerEntry 4 } + + +-- The deprecated TCP Connection table + +tcpConnTable OBJECT-TYPE + SYNTAX SEQUENCE OF TcpConnEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "A table containing information about existing IPv4-specific + TCP connections or listeners. This table has been + deprecated in favor of the version neutral + tcpConnectionTable." + ::= { tcp 13 } + +tcpConnEntry OBJECT-TYPE + SYNTAX TcpConnEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "A conceptual row of the tcpConnTable containing information + about a particular current IPv4 TCP connection. Each row + of this table is transient in that it ceases to exist when + (or soon after) the connection makes the transition to the + CLOSED state." + INDEX { tcpConnLocalAddress, + tcpConnLocalPort, + tcpConnRemAddress, + tcpConnRemPort } + ::= { tcpConnTable 1 } + +TcpConnEntry ::= SEQUENCE { + tcpConnState INTEGER, + tcpConnLocalAddress IpAddress, + tcpConnLocalPort Integer32, + tcpConnRemAddress IpAddress, + tcpConnRemPort Integer32 + + + + } + +tcpConnState OBJECT-TYPE + SYNTAX INTEGER { + closed(1), + listen(2), + synSent(3), + synReceived(4), + established(5), + finWait1(6), + finWait2(7), + closeWait(8), + lastAck(9), + closing(10), + timeWait(11), + deleteTCB(12) + } + MAX-ACCESS read-write + STATUS deprecated + DESCRIPTION + "The state of this TCP connection. + + The only value that may be set by a management station is + deleteTCB(12). Accordingly, it is appropriate for an agent + to return a `badValue' response if a management station + attempts to set this object to any other value. + + If a management station sets this object to the value + deleteTCB(12), then the TCB (as defined in [RFC793]) of + the corresponding connection on the managed node is + deleted, resulting in immediate termination of the + connection. + + As an implementation-specific option, a RST segment may be + sent from the managed node to the other TCP endpoint (note, + however, that RST segments are not sent reliably)." + ::= { tcpConnEntry 1 } + +tcpConnLocalAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The local IP address for this TCP connection. In the case + of a connection in the listen state willing to + accept connections for any IP interface associated with the + node, the value 0.0.0.0 is used." + ::= { tcpConnEntry 2 } + + + +tcpConnLocalPort OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The local port number for this TCP connection." + ::= { tcpConnEntry 3 } + +tcpConnRemAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The remote IP address for this TCP connection." + ::= { tcpConnEntry 4 } + +tcpConnRemPort OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The remote port number for this TCP connection." + ::= { tcpConnEntry 5 } + +-- conformance information + +tcpMIBConformance OBJECT IDENTIFIER ::= { tcpMIB 2 } + +tcpMIBCompliances OBJECT IDENTIFIER ::= { tcpMIBConformance 1 } +tcpMIBGroups OBJECT IDENTIFIER ::= { tcpMIBConformance 2 } + +-- compliance statements + +tcpMIBCompliance2 MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for systems that implement TCP. + + A number of INDEX objects cannot be + represented in the form of OBJECT clauses in SMIv2 but + have the following compliance requirements, + expressed in OBJECT clause form in this description + clause: + + -- OBJECT tcpConnectionLocalAddressType + -- SYNTAX InetAddressType { ipv4(1), ipv6(2) } + -- DESCRIPTION + -- This MIB requires support for only global IPv4 + + + + -- and IPv6 address types. + -- + -- OBJECT tcpConnectionRemAddressType + -- SYNTAX InetAddressType { ipv4(1), ipv6(2) } + -- DESCRIPTION + -- This MIB requires support for only global IPv4 + -- and IPv6 address types. + -- + -- OBJECT tcpListenerLocalAddressType + -- SYNTAX InetAddressType { unknown(0), ipv4(1), + -- ipv6(2) } + -- DESCRIPTION + -- This MIB requires support for only global IPv4 + -- and IPv6 address types. The type unknown also + -- needs to be supported to identify a special + -- case in the listener table: a listen using + -- both IPv4 and IPv6 addresses on the device. + -- + " + MODULE -- this module + MANDATORY-GROUPS { tcpBaseGroup, tcpConnectionGroup, + tcpListenerGroup } + GROUP tcpHCGroup + DESCRIPTION + "This group is mandatory for systems that are capable + of receiving or transmitting more than 1 million TCP + segments per second. 1 million segments per second will + cause a Counter32 to wrap in just over an hour." + OBJECT tcpConnectionState + SYNTAX INTEGER { closed(1), listen(2), synSent(3), + synReceived(4), established(5), + finWait1(6), finWait2(7), closeWait(8), + lastAck(9), closing(10), timeWait(11) } + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required, nor is support for the value + deleteTCB (12)." + ::= { tcpMIBCompliances 2 } + +tcpMIBCompliance MODULE-COMPLIANCE + STATUS deprecated + DESCRIPTION + "The compliance statement for IPv4-only systems that + implement TCP. In order to be IP version independent, this + compliance statement is deprecated in favor of + tcpMIBCompliance2. However, agents are still encouraged + to implement these objects in order to interoperate with + the deployed base of managers." + + + + MODULE -- this module + MANDATORY-GROUPS { tcpGroup } + OBJECT tcpConnState + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + ::= { tcpMIBCompliances 1 } + + +-- units of conformance + +tcpGroup OBJECT-GROUP + OBJECTS { tcpRtoAlgorithm, tcpRtoMin, tcpRtoMax, + tcpMaxConn, tcpActiveOpens, + tcpPassiveOpens, tcpAttemptFails, + tcpEstabResets, tcpCurrEstab, tcpInSegs, + tcpOutSegs, tcpRetransSegs, tcpConnState, + tcpConnLocalAddress, tcpConnLocalPort, + tcpConnRemAddress, tcpConnRemPort, + tcpInErrs, tcpOutRsts } + STATUS deprecated + DESCRIPTION + "The tcp group of objects providing for management of TCP + entities." + ::= { tcpMIBGroups 1 } + +tcpBaseGroup OBJECT-GROUP + OBJECTS { tcpRtoAlgorithm, tcpRtoMin, tcpRtoMax, + tcpMaxConn, tcpActiveOpens, + tcpPassiveOpens, tcpAttemptFails, + tcpEstabResets, tcpCurrEstab, tcpInSegs, + tcpOutSegs, tcpRetransSegs, + tcpInErrs, tcpOutRsts } + STATUS current + DESCRIPTION + "The group of counters common to TCP entities." + ::= { tcpMIBGroups 2 } + +tcpConnectionGroup OBJECT-GROUP + OBJECTS { tcpConnectionState, tcpConnectionProcess } + STATUS current + DESCRIPTION + "The group provides general information about TCP + connections." + ::= { tcpMIBGroups 3 } + +tcpListenerGroup OBJECT-GROUP + OBJECTS { tcpListenerProcess } + + + + STATUS current + DESCRIPTION + "This group has objects providing general information about + TCP listeners." + ::= { tcpMIBGroups 4 } + +tcpHCGroup OBJECT-GROUP + OBJECTS { tcpHCInSegs, tcpHCOutSegs } + STATUS current + DESCRIPTION + "The group of objects providing for counters of high speed + TCP implementations." + ::= { tcpMIBGroups 5 } + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/UDP-MIB b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/UDP-MIB new file mode 100644 index 0000000..204eaeb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/Mibs/UDP-MIB @@ -0,0 +1,579 @@ +UDP-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Integer32, Counter32, Counter64, + Unsigned32, IpAddress, mib-2 FROM SNMPv2-SMI + MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF + InetAddress, InetAddressType, + InetPortNumber FROM INET-ADDRESS-MIB; + +udpMIB MODULE-IDENTITY + LAST-UPDATED "200505200000Z" -- May 20, 2005 + ORGANIZATION + "IETF IPv6 Working Group + http://www.ietf.org/html.charters/ipv6-charter.html" + CONTACT-INFO + "Bill Fenner (editor) + + AT&T Labs -- Research + 75 Willow Rd. + Menlo Park, CA 94025 + + Phone: +1 650 330-7893 + Email: + + John Flick (editor) + + Hewlett-Packard Company + 8000 Foothills Blvd. M/S 5557 + Roseville, CA 95747 + + Phone: +1 916 785 4018 + Email: + + Send comments to " + + + + DESCRIPTION + "The MIB module for managing UDP implementations. + Copyright (C) The Internet Society (2005). This + version of this MIB module is part of RFC 4113; + see the RFC itself for full legal notices." + REVISION "200505200000Z" -- May 20, 2005 + DESCRIPTION + "IP version neutral revision, incorporating the + following revisions: + + - Added udpHCInDatagrams and udpHCOutDatagrams in order + to provide high-capacity counters for fast networks. + - Added text to the descriptions of all counter objects + to indicate how discontinuities are detected. + - Deprecated the IPv4-specific udpTable and replaced it + with the version neutral udpEndpointTable. This + table includes support for connected UDP endpoints + and support for identification of the operating + system process associated with a UDP endpoint. + - Deprecated the udpGroup and replaced it with object + groups representing the current set of objects. + - Deprecated udpMIBCompliance and replaced it with + udpMIBCompliance2, which includes the compliance + information for the new object groups. + + This version published as RFC 4113." + REVISION "199411010000Z" -- November 1, 1994 + DESCRIPTION + "Initial SMIv2 version, published as RFC 2013." + REVISION "199103310000Z" -- March 31, 1991 + DESCRIPTION + "The initial revision of this MIB module was part of + MIB-II, published as RFC 1213." + ::= { mib-2 50 } + +-- the UDP group + +udp OBJECT IDENTIFIER ::= { mib-2 7 } + +udpInDatagrams OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of UDP datagrams delivered to UDP + users. + + + + + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by discontinuities in the + value of sysUpTime." + ::= { udp 1 } + +udpNoPorts OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of received UDP datagrams for which + there was no application at the destination port. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by discontinuities in the + value of sysUpTime." + ::= { udp 2 } + +udpInErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received UDP datagrams that could not be + delivered for reasons other than the lack of an + application at the destination port. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by discontinuities in the + value of sysUpTime." + ::= { udp 3 } + +udpOutDatagrams OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of UDP datagrams sent from this + entity. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by discontinuities in the + value of sysUpTime." + ::= { udp 4 } + + + +udpHCInDatagrams OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of UDP datagrams delivered to UDP + users, for devices that can receive more than 1 + million UDP datagrams per second. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by discontinuities in the + value of sysUpTime." + ::= { udp 8 } + +udpHCOutDatagrams OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of UDP datagrams sent from this + entity, for devices that can transmit more than 1 + million UDP datagrams per second. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by discontinuities in the + value of sysUpTime." + ::= { udp 9 } + +-- +-- { udp 6 } was defined as the ipv6UdpTable in RFC2454's +-- IPV6-UDP-MIB. This RFC obsoletes RFC 2454, so { udp 6 } is +-- obsoleted. +-- + +-- The UDP "Endpoint" table. + +udpEndpointTable OBJECT-TYPE + SYNTAX SEQUENCE OF UdpEndpointEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing information about this entity's UDP + endpoints on which a local application is currently + accepting or sending datagrams. + + + + + + The address type in this table represents the address + type used for the communication, irrespective of the + higher-layer abstraction. For example, an application + using IPv6 'sockets' to communicate via IPv4 between + ::ffff:10.0.0.1 and ::ffff:10.0.0.2 would use + InetAddressType ipv4(1). + + Unlike the udpTable in RFC 2013, this table also allows + the representation of an application that completely + specifies both local and remote addresses and ports. A + listening application is represented in three possible + ways: + + 1) An application that is willing to accept both IPv4 + and IPv6 datagrams is represented by a + udpEndpointLocalAddressType of unknown(0) and a + udpEndpointLocalAddress of ''h (a zero-length + octet-string). + + 2) An application that is willing to accept only IPv4 + or only IPv6 datagrams is represented by a + udpEndpointLocalAddressType of the appropriate + address type and a udpEndpointLocalAddress of + '0.0.0.0' or '::' respectively. + + 3) An application that is listening for datagrams only + for a specific IP address but from any remote + system is represented by a + udpEndpointLocalAddressType of the appropriate + address type, with udpEndpointLocalAddress + specifying the local address. + + In all cases where the remote is a wildcard, the + udpEndpointRemoteAddressType is unknown(0), the + udpEndpointRemoteAddress is ''h (a zero-length + octet-string), and the udpEndpointRemotePort is 0. + + If the operating system is demultiplexing UDP packets + by remote address and port, or if the application has + 'connected' the socket specifying a default remote + address and port, the udpEndpointRemote* values should + be used to reflect this." + ::= { udp 7 } + +udpEndpointEntry OBJECT-TYPE + SYNTAX UdpEndpointEntry + MAX-ACCESS not-accessible + STATUS current + + + + DESCRIPTION + "Information about a particular current UDP endpoint. + + Implementers need to be aware that if the total number + of elements (octets or sub-identifiers) in + udpEndpointLocalAddress and udpEndpointRemoteAddress + exceeds 111, then OIDs of column instances in this table + will have more than 128 sub-identifiers and cannot be + accessed using SNMPv1, SNMPv2c, or SNMPv3." + INDEX { udpEndpointLocalAddressType, + udpEndpointLocalAddress, + udpEndpointLocalPort, + udpEndpointRemoteAddressType, + udpEndpointRemoteAddress, + udpEndpointRemotePort, + udpEndpointInstance } + ::= { udpEndpointTable 1 } + +UdpEndpointEntry ::= SEQUENCE { + udpEndpointLocalAddressType InetAddressType, + udpEndpointLocalAddress InetAddress, + udpEndpointLocalPort InetPortNumber, + udpEndpointRemoteAddressType InetAddressType, + udpEndpointRemoteAddress InetAddress, + udpEndpointRemotePort InetPortNumber, + udpEndpointInstance Unsigned32, + udpEndpointProcess Unsigned32 + } + +udpEndpointLocalAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type of udpEndpointLocalAddress. Only + IPv4, IPv4z, IPv6, and IPv6z addresses are expected, or + unknown(0) if datagrams for all local IP addresses are + accepted." + ::= { udpEndpointEntry 1 } + +udpEndpointLocalAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The local IP address for this UDP endpoint. + + The value of this object can be represented in three + + + + possible ways, depending on the characteristics of the + listening application: + + 1. For an application that is willing to accept both + IPv4 and IPv6 datagrams, the value of this object + must be ''h (a zero-length octet-string), with + the value of the corresponding instance of the + udpEndpointLocalAddressType object being unknown(0). + + 2. For an application that is willing to accept only IPv4 + or only IPv6 datagrams, the value of this object + must be '0.0.0.0' or '::', respectively, while the + corresponding instance of the + udpEndpointLocalAddressType object represents the + appropriate address type. + + 3. For an application that is listening for data + destined only to a specific IP address, the value + of this object is the specific IP address for which + this node is receiving packets, with the + corresponding instance of the + udpEndpointLocalAddressType object representing the + appropriate address type. + + As this object is used in the index for the + udpEndpointTable, implementors of this table should be + careful not to create entries that would result in OIDs + with more than 128 subidentifiers; else the information + cannot be accessed using SNMPv1, SNMPv2c, or SNMPv3." + ::= { udpEndpointEntry 2 } + +udpEndpointLocalPort OBJECT-TYPE + SYNTAX InetPortNumber + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The local port number for this UDP endpoint." + ::= { udpEndpointEntry 3 } + +udpEndpointRemoteAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type of udpEndpointRemoteAddress. Only + IPv4, IPv4z, IPv6, and IPv6z addresses are expected, or + unknown(0) if datagrams for all remote IP addresses are + accepted. Also, note that some combinations of + + + + udpEndpointLocalAdressType and + udpEndpointRemoteAddressType are not supported. In + particular, if the value of this object is not + unknown(0), it is expected to always refer to the + same IP version as udpEndpointLocalAddressType." + ::= { udpEndpointEntry 4 } + +udpEndpointRemoteAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The remote IP address for this UDP endpoint. If + datagrams from any remote system are to be accepted, + this value is ''h (a zero-length octet-string). + Otherwise, it has the type described by + udpEndpointRemoteAddressType and is the address of the + remote system from which datagrams are to be accepted + (or to which all datagrams will be sent). + + As this object is used in the index for the + udpEndpointTable, implementors of this table should be + careful not to create entries that would result in OIDs + with more than 128 subidentifiers; else the information + cannot be accessed using SNMPv1, SNMPv2c, or SNMPv3." + ::= { udpEndpointEntry 5 } + +udpEndpointRemotePort OBJECT-TYPE + SYNTAX InetPortNumber + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The remote port number for this UDP endpoint. If + datagrams from any remote system are to be accepted, + this value is zero." + ::= { udpEndpointEntry 6 } + +udpEndpointInstance OBJECT-TYPE + SYNTAX Unsigned32 (1..'ffffffff'h) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The instance of this tuple. This object is used to + distinguish among multiple processes 'connected' to + the same UDP endpoint. For example, on a system + implementing the BSD sockets interface, this would be + used to support the SO_REUSEADDR and SO_REUSEPORT + socket options." + + + + ::= { udpEndpointEntry 7 } + +udpEndpointProcess OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The system's process ID for the process associated with + this endpoint, or zero if there is no such process. + This value is expected to be the same as + HOST-RESOURCES-MIB::hrSWRunIndex or SYSAPPL-MIB:: + sysApplElmtRunIndex for some row in the appropriate + tables." + ::= { udpEndpointEntry 8 } + +-- The deprecated UDP Listener table + +-- The deprecated UDP listener table only contains information +-- about this entity's IPv4 UDP end-points on which a local +-- application is currently accepting datagrams. It does not +-- provide more detailed connection information, or information +-- about IPv6 endpoints. + +udpTable OBJECT-TYPE + SYNTAX SEQUENCE OF UdpEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "A table containing IPv4-specific UDP listener + information. It contains information about all local + IPv4 UDP end-points on which an application is + currently accepting datagrams. This table has been + deprecated in favor of the version neutral + udpEndpointTable." + ::= { udp 5 } + +udpEntry OBJECT-TYPE + SYNTAX UdpEntry + MAX-ACCESS not-accessible + STATUS deprecated + DESCRIPTION + "Information about a particular current UDP listener." + INDEX { udpLocalAddress, udpLocalPort } + ::= { udpTable 1 } + +UdpEntry ::= SEQUENCE { + udpLocalAddress IpAddress, + udpLocalPort Integer32 + + + +} + +udpLocalAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The local IP address for this UDP listener. In the + case of a UDP listener that is willing to accept + datagrams for any IP interface associated with the + node, the value 0.0.0.0 is used." + ::= { udpEntry 1 } + +udpLocalPort OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-only + STATUS deprecated + DESCRIPTION + "The local port number for this UDP listener." + ::= { udpEntry 2 } + +-- conformance information + +udpMIBConformance OBJECT IDENTIFIER ::= { udpMIB 2 } +udpMIBCompliances OBJECT IDENTIFIER ::= { udpMIBConformance 1 } +udpMIBGroups OBJECT IDENTIFIER ::= { udpMIBConformance 2 } + +-- compliance statements + +udpMIBCompliance2 MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for systems that implement + UDP. + + There are a number of INDEX objects that cannot be + represented in the form of OBJECT clauses in SMIv2, but + for which we have the following compliance + requirements, expressed in OBJECT clause form in this + description clause: + + -- OBJECT udpEndpointLocalAddressType + -- SYNTAX InetAddressType { unknown(0), ipv4(1), + -- ipv6(2), ipv4z(3), + -- ipv6z(4) } + -- DESCRIPTION + -- Support for dns(5) is not required. + -- OBJECT udpEndpointLocalAddress + + + + -- SYNTAX InetAddress (SIZE(0|4|8|16|20)) + -- DESCRIPTION + -- Support is only required for zero-length + -- octet-strings, and for scoped and unscoped + -- IPv4 and IPv6 addresses. + -- OBJECT udpEndpointRemoteAddressType + -- SYNTAX InetAddressType { unknown(0), ipv4(1), + -- ipv6(2), ipv4z(3), + -- ipv6z(4) } + -- DESCRIPTION + -- Support for dns(5) is not required. + -- OBJECT udpEndpointRemoteAddress + -- SYNTAX InetAddress (SIZE(0|4|8|16|20)) + -- DESCRIPTION + -- Support is only required for zero-length + -- octet-strings, and for scoped and unscoped + -- IPv4 and IPv6 addresses. + " + MODULE -- this module + MANDATORY-GROUPS { udpBaseGroup, udpEndpointGroup } + GROUP udpHCGroup + DESCRIPTION + "This group is mandatory for systems that + are capable of receiving or transmitting more than + 1 million UDP datagrams per second. 1 million + datagrams per second will cause a Counter32 to + wrap in just over an hour." + ::= { udpMIBCompliances 2 } + +udpMIBCompliance MODULE-COMPLIANCE + STATUS deprecated + DESCRIPTION + "The compliance statement for IPv4-only systems that + implement UDP. For IP version independence, this + compliance statement is deprecated in favor of + udpMIBCompliance2. However, agents are still + encouraged to implement these objects in order to + interoperate with the deployed base of managers." + MODULE -- this module + MANDATORY-GROUPS { udpGroup } + ::= { udpMIBCompliances 1 } + +-- units of conformance + +udpGroup OBJECT-GROUP + OBJECTS { udpInDatagrams, udpNoPorts, + udpInErrors, udpOutDatagrams, + udpLocalAddress, udpLocalPort } + + + + STATUS deprecated + DESCRIPTION + "The deprecated group of objects providing for + management of UDP over IPv4." + ::= { udpMIBGroups 1 } + +udpBaseGroup OBJECT-GROUP + OBJECTS { udpInDatagrams, udpNoPorts, udpInErrors, + udpOutDatagrams } + STATUS current + DESCRIPTION + "The group of objects providing for counters of UDP + statistics." + ::= { udpMIBGroups 2 } + +udpHCGroup OBJECT-GROUP + OBJECTS { udpHCInDatagrams, udpHCOutDatagrams } + STATUS current + DESCRIPTION + "The group of objects providing for counters of high + speed UDP implementations." + ::= { udpMIBGroups 3 } + +udpEndpointGroup OBJECT-GROUP + OBJECTS { udpEndpointProcess } + STATUS current + DESCRIPTION + "The group of objects providing for the IP version + independent management of UDP 'endpoints'." + ::= { udpMIBGroups 4 } + +END diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/DisplayHint.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/DisplayHint.cs new file mode 100644 index 0000000..82f6e44 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/DisplayHint.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Collections; + +namespace Lextm.SharpSnmpLib.Mib +{ + public class DisplayHint + { + private enum NumType { + dec, + hex, + oct, + bin, + str + } + + private string _str; + private NumType _type; + private int _decimalPoints = 0; + + public DisplayHint(string str) + { + _str = str; + if (str.StartsWith("d")) + { + _type = NumType.dec; + if (str.StartsWith("d-")) + { + _decimalPoints = Convert.ToInt32(str.Substring(2)); + } + } + else if (str.StartsWith("o")) + { + _type = NumType.oct; + } + else if (str.StartsWith("h")) + { + _type = NumType.hex; + } + else if (str.StartsWith("b")) + { + _type = NumType.bin; + } + else + { + _type = NumType.str; + foreach (char c in str) + { + + } + } + + } + + public override string ToString() + { + return _str; + } + + internal object Decode(int i) + { + switch (_type) + { + case NumType.dec: + if (_decimalPoints == 0) + { + return i; + } + else + { + return i / Math.Pow(10.0, _decimalPoints); + } + case NumType.hex: + return System.Convert.ToString(i, 16); + case NumType.oct: + return System.Convert.ToString(i, 8); + case NumType.bin: + return System.Convert.ToString(i, 2); + default: + return null; + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/AgentCapabilities.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/AgentCapabilities.cs new file mode 100644 index 0000000..d802735 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/AgentCapabilities.cs @@ -0,0 +1,29 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/31 + * Time: 13:18 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + /// + /// The AGENT-CAPABILITIES construct is used to specify implementation characteristics of an SNMP agent sub-system with respect to object types and events. + /// + public sealed class AgentCapabilities : EntityBase + { + /// + /// Creates an instance. + /// + /// + /// + /// + public AgentCapabilities(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + } + + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/EntityBase.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/EntityBase.cs new file mode 100644 index 0000000..015cc4a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/EntityBase.cs @@ -0,0 +1,46 @@ +using System; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + public abstract class EntityBase: IEntity + { + private readonly IModule _module; + private string _parent; + private readonly uint _value; + private readonly string _name; + + public EntityBase(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + { + _module = module; + _name = preAssignSymbols[0].ToString(); + + Lexer.ParseOidValue(symbols, out _parent, out _value); + } + + public IModule Module + { + get { return _module; } + } + + public string Parent + { + get { return _parent; } + set { _parent = value; } + } + + public uint Value + { + get { return _value; } + } + + public string Name + { + get { return _name; } + } + + public virtual string Description + { + get { return string.Empty; } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/IEntity.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/IEntity.cs new file mode 100644 index 0000000..d2840c2 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/IEntity.cs @@ -0,0 +1,62 @@ +// Entity interface. +// Copyright (C) 2008-2010 Malcolm Crowe, Lex Li, and other contributors. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/19 + * Time: 20:10 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + /// + /// Basic interface for all elements building up the MIB tree, thus having an OID as value. + /// + public interface IEntity : IDeclaration + { + /// + /// Parent name. + /// + string Parent + { + get; + set; + } + + /// + /// Value. + /// + uint Value + { + get; + } + + /// + /// Gets the description. + /// + /// The description. + string Description + { + get; + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ModuleCompliance.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ModuleCompliance.cs new file mode 100644 index 0000000..c5fb586 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ModuleCompliance.cs @@ -0,0 +1,23 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/21 + * Time: 19:35 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + /// + /// Description of ModuleComplianceNode. + /// + public sealed class ModuleCompliance : EntityBase + { + public ModuleCompliance(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + } + + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ModuleIdentity.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ModuleIdentity.cs new file mode 100644 index 0000000..6903e64 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ModuleIdentity.cs @@ -0,0 +1,10 @@ +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + public sealed class ModuleIdentity : EntityBase + { + public ModuleIdentity(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/NotificationGroup.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/NotificationGroup.cs new file mode 100644 index 0000000..129487a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/NotificationGroup.cs @@ -0,0 +1,22 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/21 + * Time: 19:34 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + /// + /// Description of NotificationGroupNode. + /// + public sealed class NotificationGroup : EntityBase + { + public NotificationGroup(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/NotificationType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/NotificationType.cs new file mode 100644 index 0000000..aa46358 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/NotificationType.cs @@ -0,0 +1,11 @@ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + public sealed class NotificationType : EntityBase + { + public NotificationType(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectGroup.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectGroup.cs new file mode 100644 index 0000000..78a6401 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectGroup.cs @@ -0,0 +1,22 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/21 + * Time: 19:27 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + /// + /// Description of ObjectGroupNode. + /// + public sealed class ObjectGroup : EntityBase + { + public ObjectGroup(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectIdentity.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectIdentity.cs new file mode 100644 index 0000000..7a6e32e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectIdentity.cs @@ -0,0 +1,21 @@ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + /// + /// Object identifier node. + /// + public sealed class ObjectIdentity : EntityBase + { + + /// + /// Creates a . + /// + /// Module name + /// Header + /// Lexer + public ObjectIdentity(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectType.cs new file mode 100644 index 0000000..d38270f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/ObjectType.cs @@ -0,0 +1,336 @@ +using System; +using System.Collections.Generic; +using Lextm.SharpSnmpLib.Mib.Elements.Types; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + public sealed class ObjectType : EntityBase, ITypeReferrer + { + private ITypeAssignment _syntax; + private string _units; + private MaxAccess _access; + private Status _status; + private string _description; + private string _reference; + private IList _indices; + private string _augments; + private string _defVal; + + public ObjectType(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + ParseProperties(preAssignSymbols); + } + + private void ParseProperties(SymbolList header) + { + ISymbolEnumerator headerSymbols = header.GetSymbolEnumerator(); + Symbol temp = headerSymbols.NextNonEOLSymbol(); + + // Skip name + temp = headerSymbols.NextNonEOLSymbol(); + temp.Expect(Symbol.ObjectType); + + _syntax = ParseSyntax (Module, headerSymbols); + _units = ParseUnits (headerSymbols); + _access = ParseAccess (headerSymbols); + _status = ParseStatus (headerSymbols); + _description = ParseDescription (headerSymbols); + _reference = ParseReference (headerSymbols); + _indices = ParseIndices (headerSymbols); + _augments = ParseAugments (headerSymbols); + _defVal = ParseDefVal (headerSymbols); + } + + private static string ParseAugments(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.Augments) + { + string augment = null; + + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.OpenBracket); + + current = symbols.NextNonEOLSymbol(); + augment = current.ToString(); + + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.CloseBracket); + + return augment; + } + else if (current != null) + { + symbols.PutBack(current); + } + + return null; + } + + private static string ParseDefVal(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.DefVal) + { + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.OpenBracket); + + string defVal = null; + current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.OpenBracket) + { + int depth = 1; + // TODO: decode this. + while (depth > 0) + { + current = symbols.NextNonEOLSymbol(); + if (current == Symbol.OpenBracket) + { + depth++; + } + else if (current == Symbol.CloseBracket) + { + depth--; + } + } + } + else + { + defVal = current.ToString(); + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.CloseBracket); + } + + return defVal; + } + else if (current != null) + { + symbols.PutBack(current); + } + + return null; + } + + private static IList ParseIndices(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.Index) + { + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.OpenBracket); + + List indices = new List(); + + while (current != Symbol.CloseBracket) + { + current = symbols.NextNonEOLSymbol(); + + bool lastIndex = false; + if (current == Symbol.Implied) + { + current = symbols.NextNonEOLSymbol(); + lastIndex = true; // 'IMPLIED' may only be used for last index + } + + current.Assert((current != Symbol.Comma) && (current != Symbol.CloseBracket), "Expected index name but found symbol!"); + indices.Add(current.ToString()); + + current = symbols.NextNonEOLSymbol(); + if (lastIndex) + { + current.Expect(Symbol.CloseBracket); + } + else + { + current.Expect(Symbol.Comma, Symbol.CloseBracket); + } + } + + return indices; + } + else if (current != null) + { + symbols.PutBack(current); + } + + return null; + } + + private static string ParseReference(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.Reference) + { + return symbols.NextNonEOLSymbol().ToString(); + } + else if (current != null) + { + symbols.PutBack(current); + } + + return null; + } + + private static string ParseDescription(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.Description) + { + return symbols.NextNonEOLSymbol().ToString().Trim(new char[] { '"' }); + } + else if (current != null) + { + symbols.PutBack(current); + } + + return null; + } + + private static Status ParseStatus(ISymbolEnumerator symbols) + { + Status status = Status.obsolete; + + Symbol current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.Status); + + current = symbols.NextNonEOLSymbol(); + try + { + status = (Status)Enum.Parse(typeof(Status), current.ToString()); + } + catch (ArgumentException) + { + current.Assert(false, "Invalid/Unknown status"); + } + + return status; + } + + private static MaxAccess ParseAccess(ISymbolEnumerator symbols) + { + MaxAccess access = MaxAccess.notAccessible; + + Symbol current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.MaxAccess, Symbol.Access); + + current = symbols.NextNonEOLSymbol(); + switch (current.ToString()) + { + case "not-accessible": + access = MaxAccess.notAccessible; + break; + case "accessible-for-notify": + access = MaxAccess.accessibleForNotify; + break; + case "read-only": + access = MaxAccess.readOnly; + break; + case "read-write": + access = MaxAccess.readWrite; + break; + case "read-create": + access = MaxAccess.readCreate; + break; + case "write-only": + access = MaxAccess.readWrite; + break; + default: + current.Assert(false, "Invalid/Unknown access"); + break; + } + + return access; + } + + private static string ParseUnits(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.Units) + { + return symbols.NextNonEOLSymbol().ToString(); + } + else if (current != null) + { + symbols.PutBack(current); + } + + return null; + } + + private static ITypeAssignment ParseSyntax(IModule module, ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.Syntax); + + return Lexer.ParseBasicTypeDef(module, String.Empty, symbols, isMacroSyntax: true); + } + + private static bool IsProperty(Symbol sym) + { + string s = sym.ToString(); + return s == "SYNTAX" || s == "MAX-ACCESS" || s == "STATUS" || s == "DESCRIPTION"; + } + + public ITypeAssignment Syntax + { + get { return _syntax; } + internal set { _syntax = value; } + } + + public override string Description + { + get { return _description; } + } + + public MaxAccess Access + { + get { return _access; } + } + + public IList Indices + { + get { return _indices; } + } + + public string Augments + { + get { return _augments; } + } + + #region ITypeReferrer Member + + public ITypeAssignment ReferredType + { + get { return _syntax; } + set { _syntax = value; } + } + + public ITypeAssignment BaseType + { + get + { + ITypeReferrer tr = this; + ITypeAssignment result = null; + + while ((tr != null) && (tr.ReferredType != null)) + { + result = tr.ReferredType; + tr = tr.ReferredType as ITypeReferrer; + } + + return result; + } + } + + #endregion + + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/OidValueAssignment.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/OidValueAssignment.cs new file mode 100644 index 0000000..f9543f8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Entities/OidValueAssignment.cs @@ -0,0 +1,30 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/17 + * Time: 20:49 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Entities +{ + /// + /// Object identifier node. + /// + public sealed class OidValueAssignment : EntityBase + { + /// + /// Creates a . + /// + /// Module + /// Name + /// Lexer + public OidValueAssignment(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + : base(module, preAssignSymbols, symbols) + { + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Exports.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Exports.cs new file mode 100644 index 0000000..d95ab6a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Exports.cs @@ -0,0 +1,56 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/6/7 + * Time: 17:34 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements +{ + /// + /// Description of Exports. + /// + public sealed class Exports: IElement + { + private IModule _module; + private readonly IList _types = new List(); + + public Exports(IModule module, ISymbolEnumerator s) + { + _module = module; + + Symbol previous = null; + Symbol current; + do + { + current = s.NextSymbol(); + + if (current == Symbol.EOL) + { + continue; + } + else if (((current == Symbol.Comma) || (current == Symbol.Semicolon)) && (previous != null)) + { + previous.AssertIsValidIdentifier(); + _types.Add(previous.ToString()); + } + + previous = current; + } + while (current != Symbol.Semicolon); + } + + #region IElement Member + + public IModule Module + { + get { return _module; } + } + + #endregion + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/IDeclaration.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/IDeclaration.cs new file mode 100644 index 0000000..d3382a5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/IDeclaration.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Lextm.SharpSnmpLib.Mib.Elements +{ + public interface IDeclaration: IElement + { + /// + /// Name. + /// + string Name + { + get; + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/IElement.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/IElement.cs new file mode 100644 index 0000000..ebabb86 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/IElement.cs @@ -0,0 +1,35 @@ +// Construct interface. +// Copyright (C) 2008-2010 Malcolm Crowe, Lex Li, and other contributors. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +namespace Lextm.SharpSnmpLib.Mib.Elements +{ + /// + /// Construct interface. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces")] + public interface IElement + { + /// + /// Containing module. + /// + IModule Module + { + get; + } + + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/ITypeReferrer.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/ITypeReferrer.cs new file mode 100644 index 0000000..406e60c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/ITypeReferrer.cs @@ -0,0 +1,10 @@ +using Lextm.SharpSnmpLib.Mib.Elements.Types; + +namespace Lextm.SharpSnmpLib.Mib.Elements +{ + public interface ITypeReferrer + { + ITypeAssignment ReferredType { get; set; } + ITypeAssignment BaseType { get; } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Imports.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Imports.cs new file mode 100644 index 0000000..2f61498 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Imports.cs @@ -0,0 +1,81 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/31 + * Time: 12:07 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements +{ + /// + /// The IMPORTS construct is used to specify items used in the current MIB module which are defined in another MIB module or ASN.1 module. + /// + public sealed class Imports : List, IElement + { + private IModule _module; + + /// + /// Creates an instance. + /// + /// + public Imports(IModule module, ISymbolEnumerator symbols) + { + _module = module; + + Symbol current; + while ((current = symbols.NextSymbol()) != Symbol.Semicolon) + { + if (current == Symbol.EOL) + { + continue; + } + + ImportsFrom imports = new ImportsFrom(current, symbols); + + this.Add(imports); + } + } + + public IList Dependents + { + get + { + List result = new List(); + + foreach (ImportsFrom import in this) + { + result.Add(import.Module); + } + + return result; + } + } + + public ImportsFrom GetImportFromType(string type) + { + foreach (ImportsFrom import in this) + { + if (import.Types.Contains(type)) + { + return import; + } + } + + return null; + } + + #region IElement Member + + public IModule Module + { + get { return _module; } + } + + #endregion + + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/ImportsFrom.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/ImportsFrom.cs new file mode 100644 index 0000000..f2d7033 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/ImportsFrom.cs @@ -0,0 +1,60 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/31 + * Time: 12:07 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements +{ + public sealed class ImportsFrom + { + private readonly string _module; + private readonly List _types = new List(); + + public ImportsFrom(Symbol last, ISymbolEnumerator symbols) + { + Symbol previous = last; + Symbol current; + while ((current = symbols.NextSymbol()) != Symbol.From) + { + if (current == Symbol.EOL) + { + continue; + } + + if (current == Symbol.Comma) + { + previous.AssertIsValidIdentifier(); + _types.Add(previous.ToString()); + } + + previous = current; + } + + previous.AssertIsValidIdentifier(); + _types.Add(previous.ToString()); + + _module = symbols.NextSymbol().ToString().ToUpperInvariant(); // module names are uppercase + } + + public string Module + { + get { return _module; } + } + + public IList Types + { + get { return _types; } + } + + public override string ToString() + { + return string.Join(", ", _types.ToArray()) + " FROM " + _module; + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/TrapType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/TrapType.cs new file mode 100644 index 0000000..6241267 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/TrapType.cs @@ -0,0 +1,48 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/31 + * Time: 12:20 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace Lextm.SharpSnmpLib.Mib.Elements +{ + public sealed class TrapType : IDeclaration + { + private readonly IModule _module; + private readonly string _name; + private readonly int _value; + + public TrapType(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + { + _module = module; + _name = preAssignSymbols[0].ToString(); + + Symbol valueSymbol = symbols.NextNonEOLSymbol(); + + bool succeeded = int.TryParse(valueSymbol.ToString(), out _value); + valueSymbol.Assert(succeeded, "not a decimal"); + } + + public int Value + { + get { return _value; } + } + + #region IDeclaration Member + + public IModule Module + { + get { return _module; } + } + + public string Name + { + get { return _name; } + } + + #endregion + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/BaseType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/BaseType.cs new file mode 100644 index 0000000..0da04ec --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/BaseType.cs @@ -0,0 +1,54 @@ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public abstract class BaseType : ITypeAssignment + { + private IModule _module; + private string _name; + + protected BaseType(IModule module, string name) + { + _module = module; + _name = name; + } + + public virtual IModule Module + { + // differentiate between: + // FddiTimeNano ::= INTEGER (0..2147483647) + // which is an IntegerType which appears under Types in a MibModule and therefore has a name and module + // and + // SYNTAX INTEGER (0..2147483647) + // which is also an IntegerType but not defined as a separate type and therefore has NO name and NO module + get + { + if (!string.IsNullOrEmpty(_name)) + { + return _module; + } + else + { + return null; + } + } + protected set { _module = value; } + } + + public virtual string Name + { + get + { + if (!string.IsNullOrEmpty(_name)) + { + return _name; + } + else + { + return "{ Implicit Base Type }"; + } + } + protected set { _name = value; } + } + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/BitsType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/BitsType.cs new file mode 100644 index 0000000..a92948a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/BitsType.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public class BitsType : BaseType + { + private ValueMap _map; + + public BitsType(IModule module, string name, ISymbolEnumerator symbols) + : base(module, name) + { + _map = Lexer.DecodeEnumerations(symbols); + } + + public ValueMap Map + { + get { return _map; } + } + + public string this[int value] + { + get { return _map[value]; } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Choice.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Choice.cs new file mode 100644 index 0000000..22d87e4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Choice.cs @@ -0,0 +1,35 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/31 + * Time: 11:39 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + /// + /// The CHOICE type represents a list of alternatives.. + /// + public sealed class Choice : BaseType + { + /// + /// Creates a instance. + /// + /// + /// + /// + public Choice(IModule module, string name, ISymbolEnumerator symbols) + : base(module, name) + { + while (symbols.NextNonEOLSymbol() != Symbol.OpenBracket) + { + } + + while (symbols.NextNonEOLSymbol() != Symbol.CloseBracket) + { + } + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/ITypeAssignment.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/ITypeAssignment.cs new file mode 100644 index 0000000..65ea975 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/ITypeAssignment.cs @@ -0,0 +1,6 @@ +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public interface ITypeAssignment : IDeclaration + { + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/IntegerType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/IntegerType.cs new file mode 100644 index 0000000..a47de79 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/IntegerType.cs @@ -0,0 +1,117 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/7/25 + * Time: 20:41 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System; +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + /// + /// The INTEGER type represents a list of alternatives, or a range of numbers.. + /// Includes Integer32 as it's indistinguishable from INTEGER. + /// + /** + * As this type is used for Integer32 as well as INTEGER it incorrectly + * allows enumeration sub-typing of Integer32. This is ok as currently we + * do not care about detecting incorrect MIBs and this doesn't block the + * decoding of correct MIBs. + */ + public sealed class IntegerType : BaseType + { + public enum Types + { + Integer, + Integer32 + } + + private Types _type; + private bool _isEnumeration; + private ValueMap _map; + private ValueRanges _ranges; + + /// + /// Creates an instance. + /// + /// + /// + /// + public IntegerType(IModule module, string name, Symbol type, ISymbolEnumerator symbols) + : base (module, name) + { + Types? t = GetExactType(type); + type.Assert(t.HasValue, "Unknown symbol for unsigned type!"); + _type = t.Value; + + _isEnumeration = false; + + Symbol current = symbols.NextNonEOLSymbol(); + if (current == Symbol.OpenBracket) + { + _isEnumeration = true; + symbols.PutBack(current); + _map = Lexer.DecodeEnumerations(symbols); + } + else if (current == Symbol.OpenParentheses) + { + symbols.PutBack(current); + _ranges = Lexer.DecodeRanges(symbols); + current.Assert(!_ranges.IsSizeDeclaration, "SIZE keyword is not allowed for ranges of integer types!"); + } + else + { + symbols.PutBack(current); + } + } + + public Types Type + { + get { return _type; } + } + + public ValueRanges Ranges + { + get { return _ranges; } + } + + public bool IsEnumeration + { + get + { + return _isEnumeration; + } + } + + public ValueMap Enumeration + { + get { return _isEnumeration ? _map : null; } + } + + internal static Types? GetExactType(Symbol symbol) + { + if (symbol == Symbol.Integer) + { + // represents the ASN.1 builtin INTEGER type: + // may be represent any arbitrary (signed/unsigned) integer (in theory may have any size) + return Types.Integer; + } + else if (symbol == Symbol.Integer32) + { + // Integer32 ::= INTEGER (-2147483648..2147483647) // from SNMPv2-SMI + return Types.Integer32; + } + + return null; + } + + internal static bool IsIntegerType(Symbol symbol) + { + return GetExactType(symbol).HasValue; + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/IpAddressType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/IpAddressType.cs new file mode 100644 index 0000000..afcaa6c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/IpAddressType.cs @@ -0,0 +1,21 @@ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public class IpAddressType : OctetStringType + { + public IpAddressType(IModule module, string name, ISymbolEnumerator symbols) + : base(module, name, symbols) + { + if (this.Size.Count != 0) + { + throw new MibException("Size definition not allowed for IpAddress type!"); + } + + // IpAddress type is defined as: + // IpAddress ::= + // [APPLICATION 0] + // IMPLICIT OCTET STRING (SIZE (4)) + this.Size.Add(new ValueRange(4, null)); + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Macro.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Macro.cs new file mode 100644 index 0000000..ac55f0b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Macro.cs @@ -0,0 +1,34 @@ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public sealed class Macro : ITypeAssignment + { + private IModule _module; + private string _name; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "temp")] + public Macro(IModule module, SymbolList preAssignSymbols, ISymbolEnumerator symbols) + { + _module = module; + _name = preAssignSymbols[0].ToString(); + + while (symbols.NextNonEOLSymbol() != Symbol.Begin) + { + } + + while (symbols.NextNonEOLSymbol() != Symbol.End) + { + } + } + + public IModule Module + { + get { return _module; } + } + + public string Name + { + get { return _name; } + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/ObjectIdentifierType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/ObjectIdentifierType.cs new file mode 100644 index 0000000..d8243e8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/ObjectIdentifierType.cs @@ -0,0 +1,11 @@ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public class ObjectIdentifierType : BaseType + { + public ObjectIdentifierType(IModule module, string name, ISymbolEnumerator symbols) + : base(module, name) + { + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/OctetStringType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/OctetStringType.cs new file mode 100644 index 0000000..ed3ef5f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/OctetStringType.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public class OctetStringType : BaseType + { + private ValueRanges _size; + + public OctetStringType(IModule module, string name, ISymbolEnumerator symbols) + : base(module, name) + { + Symbol current = symbols.NextNonEOLSymbol(); + if (current == Symbol.OpenParentheses) + { + symbols.PutBack(current); + _size = Lexer.DecodeRanges(symbols); + current.Assert(_size.IsSizeDeclaration, "SIZE keyword is required for ranges of octet string!"); + } + else + { + symbols.PutBack(current); + _size = new ValueRanges(isSizeDecl: true); + } + } + + public ValueRanges Size + { + get { return _size; } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/OpaqueType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/OpaqueType.cs new file mode 100644 index 0000000..fac9669 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/OpaqueType.cs @@ -0,0 +1,11 @@ + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public class OpaqueType : OctetStringType + { + public OpaqueType(IModule module, string name, ISymbolEnumerator symbols) + : base(module, name, symbols) + { + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Sequence.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Sequence.cs new file mode 100644 index 0000000..0e3de9e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/Sequence.cs @@ -0,0 +1,46 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/21 + * Time: 19:43 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + /// + /// The SEQUENCE type represents a set of specified types. This is roughtly analogous to a struct in C. + /// + public sealed class Sequence : BaseType + { + /// + /// Creates a instance. + /// + /// The module. + /// The name. + /// The enumerator. + public Sequence(IModule module, string name, ISymbolEnumerator symbols) + : base(module, name) + { + // parse between ( ) + Symbol temp = symbols.NextNonEOLSymbol(); + int bracketSection = 0; + temp.Expect(Symbol.OpenBracket); + bracketSection++; + while (bracketSection > 0) + { + temp = symbols.NextNonEOLSymbol(); + if (temp == Symbol.OpenBracket) + { + bracketSection++; + } + else if (temp == Symbol.CloseBracket) + { + bracketSection--; + } + } + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/SequenceOf.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/SequenceOf.cs new file mode 100644 index 0000000..e362ae2 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/SequenceOf.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + /// + /// The SEQUENCE OF type represents a list of data sets.. + /// + public sealed class SequenceOf : BaseType + { + private string _type; + + public SequenceOf(IModule module, string name, ISymbolEnumerator sym) + : base(module, name) + { + _type = sym.NextNonEOLSymbol().ToString(); + } + + public string Type + { + get { return _type; } + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/TextualConvention.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/TextualConvention.cs new file mode 100644 index 0000000..7689f1e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/TextualConvention.cs @@ -0,0 +1,238 @@ +using System; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + public sealed class TextualConvention : ITypeAssignment, ITypeReferrer + { + private IModule _module; + private string _name; + private DisplayHint _displayHint; + private Status _status; + private string _description; + private string _reference; + private ITypeAssignment _syntax; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "module")] + public TextualConvention(IModule module, string name, ISymbolEnumerator symbols) + { + _module = module; + _name = name; + + _displayHint = ParseDisplayHint(symbols); + _status = ParseStatus(symbols); + _description = ParseDescription(symbols); + _reference = ParseReference(symbols); + _syntax = ParseSyntax(module, symbols); + } + + private static DisplayHint ParseDisplayHint(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.DisplayHint) + { + return new DisplayHint(symbols.NextNonEOLSymbol().ToString().Trim(new char[] { '"' })); + } + + symbols.PutBack(current); + return null; + } + + private static Status ParseStatus(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.Status); + + try + { + return (Status)Enum.Parse(typeof(Status), symbols.NextNonEOLSymbol().ToString()); + } + catch (ArgumentException) + { + current.Assert(false, "Invalid/Unknown status"); + } + + return Status.current; + } + + private static string ParseDescription(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.Description); + + return symbols.NextNonEOLSymbol().ToString().Trim(new char[] { '"' }); + } + + private static string ParseReference(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.Reference) + { + string reference = symbols.NextNonEOLSymbol().ToString(); + if ((reference.Length >= 2) && reference.StartsWith("\"") && reference.EndsWith("\"")) + { + return reference.Substring(1, reference.Length-2); + } + + return reference; + } + + symbols.PutBack(current); + return null; + } + + private static ITypeAssignment ParseSyntax(IModule module, ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.Syntax); + + /* + * RFC2579 definition: + * Syntax ::= -- Must be one of the following: + * -- a base type (or its refinement), or + * -- a BITS pseudo-type + * type + * | "BITS" "{" NamedBits "}" + * + * From section 3.5: + * The data structure must be one of the alternatives defined + * in the ObjectSyntax CHOICE or the BITS construct. Note + * that this means that the SYNTAX clause of a Textual + * Convention can not refer to a previously defined Textual + * Convention. + * + * The SYNTAX clause of a TEXTUAL CONVENTION macro may be + * sub-typed in the same way as the SYNTAX clause of an + * OBJECT-TYPE macro. + * + * Therefore the possible values are (grouped by underlying type): + * INTEGER, Integer32 + * OCTET STRING, Opaque + * OBJECT IDENTIFIER + * IpAddress + * Counter64 + * Unsigned32, Counter32, Gauge32, TimeTicks + * BITS + * With appropriate sub-typing. + */ + + return Lexer.ParseBasicTypeDef(module, String.Empty, symbols, isMacroSyntax: true); + } + + public IModule Module + { + get { return _module; } + } + + public string Name + { + get { return _name; } + } + + public string DisplayHint + { + get { return _displayHint == null ? null : _displayHint.ToString(); } + } + + public Status Status + { + get { return _status; } + } + + public string Description + { + get { return _description; } + } + + public string Reference + { + get { return _reference; } + } + + public ITypeAssignment Syntax + { + get { return _syntax; } + } + + //internal object Decode(Variable v) + //{ + // if (_syntax is IntegerType) + // { + // Integer32 i = v.Data as Integer32; + // if (i == null || (_syntax as IntegerType).IsEnumeration) + // { + // return null; + // } + // else if (_displayHint != null) + // { + // return _displayHint.Decode(i.ToInt32()); + // } + // else + // { + // return i.ToInt32(); + // } + // } + // else if (_syntax is UnsignedType) + // { + // Integer32 i = v.Data as Integer32; + // if (i == null) + // { + // return null; + // } + // else if (_displayHint != null) + // { + // return _displayHint.Decode(i.ToInt32()); + // } + // else + // { + // return i.ToInt32(); + // } + // } + // else if (_syntax is OctetStringType) + // { + // OctetString o = v.Data as OctetString; + // if (o == null) + // { + // return null; + // } + // else + // { + // // TODO: Follow the format specifier for octet strings. + // return null; + // } + // } + // else + // { + // return null; + // } + //} + + #region ITypeReferrer Member + + public ITypeAssignment ReferredType + { + get { return _syntax; } + set { _syntax = value; } + } + + public ITypeAssignment BaseType + { + get + { + ITypeReferrer tr = this; + ITypeAssignment result = this; + + while ((tr != null) && (tr.ReferredType != null)) + { + result = tr.ReferredType; + tr = tr.ReferredType as ITypeReferrer; + } + + return result; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/TypeAssignment.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/TypeAssignment.cs new file mode 100644 index 0000000..b5f51d8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/TypeAssignment.cs @@ -0,0 +1,147 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/18 + * Time: 13:24 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using System; +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + /* Please be aware of the following possible constructs: + * + * isnsRegEntityIndex OBJECT-TYPE + * SYNTAX IsnsEntityIndexIdOrZero + * ( 1 .. 4294967295 ) + * MAX-ACCESS not-accessible + * + * + */ + + /// + /// + public sealed class TypeAssignment : ITypeAssignment + { + private IModule _module; + private string _name; + private string _type; + private ValueRanges _ranges; + private ValueMap _map; + + /// + /// Creates an . + /// + /// The module. + /// The name. + /// The type. + /// The symbols. + /// if set to true indicates that the syntax clause of a macro is parsed (e.g. OBJECT-TYPE, TEXTUAL-CONVENTION). + public TypeAssignment(IModule module, string name, Symbol type, ISymbolEnumerator symbols, bool isMacroSyntax) + { + _module = module; + _name = name; + + SymbolList typeSymbols = new SymbolList(); + typeSymbols.Add(type); + + Symbol current = symbols.NextSymbol(); + while (current != Symbol.EOL) + { + if (current == Symbol.OpenParentheses) + { + // parse range of unknown type + symbols.PutBack(current); + _ranges = Lexer.DecodeRanges(symbols); + break; + } + else if (current == Symbol.OpenBracket) + { + symbols.PutBack(current); + _map = Lexer.DecodeEnumerations(symbols); + break; + } + + typeSymbols.Add(current); + current = symbols.NextSymbol(); + } + + _type = typeSymbols.Join(" "); + + if ((_ranges == null) && (_map == null)) + { + current = symbols.NextNonEOLSymbol(); + if (current == Symbol.OpenParentheses) + { + // parse range of unknown type + symbols.PutBack(current); + _ranges = Lexer.DecodeRanges(symbols); + } + else if (current == Symbol.OpenBracket) + { + symbols.PutBack(current); + _map = Lexer.DecodeEnumerations(symbols); + } + else if (current != null) + { + symbols.PutBack(current); + } + } + + if (isMacroSyntax) + { + // inside a macro the syntax is limited to one line, except there are brackets used for ranges/enums + return; + } + + // outside macro Syntax clause we wait for two consecutive linebreaks with a following valid identifier as end condition + Symbol previous = current; + Symbol veryPrevious = null; + + while ((current = symbols.NextSymbol()) != null) + { + if ((veryPrevious == Symbol.EOL) && (previous == Symbol.EOL) && current.IsValidIdentifier()) + { + symbols.PutBack(current); + return; + } + + veryPrevious = previous; + previous = current; + } + + previous.Assert(false, "end of file reached"); + } + + public string Type + { + get { return _type; } + } + + public ValueRanges Ranges + { + get { return _ranges; } + } + + public IDictionary Map + { + get { return _map; } + } + + #region ITypeAssignment Member + + public IModule Module + { + get { return _module; } + } + + public string Name + { + get { return _name; } + } + + #endregion + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/UnsignedType.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/UnsignedType.cs new file mode 100644 index 0000000..264113d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Elements/Types/UnsignedType.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib.Elements.Types +{ + /** + * As this type is used for Counter32 and TimeTicks as well as Unsigned32 + * and Gauge32 it incorrectly allows range restrictions of Counter32 and + * TimeTicks. This is ok as currently we do not care about detecting + * incorrect MIBs and this doesn't block the decoding of correct MIBs. + */ + public class UnsignedType : BaseType + { + public enum Types + { + Unsigned32, + Gauge32, + Counter32, + TimeTicks, + Counter64, + } + + private Types _type; + private ValueRanges _ranges; + + public UnsignedType(IModule module, string name, Symbol type, ISymbolEnumerator symbols) + : base(module, name) + { + Types? t = GetExactType(type); + type.Assert(t.HasValue, "Unknown symbol for unsigned type!"); + _type = t.Value; + + Symbol current = symbols.NextNonEOLSymbol(); + if (current == Symbol.OpenParentheses) + { + current.Assert((_type != Types.Counter64), "Ranges are not supported for Counter64 type!"); // our internal struct can only hold int64 values + + symbols.PutBack(current); + _ranges = Lexer.DecodeRanges(symbols); + current.Assert(!_ranges.IsSizeDeclaration, "SIZE keyword is not allowed for ranges of unsigned types!"); + } + else + { + symbols.PutBack(current); + } + } + + public Types Type + { + get { return _type; } + } + + public ValueRanges Ranges + { + get { return _ranges; } + } + + internal static Types? GetExactType(Symbol symbol) + { + if (symbol == Symbol.Unsigned32) + { + // [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) // from SNMPv2-SMI + return Types.Unsigned32; + } + else if (symbol == Symbol.Gauge32) + { + // [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) // from SNMPv2-SMI + return Types.Gauge32; + } + else if (symbol == Symbol.Counter32) + { + // [APPLICATION 1] IMPLICIT INTEGER (0..4294967295) // from SNMPv2-SMI + return Types.Counter32; + } + else if (symbol == Symbol.TimeTicks) + { + // [APPLICATION 3] IMPLICIT INTEGER (0..4294967295) // from SNMPv2-SMI + RFC1155-SMI + return Types.TimeTicks; + } + else if (symbol == Symbol.Gauge) + { + // [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) // from RFC1155-SMI + return Types.Gauge32; + } + else if (symbol == Symbol.Counter) + { + // [APPLICATION 1] IMPLICIT INTEGER (0..4294967295) // from RFC1155-SMI + return Types.Counter32; + } + else if (symbol == Symbol.Counter64) + { + // [APPLICATION 6] IMPLICIT INTEGER (0..18446744073709551615) // from SNMPv2-SMI + return Types.Counter64; + } + + return null; + } + + internal static bool IsUnsignedType(Symbol symbol) + { + return GetExactType(symbol).HasValue; + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/IModule.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/IModule.cs new file mode 100644 index 0000000..620a421 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/IModule.cs @@ -0,0 +1,81 @@ +// Module interface. +// Copyright (C) 2008-2010 Malcolm Crowe, Lex Li, and other contributors. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +/* + * Created by SharpDevelop. + * User: lextm + * Date: 5/1/2009 + * Time: 10:40 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using System.Collections.Generic; +using Lextm.SharpSnmpLib.Mib.Elements.Entities; +using Lextm.SharpSnmpLib.Mib.Elements.Types; +using Lextm.SharpSnmpLib.Mib.Elements; + +namespace Lextm.SharpSnmpLib.Mib +{ + /// + /// MIB Module interface. + /// + public interface IModule + { + /// + /// Module name. + /// + string Name + { + get; + } + + Exports Exports + { + get; + } + + Imports Imports + { + get; + } + + /// + /// Entities + Types + all other elements implementing IDeclaration + /// + IList Declarations + { + get; + } + + /// + /// Entities. + /// + IList Entities + { + get; + } + + /// + /// Known types. + /// + IList Types + { + get; + } + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ISymbolEnumerator.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ISymbolEnumerator.cs new file mode 100644 index 0000000..705fe0b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ISymbolEnumerator.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib +{ + public interface ISymbolEnumerator: IEnumerator + { + bool PutBack(Symbol item); + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Lexer.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Lexer.cs new file mode 100644 index 0000000..4b50977 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Lexer.cs @@ -0,0 +1,581 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/17 + * Time: 16:50 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Lextm.SharpSnmpLib.Mib.Elements.Types; + +namespace Lextm.SharpSnmpLib.Mib +{ + /// + /// Lexer class that parses MIB files into symbol list. + /// + public sealed class Lexer + { + private readonly SymbolList _symbols = new SymbolList(); + + public Lexer(string file) + : this(file, new StreamReader(file)) + { + } + + public Lexer(string file, TextReader stream) + { + this.Parse(file, stream); + } + + + public ISymbolEnumerator GetEnumerator() + { + return _symbols.GetSymbolEnumerator(); + } + + + #region Parsing of MIB File + + private class ParseParams + { + public string File; + public StringBuilder Temp = new StringBuilder(); + public bool StringSection = false; + public bool AssignSection = false; + public bool AssignAhead = false; + public bool DotSection = false; + + } + + /// + /// Parses MIB file to symbol list. + /// + /// File + /// File stream + private void Parse(string file, TextReader stream) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + ParseParams pp = new ParseParams(); + pp.File = file; + + string line; + int row = 0; + while ((line = stream.ReadLine()) != null) + { + if (!pp.StringSection && line.TrimStart().StartsWith("--", StringComparison.Ordinal)) + { + row++; + continue; // commented line + } + + ParseLine(pp, line, row); + row++; + } + } + + private void ParseLine(ParseParams pp, string line, int row) + { + line = line + "\n"; + int count = line.Length; + for (int i = 0; i < count; i++) + { + char current = line[i]; + bool moveNext = Parse(pp, current, row, i); + if (moveNext) + { + break; + } + } + } + + private bool Parse(ParseParams pp, char current, int row, int column) + { + switch (current) + { + case '\n': + case '{': + case '}': + case '(': + case ')': + case '[': + case ']': + case ';': + case ',': + case '|': + if (!pp.StringSection) + { + bool moveNext = ParseLastSymbol(pp, row, column); + if (moveNext) + { + _symbols.Add(CreateSpecialSymbol(pp.File, '\n', row, column)); + return true; + } + + _symbols.Add(CreateSpecialSymbol(pp.File, current, row, column)); + return false; + } + + break; + case '"': + pp.StringSection = !pp.StringSection; + break; + case '\r': + return false; + default: + if ((int)current == 0x1A) + { + // IMPORTANT: ignore invisible characters such as SUB. + return false; + } + + if (Char.IsWhiteSpace(current) && !pp.AssignSection && !pp.StringSection) + { + bool moveNext = ParseLastSymbol(pp, row, column); + if (moveNext) + { + _symbols.Add(CreateSpecialSymbol(pp.File, '\n', row, column)); + return true; + } + + return false; + } + + if (pp.AssignAhead) + { + pp.AssignAhead = false; + ParseLastSymbol(pp, row, column); + break; + } + + if (pp.DotSection && current != '.') + { + ParseLastSymbol(pp, row, column); + pp.DotSection = false; + } + + if (current == '.' && !pp.StringSection) + { + if (!pp.DotSection) + { + ParseLastSymbol(pp, row, column); + pp.DotSection = true; + } + } + + if (current == ':' && !pp.StringSection) + { + if (!pp.AssignSection) + { + ParseLastSymbol(pp, row, column); + } + + pp.AssignSection = true; + } + + if (current == '=' && !pp.StringSection) + { + pp.AssignSection = false; + pp.AssignAhead = true; + } + + break; + } + + pp.Temp.Append(current); + return false; + } + + private bool ParseLastSymbol(ParseParams pp, int row, int column) + { + if (pp.Temp.Length > 0) + { + Symbol s = new Symbol(pp.File, pp.Temp.ToString(), row, column); + + pp.Temp.Length = 0; + + if (s.ToString().StartsWith(Symbol.Comment.ToString())) + { + // ignore the rest symbols on this line because they are in comment. + return true; + } + + _symbols.Add(s); + } + + return false; + } + + private static Symbol CreateSpecialSymbol(string file, char value, int row, int column) + { + string str; + switch (value) + { + case '\n': + str = Environment.NewLine; + break; + case '{': + str = "{"; + break; + case '}': + str = "}"; + break; + case '(': + str = "("; + break; + case ')': + str = ")"; + break; + case '[': + str = "["; + break; + case ']': + str = "]"; + break; + case ';': + str = ";"; + break; + case ',': + str = ","; + break; + case '|': + str = "|"; + break; + default: + throw new ArgumentException("value is not a special character"); + } + + return new Symbol(file, str, row, column); + } + + #endregion + + #region Static Parse Helper + + public static ITypeAssignment ParseBasicTypeDef(IModule module, string name, ISymbolEnumerator symbols, bool isMacroSyntax = false) + { + Symbol current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.Bits) + { + return new BitsType(module, name, symbols); + } + if (IntegerType.IsIntegerType(current)) + { + return new IntegerType(module, name, current, symbols); + } + if (UnsignedType.IsUnsignedType(current)) + { + return new UnsignedType(module, name, current, symbols); + } + if (current == Symbol.Opaque) + { + return new OpaqueType(module, name, symbols); + } + if (current == Symbol.IpAddress) + { + return new IpAddressType(module, name, symbols); + } + if (current == Symbol.TextualConvention) + { + return new TextualConvention(module, name, symbols); + } + if (current == Symbol.Octet) + { + Symbol next = symbols.NextNonEOLSymbol(); + + if (next == Symbol.String) + { + return new OctetStringType(module, name, symbols); + } + + symbols.PutBack(next); + } + if (current == Symbol.Object) + { + Symbol next = symbols.NextNonEOLSymbol(); + + if (next == Symbol.Identifier) + { + return new ObjectIdentifierType(module, name, symbols); + } + + symbols.PutBack(next); + } + if (current == Symbol.Sequence) + { + Symbol next = symbols.NextNonEOLSymbol(); + + if (next == Symbol.Of) + { + return new SequenceOf(module, name, symbols); + } + else + { + symbols.PutBack(next); + return new Sequence(module, name, symbols); + } + } + if (current == Symbol.Choice) + { + return new Choice(module, name, symbols); + } + + + return new TypeAssignment(module, name, current, symbols, isMacroSyntax); + } + + public static void ParseOidValue(ISymbolEnumerator symbols, out string parent, out uint value) + { + parent = null; + value = 0; + + Symbol current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.OpenBracket); + + Symbol previous = null; + StringBuilder longParent = new StringBuilder(); + + current = symbols.NextNonEOLSymbol(); + longParent.Append(current); + + while ((current = symbols.NextNonEOLSymbol()) != null) + { + bool succeeded; + + if (current == Symbol.OpenParentheses) + { + longParent.Append(current); + + current = symbols.NextNonEOLSymbol(); + succeeded = UInt32.TryParse(current.ToString(), out value); + current.Assert(succeeded, "not a decimal"); + longParent.Append(current); + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.CloseParentheses); + longParent.Append(current); + continue; + } + + if (current == Symbol.CloseBracket) + { + parent = longParent.ToString(); + return; + } + + succeeded = UInt32.TryParse(current.ToString(), out value); + if (succeeded) + { + // numerical way + while ((current = symbols.NextNonEOLSymbol()) != Symbol.CloseBracket) + { + longParent.Append(".").Append(value); + succeeded = UInt32.TryParse(current.ToString(), out value); + current.Assert(succeeded, "not a decimal"); + } + + current.Expect(Symbol.CloseBracket); + parent = longParent.ToString(); + return; + } + + longParent.Append("."); + longParent.Append(current); + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.OpenParentheses); + longParent.Append(current); + current = symbols.NextNonEOLSymbol(); + succeeded = UInt32.TryParse(current.ToString(), out value); + current.Assert(succeeded, "not a decimal"); + longParent.Append(current); + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.CloseParentheses); + longParent.Append(current); + previous = current; + } + + throw MibException.Create("end of file reached", previous); + } + + + public static ValueRanges DecodeRanges(ISymbolEnumerator symbols) + { + ValueRanges result = new ValueRanges(); + + Symbol startSymbol = symbols.NextNonEOLSymbol(); + Symbol current = startSymbol; + current.Expect(Symbol.OpenParentheses); + + while (current != Symbol.CloseParentheses) + { + Symbol value1Symbol = symbols.NextNonEOLSymbol(); + + if ((value1Symbol == Symbol.Size) && !result.IsSizeDeclaration) + { + result.IsSizeDeclaration = true; + symbols.NextNonEOLSymbol().Expect(Symbol.OpenParentheses); + continue; + } + + // check for valid number + Int64? value1 = DecodeNumber(value1Symbol); + if (!value1.HasValue) + { + value1Symbol.Assert(false, "Invalid range declaration!"); + } + + // process next symbol + ValueRange range; + current = symbols.NextNonEOLSymbol(); + + if (current == Symbol.DoubleDot) + { + // its a continous range + Symbol value2Symbol = symbols.NextNonEOLSymbol(); + Int64? value2 = DecodeNumber(value2Symbol); + value2Symbol.Assert(value2.HasValue && (value2.Value >= value1.Value), "Invalid range declaration!"); + + if (value2.Value == value1.Value) + { + range = new ValueRange(value1.Value, null); + } + else + { + range = new ValueRange(value1.Value, value2.Value); + } + + current = symbols.NextNonEOLSymbol(); + } + else + { + // its a single number + range = new ValueRange(value1.Value, null); + } + + // validate range + if (result.IsSizeDeclaration) + { + value1Symbol.Assert(range.Start >= 0, "Invalid range declaration! Size must be greater than 0"); + } + + result.Add(range); + + // check next symbol + current.Expect(Symbol.Pipe, Symbol.CloseParentheses); + } + + if (result.IsSizeDeclaration) + { + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.CloseParentheses); + } + + // validate ranges in between + for (int i=0; i 3)) + { + // search second apostrophe + int end = numString.IndexOf('\'', 1); + if (end == (numString.Length - 2)) + { + try + { + switch (numString[numString.Length - 1]) + { + case 'b': + case 'B': + result = Convert.ToInt64(numString.Substring(1, numString.Length - 3), 2); + return result; + case 'h': + case 'H': + result = Convert.ToInt64(numString.Substring(1, numString.Length - 3), 16); + return result; + } + } + catch + { + } + } + } + else if (Int64.TryParse(numString, out result)) + { + return result; + } + } + + return null; + } + + public static ValueMap DecodeEnumerations(ISymbolEnumerator symbols) + { + Symbol current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.OpenBracket); + + ValueMap map = new ValueMap(); + do + { + current = symbols.NextNonEOLSymbol(); + string identifier = current.ToString(); + + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.OpenParentheses); + + current = symbols.NextNonEOLSymbol(); + Int64 enumValue; + if (Int64.TryParse(current.ToString(), out enumValue)) + { + try + { + // Have to include the number as it seems repeated identifiers are allowed ?? + map.Add(enumValue, String.Format("{0}({1})", identifier, enumValue)); + } + catch (ArgumentException ex) + { + current.Assert(false, ex.Message); + } + } + else + { + // Need to get "DefinedValue". + } + + current = symbols.NextNonEOLSymbol(); + current.Expect(Symbol.CloseParentheses); + + current = symbols.NextNonEOLSymbol(); + } while (current == Symbol.Comma); + + current.Expect(Symbol.CloseBracket); + + return map; + } + + #endregion + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MaxAccess.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MaxAccess.cs new file mode 100644 index 0000000..d1aab50 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MaxAccess.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Lextm.SharpSnmpLib.Mib +{ + public enum MaxAccess + { + notAccessible, + accessibleForNotify, + readOnly, + readWrite, + readCreate + } + +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibDocument.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibDocument.cs new file mode 100644 index 0000000..4ebedc1 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibDocument.cs @@ -0,0 +1,57 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/17 + * Time: 17:38 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib +{ + /// + /// MIB document. + /// + public sealed class MibDocument + { + private readonly List _modules = new List(); + + /// + /// Initializes a new instance of the class. + /// + /// The file. + public MibDocument(string file) + : this(new Lexer(file)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The lexer. + public MibDocument(Lexer lexer) + { + ISymbolEnumerator symbols = lexer.GetEnumerator(); + + Symbol current; + while ((current = symbols.NextNonEOLSymbol()) != null) + { + symbols.PutBack(current); + _modules.Add(new MibModule(symbols)); + } + } + + /// + /// containing in this document. + /// + public IList Modules + { + get + { + return _modules; + } + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibException.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibException.cs new file mode 100644 index 0000000..6f01370 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibException.cs @@ -0,0 +1,113 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/17 + * Time: 16:33 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System; +using System.Globalization; +#if (!SILVERLIGHT) +using System.Runtime.Serialization; +using System.Security.Permissions; +#endif + +namespace Lextm.SharpSnmpLib.Mib +{ + /// + /// Description of MibException. + /// + [Serializable] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Mib")] + public sealed class MibException : Exception + { + /// + /// Symbol. + /// + public Symbol Symbol { get; private set; } + + /// + /// Creates a . + /// + public MibException() + { + } + + /// + /// Creates a instance with a specific . + /// + /// Message + public MibException(string message) : base(message) + { + } + + /// + /// Creates a instance with a specific and an . + /// + /// Message + /// Inner exception + public MibException(string message, Exception inner) + : base(message, inner) + { + } +#if (!SILVERLIGHT) + /// + /// Creates a instance. + /// + /// Info + /// Context + private MibException(SerializationInfo info, StreamingContext context) : base(info, context) + { + if (info == null) + { + throw new ArgumentNullException("info"); + } + + Symbol = (Symbol)info.GetValue("Symbol", typeof(Symbol)); + } + + /// + /// Gets object data. + /// + /// Info + /// Context + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("Symbol", Symbol); + } +#endif + + /// + /// Creates a with a specific . + /// + /// Message + /// Symbol + /// + public static MibException Create(string message, Symbol symbol) + { + if (symbol == null) + { + throw new ArgumentNullException("symbol"); + } + + if (String.IsNullOrEmpty(message)) + { + message = "Unknown MIB Exception"; + } + + message = String.Format( + "{0} (file: \"{1}\"; row: {2}; column: {3})", + message, + symbol.File, + symbol.Row + 1, + symbol.Column + 1); + + MibException ex = new MibException(message) { Symbol = symbol }; + return ex; + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibModule.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibModule.cs new file mode 100644 index 0000000..48fc8a9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibModule.cs @@ -0,0 +1,294 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/17 + * Time: 17:38 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System; +using System.Collections.Generic; +using Lextm.SharpSnmpLib.Mib.Elements; +using Lextm.SharpSnmpLib.Mib.Elements.Entities; +using Lextm.SharpSnmpLib.Mib.Elements.Types; + +namespace Lextm.SharpSnmpLib.Mib +{ + /// + /// MIB module class. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Mib")] + public sealed class MibModule : IModule + { + private readonly string _name; + private readonly Imports _imports; + private readonly Exports _exports; + private readonly List _tokens = new List(); + + /// + /// Creates a with a specific . + /// + /// Module name + /// Lexer + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "lexer")] + public MibModule(ISymbolEnumerator symbols) + { + if (symbols == null) + { + throw new ArgumentNullException("lexer"); + } + + Symbol temp = symbols.NextNonEOLSymbol(); + temp.AssertIsValidIdentifier(); + _name = temp.ToString().ToUpperInvariant(); // all module names are uppercase + + temp = symbols.NextNonEOLSymbol(); + temp.Expect(Symbol.Definitions); + + temp = symbols.NextNonEOLSymbol(); + temp.Expect(Symbol.Assign); + + temp = symbols.NextSymbol(); + temp.Expect(Symbol.Begin); + + temp = symbols.NextNonEOLSymbol(); + if (temp == Symbol.Imports) + { + _imports = ParseDependents(symbols); + } + else if (temp == Symbol.Exports) + { + _exports = ParseExports(symbols); + } + else + { + symbols.PutBack(temp); + } + + ParseEntities(symbols); + } + + #region Accessors + + /// + /// Module name. + /// + public string Name + { + get { return _name; } + } + + public Exports Exports + { + get { return _exports; } + } + + public Imports Imports + { + get { return _imports; } + } + + public List Tokens + { + get { return this._tokens; } + } + + /// + /// Entities + Types + all other elements implementing IDeclaration + /// + public IList Declarations + { + get + { + IList result = new List(); + foreach (IElement e in _tokens) + { + IDeclaration decl = e as IDeclaration; + if (decl != null) + { + result.Add(decl); + } + } + + return result; + } + } + + /// + /// OID nodes. + /// + public IList Entities + { + get + { + IList result = new List(); + foreach (IElement e in _tokens) + { + IEntity entity = e as IEntity; + if (entity != null) + { + result.Add(entity); + } + } + + return result; + } + } + + public IList Types + { + get + { + IList result = new List(); + foreach (IElement e in _tokens) + { + ITypeAssignment type = e as ITypeAssignment; + if (type != null) + { + result.Add(type); + } + } + + return result; + } + } + + #endregion + + #region Parsing of Symbols + + private Exports ParseExports(ISymbolEnumerator symbols) + { + return new Exports(this, symbols); + } + + private Imports ParseDependents(ISymbolEnumerator symbols) + { + return new Imports(this, symbols); + } + + private void ParseEntities(ISymbolEnumerator symbols) + { + Symbol temp = symbols.NextNonEOLSymbol(); + SymbolList buffer = new SymbolList(); + + while (temp != Symbol.End) + { + if (temp == Symbol.Assign) + { + ParseEntity(buffer, symbols); + buffer.Clear(); + // skip linebreaks behind an entity + temp = symbols.NextNonEOLSymbol(); + } + else + { + buffer.Add(temp); + temp = symbols.NextSymbol(); + } + } + } + + private void ParseEntity(SymbolList preAssignSymbols, ISymbolEnumerator symbols) + { + if ((preAssignSymbols == null) || (preAssignSymbols.Count == 0)) + { + Symbol s = symbols.NextSymbol(); + if (s != null) + { + s.Assert(false, "Invalid Entitiy declaration"); + } + else + { + throw new MibException("Invalid Entitiy declaration"); + } + } + + // check for a valid identifier + preAssignSymbols[0].AssertIsValidIdentifier(); + + if (preAssignSymbols.Count == 1) + { + // its a typedef + _tokens.Add(Lexer.ParseBasicTypeDef(this, preAssignSymbols[0].ToString(), symbols, isMacroSyntax: false)); + return; + } + + ISymbolEnumerator preAssignSymbolsEnumerator = preAssignSymbols.GetSymbolEnumerator(); + preAssignSymbolsEnumerator.NextNonEOLSymbol(); // returns identifier + Symbol type = preAssignSymbolsEnumerator.NextNonEOLSymbol(); + + // parse declarations + if (type == Symbol.Object) + { + Symbol next = preAssignSymbolsEnumerator.NextNonEOLSymbol(); + + if (next == Symbol.Identifier) + { + _tokens.Add(new OidValueAssignment(this, preAssignSymbols, symbols)); + return; + } + else if (next != null) + { + preAssignSymbolsEnumerator.PutBack(next); + } + } + if (type == Symbol.ModuleIdentity) + { + _tokens.Add(new ModuleIdentity(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.ObjectType) + { + _tokens.Add(new ObjectType(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.ObjectGroup) + { + _tokens.Add(new ObjectGroup(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.NotificationGroup) + { + _tokens.Add(new NotificationGroup(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.ModuleCompliance) + { + _tokens.Add(new ModuleCompliance(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.NotificationType) + { + _tokens.Add(new NotificationType(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.ObjectIdentity) + { + _tokens.Add(new ObjectIdentity(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.Macro) + { + _tokens.Add(new Macro(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.TrapType) + { + _tokens.Add(new TrapType(this, preAssignSymbols, symbols)); + return; + } + if (type == Symbol.AgentCapabilities) + { + _tokens.Add(new AgentCapabilities(this, preAssignSymbols, symbols)); + return; + } + + preAssignSymbols[1].Assert(false, "Unknown/Invalid declaration"); + } + + #endregion + + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibResolver.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibResolver.cs new file mode 100644 index 0000000..0bfdd26 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibResolver.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Reflection; + +namespace Lextm.SharpSnmpLib.Mib +{ + public interface IMibResolver + { + IModule Resolve(string moduleName); + } + + public class FileSystemMibResolver : IMibResolver + { + private string _path; + private bool _recursive; + + public FileSystemMibResolver(string path, bool recursive) + { + _path = path; + _recursive = recursive; + } + + #region IMibResolver Member + + public IModule Resolve(string moduleName) + { + if (Directory.Exists(_path)) + { + string[] matchedFiles = Directory.GetFiles( + _path, + "*", + (_recursive) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); + + if ((matchedFiles != null) && (matchedFiles.Length >= 1)) + { + foreach (string matchedFile in matchedFiles) + { + if (Path.GetFileNameWithoutExtension(matchedFile.ToLowerInvariant()) == moduleName.ToLowerInvariant()) + { + try + { + MibDocument md = new MibDocument (matchedFile); + if (md.Modules.Count > 0) + { + return md.Modules [0]; + } + } catch + { + } + } + } + } + } + + return null; + } + + #endregion + + } + + // earlier code for search of versioned MIBs: + // + //private const string Pattern = "-V[0-9]+$"; + //public static bool AllDependentsAvailable(MibModule module, IDictionary modules) + //{ + // foreach (string dependent in module.Dependents) + // { + // if (!DependentFound(dependent, modules)) + // { + // return false; + // } + // } + + // return true; + //} + + //private static bool DependentFound(string dependent, IDictionary modules) + //{ + // if (!Regex.IsMatch(dependent, Pattern)) + // { + // return modules.ContainsKey(dependent); + // } + + // if (modules.ContainsKey(dependent)) + // { + // return true; + // } + + // string dependentNonVersion = Regex.Replace(dependent, Pattern, string.Empty); + // return modules.ContainsKey(dependentNonVersion); + //} + +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTree.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTree.cs new file mode 100644 index 0000000..54ec351 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTree.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using Lextm.SharpSnmpLib.Mib.Elements.Entities; + +namespace Lextm.SharpSnmpLib.Mib +{ + /// + /// Builds up a tree from a single MIB + /// + public class MibTree + { + private readonly List _root = new List(); + + public MibTree(MibModule module) + { + IList entities = module.Entities; + + if (entities.Count > 0) + { + // try to find module identity as root + foreach (IEntity element in entities) + { + ModuleIdentity mi = element as ModuleIdentity; + + if (mi != null) + { + _root.Add(new MibTreeNode(null, mi)); + } + } + + // gather all items below ModuleIdentity + foreach (MibTreeNode mibTreeNode in _root) + { + entities.Remove (mibTreeNode.Entity); + BuildTree(mibTreeNode, entities); + UpdateTreeNodeTypes(mibTreeNode); + } + + // find OID assignments as root, if there are any that are not below ModuleIdentity + // FIXME: There may be multiple OID assignments that create a tree (find the root ones!) + foreach (IEntity element in entities) + { + OidValueAssignment oa = element as OidValueAssignment; + + if (oa != null) + { + _root.Add(new MibTreeNode(null, oa)); + } + } + + if (_root.Count == 0) + { + //no module identity, assume first entity is root + _root.Add(new MibTreeNode(null, entities[0])); + } + + foreach (MibTreeNode mibTreeNode in _root) + { + if (entities.Contains (mibTreeNode.Entity)) + { + entities.Remove (mibTreeNode.Entity); + } + BuildTree(mibTreeNode, entities); + UpdateTreeNodeTypes(mibTreeNode); + } + } + } + + public IList Root + { + get { return _root; } + } + + + private void BuildTree(MibTreeNode node, IList entities) + { + int i = 0; + while (i < entities.Count) + { + if (entities[i].Parent == node.Entity.Name) + { + node.AddChild(entities[i]); + entities.RemoveAt(i); + } + else + { + i++; + } + } + + foreach (MibTreeNode childNode in node.ChildNodes) + { + BuildTree(childNode, entities); + } + } + + private void UpdateTreeNodeTypes(MibTreeNode node) + { + node.UpdateNodeType(); + foreach (MibTreeNode childNode in node.ChildNodes) + { + UpdateTreeNodeTypes(childNode); + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTreeNode.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTreeNode.cs new file mode 100644 index 0000000..40ae44d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTreeNode.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using Lextm.SharpSnmpLib.Mib.Elements.Entities; +using Lextm.SharpSnmpLib.Mib.Elements.Types; + +namespace Lextm.SharpSnmpLib.Mib +{ + [Flags] + public enum MibTreeNodeType + { + Unknown = 0, + Container = (1 << 0), + Scalar = (1 << 1), + Table = (1 << 2), + TableRow = (1 << 3), + TableCell = (1 << 4), + NotificationRelated = (1 << 5), + ConformanceRelated = (1 << 6) + } + + + public class MibTreeNode + { + private MibTreeNode _parent = null; + private List _childNodes = new List(); + private IEntity _entity = null; + private MibTreeNodeType _nodeType = MibTreeNodeType.Unknown; + + public MibTreeNode(MibTreeNode parent, IEntity entity) + { + _parent = parent; + _entity = entity; + } + + public MibTreeNode Parent + { + get { return _parent; } + } + + public IEntity Entity + { + get { return _entity; } + } + + public MibTreeNodeType NodeType + { + get { return _nodeType; } + } + + public List ChildNodes + { + get { return _childNodes; } + } + + public MibTreeNode AddChild(IEntity element) + { + MibTreeNode result = new MibTreeNode(this, element); + this.ChildNodes.Add(result); + + return result; + } + + public void UpdateNodeType() + { + _nodeType = MibTreeNodeType.Unknown; + + if (_entity != null) + { + if ((_entity is OidValueAssignment) || (_entity is ObjectIdentity)) + { + _nodeType |= MibTreeNodeType.Container; + return; + } + else if (_childNodes.Count > 0) + { + _nodeType |= MibTreeNodeType.Container; + } + + if (_entity is ObjectType) + { + ObjectType ot = _entity as ObjectType; + + if (ot.Syntax is SequenceOf) + { + _nodeType |= MibTreeNodeType.Table; + } + else if (ot.Syntax is Sequence) + { + _nodeType |= MibTreeNodeType.TableRow; + } + else if ((_parent != null) && ((_parent.NodeType & MibTreeNodeType.TableRow) != 0)) + { + _nodeType |= MibTreeNodeType.TableCell; + _nodeType |= MibTreeNodeType.Scalar; + } + else + { + _nodeType |= MibTreeNodeType.Scalar; + } + } + else if ((_entity is ModuleCompliance) || (_entity is ObjectGroup) || (_entity is NotificationGroup)) + { + _nodeType |= MibTreeNodeType.ConformanceRelated; + } + else if ((_entity is NotificationGroup) || (_entity is NotificationType)) + { + _nodeType |= MibTreeNodeType.NotificationRelated; + } + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTypesResolver.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTypesResolver.cs new file mode 100644 index 0000000..98dabc7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/MibTypesResolver.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Lextm.SharpSnmpLib.Mib.Elements; +using Lextm.SharpSnmpLib.Mib.Elements.Entities; +using Lextm.SharpSnmpLib.Mib.Elements.Types; + +namespace Lextm.SharpSnmpLib.Mib +{ + public static class MibTypesResolver + { + private static readonly Regex _namedOidPathRegex = new Regex(@"^(?[^\(]+)\((?\d+)\)$"); + private static readonly List _resolver = new List(); + private static readonly List _cachedModules = new List(); + + public static void RegisterResolver(IMibResolver resolver) + { + if (resolver != null) + { + _resolver.Add(resolver); + } + } + + public static IModule ResolveModule(string moduleName) + { + // check if module is already cached + foreach (MibModule cachedModule in _cachedModules) + { + if (cachedModule.Name == moduleName) + { + return cachedModule; + } + } + + foreach (IMibResolver resolver in _resolver) + { + IModule resolvedModule = resolver.Resolve(moduleName); + if (resolvedModule != null) + { + ResolveTypes(resolvedModule); + _cachedModules.Add(resolvedModule); + return resolvedModule; + } + } + + return null; + } + + public static void ResolveTypes(IModule module) + { + foreach (IEntity entity in module.Entities) + { + ITypeReferrer typeReferringEntity = entity as ITypeReferrer; + + if (typeReferringEntity != null) + { + CheckTypeReferrer(module, typeReferringEntity); + } + } + + if (!_cachedModules.Contains(module)) + { + _cachedModules.Add(module); + } + } + + private static void CheckTypeReferrer(IModule module, ITypeReferrer typeReferringEntity) + { + TypeAssignment unknownType = typeReferringEntity.ReferredType as TypeAssignment; + if (unknownType != null) + { + typeReferringEntity.ReferredType = ResolveType(module, unknownType); + + if (typeReferringEntity.ReferredType is TypeAssignment) + { + Console.WriteLine(String.Format("Could not resolve type '{0}' declared in module '{1}'", (typeReferringEntity.ReferredType as TypeAssignment).Type, typeReferringEntity.ReferredType.Module.Name)); + } + } + + ITypeReferrer nextTypeReferringEntity = typeReferringEntity.ReferredType as ITypeReferrer; + if (nextTypeReferringEntity != null) + { + CheckTypeReferrer(module, nextTypeReferringEntity); + } + } + + public static ITypeAssignment ResolveType(IModule module, TypeAssignment type) + { + ITypeAssignment result = ResolveDeclaration(module, type.Type) as ITypeAssignment; + + return (result != null) ? result : type; + } + + public static IDeclaration ResolveDeclaration(IModule module, string name) + { + if ((module == null) || String.IsNullOrEmpty(name)) + { + return null; + } + + // check module internal types + foreach (IDeclaration decl in module.Declarations) + { + if (decl.Name == name) + { + return decl; + } + } + + // check if type is imported + if (module.Imports != null) + { + ImportsFrom imports = module.Imports.GetImportFromType(name); + if (imports != null) + { + IModule importedModule = ResolveModule(imports.Module); + if (importedModule != null) + { + return ResolveDeclaration(importedModule, name); + } + } + } + + return null; + } + + public static ObjectIdentifier ResolveOid(IEntity entity) + { + ObjectIdentifier result = new ObjectIdentifier(); + + if (entity != null) + { + ResolveOid(entity, result); + } + + return result; + } + + private static void ResolveOid(IEntity entity, ObjectIdentifier result) + { + result.Prepend(entity.Name, entity.Value); + + // check parent + if (!String.IsNullOrEmpty(entity.Parent)) + { + string[] pathParts = entity.Parent.Split('.'); + uint value; + + // all parts except the first should have their value directly or indirectly with them + if (pathParts.Length > 1) + { + for (int i=pathParts.Length-1; i>=1; i--) + { + if (uint.TryParse(pathParts[i], out value)) + { + result.Prepend("", value); + } + else + { + Match m = _namedOidPathRegex.Match(pathParts[i]); + if (m.Success) + { + result.Prepend(m.Groups["Name"].Value, uint.Parse(m.Groups["Value"].Value)); + } + else + { + throw new MibException("Invalid OID path detected for entity '" + entity.Name + "' in module '" + entity.Module + "'!"); + } + } + } + } + + // parse root part: either another entity or a standard root object + if (IsOidRoot(pathParts[0], out value)) + { + result.Prepend(pathParts[0], value); + } + else + { + // try to find entity inside this module + if (entity.Module != null) + { + entity = ResolveDeclaration(entity.Module, pathParts[0]) as IEntity; + + if (entity != null) + { + ResolveOid(entity, result); + } + else + { + result.Prepend("", uint.MaxValue); + } + } + else + { + result.Prepend("", uint.MaxValue); + } + } + } + } + + public static bool IsOidRoot(string name, out uint value) + { + value = uint.MaxValue; + + switch (name) + { + case "ccitt": value = 0; return true; + case "iso": value = 1; return true; + case "joint-iso-ccitt": value = 2; return true; + } + + return false; + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ObjectIdentifier.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ObjectIdentifier.cs new file mode 100644 index 0000000..f5716b3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ObjectIdentifier.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Text; + +namespace Lextm.SharpSnmpLib.Mib +{ + public class ObjectIdentifier: List> + { + public void Add(string name, uint oid) + { + this.Add(new KeyValuePair(name, oid)); + } + + public void Prepend(string name, uint oid) + { + this.Insert(0, new KeyValuePair(name, oid)); + } + + public void Insert(int index, string name, uint oid) + { + this.Insert(index, new KeyValuePair(name, oid)); + } + + public string GetOidString() + { + StringBuilder result = new StringBuilder(); + + foreach (KeyValuePair level in this) + { + result.Append(level.Value); + result.Append('.'); + } + + if (result.Length > 0) + { + result.Length--; + } + + return result.ToString(); + } + + public uint[] GetOidValues() + { + List result = new List(); + + foreach (KeyValuePair level in this) + { + result.Add(level.Value); + } + + return result.ToArray(); + } + + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Status.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Status.cs new file mode 100644 index 0000000..9f0519c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Status.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Lextm.SharpSnmpLib.Mib +{ + public enum Status + { + current, + deprecated, + obsolete, + mandatory, + optional + } + +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Symbol.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Symbol.cs new file mode 100644 index 0000000..7d46766 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/Symbol.cs @@ -0,0 +1,357 @@ +/* + * Created by SharpDevelop. + * User: lextm + * Date: 2008/5/17 + * Time: 17:14 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +using System; +using System.Configuration; +using System.Globalization; +using System.Text; + +namespace Lextm.SharpSnmpLib.Mib +{ + /// + /// Description of Symbol. + /// + public sealed class Symbol : IEquatable + { + private readonly string _text; + private readonly int _row; + private readonly int _column; + private readonly string _file; + + private Symbol(string text) : this(null, text, -1, -1) + { + } + + /// + /// Creates a . + /// + /// File + /// Text + /// Row number + /// column number + public Symbol(string file, string text, int row, int column) + { + _file = file; + _text = text; + _row = row; + _column = column; + } + + /// + /// File. + /// + public string File + { + get + { + return _file; + } + } + + /// + /// Row number. + /// + public int Row + { + get + { + return _row; + } + } + + /// + /// Column number. + /// + public int Column + { + get + { + return _column; + } + } + + /// + /// Returns a that represents this . + /// + /// + public override string ToString() + { + return _text; + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + /// + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return GetType() == obj.GetType() && Equals((Symbol)obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// A hash code for the current . + public override int GetHashCode() + { + return _text.GetHashCode(); + } + + /// + /// The equality operator. + /// + /// Left object + /// Right object + /// + /// Returns true if the values of its operands are equal, false otherwise. + public static bool operator ==(Symbol left, Symbol right) + { + return Equals(left, right); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// Left object + /// Right object + /// + /// Returns true if the values of its operands are equal, false otherwise. + public static bool Equals(Symbol left, Symbol right) + { + object l = left; + object r = right; + if (l == r) + { + return true; + } + + if (l == null || r == null) + { + return false; + } + + return left._text.Equals(right._text); + } + + /// + /// The inequality operator. + /// + /// Left object + /// Right object + /// + /// Returns true if the values of its operands are not equal, false otherwise. + public static bool operator !=(Symbol left, Symbol right) + { + return !(left == right); + } + + #region IEquatable Members + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. + /// + public bool Equals(Symbol other) + { + return Equals(this, other); + } + + #endregion + + public static readonly Symbol Definitions = new Symbol("DEFINITIONS"); + public static readonly Symbol Begin = new Symbol("BEGIN"); + public static readonly Symbol Object = new Symbol("OBJECT"); + public static readonly Symbol Identifier = new Symbol("IDENTIFIER"); + public static readonly Symbol Assign = new Symbol("::="); + public static readonly Symbol OpenBracket = new Symbol("{"); + public static readonly Symbol CloseBracket = new Symbol("}"); + public static readonly Symbol Comment = new Symbol("--"); + public static readonly Symbol Imports = new Symbol("IMPORTS"); + public static readonly Symbol Semicolon = new Symbol(";"); + public static readonly Symbol From = new Symbol("FROM"); + public static readonly Symbol ModuleIdentity = new Symbol("MODULE-IDENTITY"); + public static readonly Symbol ObjectType = new Symbol("OBJECT-TYPE"); + public static readonly Symbol ObjectGroup = new Symbol("OBJECT-GROUP"); + public static readonly Symbol NotificationGroup = new Symbol("NOTIFICATION-GROUP"); + public static readonly Symbol ModuleCompliance = new Symbol("MODULE-COMPLIANCE"); + public static readonly Symbol Sequence = new Symbol("SEQUENCE"); + public static readonly Symbol NotificationType = new Symbol("NOTIFICATION-TYPE"); + public static readonly Symbol EOL = new Symbol(Environment.NewLine); + public static readonly Symbol ObjectIdentity = new Symbol("OBJECT-IDENTITY"); + public static readonly Symbol End = new Symbol("END"); + public static readonly Symbol Macro = new Symbol("MACRO"); + public static readonly Symbol Choice = new Symbol("CHOICE"); + public static readonly Symbol TrapType = new Symbol("TRAP-TYPE"); + public static readonly Symbol AgentCapabilities = new Symbol("AGENT-CAPABILITIES"); + public static readonly Symbol Comma = new Symbol(","); + public static readonly Symbol TextualConvention = new Symbol("TEXTUAL-CONVENTION"); + public static readonly Symbol Syntax = new Symbol("SYNTAX"); + public static readonly Symbol Bits = new Symbol("BITS"); + public static readonly Symbol Octet = new Symbol("OCTET"); + public static readonly Symbol String = new Symbol("STRING"); + public static readonly Symbol OpenParentheses = new Symbol("("); + public static readonly Symbol CloseParentheses = new Symbol(")"); + public static readonly Symbol Exports = new Symbol("EXPORTS"); + public static readonly Symbol DisplayHint = new Symbol("DISPLAY-HINT"); + public static readonly Symbol Status = new Symbol("STATUS"); + public static readonly Symbol Description = new Symbol("DESCRIPTION"); + public static readonly Symbol Reference = new Symbol("REFERENCE"); + public static readonly Symbol DoubleDot = new Symbol(".."); + public static readonly Symbol Pipe = new Symbol("|"); + public static readonly Symbol Size = new Symbol("SIZE"); + public static readonly Symbol Units = new Symbol("UNITS"); + public static readonly Symbol MaxAccess = new Symbol("MAX-ACCESS"); + public static readonly Symbol Access = new Symbol("ACCESS"); + public static readonly Symbol Index = new Symbol("INDEX"); + public static readonly Symbol Augments = new Symbol("AUGMENTS"); + public static readonly Symbol DefVal = new Symbol("DEFVAL"); + public static readonly Symbol Of = new Symbol("OF"); + public static readonly Symbol Integer = new Symbol("INTEGER"); + public static readonly Symbol Integer32 = new Symbol("Integer32"); + public static readonly Symbol IpAddress = new Symbol("IpAddress"); + public static readonly Symbol Counter32 = new Symbol("Counter32"); + public static readonly Symbol Counter = new Symbol("Counter"); + public static readonly Symbol TimeTicks = new Symbol("TimeTicks"); + public static readonly Symbol Opaque = new Symbol("Opaque"); + public static readonly Symbol Counter64 = new Symbol("Counter64"); + public static readonly Symbol Unsigned32 = new Symbol("Unsigned32"); + public static readonly Symbol Gauge32 = new Symbol("Gauge32"); + public static readonly Symbol Gauge = new Symbol("Gauge"); + public static readonly Symbol TruthValue = new Symbol("TruthValue"); + public static readonly Symbol Implied = new Symbol("IMPLIED"); + + internal void Expect(Symbol expected, params Symbol[] orExpected) + { + bool isExpected = (this == expected); + + if (!isExpected && (orExpected != null) && (orExpected.Length > 0)) + { + // check the alternatives + for (int i=0; i 64)) + { + message = "an identifier must consist of 1 to 64 letters, digits, and hyphens"; + return false; + } + + if (!Char.IsLetter(name[0])) + { + message = "the initial character must be a letter"; + return false; + } + + if (name.EndsWith("-", StringComparison.Ordinal)) + { + message = "a hyphen cannot be the last character of an identifier"; + return false; + } + + if (name.Contains("--")) + { + message = "a hyphen cannot be immediately followed by another hyphen in an identifier"; + return false; + } + + if (UseStricterValidation && name.Contains("_")) + { + message = "underscores are not allowed in identifiers"; + return false; + } + + // TODO: SMIv2 forbids "-" except in module names and keywords + message = null; + return true; + } + + private static bool? _useStricterValidation; + + private static bool UseStricterValidation + { + get + { + if (_useStricterValidation == null) + { + object setting = ConfigurationManager.AppSettings["StricterValidationEnabled"]; + _useStricterValidation = setting != null && Convert.ToBoolean(setting.ToString(), CultureInfo.InvariantCulture); + } + + return _useStricterValidation.Value; + } + } + } +} \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/SymbolList.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/SymbolList.cs new file mode 100644 index 0000000..6dcbb0c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/SymbolList.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Lextm.SharpSnmpLib.Mib +{ + public class SymbolList : List + { + public class SymbolEnumerator : ISymbolEnumerator + { + private SymbolList _list = null; + private int _index = -1; + + internal SymbolEnumerator(SymbolList list) + { + if (list == null) + { + throw new ArgumentNullException("lexer"); + } + + _list = list; + } + + #region ISymbolEnumerator Member + + public bool PutBack(Symbol item) + { + if ((_index < 0) || (_index >= _list.Count) || (item != _list[_index])) + { + throw new ArgumentException(@"wrong last symbol", "last"); + //return false; + } + + _index--; + return true; + } + + #endregion + + #region IEnumerator Member + + public Symbol Current + { + get + { + if ((_index >= 0) && (_index <= _list.Count)) + { + return _list[_index]; + } + + return null; + } + } + + #endregion + + #region IDisposable Member + + public void Dispose() + { + } + + #endregion + + #region IEnumerator Member + + object System.Collections.IEnumerator.Current + { + get { return this.Current; } + } + + public bool MoveNext() + { + _index++; + return (_index >= 0) && (_index < _list.Count); + } + + public void Reset() + { + _index = -1; + } + + #endregion + } + + /// + /// Initializes a new instance of the class. + /// + public SymbolList() + { + } + + public ISymbolEnumerator GetSymbolEnumerator() + { + return new SymbolEnumerator(this); + } + + public string Join(string separator) + { + if (separator == null) + separator = ""; + + StringBuilder result = new StringBuilder(); + + foreach (Symbol s in this) + { + result.Append(s); + result.Append(separator); + } + + if (result.Length > 0) + { + result.Length -= separator.Length; + } + + return result.ToString(); + } + } + + public static class SymbolEnumeratorExtension + { + public static Symbol NextSymbol(this IEnumerator enumerator) + { + if (enumerator.MoveNext()) + { + return enumerator.Current; + } + + return null; + } + + public static Symbol NextNonEOLSymbol(this IEnumerator enumerator) + { + while (enumerator.MoveNext()) + { + if (enumerator.Current != Symbol.EOL) + { + return enumerator.Current; + } + } + + return null; + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ValueMap.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ValueMap.cs new file mode 100644 index 0000000..7ccdcd3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ValueMap.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib +{ + public class ValueMap : Dictionary + { + public ValueMap() + { + } + + /// + /// Returns the values of the map as continous range. At best as one range. + /// + /// + public ValueRanges GetContinousRanges() + { + ValueRanges result = new ValueRanges(); + + if (this.Count > 0) + { + List values = new List(this.Keys); + values.Sort(); + + Int64 last = values[0]; + Int64 offset = values[0]; + for (int i=1; i + /// Gets the highest value contained in this value map. + /// + /// + public Int64 GetHighestValue() + { + Int64 result = 0; + + foreach (Int64 value in this.Keys) + { + if (value > result) + { + result = value; + } + } + + return result; + } + + /// + /// Interprets the single values as bit positions and creates a mask of it. + /// + /// + public UInt32 GetBitMask() + { + UInt32 result = 0; + + foreach (Int64 key in this.Keys) + { + if (key < 0) + { + throw new NotSupportedException("Negative numbers are not allowed for Bits!"); + } + if (key > 31) + { + throw new NotSupportedException("Bits with more than 32 bits are not supported!"); + } + + result |= (UInt32)(1 << (int)key); + } + + return result; + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ValueRange.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ValueRange.cs new file mode 100644 index 0000000..9583399 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Mib/ValueRange.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; + +namespace Lextm.SharpSnmpLib.Mib +{ + public class ValueRanges: List + { + public bool IsSizeDeclaration { get; internal set; } + + public ValueRanges(bool isSizeDecl = false) + { + IsSizeDeclaration = isSizeDecl; + } + + public bool Contains(Int64 value) + { + foreach (ValueRange range in this) + { + if (range.Contains(value)) + { + return true; + } + } + + return false; + } + } + + public class ValueRange + { + private readonly Int64 _start; + private readonly Int64? _end; + + public ValueRange(Int64 first, Int64? second) + { + _start = first; + _end = second; + } + + public Int64 Start + { + get { return _start; } + } + + public Int64? End + { + get { return _end; } + } + + public bool IntersectsWith(ValueRange other) + { + if (this._end == null) + { + return other.Contains(this._start); + } + else if (other._end == null) + { + return this.Contains(other._start); + } + + return (this._start <= other.End) && (this._end >= other._start); + } + + public bool Contains(Int64 value) + { + if (_end == null) + { + return value == _start; + } + else + { + return (_start <= value) && (value <= _end); + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/AssemblyInfo.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6d37a12 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/AssemblyInfo.cs @@ -0,0 +1,61 @@ +// #SNMP Library. An open source SNMP implementation for .NET. +// Copyright (C) 2008 Lex Y. Li +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#region Using directives + +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SharpSnmpLib")] +[assembly: AssemblyDescription("#SNMP Library for .NET")] +[assembly: AssemblyConfiguration("Lesser GPL 2.1+")] +[assembly: AssemblyCompany("LeXtudio")] +[assembly: AssemblyProduct("#SNMPLib")] +[assembly: AssemblyCopyright("(C) 2008-2010 Malcolm Crowe, Lex Li, Steve Santacroce, and other contributors.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose index type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("7.0.011207.31")] +#if (!CF) +[assembly: AssemblyFileVersion("7.0.011207.31")] +#endif +[assembly: NeutralResourcesLanguage("en-US")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lextm")] + +[assembly: InternalsVisibleTo("SharpSnmpLib.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/Resources.Designer.cs b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/Resources.Designer.cs new file mode 100644 index 0000000..3135dd3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.225 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace Lextm.SharpSnmpLib.Mib { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Lextm.SharpSnmpLib.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/Resources.resx b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/Resources.resx new file mode 100644 index 0000000..5ea0895 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/Properties/Resources.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/SharpSnmpLib.Mib.csproj b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/SharpSnmpLib.Mib.csproj new file mode 100644 index 0000000..fc6f726 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/SharpSnmpLib.Mib.csproj @@ -0,0 +1,139 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {CBE20411-5DB7-487D-825D-7694267BB6F5} + Library + Properties + Lextm.SharpSnmpLib + SharpSnmpLib.Mib + ..\bin\SharpSnmpLib.Mib.xml + 512 + + True + sharpsnmplib.snk + False + File + + + + + + + + + False + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Lextm.SharpSnmpLib.Mib + Designer + + + + + + + + \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/license.txt b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/license.txt new file mode 100644 index 0000000..9efd350 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/license.txt @@ -0,0 +1,458 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/sharpsnmplib.snk b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/sharpsnmplib.snk new file mode 100644 index 0000000000000000000000000000000000000000..fe6f345af4820b6758f6909a2d489ac524eaf889 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098^0|he0B_x?RL7>o-AT$p+6ydg1bA=n+ z(TEjM3jFn-l#LX?9O>S*pzolg8Y)VYs$ys(8qZ|eZM(eWal@XvaKlm#iy_MFa zktG6@Uk-Mk8478nIotb|p1Rj6Z+5X3-=(x)I^Xr$16a|_jT}#2q z4>os95Q>xdhUx6jI|K!RX~hw1f!(|e#!5;$P6^h;CwzlPPCzNSv5=EJ`&INw3Z=7z zpCl>Gkp_CX1_JoM)uqwp7RO;18$@HiTuXhh8G#I(lCQ>caMi|5DbsK=Ij`BFCHTSV z)k^SjW~pvk&&IH|pd-nVVYBt0_=U$ArZ<6G8WE6Fm+B494}Ss(+%Lc_4Ia8yP8XWx z#8OMKTbAeI*{B6YjJi>DVo}}N8VwM^Q}Dt#D!-QYa4P@XO=Y9CSTeQ4?wa literal 0 HcmV?d00001 diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/example/compile_udp_mib.sh b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/example/compile_udp_mib.sh new file mode 100644 index 0000000..4cfb5ca --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/example/compile_udp_mib.sh @@ -0,0 +1 @@ +../LwipMibCompiler/bin/Debug/LwipMibCompiler.exe ../Mibs/UDP-MIB ./ ../Mibs/ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/README b/sdk/Src/Network/lwip2.0.3/apps/snmp/README new file mode 100644 index 0000000..1f9db09 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/README @@ -0,0 +1,38 @@ +lwIP SNMPv2c agent +================== + +Based on SNMP stack written by Christiaan Simons + +Rewritten by Martin Hentschel and +Dirk Ziegelmeier + +Features: + - SNMPv2c support. + - Low RAM usage - no memory pools, stack only. + - MIB2 implementation is separated from SNMP stack. + - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB. + - Simple and generic API for MIB implementation. + - Comfortable node types and helper functions for scalar arrays and tables. + - Counter64, bit and truthvalue datatype support. + - Callbacks for SNMP writes e.g. to implement persistency. + - Runs on two APIs: RAW and netconn. + - Async API is gone - the stack now supports netconn API instead, + so blocking operations can be done in MIB calls. + SNMP runs in a worker thread when netconn API is used. + - Simplified thread sync support for MIBs - useful when MIBs + need to access variables shared with other threads where no locking is + possible. Used in MIB2 to access lwIP stats from lwIP thread. + +MIB compiler (code generator): + - Written in C#. MIB viewer used Windows Forms. + - Developed on Windows with Visual Studio 2010. + - Can be compiled and used under Linux with http://www.monodevelop.com/. + - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4) + (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate). + - MIB parser, C file generation framework and LWIP code generation are cleanly + separated, which means the code may be useful as a base for code generation + of other SNMP agents. + +Notes: + - Stack and MIB compiler were used to implement a Profinet device. + Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB. diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_asn1.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_asn1.c new file mode 100644 index 0000000..f35b760 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_asn1.c @@ -0,0 +1,749 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + * Martin Hentschel + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "snmp_asn1.h" + +#define PBUF_OP_EXEC(code) \ + if ((code) != ERR_OK) { \ + return ERR_BUF; \ + } + +/** + * Encodes a TLV into a pbuf stream. + * + * @param pbuf_stream points to a pbuf stream + * @param tlv TLV to encode + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv) +{ + u8_t data; + u8_t length_bytes_required; + + /* write type */ + if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) { + /* extended format is not used by SNMP so we do not accept those values */ + return ERR_ARG; + } + if (tlv->type_len != 0) { + /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */ + return ERR_ARG; + } + + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type)); + tlv->type_len = 1; + + /* write length */ + if (tlv->value_len <= 127) { + length_bytes_required = 1; + } else if (tlv->value_len <= 255) { + length_bytes_required = 2; + } else { + length_bytes_required = 3; + } + + /* check for forced min length */ + if (tlv->length_len > 0) { + if (tlv->length_len < length_bytes_required) { + /* unable to code requested length in requested number of bytes */ + return ERR_ARG; + } + + length_bytes_required = tlv->length_len; + } else { + tlv->length_len = length_bytes_required; + } + + if (length_bytes_required > 1) { + /* multi byte representation required */ + length_bytes_required--; + data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */ + + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); + + while (length_bytes_required > 1) { + if (length_bytes_required == 2) { + /* append high byte */ + data = (u8_t)(tlv->value_len >> 8); + } else { + /* append leading 0x00 */ + data = 0x00; + } + + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); + length_bytes_required--; + } + } + + /* append low byte */ + data = (u8_t)(tlv->value_len & 0xFF); + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); + + return ERR_OK; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len) +{ + PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len)); + + return ERR_OK; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value) +{ + if (octets_needed > 5) { + return ERR_ARG; + } + if (octets_needed == 5) { + /* not enough bits in 'value' add leading 0x00 */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00)); + octets_needed--; + } + + while (octets_needed > 1) { + octets_needed--; + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); + } + + /* (only) one least significant octet */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value)); + + return ERR_OK; +} + +/** + * Encodes u64_t (counter64) into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u64t_cnt() + */ +err_t +snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value) +{ + if (octets_needed > 9) { + return ERR_ARG; + } + if (octets_needed == 9) { + /* not enough bits in 'value' add leading 0x00 */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00)); + octets_needed--; + } + + while (octets_needed > 4) { + octets_needed--; + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3)))); + } + + /* skip to low u32 */ + value++; + + while (octets_needed > 1) { + octets_needed--; + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3)))); + } + + /* always write at least one octet (also in case of value == 0) */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value))); + + return ERR_OK; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value) +{ + while (octets_needed > 1) { + octets_needed--; + + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); + } + + /* (only) one least significant octet */ + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value)); + + return ERR_OK; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param pbuf_stream points to a pbuf stream + * @param oid points to object identifier array + * @param oid_len object identifier array length + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len) +{ + if (oid_len > 1) { + /* write compressed first two sub id's */ + u32_t compressed_byte = ((oid[0] * 40) + oid[1]); + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte)); + oid_len -= 2; + oid += 2; + } else { + /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + + while (oid_len > 0) { + u32_t sub_id; + u8_t shift, tail; + + oid_len--; + sub_id = *oid; + tail = 0; + shift = 28; + while (shift > 0) { + u8_t code; + + code = (u8_t)(sub_id >> shift); + if ((code != 0) || (tail != 0)) { + tail = 1; + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80)); + } + shift -= 7; + } + PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F)); + + /* proceed to next sub-identifier */ + oid++; + } + return ERR_OK; +} + +/** + * Returns octet count for length. + * + * @param length parameter length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) { + *octets_needed = 1; + } else if (length < 0x100U) { + *octets_needed = 2; + } else { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value value to be encoded + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) { + *octets_needed = 1; + } else if (value < 0x8000UL) { + *octets_needed = 2; + } else if (value < 0x800000UL) { + *octets_needed = 3; + } else if (value < 0x80000000UL) { + *octets_needed = 4; + } else { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an u64_t. + * + * @param value value to be encoded + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed) +{ + /* check if high u32 is 0 */ + if (*value == 0x00) { + /* only low u32 is important */ + value++; + snmp_asn1_enc_u32t_cnt(*value, octets_needed); + } else { + /* low u32 does not matter for length determination */ + snmp_asn1_enc_u32t_cnt(*value, octets_needed); + *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */ + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value value to be encoded + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) { + value = ~value; + } + if (value < 0x80L) { + *octets_needed = 1; + } else if (value < 0x8000L) { + *octets_needed = 2; + } else if (value < 0x800000L) { + *octets_needed = 3; + } else { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param oid points to object identifier array + * @param oid_len object identifier array length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed) +{ + u32_t sub_id; + + *octets_needed = 0; + if (oid_len > 1) { + /* compressed prefix in one octet */ + (*octets_needed)++; + oid_len -= 2; + oid += 2; + } + while (oid_len > 0) { + oid_len--; + sub_id = *oid; + + sub_id >>= 7; + (*octets_needed)++; + while (sub_id > 0) { + sub_id >>= 7; + (*octets_needed)++; + } + oid++; + } +} + +/** + * Decodes a TLV from a pbuf stream. + * + * @param pbuf_stream points to a pbuf stream + * @param tlv returns decoded TLV + * @return ERR_OK if successful, ERR_VAL if we can't decode + */ +err_t +snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv) +{ + u8_t data; + + /* decode type first */ + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + tlv->type = data; + + if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) { + /* extended format is not used by SNMP so we do not accept those values */ + return ERR_VAL; + } + tlv->type_len = 1; + + /* now, decode length */ + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + + if (data < 0x80) { /* short form */ + tlv->length_len = 1; + tlv->value_len = data; + } else if (data > 0x80) { /* long form */ + u8_t length_bytes = data - 0x80; + tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */ + tlv->value_len = 0; + + while (length_bytes > 0) { + /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */ + if (tlv->value_len > 0xFF) { + return ERR_VAL; + } + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + tlv->value_len <<= 8; + tlv->value_len |= data; + + /* take care for special value used for indefinite length */ + if (tlv->value_len == 0xFFFF) { + return ERR_VAL; + } + + length_bytes--; + } + } else { /* data == 0x80 indefinite length form */ + /* (not allowed for SNMP; RFC 1157, 3.2.2) */ + return ERR_VAL; + } + + return ERR_OK; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value) +{ + u8_t data; + + if ((len > 0) && (len <= 5)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + + /* expecting sign bit to be zero, only unsigned please! */ + if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) { + *value = data; + len--; + + while (len > 0) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + *value <<= 8; + *value |= data; + } + + return ERR_OK; + } + } + + return ERR_VAL; +} + +/** + * Decodes large positive integer (counter64) into 2x u32_t. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value) +{ + u8_t data; + + if (len <= 4) { + /* high u32 is 0 */ + *value = 0; + /* directly skip to low u32 */ + value++; + } + + if ((len > 0) && (len <= 9)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + + /* expecting sign bit to be zero, only unsigned please! */ + if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) { + *value = data; + len--; + + while (len > 0) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + + if (len == 4) { + /* skip to low u32 */ + value++; + *value = 0; + } else { + *value <<= 8; + } + + *value |= data; + len--; + } + + return ERR_OK; + } + } + + return ERR_VAL; +} + +/** + * Decodes integer into s32_t. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + u8_t data; + + if ((len > 0) && (len < 5)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + if (data & 0x80) { + /* negative, start from -1 */ + *value = -1; + sign = 1; + *lsb_ptr &= data; + } else { + /* positive, start from 0 */ + *value = 0; + sign = 0; + *lsb_ptr |= data; + } + + /* OR/AND octets with value */ + while (len > 0) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + +#if BYTE_ORDER == LITTLE_ENDIAN + *value <<= 8; +#endif +#if BYTE_ORDER == BIG_ENDIAN + *value >>= 8; +#endif + + if (sign) { + *lsb_ptr |= 255; + *lsb_ptr &= data; + } else { + *lsb_ptr |= data; + } + } + + return ERR_OK; + } + + return ERR_VAL; +} + +/** + * Decodes object identifier from incoming message into array of u32_t. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded object identifier + * @param oid return decoded object identifier + * @param oid_len return decoded object identifier length + * @param oid_max_len size of oid buffer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len) +{ + u32_t *oid_ptr; + u8_t data; + + *oid_len = 0; + oid_ptr = oid; + if (len > 0) { + if (oid_max_len < 2) { + return ERR_MEM; + } + + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + /* first compressed octet */ + if (data == 0x2B) { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } else if (data < 40) { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = data; + oid_ptr++; + } else if (data < 80) { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = data - 40; + oid_ptr++; + } else { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = data - 80; + oid_ptr++; + } + *oid_len = 2; + } else { + /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */ + return ERR_OK; + } + + while ((len > 0) && (*oid_len < oid_max_len)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + if ((data & 0x80) == 0x00) { + /* sub-identifier uses single octet */ + *oid_ptr = data; + } else { + /* sub-identifier uses multiple octets */ + u32_t sub_id = (data & ~0x80); + while ((len > 0) && ((data & 0x80) != 0)) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); + len--; + + sub_id = (sub_id << 7) + (data & ~0x80); + } + + if ((data & 0x80) != 0) { + /* "more bytes following" bit still set at end of len */ + return ERR_VAL; + } + *oid_ptr = sub_id; + } + oid_ptr++; + (*oid_len)++; + } + + if (len > 0) { + /* OID to long to fit in our buffer */ + return ERR_MEM; + } + + return ERR_OK; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param pbuf_stream points to a pbuf stream + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param buf return raw bytes + * @param buf_len returns length of the raw return value + * @param buf_max_len buffer size + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len) +{ + if (len > buf_max_len) { + /* not enough dst space */ + return ERR_MEM; + } + *buf_len = len; + + while (len > 0) { + PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf)); + buf++; + len--; + } + + return ERR_OK; +} + +#endif /* LWIP_SNMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_asn1.h b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_asn1.h new file mode 100644 index 0000000..ec50d8c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_asn1.h @@ -0,0 +1,108 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) codec. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + * Martin Hentschel + * Elias Oenal + */ + +#ifndef LWIP_HDR_APPS_SNMP_ASN1_H +#define LWIP_HDR_APPS_SNMP_ASN1_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP + +#include "lwip/err.h" +#include "lwip/apps/snmp_core.h" +#include "snmp_pbuf_stream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80 + +#define SNMP_ASN1_CLASS_MASK 0xC0 +#define SNMP_ASN1_CONTENTTYPE_MASK 0x20 +#define SNMP_ASN1_DATATYPE_MASK 0x1F +#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */ + +/* context specific (SNMP) tags (from SNMP spec. RFC1157) */ +#define SNMP_ASN1_CONTEXT_PDU_GET_REQ 0 +#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1 +#define SNMP_ASN1_CONTEXT_PDU_GET_RESP 2 +#define SNMP_ASN1_CONTEXT_PDU_SET_REQ 3 +#define SNMP_ASN1_CONTEXT_PDU_TRAP 4 +#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5 + +#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT 0 +#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW 2 + +struct snmp_asn1_tlv +{ + u8_t type; /* only U8 because extended types are not specified by SNMP */ + u8_t type_len; /* encoded length of 'type' field (normally 1) */ + u8_t length_len; /* indicates how many bytes are required to encode the 'value_len' field */ + u16_t value_len; /* encoded length of the value */ +}; +#define SNMP_ASN1_TLV_HDR_LENGTH(tlv) ((tlv).type_len + (tlv).length_len) +#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len) +#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0); + +err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv); +err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value); +err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value); +err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value); +err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len); +err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len); + +err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv); + +void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed); +void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed); +void snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed); +void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed); +void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed); +err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len); +err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value); +err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value); +err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value); +err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_core.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_core.c new file mode 100644 index 0000000..c041833 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_core.c @@ -0,0 +1,1349 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + * Martin Hentschel +*/ + +/** + * @defgroup snmp SNMPv2c agent + * @ingroup apps + * SNMPv2c compatible agent\n + * There is also a MIB compiler and a MIB viewer in lwIP contrib repository + * (lwip-contrib/apps/LwipMibCompiler).\n + * The agent implements the most important MIB2 MIBs including IPv6 support + * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version + * whithout IPv6 statistics (TODO).\n + * Rewritten by Martin Hentschel and + * Dirk Ziegelmeier \n + * Work on SNMPv3 has started, but is not finished.\n + * + * 0 Agent Capabilities + * ==================== + * + * Features: + * --------- + * - SNMPv2c support. + * - Low RAM usage - no memory pools, stack only. + * - MIB2 implementation is separated from SNMP stack. + * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB. + * - Simple and generic API for MIB implementation. + * - Comfortable node types and helper functions for scalar arrays and tables. + * - Counter64, bit and truthvalue datatype support. + * - Callbacks for SNMP writes e.g. to implement persistency. + * - Runs on two APIs: RAW and netconn. + * - Async API is gone - the stack now supports netconn API instead, + * so blocking operations can be done in MIB calls. + * SNMP runs in a worker thread when netconn API is used. + * - Simplified thread sync support for MIBs - useful when MIBs + * need to access variables shared with other threads where no locking is + * possible. Used in MIB2 to access lwIP stats from lwIP thread. + * + * MIB compiler (code generator): + * ------------------------------ + * - Provided in lwIP contrib repository. + * - Written in C#. MIB viewer used Windows Forms. + * - Developed on Windows with Visual Studio 2010. + * - Can be compiled and used on all platforms with http://www.monodevelop.com/. + * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4) + * (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate). + * - MIB parser, C file generation framework and LWIP code generation are cleanly + * separated, which means the code may be useful as a base for code generation + * of other SNMP agents. + * + * Notes: + * ------ + * - Stack and MIB compiler were used to implement a Profinet device. + * Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB. + * + * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416 + * ------------------------------------------- + * Note the S in SNMP stands for "Simple". Note that "Simple" is + * relative. SNMP is simple compared to the complex ISO network + * management protocols CMIP (Common Management Information Protocol) + * and CMOT (CMip Over Tcp). + * + * MIB II + * ------ + * The standard lwIP stack management information base. + * This is a required MIB, so this is always enabled. + * The groups EGP, CMOT and transmission are disabled by default. + * + * Most mib-2 objects are not writable except: + * sysName, sysLocation, sysContact, snmpEnableAuthenTraps. + * Writing to or changing the ARP and IP address and route + * tables is not possible. + * + * Note lwIP has a very limited notion of IP routing. It currently + * doen't have a route table and doesn't have a notion of the U,G,H flags. + * Instead lwIP uses the interface list with only one default interface + * acting as a single gateway interface (G) for the default route. + * + * The agent returns a "virtual table" with the default route 0.0.0.0 + * for the default interface and network routes (no H) for each + * network interface in the netif_list. + * All routes are considered to be up (U). + * + * Loading additional MIBs + * ----------------------- + * MIBs can only be added in compile-time, not in run-time. + * + * + * 1 Building the Agent + * ==================== + * First of all you'll need to add the following define + * to your local lwipopts.h: + * \#define LWIP_SNMP 1 + * + * and add the source files your makefile. + * + * Note you'll might need to adapt you network driver to update + * the mib2 variables for your interface. + * + * 2 Running the Agent + * =================== + * The following function calls must be made in your program to + * actually get the SNMP agent running. + * + * Before starting the agent you should supply pointers + * for sysContact, sysLocation, and snmpEnableAuthenTraps. + * You can do this by calling + * + * - snmp_mib2_set_syscontact() + * - snmp_mib2_set_syslocation() + * - snmp_set_auth_traps_enabled() + * + * You can register a callback which is called on successful write access: + * snmp_set_write_callback(). + * + * Additionally you may want to set + * + * - snmp_mib2_set_sysdescr() + * - snmp_set_device_enterprise_oid() + * - snmp_mib2_set_sysname() + * + * Also before starting the agent you need to setup + * one or more trap destinations using these calls: + * + * - snmp_trap_dst_enable() + * - snmp_trap_dst_ip_set() + * + * If you need more than MIB2, set the MIBs you want to use + * by snmp_set_mibs(). + * + * Finally, enable the agent by calling snmp_init() + * + * @defgroup snmp_core Core + * @ingroup snmp + * + * @defgroup snmp_traps Traps + * @ingroup snmp + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "snmp_core_priv.h" +#include "lwip/netif.h" +#include + + +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif + +struct snmp_statistics snmp_stats; +static const struct snmp_obj_id snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID}; +static const struct snmp_obj_id* snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default; + +const u32_t snmp_zero_dot_zero_values[] = { 0, 0 }; +const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values }; + + +#if SNMP_LWIP_MIB2 +#include "lwip/apps/snmp_mib2.h" +static const struct snmp_mib* const default_mibs[] = { &mib2 }; +static u8_t snmp_num_mibs = 1; +#else +static const struct snmp_mib* const default_mibs[] = { NULL }; +static u8_t snmp_num_mibs = 0; +#endif + +/* List of known mibs */ +static struct snmp_mib const * const *snmp_mibs = default_mibs; + +/** + * @ingroup snmp_core + * Sets the MIBs to use. + * Example: call snmp_set_mibs() as follows: + * static const struct snmp_mib *my_snmp_mibs[] = { + * &mib2, + * &private_mib + * }; + * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs)); + */ +void +snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs) +{ + LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL)); + LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0)); + snmp_mibs = mibs; + snmp_num_mibs = num_mibs; +} + +/** + * @ingroup snmp_core + * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device) + * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used). + * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor + * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It + * is not allowed to use LWIP enterprise ID! + * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own + * enterprise oid. + * e.g. + * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a) + * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b) + * for more details see description of 'sysObjectID' field in RFC1213-MIB + */ +void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid) +{ + if (device_enterprise_oid == NULL) { + snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default; + } else { + snmp_device_enterprise_oid = device_enterprise_oid; + } +} + +/** + * @ingroup snmp_core + * Get 'device enterprise oid' + */ +const struct snmp_obj_id* snmp_get_device_enterprise_oid(void) +{ + return snmp_device_enterprise_oid; +} + +#if LWIP_IPV4 +/** + * Conversion from InetAddressIPv4 oid to lwIP ip4_addr + * @param oid points to u32_t ident[4] input + * @param ip points to output struct + */ +u8_t +snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip) +{ + if ((oid[0] > 0xFF) || + (oid[1] > 0xFF) || + (oid[2] > 0xFF) || + (oid[3] > 0xFF)) { + ip4_addr_copy(*ip, *IP4_ADDR_ANY4); + return 0; + } + + IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]); + return 1; +} + +/** + * Convert ip4_addr to InetAddressIPv4 (no InetAddressType) + * @param ip points to input struct + * @param oid points to u32_t ident[4] output + */ +void +snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid) +{ + oid[0] = ip4_addr1(ip); + oid[1] = ip4_addr2(ip); + oid[2] = ip4_addr3(ip); + oid[3] = ip4_addr4(ip); +} +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +/** + * Conversion from InetAddressIPv6 oid to lwIP ip6_addr + * @param oid points to u32_t oid[16] input + * @param ip points to output struct + */ +u8_t +snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip) +{ + if ((oid[0] > 0xFF) || + (oid[1] > 0xFF) || + (oid[2] > 0xFF) || + (oid[3] > 0xFF) || + (oid[4] > 0xFF) || + (oid[5] > 0xFF) || + (oid[6] > 0xFF) || + (oid[7] > 0xFF) || + (oid[8] > 0xFF) || + (oid[9] > 0xFF) || + (oid[10] > 0xFF) || + (oid[11] > 0xFF) || + (oid[12] > 0xFF) || + (oid[13] > 0xFF) || + (oid[14] > 0xFF) || + (oid[15] > 0xFF)) { + ip6_addr_set_any(ip); + return 0; + } + + ip->addr[0] = (oid[0] << 24) | (oid[1] << 16) | (oid[2] << 8) | (oid[3] << 0); + ip->addr[1] = (oid[4] << 24) | (oid[5] << 16) | (oid[6] << 8) | (oid[7] << 0); + ip->addr[2] = (oid[8] << 24) | (oid[9] << 16) | (oid[10] << 8) | (oid[11] << 0); + ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0); + return 1; +} + +/** + * Convert ip6_addr to InetAddressIPv6 (no InetAddressType) + * @param ip points to input struct + * @param oid points to u32_t ident[16] output + */ +void +snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid) +{ + oid[0] = (ip->addr[0] & 0xFF000000) >> 24; + oid[1] = (ip->addr[0] & 0x00FF0000) >> 16; + oid[2] = (ip->addr[0] & 0x0000FF00) >> 8; + oid[3] = (ip->addr[0] & 0x000000FF) >> 0; + oid[4] = (ip->addr[1] & 0xFF000000) >> 24; + oid[5] = (ip->addr[1] & 0x00FF0000) >> 16; + oid[6] = (ip->addr[1] & 0x0000FF00) >> 8; + oid[7] = (ip->addr[1] & 0x000000FF) >> 0; + oid[8] = (ip->addr[2] & 0xFF000000) >> 24; + oid[9] = (ip->addr[2] & 0x00FF0000) >> 16; + oid[10] = (ip->addr[2] & 0x0000FF00) >> 8; + oid[11] = (ip->addr[2] & 0x000000FF) >> 0; + oid[12] = (ip->addr[3] & 0xFF000000) >> 24; + oid[13] = (ip->addr[3] & 0x00FF0000) >> 16; + oid[14] = (ip->addr[3] & 0x0000FF00) >> 8; + oid[15] = (ip->addr[3] & 0x000000FF) >> 0; +} +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV4 || LWIP_IPV6 +/** + * Convert to InetAddressType+InetAddress+InetPortNumber + * @param ip IP address + * @param port Port + * @param oid OID + * @return OID length + */ +u8_t +snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid) +{ + u8_t idx; + + idx = snmp_ip_to_oid(ip, oid); + oid[idx] = port; + idx++; + + return idx; +} + +/** + * Convert to InetAddressType+InetAddress + * @param ip IP address + * @param oid OID + * @return OID length + */ +u8_t +snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid) +{ + if (IP_IS_ANY_TYPE_VAL(*ip)) { + oid[0] = 0; /* any */ + oid[1] = 0; /* no IP OIDs follow */ + return 2; + } else if (IP_IS_V6(ip)) { +#if LWIP_IPV6 + oid[0] = 2; /* ipv6 */ + oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */ + snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]); + return 18; +#else /* LWIP_IPV6 */ + return 0; +#endif /* LWIP_IPV6 */ + } else { +#if LWIP_IPV4 + oid[0] = 1; /* ipv4 */ + oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */ + snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]); + return 6; +#else /* LWIP_IPV4 */ + return 0; +#endif /* LWIP_IPV4 */ + } +} + +/** + * Convert from InetAddressType+InetAddress to ip_addr_t + * @param oid OID + * @param oid_len OID length + * @param ip IP address + * @return Parsed OID length + */ +u8_t +snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip) +{ + /* InetAddressType */ + if (oid_len < 1) { + return 0; + } + + if (oid[0] == 0) { /* any */ + /* 1x InetAddressType, 1x OID len */ + if (oid_len < 2) { + return 0; + } + if (oid[1] != 0) { + return 0; + } + + memset(ip, 0, sizeof(*ip)); + IP_SET_TYPE(ip, IPADDR_TYPE_ANY); + + return 2; + } else if (oid[0] == 1) { /* ipv4 */ +#if LWIP_IPV4 + /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */ + if (oid_len < 6) { + return 0; + } + + /* 4x ipv4 OID */ + if (oid[1] != 4) { + return 0; + } + + IP_SET_TYPE(ip, IPADDR_TYPE_V4); + if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) { + return 0; + } + + return 6; +#else /* LWIP_IPV4 */ + return 0; +#endif /* LWIP_IPV4 */ + } else if (oid[0] == 2) { /* ipv6 */ +#if LWIP_IPV6 + /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */ + if (oid_len < 18) { + return 0; + } + + /* 16x ipv6 OID */ + if (oid[1] != 16) { + return 0; + } + + IP_SET_TYPE(ip, IPADDR_TYPE_V6); + if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) { + return 0; + } + + return 18; +#else /* LWIP_IPV6 */ + return 0; +#endif /* LWIP_IPV6 */ + } else { /* unsupported InetAddressType */ + return 0; + } +} + +/** + * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t + * @param oid OID + * @param oid_len OID length + * @param ip IP address + * @param port Port + * @return Parsed OID length + */ +u8_t +snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port) +{ + u8_t idx = 0; + + /* InetAddressType + InetAddress */ + idx += snmp_oid_to_ip(&oid[idx], oid_len-idx, ip); + if (idx == 0) { + return 0; + } + + /* InetPortNumber */ + if (oid_len < (idx+1)) { + return 0; + } + if (oid[idx] > 0xffff) { + return 0; + } + *port = (u16_t)oid[idx]; + idx++; + + return idx; +} + +#endif /* LWIP_IPV4 || LWIP_IPV6 */ + +/** + * Assign an OID to struct snmp_obj_id + * @param target Assignment target + * @param oid OID + * @param oid_len OID length + */ +void +snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len) +{ + LWIP_ASSERT("oid_len <= LWIP_SNMP_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN); + + target->len = oid_len; + + if (oid_len > 0) { + MEMCPY(target->id, oid, oid_len * sizeof(u32_t)); + } +} + +/** + * Prefix an OID to OID in struct snmp_obj_id + * @param target Assignment target to prefix + * @param oid OID + * @param oid_len OID length + */ +void +snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len) +{ + LWIP_ASSERT("target->len + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN); + + if (oid_len > 0) { + /* move existing OID to make room at the beginning for OID to insert */ + int i; + for (i = target->len-1; i>=0; i--) { + target->id[i + oid_len] = target->id[i]; + } + + /* paste oid at the beginning */ + MEMCPY(target->id, oid, oid_len * sizeof(u32_t)); + } +} + +/** + * Combine two OIDs into struct snmp_obj_id + * @param target Assignmet target + * @param oid1 OID 1 + * @param oid1_len OID 1 length + * @param oid2 OID 2 + * @param oid2_len OID 2 length + */ +void +snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) +{ + snmp_oid_assign(target, oid1, oid1_len); + snmp_oid_append(target, oid2, oid2_len); +} + +/** + * Append OIDs to struct snmp_obj_id + * @param target Assignment target to append to + * @param oid OID + * @param oid_len OID length + */ +void +snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len) +{ + LWIP_ASSERT("offset + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN); + + if (oid_len > 0) { + MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t)); + target->len += oid_len; + } +} + +/** + * Compare two OIDs + * @param oid1 OID 1 + * @param oid1_len OID 1 length + * @param oid2 OID 2 + * @param oid2_len OID 2 length + * @return -1: OID1<OID2 1: OID1 >OID2 0: equal + */ +s8_t +snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) +{ + u8_t level = 0; + LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0)); + LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0)); + + while ((level < oid1_len) && (level < oid2_len)) { + if (*oid1 < *oid2) { + return -1; + } + if (*oid1 > *oid2) { + return 1; + } + + level++; + oid1++; + oid2++; + } + + /* common part of both OID's is equal, compare length */ + if (oid1_len < oid2_len) { + return -1; + } + if (oid1_len > oid2_len) { + return 1; + } + + /* they are equal */ + return 0; +} + + +/** + * Check of two OIDs are equal + * @param oid1 OID 1 + * @param oid1_len OID 1 length + * @param oid2 OID 2 + * @param oid2_len OID 2 length + * @return 1: equal 0: non-equal + */ +u8_t +snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) +{ + return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0)? 1 : 0; +} + +/** + * Convert netif to interface index + * @param netif netif + * @return index + */ +u8_t +netif_to_num(const struct netif *netif) +{ + u8_t result = 0; + struct netif *netif_iterator = netif_list; + + while (netif_iterator != NULL) { + result++; + + if (netif_iterator == netif) { + return result; + } + + netif_iterator = netif_iterator->next; + } + + LWIP_ASSERT("netif not found in netif_list", 0); + return 0; +} + +static const struct snmp_mib* +snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len) +{ + const u32_t* list_oid; + const u32_t* searched_oid; + u8_t i, l; + + u8_t max_match_len = 0; + const struct snmp_mib* matched_mib = NULL; + + LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL)); + + if (oid_len == 0) { + return NULL; + } + + for (i = 0; i < snmp_num_mibs; i++) { + LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL)); + LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL)); + + if (oid_len >= snmp_mibs[i]->base_oid_len) { + l = snmp_mibs[i]->base_oid_len; + list_oid = snmp_mibs[i]->base_oid; + searched_oid = oid; + + while (l > 0) { + if (*list_oid != *searched_oid) { + break; + } + + l--; + list_oid++; + searched_oid++; + } + + if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) { + max_match_len = snmp_mibs[i]->base_oid_len; + matched_mib = snmp_mibs[i]; + } + } + } + + return matched_mib; +} + +static const struct snmp_mib* +snmp_get_next_mib(const u32_t *oid, u8_t oid_len) +{ + u8_t i; + const struct snmp_mib* next_mib = NULL; + + LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL)); + + if (oid_len == 0) { + return NULL; + } + + for (i = 0; i < snmp_num_mibs; i++) { + if (snmp_mibs[i]->base_oid != NULL) { + /* check if mib is located behind starting point */ + if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) { + if ((next_mib == NULL) || + (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, + next_mib->base_oid, next_mib->base_oid_len) < 0)) { + next_mib = snmp_mibs[i]; + } + } + } + } + + return next_mib; +} + +static const struct snmp_mib* +snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) +{ + const struct snmp_mib* next_mib = snmp_get_next_mib(oid1, oid1_len); + + LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL)); + LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0)); + + if (next_mib != NULL) { + if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) { + return next_mib; + } + } + + return NULL; +} + +u8_t +snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance) +{ + u8_t result = SNMP_ERR_NOSUCHOBJECT; + const struct snmp_mib *mib; + const struct snmp_node *mn = NULL; + + mib = snmp_get_mib_from_oid(oid, oid_len); + if (mib != NULL) { + u8_t oid_instance_len; + + mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len); + if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) { + /* get instance */ + const struct snmp_leaf_node* leaf_node = (const struct snmp_leaf_node*)(const void*)mn; + + node_instance->node = mn; + snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len); + + result = leaf_node->get_instance( + oid, + oid_len - oid_instance_len, + node_instance); + +#ifdef LWIP_DEBUG + if (result == SNMP_ERR_NOERROR) { + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) { + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n")); + } + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) { + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n")); + } + } +#endif + } + } + + return result; +} + +u8_t +snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance) +{ + const struct snmp_mib *mib; + const struct snmp_node *mn = NULL; + const u32_t* start_oid = NULL; + u8_t start_oid_len = 0; + + /* resolve target MIB from passed OID */ + mib = snmp_get_mib_from_oid(oid, oid_len); + if (mib == NULL) { + /* passed OID does not reference any known MIB, start at the next closest MIB */ + mib = snmp_get_next_mib(oid, oid_len); + + if (mib != NULL) { + start_oid = mib->base_oid; + start_oid_len = mib->base_oid_len; + } + } else { + start_oid = oid; + start_oid_len = oid_len; + } + + /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */ + while ((mib != NULL) && (mn == NULL)) { + u8_t oid_instance_len; + + /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */ + mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len); + if (mn != NULL) { + snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */ + snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */ + } else { + /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */ + mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid); + node_instance->instance_oid.len = 0; + } + + /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */ + node_instance->node = mn; + while (mn != NULL) { + u8_t result; + + /* clear fields which may have values from previous loops */ + node_instance->asn1_type = 0; + node_instance->access = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE; + node_instance->get_value = NULL; + node_instance->set_test = NULL; + node_instance->set_value = NULL; + node_instance->release_instance = NULL; + node_instance->reference.ptr = NULL; + node_instance->reference_len = 0; + + result = ((const struct snmp_leaf_node*)(const void*)mn)->get_next_instance( + node_oid->id, + node_oid->len, + node_instance); + + if (result == SNMP_ERR_NOERROR) { +#ifdef LWIP_DEBUG + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) { + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n")); + } + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) { + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n")); + } +#endif + + /* validate node because the node may be not accessible for example (but let the caller decide what is valid */ + if ((validate_node_instance_method == NULL) || + (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) { + /* node_oid "returns" the full result OID (including the instance part) */ + snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len); + break; + } + + if (node_instance->release_instance != NULL) { + node_instance->release_instance(node_instance); + } + /* + the instance itself is not valid, ask for next instance from same node. + we don't have to change any variables because node_instance->instance_oid is used as input (starting point) + as well as output (resulting next OID), so we have to simply call get_next_instance method again + */ + } else { + if (node_instance->release_instance != NULL) { + node_instance->release_instance(node_instance); + } + + /* the node has no further instance, skip to next node */ + mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */ + if (mn != NULL) { + /* prepare for next loop */ + snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len); + node_instance->instance_oid.len = 0; + node_instance->node = mn; + } + } + } + + if (mn != NULL) { + /* + we found a suitable next node, + now we have to check if a inner MIB is located between the searched OID and the resulting OID. + this is possible because MIB's may be located anywhere in the global tree, that means also in + the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another + MIB having .3 as root node may exist) + */ + const struct snmp_mib *intermediate_mib; + intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len); + + if (intermediate_mib != NULL) { + /* search for first node inside intermediate mib in next loop */ + if (node_instance->release_instance != NULL) { + node_instance->release_instance(node_instance); + } + + mn = NULL; + mib = intermediate_mib; + start_oid = mib->base_oid; + start_oid_len = mib->base_oid_len; + } + /* else { we found out target node } */ + } else { + /* + there is no further (suitable) node inside this MIB, search for the next MIB with following priority + 1. search for inner MIB's (whose root is located inside tree of current MIB) + 2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any + 3. take the next closest MIB (not being related to the current MIB) + */ + const struct snmp_mib *next_mib; + next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */ + + /* is the found MIB an inner MIB? (point 1) */ + if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) && + (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) { + /* yes it is -> continue at inner MIB */ + mib = next_mib; + start_oid = mib->base_oid; + start_oid_len = mib->base_oid_len; + } else { + /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */ + if (mib->base_oid_len > 1) { + mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1); + + if (mib == NULL) { + /* no surrounding mib, use next mib encountered above (point 3) */ + mib = next_mib; + + if (mib != NULL) { + start_oid = mib->base_oid; + start_oid_len = mib->base_oid_len; + } + } + /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */ + } + } + } + } + + if (mib == NULL) { + /* loop is only left when mib == null (error) or mib_node != NULL (success) */ + return SNMP_ERR_ENDOFMIBVIEW; + } + + return SNMP_ERR_NOERROR; +} + +/** + * Searches tree for the supplied object identifier. + * + */ +const struct snmp_node * +snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len) +{ + const struct snmp_node* const* node = &mib->root_node; + u8_t oid_offset = mib->base_oid_len; + + while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) { + /* search for matching sub node */ + u32_t subnode_oid = *(oid + oid_offset); + + u32_t i = (*(const struct snmp_tree_node* const*)node)->subnode_count; + node = (*(const struct snmp_tree_node* const*)node)->subnodes; + while ((i > 0) && ((*node)->oid != subnode_oid)) { + node++; + i--; + } + + if (i == 0) { + /* no matching subnode found */ + return NULL; + } + + oid_offset++; + } + + if ((*node)->node_type != SNMP_NODE_TREE) { + /* we found a leaf node */ + *oid_instance_len = oid_len - oid_offset; + return (*node); + } + + return NULL; +} + +const struct snmp_node* +snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret) +{ + u8_t oid_offset = mib->base_oid_len; + const struct snmp_node* const* node; + const struct snmp_tree_node* node_stack[SNMP_MAX_OBJ_ID_LEN]; + s32_t nsi = 0; /* NodeStackIndex */ + u32_t subnode_oid; + + if (mib->root_node->node_type != SNMP_NODE_TREE) { + /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */ + return NULL; + } + + /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */ + node_stack[nsi] = (const struct snmp_tree_node*)(const void*)mib->root_node; + while (oid_offset < oid_len) { + /* search for matching sub node */ + u32_t i = node_stack[nsi]->subnode_count; + node = node_stack[nsi]->subnodes; + + subnode_oid = *(oid + oid_offset); + + while ((i > 0) && ((*node)->oid != subnode_oid)) { + node++; + i--; + } + + if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) { + /* no (matching) tree-subnode found */ + break; + } + nsi++; + node_stack[nsi] = (const struct snmp_tree_node*)(const void*)(*node); + + oid_offset++; + } + + + if (oid_offset >= oid_len) { + /* passed oid references a tree node -> return first useable sub node of it */ + subnode_oid = 0; + } else { + subnode_oid = *(oid + oid_offset) + 1; + } + + while (nsi >= 0) { + const struct snmp_node* subnode = NULL; + + /* find next node on current level */ + s32_t i = node_stack[nsi]->subnode_count; + node = node_stack[nsi]->subnodes; + while (i > 0) { + if ((*node)->oid == subnode_oid) { + subnode = *node; + break; + } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) { + subnode = *node; + } + + node++; + i--; + } + + if (subnode == NULL) { + /* no further node found on this level, go one level up and start searching with index of current node*/ + subnode_oid = node_stack[nsi]->node.oid + 1; + nsi--; + } else { + if (subnode->node_type == SNMP_NODE_TREE) { + /* next is a tree node, go into it and start searching */ + nsi++; + node_stack[nsi] = (const struct snmp_tree_node*)(const void*)subnode; + subnode_oid = 0; + } else { + /* we found a leaf node -> fill oidret and return it */ + snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len); + i = 1; + while (i <= nsi) { + oidret->id[oidret->len] = node_stack[i]->node.oid; + oidret->len++; + i++; + } + + oidret->id[oidret->len] = subnode->oid; + oidret->len++; + + return subnode; + } + } + } + + return NULL; +} + +/** initialize struct next_oid_state using this function before passing it to next_oid_check */ +void +snmp_next_oid_init(struct snmp_next_oid_state *state, + const u32_t *start_oid, u8_t start_oid_len, + u32_t *next_oid_buf, u8_t next_oid_max_len) +{ + state->start_oid = start_oid; + state->start_oid_len = start_oid_len; + state->next_oid = next_oid_buf; + state->next_oid_len = 0; + state->next_oid_max_len = next_oid_max_len; + state->status = SNMP_NEXT_OID_STATUS_NO_MATCH; +} + +/** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check(); +this methid is intended if the complete OID is not yet known but it is very expensive to build it up, +so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/ +u8_t +snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len) +{ + if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) { + u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len; + + /* check passed OID is located behind start offset */ + if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) { + /* check if new oid is located closer to start oid than current closest oid */ + if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) || + (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) { + return 1; + } + } + } + + return 0; +} + +/** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */ +u8_t +snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference) +{ + /* do not overwrite a fail result */ + if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) { + /* check passed OID is located behind start offset */ + if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) { + /* check if new oid is located closer to start oid than current closest oid */ + if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) || + (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) { + if (oid_len <= state->next_oid_max_len) { + MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t)); + state->next_oid_len = oid_len; + state->status = SNMP_NEXT_OID_STATUS_SUCCESS; + state->reference = reference; + return 1; + } else { + state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL; + } + } + } + } + + return 0; +} + +u8_t +snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len) +{ + u8_t i; + + if (oid_len != oid_ranges_len) { + return 0; + } + + for (i = 0; i < oid_ranges_len; i++) { + if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) { + return 0; + } + } + + return 1; +} + +snmp_err_t +snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value) +{ + LWIP_UNUSED_ARG(instance); + LWIP_UNUSED_ARG(value_len); + LWIP_UNUSED_ARG(value); + + return SNMP_ERR_NOERROR; +} + +/** + * Decodes BITS pseudotype value from ASN.1 OctetString. + * + * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly + * be encoded/decoded by the agent. Instead call this function as required from + * get/test/set methods. + * + * @param buf points to a buffer holding the ASN1 octet string + * @param buf_len length of octet string + * @param bit_value decoded Bit value with Bit0 == LSB + * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit + */ +err_t +snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value) +{ + u8_t b; + u8_t bits_processed = 0; + *bit_value = 0; + + while (buf_len > 0) { + /* any bit set in this byte? */ + if (*buf != 0x00) { + if (bits_processed >= 32) { + /* accept more than 4 bytes, but only when no bits are set */ + return ERR_VAL; + } + + b = *buf; + do { + if (b & 0x80) { + *bit_value |= (1 << bits_processed); + } + bits_processed++; + b <<= 1; + } + while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */ + } else { + bits_processed += 8; + } + + buf_len--; + buf++; + } + + return ERR_OK; +} + +err_t +snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value) +{ + /* defined by RFC1443: + TruthValue ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents a boolean value." + SYNTAX INTEGER { true(1), false(2) } + */ + + if ((asn1_value == NULL) || (bool_value == NULL)) { + return ERR_ARG; + } + + if (*asn1_value == 1) { + *bool_value = 1; + } else if (*asn1_value == 2) { + *bool_value = 0; + } else { + return ERR_VAL; + } + + return ERR_OK; +} + +/** + * Encodes BITS pseudotype value into ASN.1 OctetString. + * + * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly + * be encoded/decoded by the agent. Instead call this function as required from + * get/test/set methods. + * + * @param buf points to a buffer where the resulting ASN1 octet string is stored to + * @param buf_len max length of the bufffer + * @param bit_value Bit value to encode with Bit0 == LSB + * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independant from their truth value) + * @return number of bytes used from buffer to store the resulting OctetString + */ +u8_t +snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count) +{ + u8_t len = 0; + u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */ + + while ((buf_len > 0) && (bit_value != 0x00)) { + s8_t i = 7; + *buf = 0x00; + while (i >= 0) { + if (bit_value & 0x01) { + *buf |= 0x01; + } + + if (i > 0) { + *buf <<= 1; + } + + bit_value >>= 1; + i--; + } + + buf++; + buf_len--; + len++; + } + + if (len < min_bytes) { + buf += len; + buf_len -= len; + + while ((len < min_bytes) && (buf_len > 0)) { + *buf = 0x00; + buf++; + buf_len--; + len++; + } + } + + return len; +} + +u8_t +snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value) +{ + /* defined by RFC1443: + TruthValue ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Represents a boolean value." + SYNTAX INTEGER { true(1), false(2) } + */ + + if (asn1_value == NULL) { + return 0; + } + + if (bool_value) { + *asn1_value = 1; /* defined by RFC1443 */ + } else { + *asn1_value = 2; /* defined by RFC1443 */ + } + + return sizeof(s32_t); +} + +#endif /* LWIP_SNMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_core_priv.h b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_core_priv.h new file mode 100644 index 0000000..5552177 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_core_priv.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H +#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_core.h" +#include "snmp_asn1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* (outdated) SNMPv1 error codes + * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request + */ +#define SNMP_ERR_NOSUCHNAME 2 +#define SNMP_ERR_BADVALUE 3 +#define SNMP_ERR_READONLY 4 +/* error codes which are internal and shall not be used by MIBS + * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request + */ +#define SNMP_ERR_TOOBIG 1 +#define SNMP_ERR_AUTHORIZATIONERROR 16 +#define SNMP_ERR_NOSUCHOBJECT SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT +#define SNMP_ERR_ENDOFMIBVIEW SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW + + +const struct snmp_node* snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len); +const struct snmp_node* snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret); + +typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance*, void*); + +u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance); +u8_t snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2.c new file mode 100644 index 0000000..9d8c43c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2.c @@ -0,0 +1,116 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + * Christiaan Simons + */ + +/** + * @defgroup snmp_mib2 MIB2 + * @ingroup snmp + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP && SNMP_LWIP_MIB2 /* don't build if not configured for use in lwipopts.h */ + +#if !LWIP_STATS +#error LWIP_SNMP MIB2 needs LWIP_STATS (for MIB2) +#endif +#if !MIB2_STATS +#error LWIP_SNMP MIB2 needs MIB2_STATS (for MIB2) +#endif + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_scalar.h" + +#if SNMP_USE_NETCONN +#include "lwip/tcpip.h" +#include "lwip/priv/tcpip_priv.h" +void +snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg) +{ +#if LWIP_TCPIP_CORE_LOCKING + LOCK_TCPIP_CORE(); + fn(arg); + UNLOCK_TCPIP_CORE(); +#else + tcpip_callback(fn, arg); +#endif +} + +struct snmp_threadsync_instance snmp_mib2_lwip_locks; +#endif + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* --- mib-2 .1.3.6.1.2.1 ----------------------------------------------------- */ +extern const struct snmp_scalar_array_node snmp_mib2_snmp_root; +extern const struct snmp_tree_node snmp_mib2_udp_root; +extern const struct snmp_tree_node snmp_mib2_tcp_root; +extern const struct snmp_scalar_array_node snmp_mib2_icmp_root; +extern const struct snmp_tree_node snmp_mib2_interface_root; +extern const struct snmp_scalar_array_node snmp_mib2_system_node; +extern const struct snmp_tree_node snmp_mib2_at_root; +extern const struct snmp_tree_node snmp_mib2_ip_root; + +static const struct snmp_node* const mib2_nodes[] = { + &snmp_mib2_system_node.node.node, + &snmp_mib2_interface_root.node, +#if LWIP_ARP && LWIP_IPV4 + &snmp_mib2_at_root.node, +#endif /* LWIP_ARP && LWIP_IPV4 */ +#if LWIP_IPV4 + &snmp_mib2_ip_root.node, +#endif /* LWIP_IPV4 */ +#if LWIP_ICMP + &snmp_mib2_icmp_root.node.node, +#endif /* LWIP_ICMP */ +#if LWIP_TCP + &snmp_mib2_tcp_root.node, +#endif /* LWIP_TCP */ +#if LWIP_UDP + &snmp_mib2_udp_root.node, +#endif /* LWIP_UDP */ + &snmp_mib2_snmp_root.node.node +}; + +static const struct snmp_tree_node mib2_root = SNMP_CREATE_TREE_NODE(1, mib2_nodes); + +static const u32_t mib2_base_oid_arr[] = { 1,3,6,1,2,1 }; +const struct snmp_mib mib2 = SNMP_MIB_CREATE(mib2_base_oid_arr, &mib2_root.node); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_icmp.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_icmp.c new file mode 100644 index 0000000..995bd32 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_icmp.c @@ -0,0 +1,182 @@ +/** + * @file + * Management Information Base II (RFC1213) ICMP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + * Christiaan Simons + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/icmp.h" +#include "lwip/stats.h" + +#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +/* --- icmp .1.3.6.1.2.1.5 ----------------------------------------------------- */ + +static s16_t +icmp_get_value(const struct snmp_scalar_array_node_def *node, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + + switch (node->oid) { + case 1: /* icmpInMsgs */ + *uint_ptr = STATS_GET(mib2.icmpinmsgs); + return sizeof(*uint_ptr); + case 2: /* icmpInErrors */ + *uint_ptr = STATS_GET(mib2.icmpinerrors); + return sizeof(*uint_ptr); + case 3: /* icmpInDestUnreachs */ + *uint_ptr = STATS_GET(mib2.icmpindestunreachs); + return sizeof(*uint_ptr); + case 4: /* icmpInTimeExcds */ + *uint_ptr = STATS_GET(mib2.icmpintimeexcds); + return sizeof(*uint_ptr); + case 5: /* icmpInParmProbs */ + *uint_ptr = STATS_GET(mib2.icmpinparmprobs); + return sizeof(*uint_ptr); + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = STATS_GET(mib2.icmpinsrcquenchs); + return sizeof(*uint_ptr); + case 7: /* icmpInRedirects */ + *uint_ptr = STATS_GET(mib2.icmpinredirects); + return sizeof(*uint_ptr); + case 8: /* icmpInEchos */ + *uint_ptr = STATS_GET(mib2.icmpinechos); + return sizeof(*uint_ptr); + case 9: /* icmpInEchoReps */ + *uint_ptr = STATS_GET(mib2.icmpinechoreps); + return sizeof(*uint_ptr); + case 10: /* icmpInTimestamps */ + *uint_ptr = STATS_GET(mib2.icmpintimestamps); + return sizeof(*uint_ptr); + case 11: /* icmpInTimestampReps */ + *uint_ptr = STATS_GET(mib2.icmpintimestampreps); + return sizeof(*uint_ptr); + case 12: /* icmpInAddrMasks */ + *uint_ptr = STATS_GET(mib2.icmpinaddrmasks); + return sizeof(*uint_ptr); + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = STATS_GET(mib2.icmpinaddrmaskreps); + return sizeof(*uint_ptr); + case 14: /* icmpOutMsgs */ + *uint_ptr = STATS_GET(mib2.icmpoutmsgs); + return sizeof(*uint_ptr); + case 15: /* icmpOutErrors */ + *uint_ptr = STATS_GET(mib2.icmpouterrors); + return sizeof(*uint_ptr); + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = STATS_GET(mib2.icmpoutdestunreachs); + return sizeof(*uint_ptr); + case 17: /* icmpOutTimeExcds */ + *uint_ptr = STATS_GET(mib2.icmpouttimeexcds); + return sizeof(*uint_ptr); + case 18: /* icmpOutParmProbs: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 19: /* icmpOutSrcQuenchs: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 20: /* icmpOutRedirects: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 21: /* icmpOutEchos */ + *uint_ptr = STATS_GET(mib2.icmpoutechos); + return sizeof(*uint_ptr); + case 22: /* icmpOutEchoReps */ + *uint_ptr = STATS_GET(mib2.icmpoutechoreps); + return sizeof(*uint_ptr); + case 23: /* icmpOutTimestamps: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 24: /* icmpOutTimestampReps: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 25: /* icmpOutAddrMasks: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + case 26: /* icmpOutAddrMaskReps: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_value(): unknown id: %"S32_F"\n", node->oid)); + break; + } + + return 0; +} + + +static const struct snmp_scalar_array_node_def icmp_nodes[] = { + { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 7, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {23, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, + {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY} +}; + +const struct snmp_scalar_array_node snmp_mib2_icmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(5, icmp_nodes, icmp_get_value, NULL, NULL); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_interfaces.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_interfaces.c new file mode 100644 index 0000000..979b507 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_interfaces.c @@ -0,0 +1,375 @@ +/** + * @file + * Management Information Base II (RFC1213) INTERFACES objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + * Christiaan Simons + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/netif.h" +#include "lwip/stats.h" + +#include + +#if LWIP_SNMP && SNMP_LWIP_MIB2 + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + + +/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */ + +static s16_t +interfaces_get_value(struct snmp_node_instance* instance, void* value) +{ + if (instance->node->oid == 1) { + s32_t *sint_ptr = (s32_t*)value; + s32_t num_netifs = 0; + + struct netif *netif = netif_list; + while (netif != NULL) { + num_netifs++; + netif = netif->next; + } + + *sint_ptr = num_netifs; + return sizeof(*sint_ptr); + } + + return 0; +} + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range interfaces_Table_oid_ranges[] = { + { 1, 0xff } /* netif->num is u8_t */ +}; + +static const u8_t iftable_ifOutQLen = 0; + +static const u8_t iftable_ifOperStatus_up = 1; +static const u8_t iftable_ifOperStatus_down = 2; + +static const u8_t iftable_ifAdminStatus_up = 1; +static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7; +static const u8_t iftable_ifAdminStatus_down = 2; + +static snmp_err_t +interfaces_Table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance) +{ + u32_t ifIndex; + struct netif *netif; + + LWIP_UNUSED_ARG(column); + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get netif index from incoming OID */ + ifIndex = row_oid[0]; + + /* find netif with index */ + netif = netif_list; + while (netif != NULL) { + if (netif_to_num(netif) == ifIndex) { + /* store netif pointer for subsequent operations (get/test/set) */ + cell_instance->reference.ptr = netif; + return SNMP_ERR_NOERROR; + } + netif = netif->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +interfaces_Table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance) +{ + struct netif *netif; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)]; + + LWIP_UNUSED_ARG(column); + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + netif = netif_list; + while (netif != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)]; + test_oid[0] = netif_to_num(netif); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif); + + netif = netif->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* store netif pointer for subsequent operations (get/test/set) */ + cell_instance->reference.ptr = /* (struct netif*) */state.reference; + return SNMP_ERR_NOERROR; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static s16_t +interfaces_Table_get_value(struct snmp_node_instance* instance, void* value) +{ + struct netif *netif = (struct netif*)instance->reference.ptr; + u32_t* value_u32 = (u32_t*)value; + s32_t* value_s32 = (s32_t*)value; + u16_t value_len; + + switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id)) + { + case 1: /* ifIndex */ + *value_s32 = netif_to_num(netif); + value_len = sizeof(*value_s32); + break; + case 2: /* ifDescr */ + value_len = sizeof(netif->name); + MEMCPY(value, netif->name, value_len); + break; + case 3: /* ifType */ + *value_s32 = netif->link_type; + value_len = sizeof(*value_s32); + break; + case 4: /* ifMtu */ + *value_s32 = netif->mtu; + value_len = sizeof(*value_s32); + break; + case 5: /* ifSpeed */ + *value_u32 = netif->link_speed; + value_len = sizeof(*value_u32); + break; + case 6: /* ifPhysAddress */ + value_len = sizeof(netif->hwaddr); + MEMCPY(value, &netif->hwaddr, value_len); + break; + case 7: /* ifAdminStatus */ + if (netif_is_up(netif)) { + *value_s32 = iftable_ifOperStatus_up; + } else { + *value_s32 = iftable_ifOperStatus_down; + } + value_len = sizeof(*value_s32); + break; + case 8: /* ifOperStatus */ + if (netif_is_up(netif)) { + if (netif_is_link_up(netif)) { + *value_s32 = iftable_ifAdminStatus_up; + } else { + *value_s32 = iftable_ifAdminStatus_lowerLayerDown; + } + } else { + *value_s32 = iftable_ifAdminStatus_down; + } + value_len = sizeof(*value_s32); + break; + case 9: /* ifLastChange */ + *value_u32 = netif->ts; + value_len = sizeof(*value_u32); + break; + case 10: /* ifInOctets */ + *value_u32 = netif->mib2_counters.ifinoctets; + value_len = sizeof(*value_u32); + break; + case 11: /* ifInUcastPkts */ + *value_u32 = netif->mib2_counters.ifinucastpkts; + value_len = sizeof(*value_u32); + break; + case 12: /* ifInNUcastPkts */ + *value_u32 = netif->mib2_counters.ifinnucastpkts; + value_len = sizeof(*value_u32); + break; + case 13: /* ifInDiscards */ + *value_u32 = netif->mib2_counters.ifindiscards; + value_len = sizeof(*value_u32); + break; + case 14: /* ifInErrors */ + *value_u32 = netif->mib2_counters.ifinerrors; + value_len = sizeof(*value_u32); + break; + case 15: /* ifInUnkownProtos */ + *value_u32 = netif->mib2_counters.ifinunknownprotos; + value_len = sizeof(*value_u32); + break; + case 16: /* ifOutOctets */ + *value_u32 = netif->mib2_counters.ifoutoctets; + value_len = sizeof(*value_u32); + break; + case 17: /* ifOutUcastPkts */ + *value_u32 = netif->mib2_counters.ifoutucastpkts; + value_len = sizeof(*value_u32); + break; + case 18: /* ifOutNUcastPkts */ + *value_u32 = netif->mib2_counters.ifoutnucastpkts; + value_len = sizeof(*value_u32); + break; + case 19: /* ifOutDiscarts */ + *value_u32 = netif->mib2_counters.ifoutdiscards; + value_len = sizeof(*value_u32); + break; + case 20: /* ifOutErrors */ + *value_u32 = netif->mib2_counters.ifouterrors; + value_len = sizeof(*value_u32); + break; + case 21: /* ifOutQLen */ + *value_u32 = iftable_ifOutQLen; + value_len = sizeof(*value_u32); + break; + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + case 22: /* ifSpecific */ + value_len = snmp_zero_dot_zero.len * sizeof(u32_t); + MEMCPY(value, snmp_zero_dot_zero.id, value_len); + break; + default: + return 0; + } + + return value_len; +} + +#if !SNMP_SAFE_REQUESTS + +static snmp_err_t +interfaces_Table_set_test(struct snmp_node_instance* instance, u16_t len, void *value) +{ + s32_t *sint_ptr = (s32_t*)value; + + /* stack should never call this method for another column, + because all other columns are set to readonly */ + LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7)); + LWIP_UNUSED_ARG(len); + + if (*sint_ptr == 1 || *sint_ptr == 2) { + return SNMP_ERR_NOERROR; + } + + return SNMP_ERR_WRONGVALUE; +} + +static snmp_err_t +interfaces_Table_set_value(struct snmp_node_instance* instance, u16_t len, void *value) +{ + struct netif *netif = (struct netif*)instance->reference.ptr; + s32_t *sint_ptr = (s32_t*)value; + + /* stack should never call this method for another column, + because all other columns are set to readonly */ + LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7)); + LWIP_UNUSED_ARG(len); + + if (*sint_ptr == 1) { + netif_set_up(netif); + } else if (*sint_ptr == 2) { + netif_set_down(netif); + } + + return SNMP_ERR_NOERROR; +} + +#endif /* SNMP_SAFE_REQUESTS */ + +static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value); + +static const struct snmp_table_col_def interfaces_Table_columns[] = { + { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */ + { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */ + { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */ + { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */ + { 5, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */ + { 6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */ +#if !SNMP_SAFE_REQUESTS + { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */ +#else + { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */ +#endif + { 8, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */ + { 9, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */ + { 10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */ + { 11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */ + { 12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */ + { 13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */ + { 14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */ + { 15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */ + { 16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */ + { 17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */ + { 18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */ + { 19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */ + { 20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */ + { 21, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */ + { 22, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY } /* ifSpecific */ +}; + +#if !SNMP_SAFE_REQUESTS +static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE( + 2, interfaces_Table_columns, + interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance, + interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value); +#else +static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE( + 2, interfaces_Table_columns, + interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance, + interfaces_Table_get_value, NULL, NULL); +#endif + +/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ +CREATE_LWIP_SYNC_NODE(1, interfaces_Number) +CREATE_LWIP_SYNC_NODE(2, interfaces_Table) + +static const struct snmp_node* const interface_nodes[] = { + &SYNC_NODE_NAME(interfaces_Number).node.node, + &SYNC_NODE_NAME(interfaces_Table).node.node +}; + +const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_ip.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_ip.c new file mode 100644 index 0000000..4f05180 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_ip.c @@ -0,0 +1,743 @@ +/** + * @file + * Management Information Base II (RFC1213) IP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + * Christiaan Simons + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/stats.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/etharp.h" + +#if LWIP_SNMP && SNMP_LWIP_MIB2 + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +#if LWIP_IPV4 +/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */ + +static s16_t +ip_get_value(struct snmp_node_instance* instance, void* value) +{ + s32_t* sint_ptr = (s32_t*)value; + u32_t* uint_ptr = (u32_t*)value; + + switch (instance->node->oid) { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + return sizeof(*sint_ptr); + case 2: /* ipDefaultTTL */ + *sint_ptr = IP_DEFAULT_TTL; + return sizeof(*sint_ptr); + case 3: /* ipInReceives */ + *uint_ptr = STATS_GET(mib2.ipinreceives); + return sizeof(*uint_ptr); + case 4: /* ipInHdrErrors */ + *uint_ptr = STATS_GET(mib2.ipinhdrerrors); + return sizeof(*uint_ptr); + case 5: /* ipInAddrErrors */ + *uint_ptr = STATS_GET(mib2.ipinaddrerrors); + return sizeof(*uint_ptr); + case 6: /* ipForwDatagrams */ + *uint_ptr = STATS_GET(mib2.ipforwdatagrams); + return sizeof(*uint_ptr); + case 7: /* ipInUnknownProtos */ + *uint_ptr = STATS_GET(mib2.ipinunknownprotos); + return sizeof(*uint_ptr); + case 8: /* ipInDiscards */ + *uint_ptr = STATS_GET(mib2.ipindiscards); + return sizeof(*uint_ptr); + case 9: /* ipInDelivers */ + *uint_ptr = STATS_GET(mib2.ipindelivers); + return sizeof(*uint_ptr); + case 10: /* ipOutRequests */ + *uint_ptr = STATS_GET(mib2.ipoutrequests); + return sizeof(*uint_ptr); + case 11: /* ipOutDiscards */ + *uint_ptr = STATS_GET(mib2.ipoutdiscards); + return sizeof(*uint_ptr); + case 12: /* ipOutNoRoutes */ + *uint_ptr = STATS_GET(mib2.ipoutnoroutes); + return sizeof(*uint_ptr); + case 13: /* ipReasmTimeout */ +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + return sizeof(*sint_ptr); + case 14: /* ipReasmReqds */ + *uint_ptr = STATS_GET(mib2.ipreasmreqds); + return sizeof(*uint_ptr); + case 15: /* ipReasmOKs */ + *uint_ptr = STATS_GET(mib2.ipreasmoks); + return sizeof(*uint_ptr); + case 16: /* ipReasmFails */ + *uint_ptr = STATS_GET(mib2.ipreasmfails); + return sizeof(*uint_ptr); + case 17: /* ipFragOKs */ + *uint_ptr = STATS_GET(mib2.ipfragoks); + return sizeof(*uint_ptr); + case 18: /* ipFragFails */ + *uint_ptr = STATS_GET(mib2.ipfragfails); + return sizeof(*uint_ptr); + case 19: /* ipFragCreates */ + *uint_ptr = STATS_GET(mib2.ipfragcreates); + return sizeof(*uint_ptr); + case 23: /* ipRoutingDiscards: not supported -> always 0 */ + *uint_ptr = 0; + return sizeof(*uint_ptr); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid)); + break; + } + + return 0; +} + +/** + * Test ip object value before setting. + * + * @param instance node instance + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static snmp_err_t +ip_set_test(struct snmp_node_instance* instance, u16_t len, void *value) +{ + snmp_err_t ret = SNMP_ERR_WRONGVALUE; + s32_t *sint_ptr = (s32_t*)value; + + LWIP_UNUSED_ARG(len); + switch (instance->node->oid) { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + ret = SNMP_ERR_NOERROR; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) { + ret = SNMP_ERR_NOERROR; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid)); + break; + } + + return ret; +} + +static snmp_err_t +ip_set_value(struct snmp_node_instance* instance, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(instance); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); + /* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */ + return SNMP_ERR_NOERROR; +} + +/* --- ipAddrTable --- */ + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = { + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff } /* IP D */ +}; + +static snmp_err_t +ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + LWIP_UNUSED_ARG(value_len); + + switch (*column) { + case 1: /* ipAdEntAddr */ + value->u32 = netif_ip4_addr(netif)->addr; + break; + case 2: /* ipAdEntIfIndex */ + value->u32 = netif_to_num(netif); + break; + case 3: /* ipAdEntNetMask */ + value->u32 = netif_ip4_netmask(netif)->addr; + break; + case 4: /* ipAdEntBcastAddr */ + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + value->u32 = IPADDR_BROADCAST & 1; + break; + case 5: /* ipAdEntReasmMaxSize */ +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + value->u32 = 0; +#endif + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +ip_AddrTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t ip; + struct netif *netif; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IP from incoming OID */ + snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */ + + /* find netif with requested ip */ + netif = netif_list; + while (netif != NULL) { + if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) { + /* fill in object properties */ + return ip_AddrTable_get_cell_value_core(netif, column, value, value_len); + } + + netif = netif->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +ip_AddrTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct netif *netif; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + netif = netif_list; + while (netif != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)]; + snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif); + + netif = netif->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return ip_AddrTable_get_cell_value_core((struct netif*)state.reference, column, value, value_len); + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +/* --- ipRouteTable --- */ + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = { + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff }, /* IP D */ +}; + +static snmp_err_t +ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + switch (*column) { + case 1: /* ipRouteDest */ + if (default_route) { + /* default rte has 0.0.0.0 dest */ + value->u32 = IP4_ADDR_ANY4->addr; + } else { + /* netifs have netaddress dest */ + ip4_addr_t tmp; + ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif)); + value->u32 = tmp.addr; + } + break; + case 2: /* ipRouteIfIndex */ + value->u32 = netif_to_num(netif); + break; + case 3: /* ipRouteMetric1 */ + if (default_route) { + value->s32 = 1; /* default */ + } else { + value->s32 = 0; /* normal */ + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + value->s32 = -1; /* none */ + break; + case 7: /* ipRouteNextHop */ + if (default_route) { + /* default rte: gateway */ + value->u32 = netif_ip4_gw(netif)->addr; + } else { + /* other rtes: netif ip_addr */ + value->u32 = netif_ip4_addr(netif)->addr; + } + break; + case 8: /* ipRouteType */ + if (default_route) { + /* default rte is indirect */ + value->u32 = 4; /* indirect */ + } else { + /* other rtes are direct */ + value->u32 = 3; /* direct */ + } + break; + case 9: /* ipRouteProto */ + /* locally defined routes */ + value->u32 = 2; /* local */ + break; + case 10: /* ipRouteAge */ + /* @todo (sysuptime - timestamp last change) / 100 */ + value->u32 = 0; + break; + case 11: /* ipRouteMask */ + if (default_route) { + /* default rte use 0.0.0.0 mask */ + value->u32 = IP4_ADDR_ANY4->addr; + } else { + /* other rtes use netmask */ + value->u32 = netif_ip4_netmask(netif)->addr; + } + break; + case 12: /* ipRouteMetric5 */ + value->s32 = -1; /* none */ + break; + case 13: /* ipRouteInfo */ + value->const_ptr = snmp_zero_dot_zero.id; + *value_len = snmp_zero_dot_zero.len * sizeof(u32_t); + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +ip_RouteTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t test_ip; + struct netif *netif; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IP and port from incoming OID */ + snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */ + + /* default route is on default netif */ + if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) { + /* fill in object properties */ + return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len); + } + + /* find netif with requested route */ + netif = netif_list; + while (netif != NULL) { + ip4_addr_t dst; + ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif)); + + if (ip4_addr_cmp(&dst, &test_ip)) { + /* fill in object properties */ + return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len); + } + + netif = netif->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +ip_RouteTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct netif *netif; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)]; + u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)); + + /* check default route */ + if (netif_default != NULL) { + snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[0]); + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default); + } + + /* iterate over all possible OIDs to find the next one */ + netif = netif_list; + while (netif != NULL) { + ip4_addr_t dst; + ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif)); + + /* check generated OID: is it a candidate for the next one? */ + if (!ip4_addr_isany_val(dst)) { + snmp_ip4_to_oid(&dst, &test_oid[0]); + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif); + } + + netif = netif->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + ip4_addr_t dst; + snmp_oid_to_ip4(&result_temp[0], &dst); + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return ip_RouteTable_get_cell_value_core((struct netif*)state.reference, ip4_addr_isany_val(dst), column, value, value_len); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +#if LWIP_ARP && LWIP_IPV4 +/* --- ipNetToMediaTable --- */ + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = { + { 1, 0xff }, /* IfIndex */ + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff } /* IP D */ +}; + +static snmp_err_t +ip_NetToMediaTable_get_cell_value_core(u8_t arp_table_index, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t *ip; + struct netif *netif; + struct eth_addr *ethaddr; + + etharp_get_entry(arp_table_index, &ip, &netif, ðaddr); + + /* value */ + switch (*column) { + case 1: /* atIfIndex / ipNetToMediaIfIndex */ + value->u32 = netif_to_num(netif); + break; + case 2: /* atPhysAddress / ipNetToMediaPhysAddress */ + value->ptr = ethaddr; + *value_len = sizeof(*ethaddr); + break; + case 3: /* atNetAddress / ipNetToMediaNetAddress */ + value->u32 = ip->addr; + break; + case 4: /* ipNetToMediaType */ + value->u32 = 3; /* dynamic*/ + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +ip_NetToMediaTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t ip_in; + u8_t netif_index; + u8_t i; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IP from incoming OID */ + netif_index = (u8_t)row_oid[0]; + snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */ + + /* find requested entry */ + for (i=0; iid, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + for (i=0; i + * Christiaan Simons + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_scalar.h" + +#if LWIP_SNMP && SNMP_LWIP_MIB2 + +#define MIB2_AUTH_TRAPS_ENABLED 1 +#define MIB2_AUTH_TRAPS_DISABLED 2 + +/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */ +static s16_t +snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + switch (node->oid) { + case 1: /* snmpInPkts */ + *uint_ptr = snmp_stats.inpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmp_stats.outpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmp_stats.inbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmp_stats.inbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmp_stats.inbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmp_stats.inasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmp_stats.intoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmp_stats.innosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmp_stats.inbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmp_stats.inreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmp_stats.ingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmp_stats.intotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmp_stats.intotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmp_stats.ingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmp_stats.ingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmp_stats.insetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmp_stats.ingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmp_stats.intraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmp_stats.outtoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmp_stats.outnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmp_stats.outbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmp_stats.outgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmp_stats.outgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmp_stats.outgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmp_stats.outsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmp_stats.outgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmp_stats.outtraps; + break; + case 30: /* snmpEnableAuthenTraps */ + if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) { + *uint_ptr = MIB2_AUTH_TRAPS_DISABLED; + } else { + *uint_ptr = MIB2_AUTH_TRAPS_ENABLED; + } + break; + case 31: /* snmpSilentDrops */ + *uint_ptr = 0; /* not supported */ + break; + case 32: /* snmpProxyDrops */ + *uint_ptr = 0; /* not supported */ + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_value(): unknown id: %"S32_F"\n", node->oid)); + return 0; + } + + return sizeof(*uint_ptr); +} + +static snmp_err_t +snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value) +{ + snmp_err_t ret = SNMP_ERR_WRONGVALUE; + LWIP_UNUSED_ARG(len); + + if (node->oid == 30) { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = (s32_t*)value; + + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) { + ret = SNMP_ERR_NOERROR; + } + } + return ret; +} + +static snmp_err_t +snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(len); + + if (node->oid == 30) { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) { + snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED); + } else { + snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED); + } + } + + return SNMP_ERR_NOERROR; +} + +/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */ +static const struct snmp_scalar_array_node_def snmp_nodes[] = { + { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInPkts */ + { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutPkts */ + { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadVersions */ + { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityNames */ + { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityUses */ + { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInASNParseErrs */ + { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTooBigs */ + { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInNoSuchNames */ + {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadValues */ + {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInReadOnlys */ + {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGenErrs */ + {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalReqVars */ + {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalSetVars */ + {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetRequests */ + {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetNexts */ + {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInSetRequests */ + {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetResponses */ + {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTraps */ + {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTooBigs */ + {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutNoSuchNames */ + {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutBadValues */ + {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGenErrs */ + {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetRequests */ + {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetNexts */ + {27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutSetRequests */ + {28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetResponses */ + {29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTraps */ + {30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */ + {31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpSilentDrops */ + {32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY} /* snmpProxyDrops */ +}; + +const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_system.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_system.c new file mode 100644 index 0000000..90e5780 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_system.c @@ -0,0 +1,377 @@ +/** + * @file + * Management Information Base II (RFC1213) SYSTEM objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + * Christiaan Simons + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/sys.h" + +#include + +#if LWIP_SNMP && SNMP_LWIP_MIB2 + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */ + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC; +static const u8_t* sysdescr = sysdescr_default; +static const u16_t* sysdescr_len = NULL; /* use strlen for determining len */ + +/** mib-2.system.sysContact */ +static const u8_t syscontact_default[] = SNMP_LWIP_MIB2_SYSCONTACT; +static const u8_t* syscontact = syscontact_default; +static const u16_t* syscontact_len = NULL; /* use strlen for determining len */ +static u8_t* syscontact_wr = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */ +static u16_t* syscontact_wr_len = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */ +static u16_t syscontact_bufsize = 0; /* 0=not writable */ + +/** mib-2.system.sysName */ +static const u8_t sysname_default[] = SNMP_LWIP_MIB2_SYSNAME; +static const u8_t* sysname = sysname_default; +static const u16_t* sysname_len = NULL; /* use strlen for determining len */ +static u8_t* sysname_wr = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */ +static u16_t* sysname_wr_len = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */ +static u16_t sysname_bufsize = 0; /* 0=not writable */ + +/** mib-2.system.sysLocation */ +static const u8_t syslocation_default[] = SNMP_LWIP_MIB2_SYSLOCATION; +static const u8_t* syslocation = syslocation_default; +static const u16_t* syslocation_len = NULL; /* use strlen for determining len */ +static u8_t* syslocation_wr = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */ +static u16_t* syslocation_wr_len = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */ +static u16_t syslocation_bufsize = 0; /* 0=not writable */ + +/** + * @ingroup snmp_mib2 + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void +snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len) +{ + if (str != NULL) { + sysdescr = str; + sysdescr_len = len; + } +} + +/** + * @ingroup snmp_mib2 + * Initializes sysContact pointers + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator. + * if set to NULL it is assumed that ocstr is NULL-terminated. + * @param bufsize size of the buffer in bytes. + * (this is required because the buffer can be overwritten by snmp-set) + * if ocstrlen is NULL buffer needs space for terminating 0 byte. + * otherwise complete buffer is used for string. + * if bufsize is set to 0, the value is regarded as read-only. + */ +void +snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize) +{ + if (ocstr != NULL) { + syscontact = ocstr; + syscontact_wr = ocstr; + syscontact_len = ocstrlen; + syscontact_wr_len = ocstrlen; + syscontact_bufsize = bufsize; + } +} + +/** + * @ingroup snmp_mib2 + * see \ref snmp_mib2_set_syscontact but set pointer to readonly memory + */ +void +snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen) +{ + if (ocstr != NULL) { + syscontact = ocstr; + syscontact_len = ocstrlen; + syscontact_wr = NULL; + syscontact_wr_len = NULL; + syscontact_bufsize = 0; + } +} + + +/** + * @ingroup snmp_mib2 + * Initializes sysName pointers + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator. + * if set to NULL it is assumed that ocstr is NULL-terminated. + * @param bufsize size of the buffer in bytes. + * (this is required because the buffer can be overwritten by snmp-set) + * if ocstrlen is NULL buffer needs space for terminating 0 byte. + * otherwise complete buffer is used for string. + * if bufsize is set to 0, the value is regarded as read-only. + */ +void +snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize) +{ + if (ocstr != NULL) { + sysname = ocstr; + sysname_wr = ocstr; + sysname_len = ocstrlen; + sysname_wr_len = ocstrlen; + sysname_bufsize = bufsize; + } +} + +/** + * @ingroup snmp_mib2 + * see \ref snmp_mib2_set_sysname but set pointer to readonly memory + */ +void +snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen) +{ + if (ocstr != NULL) { + sysname = ocstr; + sysname_len = ocstrlen; + sysname_wr = NULL; + sysname_wr_len = NULL; + sysname_bufsize = 0; + } +} + +/** + * @ingroup snmp_mib2 + * Initializes sysLocation pointers + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator. + * if set to NULL it is assumed that ocstr is NULL-terminated. + * @param bufsize size of the buffer in bytes. + * (this is required because the buffer can be overwritten by snmp-set) + * if ocstrlen is NULL buffer needs space for terminating 0 byte. + * otherwise complete buffer is used for string. + * if bufsize is set to 0, the value is regarded as read-only. + */ +void +snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize) +{ + if (ocstr != NULL) { + syslocation = ocstr; + syslocation_wr = ocstr; + syslocation_len = ocstrlen; + syslocation_wr_len = ocstrlen; + syslocation_bufsize = bufsize; + } +} + +/** + * @ingroup snmp_mib2 + * see \ref snmp_mib2_set_syslocation but set pointer to readonly memory + */ +void +snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen) +{ + if (ocstr != NULL) { + syslocation = ocstr; + syslocation_len = ocstrlen; + syslocation_wr = NULL; + syslocation_wr_len = NULL; + syslocation_bufsize = 0; + } +} + + +static s16_t +system_get_value(const struct snmp_scalar_array_node_def *node, void *value) +{ + const u8_t* var = NULL; + const s16_t* var_len; + u16_t result; + + switch (node->oid) { + case 1: /* sysDescr */ + var = sysdescr; + var_len = (const s16_t*)sysdescr_len; + break; + case 2: /* sysObjectID */ + { + const struct snmp_obj_id* dev_enterprise_oid = snmp_get_device_enterprise_oid(); + MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t)); + return dev_enterprise_oid->len * sizeof(u32_t); + } + case 3: /* sysUpTime */ + MIB2_COPY_SYSUPTIME_TO((u32_t*)value); + return sizeof(u32_t); + case 4: /* sysContact */ + var = syscontact; + var_len = (const s16_t*)syscontact_len; + break; + case 5: /* sysName */ + var = sysname; + var_len = (const s16_t*)sysname_len; + break; + case 6: /* sysLocation */ + var = syslocation; + var_len = (const s16_t*)syslocation_len; + break; + case 7: /* sysServices */ + *(s32_t*)value = SNMP_SYSSERVICES; + return sizeof(s32_t); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_value(): unknown id: %"S32_F"\n", node->oid)); + return 0; + } + + /* handle string values (OID 1,4,5 and 6) */ + LWIP_ASSERT("", (value != NULL)); + if (var_len == NULL) { + result = (s16_t)strlen((const char*)var); + } else { + result = *var_len; + } + MEMCPY(value, var, result); + return result; +} + +static snmp_err_t +system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value) +{ + snmp_err_t ret = SNMP_ERR_WRONGVALUE; + const u16_t* var_bufsize = NULL; + const u16_t* var_wr_len; + + LWIP_UNUSED_ARG(value); + + switch (node->oid) { + case 4: /* sysContact */ + var_bufsize = &syscontact_bufsize; + var_wr_len = syscontact_wr_len; + break; + case 5: /* sysName */ + var_bufsize = &sysname_bufsize; + var_wr_len = sysname_wr_len; + break; + case 6: /* sysLocation */ + var_bufsize = &syslocation_bufsize; + var_wr_len = syslocation_wr_len; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_test(): unknown id: %"S32_F"\n", node->oid)); + return ret; + } + + /* check if value is writable at all */ + if (*var_bufsize > 0) { + if (var_wr_len == NULL) { + /* we have to take the terminating 0 into account */ + if (len < *var_bufsize) { + ret = SNMP_ERR_NOERROR; + } + } else { + if (len <= *var_bufsize) { + ret = SNMP_ERR_NOERROR; + } + } + } else { + ret = SNMP_ERR_NOTWRITABLE; + } + + return ret; +} + +static snmp_err_t +system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value) +{ + u8_t* var_wr = NULL; + u16_t* var_wr_len; + + switch (node->oid) { + case 4: /* sysContact */ + var_wr = syscontact_wr; + var_wr_len = syscontact_wr_len; + break; + case 5: /* sysName */ + var_wr = sysname_wr; + var_wr_len = sysname_wr_len; + break; + case 6: /* sysLocation */ + var_wr = syslocation_wr; + var_wr_len = syslocation_wr_len; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_value(): unknown id: %"S32_F"\n", node->oid)); + return SNMP_ERR_GENERROR; + } + + /* no need to check size of target buffer, this was already done in set_test method */ + LWIP_ASSERT("", var_wr != NULL); + MEMCPY(var_wr, value, len); + + if (var_wr_len == NULL) { + /* add terminating 0 */ + var_wr[len] = 0; + } else { + *var_wr_len = len; + } + + return SNMP_ERR_NOERROR; +} + +static const struct snmp_scalar_array_node_def system_nodes[] = { + {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysDescr */ + {2, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysObjectID */ + {3, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysUpTime */ + {4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */ + {5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */ + {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */ + {7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY} /* sysServices */ +}; + +const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value); + +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_tcp.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_tcp.c new file mode 100644 index 0000000..21f6965 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_tcp.c @@ -0,0 +1,594 @@ +/** + * @file + * Management Information Base II (RFC1213) TCP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + * Christiaan Simons + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/tcp.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/stats.h" + +#include + +#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */ + +static s16_t +tcp_get_value(struct snmp_node_instance* instance, void* value) +{ + u32_t *uint_ptr = (u32_t*)value; + s32_t *sint_ptr = (s32_t*)value; + + switch (instance->node->oid) { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + return sizeof(*sint_ptr); + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + return sizeof(*sint_ptr); + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + return sizeof(*sint_ptr); + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + return sizeof(*sint_ptr); + case 5: /* tcpActiveOpens */ + *uint_ptr = STATS_GET(mib2.tcpactiveopens); + return sizeof(*uint_ptr); + case 6: /* tcpPassiveOpens */ + *uint_ptr = STATS_GET(mib2.tcppassiveopens); + return sizeof(*uint_ptr); + case 7: /* tcpAttemptFails */ + *uint_ptr = STATS_GET(mib2.tcpattemptfails); + return sizeof(*uint_ptr); + case 8: /* tcpEstabResets */ + *uint_ptr = STATS_GET(mib2.tcpestabresets); + return sizeof(*uint_ptr); + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + return sizeof(*uint_ptr); + case 10: /* tcpInSegs */ + *uint_ptr = STATS_GET(mib2.tcpinsegs); + return sizeof(*uint_ptr); + case 11: /* tcpOutSegs */ + *uint_ptr = STATS_GET(mib2.tcpoutsegs); + return sizeof(*uint_ptr); + case 12: /* tcpRetransSegs */ + *uint_ptr = STATS_GET(mib2.tcpretranssegs); + return sizeof(*uint_ptr); + case 14: /* tcpInErrs */ + *uint_ptr = STATS_GET(mib2.tcpinerrs); + return sizeof(*uint_ptr); + case 15: /* tcpOutRsts */ + *uint_ptr = STATS_GET(mib2.tcpoutrsts); + return sizeof(*uint_ptr); + case 17: /* tcpHCInSegs */ + memset(value, 0, 2*sizeof(u32_t)); /* not supported */ + return 2*sizeof(u32_t); + case 18: /* tcpHCOutSegs */ + memset(value, 0, 2*sizeof(u32_t)); /* not supported */ + return 2*sizeof(u32_t); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid)); + break; + } + + return 0; +} + +/* --- tcpConnTable --- */ + +#if LWIP_IPV4 + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = { + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff }, /* IP D */ + { 0, 0xffff }, /* Port */ + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff }, /* IP D */ + { 0, 0xffff } /* Port */ +}; + +static snmp_err_t +tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + LWIP_UNUSED_ARG(value_len); + + /* value */ + switch (*column) { + case 1: /* tcpConnState */ + value->u32 = pcb->state + 1; + break; + case 2: /* tcpConnLocalAddress */ + value->u32 = ip_2_ip4(&pcb->local_ip)->addr; + break; + case 3: /* tcpConnLocalPort */ + value->u32 = pcb->local_port; + break; + case 4: /* tcpConnRemAddress */ + if (pcb->state == LISTEN) { + value->u32 = IP4_ADDR_ANY4->addr; + } else { + value->u32 = ip_2_ip4(&pcb->remote_ip)->addr; + } + break; + case 5: /* tcpConnRemPort */ + if (pcb->state == LISTEN) { + value->u32 = 0; + } else { + value->u32 = pcb->remote_port; + } + break; + default: + LWIP_ASSERT("invalid id", 0); + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +tcp_ConnTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + u8_t i; + ip4_addr_t local_ip; + ip4_addr_t remote_ip; + u16_t local_port; + u16_t remote_port; + struct tcp_pcb *pcb; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IPs and ports from incoming OID */ + snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */ + local_port = (u16_t)row_oid[4]; + snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */ + remote_port = (u16_t)row_oid[9]; + + /* find tcp_pcb with requested ips and ports */ + for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) { + pcb = *tcp_pcb_lists[i]; + + while (pcb != NULL) { + /* do local IP and local port match? */ + if (IP_IS_V4_VAL(pcb->local_ip) && + ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) { + + /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */ + if (pcb->state == LISTEN) { + if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY4) && (remote_port == 0)) { + /* fill in object properties */ + return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len); + } + } else { + if (IP_IS_V4_VAL(pcb->remote_ip) && + ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) { + /* fill in object properties */ + return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len); + } + } + } + + pcb = pcb->next; + } + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +tcp_ConnTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + u8_t i; + struct tcp_pcb *pcb; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) { + pcb = *tcp_pcb_lists[i]; + while (pcb != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)]; + + if (IP_IS_V4_VAL(pcb->local_ip)) { + snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]); + test_oid[4] = pcb->local_port; + + /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */ + if (pcb->state == LISTEN) { + snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[5]); + test_oid[9] = 0; + } else { + if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */ + continue; + } + snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]); + test_oid[9] = pcb->remote_port; + } + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb); + } + + pcb = pcb->next; + } + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return tcp_ConnTable_get_cell_value_core((struct tcp_pcb*)state.reference, column, value, value_len); + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +#endif /* LWIP_IPV4 */ + +/* --- tcpConnectionTable --- */ + +static snmp_err_t +tcp_ConnectionTable_get_cell_value_core(const u32_t* column, struct tcp_pcb *pcb, union snmp_variant_value* value) +{ + /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */ + switch (*column) { + case 7: /* tcpConnectionState */ + value->u32 = pcb->state + 1; + break; + case 8: /* tcpConnectionProcess */ + value->u32 = 0; /* not supported */ + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +tcp_ConnectionTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip_addr_t local_ip, remote_ip; + u16_t local_port, remote_port; + struct tcp_pcb *pcb; + u8_t idx = 0; + u8_t i; + struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs}; + + LWIP_UNUSED_ARG(value_len); + + /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* find tcp_pcb with requested ip and port*/ + for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) { + pcb = *tcp_pcb_nonlisten_lists[i]; + + while (pcb != NULL) { + if (ip_addr_cmp(&local_ip, &pcb->local_ip) && + (local_port == pcb->local_port) && + ip_addr_cmp(&remote_ip, &pcb->remote_ip) && + (remote_port == pcb->remote_port)) { + /* fill in object properties */ + return tcp_ConnectionTable_get_cell_value_core(column, pcb, value); + } + pcb = pcb->next; + } + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct tcp_pcb *pcb; + struct snmp_next_oid_state state; + /* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress + 1x tcpConnectionLocalPort + * 1x tcpConnectionRemAddressType + 1x OID len + 16x tcpConnectionRemAddress + 1x tcpConnectionRemPort */ + u32_t result_temp[38]; + u8_t i; + struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs}; + + LWIP_UNUSED_ARG(value_len); + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp)); + + /* iterate over all possible OIDs to find the next one */ + for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) { + pcb = *tcp_pcb_nonlisten_lists[i]; + + while (pcb != NULL) { + u8_t idx = 0; + u32_t test_oid[LWIP_ARRAYSIZE(result_temp)]; + + /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */ + idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]); + + /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */ + idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, idx, pcb); + + pcb = pcb->next; + } + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb*)state.reference, value); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +/* --- tcpListenerTable --- */ + +static snmp_err_t +tcp_ListenerTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value) +{ + /* all items except tcpListenerProcess are declared as not-accessible */ + switch (*column) { + case 4: /* tcpListenerProcess */ + value->u32 = 0; /* not supported */ + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +tcp_ListenerTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip_addr_t local_ip; + u16_t local_port; + struct tcp_pcb_listen *pcb; + u8_t idx = 0; + + LWIP_UNUSED_ARG(value_len); + + /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* find tcp_pcb with requested ip and port*/ + pcb = tcp_listen_pcbs.listen_pcbs; + while (pcb != NULL) { + if (ip_addr_cmp(&local_ip, &pcb->local_ip) && + (local_port == pcb->local_port)) { + /* fill in object properties */ + return tcp_ListenerTable_get_cell_value_core(column, value); + } + pcb = pcb->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct tcp_pcb_listen *pcb; + struct snmp_next_oid_state state; + /* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress + 1x tcpListenerLocalPort */ + u32_t result_temp[19]; + + LWIP_UNUSED_ARG(value_len); + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp)); + + /* iterate over all possible OIDs to find the next one */ + pcb = tcp_listen_pcbs.listen_pcbs; + while (pcb != NULL) { + u8_t idx = 0; + u32_t test_oid[LWIP_ARRAYSIZE(result_temp)]; + + /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */ + idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]); + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, idx, NULL); + + pcb = pcb->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return tcp_ListenerTable_get_cell_value_core(column, value); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +static const struct snmp_scalar_node tcp_RtoAlgorithm = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value); +static const struct snmp_scalar_node tcp_RtoMin = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value); +static const struct snmp_scalar_node tcp_RtoMax = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value); +static const struct snmp_scalar_node tcp_MaxConn = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value); +static const struct snmp_scalar_node tcp_ActiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_PassiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_AttemptFails = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_EstabResets = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_CurrEstab = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value); +static const struct snmp_scalar_node tcp_InSegs = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_OutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_RetransSegs = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_InErrs = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_OutRsts = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value); +static const struct snmp_scalar_node tcp_HCInSegs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value); +static const struct snmp_scalar_node tcp_HCOutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value); + +#if LWIP_IPV4 +static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = { + { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */ + { 2, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */ + { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */ + { 4, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */ + { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnRemPort */ +}; + +static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value); +#endif /* LWIP_IPV4 */ + +static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = { + /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */ + { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */ + { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnectionProcess */ +}; + +static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value); + + +static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = { + /* all items except tcpListenerProcess are declared as not-accessible */ + { 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpListenerProcess */ +}; + +static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value); + +/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ +CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm) +CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin) +CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax) +CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn) +CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens) +CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens) +CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails) +CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets) +CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab) +CREATE_LWIP_SYNC_NODE(10, tcp_InSegs) +CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs) +CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs) +#if LWIP_IPV4 +CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable) +#endif /* LWIP_IPV4 */ +CREATE_LWIP_SYNC_NODE(14, tcp_InErrs) +CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts) +CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs) +CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs) +CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable) +CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable) + +static const struct snmp_node* const tcp_nodes[] = { + &SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node, + &SYNC_NODE_NAME(tcp_RtoMin).node.node, + &SYNC_NODE_NAME(tcp_RtoMax).node.node, + &SYNC_NODE_NAME(tcp_MaxConn).node.node, + &SYNC_NODE_NAME(tcp_ActiveOpens).node.node, + &SYNC_NODE_NAME(tcp_PassiveOpens).node.node, + &SYNC_NODE_NAME(tcp_AttemptFails).node.node, + &SYNC_NODE_NAME(tcp_EstabResets).node.node, + &SYNC_NODE_NAME(tcp_CurrEstab).node.node, + &SYNC_NODE_NAME(tcp_InSegs).node.node, + &SYNC_NODE_NAME(tcp_OutSegs).node.node, + &SYNC_NODE_NAME(tcp_RetransSegs).node.node, +#if LWIP_IPV4 + &SYNC_NODE_NAME(tcp_ConnTable).node.node, +#endif /* LWIP_IPV4 */ + &SYNC_NODE_NAME(tcp_InErrs).node.node, + &SYNC_NODE_NAME(tcp_OutRsts).node.node, + &SYNC_NODE_NAME(tcp_HCInSegs).node.node, + &SYNC_NODE_NAME(tcp_HCOutSegs).node.node, + &SYNC_NODE_NAME(tcp_ConnectionTable).node.node, + &SYNC_NODE_NAME(tcp_ListenerTable).node.node +}; + +const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes); +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_udp.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_udp.c new file mode 100644 index 0000000..6a983df --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_mib2_udp.c @@ -0,0 +1,357 @@ +/** + * @file + * Management Information Base II (RFC1213) UDP objects and functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + * Christiaan Simons + */ + +#include "lwip/snmp.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_mib2.h" +#include "lwip/apps/snmp_table.h" +#include "lwip/apps/snmp_scalar.h" +#include "lwip/udp.h" +#include "lwip/stats.h" + +#include + +#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP + +#if SNMP_USE_NETCONN +#define SYNC_NODE_NAME(node_name) node_name ## _synced +#define CREATE_LWIP_SYNC_NODE(oid, node_name) \ + static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks); +#else +#define SYNC_NODE_NAME(node_name) node_name +#define CREATE_LWIP_SYNC_NODE(oid, node_name) +#endif + +/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */ + +static s16_t +udp_get_value(struct snmp_node_instance* instance, void* value) +{ + u32_t *uint_ptr = (u32_t*)value; + + switch (instance->node->oid) { + case 1: /* udpInDatagrams */ + *uint_ptr = STATS_GET(mib2.udpindatagrams); + return sizeof(*uint_ptr); + case 2: /* udpNoPorts */ + *uint_ptr = STATS_GET(mib2.udpnoports); + return sizeof(*uint_ptr); + case 3: /* udpInErrors */ + *uint_ptr = STATS_GET(mib2.udpinerrors); + return sizeof(*uint_ptr); + case 4: /* udpOutDatagrams */ + *uint_ptr = STATS_GET(mib2.udpoutdatagrams); + return sizeof(*uint_ptr); + case 8: /* udpHCInDatagrams */ + memset(value, 0, 2*sizeof(u32_t)); /* not supported */ + return 2*sizeof(u32_t); + case 9: /* udpHCOutDatagrams */ + memset(value, 0, 2*sizeof(u32_t)); /* not supported */ + return 2*sizeof(u32_t); + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid)); + break; + } + + return 0; +} + +/* --- udpEndpointTable --- */ + +static snmp_err_t +udp_endpointTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value) +{ + /* all items except udpEndpointProcess are declared as not-accessible */ + switch (*column) { + case 8: /* udpEndpointProcess */ + value->u32 = 0; /* not supported */ + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +udp_endpointTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip_addr_t local_ip, remote_ip; + u16_t local_port, remote_port; + struct udp_pcb *pcb; + u8_t idx = 0; + + LWIP_UNUSED_ARG(value_len); + + /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */ + idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port); + if (idx == 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* udpEndpointInstance */ + if (row_oid_len < (idx+1)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + if (row_oid[idx] != 0) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* find udp_pcb with requested ip and port*/ + pcb = udp_pcbs; + while (pcb != NULL) { + if (ip_addr_cmp(&local_ip, &pcb->local_ip) && + (local_port == pcb->local_port) && + ip_addr_cmp(&remote_ip, &pcb->remote_ip) && + (remote_port == pcb->remote_port)) { + /* fill in object properties */ + return udp_endpointTable_get_cell_value_core(column, value); + } + pcb = pcb->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +udp_endpointTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct udp_pcb *pcb; + struct snmp_next_oid_state state; + /* 1x udpEndpointLocalAddressType + 1x OID len + 16x udpEndpointLocalAddress + 1x udpEndpointLocalPort + + * 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort + + * 1x udpEndpointInstance = 39 + */ + u32_t result_temp[39]; + + LWIP_UNUSED_ARG(value_len); + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp)); + + /* iterate over all possible OIDs to find the next one */ + pcb = udp_pcbs; + while (pcb != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(result_temp)]; + u8_t idx = 0; + + /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */ + idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]); + + /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */ + idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]); + + test_oid[idx] = 0; /* udpEndpointInstance */ + idx++; + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, idx, NULL); + + pcb = pcb->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return udp_endpointTable_get_cell_value_core(column, value); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +/* --- udpTable --- */ + +#if LWIP_IPV4 + +/* list of allowed value ranges for incoming OID */ +static const struct snmp_oid_range udp_Table_oid_ranges[] = { + { 0, 0xff }, /* IP A */ + { 0, 0xff }, /* IP B */ + { 0, 0xff }, /* IP C */ + { 0, 0xff }, /* IP D */ + { 1, 0xffff } /* Port */ +}; + +static snmp_err_t +udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len) +{ + LWIP_UNUSED_ARG(value_len); + + switch (*column) { + case 1: /* udpLocalAddress */ + /* set reference to PCB local IP and return a generic node that copies IP4 addresses */ + value->u32 = ip_2_ip4(&pcb->local_ip)->addr; + break; + case 2: /* udpLocalPort */ + /* set reference to PCB local port and return a generic node that copies u16_t values */ + value->u32 = pcb->local_port; + break; + default: + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static snmp_err_t +udp_Table_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len) +{ + ip4_addr_t ip; + u16_t port; + struct udp_pcb *pcb; + + /* check if incoming OID length and if values are in plausible range */ + if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + /* get IP and port from incoming OID */ + snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */ + port = (u16_t)row_oid[4]; + + /* find udp_pcb with requested ip and port*/ + pcb = udp_pcbs; + while (pcb != NULL) { + if (IP_IS_V4_VAL(pcb->local_ip)) { + if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) { + /* fill in object properties */ + return udp_Table_get_cell_value_core(pcb, column, value, value_len); + } + } + pcb = pcb->next; + } + + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; +} + +static snmp_err_t +udp_Table_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len) +{ + struct udp_pcb *pcb; + struct snmp_next_oid_state state; + u32_t result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)]; + + /* init struct to search next oid */ + snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges)); + + /* iterate over all possible OIDs to find the next one */ + pcb = udp_pcbs; + while (pcb != NULL) { + u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)]; + + if (IP_IS_V4_VAL(pcb->local_ip)) { + snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]); + test_oid[4] = pcb->local_port; + + /* check generated OID: is it a candidate for the next one? */ + snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb); + } + + pcb = pcb->next; + } + + /* did we find a next one? */ + if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) { + snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len); + /* fill in object properties */ + return udp_Table_get_cell_value_core((struct udp_pcb*)state.reference, column, value, value_len); + } else { + /* not found */ + return SNMP_ERR_NOSUCHINSTANCE; + } +} + +#endif /* LWIP_IPV4 */ + +static const struct snmp_scalar_node udp_inDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER, udp_get_value); +static const struct snmp_scalar_node udp_noPorts = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER, udp_get_value); +static const struct snmp_scalar_node udp_inErrors = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, udp_get_value); +static const struct snmp_scalar_node udp_outDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, udp_get_value); +static const struct snmp_scalar_node udp_HCInDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value); +static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value); + +#if LWIP_IPV4 +static const struct snmp_table_simple_col_def udp_Table_columns[] = { + { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */ + { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpLocalPort */ +}; +static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value); +#endif /* LWIP_IPV4 */ + +static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = { + /* all items except udpEndpointProcess are declared as not-accessible */ + { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpEndpointProcess */ +}; + +static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value); + +/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ +CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams) +CREATE_LWIP_SYNC_NODE(2, udp_noPorts) +CREATE_LWIP_SYNC_NODE(3, udp_inErrors) +CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams) +#if LWIP_IPV4 +CREATE_LWIP_SYNC_NODE(5, udp_Table) +#endif /* LWIP_IPV4 */ +CREATE_LWIP_SYNC_NODE(7, udp_endpointTable) +CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams) +CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams) + +static const struct snmp_node* const udp_nodes[] = { + &SYNC_NODE_NAME(udp_inDatagrams).node.node, + &SYNC_NODE_NAME(udp_noPorts).node.node, + &SYNC_NODE_NAME(udp_inErrors).node.node, + &SYNC_NODE_NAME(udp_outDatagrams).node.node, +#if LWIP_IPV4 + &SYNC_NODE_NAME(udp_Table).node.node, +#endif /* LWIP_IPV4 */ + &SYNC_NODE_NAME(udp_endpointTable).node.node, + &SYNC_NODE_NAME(udp_HCInDatagrams).node.node, + &SYNC_NODE_NAME(udp_HCOutDatagrams).node.node +}; + +const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes); +#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_msg.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_msg.c new file mode 100644 index 0000000..0cb7ca9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_msg.c @@ -0,0 +1,1668 @@ +/** + * @file + * SNMP message processing (RFC1157). + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + * Martin Hentschel + * Elias Oenal + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "snmp_msg.h" +#include "snmp_asn1.h" +#include "snmp_core_priv.h" +#include "lwip/ip_addr.h" +#include "lwip/stats.h" + +#if LWIP_SNMP_V3 +#include "lwip/apps/snmpv3.h" +#include "snmpv3_priv.h" +#ifdef LWIP_SNMPV3_INCLUDE_ENGINE +#include LWIP_SNMPV3_INCLUDE_ENGINE +#endif +#endif + +#include + +/* public (non-static) constants */ +/** SNMP community string */ +const char *snmp_community = SNMP_COMMUNITY; +/** SNMP community string for write access */ +const char *snmp_community_write = SNMP_COMMUNITY_WRITE; +/** SNMP community string for sending traps */ +const char *snmp_community_trap = SNMP_COMMUNITY_TRAP; + +snmp_write_callback_fct snmp_write_callback = NULL; +void* snmp_write_callback_arg = NULL; + +/** + * @ingroup snmp_core + * Returns current SNMP community string. + * @return current SNMP community string + */ +const char * +snmp_get_community(void) +{ + return snmp_community; +} + +/** + * @ingroup snmp_core + * Sets SNMP community string. + * The string itself (its storage) must be valid throughout the whole life of + * program (or until it is changed to sth else). + * + * @param community is a pointer to new community string + */ +void +snmp_set_community(const char * const community) +{ + LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN); + snmp_community = community; +} + +/** + * @ingroup snmp_core + * Returns current SNMP write-access community string. + * @return current SNMP write-access community string + */ +const char * +snmp_get_community_write(void) +{ + return snmp_community_write; +} + +/** + * @ingroup snmp_traps + * Returns current SNMP community string used for sending traps. + * @return current SNMP community string used for sending traps + */ +const char * +snmp_get_community_trap(void) +{ + return snmp_community_trap; +} + +/** + * @ingroup snmp_core + * Sets SNMP community string for write-access. + * The string itself (its storage) must be valid throughout the whole life of + * program (or until it is changed to sth else). + * + * @param community is a pointer to new write-access community string + */ +void +snmp_set_community_write(const char * const community) +{ + LWIP_ASSERT("community string must not be NULL", community != NULL); + LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN); + snmp_community_write = community; +} + +/** + * @ingroup snmp_traps + * Sets SNMP community string used for sending traps. + * The string itself (its storage) must be valid throughout the whole life of + * program (or until it is changed to sth else). + * + * @param community is a pointer to new trap community string + */ +void +snmp_set_community_trap(const char * const community) +{ + LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN); + snmp_community_trap = community; +} + +/** + * @ingroup snmp_core + * Callback fired on every successful write access + */ +void +snmp_set_write_callback(snmp_write_callback_fct write_callback, void* callback_arg) +{ + snmp_write_callback = write_callback; + snmp_write_callback_arg = callback_arg; +} + +/* ----------------------------------------------------------------------- */ +/* forward declarations */ +/* ----------------------------------------------------------------------- */ + +static err_t snmp_process_get_request(struct snmp_request *request); +static err_t snmp_process_getnext_request(struct snmp_request *request); +static err_t snmp_process_getbulk_request(struct snmp_request *request); +static err_t snmp_process_set_request(struct snmp_request *request); + +static err_t snmp_parse_inbound_frame(struct snmp_request *request); +static err_t snmp_prepare_outbound_frame(struct snmp_request *request); +static err_t snmp_complete_outbound_frame(struct snmp_request *request); +static void snmp_execute_write_callbacks(struct snmp_request *request); + + +/* ----------------------------------------------------------------------- */ +/* implementation */ +/* ----------------------------------------------------------------------- */ + +void +snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port) +{ + err_t err; + struct snmp_request request; + + memset(&request, 0, sizeof(request)); + request.handle = handle; + request.source_ip = source_ip; + request.source_port = port; + request.inbound_pbuf = p; + + snmp_stats.inpkts++; + + err = snmp_parse_inbound_frame(&request); + if (err == ERR_OK) { + err = snmp_prepare_outbound_frame(&request); + if (err == ERR_OK) { + + if (request.error_status == SNMP_ERR_NOERROR) { + /* only process frame if we do not already have an error to return (e.g. all readonly) */ + if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_REQ) { + err = snmp_process_get_request(&request); + } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ) { + err = snmp_process_getnext_request(&request); + } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) { + err = snmp_process_getbulk_request(&request); + } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) { + err = snmp_process_set_request(&request); + } + } + + if (err == ERR_OK) { + err = snmp_complete_outbound_frame(&request); + + if (err == ERR_OK) { + err = snmp_sendto(request.handle, request.outbound_pbuf, request.source_ip, request.source_port); + + if ((request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) + && (request.error_status == SNMP_ERR_NOERROR) + && (snmp_write_callback != NULL)) { + /* raise write notification for all written objects */ + snmp_execute_write_callbacks(&request); + } + } + } + } + + if (request.outbound_pbuf != NULL) { + pbuf_free(request.outbound_pbuf); + } + } +} + +static u8_t +snmp_msg_getnext_validate_node_inst(struct snmp_node_instance* node_instance, void* validate_arg) +{ + if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != SNMP_NODE_INSTANCE_ACCESS_READ) || (node_instance->get_value == NULL)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + if ((node_instance->asn1_type == SNMP_ASN1_TYPE_COUNTER64) && (((struct snmp_request*)validate_arg)->version == SNMP_VERSION_1)) { + /* according to RFC 2089 skip Counter64 objects in GetNext requests from v1 clients */ + return SNMP_ERR_NOSUCHINSTANCE; + } + + return SNMP_ERR_NOERROR; +} + +static void +snmp_process_varbind(struct snmp_request *request, struct snmp_varbind *vb, u8_t get_next) +{ + err_t err; + struct snmp_node_instance node_instance; + memset(&node_instance, 0, sizeof(node_instance)); + + if (get_next) { + struct snmp_obj_id result_oid; + request->error_status = snmp_get_next_node_instance_from_oid(vb->oid.id, vb->oid.len, snmp_msg_getnext_validate_node_inst, request, &result_oid, &node_instance); + + if (request->error_status == SNMP_ERR_NOERROR) { + snmp_oid_assign(&vb->oid, result_oid.id, result_oid.len); + } + } else { + request->error_status = snmp_get_node_instance_from_oid(vb->oid.id, vb->oid.len, &node_instance); + + if (request->error_status == SNMP_ERR_NOERROR) { + /* use 'getnext_validate' method for validation to avoid code duplication (some checks have to be executed here) */ + request->error_status = snmp_msg_getnext_validate_node_inst(&node_instance, request); + + if (request->error_status != SNMP_ERR_NOERROR) { + if (node_instance.release_instance != NULL) { + node_instance.release_instance(&node_instance); + } + } + } + } + + if (request->error_status != SNMP_ERR_NOERROR) { + if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) { + if ((request->version == SNMP_VERSION_2c) || request->version == SNMP_VERSION_3) { + /* in SNMP v2c a varbind related exception is stored in varbind and not in frame header */ + vb->type = (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | (request->error_status & SNMP_VARBIND_EXCEPTION_MASK)); + vb->value_len = 0; + + err = snmp_append_outbound_varbind(&(request->outbound_pbuf_stream), vb); + if (err == ERR_OK) { + /* we stored the exception in varbind -> go on */ + request->error_status = SNMP_ERR_NOERROR; + } else if (err == ERR_BUF) { + request->error_status = SNMP_ERR_TOOBIG; + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } + } else { + /* according to RFC 1157/1905, all other errors only return genError */ + request->error_status = SNMP_ERR_GENERROR; + } + } else { + s16_t len = node_instance.get_value(&node_instance, vb->value); + vb->type = node_instance.asn1_type; + + if(len >= 0) { + vb->value_len = (u16_t)len; /* cast is OK because we checked >= 0 above */ + + LWIP_ASSERT("SNMP_MAX_VALUE_SIZE is configured too low", (vb->value_len & ~SNMP_GET_VALUE_RAW_DATA) <= SNMP_MAX_VALUE_SIZE); + err = snmp_append_outbound_varbind(&request->outbound_pbuf_stream, vb); + + if (err == ERR_BUF) { + request->error_status = SNMP_ERR_TOOBIG; + } else if (err != ERR_OK) { + request->error_status = SNMP_ERR_GENERROR; + } + } else { + request->error_status = SNMP_ERR_GENERROR; + } + + if (node_instance.release_instance != NULL) { + node_instance.release_instance(&node_instance); + } + } +} + + +/** + * Service an internal or external event for SNMP GET. + * + * @param request points to the associated message process state + */ +static err_t +snmp_process_get_request(struct snmp_request *request) +{ + snmp_vb_enumerator_err_t err; + struct snmp_varbind vb; + vb.value = request->value_buffer; + + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get request\n")); + + while (request->error_status == SNMP_ERR_NOERROR) { + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) { + snmp_process_varbind(request, &vb, 0); + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) { + /* malformed ASN.1, don't answer */ + return ERR_ARG; + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } + + return ERR_OK; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request points to the associated message process state + */ +static err_t +snmp_process_getnext_request(struct snmp_request *request) +{ + snmp_vb_enumerator_err_t err; + struct snmp_varbind vb; + vb.value = request->value_buffer; + + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-next request\n")); + + while (request->error_status == SNMP_ERR_NOERROR) { + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) { + snmp_process_varbind(request, &vb, 1); + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) { + /* malformed ASN.1, don't answer */ + return ERR_ARG; + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } + + return ERR_OK; +} + +/** + * Service an internal or external event for SNMP GETBULKT. + * + * @param request points to the associated message process state + */ +static err_t +snmp_process_getbulk_request(struct snmp_request *request) +{ + snmp_vb_enumerator_err_t err; + s32_t non_repeaters = request->non_repeaters; + s32_t repetitions; + u16_t repetition_offset = 0; + struct snmp_varbind_enumerator repetition_varbind_enumerator; + struct snmp_varbind vb; + vb.value = request->value_buffer; + + if (SNMP_LWIP_GETBULK_MAX_REPETITIONS > 0) { + repetitions = LWIP_MIN(request->max_repetitions, SNMP_LWIP_GETBULK_MAX_REPETITIONS); + } else { + repetitions = request->max_repetitions; + } + + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-bulk request\n")); + + /* process non repeaters and first repetition */ + while (request->error_status == SNMP_ERR_NOERROR) { + if (non_repeaters == 0) { + repetition_offset = request->outbound_pbuf_stream.offset; + + if (repetitions == 0) { + /* do not resolve repeaters when repetitions is set to 0 */ + break; + } + repetitions--; + } + + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) { + /* malformed ASN.1, don't answer */ + return ERR_ARG; + } else if ((err != SNMP_VB_ENUMERATOR_ERR_OK) || (vb.type != SNMP_ASN1_TYPE_NULL) || (vb.value_len != 0)) { + request->error_status = SNMP_ERR_GENERROR; + } else { + snmp_process_varbind(request, &vb, 1); + non_repeaters--; + } + } + + /* process repetitions > 1 */ + while ((request->error_status == SNMP_ERR_NOERROR) && (repetitions > 0) && (request->outbound_pbuf_stream.offset != repetition_offset)) { + + u8_t all_endofmibview = 1; + + snmp_vb_enumerator_init(&repetition_varbind_enumerator, request->outbound_pbuf, repetition_offset, request->outbound_pbuf_stream.offset - repetition_offset); + repetition_offset = request->outbound_pbuf_stream.offset; /* for next loop */ + + while (request->error_status == SNMP_ERR_NOERROR) { + vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned) */ + err = snmp_vb_enumerator_get_next(&repetition_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + vb.value = request->value_buffer; + snmp_process_varbind(request, &vb, 1); + + if (request->error_status != SNMP_ERR_NOERROR) { + /* already set correct error-index (here it cannot be taken from inbound varbind enumerator) */ + request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count; + } else if (vb.type != (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW)) { + all_endofmibview = 0; + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else { + LWIP_DEBUGF(SNMP_DEBUG, ("Very strange, we cannot parse the varbind output that we created just before!")); + request->error_status = SNMP_ERR_GENERROR; + request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count; + } + } + + if ((request->error_status == SNMP_ERR_NOERROR) && all_endofmibview) { + /* stop when all varbinds in a loop return EndOfMibView */ + break; + } + + repetitions--; + } + + if (request->error_status == SNMP_ERR_TOOBIG) { + /* for GetBulk it is ok, if not all requested variables fit into the response -> just return the varbinds added so far */ + request->error_status = SNMP_ERR_NOERROR; + } + + return ERR_OK; +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request points to the associated message process state + */ +static err_t +snmp_process_set_request(struct snmp_request *request) +{ + snmp_vb_enumerator_err_t err; + struct snmp_varbind vb; + vb.value = request->value_buffer; + + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP set request\n")); + + /* perform set test on all objects */ + while (request->error_status == SNMP_ERR_NOERROR) { + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + struct snmp_node_instance node_instance; + memset(&node_instance, 0, sizeof(node_instance)); + + request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance); + if (request->error_status == SNMP_ERR_NOERROR) { + if (node_instance.asn1_type != vb.type) { + request->error_status = SNMP_ERR_WRONGTYPE; + } else if (((node_instance.access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != SNMP_NODE_INSTANCE_ACCESS_WRITE) || (node_instance.set_value == NULL)) { + request->error_status = SNMP_ERR_NOTWRITABLE; + } else { + if (node_instance.set_test != NULL) { + request->error_status = node_instance.set_test(&node_instance, vb.value_len, vb.value); + } + } + + if (node_instance.release_instance != NULL) { + node_instance.release_instance(&node_instance); + } + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else if (err == SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH) { + request->error_status = SNMP_ERR_WRONGLENGTH; + } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) { + /* malformed ASN.1, don't answer */ + return ERR_ARG; + } else { + request->error_status = SNMP_ERR_GENERROR; + } + } + + /* perform real set operation on all objects */ + if (request->error_status == SNMP_ERR_NOERROR) { + snmp_vb_enumerator_init(&request->inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len); + while (request->error_status == SNMP_ERR_NOERROR) { + err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb); + if (err == SNMP_VB_ENUMERATOR_ERR_OK) { + struct snmp_node_instance node_instance; + memset(&node_instance, 0, sizeof(node_instance)); + request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance); + if (request->error_status == SNMP_ERR_NOERROR) { + if (node_instance.set_value(&node_instance, vb.value_len, vb.value) != SNMP_ERR_NOERROR) { + if (request->inbound_varbind_enumerator.varbind_count == 1) { + request->error_status = SNMP_ERR_COMMITFAILED; + } else { + /* we cannot undo the set operations done so far */ + request->error_status = SNMP_ERR_UNDOFAILED; + } + } + + if (node_instance.release_instance != NULL) { + node_instance.release_instance(&node_instance); + } + } + } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) { + /* no more varbinds in request */ + break; + } else { + /* first time enumerating varbinds work but second time not, although nothing should have changed in between ??? */ + request->error_status = SNMP_ERR_GENERROR; + } + } + } + + return ERR_OK; +} + +#define PARSE_EXEC(code, retValue) \ + if ((code) != ERR_OK) { \ + LWIP_DEBUGF(SNMP_DEBUG, ("Malformed ASN.1 detected.\n")); \ + snmp_stats.inasnparseerrs++; \ + return retValue; \ + } + +#define PARSE_ASSERT(cond, retValue) \ + if (!(cond)) { \ + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP parse assertion failed!: " # cond)); \ + snmp_stats.inasnparseerrs++; \ + return retValue; \ + } + +#define BUILD_EXEC(code, retValue) \ + if ((code) != ERR_OK) { \ + LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound frame!: " # code)); \ + return retValue; \ + } + +#define IF_PARSE_EXEC(code) PARSE_EXEC(code, ERR_ARG) +#define IF_PARSE_ASSERT(code) PARSE_ASSERT(code, ERR_ARG) + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param request points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_VAL SNMP header is either malformed or rejected + */ +static err_t +snmp_parse_inbound_frame(struct snmp_request *request) +{ + struct snmp_pbuf_stream pbuf_stream; + struct snmp_asn1_tlv tlv; + s32_t parent_tlv_value_len; + s32_t s32_value; + err_t err; + + IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len)); + + /* decode main container consisting of version, community and PDU */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length)); + parent_tlv_value_len = tlv.value_len; + + /* decode version */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + if ((s32_value != SNMP_VERSION_1) && + (s32_value != SNMP_VERSION_2c) +#if LWIP_SNMP_V3 + && (s32_value != SNMP_VERSION_3) +#endif + ) + { + /* unsupported SNMP version */ + snmp_stats.inbadversions++; + return ERR_ARG; + } + request->version = (u8_t)s32_value; + +#if LWIP_SNMP_V3 + if (request->version == SNMP_VERSION_3) { + u16_t u16_value; + u16_t inbound_msgAuthenticationParameters_offset; + + /* SNMPv3 doesn't use communities */ + /* @todo: Differentiate read/write access */ + strcpy((char*)request->community, snmp_community); + request->community_strlen = (u16_t)strlen(snmp_community); + + /* RFC3414 globalData */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + /* decode msgID */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + request->msg_id = s32_value; + + /* decode msgMaxSize */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + request->msg_max_size = s32_value; + + /* decode msgFlags */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + request->msg_flags = (u8_t)s32_value; + + /* decode msgSecurityModel */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + request->msg_security_model = s32_value; + + /* RFC3414 msgSecurityParameters + * The User-based Security Model defines the contents of the OCTET + * STRING as a SEQUENCE. + * + * We skip the protective dummy OCTET STRING header + * to access the SEQUENCE header. + */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + /* msgSecurityParameters SEQUENCE header */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + /* decode msgAuthoritativeEngineID */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authoritative_engine_id, + &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH)); + request->msg_authoritative_engine_id_len = (u8_t)u16_value; + + /* msgAuthoritativeEngineBoots */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_boots)); + + /* msgAuthoritativeEngineTime */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time)); + /* @todo: Implement time window checking */ + + /* msgUserName */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name, + &u16_value, SNMP_V3_MAX_USER_LENGTH)); + request->msg_user_name_len = (u8_t)u16_value; + /* @todo: Implement unknown user error response */ + IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, NULL, NULL)); + + /* msgAuthenticationParameters */ + memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + /* Remember position */ + inbound_msgAuthenticationParameters_offset = pbuf_stream.offset; + LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset); + /* Read auth parameters */ + IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH); + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters, + &u16_value, tlv.value_len)); + +#if LWIP_SNMP_V3_CRYPTO + if (request->msg_flags & SNMP_V3_AUTH_FLAG) { + const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 }; + u8_t key[20]; + u8_t algo; + u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)]; + struct snmp_pbuf_stream auth_stream; + + /* Rewind stream */ + IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len)); + IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&pbuf_stream, inbound_msgAuthenticationParameters_offset)); + /* Set auth parameters to zero for verification */ + IF_PARSE_EXEC(snmp_asn1_enc_raw(&pbuf_stream, zero_arr, tlv.value_len)); + + /* Verify authentication */ + IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len)); + + IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL)); + IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, algo, hmac)); + /* @todo: Implement error response */ + IF_PARSE_EXEC(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)); + } +#else + /* Ungraceful exit if we encounter cryptography and don't support it. + * @todo: Implement error response + */ + IF_PARSE_ASSERT(!(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG))); +#endif + + /* msgPrivacyParameters */ + memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH); + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters, + &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH)); + +#if LWIP_SNMP_V3_CRYPTO + /* Decrypt message */ + if (request->msg_flags & SNMP_V3_PRIV_FLAG) { + u8_t key[20]; + u8_t algo; + + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key)); + IF_PARSE_EXEC(snmpv3_crypt(&pbuf_stream, tlv.value_len, key, + request->msg_privacy_parameters, request->msg_authoritative_engine_boots, + request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT)); + } +#endif + + /* Scoped PDU + * Encryption context + */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE); + parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + /* contextEngineID */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id, + &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH)); + request->context_engine_id_len = (u8_t)u16_value; + + /* contextName */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name, + &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH)); + request->context_name_len = (u8_t)u16_value; + } else +#endif + { + /* decode community */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN); + if (err == ERR_MEM) { + /* community string does not fit in our buffer -> its too long -> its invalid */ + request->community_strlen = 0; + snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len); + } else { + IF_PARSE_ASSERT(err == ERR_OK); + } + /* add zero terminator */ + request->community[request->community_strlen] = 0; + } + + /* decode PDU type (next container level) */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.value_len <= pbuf_stream.length); + request->inbound_padding_len = pbuf_stream.length - tlv.value_len; + parent_tlv_value_len = tlv.value_len; + + /* validate PDU type */ + switch(tlv.type) { + case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_stats.ingetrequests++; + break; + case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_stats.ingetnexts++; + break; + case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ): + /* GetBulkRequest PDU */ + if (request->version < SNMP_VERSION_2c) { + /* RFC2089: invalid, drop packet */ + return ERR_ARG; + } + break; + case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_stats.insetrequests++; + break; + default: + /* unsupported input PDU for this agent (no parse error) */ + LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \ + return ERR_ARG; + break; + } + request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK; + + /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */ + if (request->community_strlen == 0) { + /* community string was too long or really empty*/ + snmp_stats.inbadcommunitynames++; + snmp_authfail_trap(); + return ERR_ARG; + } else if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) { + if (snmp_community_write[0] == 0) { + /* our write community is empty, that means all our objects are readonly */ + request->error_status = SNMP_ERR_NOTWRITABLE; + request->error_index = 1; + } else if (strncmp(snmp_community_write, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) { + /* community name does not match */ + snmp_stats.inbadcommunitynames++; + snmp_authfail_trap(); + return ERR_ARG; + } + } else { + if (strncmp(snmp_community, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) { + /* community name does not match */ + snmp_stats.inbadcommunitynames++; + snmp_authfail_trap(); + return ERR_ARG; + } + } + + /* decode request ID */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->request_id)); + + /* decode error status / non-repeaters */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) { + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->non_repeaters)); + if (request->non_repeaters < 0) { + /* RFC 1905, 4.2.3 */ + request->non_repeaters = 0; + } + } else { + /* only check valid value, don't touch 'request->error_status', maybe a response error status was already set to above; */ + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value)); + IF_PARSE_ASSERT(s32_value == SNMP_ERR_NOERROR); + } + + /* decode error index / max-repetitions */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER); + parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv); + IF_PARSE_ASSERT(parent_tlv_value_len > 0); + + if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) { + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->max_repetitions)); + if (request->max_repetitions < 0) { + /* RFC 1905, 4.2.3 */ + request->max_repetitions = 0; + } + } else { + IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->error_index)); + IF_PARSE_ASSERT(s32_value == 0); + } + + /* decode varbind-list type (next container level) */ + IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv)); + IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= pbuf_stream.length)); + + request->inbound_varbind_offset = pbuf_stream.offset; + request->inbound_varbind_len = pbuf_stream.length - request->inbound_padding_len; + snmp_vb_enumerator_init(&(request->inbound_varbind_enumerator), request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len); + + return ERR_OK; +} + +#define OF_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG) + +static err_t +snmp_prepare_outbound_frame(struct snmp_request *request) +{ + struct snmp_asn1_tlv tlv; + struct snmp_pbuf_stream* pbuf_stream = &(request->outbound_pbuf_stream); + + /* try allocating pbuf(s) for maximum response size */ + request->outbound_pbuf = pbuf_alloc(PBUF_TRANSPORT, 1472, PBUF_RAM); + if (request->outbound_pbuf == NULL) { + return ERR_MEM; + } + + snmp_pbuf_stream_init(pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len); + + /* 'Message' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + + /* version */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(request->version, &tlv.value_len); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->version) ); + +#if LWIP_SNMP_V3 + if (request->version < SNMP_VERSION_3) { +#endif + /* community */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->community_strlen); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + OF_BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, request->community, request->community_strlen) ); +#if LWIP_SNMP_V3 + } else { + const char* id; + + /* globalData */ + request->outbound_msg_global_data_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + /* msgID */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + snmp_asn1_enc_s32t_cnt(request->msg_id, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_id)); + + /* msgMaxSize */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + snmp_asn1_enc_s32t_cnt(request->msg_max_size, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_max_size)); + + /* msgFlags */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 1); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, &request->msg_flags, 1)); + + /* msgSecurityModel */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + snmp_asn1_enc_s32t_cnt(request->msg_security_model, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_security_model)); + + /* end of msgGlobalData */ + request->outbound_msg_global_data_end = pbuf_stream->offset; + + /* msgSecurityParameters */ + request->outbound_msg_security_parameters_str_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + request->outbound_msg_security_parameters_seq_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + /* msgAuthoritativeEngineID */ + snmpv3_get_engine_id(&id, &request->msg_authoritative_engine_id_len); + MEMCPY(request->msg_authoritative_engine_id, id, request->msg_authoritative_engine_id_len); + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_authoritative_engine_id_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authoritative_engine_id, request->msg_authoritative_engine_id_len)); + + request->msg_authoritative_engine_time = snmpv3_get_engine_time(); + request->msg_authoritative_engine_boots = snmpv3_get_engine_boots(); + + /* msgAuthoritativeEngineBoots */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_boots, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_boots)); + + /* msgAuthoritativeEngineTime */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_time, &tlv.value_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_time)); + + /* msgUserName */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_user_name_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_user_name, request->msg_user_name_len)); + +#if LWIP_SNMP_V3_CRYPTO + /* msgAuthenticationParameters */ + if (request->msg_flags & SNMP_V3_AUTH_FLAG) { + memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + request->outbound_msg_authentication_parameters_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH)); + } else +#endif + { + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + } + +#if LWIP_SNMP_V3_CRYPTO + /* msgPrivacyParameters */ + if (request->msg_flags & SNMP_V3_PRIV_FLAG) { + snmpv3_build_priv_param(request->msg_privacy_parameters); + + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_privacy_parameters, SNMP_V3_MAX_PRIV_PARAM_LENGTH)); + } else +#endif + { + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + } + + /* End of msgSecurityParameters, so we can calculate the length of this sequence later */ + request->outbound_msg_security_parameters_end = pbuf_stream->offset; + +#if LWIP_SNMP_V3_CRYPTO + /* For encryption we have to encapsulate the payload in an octet string */ + if (request->msg_flags & SNMP_V3_PRIV_FLAG) { + request->outbound_scoped_pdu_string_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + } +#endif + /* Scoped PDU + * Encryption context + */ + request->outbound_scoped_pdu_seq_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + /* contextEngineID */ + snmpv3_get_engine_id(&id, &request->context_engine_id_len); + MEMCPY(request->context_engine_id, id, request->context_engine_id_len); + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_engine_id_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_engine_id, request->context_engine_id_len)); + + /* contextName */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_name_len); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_name, request->context_name_len)); + } +#endif + + /* 'PDU' sequence */ + request->outbound_pdu_offset = pbuf_stream->offset; + SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, 0); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + + /* request ID */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(request->request_id, &tlv.value_len); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->request_id) ); + + /* error status */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + request->outbound_error_status_offset = pbuf_stream->offset; + OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) ); + + /* error index */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + request->outbound_error_index_offset = pbuf_stream->offset; + OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) ); + + /* 'VarBindList' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); + + request->outbound_varbind_offset = pbuf_stream->offset; + + return ERR_OK; +} + +/** Calculate the length of a varbind list */ +err_t +snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len) +{ + /* calculate required lengths */ + snmp_asn1_enc_oid_cnt(varbind->oid.id, varbind->oid.len, &len->oid_value_len); + snmp_asn1_enc_length_cnt(len->oid_value_len, &len->oid_len_len); + + if (varbind->value_len == 0) { + len->value_value_len = 0; + } else if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) { + len->value_value_len = varbind->value_len & (~SNMP_GET_VALUE_RAW_DATA); + } else { + switch (varbind->type) { + case SNMP_ASN1_TYPE_INTEGER: + if (varbind->value_len != sizeof (s32_t)) { + return ERR_VAL; + } + snmp_asn1_enc_s32t_cnt(*((s32_t*) varbind->value), &len->value_value_len); + break; + case SNMP_ASN1_TYPE_COUNTER: + case SNMP_ASN1_TYPE_GAUGE: + case SNMP_ASN1_TYPE_TIMETICKS: + if (varbind->value_len != sizeof (u32_t)) { + return ERR_VAL; + } + snmp_asn1_enc_u32t_cnt(*((u32_t*) varbind->value), &len->value_value_len); + break; + case SNMP_ASN1_TYPE_OCTET_STRING: + case SNMP_ASN1_TYPE_IPADDR: + case SNMP_ASN1_TYPE_OPAQUE: + len->value_value_len = varbind->value_len; + break; + case SNMP_ASN1_TYPE_NULL: + if (varbind->value_len != 0) { + return ERR_VAL; + } + len->value_value_len = 0; + break; + case SNMP_ASN1_TYPE_OBJECT_ID: + if ((varbind->value_len & 0x03) != 0) { + return ERR_VAL; + } + snmp_asn1_enc_oid_cnt((u32_t*) varbind->value, varbind->value_len >> 2, &len->value_value_len); + break; + case SNMP_ASN1_TYPE_COUNTER64: + if (varbind->value_len != (2 * sizeof (u32_t))) { + return ERR_VAL; + } + snmp_asn1_enc_u64t_cnt((u32_t*) varbind->value, &len->value_value_len); + break; + default: + /* unsupported type */ + return ERR_VAL; + } + } + snmp_asn1_enc_length_cnt(len->value_value_len, &len->value_len_len); + + len->vb_value_len = 1 + len->oid_len_len + len->oid_value_len + 1 + len->value_len_len + len->value_value_len; + snmp_asn1_enc_length_cnt(len->vb_value_len, &len->vb_len_len); + + return ERR_OK; +} + +#define OVB_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG) + +err_t +snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind) +{ + struct snmp_asn1_tlv tlv; + struct snmp_varbind_len len; + err_t err; + + err = snmp_varbind_length(varbind, &len); + + if (err != ERR_OK) { + return err; + } + + /* check length already before adding first data because in case of GetBulk, + * data added so far is returned and therefore no partial data shall be added + */ + if ((1 + len.vb_len_len + len.vb_value_len) > pbuf_stream->length) { + return ERR_BUF; + } + + /* 'VarBind' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, len.vb_len_len, len.vb_value_len); + OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + /* VarBind OID */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, len.oid_len_len, len.oid_value_len); + OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, varbind->oid.id, varbind->oid.len)); + + /* VarBind value */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, varbind->type, len.value_len_len, len.value_value_len); + OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv)); + + if (len.value_value_len > 0) { + if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) { + OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t*) varbind->value, len.value_value_len)); + } else { + switch (varbind->type) { + case SNMP_ASN1_TYPE_INTEGER: + OVB_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, len.value_value_len, *((s32_t*) varbind->value))); + break; + case SNMP_ASN1_TYPE_COUNTER: + case SNMP_ASN1_TYPE_GAUGE: + case SNMP_ASN1_TYPE_TIMETICKS: + OVB_BUILD_EXEC(snmp_asn1_enc_u32t(pbuf_stream, len.value_value_len, *((u32_t*) varbind->value))); + break; + case SNMP_ASN1_TYPE_OCTET_STRING: + case SNMP_ASN1_TYPE_IPADDR: + case SNMP_ASN1_TYPE_OPAQUE: + OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t*) varbind->value, len.value_value_len)); + len.value_value_len = varbind->value_len; + break; + case SNMP_ASN1_TYPE_OBJECT_ID: + OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, (u32_t*) varbind->value, varbind->value_len / sizeof (u32_t))); + break; + case SNMP_ASN1_TYPE_COUNTER64: + OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, (u32_t*) varbind->value)); + break; + default: + LWIP_ASSERT("Unknown variable type", 0); + break; + } + } + } + + return ERR_OK; +} + +static err_t +snmp_complete_outbound_frame(struct snmp_request *request) +{ + struct snmp_asn1_tlv tlv; + u16_t frame_size; + u8_t outbound_padding = 0; + + if (request->version == SNMP_VERSION_1) { + if (request->error_status != SNMP_ERR_NOERROR) { + /* map v2c error codes to v1 compliant error code (according to RFC 2089) */ + switch (request->error_status) { + /* mapping of implementation specific "virtual" error codes + * (during processing of frame we already stored them in error_status field, + * so no need to check all varbinds here for those exceptions as suggested by RFC) */ + case SNMP_ERR_NOSUCHINSTANCE: + case SNMP_ERR_NOSUCHOBJECT: + case SNMP_ERR_ENDOFMIBVIEW: + request->error_status = SNMP_ERR_NOSUCHNAME; + break; + /* mapping according to RFC */ + case SNMP_ERR_WRONGVALUE: + case SNMP_ERR_WRONGENCODING: + case SNMP_ERR_WRONGTYPE: + case SNMP_ERR_WRONGLENGTH: + case SNMP_ERR_INCONSISTENTVALUE: + request->error_status = SNMP_ERR_BADVALUE; + break; + case SNMP_ERR_NOACCESS: + case SNMP_ERR_NOTWRITABLE: + case SNMP_ERR_NOCREATION: + case SNMP_ERR_INCONSISTENTNAME: + case SNMP_ERR_AUTHORIZATIONERROR: + request->error_status = SNMP_ERR_NOSUCHNAME; + break; + case SNMP_ERR_RESOURCEUNAVAILABLE: + case SNMP_ERR_COMMITFAILED: + case SNMP_ERR_UNDOFAILED: + default: + request->error_status = SNMP_ERR_GENERROR; + break; + } + } + } else { + if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) { + /* map error codes to according to RFC 1905 (4.2.5. The SetRequest-PDU) return 'NotWritable' for unknown OIDs) */ + switch (request->error_status) { + case SNMP_ERR_NOSUCHINSTANCE: + case SNMP_ERR_NOSUCHOBJECT: + case SNMP_ERR_ENDOFMIBVIEW: + request->error_status = SNMP_ERR_NOTWRITABLE; + break; + default: + break; + } + } + + if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) { + /* should never occur because v2 frames store exceptions directly inside varbinds and not as frame error_status */ + LWIP_DEBUGF(SNMP_DEBUG, ("snmp_complete_outbound_frame() > Found v2 request with varbind exception code stored as error status!\n")); + return ERR_ARG; + } + } + + if ((request->error_status != SNMP_ERR_NOERROR) || (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)) { + /* all inbound vars are returned in response without any modification for error responses and successful set requests*/ + struct snmp_pbuf_stream inbound_stream; + OF_BUILD_EXEC( snmp_pbuf_stream_init(&inbound_stream, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len) ); + OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, request->outbound_varbind_offset, request->outbound_pbuf->tot_len - request->outbound_varbind_offset) ); + snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0); + } + + frame_size = request->outbound_pbuf_stream.offset; + +#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO + /* Calculate padding for encryption */ + if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) { + u8_t i; + outbound_padding = (8 - (u8_t)((frame_size - request->outbound_scoped_pdu_seq_offset) & 0x07)) & 0x07; + for (i = 0; i < outbound_padding; i++) { + snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0); + } + } +#endif + + /* complete missing length in 'Message' sequence ; 'Message' tlv is located at the beginning (offset 0) */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size + outbound_padding - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */ + OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, 0, request->outbound_pbuf->tot_len) ); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) ); + +#if LWIP_SNMP_V3 + if (request->version == SNMP_VERSION_3) { + /* complete missing length in 'globalData' sequence */ + /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_global_data_end + - request->outbound_msg_global_data_offset - 1 - 1); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_global_data_offset)); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + + /* complete missing length in 'msgSecurityParameters' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, request->outbound_msg_security_parameters_end + - request->outbound_msg_security_parameters_str_offset - 1 - 1); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_str_offset)); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_security_parameters_end + - request->outbound_msg_security_parameters_seq_offset - 1 - 1); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_seq_offset)); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + + /* complete missing length in scoped PDU sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_scoped_pdu_seq_offset - 1 - 3); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_seq_offset)); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + } +#endif + + /* complete missing length in 'PDU' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, + frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */ + OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) ); + OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) ); + + /* process and encode final error status */ + if (request->error_status != 0) { + u16_t len; + snmp_asn1_enc_s32t_cnt(request->error_status, &len); + if (len != 1) { + /* error, we only reserved one byte for it */ + return ERR_ARG; + } + OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_status_offset) ); + OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_status) ); + + /* for compatibility to v1, log statistics; in v2 (RFC 1907) these statistics are obsoleted */ + switch (request->error_status) { + case SNMP_ERR_TOOBIG: + snmp_stats.outtoobigs++; + break; + case SNMP_ERR_NOSUCHNAME: + snmp_stats.outnosuchnames++; + break; + case SNMP_ERR_BADVALUE: + snmp_stats.outbadvalues++; + break; + case SNMP_ERR_GENERROR: + default: + snmp_stats.outgenerrs++; + break; + } + + if (request->error_status == SNMP_ERR_TOOBIG) { + request->error_index = 0; /* defined by RFC 1157 */ + } else if (request->error_index == 0) { + /* set index to varbind where error occured (if not already set before, e.g. during GetBulk processing) */ + request->error_index = request->inbound_varbind_enumerator.varbind_count; + } + } else { + if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) { + snmp_stats.intotalsetvars += request->inbound_varbind_enumerator.varbind_count; + } else { + snmp_stats.intotalreqvars += request->inbound_varbind_enumerator.varbind_count; + } + } + + /* encode final error index*/ + if (request->error_index != 0) { + u16_t len; + snmp_asn1_enc_s32t_cnt(request->error_index, &len); + if (len != 1) { + /* error, we only reserved one byte for it */ + return ERR_VAL; + } + OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_index_offset) ); + OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_index) ); + } + + /* complete missing length in 'VarBindList' sequence ; 'VarBindList' tlv is located directly before varbind offset */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_varbind_offset); + OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_varbind_offset - 1 - 3) ); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */ + OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) ); + + /* Authenticate response */ +#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO + /* Encrypt response */ + if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) { + u8_t key[20]; + u8_t algo; + + /* complete missing length in PDU sequence */ + OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len)); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_string_offset)); + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, frame_size + outbound_padding + - request->outbound_scoped_pdu_string_offset - 1 - 3); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv)); + + OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key)); + + OF_BUILD_EXEC(snmpv3_crypt(&request->outbound_pbuf_stream, tlv.value_len, key, + request->msg_privacy_parameters, request->msg_authoritative_engine_boots, + request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_ENCRYPT)); + } + + if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_AUTH_FLAG)) { + u8_t key[20]; + u8_t algo; + u8_t hmac[20]; + + OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL)); + OF_BUILD_EXEC(snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), + request->outbound_pbuf, 0, request->outbound_pbuf->tot_len)); + OF_BUILD_EXEC(snmpv3_auth(&request->outbound_pbuf_stream, frame_size + outbound_padding, key, algo, hmac)); + + MEMCPY(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, + request->outbound_pbuf, 0, request->outbound_pbuf->tot_len)); + OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&request->outbound_pbuf_stream, + request->outbound_msg_authentication_parameters_offset)); + + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH); + OF_BUILD_EXEC(snmp_ans1_enc_tlv(&request->outbound_pbuf_stream, &tlv)); + OF_BUILD_EXEC(snmp_asn1_enc_raw(&request->outbound_pbuf_stream, + request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH)); + } +#endif + + pbuf_realloc(request->outbound_pbuf, frame_size + outbound_padding); + + snmp_stats.outgetresponses++; + snmp_stats.outpkts++; + + return ERR_OK; +} + +static void +snmp_execute_write_callbacks(struct snmp_request *request) +{ + struct snmp_varbind_enumerator inbound_varbind_enumerator; + struct snmp_varbind vb; + + snmp_vb_enumerator_init(&inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len); + vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned, which we don't need here) */ + + while (snmp_vb_enumerator_get_next(&inbound_varbind_enumerator, &vb) == SNMP_VB_ENUMERATOR_ERR_OK) { + snmp_write_callback(vb.oid.id, vb.oid.len, snmp_write_callback_arg); + } +} + + +/* ----------------------------------------------------------------------- */ +/* VarBind enumerator methods */ +/* ----------------------------------------------------------------------- */ + +void +snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length) +{ + snmp_pbuf_stream_init(&(enumerator->pbuf_stream), p, offset, length); + enumerator->varbind_count = 0; +} + +#define VB_PARSE_EXEC(code) PARSE_EXEC(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) +#define VB_PARSE_ASSERT(code) PARSE_ASSERT(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) + +snmp_vb_enumerator_err_t +snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind) +{ + struct snmp_asn1_tlv tlv; + u16_t varbind_len; + err_t err; + + if (enumerator->pbuf_stream.length == 0) + { + return SNMP_VB_ENUMERATOR_ERR_EOVB; + } + enumerator->varbind_count++; + + /* decode varbind itself (parent container of a varbind) */ + VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv)); + VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length)); + varbind_len = tlv.value_len; + + /* decode varbind name (object id) */ + VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv)); + VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length)); + + VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN)); + varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv); + + /* decode varbind value (object id) */ + VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv)); + VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length)); + varbind->type = tlv.type; + + /* shall the value be decoded ? */ + if (varbind->value != NULL) { + switch (varbind->type) { + case SNMP_ASN1_TYPE_INTEGER: + VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t*)varbind->value)); + varbind->value_len = sizeof(s32_t*); + break; + case SNMP_ASN1_TYPE_COUNTER: + case SNMP_ASN1_TYPE_GAUGE: + case SNMP_ASN1_TYPE_TIMETICKS: + VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value)); + varbind->value_len = sizeof(u32_t*); + break; + case SNMP_ASN1_TYPE_OCTET_STRING: + case SNMP_ASN1_TYPE_OPAQUE: + err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE); + if (err == ERR_MEM) { + return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH; + } + VB_PARSE_ASSERT(err == ERR_OK); + break; + case SNMP_ASN1_TYPE_NULL: + varbind->value_len = 0; + break; + case SNMP_ASN1_TYPE_OBJECT_ID: + /* misuse tlv.length_len as OID_length transporter */ + err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN); + if (err == ERR_MEM) { + return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH; + } + VB_PARSE_ASSERT(err == ERR_OK); + varbind->value_len = tlv.length_len * sizeof(u32_t); + break; + case SNMP_ASN1_TYPE_IPADDR: + if (tlv.value_len == 4) { + /* must be exactly 4 octets! */ + VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE)); + } else { + VB_PARSE_ASSERT(0); + } + break; + case SNMP_ASN1_TYPE_COUNTER64: + VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value)); + varbind->value_len = 2 * sizeof(u32_t*); + break; + default: + VB_PARSE_ASSERT(0); + break; + } + } else { + snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len); + varbind->value_len = tlv.value_len; + } + + return SNMP_VB_ENUMERATOR_ERR_OK; +} + +#endif /* LWIP_SNMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_msg.h b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_msg.h new file mode 100644 index 0000000..2d01ef3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_msg.h @@ -0,0 +1,194 @@ +/** + * @file + * SNMP Agent message handling structures (internal API, do not use in client code). + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + * Martin Hentschel + * Elias Oenal + */ + +#ifndef LWIP_HDR_APPS_SNMP_MSG_H +#define LWIP_HDR_APPS_SNMP_MSG_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP + +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "snmp_pbuf_stream.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#if LWIP_SNMP_V3 +#include "snmpv3_priv.h" +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* The listen port of the SNMP agent. Clients have to make their requests to + this port. Most standard clients won't work if you change this! */ +#ifndef SNMP_IN_PORT +#define SNMP_IN_PORT 161 +#endif +/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't + work if you change this! */ +#ifndef SNMP_TRAP_PORT +#define SNMP_TRAP_PORT 162 +#endif + +/* version defines used in PDU */ +#define SNMP_VERSION_1 0 +#define SNMP_VERSION_2c 1 +#define SNMP_VERSION_3 3 + +struct snmp_varbind_enumerator +{ + struct snmp_pbuf_stream pbuf_stream; + u16_t varbind_count; +}; + +typedef enum { + SNMP_VB_ENUMERATOR_ERR_OK = 0, + SNMP_VB_ENUMERATOR_ERR_EOVB = 1, + SNMP_VB_ENUMERATOR_ERR_ASN1ERROR = 2, + SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH = 3 +} snmp_vb_enumerator_err_t; + +void snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length); +snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind); + +struct snmp_request +{ + /* Communication handle */ + void *handle; + /* source IP address */ + const ip_addr_t *source_ip; + /* source UDP port */ + u16_t source_port; + /* incoming snmp version */ + u8_t version; + /* community name (zero terminated) */ + u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1]; + /* community string length (exclusive zero term) */ + u16_t community_strlen; + /* request type */ + u8_t request_type; + /* request ID */ + s32_t request_id; + /* error status */ + s32_t error_status; + /* error index */ + s32_t error_index; + /* non-repeaters (getBulkRequest (SNMPv2c)) */ + s32_t non_repeaters; + /* max-repetitions (getBulkRequest (SNMPv2c)) */ + s32_t max_repetitions; + +#if LWIP_SNMP_V3 + s32_t msg_id; + s32_t msg_max_size; + u8_t msg_flags; + s32_t msg_security_model; + u8_t msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH]; + u8_t msg_authoritative_engine_id_len; + s32_t msg_authoritative_engine_boots; + s32_t msg_authoritative_engine_time; + u8_t msg_user_name[SNMP_V3_MAX_USER_LENGTH]; + u8_t msg_user_name_len; + u8_t msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH]; + u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH]; + u8_t context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH]; + u8_t context_engine_id_len; + u8_t context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH]; + u8_t context_name_len; +#endif + + struct pbuf *inbound_pbuf; + struct snmp_varbind_enumerator inbound_varbind_enumerator; + u16_t inbound_varbind_offset; + u16_t inbound_varbind_len; + u16_t inbound_padding_len; + + struct pbuf *outbound_pbuf; + struct snmp_pbuf_stream outbound_pbuf_stream; + u16_t outbound_pdu_offset; + u16_t outbound_error_status_offset; + u16_t outbound_error_index_offset; + u16_t outbound_varbind_offset; +#if LWIP_SNMP_V3 + u16_t outbound_msg_global_data_offset; + u16_t outbound_msg_global_data_end; + u16_t outbound_msg_security_parameters_str_offset; + u16_t outbound_msg_security_parameters_seq_offset; + u16_t outbound_msg_security_parameters_end; + u16_t outbound_msg_authentication_parameters_offset; + u16_t outbound_scoped_pdu_seq_offset; + u16_t outbound_scoped_pdu_string_offset; +#endif + + u8_t value_buffer[SNMP_MAX_VALUE_SIZE]; +}; + +/** A helper struct keeping length information about varbinds */ +struct snmp_varbind_len +{ + u8_t vb_len_len; + u16_t vb_value_len; + u8_t oid_len_len; + u16_t oid_value_len; + u8_t value_len_len; + u16_t value_value_len; +}; + +/** Agent community string */ +extern const char *snmp_community; +/** Agent community string for write access */ +extern const char *snmp_community_write; +/** handle for sending traps */ +extern void* snmp_traps_handle; + +void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port); +err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port); +u8_t snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result); +err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len); +err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_MSG_H */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_netconn.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_netconn.c new file mode 100644 index 0000000..24c3e26 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_netconn.c @@ -0,0 +1,121 @@ +/** + * @file + * SNMP netconn frontend. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP && SNMP_USE_NETCONN + +#include +#include "lwip/api.h" +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "snmp_msg.h" +#include "lwip/sys.h" + +/** SNMP netconn API worker thread */ +static void +snmp_netconn_thread(void *arg) +{ + struct netconn *conn; + struct netbuf *buf; + err_t err; + LWIP_UNUSED_ARG(arg); + + /* Bind to SNMP port with default IP address */ +#if LWIP_IPV6 + conn = netconn_new(NETCONN_UDP_IPV6); + netconn_bind(conn, IP6_ADDR_ANY, SNMP_IN_PORT); +#else /* LWIP_IPV6 */ + conn = netconn_new(NETCONN_UDP); + netconn_bind(conn, IP4_ADDR_ANY, SNMP_IN_PORT); +#endif /* LWIP_IPV6 */ + LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;); + + snmp_traps_handle = conn; + + do { + err = netconn_recv(conn, &buf); + + if (err == ERR_OK) { + snmp_receive(conn, buf->p, &buf->addr, buf->port); + } + + if (buf != NULL) { + netbuf_delete(buf); + } + } while(1); +} + +err_t +snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port) +{ + err_t result; + struct netbuf buf; + + memset(&buf, 0, sizeof(buf)); + buf.p = p; + result = netconn_sendto((struct netconn*)handle, &buf, dst, port); + + return result; +} + +u8_t +snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result) +{ + struct netconn* conn = (struct netconn*)handle; + struct netif *dst_if; + const ip_addr_t* dst_ip; + + LWIP_UNUSED_ARG(conn); /* unused in case of IPV4 only configuration */ + + ip_route_get_local_ip(&conn->pcb.udp->local_ip, dst, dst_if, dst_ip); + + if ((dst_if != NULL) && (dst_ip != NULL)) { + ip_addr_copy(*result, *dst_ip); + return 1; + } else { + return 0; + } +} + +/** + * Starts SNMP Agent. + */ +void +snmp_init(void) +{ + sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO); +} + +#endif /* LWIP_SNMP && SNMP_USE_NETCONN */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_pbuf_stream.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_pbuf_stream.c new file mode 100644 index 0000000..3c1217d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_pbuf_stream.c @@ -0,0 +1,156 @@ +/** + * @file + * SNMP pbuf stream wrapper implementation (internal API, do not use in client code). + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "snmp_pbuf_stream.h" +#include "lwip/def.h" +#include + +err_t +snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length) +{ + pbuf_stream->offset = offset; + pbuf_stream->length = length; + pbuf_stream->pbuf = p; + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data) +{ + if (pbuf_stream->length == 0) { + return ERR_BUF; + } + + if (pbuf_copy_partial(pbuf_stream->pbuf, data, 1, pbuf_stream->offset) == 0) { + return ERR_BUF; + } + + pbuf_stream->offset++; + pbuf_stream->length--; + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data) +{ + return snmp_pbuf_stream_writebuf(pbuf_stream, &data, 1); +} + +err_t +snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len) +{ + if (pbuf_stream->length < buf_len) { + return ERR_BUF; + } + + if (pbuf_take_at(pbuf_stream->pbuf, buf, buf_len, pbuf_stream->offset) != ERR_OK) { + return ERR_BUF; + } + + pbuf_stream->offset += buf_len; + pbuf_stream->length -= buf_len; + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len) +{ + + if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) { + return ERR_ARG; + } + if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) { + return ERR_ARG; + } + + if (len == 0) { + len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length); + } + + while (len > 0) { + u16_t chunk_len; + err_t err; + u16_t target_offset; + struct pbuf* pbuf = pbuf_skip(pbuf_stream->pbuf, pbuf_stream->offset, &target_offset); + + if ((pbuf == NULL) || (pbuf->len == 0)) { + return ERR_BUF; + } + + chunk_len = LWIP_MIN(len, pbuf->len); + err = snmp_pbuf_stream_writebuf(target_pbuf_stream, &((u8_t*)pbuf->payload)[target_offset], chunk_len); + if (err != ERR_OK) { + return err; + } + + pbuf_stream->offset += chunk_len; + pbuf_stream->length -= chunk_len; + len -= chunk_len; + } + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset) +{ + if ((offset < 0) || (offset > pbuf_stream->length)) { + /* we cannot seek backwards or forward behind stream end */ + return ERR_ARG; + } + + pbuf_stream->offset += (u16_t)offset; + pbuf_stream->length -= (u16_t)offset; + + return ERR_OK; +} + +err_t +snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset) +{ + s32_t rel_offset = offset - pbuf_stream->offset; + return snmp_pbuf_stream_seek(pbuf_stream, rel_offset); +} + +#endif /* LWIP_SNMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_pbuf_stream.h b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_pbuf_stream.h new file mode 100644 index 0000000..9778de7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_pbuf_stream.h @@ -0,0 +1,73 @@ +/** + * @file + * SNMP pbuf stream wrapper (internal API, do not use in client code). + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H +#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP + +#include "lwip/err.h" +#include "lwip/pbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct snmp_pbuf_stream +{ + struct pbuf* pbuf; + u16_t offset; + u16_t length; +}; + +err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length); +err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data); +err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data); +err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len); +err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len); +err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset); +err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_raw.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_raw.c new file mode 100644 index 0000000..4a40864 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_raw.c @@ -0,0 +1,100 @@ +/** + * @file + * SNMP RAW API frontend. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + */ + +#include "lwip/apps/snmp_opts.h" +#include "lwip/ip_addr.h" + +#if LWIP_SNMP && SNMP_USE_RAW + +#include "lwip/udp.h" +#include "lwip/ip.h" +#include "snmp_msg.h" + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + LWIP_UNUSED_ARG(arg); + + snmp_receive(pcb, p, addr, port); + + pbuf_free(p); +} + +err_t +snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port) +{ + return udp_sendto((struct udp_pcb*)handle, p, dst, port); +} + +u8_t +snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result) +{ + struct udp_pcb* udp_pcb = (struct udp_pcb*)handle; + struct netif *dst_if; + const ip_addr_t* dst_ip; + + LWIP_UNUSED_ARG(udp_pcb); /* unused in case of IPV4 only configuration */ + + ip_route_get_local_ip(&udp_pcb->local_ip, dst, dst_if, dst_ip); + + if ((dst_if != NULL) && (dst_ip != NULL)) { + ip_addr_copy(*result, *dst_ip); + return 1; + } else { + return 0; + } +} + +/** + * @ingroup snmp_core + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ANY_TYPE port 161. + */ +void +snmp_init(void) +{ + err_t err; + + struct udp_pcb *snmp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ERROR("snmp_raw: no PCB", (snmp_pcb != NULL), return;); + + snmp_traps_handle = snmp_pcb; + + udp_recv(snmp_pcb, snmp_recv, (void *)SNMP_IN_PORT); + err = udp_bind(snmp_pcb, IP_ANY_TYPE, SNMP_IN_PORT); + LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;); +} + +#endif /* LWIP_SNMP && SNMP_USE_RAW */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_scalar.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_scalar.c new file mode 100644 index 0000000..136c9ec --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_scalar.c @@ -0,0 +1,220 @@ +/** + * @file + * SNMP scalar node support implementation. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_scalar.h" +#include "lwip/apps/snmp_core.h" + +static s16_t snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value); +static snmp_err_t snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value); +static snmp_err_t snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value); + +snmp_err_t +snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + const struct snmp_scalar_node* scalar_node = (const struct snmp_scalar_node*)(const void*)instance->node; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* scalar only has one dedicated instance: .0 */ + if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + + instance->access = scalar_node->access; + instance->asn1_type = scalar_node->asn1_type; + instance->get_value = scalar_node->get_value; + instance->set_test = scalar_node->set_test; + instance->set_value = scalar_node->set_value; + return SNMP_ERR_NOERROR; +} + +snmp_err_t +snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + /* because our only instance is .0 we can only return a next instance if no instance oid is passed */ + if (instance->instance_oid.len == 0) { + instance->instance_oid.len = 1; + instance->instance_oid.id[0] = 0; + + return snmp_scalar_get_instance(root_oid, root_oid_len, instance); + } + + return SNMP_ERR_NOSUCHINSTANCE; +} + + +snmp_err_t +snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0)) { + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes; + u32_t i = 0; + + while (i < array_node->array_node_count) { + if (array_node_def->oid == instance->instance_oid.id[0]) { + break; + } + + array_node_def++; + i++; + } + + if (i < array_node->array_node_count) { + instance->access = array_node_def->access; + instance->asn1_type = array_node_def->asn1_type; + instance->get_value = snmp_scalar_array_get_value; + instance->set_test = snmp_scalar_array_set_test; + instance->set_value = snmp_scalar_array_set_value; + instance->reference.const_ptr = array_node_def; + + return SNMP_ERR_NOERROR; + } + } + + return SNMP_ERR_NOSUCHINSTANCE; +} + +snmp_err_t +snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes; + const struct snmp_scalar_array_node_def* result = NULL; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0)) { + /* return node with lowest OID */ + u16_t i = 0; + + result = array_node_def; + array_node_def++; + + for (i = 1; i < array_node->array_node_count; i++) { + if (array_node_def->oid < result->oid) { + result = array_node_def; + } + array_node_def++; + } + } else if (instance->instance_oid.len >= 1) { + if (instance->instance_oid.len == 1) { + /* if we have the requested OID we return its instance, otherwise we search for the next available */ + u16_t i = 0; + while (i < array_node->array_node_count) { + if (array_node_def->oid == instance->instance_oid.id[0]) { + result = array_node_def; + break; + } + + array_node_def++; + i++; + } + } + if (result == NULL) { + u32_t oid_dist = 0xFFFFFFFFUL; + u16_t i = 0; + array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */ + while (i < array_node->array_node_count) { + if ((array_node_def->oid > instance->instance_oid.id[0]) && + ((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist)) { + result = array_node_def; + oid_dist = array_node_def->oid - instance->instance_oid.id[0]; + } + + array_node_def++; + i++; + } + } + } + + if (result == NULL) { + /* nothing to return */ + return SNMP_ERR_NOSUCHINSTANCE; + } + + instance->instance_oid.len = 2; + instance->instance_oid.id[0] = result->oid; + instance->instance_oid.id[1] = 0; + + instance->access = result->access; + instance->asn1_type = result->asn1_type; + instance->get_value = snmp_scalar_array_get_value; + instance->set_test = snmp_scalar_array_set_test; + instance->set_value = snmp_scalar_array_set_value; + instance->reference.const_ptr = result; + + return SNMP_ERR_NOERROR; +} + +static s16_t +snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value) +{ + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr; + + return array_node->get_value(array_node_def, value); +} + +static snmp_err_t +snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value) +{ + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr; + + return array_node->set_test(array_node_def, value_len, value); +} + +static snmp_err_t +snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value) +{ + const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node; + const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr; + + return array_node->set_value(array_node_def, value_len, value); +} + +#endif /* LWIP_SNMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_table.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_table.c new file mode 100644 index 0000000..63ca595 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_table.c @@ -0,0 +1,343 @@ +/** + * @file + * SNMP table support implementation. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_core.h" +#include "lwip/apps/snmp_table.h" +#include + +snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE; + const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */ + /* fixed row entry always has oid 1 */ + if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) { + /* search column */ + const struct snmp_table_col_def* col_def = table_node->columns; + u16_t i = table_node->column_count; + while (i > 0) { + if (col_def->index == instance->instance_oid.id[1]) { + break; + } + + col_def++; + i--; + } + + if (i > 0) { + /* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */ + instance->asn1_type = col_def->asn1_type; + instance->access = col_def->access; + instance->get_value = table_node->get_value; + instance->set_test = table_node->set_test; + instance->set_value = table_node->set_value; + + ret = table_node->get_cell_instance( + &(instance->instance_oid.id[1]), + &(instance->instance_oid.id[2]), + instance->instance_oid.len-2, + instance); + } + } + + return ret; +} + +snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node; + const struct snmp_table_col_def* col_def; + struct snmp_obj_id row_oid; + u32_t column = 0; + snmp_err_t result; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* check that first part of id is 0 or 1, referencing fixed row entry */ + if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + if (instance->instance_oid.len > 1) { + column = instance->instance_oid.id[1]; + } + if (instance->instance_oid.len > 2) { + snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2); + } else { + row_oid.len = 0; + } + + instance->get_value = table_node->get_value; + instance->set_test = table_node->set_test; + instance->set_value = table_node->set_value; + + /* resolve column and value */ + do { + u16_t i; + const struct snmp_table_col_def* next_col_def = NULL; + col_def = table_node->columns; + + for (i = 0; i < table_node->column_count; i++) { + if (col_def->index == column) { + next_col_def = col_def; + break; + } else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index))) { + next_col_def = col_def; + } + col_def++; + } + + if (next_col_def == NULL) { + /* no further column found */ + return SNMP_ERR_NOSUCHINSTANCE; + } + + instance->asn1_type = next_col_def->asn1_type; + instance->access = next_col_def->access; + + result = table_node->get_next_cell_instance( + &next_col_def->index, + &row_oid, + instance); + + if (result == SNMP_ERR_NOERROR) { + col_def = next_col_def; + break; + } + + row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */ + column = next_col_def->index + 1; + } while (1); + + /* build resulting oid */ + instance->instance_oid.len = 2; + instance->instance_oid.id[0] = 1; + instance->instance_oid.id[1] = col_def->index; + snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len); + + return SNMP_ERR_NOERROR; +} + + +snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE; + const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */ + /* fixed row entry always has oid 1 */ + if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) { + ret = table_node->get_cell_value( + &(instance->instance_oid.id[1]), + &(instance->instance_oid.id[2]), + instance->instance_oid.len-2, + &instance->reference, + &instance->reference_len); + + if (ret == SNMP_ERR_NOERROR) { + /* search column */ + const struct snmp_table_simple_col_def* col_def = table_node->columns; + u32_t i = table_node->column_count; + while (i > 0) { + if (col_def->index == instance->instance_oid.id[1]) { + break; + } + + col_def++; + i--; + } + + if (i > 0) { + instance->asn1_type = col_def->asn1_type; + instance->access = SNMP_NODE_INSTANCE_READ_ONLY; + instance->set_test = NULL; + instance->set_value = NULL; + + switch (col_def->data_type) { + case SNMP_VARIANT_VALUE_TYPE_U32: + instance->get_value = snmp_table_extract_value_from_u32ref; + break; + case SNMP_VARIANT_VALUE_TYPE_S32: + instance->get_value = snmp_table_extract_value_from_s32ref; + break; + case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */ + case SNMP_VARIANT_VALUE_TYPE_CONST_PTR: + instance->get_value = snmp_table_extract_value_from_refconstptr; + break; + default: + LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type)); + return SNMP_ERR_GENERROR; + } + + ret = SNMP_ERR_NOERROR; + } else { + ret = SNMP_ERR_NOSUCHINSTANCE; + } + } + } + + return ret; +} + +snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node; + const struct snmp_table_simple_col_def* col_def; + struct snmp_obj_id row_oid; + u32_t column = 0; + snmp_err_t result; + + LWIP_UNUSED_ARG(root_oid); + LWIP_UNUSED_ARG(root_oid_len); + + /* check that first part of id is 0 or 1, referencing fixed row entry */ + if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) { + return SNMP_ERR_NOSUCHINSTANCE; + } + if (instance->instance_oid.len > 1) { + column = instance->instance_oid.id[1]; + } + if (instance->instance_oid.len > 2) { + snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2); + } else { + row_oid.len = 0; + } + + /* resolve column and value */ + do { + u32_t i; + const struct snmp_table_simple_col_def* next_col_def = NULL; + col_def = table_node->columns; + + for (i = 0; i < table_node->column_count; i++) { + if (col_def->index == column) { + next_col_def = col_def; + break; + } else if ((col_def->index > column) && ((next_col_def == NULL) || + (col_def->index < next_col_def->index))) { + next_col_def = col_def; + } + col_def++; + } + + if (next_col_def == NULL) { + /* no further column found */ + return SNMP_ERR_NOSUCHINSTANCE; + } + + result = table_node->get_next_cell_instance_and_value( + &next_col_def->index, + &row_oid, + &instance->reference, + &instance->reference_len); + + if (result == SNMP_ERR_NOERROR) { + col_def = next_col_def; + break; + } + + row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */ + column = next_col_def->index + 1; + } + while (1); + + instance->asn1_type = col_def->asn1_type; + instance->access = SNMP_NODE_INSTANCE_READ_ONLY; + instance->set_test = NULL; + instance->set_value = NULL; + + switch (col_def->data_type) { + case SNMP_VARIANT_VALUE_TYPE_U32: + instance->get_value = snmp_table_extract_value_from_u32ref; + break; + case SNMP_VARIANT_VALUE_TYPE_S32: + instance->get_value = snmp_table_extract_value_from_s32ref; + break; + case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */ + case SNMP_VARIANT_VALUE_TYPE_CONST_PTR: + instance->get_value = snmp_table_extract_value_from_refconstptr; + break; + default: + LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type)); + return SNMP_ERR_GENERROR; + } + + /* build resulting oid */ + instance->instance_oid.len = 2; + instance->instance_oid.id[0] = 1; + instance->instance_oid.id[1] = col_def->index; + snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len); + + return SNMP_ERR_NOERROR; +} + + +s16_t +snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value) +{ + s32_t *dst = (s32_t*)value; + *dst = instance->reference.s32; + return sizeof(*dst); +} + +s16_t +snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value) +{ + u32_t *dst = (u32_t*)value; + *dst = instance->reference.u32; + return sizeof(*dst); +} + +s16_t +snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value) +{ + MEMCPY(value, instance->reference.const_ptr, instance->reference_len); + return (u16_t)instance->reference_len; +} + +#endif /* LWIP_SNMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_threadsync.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_threadsync.c new file mode 100644 index 0000000..204f265 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_threadsync.c @@ -0,0 +1,219 @@ +/** + * @file + * SNMP thread synchronization implementation. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dirk Ziegelmeier + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_threadsync.h" +#include "lwip/apps/snmp_core.h" +#include "lwip/sys.h" +#include + +static void +call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn) +{ + sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex); + call_data->threadsync_node->instance->sync_fn(fn, call_data); + sys_sem_wait(&call_data->threadsync_node->instance->sem); + sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex); +} + +static void +threadsync_get_value_synced(void *ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + + call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static s16_t +threadsync_get_value(struct snmp_node_instance* instance, void* value) +{ + struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr; + + call_data->arg1.value = value; + call_synced_function(call_data, threadsync_get_value_synced); + + return call_data->retval.s16; +} + +static void +threadsync_set_test_synced(void *ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + + call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static snmp_err_t +threadsync_set_test(struct snmp_node_instance* instance, u16_t len, void *value) +{ + struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr; + + call_data->arg1.value = value; + call_data->arg2.len = len; + call_synced_function(call_data, threadsync_set_test_synced); + + return call_data->retval.err; +} + +static void +threadsync_set_value_synced(void *ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + + call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static snmp_err_t +threadsync_set_value(struct snmp_node_instance* instance, u16_t len, void *value) +{ + struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr; + + call_data->arg1.value = value; + call_data->arg2.len = len; + call_synced_function(call_data, threadsync_set_value_synced); + + return call_data->retval.err; +} + +static void +threadsync_release_instance_synced(void* ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + + call_data->proxy_instance.release_instance(&call_data->proxy_instance); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static void +threadsync_release_instance(struct snmp_node_instance *instance) +{ + struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr; + + if (call_data->proxy_instance.release_instance != NULL) { + call_synced_function(call_data, threadsync_release_instance_synced); + } +} + +static void +get_instance_synced(void* ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node; + + call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static void +get_next_instance_synced(void* ctx) +{ + struct threadsync_data *call_data = (struct threadsync_data*)ctx; + const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node; + + call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance); + + sys_sem_signal(&call_data->threadsync_node->instance->sem); +} + +static snmp_err_t +do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance, snmp_threadsync_called_fn fn) +{ + const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node*)(const void*)instance->node; + struct threadsync_data *call_data = &threadsync_node->instance->data; + + if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) { + LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID")); + return SNMP_ERR_NOSUCHINSTANCE; + } + + memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance)); + + instance->reference.ptr = call_data; + snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len); + + call_data->proxy_instance.node = &threadsync_node->target->node; + call_data->threadsync_node = threadsync_node; + + call_data->arg1.root_oid = root_oid; + call_data->arg2.root_oid_len = root_oid_len; + call_synced_function(call_data, fn); + + if (call_data->retval.err == SNMP_ERR_NOERROR) { + instance->access = call_data->proxy_instance.access; + instance->asn1_type = call_data->proxy_instance.asn1_type; + instance->release_instance = threadsync_release_instance; + instance->get_value = (call_data->proxy_instance.get_value != NULL)? threadsync_get_value : NULL; + instance->set_value = (call_data->proxy_instance.set_value != NULL)? threadsync_set_value : NULL; + instance->set_test = (call_data->proxy_instance.set_test != NULL)? threadsync_set_test : NULL; + snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len); + } + + return call_data->retval.err; +} + +snmp_err_t +snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + return do_sync(root_oid, root_oid_len, instance, get_instance_synced); +} + +snmp_err_t +snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance) +{ + return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced); +} + +/** Initializes thread synchronization instance */ +void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn) +{ + err_t err = sys_mutex_new(&instance->sem_usage_mutex); + LWIP_ASSERT("Failed to set up mutex", err == ERR_OK); + err = sys_sem_new(&instance->sem, 0); + LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK); + instance->sync_fn = sync_fn; +} + +#endif /* LWIP_SNMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_traps.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_traps.c new file mode 100644 index 0000000..0d2df64 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmp_traps.c @@ -0,0 +1,445 @@ +/** + * @file + * SNMPv1 traps implementation. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * Christiaan Simons + * + */ + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include + +#include "lwip/snmp.h" +#include "lwip/sys.h" +#include "lwip/apps/snmp.h" +#include "lwip/apps/snmp_core.h" +#include "snmp_msg.h" +#include "snmp_asn1.h" +#include "snmp_core_priv.h" + +struct snmp_msg_trap +{ + /* source enterprise ID (sysObjectID) */ + const struct snmp_obj_id *enterprise; + /* source IP address, raw network order format */ + ip_addr_t sip; + /* generic trap code */ + u32_t gen_trap; + /* specific trap code */ + u32_t spc_trap; + /* timestamp */ + u32_t ts; + /* snmp_version */ + u32_t snmp_version; + + /* output trap lengths used in ASN encoding */ + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding sequence length */ + u16_t seqlen; + /* encoding varbinds sequence length */ + u16_t vbseqlen; +}; + +static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len); +static void snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream); +static void snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds); + +/** Agent community string for sending traps */ +extern const char *snmp_community_trap; + +void* snmp_traps_handle; + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + ip_addr_t dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +static u8_t snmp_auth_traps_enabled = 0; + +/** + * @ingroup snmp_traps + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * @ingroup snmp_traps + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) { + ip_addr_set(&trap_dst[dst_idx].dip, dst); + } +} + +/** + * @ingroup snmp_traps + * Enable/disable authentication traps + */ +void +snmp_set_auth_traps_enabled(u8_t enable) +{ + snmp_auth_traps_enabled = enable; +} + +/** + * @ingroup snmp_traps + * Get authentication traps enabled state + */ +u8_t +snmp_get_auth_traps_enabled(void) +{ + return snmp_auth_traps_enabled; +} + + +/** + * @ingroup snmp_traps + * Sends a generic or enterprise specific trap message. + * + * @param eoid points to enterprise object identifier + * @param generic_trap is the trap code + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @param varbinds linked list of varbinds to be sent + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the use of the enterprise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(const struct snmp_obj_id* eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds) +{ + struct snmp_msg_trap trap_msg; + struct snmp_trap_dst *td; + struct pbuf *p; + u16_t i, tot_len; + err_t err = ERR_OK; + + trap_msg.snmp_version = 0; + + for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) { + if ((td->enable != 0) && !ip_addr_isany(&td->dip)) { + /* lookup current source address for this dst */ + if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) { + if (eoid == NULL) { + trap_msg.enterprise = snmp_get_device_enterprise_oid(); + } else { + trap_msg.enterprise = eoid; + } + + trap_msg.gen_trap = generic_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) { + trap_msg.spc_trap = specific_trap; + } else { + trap_msg.spc_trap = 0; + } + + MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM); + if (p != NULL) { + struct snmp_pbuf_stream pbuf_stream; + snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len); + + /* pass 1, encode packet ino the pbuf(s) */ + snmp_trap_header_enc(&trap_msg, &pbuf_stream); + snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds); + + snmp_stats.outtraps++; + snmp_stats.outpkts++; + + /** send to the TRAP destination */ + snmp_sendto(snmp_traps_handle, p, &td->dip, SNMP_TRAP_PORT); + pbuf_free(p); + } else { + err = ERR_MEM; + } + } else { + /* routing error */ + err = ERR_RTE; + } + } + } + return err; +} + +/** + * @ingroup snmp_traps + * Send generic SNMP trap + */ +err_t +snmp_send_trap_generic(s32_t generic_trap) +{ + static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } }; + return snmp_send_trap(&oid, generic_trap, 0, NULL); +} + +/** + * @ingroup snmp_traps + * Send specific SNMP trap with variable bindings + */ +err_t +snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds) +{ + return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds); +} + +/** + * @ingroup snmp_traps + * Send coldstart trap + */ +void +snmp_coldstart_trap(void) +{ + snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART); +} + +/** + * @ingroup snmp_traps + * Send authentication failure trap (used internally by agent) + */ +void +snmp_authfail_trap(void) +{ + if (snmp_auth_traps_enabled != 0) { + snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE); + } +} + +static u16_t +snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds) +{ + struct snmp_varbind *varbind; + u16_t tot_len; + u8_t tot_len_len; + + tot_len = 0; + varbind = varbinds; + while (varbind != NULL) { + struct snmp_varbind_len len; + + if (snmp_varbind_length(varbind, &len) == ERR_OK) { + tot_len += 1 + len.vb_len_len + len.vb_value_len; + } + + varbind = varbind->next; + } + + trap->vbseqlen = tot_len; + snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len); + tot_len += 1 + tot_len_len; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param trap Trap message + * @param vb_len varbind-list length + * @return the required length for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len) +{ + u16_t tot_len; + u16_t len; + u8_t lenlen; + + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(trap->ts, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + if (IP_IS_V6_VAL(trap->sip)) { +#if LWIP_IPV6 + len = sizeof(ip_2_ip6(&trap->sip)->addr); +#endif + } else { +#if LWIP_IPV4 + len = sizeof(ip_2_ip4(&trap->sip)->addr); +#endif + } + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + trap->pdulen = tot_len; + snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen); + tot_len += 1 + lenlen; + + trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF); + snmp_asn1_enc_length_cnt(trap->comlen, &lenlen); + tot_len += 1 + lenlen + trap->comlen; + + snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len); + snmp_asn1_enc_length_cnt(len, &lenlen); + tot_len += 1 + len + lenlen; + + trap->seqlen = tot_len; + snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen); + tot_len += 1 + lenlen; + + return tot_len; +} + +static void +snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds) +{ + struct snmp_asn1_tlv tlv; + struct snmp_varbind *varbind; + + varbind = varbinds; + + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + + while (varbind != NULL) { + snmp_append_outbound_varbind(pbuf_stream, varbind); + + varbind = varbind->next; + } +} + +/** + * Encodes trap header from head to tail. + */ +static void +snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream) +{ + struct snmp_asn1_tlv tlv; + + /* 'Message' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + + /* version */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version); + + /* community */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen); + + /* 'PDU' sequence */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + + /* object ID */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0); + snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len); + + /* IP addr */ + if (IP_IS_V6_VAL(trap->sip)) { +#if LWIP_IPV6 + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr)); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)); +#endif + } else { +#if LWIP_IPV4 + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr)); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)); +#endif + } + + /* trap length */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap); + + /* specific trap */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); + snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap); + + /* timestamp */ + SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0); + snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len); + snmp_ans1_enc_tlv(pbuf_stream, &tlv); + snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts); +} + +#endif /* LWIP_SNMP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3.c new file mode 100644 index 0000000..69fb3a0 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3.c @@ -0,0 +1,136 @@ +/** + * @file + * Additional SNMPv3 functionality RFC3414 and RFC3826. + */ + +/* + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal + */ + +#include "snmpv3_priv.h" +#include "lwip/apps/snmpv3.h" +#include "lwip/sys.h" +#include + +#if LWIP_SNMP && LWIP_SNMP_V3 + +#ifdef LWIP_SNMPV3_INCLUDE_ENGINE +#include LWIP_SNMPV3_INCLUDE_ENGINE +#endif + +#define SNMP_MAX_TIME_BOOT 2147483647UL + +/** Call this if engine has been changed. Has to reset boots, see below */ +void +snmpv3_engine_id_changed(void) +{ + snmpv3_set_engine_boots(0); +} + +/** According to RFC3414 2.2.2. + * + * The number of times that the SNMP engine has + * (re-)initialized itself since snmpEngineID + * was last configured. + */ +u32_t +snmpv3_get_engine_boots_internal(void) +{ + if (snmpv3_get_engine_boots() == 0 || + snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) { + return snmpv3_get_engine_boots(); + } + + snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT); + return snmpv3_get_engine_boots(); +} + +/** RFC3414 2.2.2. + * + * Once the timer reaches 2147483647 it gets reset to zero and the + * engine boot ups get incremented. + */ +u32_t +snmpv3_get_engine_time_internal(void) +{ + if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) { + snmpv3_reset_engine_time(); + + if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) { + snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1); + } else { + snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT); + } + } + + return snmpv3_get_engine_time(); +} + +#if LWIP_SNMP_V3_CRYPTO + +/* This function ignores the byte order suggestion in RFC3414 + * since it simply doesn't influence the effectiveness of an IV. + * + * Implementing RFC3826 priv param algorithm if LWIP_RAND is available. + * + * @todo: This is a potential thread safety issue. + */ +err_t +snmpv3_build_priv_param(u8_t* priv_param) +{ +#ifdef LWIP_RAND /* Based on RFC3826 */ + static u8_t init; + static u32_t priv1, priv2; + + /* Lazy initialisation */ + if (init == 0) { + init = 1; + priv1 = LWIP_RAND(); + priv2 = LWIP_RAND(); + } + + SMEMCPY(&priv_param[0], &priv1, sizeof(priv1)); + SMEMCPY(&priv_param[4], &priv2, sizeof(priv2)); + + /* Emulate 64bit increment */ + priv1++; + if (!priv1) { /* Overflow */ + priv2++; + } +#else /* Based on RFC3414 */ + static u32_t ctr; + u32_t boots = LWIP_SNMPV3_GET_ENGINE_BOOTS(); + SMEMCPY(&priv_param[0], &boots, 4); + SMEMCPY(&priv_param[4], &ctr, 4); + ctr++; +#endif + return ERR_OK; +} +#endif /* LWIP_SNMP_V3_CRYPTO */ + +#endif diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_dummy.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_dummy.c new file mode 100644 index 0000000..bdfe844 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_dummy.c @@ -0,0 +1,145 @@ +/** + * @file + * Dummy SNMPv3 functions. + */ + +/* + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal + * Dirk Ziegelmeier + */ + +#include "lwip/apps/snmpv3.h" +#include "snmpv3_priv.h" +#include +#include "lwip/err.h" + +#if LWIP_SNMP && LWIP_SNMP_V3 + +/** + * @param username is a pointer to a string. + * @param auth_algo is a pointer to u8_t. The implementation has to set this if user was found. + * @param auth_key is a pointer to a pointer to a string. Implementation has to set this if user was found. + * @param priv_algo is a pointer to u8_t. The implementation has to set this if user was found. + * @param priv_key is a pointer to a pointer to a string. Implementation has to set this if user was found. + */ +err_t +snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key) +{ + const char* engine_id; + u8_t engine_id_len; + + if(strlen(username) == 0) { + return ERR_OK; + } + + if(memcmp(username, "lwip", 4) != 0) { + return ERR_VAL; + } + + snmpv3_get_engine_id(&engine_id, &engine_id_len); + + if(auth_key != NULL) { + snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10, + (const u8_t*)engine_id, engine_id_len, + auth_key); + *auth_algo = SNMP_V3_AUTH_ALGO_SHA; + } + if(priv_key != NULL) { + snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10, + (const u8_t*)engine_id, engine_id_len, + priv_key); + *priv_algo = SNMP_V3_PRIV_ALGO_DES; + } + return ERR_OK; +} + +/** + * Get engine ID from persistence + * @param id + * @param len + */ +void +snmpv3_get_engine_id(const char **id, u8_t *len) +{ + *id = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"; + *len = 12; +} + +/** + * Store engine ID in persistence + * @param id + * @param len + */ +err_t +snmpv3_set_engine_id(const char *id, u8_t len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(len); + return ERR_OK; +} + +/** + * Get engine boots from persistence. Must be increased on each boot. + * @return + */ +u32_t +snmpv3_get_engine_boots(void) +{ + return 0; +} + +/** + * Store engine boots in persistence + * @param boots + */ +void +snmpv3_set_engine_boots(u32_t boots) +{ + LWIP_UNUSED_ARG(boots); +} + +/** + * RFC3414 2.2.2. + * Once the timer reaches 2147483647 it gets reset to zero and the + * engine boot ups get incremented. + */ +u32_t +snmpv3_get_engine_time(void) +{ + return 0; +} + +/** + * Reset current engine time to 0 + */ +void +snmpv3_reset_engine_time(void) +{ +} + +#endif /* LWIP_SNMP && LWIP_SNMP_V3 */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_mbedtls.c b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_mbedtls.c new file mode 100644 index 0000000..0b1eefb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_mbedtls.c @@ -0,0 +1,331 @@ +/** + * @file + * SNMPv3 crypto/auth functions implemented for ARM mbedtls. + */ + +/* + * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal + * Dirk Ziegelmeier + */ + +#include "lwip/apps/snmpv3.h" +#include "snmpv3_priv.h" +#include "lwip/arch.h" +#include "snmp_msg.h" +#include "lwip/sys.h" +#include + +#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS + +#include "mbedtls/md.h" +#include "mbedtls/cipher.h" + +#include "mbedtls/md5.h" +#include "mbedtls/sha1.h" + +err_t +snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, + const u8_t* key, u8_t algo, u8_t* hmac_out) +{ + u32_t i; + u8_t key_len; + const mbedtls_md_info_t *md_info; + mbedtls_md_context_t ctx; + struct snmp_pbuf_stream read_stream; + snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length); + + if (algo == SNMP_V3_AUTH_ALGO_MD5) { + md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); + key_len = SNMP_V3_MD5_LEN; + } else if (algo == SNMP_V3_AUTH_ALGO_SHA) { + md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + key_len = SNMP_V3_SHA_LEN; + } else { + return ERR_ARG; + } + + mbedtls_md_init(&ctx); + if(mbedtls_md_setup(&ctx, md_info, 1) != 0) { + return ERR_ARG; + } + + if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) { + goto free_md; + } + + for (i = 0; i < length; i++) { + u8_t byte; + + if (snmp_pbuf_stream_read(&read_stream, &byte)) { + goto free_md; + } + + if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) { + goto free_md; + } + } + + if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) { + goto free_md; + } + + mbedtls_md_free(&ctx); + return ERR_OK; + +free_md: + mbedtls_md_free(&ctx); + return ERR_ARG; +} + +#if LWIP_SNMP_V3_CRYPTO + +err_t +snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, + const u8_t* key, const u8_t* priv_param, const u32_t engine_boots, + const u32_t engine_time, u8_t algo, u8_t mode) +{ + size_t i; + mbedtls_cipher_context_t ctx; + const mbedtls_cipher_info_t *cipher_info; + + struct snmp_pbuf_stream read_stream; + struct snmp_pbuf_stream write_stream; + snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length); + snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length); + mbedtls_cipher_init(&ctx); + + if (algo == SNMP_V3_PRIV_ALGO_DES) { + u8_t iv_local[8]; + u8_t out_bytes[8]; + size_t out_len; + + /* RFC 3414 mandates padding for DES */ + if ((length & 0x07) != 0) { + return ERR_ARG; + } + + cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC); + if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) { + return ERR_ARG; + } + if(mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) { + return ERR_ARG; + } + if(mbedtls_cipher_setkey(&ctx, key, 8*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { + goto error; + } + + /* Prepare IV */ + for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) { + iv_local[i] = priv_param[i] ^ key[i + 8]; + } + if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { + goto error; + } + + for (i = 0; i < length; i += 8) { + size_t j; + u8_t in_bytes[8]; + out_len = LWIP_ARRAYSIZE(out_bytes) ; + + for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) { + snmp_pbuf_stream_read(&read_stream, &in_bytes[j]); + } + + if(mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) { + goto error; + } + + snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len); + } + + out_len = LWIP_ARRAYSIZE(out_bytes); + if(mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) { + goto error; + } + snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len); + } else if (algo == SNMP_V3_PRIV_ALGO_AES) { + u8_t iv_local[16]; + + cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128); + if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) { + return ERR_ARG; + } + if(mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { + goto error; + } + + /* + * IV is the big endian concatenation of boots, + * uptime and priv param - see RFC3826. + */ + iv_local[0 + 0] = (engine_boots >> 24) & 0xFF; + iv_local[0 + 1] = (engine_boots >> 16) & 0xFF; + iv_local[0 + 2] = (engine_boots >> 8) & 0xFF; + iv_local[0 + 3] = (engine_boots >> 0) & 0xFF; + iv_local[4 + 0] = (engine_time >> 24) & 0xFF; + iv_local[4 + 1] = (engine_time >> 16) & 0xFF; + iv_local[4 + 2] = (engine_time >> 8) & 0xFF; + iv_local[4 + 3] = (engine_time >> 0) & 0xFF; + SMEMCPY(iv_local + 8, priv_param, 8); + if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { + goto error; + } + + for (i = 0; i < length; i++) { + u8_t in_byte; + u8_t out_byte; + size_t out_len = sizeof(out_byte); + + snmp_pbuf_stream_read(&read_stream, &in_byte); + if(mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) { + goto error; + } + snmp_pbuf_stream_write(&write_stream, out_byte); + } + } else { + return ERR_ARG; + } + + mbedtls_cipher_free(&ctx); + return ERR_OK; + +error: + mbedtls_cipher_free(&ctx); + return ERR_OK; +} + +#endif /* LWIP_SNMP_V3_CRYPTO */ + +/* A.2.1. Password to Key Sample Code for MD5 */ +void +snmpv3_password_to_key_md5( + const u8_t *password, /* IN */ + u8_t passwordlen, /* IN */ + const u8_t *engineID, /* IN - pointer to snmpEngineID */ + u8_t engineLength,/* IN - length of snmpEngineID */ + u8_t *key) /* OUT - pointer to caller 16-octet buffer */ +{ + mbedtls_md5_context MD; + u8_t *cp, password_buf[64]; + u32_t password_index = 0; + u8_t i; + u32_t count = 0; + + mbedtls_md5_init(&MD); /* initialize MD5 */ + mbedtls_md5_starts(&MD); + + /**********************************************/ + /* Use while loop until we've done 1 Megabyte */ + /**********************************************/ + while (count < 1048576) { + cp = password_buf; + for (i = 0; i < 64; i++) { + /*************************************************/ + /* Take the next octet of the password, wrapping */ + /* to the beginning of the password as necessary.*/ + /*************************************************/ + *cp++ = password[password_index++ % passwordlen]; + } + mbedtls_md5_update(&MD, password_buf, 64); + count += 64; + } + mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */ + + /*****************************************************/ + /* Now localize the key with the engineID and pass */ + /* through MD5 to produce final key */ + /* May want to ensure that engineLength <= 32, */ + /* otherwise need to use a buffer larger than 64 */ + /*****************************************************/ + SMEMCPY(password_buf, key, 16); + MEMCPY(password_buf + 16, engineID, engineLength); + SMEMCPY(password_buf + 16 + engineLength, key, 16); + + mbedtls_md5_starts(&MD); + mbedtls_md5_update(&MD, password_buf, 32 + engineLength); + mbedtls_md5_finish(&MD, key); + + mbedtls_md5_free(&MD); + return; +} + +/* A.2.2. Password to Key Sample Code for SHA */ +void +snmpv3_password_to_key_sha( + const u8_t *password, /* IN */ + u8_t passwordlen, /* IN */ + const u8_t *engineID, /* IN - pointer to snmpEngineID */ + u8_t engineLength,/* IN - length of snmpEngineID */ + u8_t *key) /* OUT - pointer to caller 20-octet buffer */ +{ + mbedtls_sha1_context SH; + u8_t *cp, password_buf[72]; + u32_t password_index = 0; + u8_t i; + u32_t count = 0; + + mbedtls_sha1_init(&SH); /* initialize SHA */ + mbedtls_sha1_starts(&SH); + + /**********************************************/ + /* Use while loop until we've done 1 Megabyte */ + /**********************************************/ + while (count < 1048576) { + cp = password_buf; + for (i = 0; i < 64; i++) { + /*************************************************/ + /* Take the next octet of the password, wrapping */ + /* to the beginning of the password as necessary.*/ + /*************************************************/ + *cp++ = password[password_index++ % passwordlen]; + } + mbedtls_sha1_update(&SH, password_buf, 64); + count += 64; + } + mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */ + + /*****************************************************/ + /* Now localize the key with the engineID and pass */ + /* through SHA to produce final key */ + /* May want to ensure that engineLength <= 32, */ + /* otherwise need to use a buffer larger than 72 */ + /*****************************************************/ + SMEMCPY(password_buf, key, 20); + MEMCPY(password_buf + 20, engineID, engineLength); + SMEMCPY(password_buf + 20 + engineLength, key, 20); + + mbedtls_sha1_starts(&SH); + mbedtls_sha1_update(&SH, password_buf, 40 + engineLength); + mbedtls_sha1_finish(&SH, key); + + mbedtls_sha1_free(&SH); + return; +} + +#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_priv.h b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_priv.h new file mode 100644 index 0000000..b87666d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/snmp/snmpv3_priv.h @@ -0,0 +1,66 @@ +/** + * @file + * Additional SNMPv3 functionality RFC3414 and RFC3826 (internal API, do not use in client code). + */ + +/* + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal + */ + +#ifndef LWIP_HDR_APPS_SNMP_V3_PRIV_H +#define LWIP_HDR_APPS_SNMP_V3_PRIV_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP && LWIP_SNMP_V3 + +#include "snmp_pbuf_stream.h" + +/* According to RFC 3411 */ +#define SNMP_V3_MAX_ENGINE_ID_LENGTH 32 +#define SNMP_V3_MAX_USER_LENGTH 32 + +#define SNMP_V3_MAX_AUTH_PARAM_LENGTH 12 +#define SNMP_V3_MAX_PRIV_PARAM_LENGTH 8 + +#define SNMP_V3_AUTH_FLAG 0x01 +#define SNMP_V3_PRIV_FLAG 0x02 + +#define SNMP_V3_MD5_LEN 16 +#define SNMP_V3_SHA_LEN 20 + +u32_t snmpv3_get_engine_boots_internal(void); +u32_t snmpv3_get_engine_time_internal(void); +err_t snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, u8_t algo, u8_t* hmac_out); +err_t snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, + const u8_t* priv_param, const u32_t engine_boots, const u32_t engine_time, u8_t algo, u8_t mode); +err_t snmpv3_build_priv_param(u8_t* priv_param); + +#endif + +#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/sntp/sntp.c b/sdk/Src/Network/lwip2.0.3/apps/sntp/sntp.c new file mode 100644 index 0000000..71b2abe --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/sntp/sntp.c @@ -0,0 +1,727 @@ +/** + * @file + * SNTP client module + */ + +/* + * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Frédéric Bernon, Simon Goldschmidt + */ + + +/** + * @defgroup sntp SNTP + * @ingroup apps + * + * This is simple "SNTP" client for the lwIP raw API. + * It is a minimal implementation of SNTPv4 as specified in RFC 4330. + * + * For a list of some public NTP servers, see this link : + * http://support.ntp.org/bin/view/Servers/NTPPoolServers + * + * @todo: + * - set/change servers at runtime + * - complete SNTP_CHECK_RESPONSE checks 3 and 4 + */ + +#include "lwip/apps/sntp.h" + +#include "lwip/opt.h" +#include "lwip/timeouts.h" +#include "lwip/udp.h" +#include "lwip/dns.h" +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" +#include "lwip/dhcp.h" + +#include +#include + +#if LWIP_UDP + +/* Handle support for more than one server via SNTP_MAX_SERVERS */ +#if SNTP_MAX_SERVERS > 1 +#define SNTP_SUPPORT_MULTIPLE_SERVERS 1 +#else /* NTP_MAX_SERVERS > 1 */ +#define SNTP_SUPPORT_MULTIPLE_SERVERS 0 +#endif /* NTP_MAX_SERVERS > 1 */ + +#if (SNTP_UPDATE_DELAY < 15000) && !defined(SNTP_SUPPRESS_DELAY_CHECK) +#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!" +#endif + +/* Configure behaviour depending on microsecond or second precision */ +#ifdef SNTP_SET_SYSTEM_TIME_US +#define SNTP_CALC_TIME_US 1 +#define SNTP_RECEIVE_TIME_SIZE 2 +#else +#define SNTP_SET_SYSTEM_TIME_US(sec, us) +#define SNTP_CALC_TIME_US 0 +#define SNTP_RECEIVE_TIME_SIZE 1 +#endif + + +/* the various debug levels for this file */ +#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE) +#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE) +#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) +#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS) + +#define SNTP_ERR_KOD 1 + +/* SNTP protocol defines */ +#define SNTP_MSG_LEN 48 + +#define SNTP_OFFSET_LI_VN_MODE 0 +#define SNTP_LI_MASK 0xC0 +#define SNTP_LI_NO_WARNING 0x00 +#define SNTP_LI_LAST_MINUTE_61_SEC 0x01 +#define SNTP_LI_LAST_MINUTE_59_SEC 0x02 +#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */ + +#define SNTP_VERSION_MASK 0x38 +#define SNTP_VERSION (4/* NTP Version 4*/<<3) + +#define SNTP_MODE_MASK 0x07 +#define SNTP_MODE_CLIENT 0x03 +#define SNTP_MODE_SERVER 0x04 +#define SNTP_MODE_BROADCAST 0x05 + +#define SNTP_OFFSET_STRATUM 1 +#define SNTP_STRATUM_KOD 0x00 + +#define SNTP_OFFSET_ORIGINATE_TIME 24 +#define SNTP_OFFSET_RECEIVE_TIME 32 +#define SNTP_OFFSET_TRANSMIT_TIME 40 + +/* number of seconds between 1900 and 1970 (MSB=1)*/ +#define DIFF_SEC_1900_1970 (2208988800UL) +/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */ +#define DIFF_SEC_1970_2036 (2085978496UL) + +/** + * SNTP packet format (without optional fields) + * Timestamps are coded as 64 bits: + * - 32 bits seconds since Jan 01, 1970, 00:00 + * - 32 bits seconds fraction (0-padded) + * For future use, if the MSB in the seconds part is set, seconds are based + * on Feb 07, 2036, 06:28:16. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct sntp_msg { + PACK_STRUCT_FLD_8(u8_t li_vn_mode); + PACK_STRUCT_FLD_8(u8_t stratum); + PACK_STRUCT_FLD_8(u8_t poll); + PACK_STRUCT_FLD_8(u8_t precision); + PACK_STRUCT_FIELD(u32_t root_delay); + PACK_STRUCT_FIELD(u32_t root_dispersion); + PACK_STRUCT_FIELD(u32_t reference_identifier); + PACK_STRUCT_FIELD(u32_t reference_timestamp[2]); + PACK_STRUCT_FIELD(u32_t originate_timestamp[2]); + PACK_STRUCT_FIELD(u32_t receive_timestamp[2]); + PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* function prototypes */ +static void sntp_request(void *arg); + +/** The operating mode */ +static u8_t sntp_opmode; + +/** The UDP pcb used by the SNTP client */ +static struct udp_pcb* sntp_pcb; +/** Names/Addresses of servers */ +struct sntp_server { +#if SNTP_SERVER_DNS + char* name; +#endif /* SNTP_SERVER_DNS */ + ip_addr_t addr; +}; +static struct sntp_server sntp_servers[SNTP_MAX_SERVERS]; + +#if SNTP_GET_SERVERS_FROM_DHCP +static u8_t sntp_set_servers_from_dhcp; +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ +#if SNTP_SUPPORT_MULTIPLE_SERVERS +/** The currently used server (initialized to 0) */ +static u8_t sntp_current_server; +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +#define sntp_current_server 0 +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + +#if SNTP_RETRY_TIMEOUT_EXP +#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT +/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */ +static u32_t sntp_retry_timeout; +#else /* SNTP_RETRY_TIMEOUT_EXP */ +#define SNTP_RESET_RETRY_TIMEOUT() +#define sntp_retry_timeout SNTP_RETRY_TIMEOUT +#endif /* SNTP_RETRY_TIMEOUT_EXP */ + +#if SNTP_CHECK_RESPONSE >= 1 +/** Saves the last server address to compare with response */ +static ip_addr_t sntp_last_server_address; +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + +#if SNTP_CHECK_RESPONSE >= 2 +/** Saves the last timestamp sent (which is sent back by the server) + * to compare against in response */ +static u32_t sntp_last_timestamp_sent[2]; +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + +/** + * SNTP processing of received timestamp + */ +static void +sntp_process(u32_t *receive_timestamp) +{ + /* convert SNTP time (1900-based) to unix GMT time (1970-based) + * if MSB is 0, SNTP time is 2036-based! + */ + u32_t rx_secs = lwip_ntohl(receive_timestamp[0]); + int is_1900_based = ((rx_secs & 0x80000000) != 0); + u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036); + time_t tim = t; + +#if SNTP_CALC_TIME_US + u32_t us = lwip_ntohl(receive_timestamp[1]) / 4295; + SNTP_SET_SYSTEM_TIME_US(t, us); + /* display local time from GMT time */ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&tim), us)); + +#else /* SNTP_CALC_TIME_US */ + + /* change system time and/or the update the RTC clock */ + SNTP_SET_SYSTEM_TIME(t); + /* display local time from GMT time */ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim))); +#endif /* SNTP_CALC_TIME_US */ + LWIP_UNUSED_ARG(tim); +} + +/** + * Initialize request struct to be sent to server. + */ +static void +sntp_initialize_request(struct sntp_msg *req) +{ + memset(req, 0, SNTP_MSG_LEN); + req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT; + +#if SNTP_CHECK_RESPONSE >= 2 + { + u32_t sntp_time_sec, sntp_time_us; + /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */ + SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us); + sntp_last_timestamp_sent[0] = lwip_htonl(sntp_time_sec + DIFF_SEC_1900_1970); + req->transmit_timestamp[0] = sntp_last_timestamp_sent[0]; + /* we send/save us instead of fraction to be faster... */ + sntp_last_timestamp_sent[1] = lwip_htonl(sntp_time_us); + req->transmit_timestamp[1] = sntp_last_timestamp_sent[1]; + } +#endif /* SNTP_CHECK_RESPONSE >= 2 */ +} + +/** + * Retry: send a new request (and increase retry timeout). + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_retry(void* arg) +{ + LWIP_UNUSED_ARG(arg); + + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n", + sntp_retry_timeout)); + + /* set up a timer to send a retry and increase the retry delay */ + sys_timeout(sntp_retry_timeout, sntp_request, NULL); + +#if SNTP_RETRY_TIMEOUT_EXP + { + u32_t new_retry_timeout; + /* increase the timeout for next retry */ + new_retry_timeout = sntp_retry_timeout << 1; + /* limit to maximum timeout and prevent overflow */ + if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) && + (new_retry_timeout > sntp_retry_timeout)) { + sntp_retry_timeout = new_retry_timeout; + } + } +#endif /* SNTP_RETRY_TIMEOUT_EXP */ +} + +#if SNTP_SUPPORT_MULTIPLE_SERVERS +/** + * If Kiss-of-Death is received (or another packet parsing error), + * try the next server or retry the current server and increase the retry + * timeout if only one server is available. + * (implicitly, SNTP_MAX_SERVERS > 1) + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_try_next_server(void* arg) +{ + u8_t old_server, i; + LWIP_UNUSED_ARG(arg); + + old_server = sntp_current_server; + for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) { + sntp_current_server++; + if (sntp_current_server >= SNTP_MAX_SERVERS) { + sntp_current_server = 0; + } + if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr) +#if SNTP_SERVER_DNS + || (sntp_servers[sntp_current_server].name != NULL) +#endif + ) { + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n", + (u16_t)sntp_current_server)); + /* new server: reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + /* instantly send a request to the next server */ + sntp_request(NULL); + return; + } + } + /* no other valid server found */ + sntp_current_server = old_server; + sntp_retry(NULL); +} +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +/* Always retry on error if only one server is supported */ +#define sntp_try_next_server sntp_retry +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + +/** UDP recv callback for the sntp pcb */ +static void +sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + u8_t mode; + u8_t stratum; + u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE]; + err_t err; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + + /* packet received: stop retry timeout */ + sys_untimeout(sntp_try_next_server, NULL); + sys_untimeout(sntp_request, NULL); + + err = ERR_ARG; +#if SNTP_CHECK_RESPONSE >= 1 + /* check server address and port */ + if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) && + (port == SNTP_PORT)) +#else /* SNTP_CHECK_RESPONSE >= 1 */ + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + { + /* process the response */ + if (p->tot_len == SNTP_MSG_LEN) { + pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE); + mode &= SNTP_MODE_MASK; + /* if this is a SNTP response... */ + if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) || + ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) { + pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM); + if (stratum == SNTP_STRATUM_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + err = SNTP_ERR_KOD; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n")); + } else { +#if SNTP_CHECK_RESPONSE >= 2 + /* check originate_timetamp against sntp_last_timestamp_sent */ + u32_t originate_timestamp[2]; + pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME); + if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) || + (originate_timestamp[1] != sntp_last_timestamp_sent[1])) + { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n")); + } else +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */ + { + /* correct answer */ + err = ERR_OK; + pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_TRANSMIT_TIME); + } + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode)); + /* wait for correct response */ + err = ERR_TIMEOUT; + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len)); + } + } +#if SNTP_CHECK_RESPONSE >= 1 + else { + /* packet from wrong remote address or port, wait for correct response */ + err = ERR_TIMEOUT; + } +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + pbuf_free(p); + if (err == ERR_OK) { + sntp_process(receive_timestamp); + + /* Set up timeout for next request (only if poll response was received)*/ + if (sntp_opmode == SNTP_OPMODE_POLL) { + /* Correct response, reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + + sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL); + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n", + (u32_t)SNTP_UPDATE_DELAY)); + } + } else if (err != ERR_TIMEOUT) { + /* Errors are only processed in case of an explicit poll response */ + if (sntp_opmode == SNTP_OPMODE_POLL) { + if (err == SNTP_ERR_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + sntp_try_next_server(NULL); + } else { + /* another error, try the same server again */ + sntp_retry(NULL); + } + } + } +} + +/** Actually send an sntp request to a server. + * + * @param server_addr resolved IP address of the SNTP server + */ +static void +sntp_send_request(const ip_addr_t *server_addr) +{ + struct pbuf* p; + p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM); + if (p != NULL) { + struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n")); + /* initialize request message */ + sntp_initialize_request(sntpmsg); + /* send request */ + udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT); + /* free the pbuf after sending it */ + pbuf_free(p); + /* set up receive timeout: try next server or retry on timeout */ + sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL); +#if SNTP_CHECK_RESPONSE >= 1 + /* save server address to verify it in sntp_recv */ + ip_addr_set(&sntp_last_server_address, server_addr); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + } else { + LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n", + (u32_t)SNTP_RETRY_TIMEOUT)); + /* out of memory: set up a timer to send a retry */ + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL); + } +} + +#if SNTP_SERVER_DNS +/** + * DNS found callback when using DNS names as server address. + */ +static void +sntp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg) +{ + LWIP_UNUSED_ARG(hostname); + LWIP_UNUSED_ARG(arg); + + if (ipaddr != NULL) { + /* Address resolved, send request */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n")); + sntp_send_request(ipaddr); + } else { + /* DNS resolving failed -> try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n")); + sntp_try_next_server(NULL); + } +} +#endif /* SNTP_SERVER_DNS */ + +/** + * Send out an sntp request. + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_request(void *arg) +{ + ip_addr_t sntp_server_address; + err_t err; + + LWIP_UNUSED_ARG(arg); + + /* initialize SNTP server address */ +#if SNTP_SERVER_DNS + if (sntp_servers[sntp_current_server].name) { + /* always resolve the name and rely on dns-internal caching & timeout */ + ip_addr_set_zero(&sntp_servers[sntp_current_server].addr); + err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address, + sntp_dns_found, NULL); + if (err == ERR_INPROGRESS) { + /* DNS request sent, wait for sntp_dns_found being called */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n")); + return; + } else if (err == ERR_OK) { + sntp_servers[sntp_current_server].addr = sntp_server_address; + } + } else +#endif /* SNTP_SERVER_DNS */ + { + sntp_server_address = sntp_servers[sntp_current_server].addr; + err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK; + } + + if (err == ERR_OK) { + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n", + ipaddr_ntoa(&sntp_server_address))); + sntp_send_request(&sntp_server_address); + } else { + /* address conversion failed, try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n")); + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL); + } +} + +/** + * @ingroup sntp + * Initialize this module. + * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC). + */ +void +sntp_init(void) +{ +#ifdef SNTP_SERVER_ADDRESS +#if SNTP_SERVER_DNS + sntp_setservername(0, SNTP_SERVER_ADDRESS); +#else +#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0 +#endif +#endif /* SNTP_SERVER_ADDRESS */ + + if (sntp_pcb == NULL) { + sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL); + if (sntp_pcb != NULL) { + udp_recv(sntp_pcb, sntp_recv, NULL); + + if (sntp_opmode == SNTP_OPMODE_POLL) { + SNTP_RESET_RETRY_TIMEOUT(); +#if SNTP_STARTUP_DELAY + sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL); +#else + sntp_request(NULL); +#endif + } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) { + ip_set_option(sntp_pcb, SOF_BROADCAST); + udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT); + } + } + } +} + +/** + * @ingroup sntp + * Stop this module. + */ +void +sntp_stop(void) +{ + if (sntp_pcb != NULL) { + sys_untimeout(sntp_request, NULL); + sys_untimeout(sntp_try_next_server, NULL); + udp_remove(sntp_pcb); + sntp_pcb = NULL; + } +} + +/** + * @ingroup sntp + * Get enabled state. + */ +u8_t sntp_enabled(void) +{ + return (sntp_pcb != NULL)? 1 : 0; +} + +/** + * @ingroup sntp + * Sets the operating mode. + * @param operating_mode one of the available operating modes + */ +void +sntp_setoperatingmode(u8_t operating_mode) +{ + LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY); + LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL); + sntp_opmode = operating_mode; +} + +/** + * @ingroup sntp + * Gets the operating mode. + */ +u8_t +sntp_getoperatingmode(void) +{ + return sntp_opmode; +} + +#if SNTP_GET_SERVERS_FROM_DHCP +/** + * Config SNTP server handling by IP address, name, or DHCP; clear table + * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp + */ +void +sntp_servermode_dhcp(int set_servers_from_dhcp) +{ + u8_t new_mode = set_servers_from_dhcp ? 1 : 0; + if (sntp_set_servers_from_dhcp != new_mode) { + sntp_set_servers_from_dhcp = new_mode; + } +} +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ + +/** + * @ingroup sntp + * Initialize one of the NTP servers by IP address + * + * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param server IP address of the NTP server to set + */ +void +sntp_setserver(u8_t idx, const ip_addr_t *server) +{ + if (idx < SNTP_MAX_SERVERS) { + if (server != NULL) { + sntp_servers[idx].addr = (*server); + } else { + ip_addr_set_zero(&sntp_servers[idx].addr); + } +#if SNTP_SERVER_DNS + sntp_servers[idx].name = NULL; +#endif + } +} + +#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP +/** + * Initialize one of the NTP servers by IP address, required by DHCP + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver IP address of the NTP server to set + */ +void +dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server) +{ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n", + (sntp_set_servers_from_dhcp ? "Got" : "Rejected"), + ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num)); + if (sntp_set_servers_from_dhcp && num) { + u8_t i; + for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) { + ip_addr_t addr; + ip_addr_copy_from_ip4(addr, server[i]); + sntp_setserver(i, &addr); + } + for (i = num; i < SNTP_MAX_SERVERS; i++) { + sntp_setserver(i, NULL); + } + } +} +#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */ + +/** + * @ingroup sntp + * Obtain one of the currently configured by IP address (or DHCP) NTP servers + * + * @param idx the index of the NTP server + * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP + * server has not been configured by address (or at all). + */ +const ip_addr_t* +sntp_getserver(u8_t idx) +{ + if (idx < SNTP_MAX_SERVERS) { + return &sntp_servers[idx].addr; + } + return IP_ADDR_ANY; +} + +#if SNTP_SERVER_DNS +/** + * Initialize one of the NTP servers by name + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver DNS name of the NTP server to set, to be resolved at contact time + */ +void +sntp_setservername(u8_t idx, char *server) +{ + if (idx < SNTP_MAX_SERVERS) { + sntp_servers[idx].name = server; + } +} + +/** + * Obtain one of the currently configured by name NTP servers. + * + * @param numdns the index of the NTP server + * @return IP address of the indexed NTP server or NULL if the NTP + * server has not been configured by name (or at all) + */ +char * +sntp_getservername(u8_t idx) +{ + if (idx < SNTP_MAX_SERVERS) { + return sntp_servers[idx].name; + } + return NULL; +} +#endif /* SNTP_SERVER_DNS */ + +#endif /* LWIP_UDP */ diff --git a/sdk/Src/Network/lwip2.0.3/apps/tftp/tftp_server.c b/sdk/Src/Network/lwip2.0.3/apps/tftp/tftp_server.c new file mode 100644 index 0000000..243b092 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/apps/tftp/tftp_server.c @@ -0,0 +1,417 @@ +/****************************************************************//** + * + * @file tftp_server.c + * + * @author Logan Gunthorpe + * Dirk Ziegelmeier + * + * @brief Trivial File Transfer Protocol (RFC 1350) + * + * Copyright (c) Deltatee Enterprises Ltd. 2013 + * All rights reserved. + * + ********************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification,are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Logan Gunthorpe + * Dirk Ziegelmeier + * + */ + +/** + * @defgroup tftp TFTP server + * @ingroup apps + * + * This is simple TFTP server for the lwIP raw API. + */ + +#include "lwip/apps/tftp_server.h" + +#if LWIP_UDP + +#include "lwip/udp.h" +#include "lwip/timeouts.h" +#include "lwip/debug.h" + +#define TFTP_MAX_PAYLOAD_SIZE 512 +#define TFTP_HEADER_LENGTH 4 + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +enum tftp_error { + TFTP_ERROR_FILE_NOT_FOUND = 1, + TFTP_ERROR_ACCESS_VIOLATION = 2, + TFTP_ERROR_DISK_FULL = 3, + TFTP_ERROR_ILLEGAL_OPERATION = 4, + TFTP_ERROR_UNKNOWN_TRFR_ID = 5, + TFTP_ERROR_FILE_EXISTS = 6, + TFTP_ERROR_NO_SUCH_USER = 7 +}; + +#include + +struct tftp_state { + const struct tftp_context *ctx; + void *handle; + struct pbuf *last_data; + struct udp_pcb *upcb; + ip_addr_t addr; + u16_t port; + int timer; + int last_pkt; + u16_t blknum; + u8_t retries; + u8_t mode_write; +}; + +static struct tftp_state tftp_state; + +static void tftp_tmr(void* arg); + +static void +close_handle(void) +{ + tftp_state.port = 0; + ip_addr_set_any(0, &tftp_state.addr); + + if(tftp_state.last_data != NULL) { + pbuf_free(tftp_state.last_data); + tftp_state.last_data = NULL; + } + + sys_untimeout(tftp_tmr, NULL); + + if (tftp_state.handle) { + tftp_state.ctx->close(tftp_state.handle); + tftp_state.handle = NULL; + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n")); + } +} + +static void +send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str) +{ + int str_length = strlen(str); + struct pbuf* p; + u16_t* payload; + + p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM); + if(p == NULL) { + return; + } + + payload = (u16_t*) p->payload; + payload[0] = PP_HTONS(TFTP_ERROR); + payload[1] = lwip_htons(code); + MEMCPY(&payload[2], str, str_length + 1); + + udp_sendto(tftp_state.upcb, p, addr, port); + pbuf_free(p); +} + +static void +send_ack(u16_t blknum) +{ + struct pbuf* p; + u16_t* payload; + + p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM); + if(p == NULL) { + return; + } + payload = (u16_t*) p->payload; + + payload[0] = PP_HTONS(TFTP_ACK); + payload[1] = lwip_htons(blknum); + udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port); + pbuf_free(p); +} + +static void +resend_data(void) +{ + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM); + if(p == NULL) { + return; + } + + if(pbuf_copy(p, tftp_state.last_data) != ERR_OK) { + pbuf_free(p); + return; + } + + udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port); + pbuf_free(p); +} + +static void +send_data(void) +{ + u16_t *payload; + int ret; + + if(tftp_state.last_data != NULL) { + pbuf_free(tftp_state.last_data); + } + + tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM); + if(tftp_state.last_data == NULL) { + return; + } + + payload = (u16_t *) tftp_state.last_data->payload; + payload[0] = PP_HTONS(TFTP_DATA); + payload[1] = lwip_htons(tftp_state.blknum); + + ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE); + if (ret < 0) { + send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file."); + close_handle(); + return; + } + + pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret)); + resend_data(); +} + +static void +recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + u16_t *sbuf = (u16_t *) p->payload; + int opcode; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(upcb); + + if (((tftp_state.port != 0) && (port != tftp_state.port)) || + (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported"); + pbuf_free(p); + return; + } + + opcode = sbuf[0]; + + tftp_state.last_pkt = tftp_state.timer; + tftp_state.retries = 0; + + switch (opcode) { + case PP_HTONS(TFTP_RRQ): /* fall through */ + case PP_HTONS(TFTP_WRQ): + { + const char tftp_null = 0; + char filename[TFTP_MAX_FILENAME_LEN]; + char mode[TFTP_MAX_MODE_LEN]; + u16_t filename_end_offset; + u16_t mode_end_offset; + + if(tftp_state.handle != NULL) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported"); + break; + } + + sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL); + + /* find \0 in pbuf -> end of filename string */ + filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2); + if((u16_t)(filename_end_offset-2) > sizeof(filename)) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated"); + break; + } + pbuf_copy_partial(p, filename, filename_end_offset-2, 2); + + /* find \0 in pbuf -> end of mode string */ + mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1); + if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated"); + break; + } + pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1); + + tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ)); + tftp_state.blknum = 1; + + if (!tftp_state.handle) { + send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file."); + break; + } + + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read")); + ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr); + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode)); + + ip_addr_copy(tftp_state.addr, *addr); + tftp_state.port = port; + + if (opcode == PP_HTONS(TFTP_WRQ)) { + tftp_state.mode_write = 1; + send_ack(0); + } else { + tftp_state.mode_write = 0; + send_data(); + } + + break; + } + + case PP_HTONS(TFTP_DATA): + { + int ret; + u16_t blknum; + + if (tftp_state.handle == NULL) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection"); + break; + } + + if (tftp_state.mode_write != 1) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection"); + break; + } + + blknum = lwip_ntohs(sbuf[1]); + pbuf_header(p, -TFTP_HEADER_LENGTH); + + ret = tftp_state.ctx->write(tftp_state.handle, p); + if (ret < 0) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file"); + close_handle(); + } else { + send_ack(blknum); + } + + if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) { + close_handle(); + } + break; + } + + case PP_HTONS(TFTP_ACK): + { + u16_t blknum; + int lastpkt; + + if (tftp_state.handle == NULL) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection"); + break; + } + + if (tftp_state.mode_write != 0) { + send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection"); + break; + } + + blknum = lwip_ntohs(sbuf[1]); + if (blknum != tftp_state.blknum) { + send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number"); + break; + } + + lastpkt = 0; + + if (tftp_state.last_data != NULL) { + lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH); + } + + if (!lastpkt) { + tftp_state.blknum++; + send_data(); + } else { + close_handle(); + } + + break; + } + + default: + send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation"); + break; + } + + pbuf_free(p); +} + +static void +tftp_tmr(void* arg) +{ + LWIP_UNUSED_ARG(arg); + + tftp_state.timer++; + + if (tftp_state.handle == NULL) { + return; + } + + sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL); + + if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) { + if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) { + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n")); + resend_data(); + tftp_state.retries++; + } else { + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n")); + close_handle(); + } + } +} + +/** @ingroup tftp + * Initialize TFTP server. + * @param ctx TFTP callback struct + */ +err_t +tftp_init(const struct tftp_context *ctx) +{ + err_t ret; + + struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + if (pcb == NULL) { + return ERR_MEM; + } + + ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT); + if (ret != ERR_OK) { + udp_remove(pcb); + return ret; + } + + tftp_state.handle = NULL; + tftp_state.port = 0; + tftp_state.ctx = ctx; + tftp_state.timer = 0; + tftp_state.last_data = NULL; + tftp_state.upcb = pcb; + + udp_recv(pcb, recv, NULL); + + return ERR_OK; +} + +#endif /* LWIP_UDP */ diff --git a/sdk/Src/Network/lwip2.0.3/core/alg.c b/sdk/Src/Network/lwip2.0.3/core/alg.c new file mode 100644 index 0000000..7668290 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/alg.c @@ -0,0 +1,2076 @@ +/****************************************************************************** + + Copyright (C) 2015 Winner Micro electronics Co., Ltd. + + ****************************************************************************** + File Name : alg.c + Version : Initial Draft + Author : Li Limin, lilm@winnermicro.com + Created : 2015/3/7 + Last Modified : + Description : Application layer gateway, (alg) only for apsta + + History : + Date : 2015/3/7 + Author : Li Limin, lilm@winnermicro.com + Modification : Created file + +******************************************************************************/ +#include +#include +#include "wm_type_def.h" +#include "wm_timer.h" +#include "tls_common.h" +#include "tls_wireless.h" +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "lwip/icmp.h" +#include "lwip/dns.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/alg.h" +#include "netif/ethernetif.h" + +#if TLS_CONFIG_AP_OPT_FWD +extern u8 *hostapd_get_mac(void); +extern u8 *wpa_supplicant_get_mac(void); +extern u8 *wpa_supplicant_get_bssid(void); +extern struct netif *tls_get_netif(void); +extern struct tls_wif *tls_get_wif_data(void); + +//#define NAPT_ALLOC_DEBUG +#ifdef NAPT_ALLOC_DEBUG +static u16 napt4ic_cnt; +static u16 napt4tcp_cnt; +static u16 napt4udp_cnt; +#endif + +//#define NAPT_DEBUG +#ifdef NAPT_DEBUG +#define NAPT_PRINT printf +#else +#define NAPT_PRINT(...) +#endif + +#define IP_PROTO_GRE 47 + +#define NAPT_ETH_HDR_LEN sizeof(struct ethhdr) + +#define NAPT_CHKSUM_16BIT_LEN sizeof(u16) + +#define NAPT_TABLE_FOREACH(pos, head)\ + for (pos = head.next; NULL != pos; pos = pos->next) + +/* napt tcp/udp */ +struct napt_addr_4tu{ + struct napt_addr_4tu *next; + u16 src_port; + u16 new_port; + u8 src_ip; + u8 time_stamp; +}; + +/* napt icmp */ +struct napt_addr_4ic{ + struct napt_addr_4ic *next; + u16 src_id;/* icmp id */ + u16 new_id; + u8 src_ip; + u8 time_stamp; +}; + +struct napt_addr_gre{ + u8 src_ip; + u8 time_stamp; + bool is_used; +}; + +struct napt_table_head_4tu{ + struct napt_addr_4tu *next; +#ifdef NAPT_TABLE_LIMIT + u16 cnt; +#endif +}; + +struct napt_table_head_4ic{ + struct napt_addr_4ic *next; +#ifdef NAPT_TABLE_LIMIT + u16 cnt; +#endif +}; + +/* napt lock */ +static struct napt_table_head_4tu napt_table_4tcp; +static struct napt_table_head_4tu napt_table_4udp; +static struct napt_table_head_4ic napt_table_4ic; + +#define NAPT_TABLE_MUTEX_LOCK +#ifdef NAPT_TABLE_MUTEX_LOCK +static tls_os_sem_t *napt_table_lock_4tcp; +static tls_os_sem_t *napt_table_lock_4udp; +static tls_os_sem_t *napt_table_lock_4ic; +static bool napt_check_tcp = FALSE; +static bool napt_check_udp = FALSE; +static bool napt_check_ic = FALSE; +#endif + +/* tcp&udp */ +static u16 napt_curr_port; + +/* icmp id */ +static u16 napt_curr_id; + +/* gre for vpn */ +static struct napt_addr_gre gre_info; + +/***************************************************************************** + Prototype : alg_napt_mem_alloc + Description : alloc memory + Input : u32 size memory size + Output : None + Return Value : void* NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline void *alg_napt_mem_alloc(u32 size) +{ + return tls_mem_alloc(size); +} + +/***************************************************************************** + Prototype : alg_napt_mem_free + Description : free memory + Input : void *p memory + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline void alg_napt_mem_free(void *p) +{ + tls_mem_free(p); + return; +} + +/***************************************************************************** + Prototype : alg_napt_try_lock + Description : try hold lock + Input : tls_os_sem_t *lock point lock + Output : None + Return Value : int 0 success + -1 failed + ------------------------------------------------------------------------------ + + History : + 1.Date : 2019/2/1 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline int alg_napt_try_lock(tls_os_sem_t *lock) +{ + return (tls_os_sem_acquire(lock, HZ / 10) == TLS_OS_SUCCESS) ? 0 : -1; +} + +/***************************************************************************** + Prototype : alg_napt_lock + Description : hold lock + Input : tls_os_sem_t *lock point lock + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2019/2/1 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline void alg_napt_lock(tls_os_sem_t *lock) +{ + tls_os_sem_acquire(lock, 0); + return; +} + +/***************************************************************************** + Prototype : alg_napt_unlock + Description : release lock + Input : tls_os_sem_t *lock point lock + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2019/2/1 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline void alg_napt_unlock(tls_os_sem_t *lock) +{ + tls_os_sem_release(lock); +} + +#ifdef NAPT_TABLE_LIMIT +/***************************************************************************** + Prototype : alg_napt_table_is_full + Description : the napt table is full + Input : void + Output : None + Return Value : bool true the napt table is full + false the napt table not is full + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline bool alg_napt_table_is_full(void) +{ + bool is_full = false; + + if ((napt_table_4tcp.cnt + napt_table_4udp.cnt + napt_table_4ic.cnt) >= NAPT_TABLE_SIZE_MAX) + { +#ifdef NAPT_ALLOC_DEBUG + printf("@@@ napt batle: limit is reached for tcp/udp.\r\n"); +#endif + NAPT_PRINT("napt batle: limit is reached for tcp/udp.\n"); + is_full = true; + } + + return is_full; +} +#endif + +/***************************************************************************** + Prototype : alg_napt_port_alloc + Description : alloc napt port + Input : void + Output : None + Return Value : u16 0 fail + other success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline u16 alg_napt_port_alloc(void) +{ + u8_t i; + u16 cnt = 0; + struct udp_pcb *udp_pcb; + struct tcp_pcb *tcp_pcb; + struct napt_addr_4tu *napt_tcp; + struct napt_addr_4tu *napt_udp; + +again: + if (napt_curr_port++ == NAPT_LOCAL_PORT_RANGE_END) + { + napt_curr_port = NAPT_LOCAL_PORT_RANGE_START; + } + + /* udp */ + for(udp_pcb = udp_pcbs; udp_pcb != NULL; udp_pcb = udp_pcb->next) + { + if (udp_pcb->local_port == napt_curr_port) + { + if (++cnt > (NAPT_LOCAL_PORT_RANGE_END - NAPT_LOCAL_PORT_RANGE_START)) + { + return 0; + } + goto again; + } + } + + /* tcp */ + for (i = 0; i < NUM_TCP_PCB_LISTS; i++) + { + for(tcp_pcb = *tcp_pcb_lists[i]; tcp_pcb != NULL; tcp_pcb = tcp_pcb->next) + { + if (tcp_pcb->local_port == napt_curr_port) + { + if (++cnt > (NAPT_LOCAL_PORT_RANGE_END - NAPT_LOCAL_PORT_RANGE_START)) + { + return 0; + } + goto again; + } + } + } + + /* tcp napt */ + NAPT_TABLE_FOREACH(napt_tcp, napt_table_4tcp) + { + if (napt_tcp->new_port == napt_curr_port) + { + if (++cnt > (NAPT_LOCAL_PORT_RANGE_END - NAPT_LOCAL_PORT_RANGE_START)) + { + return 0; + } + goto again; + } + } + + /* udp napt */ + NAPT_TABLE_FOREACH(napt_udp, napt_table_4udp) + { + if (napt_udp->new_port == napt_curr_port) + { + if (++cnt > (NAPT_LOCAL_PORT_RANGE_END - NAPT_LOCAL_PORT_RANGE_START)) + { + return 0; + } + goto again; + } + } + + return napt_curr_port; +} + +/***************************************************************************** + Prototype : alg_napt_get_by_src_id + Description : find item by ip and id + Input : u16 id icmp echo id + u8 ip source ip + Output : None + Return Value : struct napt_addr_4ic * NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4ic *alg_napt_get_by_src_id(u16 id, u8 ip) +{ + struct napt_addr_4ic *ret = NULL; + struct napt_addr_4ic *napt; + + NAPT_TABLE_FOREACH(napt, napt_table_4ic) + { + if ((id == napt->src_id) && (ip == napt->src_ip)) + { + ret = napt; + break; + } + } + + return ret; +} + +/***************************************************************************** + Prototype : alg_napt_get_by_dst_id + Description : find item by dest id + Input : u16 id icmp echo id + Output : None + Return Value : struct napt_addr_4ic * NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4ic *alg_napt_get_by_dst_id(u16 id) +{ + struct napt_addr_4ic *ret = NULL; + struct napt_addr_4ic *napt; + + NAPT_TABLE_FOREACH(napt, napt_table_4ic) + { + if (id == napt->new_id) + { + ret = napt; + break; + } + } + + return ret; +} + +/***************************************************************************** + Prototype : alg_napt_icmp_id_alloc + Description : alloc imcp id + Input : void + Output : None + Return Value : u16 0 fail + other success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static u16 alg_napt_icmp_id_alloc(void) +{ + u16 cnt = 0; + struct napt_addr_4ic *napt; + +again: + if (napt_curr_id++ == NAPT_ICMP_ID_RANGE_END) + { + napt_curr_id = NAPT_ICMP_ID_RANGE_START; + } + + NAPT_TABLE_FOREACH(napt, napt_table_4ic) + { + if (napt_curr_id == napt->new_id) + { + if (++cnt > (NAPT_ICMP_ID_RANGE_END - NAPT_ICMP_ID_RANGE_START)) + { + return 0; + } + + goto again; + } + } + + return napt_curr_id; +} + +/***************************************************************************** + Prototype : alg_napt_table_insert_4ic + Description : create napt item + Input : u16 id source id + u8 ip source ip + Output : None + Return Value : struct napt_addr_4ic * NULL fail + !NULL success + Note : icmp item no limit + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4ic *alg_napt_table_insert_4ic(u16 id, u8 ip) +{ + struct napt_addr_4ic *napt; + +#ifdef NAPT_TABLE_LIMIT + if (true == alg_napt_table_is_full()) + { + return NULL; + } +#endif + + napt = alg_napt_mem_alloc(sizeof(struct napt_addr_4ic)); + if (NULL == napt) + { + return NULL; + } + + memset(napt, 0, sizeof(struct napt_addr_4ic)); + napt->src_id = id; + + napt->new_id = alg_napt_icmp_id_alloc(); + if (0 == napt->new_id) + { + alg_napt_mem_free(napt); + return NULL; + } + + napt->src_ip = ip; + napt->time_stamp++; + +#ifdef NAPT_TABLE_LIMIT + napt_table_4ic.cnt++; +#endif + napt->next = napt_table_4ic.next; + napt_table_4ic.next = napt; + +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt id alloc %hu\r\n", ++napt4ic_cnt); +#endif + + return napt; +} + +/***************************************************************************** + Prototype : alg_napt_table_update_4ic + Description : fresh napt item + Input : struct napt_addr_4ic *napt + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline void alg_napt_table_update_4ic(struct napt_addr_4ic *napt) +{ + if (!++napt->time_stamp) + napt->time_stamp++; + + return; +} + +/***************************************************************************** + Prototype : alg_napt_get_tcp_port_by_dest + Description : find item by port + Input : u16 port dest port + Output : None + Return Value : struct napt_addr_4tu * NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4tu *alg_napt_get_tcp_port_by_dest(u16 port) +{ + struct napt_addr_4tu *ret = NULL; + struct napt_addr_4tu *napt; + + NAPT_TABLE_FOREACH(napt, napt_table_4tcp) + { + if (napt->new_port == port) + { + ret = napt; + break; + } + } + + return ret; +} + +/***************************************************************************** + Prototype : alg_napt_get_tcp_port_by_src + Description : find item by port and ip + Input : u16 port source port + u8 ip source ip + Output : None + Return Value : struct napt_addr_4tu * NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4tu *alg_napt_get_tcp_port_by_src(u16 port, u8 ip) +{ + struct napt_addr_4tu *ret = NULL; + struct napt_addr_4tu *napt; + + NAPT_TABLE_FOREACH(napt, napt_table_4tcp) + { + if (port == napt->src_port) + { + if (ip == napt->src_ip) + { + ret = napt; + break; + } + } + } + + return ret; +} + +/***************************************************************************** + Prototype : alg_napt_table_insert_4tcp + Description : create napt item + Input : u16 src_port source port + u8 ip soutce ip + Output : None + Return Value : struct napt_addr_4tu * NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4tu *alg_napt_table_insert_4tcp(u16 src_port, u8 ip) +{ + u16 new_port; + struct napt_addr_4tu *napt; + +#ifdef NAPT_TABLE_LIMIT + if (true == alg_napt_table_is_full()) + { + return NULL; + } +#endif + + napt = alg_napt_mem_alloc(sizeof(struct napt_addr_4tu)); + if (NULL == napt) + { + return NULL; + } + + memset(napt, 0, sizeof(struct napt_addr_4tu)); + napt->src_port = src_port; + + new_port = alg_napt_port_alloc(); + if (0 == new_port) + { + alg_napt_mem_free(napt); + return NULL; + } + + napt->new_port = htons(new_port); + napt->src_ip = ip; + napt->time_stamp++; + +#ifdef NAPT_TABLE_LIMIT + napt_table_4tcp.cnt++; +#endif + napt->next = napt_table_4tcp.next; + napt_table_4tcp.next = napt; + +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt tcp port alloc %hu\r\n", ++napt4tcp_cnt); +#endif + + return napt; +} + +/***************************************************************************** + Prototype : alg_napt_table_update_4tcp + Description : fresh napt item + Input : struct napt_addr_4tu *napt napt item + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline void alg_napt_table_update_4tcp(struct napt_addr_4tu *napt) +{ + if (!++napt->time_stamp) + napt->time_stamp++; + + return; +} + +/***************************************************************************** + Prototype : alg_napt_get_udp_port_by_dest + Description : find item by port + Input : u16 port dest port + Output : None + Return Value : struct napt_addr_4tu * NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4tu *alg_napt_get_udp_port_by_dest(u16 port) +{ + struct napt_addr_4tu *ret = NULL; + struct napt_addr_4tu *napt; + + NAPT_TABLE_FOREACH(napt, napt_table_4udp) + { + if (napt->new_port == port) + { + ret = napt; + break; + } + } + + return ret; +} + +/***************************************************************************** + Prototype : alg_napt_get_udp_port_by_src + Description : find item by port and ip + Input : u16 port source port + u8 ip source ip + Output : None + Return Value : struct napt_addr_4tu * NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4tu *alg_napt_get_udp_port_by_src(u16 port, u8 ip) +{ + struct napt_addr_4tu *ret = NULL; + struct napt_addr_4tu *napt; + + NAPT_TABLE_FOREACH(napt, napt_table_4udp) + { + if (port == napt->src_port) + { + if (ip == napt->src_ip) + { + ret = napt; + break; + } + } + } + + return ret; +} + +/***************************************************************************** + Prototype : alg_napt_table_insert_4udp + Description : create new item + Input : u16 src_port source port + u8 ip dest port + Output : None + Return Value : struct napt_addr_4tu * NULL fail + !NULL success + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline struct napt_addr_4tu *alg_napt_table_insert_4udp(u16 src_port, u8 ip) +{ + u16 new_port; + struct napt_addr_4tu *napt; + +#ifdef NAPT_TABLE_LIMIT + if (true == alg_napt_table_is_full()) + { + return NULL; + } +#endif + + napt = alg_napt_mem_alloc(sizeof(struct napt_addr_4tu)); + if (NULL == napt) + { + return NULL; + } + + memset(napt, 0, sizeof(struct napt_addr_4tu)); + napt->src_port = src_port; + + new_port = alg_napt_port_alloc(); + if (0 == new_port) + { + alg_napt_mem_free(napt); + return NULL; + } + + napt->new_port = htons(new_port); + napt->src_ip = ip; + napt->time_stamp++; + +#ifdef NAPT_TABLE_LIMIT + napt_table_4udp.cnt++; +#endif + napt->next = napt_table_4udp.next; + napt_table_4udp.next = napt; + +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt udp port alloc %hu\r\n", ++napt4udp_cnt); +#endif + + return napt; +} + +/***************************************************************************** + Prototype : alg_napt_table_update_4udp + Description : fresh napt item + Input : struct napt_addr_4tu *napt + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline void alg_napt_table_update_4udp(struct napt_addr_4tu *napt) +{ + if (!++napt->time_stamp) + napt->time_stamp++; + + return; +} + +/***************************************************************************** + Prototype : alg_napt_table_check_4tcp + Description : tcp event handle + Input : void + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static void alg_napt_table_check_4tcp(void) +{ + struct napt_addr_4tu *napt4tcp; + struct napt_addr_4tu *napt4tcp_prev; + + /* tcp */ +#ifdef NAPT_TABLE_MUTEX_LOCK + if (alg_napt_try_lock(napt_table_lock_4tcp)) + { + //printf("## try tcp\r\n"); + napt_check_tcp = TRUE; + return; + } + napt_check_tcp = FALSE; +#endif + for (napt4tcp_prev = napt_table_4tcp.next;\ + NULL != napt4tcp_prev;\ + napt4tcp_prev = napt4tcp_prev->next) + { + napt4tcp = napt4tcp_prev->next; + if (NULL != napt4tcp) + { + if (0 == napt4tcp->time_stamp) + { +#ifdef NAPT_TABLE_LIMIT + napt_table_4tcp.cnt--; +#endif + napt4tcp_prev->next = napt4tcp->next; + napt4tcp->next = NULL; + alg_napt_mem_free(napt4tcp); +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt tcp port free %hu\r\n", --napt4tcp_cnt); +#endif + } + else + { + napt4tcp->time_stamp = 0; + } + } + + } + napt4tcp = napt_table_4tcp.next; + if (NULL != napt4tcp) + { + if (0 == napt4tcp->time_stamp) + { +#ifdef NAPT_TABLE_LIMIT + napt_table_4tcp.cnt--; +#endif + napt_table_4tcp.next = napt4tcp->next; + napt4tcp->next = NULL; + alg_napt_mem_free(napt4tcp); +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt tcp port free %hu\r\n", --napt4tcp_cnt); +#endif + } + else + { + napt4tcp->time_stamp = 0; + } + } +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4tcp); +#endif + return; +} + +/***************************************************************************** + Prototype : alg_napt_table_check_4udp + Description : udp event handle + Input : void + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static void alg_napt_table_check_4udp(void) +{ + struct napt_addr_4tu *napt4udp; + struct napt_addr_4tu *napt4udp_prev; + + /* udp */ +#ifdef NAPT_TABLE_MUTEX_LOCK + if (alg_napt_try_lock(napt_table_lock_4udp)) + { + //printf("## try udp\r\n"); + napt_check_udp = TRUE; + return; + } + napt_check_udp = FALSE; +#endif + for (napt4udp_prev = napt_table_4udp.next;\ + NULL != napt4udp_prev;\ + napt4udp_prev = napt4udp_prev->next) + { + napt4udp = napt4udp_prev->next; + if (NULL != napt4udp) + { + if (0 == napt4udp->time_stamp) + { +#ifdef NAPT_TABLE_LIMIT + napt_table_4udp.cnt--; +#endif + napt4udp_prev->next = napt4udp->next; + napt4udp->next = NULL; + alg_napt_mem_free(napt4udp); +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt udp port free %hu\r\n", --napt4udp_cnt); +#endif + } + else + { + napt4udp->time_stamp = 0; + } + } + + } + napt4udp = napt_table_4udp.next; + if (NULL != napt4udp) + { + if (0 == napt4udp->time_stamp) + { +#ifdef NAPT_TABLE_LIMIT + napt_table_4udp.cnt--; +#endif + napt_table_4udp.next = napt4udp->next; + napt4udp->next = NULL; + alg_napt_mem_free(napt4udp); +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt udp port free %hu\r\n", --napt4udp_cnt); +#endif + } + else + { + napt4udp->time_stamp = 0; + } + } +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4udp); +#endif + return; +} + +/***************************************************************************** + Prototype : alg_napt_table_check_4ic + Description : icmp event handle + Input : void + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static void alg_napt_table_check_4ic(void) +{ + struct napt_addr_4ic *napt4ic; + struct napt_addr_4ic *napt4ic_prev; + + /* icmp */ +#ifdef NAPT_TABLE_MUTEX_LOCK + if (alg_napt_try_lock(napt_table_lock_4ic)) + { + //printf("## try ic\r\n"); + napt_check_ic = TRUE; + return; + } + napt_check_ic = FALSE; +#endif + /* skip the first item */ + for (napt4ic_prev = napt_table_4ic.next;\ + NULL != napt4ic_prev;\ + napt4ic_prev = napt4ic_prev->next) + { + napt4ic = napt4ic_prev->next; + if (NULL != napt4ic) + { + if (0 == napt4ic->time_stamp) + { +#ifdef NAPT_TABLE_LIMIT + napt_table_4ic.cnt--; +#endif + napt4ic_prev->next = napt4ic->next; + napt4ic->next = NULL; + alg_napt_mem_free(napt4ic); +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt id free %hu\r\n", --napt4ic_cnt); +#endif + } + else + { + napt4ic->time_stamp = 0; + } + } + + } + /* check the first item */ + napt4ic = napt_table_4ic.next; + if (NULL != napt4ic) + { + if (0 == napt4ic->time_stamp) + { +#ifdef NAPT_TABLE_LIMIT + napt_table_4ic.cnt--; +#endif + napt_table_4ic.next = napt4ic->next; + napt4ic->next = NULL; + alg_napt_mem_free(napt4ic); +#ifdef NAPT_ALLOC_DEBUG + printf("@@ napt id free %hu\r\n", --napt4ic_cnt); +#endif + } + else + { + napt4ic->time_stamp = 0; + } + } +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4ic); +#endif + return; +} + +/***************************************************************************** + Prototype : alg_napt_table_check_4gre + Description : vpn event handle + Input : void + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static void alg_napt_table_check_4gre(void) +{ + if (0 == gre_info.time_stamp) + { + gre_info.is_used = false; + //gre_info.src_ip = 0; + } + else + { + gre_info.time_stamp = 0; + } + + return; +} + +/***************************************************************************** + Prototype : alg_napt_table_check + Description : napt table age check + Input : u32 type event type + Output : None + Return Value : void + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static void alg_napt_table_check(void *arg) +{ + alg_napt_table_check_4tcp(); + alg_napt_table_check_4udp(); + alg_napt_table_check_4ic(); + alg_napt_table_check_4gre(); + + return; +} + +#ifdef NAPT_TABLE_MUTEX_LOCK +static void alg_napt_table_check_try(void) +{ + if (napt_check_tcp) + alg_napt_table_check_4tcp(); + + if (napt_check_udp) + alg_napt_table_check_4udp(); + + if (napt_check_ic) + alg_napt_table_check_4ic(); + + return; +} +#endif + +/***************************************************************************** + Prototype : alg_hdr_16bitsum + Description : check sum + Input : u16 *buff packet + u16 len packet length + Output : None + Return Value : u32 sum + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline u32 alg_hdr_16bitsum(const u16 *buff, u16 len) +{ + u32 sum = 0; + + u16 *pos = (u16 *)buff; + u16 remainder_size = len; + + while (remainder_size > 1) + { + sum += *pos ++; + remainder_size -= NAPT_CHKSUM_16BIT_LEN; + } + + if (remainder_size > 0) + { + sum += *(u8*)pos; + } + + return sum; +} + +/***************************************************************************** + Prototype : alg_iphdr_chksum + Description : check sum + Input : u16 *buff ip header + u16 len ip header length + Output : None + Return Value : u16 chksum + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline u16 alg_iphdr_chksum(const u16 *buff, u16 len) +{ + u32 sum = alg_hdr_16bitsum(buff, len); + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + + return (u16)(~sum); +} + +/***************************************************************************** + Prototype : alg_tcpudphdr_chksum + Description : check sum + Input : u32 src_addr source ip + u32 dst_addr dest ip + u8 proto ip packet protocol + u16 *buff payload + u16 len payload length (without ip header) + Output : None + Return Value : u16 chksum + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline u16 alg_tcpudphdr_chksum(u32 src_addr, u32 dst_addr, u8 proto, + const u16 *buff, u16 len) +{ + u32 sum = 0; + + sum += (src_addr & 0xffffUL); + sum += ((src_addr >> 16) & 0xffffUL); + sum += (dst_addr & 0xffffUL); + sum += ((dst_addr >> 16) & 0xffffUL); + sum += (u32)htons((u16)proto); + sum += (u32)htons(len); + + sum += alg_hdr_16bitsum(buff, len); + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + + return (u16)(~sum); +} + +/***************************************************************************** + Prototype : alg_output + Description : wifi mac layer forward + Input : u8 *ehdr + u16 eth_len + Output : None + Return Value : int 0 success + -1 fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline int alg_output(u8 *ehdr, u16 eth_len) +{ + int err; + struct tls_wif *wif = tls_get_wif_data(); + err = tls_wl_if_xmit(wif, ehdr, eth_len, FALSE, FALSE); + return err; +} + +/***************************************************************************** + Prototype : alg_output2 + Description : send by lwip + Input : struct netif *netif + struct ip_hdr *ip_hdr + Output : None + Return Value : int 0 success + -1 fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline int alg_output2(struct netif *netif, const struct ip_hdr *ip_hdr) +{ + int err; + u16 len; + ip_addr_t ipaddr; + struct pbuf *pBuf; + + len = ntohs(ip_hdr->_len); + pBuf = pbuf_alloc(PBUF_LINK, len, PBUF_RAM); + if(pBuf == NULL) + { + return -1; + } + + pbuf_take(pBuf, (const void *)ip_hdr, len); + + memset(&ipaddr, 0, sizeof(ip_addr_t)); + ip_2_ip4(&ipaddr)->addr = ip_hdr->dest.addr; + + err = netif->ipfwd_output(pBuf, netif, ipaddr); + if (0 != err) + { + pbuf_free(pBuf); + } + + return err; +} + +/***************************************************************************** + Prototype : alg_deliver2lwip + Description : deliver to lwip + Input : u8 *bssid + u8 *ehdr + u16 eth_len + Output : None + Return Value : int 0 success + -1 fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +static inline int alg_deliver2lwip(const u8 *bssid, u8 *ehdr, u16 eth_len) +{ + int err; + + err = ethernetif_input(bssid, ehdr, eth_len); + + return err; +} + +/***************************************************************************** + Prototype : alg_icmp_proc + Description : icmp packet input + Input : u8 *bssid + struct ip_hdr *ip_hdr + u8 *ehdr + u16 eth_len + Output : None + Return Value : int 0 success + -1 fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + + Note: + -------- ap.. ----------- sta.. --------- + | AP |---------------| APSTA |---------------| STA | + -------- ----------- --------- +*****************************************************************************/ +static int alg_icmp_proc(const u8 *bssid, + struct ip_hdr *ip_hdr, + u8 *ehdr, u16 eth_len) +{ + int err; + struct napt_addr_4ic *napt; + struct icmp_echo_hdr *icmp_hdr; + struct netif *net_if = tls_get_netif(); + u8 *mac = wpa_supplicant_get_mac(); + u8 *mac2 = hostapd_get_mac(); + u8 iphdr_len; + + iphdr_len = (ip_hdr->_v_hl & 0x0F) * 4; + icmp_hdr = (struct icmp_echo_hdr *)((u8 *)ip_hdr + iphdr_len); + + if (0 == compare_ether_addr(bssid, mac2)) + { + if ((ip_hdr->dest.addr == ip_addr_get_ip4_u32(&net_if->next->ip_addr))|| + (ip_hdr->dest.addr == ip_addr_get_ip4_u32(&net_if->ip_addr))) + { + err = alg_deliver2lwip(bssid, ehdr, eth_len); + return err; + } + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_lock(napt_table_lock_4ic); +#endif + napt = alg_napt_get_by_src_id(icmp_hdr->id, ip_hdr->src.addr >> 24); + if (NULL == napt) + { + napt = alg_napt_table_insert_4ic(icmp_hdr->id, ip_hdr->src.addr >> 24); + if (NULL == napt) + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4ic); +#endif + return -1; + } + } + else + { + alg_napt_table_update_4ic(napt); + } + + icmp_hdr->id = napt->new_id; + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4ic); +#endif + + icmp_hdr->chksum = 0; + icmp_hdr->chksum = alg_iphdr_chksum((u16 *)icmp_hdr, ntohs(ip_hdr->_len) - iphdr_len); + + ip_hdr->src.addr = ip_addr_get_ip4_u32(&net_if->ip_addr); + ip_hdr->_chksum = 0; + ip_hdr->_chksum = alg_iphdr_chksum((u16 *)ip_hdr, iphdr_len); + + if ((ip_addr_get_ip4_u32(&net_if->ip_addr) & 0xFFFFFF) == (ip_hdr->dest.addr & 0xFFFFFF)) + { + err = alg_output2(net_if, ip_hdr); + } + else /* to the internet */ + { + memcpy(ehdr, wpa_supplicant_get_bssid(), ETH_ALEN); + memcpy(ehdr + ETH_ALEN, mac, ETH_ALEN); + /* forward to ap...*/ + err = alg_output(ehdr, eth_len); + } + } + else + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_lock(napt_table_lock_4ic); +#endif + napt = alg_napt_get_by_dst_id(icmp_hdr->id); + if (NULL != napt) + { + //alg_napt_table_update_4ic(napt); + + icmp_hdr->id = napt->src_id; + icmp_hdr->chksum = 0; + icmp_hdr->chksum = alg_iphdr_chksum((u16 *)icmp_hdr, ntohs(ip_hdr->_len) - iphdr_len); + + ip_hdr->dest.addr = ((napt->src_ip) << 24) | (ip_addr_get_ip4_u32(&net_if->next->ip_addr) & 0x00ffffff); + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4ic); +#endif + + ip_hdr->_chksum = 0; + ip_hdr->_chksum = alg_iphdr_chksum((u16 *)ip_hdr, iphdr_len); + + memcpy(ehdr, tls_dhcps_getmac((ip_addr_t *)&ip_hdr->dest.addr), ETH_ALEN); + memcpy(ehdr + ETH_ALEN, mac2, ETH_ALEN); + err = alg_output(ehdr, eth_len); + } + else + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4ic); +#endif + + err = alg_deliver2lwip(bssid, ehdr, eth_len); + } + } + + return err; +} + +/***************************************************************************** + Prototype : alg_tcp_proc + Description : tcp packet input + Input : u8 *bssid + struct ip_hdr *ip_hdr + u8 *ehdr + u16 eth_len + Output : None + Return Value : int 0 success + -1 fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + + Note: + -------- ap.. ----------- sta.. --------- + | AP |---------------| APSTA |---------------| STA | + -------- ----------- --------- +*****************************************************************************/ +static int alg_tcp_proc(const u8 *bssid, + struct ip_hdr *ip_hdr, + u8 *ehdr, u16 eth_len) +{ + int err; + u8 src_ip; + struct napt_addr_4tu *napt; + struct tcp_hdr *tcp_hdr; + struct netif *net_if = tls_get_netif(); + u8 *mac = wpa_supplicant_get_mac(); + u8 *mac2 = hostapd_get_mac(); + u8 iphdr_len; + + iphdr_len = (ip_hdr->_v_hl & 0x0F) * 4; + tcp_hdr = (struct tcp_hdr *)((u8 *)ip_hdr + iphdr_len); + + if (0 == compare_ether_addr(bssid, mac2)) + { + if ((ip_hdr->dest.addr == ip_addr_get_ip4_u32(&net_if->next->ip_addr)) || + (ip_hdr->dest.addr == ip_addr_get_ip4_u32(&net_if->ip_addr))) + { + err = alg_deliver2lwip(bssid, ehdr, eth_len); + return err; + } + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_lock(napt_table_lock_4tcp); +#endif + src_ip = ip_hdr->src.addr >> 24; + napt = alg_napt_get_tcp_port_by_src(tcp_hdr->src, src_ip); + if (NULL == napt) + { + napt = alg_napt_table_insert_4tcp(tcp_hdr->src, src_ip); + if (NULL == napt) + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4tcp); +#endif + return -1; + } + } + else + { + alg_napt_table_update_4tcp(napt); + } + + tcp_hdr->src = napt->new_port; +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4tcp); +#endif + + ip_hdr->src.addr = ip_addr_get_ip4_u32(&net_if->ip_addr); + ip_hdr->_chksum = 0; + ip_hdr->_chksum = alg_iphdr_chksum((u16 *)ip_hdr, iphdr_len); + + tcp_hdr->chksum = 0; + tcp_hdr->chksum = alg_tcpudphdr_chksum(ip_hdr->src.addr, + ip_hdr->dest.addr, + IP_PROTO_TCP, + (u16 *)tcp_hdr, + ntohs(ip_hdr->_len) - iphdr_len); + + if ((ip_addr_get_ip4_u32(&net_if->ip_addr) & 0xFFFFFF) == (ip_hdr->dest.addr & 0xFFFFFF)) + { + err = alg_output2(net_if, ip_hdr); + } + else /* to the internet */ + { + memcpy(ehdr, wpa_supplicant_get_bssid(), ETH_ALEN); + memcpy(ehdr + ETH_ALEN, mac, ETH_ALEN); + /* forward to ap...*/ + err = alg_output(ehdr, eth_len); + } + } + /* from ap... */ + else + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_lock(napt_table_lock_4tcp); +#endif + napt = alg_napt_get_tcp_port_by_dest(tcp_hdr->dest); + /* forward to sta... */ + if (NULL != napt) + { + //alg_napt_table_update_4tcp(napt); + + ip_hdr->dest.addr = (napt->src_ip << 24) | (ip_addr_get_ip4_u32(&net_if->next->ip_addr) & 0x00ffffff); + ip_hdr->_chksum = 0; + ip_hdr->_chksum = alg_iphdr_chksum((u16 *)ip_hdr, iphdr_len); + + tcp_hdr->dest = napt->src_port; + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4tcp); +#endif + + tcp_hdr->chksum = 0; + tcp_hdr->chksum = alg_tcpudphdr_chksum(ip_hdr->src.addr, + ip_hdr->dest.addr, + IP_PROTO_TCP, + (u16 *)tcp_hdr, + ntohs(ip_hdr->_len) - iphdr_len); + + memcpy(ehdr, tls_dhcps_getmac((ip_addr_t *)&ip_hdr->dest.addr), ETH_ALEN); + memcpy(ehdr + ETH_ALEN, mac2, ETH_ALEN); + err = alg_output(ehdr, eth_len); + } + /* deliver to default gateway */ + else + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4tcp); +#endif + + err = alg_deliver2lwip(bssid, ehdr, eth_len); + } + } + + return err; +} + +/***************************************************************************** + Prototype : alg_udp_proc + Description : udp packet input + Input : u8 *bssid + struct ip_hdr *ip_hdr + u8 *ehdr + u16 eth_len + Output : None + Return Value : int 0 success + -1 fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + + Note: + -------- ap... ----------- sta... --------- + | AP |---------------| APSTA |---------------| STA | + -------- ----------- --------- +*****************************************************************************/ +static int alg_udp_proc(const u8 *bssid, + struct ip_hdr *ip_hdr, + u8 *ehdr, u16 eth_len) +{ + int err = 0; + u8 src_ip; + struct napt_addr_4tu *napt; + struct udp_hdr *udp_hdr; + struct netif *net_if = tls_get_netif(); + u8 *mac = wpa_supplicant_get_mac(); + u8 *mac2 = hostapd_get_mac(); + u8 iphdr_len; + int is_dns = 0; + ip_addr_t *dns; + + iphdr_len = (ip_hdr->_v_hl & 0x0F) * 4; + udp_hdr = (struct udp_hdr *)((u8 *)ip_hdr + iphdr_len); + + /* from sta... */ + if (0 == compare_ether_addr(bssid, mac2)) + { + /* deliver to alg gateway */ + if ((ip_hdr->dest.addr == ip_addr_get_ip4_u32(&net_if->next->ip_addr))|| + (ip_hdr->dest.addr == ip_addr_get_ip4_u32(&net_if->ip_addr))) + { + err = alg_deliver2lwip(bssid, ehdr, eth_len); + if ((53 == ntohs(udp_hdr->dest)) && netif_is_up(net_if))/* dns */ + { + is_dns = 1; + } + else + { + return err; + } + } + + /* create/update napt item */ +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_lock(napt_table_lock_4udp); +#endif + src_ip = ip_hdr->src.addr >> 24; + napt = alg_napt_get_udp_port_by_src(udp_hdr->src, src_ip); + if (NULL == napt) + { + napt = alg_napt_table_insert_4udp(udp_hdr->src, src_ip); + if (NULL == napt) + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4udp); +#endif + return -1; + } + } + else + { + alg_napt_table_update_4udp(napt); + } + + udp_hdr->src = napt->new_port; + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4udp); +#endif + +redo: + if (is_dns) + { + dns = (ip_addr_t*)dns_getserver(is_dns - 1); + if ((2 == is_dns) && ip_addr_isany(dns)) + goto end; + ip_hdr->dest.addr = dns->addr; + } + + ip_hdr->src.addr = ip_addr_get_ip4_u32(&net_if->ip_addr); + ip_hdr->_chksum = 0; + ip_hdr->_chksum = alg_iphdr_chksum((u16 *)ip_hdr, iphdr_len); + + if (0 != udp_hdr->chksum) + { + udp_hdr->chksum = 0; + udp_hdr->chksum = alg_tcpudphdr_chksum(ip_hdr->src.addr, + ip_hdr->dest.addr, + IP_PROTO_UDP, + (u16 *)udp_hdr, + ntohs(ip_hdr->_len) - iphdr_len); + } + + if ((ip_addr_get_ip4_u32(&net_if->ip_addr) & 0xFFFFFF) == (ip_hdr->dest.addr & 0xFFFFFF)) + { + err = alg_output2(net_if, ip_hdr); + } + else /* to the internet */ + { + memcpy(ehdr, wpa_supplicant_get_bssid(), ETH_ALEN); + memcpy(ehdr + ETH_ALEN, mac, ETH_ALEN); + /* forward to ap...*/ + err = alg_output(ehdr, eth_len); + } + + if (1 == is_dns) + { + is_dns = 2; + goto redo; + } + } + /* form ap... */ + else + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_lock(napt_table_lock_4udp); +#endif + napt = alg_napt_get_udp_port_by_dest(udp_hdr->dest); + /* forward to sta... */ + if (NULL != napt) + { + alg_napt_table_update_4udp(napt); + + ip_hdr->dest.addr = (napt->src_ip << 24) | (ip_addr_get_ip4_u32(&net_if->next->ip_addr) & 0x00ffffff); + + udp_hdr->dest = napt->src_port; + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4udp); +#endif + + if (53 == ntohs(udp_hdr->src)) + { + ip_hdr->src.addr = ip_addr_get_ip4_u32(&net_if->next->ip_addr); + } + + ip_hdr->_chksum = 0; + ip_hdr->_chksum = alg_iphdr_chksum((u16 *)ip_hdr, iphdr_len); + + if (0 != udp_hdr->chksum) + { + udp_hdr->chksum = 0; + udp_hdr->chksum = alg_tcpudphdr_chksum(ip_hdr->src.addr, + ip_hdr->dest.addr, + IP_PROTO_UDP, + (u16 *)udp_hdr, + ntohs(ip_hdr->_len) - iphdr_len); + } + + memcpy(ehdr, tls_dhcps_getmac((ip_addr_t *)&ip_hdr->dest.addr), ETH_ALEN); + memcpy(ehdr + ETH_ALEN, mac2, ETH_ALEN); + err = alg_output(ehdr, eth_len); + } + /* deliver to default gateway */ + else + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4udp); +#endif + + err = alg_deliver2lwip(bssid, ehdr, eth_len); + } + } + +end: + return err; +} + +/***************************************************************************** + Prototype : alg_gre_proc + Description : vpn input + Input : u8 *bssid + struct ip_hdr *ip_hdr + u8 *ehdr + u16 eth_len + Output : None + Return Value : int 0 success + -1 fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + + Note: + -------- ap... ----------- sta... --------- + | AP |---------------| APSTA |---------------| STA | + -------- ----------- --------- +*****************************************************************************/ +static int alg_gre_proc(const u8 *bssid, struct ip_hdr *ip_hdr, u8 *ehdr, u16 eth_len) +{ + int err; + u8 src_ip; + u8 iphdr_len; + struct netif *net_if = tls_get_netif(); + u8 *mac = wpa_supplicant_get_mac(); + u8 *mac2 = hostapd_get_mac(); + + iphdr_len = (ip_hdr->_v_hl & 0x0F) * 4; + + /* from sta... */ + if (0 == compare_ether_addr(bssid, mac2)) + { + src_ip = ip_hdr->src.addr >> 24; + + if (src_ip == gre_info.src_ip) /* current vpn */ + { + gre_info.time_stamp++; + } + else if (true == gre_info.is_used)/* vpn used */ + { + return -1; + } + else /* new vpn */ + { + gre_info.is_used = true; + gre_info.src_ip = src_ip; + if (!++gre_info.time_stamp) + gre_info.time_stamp++; + } + + ip_hdr->src.addr = ip_addr_get_ip4_u32(&net_if->ip_addr); + ip_hdr->_chksum = 0; + ip_hdr->_chksum = alg_iphdr_chksum((u16 *)ip_hdr, iphdr_len); + + if ((ip_addr_get_ip4_u32(&net_if->ip_addr) & 0xFFFFFF) == (ip_hdr->dest.addr & 0xFFFFFF)) + { + err = alg_output2(net_if, ip_hdr); + } + else /* to the internet */ + { + memcpy(ehdr, wpa_supplicant_get_bssid(), ETH_ALEN); + memcpy(ehdr + ETH_ALEN, mac, ETH_ALEN); + /* forward to ap...*/ + err = alg_output(ehdr, eth_len); + } + } + /* from ap... */ + else + { + ip_hdr->dest.addr = (gre_info.src_ip << 24) | (ip_addr_get_ip4_u32(&net_if->next->ip_addr) & 0x00ffffff); + ip_hdr->_chksum = 0; + ip_hdr->_chksum = alg_iphdr_chksum((u16 *)ip_hdr, iphdr_len); + + memcpy(ehdr, tls_dhcps_getmac((ip_addr_t *)&ip_hdr->dest.addr), ETH_ALEN); + memcpy(ehdr + ETH_ALEN, mac2, ETH_ALEN); + err = alg_output(ehdr, eth_len); + } + + return err; +} + +/***************************************************************************** + Prototype : alg_input + Description : ip packet input + Input : u8 *bssid BSSID + u8 *pkt_body packet + u32 pkt_len length + Output : None + Return Value : int 0 success + -1 fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +int alg_input(const u8 *bssid, u8 *pkt_body, u32 pkt_len) +{ + int err; + struct ip_hdr *ip_hdr; + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_table_check_try(); +#endif + + ip_hdr = (struct ip_hdr *)(pkt_body + NAPT_ETH_HDR_LEN); + switch(ip_hdr->_proto) + { + case IP_PROTO_ICMP: + { + err = alg_icmp_proc(bssid, ip_hdr, pkt_body, (u16)pkt_len); + break; + } + case IP_PROTO_TCP: + { + err = alg_tcp_proc(bssid, ip_hdr, pkt_body, (u16)pkt_len); + break; + } + case IP_PROTO_UDP: + { + err = alg_udp_proc(bssid, ip_hdr, pkt_body, (u16)pkt_len); + break; + } + case IP_PROTO_GRE:/* vpn */ + { + err = alg_gre_proc(bssid, ip_hdr, pkt_body, (u16)pkt_len); + } + default: + { + err = -1; + break; + } + } + + return err; +} + +/***************************************************************************** + Prototype : alg_napt_port_is_used + Description : port is used + Input : u16 port port number + Output : None + Return Value : bool true used + false unused + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +bool alg_napt_port_is_used(u16 port) +{ + bool is_used = false; + struct napt_addr_4tu *napt_tcp; + struct napt_addr_4tu *napt_udp; + +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_lock(napt_table_lock_4tcp); +#endif + NAPT_TABLE_FOREACH(napt_tcp, napt_table_4tcp) + { + if (port == napt_tcp->new_port) + { + is_used = true; + break; + } + } +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4tcp); +#endif + + if (true != is_used) + { +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_lock(napt_table_lock_4udp); +#endif + NAPT_TABLE_FOREACH(napt_udp, napt_table_4udp) + { + if (port == napt_udp->new_port) + { + is_used = true; + break; + } + } +#ifdef NAPT_TABLE_MUTEX_LOCK + alg_napt_unlock(napt_table_lock_4udp); +#endif + } + + return is_used; +} + +/***************************************************************************** + Prototype : alg_napt_init + Description : Network Address Port Translation£¨napt£©initialize + Input : void + Output : None + Return Value : int 0 success + other fail + ------------------------------------------------------------------------------ + + History : + 1.Date : 2015/3/10 + Author : Li Limin, lilm@winnermicro.com + Modification : Created function + +*****************************************************************************/ +int alg_napt_init(void) +{ + int err = 0; + struct tls_timer_cfg timer_cfg; + + memset(&napt_table_4tcp, 0, sizeof(struct napt_table_head_4tu)); + memset(&napt_table_4udp, 0, sizeof(struct napt_table_head_4tu)); + memset(&napt_table_4ic, 0, sizeof(struct napt_table_head_4ic)); + + napt_curr_port = NAPT_LOCAL_PORT_RANGE_START; + napt_curr_id = NAPT_ICMP_ID_RANGE_START; + +#ifdef NAPT_TABLE_MUTEX_LOCK + err = tls_os_sem_create(&napt_table_lock_4tcp, 1); + if (TLS_OS_SUCCESS != err) + { + NAPT_PRINT("failed to init alg.\n"); + return err; + } + + err = tls_os_sem_create(&napt_table_lock_4udp, 1); + if (TLS_OS_SUCCESS != err) + { + NAPT_PRINT("failed to init alg.\n"); + return err; + } + + err = tls_os_sem_create(&napt_table_lock_4ic, 1); + if (TLS_OS_SUCCESS != err) + { + NAPT_PRINT("failed to init alg.\n"); + } +#endif + +#ifdef NAPT_ALLOC_DEBUG + napt4ic_cnt = 0; + napt4tcp_cnt = 0; + napt4udp_cnt = 0; +#endif + + memset(&timer_cfg, 0, sizeof(timer_cfg)); + timer_cfg.unit = TLS_TIMER_UNIT_MS; + timer_cfg.timeout = NAPT_TMR_INTERVAL; + timer_cfg.is_repeat = TRUE; + timer_cfg.callback = alg_napt_table_check; + err = tls_timer_create(&timer_cfg); + if (WM_TIMER_ID_INVALID != err) + { + tls_timer_start(err); + err = 0; + } + else + { + NAPT_PRINT("failed to init alg timer.\n"); + } + + return err; +} + +#endif + diff --git a/sdk/Src/Network/lwip2.0.3/core/def.c b/sdk/Src/Network/lwip2.0.3/core/def.c new file mode 100644 index 0000000..63cb74b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/def.c @@ -0,0 +1,222 @@ +/** + * @file + * Common functions used throughout the stack. + * + * These are reference implementations of the byte swapping functions. + * Again with the aim of being simple, correct and fully portable. + * Byte swapping is the second thing you would want to optimize. You will + * need to port it to your architecture and in your cc.h: + * + * \#define lwip_htons(x) your_htons + * \#define lwip_htonl(x) your_htonl + * + * Note lwip_ntohs() and lwip_ntohl() are merely references to the htonx counterparts. + * + * If you \#define them to htons() and htonl(), you should + * \#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from + * defining htonx/ntohx compatibility macros. + + * @defgroup sys_nonstandard Non-standard functions + * @ingroup sys_layer + * lwIP provides default implementations for non-standard functions. + * These can be mapped to OS functions to reduce code footprint if desired. + * All defines related to this section must not be placed in lwipopts.h, + * but in arch/cc.h! + * These options cannot be \#defined in lwipopts.h since they are not options + * of lwIP itself, but options of the lwIP port to your system. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#include + +#if BYTE_ORDER == LITTLE_ENDIAN + +#if !defined(lwip_htons) +/** + * Convert an u16_t from host- to network byte order. + * + * @param n u16_t in host byte order + * @return n in network byte order + */ +u16_t +lwip_htons(u16_t n) +{ + return (u16_t)PP_HTONS(n); +} +#endif /* lwip_htons */ + +#if !defined(lwip_htonl) +/** + * Convert an u32_t from host- to network byte order. + * + * @param n u32_t in host byte order + * @return n in network byte order + */ +u32_t +lwip_htonl(u32_t n) +{ + return (u32_t)PP_HTONL(n); +} +#endif /* lwip_htonl */ + +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +#ifndef lwip_strnstr +/** + * @ingroup sys_nonstandard + * lwIP default implementation for strnstr() non-standard function. + * This can be \#defined to strnstr() depending on your platform port. + */ +char* +lwip_strnstr(const char* buffer, const char* token, size_t n) +{ + const char* p; + size_t tokenlen = strlen(token); + if (tokenlen == 0) { + return LWIP_CONST_CAST(char *, buffer); + } + for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) { + if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) { + return LWIP_CONST_CAST(char *, p); + } + } + return NULL; +} +#endif + +#ifndef lwip_stricmp +/** + * @ingroup sys_nonstandard + * lwIP default implementation for stricmp() non-standard function. + * This can be \#defined to stricmp() depending on your platform port. + */ +int +lwip_stricmp(const char* str1, const char* str2) +{ + char c1, c2; + + do { + c1 = *str1++; + c2 = *str2++; + if (c1 != c2) { + char c1_upc = c1 | 0x20; + if ((c1_upc >= 'a') && (c1_upc <= 'z')) { + /* characters are not equal an one is in the alphabet range: + downcase both chars and check again */ + char c2_upc = c2 | 0x20; + if (c1_upc != c2_upc) { + /* still not equal */ + /* don't care for < or > */ + return 1; + } + } else { + /* characters are not equal but none is in the alphabet range */ + return 1; + } + } + } while (c1 != 0); + return 0; +} +#endif + +#ifndef lwip_strnicmp +/** + * @ingroup sys_nonstandard + * lwIP default implementation for strnicmp() non-standard function. + * This can be \#defined to strnicmp() depending on your platform port. + */ +int +lwip_strnicmp(const char* str1, const char* str2, size_t len) +{ + char c1, c2; + + do { + c1 = *str1++; + c2 = *str2++; + if (c1 != c2) { + char c1_upc = c1 | 0x20; + if ((c1_upc >= 'a') && (c1_upc <= 'z')) { + /* characters are not equal an one is in the alphabet range: + downcase both chars and check again */ + char c2_upc = c2 | 0x20; + if (c1_upc != c2_upc) { + /* still not equal */ + /* don't care for < or > */ + return 1; + } + } else { + /* characters are not equal but none is in the alphabet range */ + return 1; + } + } + } while (len-- && c1 != 0); + return 0; +} +#endif + +#ifndef lwip_itoa +/** + * @ingroup sys_nonstandard + * lwIP default implementation for itoa() non-standard function. + * This can be \#defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform port. + */ +void +lwip_itoa(char* result, size_t bufsize, int number) +{ + const int base = 10; + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + LWIP_UNUSED_ARG(bufsize); + + do { + tmp_value = number; + number /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - number * base)]; + } while(number); + + /* Apply negative sign */ + if (tmp_value < 0) { + *ptr++ = '-'; + } + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } +} +#endif diff --git a/sdk/Src/Network/lwip2.0.3/core/dns.c b/sdk/Src/Network/lwip2.0.3/core/dns.c new file mode 100644 index 0000000..bf6306f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/dns.c @@ -0,0 +1,1573 @@ +/** + * @file + * DNS - host name to IP address resolver. + * + * @defgroup dns DNS + * @ingroup callbackstyle_api + * + * Implements a DNS host name to IP address resolver. + * + * The lwIP DNS resolver functions are used to lookup a host name and + * map it to a numerical IP address. It maintains a list of resolved + * hostnames that can be queried with the dns_lookup() function. + * New hostnames can be resolved using the dns_query() function. + * + * The lwIP version of the resolver also adds a non-blocking version of + * gethostbyname() that will work with a raw API application. This function + * checks for an IP address string first and converts it if it is valid. + * gethostbyname() then does a dns_lookup() to see if the name is + * already in the table. If so, the IP is returned. If not, a query is + * issued and the function returns with a ERR_INPROGRESS status. The app + * using the dns client must then go into a waiting state. + * + * Once a hostname has been resolved (or found to be non-existent), + * the resolver code calls a specified callback function (which + * must be implemented by the module that uses the resolver). + * + * Multicast DNS queries are supported for names ending on ".local". + * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762 + * chapter 5.1), this is not a fully compliant implementation of continuous + * mDNS querying! + * + * All functions must be called from TCPIP thread. + * + * @see @ref netconn_common for thread-safe access. + */ + +/* + * Port to lwIP from uIP + * by Jim Pettinato April 2007 + * + * security fixes and more by Simon Goldschmidt + * + * uIP version Copyright (c) 2002-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*----------------------------------------------------------------------------- + * RFC 1035 - Domain names - implementation and specification + * RFC 2181 - Clarifications to the DNS Specification + *----------------------------------------------------------------------------*/ + +/** @todo: define good default values (rfc compliance) */ +/** @todo: improve answer parsing, more checkings... */ +/** @todo: check RFC1035 - 7.3. Processing responses */ +/** @todo: one-shot mDNS: dual-stack fallback to another IP version */ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/dns.h" +#include "lwip/prot/dns.h" + +#include + +/** Random generator function to create random TXIDs and source ports for queries */ +#ifndef DNS_RAND_TXID +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0) +#define DNS_RAND_TXID LWIP_RAND +#else +static u16_t dns_txid; +#define DNS_RAND_TXID() (++dns_txid) +#endif +#endif + +/** Limits the source port to be >= 1024 by default */ +#ifndef DNS_PORT_ALLOWED +#define DNS_PORT_ALLOWED(port) ((port) >= 1024) +#endif + +/** DNS maximum number of retries when asking for a name, before "timeout". */ +#ifndef DNS_MAX_RETRIES +#define DNS_MAX_RETRIES 4 +#endif + +/** DNS resource record max. TTL (one week as default) */ +#ifndef DNS_MAX_TTL +#define DNS_MAX_TTL 604800 +#elif DNS_MAX_TTL > 0x7FFFFFFF +#error DNS_MAX_TTL must be a positive 32-bit value +#endif + +#if DNS_TABLE_SIZE > 255 +#error DNS_TABLE_SIZE must fit into an u8_t +#endif +#if DNS_MAX_SERVERS > 255 +#error DNS_MAX_SERVERS must fit into an u8_t +#endif + +/* The number of parallel requests (i.e. calls to dns_gethostbyname + * that cannot be answered from the DNS table. + * This is set to the table size by default. + */ +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) +#ifndef DNS_MAX_REQUESTS +#define DNS_MAX_REQUESTS DNS_TABLE_SIZE +#else +#if DNS_MAX_REQUESTS > 255 +#error DNS_MAX_REQUESTS must fit into an u8_t +#endif +#endif +#else +/* In this configuration, both arrays have to have the same size and are used + * like one entry (used/free) */ +#define DNS_MAX_REQUESTS DNS_TABLE_SIZE +#endif + +/* The number of UDP source ports used in parallel */ +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) +#ifndef DNS_MAX_SOURCE_PORTS +#define DNS_MAX_SOURCE_PORTS DNS_MAX_REQUESTS +#else +#if DNS_MAX_SOURCE_PORTS > 255 +#error DNS_MAX_SOURCE_PORTS must fit into an u8_t +#endif +#endif +#else +#ifdef DNS_MAX_SOURCE_PORTS +#undef DNS_MAX_SOURCE_PORTS +#endif +#define DNS_MAX_SOURCE_PORTS 1 +#endif + +#if LWIP_IPV4 && LWIP_IPV6 +#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6)) +#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) (IP_IS_V6_VAL(ip) ? LWIP_DNS_ADDRTYPE_IS_IPV6(t) : (!LWIP_DNS_ADDRTYPE_IS_IPV6(t))) +#define LWIP_DNS_ADDRTYPE_ARG(x) , x +#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x +#define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0) +#else +#if LWIP_IPV6 +#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1 +#else +#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0 +#endif +#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1 +#define LWIP_DNS_ADDRTYPE_ARG(x) +#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0 +#define LWIP_DNS_SET_ADDRTYPE(x, y) +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + +#if LWIP_DNS_SUPPORT_MDNS_QUERIES +#define LWIP_DNS_ISMDNS_ARG(x) , x +#else +#define LWIP_DNS_ISMDNS_ARG(x) +#endif + +/** DNS query message structure. + No packing needed: only used locally on the stack. */ +struct dns_query { + /* DNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; +}; +#define SIZEOF_DNS_QUERY 4 + +/** DNS answer message structure. + No packing needed: only used locally on the stack. */ +struct dns_answer { + /* DNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; + u32_t ttl; + u16_t len; +}; +#define SIZEOF_DNS_ANSWER 10 +/* maximum allowed size for the struct due to non-packed */ +#define SIZEOF_DNS_ANSWER_ASSERT 12 + +/* DNS table entry states */ +typedef enum { + DNS_STATE_UNUSED = 0, + DNS_STATE_NEW = 1, + DNS_STATE_ASKING = 2, + DNS_STATE_DONE = 3 +} dns_state_enum_t; + +/** DNS table entry */ +struct dns_table_entry { + u32_t ttl; + ip_addr_t ipaddr; + u16_t txid; + u8_t state; + u8_t server_idx; + u8_t tmr; + u8_t retries; + u8_t seqno; +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) + u8_t pcb_idx; +#endif + char name[DNS_MAX_NAME_LENGTH]; +#if LWIP_IPV4 && LWIP_IPV6 + u8_t reqaddrtype; +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + u8_t is_mdns; +#endif +}; + +/** DNS request table entry: used when dns_gehostbyname cannot answer the + * request from the DNS table */ +struct dns_req_entry { + /* pointer to callback on DNS query done */ + dns_found_callback found; + /* argument passed to the callback function */ + void *arg; +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) + u8_t dns_table_idx; +#endif +#if LWIP_IPV4 && LWIP_IPV6 + u8_t reqaddrtype; +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +}; + +#if DNS_LOCAL_HOSTLIST + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Local host-list. For hostnames in this list, no + * external name resolution is performed */ +static struct local_hostlist_entry *local_hostlist_dynamic; +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE +#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST +#define DNS_LOCAL_HOSTLIST_STORAGE_POST +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ +DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] + DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; + +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +static void dns_init_local(void); +static err_t dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)); +#endif /* DNS_LOCAL_HOSTLIST */ + + +/* forward declarations */ +static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port); +static void dns_check_entries(void); +static void dns_call_found(u8_t idx, ip_addr_t* addr); + +/*----------------------------------------------------------------------------- + * Globals + *----------------------------------------------------------------------------*/ + +/* DNS variables */ +static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS]; +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) +static u8_t dns_last_pcb_idx; +#endif +static u8_t dns_seqno; +static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; +static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS]; +static ip_addr_t dns_servers[DNS_MAX_SERVERS]; + +#if LWIP_IPV4 +const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT; +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 +const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT; +#endif /* LWIP_IPV6 */ + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (if DNS_SERVER_ADDRESS is set). + */ +void +dns_init(void) +{ +#ifdef DNS_SERVER_ADDRESS + /* initialize default DNS server address */ + ip_addr_t dnsserver; + DNS_SERVER_ADDRESS(&dnsserver); + dns_setserver(0, &dnsserver); +#endif /* DNS_SERVER_ADDRESS */ + + LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY", + sizeof(struct dns_query) == SIZEOF_DNS_QUERY); + LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER", + sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT); + + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + + /* if dns client not yet initialized... */ +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0) + if (dns_pcbs[0] == NULL) { + dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY); + LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL); + + /* initialize DNS table not needed (initialized to zero since it is a + * global variable) */ + LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", + DNS_STATE_UNUSED == 0); + + /* initialize DNS client */ + udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0); + udp_recv(dns_pcbs[0], dns_recv, NULL); + } +#endif + +#if DNS_LOCAL_HOSTLIST + dns_init_local(); +#endif +} + +/** + * @ingroup dns + * Initialize one of the DNS servers. + * + * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS + * @param dnsserver IP address of the DNS server to set + */ +void +dns_setserver(u8_t numdns, const ip_addr_t *dnsserver) +{ + if (numdns < DNS_MAX_SERVERS) { + if (dnsserver != NULL) { + dns_servers[numdns] = (*dnsserver); + } else { + dns_servers[numdns] = *IP_ADDR_ANY; + } + } +} + +/** + * @ingroup dns + * Obtain one of the currently configured DNS server. + * + * @param numdns the index of the DNS server + * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS + * server has not been configured. + */ +const ip_addr_t* +dns_getserver(u8_t numdns) +{ + if (numdns < DNS_MAX_SERVERS) { + return &dns_servers[numdns]; + } else { + return IP_ADDR_ANY; + } +} + +/** + * The DNS resolver client timer - handle retries and timeouts and should + * be called every DNS_TMR_INTERVAL milliseconds (every second by default). + */ +void +dns_tmr(void) +{ + LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); + dns_check_entries(); +} + +#if DNS_LOCAL_HOSTLIST +static void +dns_init_local(void) +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) + size_t i; + struct local_hostlist_entry *entry; + /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ + struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; + size_t namelen; + for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) { + struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; + LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL); + namelen = strlen(init_entry->name); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); + if (entry != NULL) { + char* entry_name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY(entry_name, init_entry->name, namelen); + entry_name[namelen] = 0; + entry->name = entry_name; + entry->addr = init_entry->addr; + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ +} + +/** + * @ingroup dns + * Iterate the local host-list for a hostname. + * + * @param iterator_fn a function that is called for every entry in the local host-list + * @param iterator_arg 3rd argument passed to iterator_fn + * @return the number of entries in the local host-list + */ +size_t +dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg) +{ + size_t i; +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC + struct local_hostlist_entry *entry = local_hostlist_dynamic; + i = 0; + while (entry != NULL) { + if (iterator_fn != NULL) { + iterator_fn(entry->name, &entry->addr, iterator_arg); + } + i++; + entry = entry->next; + } +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) { + if (iterator_fn != NULL) { + iterator_fn(local_hostlist_static[i].name, &local_hostlist_static[i].addr, iterator_arg); + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + return i; +} + +/** + * @ingroup dns + * Scans the local host-list for a hostname. + * + * @param hostname Hostname to look for in the local host-list + * @param addr the first IP address for the hostname in the local host-list or + * IPADDR_NONE if not found. + * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 (ATTENTION: no fallback here!) + * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 (ATTENTION: no fallback here!) + * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only + * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only + * @return ERR_OK if found, ERR_ARG if not found + */ +err_t +dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype) +{ + LWIP_UNUSED_ARG(dns_addrtype); + return dns_lookup_local(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)); +} + +/* Internal implementation for dns_local_lookup and dns_lookup */ +static err_t +dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)) +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC + struct local_hostlist_entry *entry = local_hostlist_dynamic; + while (entry != NULL) { + if ((lwip_stricmp(entry->name, hostname) == 0) && + LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) { + if (addr) { + ip_addr_copy(*addr, entry->addr); + } + return ERR_OK; + } + entry = entry->next; + } +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + size_t i; + for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) { + if ((lwip_stricmp(local_hostlist_static[i].name, hostname) == 0) && + LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) { + if (addr) { + ip_addr_copy(*addr, local_hostlist_static[i].addr); + } + return ERR_OK; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + return ERR_ARG; +} + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** + * @ingroup dns + * Remove all entries from the local host-list for a specific hostname + * and/or IP address + * + * @param hostname hostname for which entries shall be removed from the local + * host-list + * @param addr address for which entries shall be removed from the local host-list + * @return the number of removed entries + */ +int +dns_local_removehost(const char *hostname, const ip_addr_t *addr) +{ + int removed = 0; + struct local_hostlist_entry *entry = local_hostlist_dynamic; + struct local_hostlist_entry *last_entry = NULL; + while (entry != NULL) { + if (((hostname == NULL) || !lwip_stricmp(entry->name, hostname)) && + ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) { + struct local_hostlist_entry *free_entry; + if (last_entry != NULL) { + last_entry->next = entry->next; + } else { + local_hostlist_dynamic = entry->next; + } + free_entry = entry; + entry = entry->next; + memp_free(MEMP_LOCALHOSTLIST, free_entry); + removed++; + } else { + last_entry = entry; + entry = entry->next; + } + } + return removed; +} + +/** + * @ingroup dns + * Add a hostname/IP address pair to the local host-list. + * Duplicates are not checked. + * + * @param hostname hostname of the new entry + * @param addr IP address of the new entry + * @return ERR_OK if succeeded or ERR_MEM on memory error + */ +err_t +dns_local_addhost(const char *hostname, const ip_addr_t *addr) +{ + struct local_hostlist_entry *entry; + size_t namelen; + char* entry_name; + LWIP_ASSERT("invalid host name (NULL)", hostname != NULL); + namelen = strlen(hostname); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + if (entry == NULL) { + return ERR_MEM; + } + entry_name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY(entry_name, hostname, namelen); + entry_name[namelen] = 0; + entry->name = entry_name; + ip_addr_copy(entry->addr, *addr); + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + return ERR_OK; +} +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** + * @ingroup dns + * Look up a hostname in the array of known hostnames. + * + * @note This function only looks in the internal array of known + * hostnames, it does not send out a query for the hostname if none + * was found. The function dns_enqueue() can be used to send a query + * for a hostname. + * + * @param name the hostname to look up + * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to + * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname + * was not found in the cached dns_table. + * @return ERR_OK if found, ERR_ARG if not found + */ +static err_t +dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)) +{ + u8_t i; +#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) +#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */ +#if DNS_LOCAL_HOSTLIST + if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) { + return ERR_OK; + } +#endif /* DNS_LOCAL_HOSTLIST */ +#ifdef DNS_LOOKUP_LOCAL_EXTERN + if (DNS_LOOKUP_LOCAL_EXTERN(name, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) { + return ERR_OK; + } +#endif /* DNS_LOOKUP_LOCAL_EXTERN */ + + /* Walk through name list, return entry if found. If not, return NULL. */ + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + if ((dns_table[i].state == DNS_STATE_DONE) && + (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0) && + LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); + ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + if (addr) { + ip_addr_copy(*addr, dns_table[i].ipaddr); + } + return ERR_OK; + } + } + + return ERR_ARG; +} + +/** + * Compare the "dotted" name "query" with the encoded name "response" + * to make sure an answer from the DNS server matches the current dns_table + * entry (otherwise, answers might arrive late for hostname not on the list + * any more). + * + * @param query hostname (not encoded) from the dns_table + * @param p pbuf containing the encoded hostname in the DNS response + * @param start_offset offset into p where the name starts + * @return 0xFFFF: names differ, other: names equal -> offset behind name + */ +static u16_t +dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset) +{ + int n; + u16_t response_offset = start_offset; + + do { + n = pbuf_try_get_at(p, response_offset++); + if (n < 0) { + return 0xFFFF; + } + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name: cannot be equal since we don't send them */ + return 0xFFFF; + } else { + /* Not compressed name */ + while (n > 0) { + int c = pbuf_try_get_at(p, response_offset); + if (c < 0) { + return 0xFFFF; + } + if ((*query) != (u8_t)c) { + return 0xFFFF; + } + ++response_offset; + ++query; + --n; + } + ++query; + } + n = pbuf_try_get_at(p, response_offset); + if (n < 0) { + return 0xFFFF; + } + } while (n != 0); + + return response_offset + 1; +} + +/** + * Walk through a compact encoded DNS name and return the end of the name. + * + * @param p pbuf containing the name + * @param query_idx start index into p pointing to encoded DNS name in the DNS server response + * @return index to end of the name + */ +static u16_t +dns_skip_name(struct pbuf* p, u16_t query_idx) +{ + int n; + u16_t offset = query_idx; + + do { + n = pbuf_try_get_at(p, offset++); + if (n < 0) { + return 0xFFFF; + } + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name: since we only want to skip it (not check it), stop here */ + break; + } else { + /* Not compressed name */ + if (offset + n >= p->tot_len) { + return 0xFFFF; + } + offset = (u16_t)(offset + n); + } + n = pbuf_try_get_at(p, offset); + if (n < 0) { + return 0xFFFF; + } + } while (n != 0); + + return offset + 1; +} + +/** + * Send a DNS query packet. + * + * @param idx the DNS table entry index for which to send a request + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t +dns_send(u8_t idx) +{ + err_t err; + struct dns_hdr hdr; + struct dns_query qry; + struct pbuf *p; + u16_t query_idx, copy_len; + const char *hostname, *hostname_part; + u8_t n; + u8_t pcb_idx; + struct dns_table_entry* entry = &dns_table[idx]; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", + (u16_t)(entry->server_idx), entry->name)); + LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS); + if (ip_addr_isany_val(dns_servers[entry->server_idx]) +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + && !entry->is_mdns +#endif + ) { + /* DNS server not valid anymore, e.g. PPP netif has been shut down */ + /* call specified callback function if provided */ + dns_call_found(idx, NULL); + /* flush this entry */ + entry->state = DNS_STATE_UNUSED; + return ERR_OK; + } + + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 + + SIZEOF_DNS_QUERY), PBUF_RAM); + if (p != NULL) { + const ip_addr_t* dst; + u16_t dst_port; + /* fill dns header */ + memset(&hdr, 0, SIZEOF_DNS_HDR); + hdr.id = lwip_htons(entry->txid); + hdr.flags1 = DNS_FLAG1_RD; + hdr.numquestions = PP_HTONS(1); + pbuf_take(p, &hdr, SIZEOF_DNS_HDR); + hostname = entry->name; + --hostname; + + /* convert hostname into suitable query format. */ + query_idx = SIZEOF_DNS_HDR; + do { + ++hostname; + hostname_part = hostname; + for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) { + ++n; + } + copy_len = (u16_t)(hostname - hostname_part); + pbuf_put_at(p, query_idx, n); + pbuf_take_at(p, hostname_part, copy_len, query_idx + 1); + query_idx += n + 1; + } while (*hostname != 0); + pbuf_put_at(p, query_idx, 0); + query_idx++; + + /* fill dns query */ + if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) { + qry.type = PP_HTONS(DNS_RRTYPE_AAAA); + } else { + qry.type = PP_HTONS(DNS_RRTYPE_A); + } + qry.cls = PP_HTONS(DNS_RRCLASS_IN); + pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx); + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) + pcb_idx = entry->pcb_idx; +#else + pcb_idx = 0; +#endif + /* send dns packet */ + LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n", + entry->txid, entry->name, entry->server_idx)); +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + if (entry->is_mdns) { + dst_port = DNS_MQUERY_PORT; +#if LWIP_IPV6 + if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) + { + dst = &dns_mquery_v6group; + } +#endif +#if LWIP_IPV4 && LWIP_IPV6 + else +#endif +#if LWIP_IPV4 + { + dst = &dns_mquery_v4group; + } +#endif + } else +#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */ + { + dst_port = DNS_SERVER_PORT; + dst = &dns_servers[entry->server_idx]; + } + err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port); + + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) +static struct udp_pcb* +dns_alloc_random_port(void) +{ + err_t err; + struct udp_pcb* ret; + + ret = udp_new_ip_type(IPADDR_TYPE_ANY); + if (ret == NULL) { + /* out of memory, have to reuse an existing pcb */ + return NULL; + } + do { + u16_t port = (u16_t)DNS_RAND_TXID(); + if (!DNS_PORT_ALLOWED(port)) { + /* this port is not allowed, try again */ + err = ERR_USE; + continue; + } + err = udp_bind(ret, IP_ANY_TYPE, port); + } while (err == ERR_USE); + if (err != ERR_OK) { + udp_remove(ret); + return NULL; + } + udp_recv(ret, dns_recv, NULL); + return ret; +} + +/** + * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used + * for sending a request + * + * @return an index into dns_pcbs + */ +static u8_t +dns_alloc_pcb(void) +{ + u8_t i; + u8_t idx; + + for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) { + if (dns_pcbs[i] == NULL) { + break; + } + } + if (i < DNS_MAX_SOURCE_PORTS) { + dns_pcbs[i] = dns_alloc_random_port(); + if (dns_pcbs[i] != NULL) { + /* succeeded */ + dns_last_pcb_idx = i; + return i; + } + } + /* if we come here, creating a new UDP pcb failed, so we have to use + an already existing one */ + for (i = 0, idx = dns_last_pcb_idx + 1; i < DNS_MAX_SOURCE_PORTS; i++, idx++) { + if (idx >= DNS_MAX_SOURCE_PORTS) { + idx = 0; + } + if (dns_pcbs[idx] != NULL) { + dns_last_pcb_idx = idx; + return idx; + } + } + return DNS_MAX_SOURCE_PORTS; +} +#endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */ + +/** + * dns_call_found() - call the found callback and check if there are duplicate + * entries for the given hostname. If there are any, their found callback will + * be called and they will be removed. + * + * @param idx dns table index of the entry that is resolved or removed + * @param addr IP address for the hostname (or NULL on error or memory shortage) + */ +static void +dns_call_found(u8_t idx, ip_addr_t* addr) +{ +#if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0) + u8_t i; +#endif + +#if LWIP_IPV4 && LWIP_IPV6 + if (addr != NULL) { + /* check that address type matches the request and adapt the table entry */ + if (IP_IS_V6_VAL(*addr)) { + LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype)); + dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6; + } else { + LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype)); + dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4; + } + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) + for (i = 0; i < DNS_MAX_REQUESTS; i++) { + if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) { + (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg); + /* flush this entry */ + dns_requests[i].found = NULL; + } + } +#else + if (dns_requests[idx].found) { + (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg); + } + dns_requests[idx].found = NULL; +#endif +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) + /* close the pcb used unless other request are using it */ + for (i = 0; i < DNS_MAX_REQUESTS; i++) { + if (i == idx) { + continue; /* only check other requests */ + } + if (dns_table[i].state == DNS_STATE_ASKING) { + if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) { + /* another request is still using the same pcb */ + dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS; + break; + } + } + } + if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) { + /* if we come here, the pcb is not used any more and can be removed */ + udp_remove(dns_pcbs[dns_table[idx].pcb_idx]); + dns_pcbs[dns_table[idx].pcb_idx] = NULL; + dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS; + } +#endif +} + +/* Create a query transmission ID that is unique for all outstanding queries */ +static u16_t +dns_create_txid(void) +{ + u16_t txid; + u8_t i; + +again: + txid = (u16_t)DNS_RAND_TXID(); + + /* check whether the ID is unique */ + for (i = 0; i < DNS_TABLE_SIZE; i++) { + if ((dns_table[i].state == DNS_STATE_ASKING) && + (dns_table[i].txid == txid)) { + /* ID already used by another pending query */ + goto again; + } + } + + return txid; +} + +/** + * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query. + * Check an entry in the dns_table: + * - send out query for new entries + * - retry old pending entries on timeout (also with different servers) + * - remove completed entries from the table if their TTL has expired + * + * @param i index of the dns_table entry to check + */ +static void +dns_check_entry(u8_t i) +{ + err_t err; + struct dns_table_entry *entry = &dns_table[i]; + + LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); + + switch (entry->state) { + case DNS_STATE_NEW: + /* initialize new entry */ + entry->txid = dns_create_txid(); + entry->state = DNS_STATE_ASKING; + entry->server_idx = 0; + entry->tmr = 1; + entry->retries = 0; + + /* send DNS packet for this entry */ + err = dns_send(i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + break; + case DNS_STATE_ASKING: + if (--entry->tmr == 0) { + if (++entry->retries == DNS_MAX_RETRIES) { + if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1]) +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + && !entry->is_mdns +#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */ + ) { + /* change of server */ + entry->server_idx++; + entry->tmr = 1; + entry->retries = 0; + } else { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name)); + /* call specified callback function if provided */ + dns_call_found(i, NULL); + /* flush this entry */ + entry->state = DNS_STATE_UNUSED; + break; + } + } else { + /* wait longer for the next retry */ + entry->tmr = entry->retries; + } + + /* send DNS packet for this entry */ + err = dns_send(i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + } + break; + case DNS_STATE_DONE: + /* if the time to live is nul */ + if ((entry->ttl == 0) || (--entry->ttl == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name)); + /* flush this entry, there cannot be any related pending entries in this state */ + entry->state = DNS_STATE_UNUSED; + } + break; + case DNS_STATE_UNUSED: + /* nothing to do */ + break; + default: + LWIP_ASSERT("unknown dns_table entry state:", 0); + break; + } +} + +/** + * Call dns_check_entry for each entry in dns_table - check all entries. + */ +static void +dns_check_entries(void) +{ + u8_t i; + + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + dns_check_entry(i); + } +} + +/** + * Save TTL and call dns_call_found for correct response. + */ +static void +dns_correct_response(u8_t idx, u32_t ttl) +{ + struct dns_table_entry *entry = &dns_table[idx]; + + entry->state = DNS_STATE_DONE; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name)); + ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr))); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + + /* read the answer resource record's TTL, and maximize it if needed */ + entry->ttl = ttl; + if (entry->ttl > DNS_MAX_TTL) { + entry->ttl = DNS_MAX_TTL; + } + dns_call_found(idx, &entry->ipaddr); + + if (entry->ttl == 0) { + /* RFC 883, page 29: "Zero values are + interpreted to mean that the RR can only be used for the + transaction in progress, and should not be cached." + -> flush this entry now */ + /* entry reused during callback? */ + if (entry->state == DNS_STATE_DONE) { + entry->state = DNS_STATE_UNUSED; + } + } +} +/** + * Receive input function for DNS response packets arriving for the dns UDP pcb. + */ +static void +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + u8_t i; + u16_t txid; + u16_t res_idx; + struct dns_hdr hdr; + struct dns_answer ans; + struct dns_query qry; + u16_t nquestions, nanswers; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(port); + + /* is the dns message big enough ? */ + if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); + /* free pbuf and return */ + goto memerr; + } + + /* copy dns payload inside static buffer for processing */ + if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) { + /* Match the ID in the DNS header with the name table. */ + txid = lwip_htons(hdr.id); + for (i = 0; i < DNS_TABLE_SIZE; i++) { + const struct dns_table_entry *entry = &dns_table[i]; + if ((entry->state == DNS_STATE_ASKING) && + (entry->txid == txid)) { + + /* We only care about the question(s) and the answers. The authrr + and the extrarr are simply discarded. */ + nquestions = lwip_htons(hdr.numquestions); + nanswers = lwip_htons(hdr.numanswers); + + /* Check for correct response. */ + if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name)); + goto memerr; /* ignore this packet */ + } + if (nquestions != 1) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); + goto memerr; /* ignore this packet */ + } + +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + if (!entry->is_mdns) +#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */ + { + /* Check whether response comes from the same network address to which the + question was sent. (RFC 5452) */ + if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) { + goto memerr; /* ignore this packet */ + } + } + + /* Check if the name in the "question" part match with the name in the entry and + skip it if equal. */ + res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR); + if (res_idx == 0xFFFF) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); + goto memerr; /* ignore this packet */ + } + + /* check if "question" part matches the request */ + if (pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx) != SIZEOF_DNS_QUERY) { + goto memerr; /* ignore this packet */ + } + if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) || + (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) || + (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); + goto memerr; /* ignore this packet */ + } + /* skip the rest of the "question" part */ + res_idx += SIZEOF_DNS_QUERY; + + /* Check for error. If so, call callback to inform. */ + if (hdr.flags2 & DNS_FLAG2_ERR_MASK) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name)); + } else { + while ((nanswers > 0) && (res_idx < p->tot_len)) { + /* skip answer resource record's host name */ + res_idx = dns_skip_name(p, res_idx); + if (res_idx == 0xFFFF) { + goto memerr; /* ignore this packet */ + } + + /* Check for IP address type and Internet class. Others are discarded. */ + if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) { + goto memerr; /* ignore this packet */ + } + res_idx += SIZEOF_DNS_ANSWER; + + if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) { +#if LWIP_IPV4 + if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) { +#if LWIP_IPV4 && LWIP_IPV6 + if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + { + ip4_addr_t ip4addr; + /* read the IP address after answer resource record's header */ + if (pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx) != sizeof(ip4_addr_t)) { + goto memerr; /* ignore this packet */ + } + ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr); + pbuf_free(p); + /* handle correct response */ + dns_correct_response(i, lwip_ntohl(ans.ttl)); + return; + } + } +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 + if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_t)))) { +#if LWIP_IPV4 && LWIP_IPV6 + if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + { + ip6_addr_t ip6addr; + /* read the IP address after answer resource record's header */ + if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx) != sizeof(ip6_addr_t)) { + goto memerr; /* ignore this packet */ + } + ip_addr_copy_from_ip6(dns_table[i].ipaddr, ip6addr); + pbuf_free(p); + /* handle correct response */ + dns_correct_response(i, lwip_ntohl(ans.ttl)); + return; + } + } +#endif /* LWIP_IPV6 */ + } + /* skip this answer */ + if ((int)(res_idx + lwip_htons(ans.len)) > 0xFFFF) { + goto memerr; /* ignore this packet */ + } + res_idx += lwip_htons(ans.len); + --nanswers; + } +#if LWIP_IPV4 && LWIP_IPV6 + if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || + (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) { + if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) { + /* IPv4 failed, try IPv6 */ + dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6; + } else { + /* IPv6 failed, try IPv4 */ + dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4; + } + pbuf_free(p); + dns_table[i].state = DNS_STATE_NEW; + dns_check_entry(i); + return; + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name)); + } + /* call callback to indicate error, clean up memory and return */ + pbuf_free(p); + dns_call_found(i, NULL); + dns_table[i].state = DNS_STATE_UNUSED; + return; + } + } + } + +memerr: + /* deallocate memory and return */ + pbuf_free(p); + return; +} + +/** + * Queues a new hostname to resolve and sends out a DNS query for that hostname + * + * @param name the hostname that is to be queried + * @param hostnamelen length of the hostname + * @param found a callback function to be called on success, failure or timeout + * @param callback_arg argument to pass to the callback function + * @return err_t return code. + */ +static err_t +dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, + void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns)) +{ + u8_t i; + u8_t lseq, lseqi; + struct dns_table_entry *entry = NULL; + size_t namelen; + struct dns_req_entry* req; + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) + u8_t r; + /* check for duplicate entries */ + for (i = 0; i < DNS_TABLE_SIZE; i++) { + if ((dns_table[i].state == DNS_STATE_ASKING) && + (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0)) { +#if LWIP_IPV4 && LWIP_IPV6 + if (dns_table[i].reqaddrtype != dns_addrtype) { + /* requested address types don't match + this can lead to 2 concurrent requests, but mixing the address types + for the same host should not be that common */ + continue; + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + /* this is a duplicate entry, find a free request entry */ + for (r = 0; r < DNS_MAX_REQUESTS; r++) { + if (dns_requests[r].found == 0) { + dns_requests[r].found = found; + dns_requests[r].arg = callback_arg; + dns_requests[r].dns_table_idx = i; + LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype); + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name)); + return ERR_INPROGRESS; + } + } + } + } + /* no duplicate entries found */ +#endif + + /* search an unused entry, or the oldest one */ + lseq = 0; + lseqi = DNS_TABLE_SIZE; + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + entry = &dns_table[i]; + /* is it an unused entry ? */ + if (entry->state == DNS_STATE_UNUSED) { + break; + } + /* check if this is the oldest completed entry */ + if (entry->state == DNS_STATE_DONE) { + u8_t age = dns_seqno - entry->seqno; + if (age > lseq) { + lseq = age; + lseqi = i; + } + } + } + + /* if we don't have found an unused entry, use the oldest completed one */ + if (i == DNS_TABLE_SIZE) { + if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { + /* no entry can be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); + return ERR_MEM; + } else { + /* use the oldest completed one */ + i = lseqi; + entry = &dns_table[i]; + } + } + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) + /* find a free request entry */ + req = NULL; + for (r = 0; r < DNS_MAX_REQUESTS; r++) { + if (dns_requests[r].found == NULL) { + req = &dns_requests[r]; + break; + } + } + if (req == NULL) { + /* no request entry can be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name)); + return ERR_MEM; + } + req->dns_table_idx = i; +#else + /* in this configuration, the entry index is the same as the request index */ + req = &dns_requests[i]; +#endif + + /* use this entry */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); + + /* fill the entry */ + entry->state = DNS_STATE_NEW; + entry->seqno = dns_seqno; + LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype); + LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype); + req->found = found; + req->arg = callback_arg; + namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1); + MEMCPY(entry->name, name, namelen); + entry->name[namelen] = 0; + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) + entry->pcb_idx = dns_alloc_pcb(); + if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) { + /* failed to get a UDP pcb */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name)); + entry->state = DNS_STATE_UNUSED; + req->found = NULL; + return ERR_MEM; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx))); +#endif + +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + entry->is_mdns = is_mdns; +#endif + + dns_seqno++; + + /* force to send query without waiting timer */ + dns_check_entry(i); + + /* dns query is enqueued */ + return ERR_INPROGRESS; +} + +/** + * @ingroup dns + * Resolve a hostname (string) into an IP address. + * NON-BLOCKING callback version for use with raw API!!! + * + * Returns immediately with one of err_t return codes: + * - ERR_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ERR_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ERR_ARG: dns client not initialized or invalid hostname + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a ip_addr_t where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param callback_arg argument to pass to the callback function + * @return a err_t return code. + */ +err_t +dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, + void *callback_arg) +{ + return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT); +} + +/** + * @ingroup dns + * Like dns_gethostbyname, but returned address type can be controlled: + * @param hostname the hostname that is to be queried + * @param addr pointer to a ip_addr_t where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param callback_arg argument to pass to the callback function + * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only + * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only + * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only + * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only + */ +err_t +dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found, + void *callback_arg, u8_t dns_addrtype) +{ + size_t hostnamelen; +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + u8_t is_mdns; +#endif + /* not initialized or no valid server yet, or invalid addr pointer + * or invalid hostname or invalid hostname length */ + if ((addr == NULL) || + (!hostname) || (!hostname[0])) { + return ERR_ARG; + } +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0) + if (dns_pcbs[0] == NULL) { + return ERR_ARG; + } +#endif + hostnamelen = strlen(hostname); + if (hostnamelen >= DNS_MAX_NAME_LENGTH) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve")); + return ERR_ARG; + } + + +#if LWIP_HAVE_LOOPIF + if (strcmp(hostname, "localhost") == 0) { + ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr); + return ERR_OK; + } +#endif /* LWIP_HAVE_LOOPIF */ + + /* host name already in octet notation? set ip addr and return ERR_OK */ + if (ipaddr_aton(hostname, addr)) { +#if LWIP_IPV4 && LWIP_IPV6 + if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) || + (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6))) +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + { + return ERR_OK; + } + } + /* already have this address cached? */ + if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) { + return ERR_OK; + } +#if LWIP_IPV4 && LWIP_IPV6 + if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) { + /* fallback to 2nd IP type and try again to lookup */ + u8_t fallback; + if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) { + fallback = LWIP_DNS_ADDRTYPE_IPV6; + } else { + fallback = LWIP_DNS_ADDRTYPE_IPV4; + } + if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) { + return ERR_OK; + } + } +#else /* LWIP_IPV4 && LWIP_IPV6 */ + LWIP_UNUSED_ARG(dns_addrtype); +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + +#if LWIP_DNS_SUPPORT_MDNS_QUERIES + if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) { + is_mdns = 1; + } else { + is_mdns = 0; + } + + if (!is_mdns) +#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */ + { + /* prevent calling found callback if no server is set, return error instead */ + if (ip_addr_isany_val(dns_servers[0])) { + return ERR_VAL; + } + } + + /* queue query with specified callback */ + return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype) + LWIP_DNS_ISMDNS_ARG(is_mdns)); +} + +#endif /* LWIP_DNS */ diff --git a/sdk/Src/Network/lwip2.0.3/core/inet_chksum.c b/sdk/Src/Network/lwip2.0.3/core/inet_chksum.c new file mode 100644 index 0000000..589157b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/inet_chksum.c @@ -0,0 +1,609 @@ +/** + * @file + * Incluse internet checksum functions.\n + * + * These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create + * your own version, link it in and in your cc.h put: + * + * \#define LWIP_CHKSUM your_checksum_routine + * + * Or you can select from the implementations below by defining + * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet_chksum.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" + +#include + +#ifndef LWIP_CHKSUM +# define LWIP_CHKSUM lwip_standard_chksum +# ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 2 +# endif +u16_t lwip_standard_chksum(const void *dataptr, int len); +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable length to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +u16_t +lwip_standard_chksum(const void *dataptr, int len) +{ + u32_t acc; + u16_t src; + const u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (const u8_t*)dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000UL) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using lwip_htons() + instead of lwip_ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return lwip_htons((u16_t)acc); +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + */ +u16_t +lwip_standard_chksum(const void *dataptr, int len) +{ + const u8_t *pb = (const u8_t *)dataptr; + const u16_t *ps; + u16_t t = 0; + u32_t sum = 0; + int odd = ((mem_ptr_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (const u16_t *)(const void *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) { + ((u8_t *)&t)[0] = *(const u8_t *)ps; + } + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits + calling this twice is probably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ +u16_t +lwip_standard_chksum(const void *dataptr, int len) +{ + const u8_t *pb = (const u8_t *)dataptr; + const u16_t *ps; + u16_t t = 0; + const u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((mem_ptr_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (const u16_t *)(const void*)pb; + + if (((mem_ptr_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (const u32_t *)(const void*)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) { + tmp++; /* add back carry */ + } + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) { + sum++; /* add back carry */ + } + + len -= 8; + } + + /* make room in upper bits */ + sum = FOLD_U32T(sum); + + ps = (const u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(const u8_t *)ps; + } + + sum += t; /* add end bytes */ + + /* Fold 32-bit sum to 16 bits + calling this twice is probably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + + /* iterate through all pbuf in chain */ + for (q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* just executing this next line is probably faster that the if statement needed + to check whether we really need to execute it, and does no harm */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + + acc += (u32_t)lwip_htons((u16_t)proto); + acc += (u32_t)lwip_htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is probably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +#if LWIP_IPV4 +/* inet_chksum_pseudo: + * + * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + const ip4_addr_t *src, const ip4_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dest destination ipv6 address (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + const ip6_addr_t *src, const ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#endif /* LWIP_IPV6 */ + +/* ip_chksum_pseudo: + * + * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + const ip_addr_t *src, const ip_addr_t *dest) +{ +#if LWIP_IPV6 + if (IP_IS_V6(dest)) { + return ip6_chksum_pseudo(p, proto, proto_len, ip_2_ip6(src), ip_2_ip6(dest)); + } +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 && LWIP_IPV6 + else +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +#if LWIP_IPV4 + { + return inet_chksum_pseudo(p, proto, proto_len, ip_2_ip4(src), ip_2_ip4(dest)); + } +#endif /* LWIP_IPV4 */ +} + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + u16_t chklen; + + /* iterate through all pbuf in chain */ + for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* fold the upper bit down */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + + acc += (u32_t)lwip_htons((u16_t)proto); + acc += (u32_t)lwip_htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is probably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +#if LWIP_IPV4 +/* inet_chksum_pseudo_partial: + * + * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. Will only compute for a + * portion of the payload. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @param chksum_len number of payload bytes used to compute chksum + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dest destination ipv6 address (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} +#endif /* LWIP_IPV6 */ + +/* ip_chksum_pseudo_partial: + * + * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest) +{ +#if LWIP_IPV6 + if (IP_IS_V6(dest)) { + return ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip6(src), ip_2_ip6(dest)); + } +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 && LWIP_IPV6 + else +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +#if LWIP_IPV4 + { + return inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip4(src), ip_2_ip4(dest)); + } +#endif /* LWIP_IPV4 */ +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarily for IP + * and ICMP. + * + * @param dataptr start of the buffer to calculate the checksum (no alignment needed) + * @param len length of the buffer to calculate the checksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ + +u16_t +inet_chksum(const void *dataptr, u16_t len) +{ + return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len); +} + +/** + * Calculate a checksum over a chain of pbufs (without pseudo-header, much like + * inet_chksum only pbufs are used). + * + * @param p pbuf chain over that the checksum should be calculated + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for (q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); +} + +/* These are some implementations for LWIP_CHKSUM_COPY, which copies data + * like MEMCPY but generates a checksum at the same time. Since this is a + * performance-sensitive function, you might want to create your own version + * in assembly targeted at your hardware by defining it in lwipopts.h: + * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len) + */ + +#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */ +/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM. + * For architectures with big caches, data might still be in cache when + * generating the checksum after copying. + */ +u16_t +lwip_chksum_copy(void *dst, const void *src, u16_t len) +{ + MEMCPY(dst, src, len); + return LWIP_CHKSUM(dst, len); +} +#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */ diff --git a/sdk/Src/Network/lwip2.0.3/core/init.c b/sdk/Src/Network/lwip2.0.3/core/init.c new file mode 100644 index 0000000..2d36dc5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/init.c @@ -0,0 +1,386 @@ +/** + * @file + * Modules initialization + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +#include "lwip/opt.h" + +#include "lwip/init.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/sockets.h" +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/timeouts.h" +#include "lwip/etharp.h" +#include "lwip/ip6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/api.h" +#include "lwip/alg.h" + +#include "netif/ppp/ppp_opts.h" +#include "netif/ppp/ppp_impl.h" + +#ifndef LWIP_SKIP_PACKING_CHECK + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct packed_struct_test +{ + PACK_STRUCT_FLD_8(u8_t dummy1); + PACK_STRUCT_FIELD(u32_t dummy2); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define PACKED_STRUCT_TEST_EXPECTED_SIZE 5 + +#endif + +/* Compile-time sanity checks for configuration errors. + * These can be done independently of LWIP_DEBUG, without penalty. + */ +#ifndef BYTE_ORDER + #error "BYTE_ORDER is not defined, you have to define it in your cc.h" +#endif +#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) + #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_UDPLITE) + #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DHCP) + #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_MULTICAST_TX_OPTIONS) + #error "If you want to use IGMP/LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ +#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) + #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" +#endif +#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) + #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) + #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) + #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +#if (LWIP_IGMP && !LWIP_MULTICAST_TX_OPTIONS) + #error "If you want to use IGMP, you have to define LWIP_MULTICAST_TX_OPTIONS==1 in your lwipopts.h" +#endif +#if (LWIP_IGMP && !LWIP_IPV4) + #error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h" +#endif +#if (LWIP_MULTICAST_TX_OPTIONS && !LWIP_IPV4) + #error "LWIP_MULTICAST_TX_OPTIONS needs LWIP_IPV4 enabled in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0))) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#endif /* !MEMP_MEM_MALLOC */ +#if LWIP_WND_SCALE +#if (LWIP_TCP && (TCP_WND > 0xffffffff)) + #error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_RCV_SCALE > 14)) + #error "The maximum valid window scale value is 14!" +#endif +#if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE))) + #error "TCP_WND is bigger than the configured LWIP_WND_SCALE allows!" +#endif +#if (LWIP_TCP && ((TCP_WND >> TCP_RCV_SCALE) == 0)) + #error "TCP_WND is too small for the configured LWIP_WND_SCALE (results in zero window)!" +#endif +#else /* LWIP_WND_SCALE */ +#if (LWIP_TCP && (TCP_WND > 0xffff)) + #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)" +#endif +#endif /* LWIP_WND_SCALE */ +#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) + #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" +#endif +#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) + #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" +#endif +#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff))) + #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" +#endif +#if (LWIP_NETIF_API && (NO_SYS==1)) + #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) + #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if (LWIP_PPP_API && (NO_SYS==1)) + #error "If you want to use PPP API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if (LWIP_PPP_API && (PPP_SUPPORT==0)) + #error "If you want to use PPP API, you have to enable PPP_SUPPORT in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP) + #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK) + #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && LWIP_AUTOIP) + #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) + #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" +#endif +#if (MEM_LIBC_MALLOC && MEM_USE_POOLS) + #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" +#endif +#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) + #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" +#endif +#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) + #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" +#endif +#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT))) + #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST" +#endif +#if PPP_SUPPORT && !PPPOS_SUPPORT && !PPPOE_SUPPORT && !PPPOL2TP_SUPPORT + #error "PPP_SUPPORT needs at least one of PPPOS_SUPPORT, PPPOE_SUPPORT or PPPOL2TP_SUPPORT turned on" +#endif +#if PPP_SUPPORT && !PPP_IPV4_SUPPORT && !PPP_IPV6_SUPPORT + #error "PPP_SUPPORT needs PPP_IPV4_SUPPORT and/or PPP_IPV6_SUPPORT turned on" +#endif +#if PPP_SUPPORT && PPP_IPV4_SUPPORT && !LWIP_IPV4 + #error "PPP_IPV4_SUPPORT needs LWIP_IPV4 turned on" +#endif +#if PPP_SUPPORT && PPP_IPV6_SUPPORT && !LWIP_IPV6 + #error "PPP_IPV6_SUPPORT needs LWIP_IPV6 turned on" +#endif +#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT) + #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT" +#endif +#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING + #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too" +#endif +#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE + #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets" +#endif +#if LWIP_NETCONN && LWIP_TCP +#if NETCONN_COPY != TCP_WRITE_FLAG_COPY + #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" +#endif +#if NETCONN_MORE != TCP_WRITE_FLAG_MORE + #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" +#endif +#endif /* LWIP_NETCONN && LWIP_TCP */ +#if LWIP_SOCKET +/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ +#if SO_REUSEADDR != SOF_REUSEADDR + #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR" +#endif +#if SO_KEEPALIVE != SOF_KEEPALIVE + #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE" +#endif +#if SO_BROADCAST != SOF_BROADCAST + #error "WARNING: SO_BROADCAST != SOF_BROADCAST" +#endif +#endif /* LWIP_SOCKET */ + + +/* Compile-time checks for deprecated options. + */ +#ifdef MEMP_NUM_TCPIP_MSG + #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef TCP_REXMIT_DEBUG + #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef RAW_STATS + #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_QUEUE_FIRST + #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_ALWAYS_INSERT + #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." +#endif +#if !NO_SYS && LWIP_TCPIP_CORE_LOCKING && LWIP_COMPAT_MUTEX && !defined(LWIP_COMPAT_MUTEX_ALLOWED) + #error "LWIP_COMPAT_MUTEX cannot prevent priority inversion. It is recommended to implement priority-aware mutexes. (Define LWIP_COMPAT_MUTEX_ALLOWED to disable this error.)" +#endif + +#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS +#define LWIP_DISABLE_TCP_SANITY_CHECKS 0 +#endif +#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS +#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 +#endif + +/* MEMP sanity checks */ +#if MEMP_MEM_MALLOC +#if !LWIP_DISABLE_MEMP_SANITY_CHECKS +#if LWIP_NETCONN || LWIP_SOCKET +#if !MEMP_NUM_NETCONN && LWIP_SOCKET +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!" +#endif +#else /* MEMP_MEM_MALLOC */ +#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB) +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* LWIP_NETCONN || LWIP_SOCKET */ +#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */ +#if MEM_USE_POOLS +#error "MEMP_MEM_MALLOC and MEM_USE_POOLS cannot be enabled at the same time" +#endif +#ifdef LWIP_HOOK_MEMP_AVAILABLE +#error "LWIP_HOOK_MEMP_AVAILABLE doesn't make sense with MEMP_MEM_MALLOC" +#endif +#endif /* MEMP_MEM_MALLOC */ + +/* TCP sanity checks */ +#if !LWIP_DISABLE_TCP_SANITY_CHECKS +#if LWIP_TCP +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_BUF < (2 * TCP_MSS) + #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDLOWAT >= TCP_SND_BUF + #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!" +#endif +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN + #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) + #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_WND < TCP_MSS + #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* LWIP_TCP */ +#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ + +/** + * @ingroup lwip_nosys + * Initialize all modules. + * Use this in NO_SYS mode. Use tcpip_init() otherwise. + */ +void +lwip_init(void) +{ +#ifndef LWIP_SKIP_CONST_CHECK + int a = 0; + LWIP_UNUSED_ARG(a); + LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void*, &a) == &a); +#endif +#ifndef LWIP_SKIP_PACKING_CHECK + LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE); +#endif + + /* Modules initialization */ + stats_init(); +#if !NO_SYS + sys_init(); +#endif /* !NO_SYS */ + mem_init(); + memp_init(); + pbuf_init(); + netif_init(); +#if LWIP_IPV4 + ip_init(); +#if LWIP_ARP + etharp_init(); +#endif /* LWIP_ARP */ +#endif /* LWIP_IPV4 */ +#if LWIP_RAW + raw_init(); +#endif /* LWIP_RAW */ +#if LWIP_UDP + udp_init(); +#endif /* LWIP_UDP */ +#if LWIP_TCP + tcp_init(); +#endif /* LWIP_TCP */ +#if LWIP_IGMP + igmp_init(); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + dns_init(); +#endif /* LWIP_DNS */ +#if PPP_SUPPORT + ppp_init(); +#endif + +#if LWIP_TIMERS + sys_timeouts_init(); +#endif /* LWIP_TIMERS */ +} diff --git a/sdk/Src/Network/lwip2.0.3/core/ip.c b/sdk/Src/Network/lwip2.0.3/core/ip.c new file mode 100644 index 0000000..247da68 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ip.c @@ -0,0 +1,153 @@ +/** + * @file + * Common IPv4 and IPv6 code + * + * @defgroup ip IP + * @ingroup callbackstyle_api + * + * @defgroup ip4 IPv4 + * @ingroup ip + * + * @defgroup ip6 IPv6 + * @ingroup ip + * + * @defgroup ipaddr IP address handling + * @ingroup infrastructure + * + * @defgroup ip4addr IPv4 only + * @ingroup ipaddr + * + * @defgroup ip6addr IPv6 only + * @ingroup ipaddr + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 || LWIP_IPV6 + +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +/** Global data for both IPv4 and IPv6 */ +struct ip_globals ip_data; + +#if LWIP_IPV4 && LWIP_IPV6 + +const ip_addr_t ip_addr_any_type = IPADDR_ANY_TYPE_INIT; + +/** + * @ingroup ipaddr + * Convert IP address string (both versions) to numeric. + * The version is auto-detected from the string. + * + * @param cp IP address string to convert + * @param addr conversion result is stored here + * @return 1 on success, 0 on error + */ +int +ipaddr_aton(const char *cp, ip_addr_t *addr) +{ + if (cp != NULL) { + const char* c; + for (c = cp; *c != 0; c++) { + if (*c == ':') { + /* contains a colon: IPv6 address */ + if (addr) { + IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V6); + } + return ip6addr_aton(cp, ip_2_ip6(addr)); + } else if (*c == '.') { + /* contains a dot: IPv4 address */ + break; + } + } + /* call ip4addr_aton as fallback or if IPv4 was found */ + if (addr) { + IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V4); + } + return ip4addr_aton(cp, ip_2_ip4(addr)); + } + return 0; +} + +/** + * @ingroup lwip_nosys + * If both IP versions are enabled, this function can dispatch packets to the correct one. + * Don't call directly, pass to netif_add() and call netif->input(). + */ +err_t +ip_input(struct pbuf *p, struct netif *inp) +{ + if (p != NULL) { + if (IP_HDR_GET_VERSION(p->payload) == 6) { + return ip6_input(p, inp); + } + return ip4_input(p, inp); + } + return ERR_VAL; +} + +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + +void print_ipaddr(ip_addr_t *ip) +{ +#if LWIP_IPV6 + if(IP_IS_V6(ip)) + { + //wm_printf("ip: %V\n", ip->u_addr.ip6.addr); + printf("ip: %x:%x:%x:%x:%x:%x:%x:%x\n", IP6_ADDR_BLOCK1(ip_2_ip6(ip)), \ + IP6_ADDR_BLOCK2(ip_2_ip6(ip)), \ + IP6_ADDR_BLOCK3(ip_2_ip6(ip)), \ + IP6_ADDR_BLOCK4(ip_2_ip6(ip)), \ + IP6_ADDR_BLOCK5(ip_2_ip6(ip)), \ + IP6_ADDR_BLOCK6(ip_2_ip6(ip)), \ + IP6_ADDR_BLOCK7(ip_2_ip6(ip)), \ + IP6_ADDR_BLOCK8(ip_2_ip6(ip))); + } + else +#endif + { + #if LWIP_IPV4 + //wm_printf("ip: %v\n", ip->u_addr.ip4.addr); + printf("ip: %d.%d.%d.%d\n", ip4_addr1(ip_2_ip4(ip)), \ + ip4_addr2(ip_2_ip4(ip)), \ + ip4_addr3(ip_2_ip4(ip)), \ + ip4_addr4(ip_2_ip4(ip))); +#endif + } +} + + +#endif /* LWIP_IPV4 || LWIP_IPV6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/autoip.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/autoip.c new file mode 100644 index 0000000..939d7f6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/autoip.c @@ -0,0 +1,527 @@ +/** + * @file + * AutoIP Automatic LinkLocal IP Configuration + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * @defgroup autoip AUTOIP + * @ingroup ip4 + * AUTOIP related functions + * USAGE: + * + * define @ref LWIP_AUTOIP 1 in your lwipopts.h + * Options: + * AUTOIP_TMR_INTERVAL msecs, + * I recommend a value of 100. The value must divide 1000 with a remainder almost 0. + * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... + * + * Without DHCP: + * - Call autoip_start() after netif_add(). + * + * With DHCP: + * - define @ref LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. + * - Configure your DHCP Client. + * + * @see netifapi_autoip + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +/* #include "lwip/udp.h" */ +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/autoip.h" +#include "lwip/etharp.h" +#include "lwip/prot/autoip.h" + +#include + +/** Pseudo random macro based on netif informations. + * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */ +#ifndef LWIP_AUTOIP_RAND +#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ + ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \ + ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \ + ((u32_t)((netif->hwaddr[4]) & 0xff))) + \ + (netif_autoip_data(netif)? netif_autoip_data(netif)->tried_llipaddr : 0)) +#endif /* LWIP_AUTOIP_RAND */ + +/** + * Macro that generates the initial IP address to be tried by AUTOIP. + * If you want to override this, define it to something else in lwipopts.h. + */ +#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR +#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \ + lwip_htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \ + ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8))) +#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */ + +/* static functions */ +static err_t autoip_arp_announce(struct netif *netif); +static void autoip_start_probing(struct netif *netif); + +/** + * @ingroup autoip + * Set a statically allocated struct autoip to work with. + * Using this prevents autoip_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct autoip + * @param autoip (uninitialised) autoip struct allocated by the application + */ +void +autoip_set_struct(struct netif *netif, struct autoip *autoip) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("autoip != NULL", autoip != NULL); + LWIP_ASSERT("netif already has a struct autoip set", + netif_autoip_data(netif) == NULL); + + /* clear data structure */ + memset(autoip, 0, sizeof(struct autoip)); + /* autoip->state = AUTOIP_STATE_OFF; */ + netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip); +} + +/** Restart AutoIP client and check the next address (conflict detected) + * + * @param netif The netif under AutoIP control + */ +static void +autoip_restart(struct netif *netif) +{ + struct autoip* autoip = netif_autoip_data(netif); + autoip->tried_llipaddr++; + autoip_start(netif); +} + +/** + * Handle a IP address conflict after an ARP conflict detection + */ +static void +autoip_handle_arp_conflict(struct netif *netif) +{ + struct autoip* autoip = netif_autoip_data(netif); + + /* RFC3927, 2.5 "Conflict Detection and Defense" allows two options where + a) means retreat on the first conflict and + b) allows to keep an already configured address when having only one + conflict in 10 seconds + We use option b) since it helps to improve the chance that one of the two + conflicting hosts may be able to retain its address. */ + + if (autoip->lastconflict > 0) { + /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n")); + + /* Active TCP sessions are aborted when removing the ip addresss */ + autoip_restart(netif); + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n")); + autoip_arp_announce(netif); + autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } +} + +/** + * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * + * @param netif network interface on which create the IP-Address + * @param ipaddr ip address to initialize + */ +static void +autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr) +{ + struct autoip* autoip = netif_autoip_data(netif); + + /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * compliant to RFC 3927 Section 2.1 + * We have 254 * 256 possibilities */ + + u32_t addr = lwip_ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif)); + addr += autoip->tried_llipaddr; + addr = AUTOIP_NET | (addr & 0xffff); + /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ + + if (addr < AUTOIP_RANGE_START) { + addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + if (addr > AUTOIP_RANGE_END) { + addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) && + (addr <= AUTOIP_RANGE_END)); + ip4_addr_set_u32(ipaddr, lwip_htonl(addr)); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (u16_t)(autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), + ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); +} + +/** + * Sends an ARP probe from a network interface + * + * @param netif network interface used to send the probe + */ +static err_t +autoip_arp_probe(struct netif *netif) +{ + struct autoip* autoip = netif_autoip_data(netif); + /* this works because netif->ip_addr is ANY */ + return etharp_request(netif, &autoip->llipaddr); +} + +/** + * Sends an ARP announce from a network interface + * + * @param netif network interface used to send the announce + */ +static err_t +autoip_arp_announce(struct netif *netif) +{ + return etharp_gratuitous(netif); +} + +/** + * Configure interface for use with current LL IP-Address + * + * @param netif network interface to configure with current LL IP-Address + */ +static err_t +autoip_bind(struct netif *netif) +{ + struct autoip* autoip = netif_autoip_data(netif); + ip4_addr_t sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + + IP4_ADDR(&sn_mask, 255, 255, 0, 0); + IP4_ADDR(&gw_addr, 0, 0, 0, 0); + + netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr); + /* interface is used by routing now that an address is set */ + + return ERR_OK; +} + +/** + * @ingroup autoip + * Start AutoIP client + * + * @param netif network interface on which start the AutoIP client + */ +err_t +autoip_start(struct netif *netif) +{ + struct autoip* autoip = netif_autoip_data(netif); + err_t result = ERR_OK; + + LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;); + + /* Set IP-Address, Netmask and Gateway to 0 to make sure that + * ARP Packets are formed correctly + */ + netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], + netif->name[1], (u16_t)netif->num)); + if (autoip == NULL) { + /* no AutoIP client attached yet? */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): starting new AUTOIP client\n")); + autoip = (struct autoip *)mem_malloc(sizeof(struct autoip)); + if (autoip == NULL) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): could not allocate autoip\n")); + return ERR_MEM; + } + memset(autoip, 0, sizeof(struct autoip)); + /* store this AutoIP client in the netif */ + netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip")); + } else { + autoip->state = AUTOIP_STATE_OFF; + autoip->ttw = 0; + autoip->sent_num = 0; + ip4_addr_set_zero(&autoip->llipaddr); + autoip->lastconflict = 0; + } + + autoip_create_addr(netif, &(autoip->llipaddr)); + autoip_start_probing(netif); + + return result; +} + +static void +autoip_start_probing(struct netif *netif) +{ + struct autoip* autoip = netif_autoip_data(netif); + + autoip->state = AUTOIP_STATE_PROBING; + autoip->sent_num = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + + /* time to wait to first probe, this is randomly + * chosen out of 0 to PROBE_WAIT seconds. + * compliant to RFC 3927 Section 2.2.1 + */ + autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND)); + + /* + * if we tried more then MAX_CONFLICTS we must limit our rate for + * acquiring and probing address + * compliant to RFC 3927 Section 2.2.1 + */ + if (autoip->tried_llipaddr > MAX_CONFLICTS) { + autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } +} + +/** + * Handle a possible change in the network configuration. + * + * If there is an AutoIP address configured, take the interface down + * and begin probing with the same address. + */ +void +autoip_network_changed(struct netif *netif) +{ + struct autoip* autoip = netif_autoip_data(netif); + + if (autoip && (autoip->state != AUTOIP_STATE_OFF)) { + autoip_start_probing(netif); + } +} + +/** + * @ingroup autoip + * Stop AutoIP client + * + * @param netif network interface on which stop the AutoIP client + */ +err_t +autoip_stop(struct netif *netif) +{ + struct autoip* autoip = netif_autoip_data(netif); + + if (autoip != NULL) { + autoip->state = AUTOIP_STATE_OFF; + if (ip4_addr_islinklocal(netif_ip4_addr(netif))) { + netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4); + } + } + return ERR_OK; +} + +/** + * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds + */ +void +autoip_tmr(void) +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + struct autoip* autoip = netif_autoip_data(netif); + /* only act on AutoIP configured interfaces */ + if (autoip != NULL) { + if (autoip->lastconflict > 0) { + autoip->lastconflict--; + } + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n", + (u16_t)(autoip->state), autoip->ttw)); + + if (autoip->ttw > 0) { + autoip->ttw--; + } + + switch(autoip->state) { + case AUTOIP_STATE_PROBING: + if (autoip->ttw == 0) { + if (autoip->sent_num >= PROBE_NUM) { + /* Switch to ANNOUNCING: now we can bind to an IP address and use it */ + autoip->state = AUTOIP_STATE_ANNOUNCING; + autoip_bind(netif); + /* autoip_bind() calls netif_set_addr(): this triggers a gratuitous ARP + which counts as an announcement */ + autoip->sent_num = 1; + autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + } else { + autoip_arp_probe(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() PROBING Sent Probe\n")); + autoip->sent_num++; + if (autoip->sent_num == PROBE_NUM) { + /* calculate time to wait to for announce */ + autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + } else { + /* calculate time to wait to next probe */ + autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) % + ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) + + PROBE_MIN * AUTOIP_TICKS_PER_SECOND); + } + } + } + break; + + case AUTOIP_STATE_ANNOUNCING: + if (autoip->ttw == 0) { + autoip_arp_announce(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() ANNOUNCING Sent Announce\n")); + autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; + autoip->sent_num++; + + if (autoip->sent_num >= ANNOUNCE_NUM) { + autoip->state = AUTOIP_STATE_BOUND; + autoip->sent_num = 0; + autoip->ttw = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + } + } + break; + + default: + /* nothing to do in other states */ + break; + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * Handles every incoming ARP Packet, called by etharp_input(). + * + * @param netif network interface to use for autoip processing + * @param hdr Incoming ARP packet + */ +void +autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + struct autoip* autoip = netif_autoip_data(netif); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n")); + if ((autoip != NULL) && (autoip->state != AUTOIP_STATE_OFF)) { + /* when ip.src == llipaddr && hw.src != netif->hwaddr + * + * when probing ip.dst == llipaddr && hw.src != netif->hwaddr + * we have a conflict and must solve it + */ + ip4_addr_t sipaddr, dipaddr; + struct eth_addr netifaddr; + ETHADDR16_COPY(netifaddr.addr, netif->hwaddr); + + /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + if (autoip->state == AUTOIP_STATE_PROBING) { + /* RFC 3927 Section 2.2.1: + * from beginning to after ANNOUNCE_WAIT + * seconds we have a conflict if + * ip.src == llipaddr OR + * ip.dst == llipaddr && hw.src != own hwaddr + */ + if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) || + (ip4_addr_isany_val(sipaddr) && + ip4_addr_cmp(&dipaddr, &autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Probe Conflict detected\n")); + autoip_restart(netif); + } + } else { + /* RFC 3927 Section 2.5: + * in any state we have a conflict if + * ip.src == llipaddr && hw.src != own hwaddr + */ + if (ip4_addr_cmp(&sipaddr, &autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Conflicting ARP-Packet detected\n")); + autoip_handle_arp_conflict(netif); + } + } + } +} + +/** check if AutoIP supplied netif->ip_addr + * + * @param netif the netif to check + * @return 1 if AutoIP supplied netif->ip_addr (state BOUND or ANNOUNCING), + * 0 otherwise + */ +u8_t +autoip_supplied_address(const struct netif *netif) +{ + if ((netif != NULL) && (netif_autoip_data(netif) != NULL)) { + struct autoip* autoip = netif_autoip_data(netif); + return (autoip->state == AUTOIP_STATE_BOUND) || (autoip->state == AUTOIP_STATE_ANNOUNCING); + } + return 0; +} + +u8_t +autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr) +{ + struct autoip* autoip = netif_autoip_data(netif); + return (autoip != NULL) && ip4_addr_cmp(addr, &(autoip->llipaddr)); +} + +#endif /* LWIP_IPV4 && LWIP_AUTOIP */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/dhcp.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/dhcp.c new file mode 100644 index 0000000..b804edc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/dhcp.c @@ -0,0 +1,2017 @@ +/** + * @file + * Dynamic Host Configuration Protocol client + * + * @defgroup dhcp4 DHCPv4 + * @ingroup ip4 + * DHCP (IPv4) related functions + * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform + * with RFC 2131 and RFC 2132. + * + * @todo: + * - Support for interfaces other than Ethernet (SLIP, PPP, ...) + * + * Options: + * @ref DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute) + * @ref DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer) + * + * dhcp_start() starts a DHCP client instance which + * configures the interface by obtaining an IP address lease and maintaining it. + * + * Use dhcp_release() to end the lease and use dhcp_stop() + * to remove the DHCP client. + * + * @see netifapi_dhcp4 + */ + +/* + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. + * + * Author: Leon Woestenberg + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 && LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/def.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" +#include "lwip/etharp.h" +#include "lwip/prot/dhcp.h" + +#include + +/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using + * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) + */ +#ifndef DHCP_CREATE_RAND_XID +#define DHCP_CREATE_RAND_XID 1 +#endif + +/** Default for DHCP_GLOBAL_XID is 0xABCD0000 + * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g. + * \#define DHCP_GLOBAL_XID_HEADER "stdlib.h" + * \#define DHCP_GLOBAL_XID rand() + */ +#ifdef DHCP_GLOBAL_XID_HEADER +#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */ +#endif + +/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU + * MTU is checked to be big enough in dhcp_start */ +#define DHCP_MAX_MSG_LEN(netif) (netif->mtu) +#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576 +/** Minimum length for reply before packet is parsed */ +#define DHCP_MIN_REPLY_LEN 44 + +#define REBOOT_TRIES 2 + +#if LWIP_DNS && LWIP_DHCP_MAX_DNS_SERVERS +#if DNS_MAX_SERVERS > LWIP_DHCP_MAX_DNS_SERVERS +#define LWIP_DHCP_PROVIDE_DNS_SERVERS LWIP_DHCP_MAX_DNS_SERVERS +#else +#define LWIP_DHCP_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS +#endif +#else +#define LWIP_DHCP_PROVIDE_DNS_SERVERS 0 +#endif + +/** Option handling: options are parsed in dhcp_parse_reply + * and saved in an array where other functions can load them from. + * This might be moved into the struct dhcp (not necessarily since + * lwIP is single-threaded and the array is only used while in recv + * callback). */ +enum dhcp_option_idx { + DHCP_OPTION_IDX_OVERLOAD = 0, + DHCP_OPTION_IDX_MSG_TYPE, + DHCP_OPTION_IDX_SERVER_ID, + DHCP_OPTION_IDX_LEASE_TIME, + DHCP_OPTION_IDX_T1, + DHCP_OPTION_IDX_T2, + DHCP_OPTION_IDX_SUBNET_MASK, + DHCP_OPTION_IDX_ROUTER, +#if LWIP_DHCP_PROVIDE_DNS_SERVERS + DHCP_OPTION_IDX_DNS_SERVER, + DHCP_OPTION_IDX_DNS_SERVER_LAST = DHCP_OPTION_IDX_DNS_SERVER + LWIP_DHCP_PROVIDE_DNS_SERVERS - 1, +#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */ +#if LWIP_DHCP_GET_NTP_SRV + DHCP_OPTION_IDX_NTP_SERVER, + DHCP_OPTION_IDX_NTP_SERVER_LAST = DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS - 1, +#endif /* LWIP_DHCP_GET_NTP_SRV */ + DHCP_OPTION_IDX_MAX +}; + +/** Holds the decoded option values, only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX]; +/** Holds a flag which option was received and is contained in dhcp_rx_options_val, + only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX]; + +static u8_t dhcp_discover_request_options[] = { + DHCP_OPTION_SUBNET_MASK, + DHCP_OPTION_ROUTER, + DHCP_OPTION_BROADCAST +#if LWIP_DHCP_PROVIDE_DNS_SERVERS + , DHCP_OPTION_DNS_SERVER +#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */ +#if LWIP_DHCP_GET_NTP_SRV + , DHCP_OPTION_NTP +#endif /* LWIP_DHCP_GET_NTP_SRV */ + }; + +#ifdef DHCP_GLOBAL_XID +static u32_t xid; +static u8_t xid_initialised; +#endif /* DHCP_GLOBAL_XID */ + +#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0) +#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1) +#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0) +#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given))) +#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx]) +#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val)) + +static struct udp_pcb *dhcp_pcb; +static u8_t dhcp_pcb_refcount; + +/* DHCP client state machine functions */ +static err_t dhcp_discover(struct netif *netif); +static err_t dhcp_select(struct netif *netif); +static void dhcp_bind(struct netif *netif); +#if DHCP_DOES_ARP_CHECK +static err_t dhcp_decline(struct netif *netif); +#endif /* DHCP_DOES_ARP_CHECK */ +static err_t dhcp_rebind(struct netif *netif); +static err_t dhcp_reboot(struct netif *netif); +static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); + +/* receive, unfold, parse and free incoming messages */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port); + +/* set the DHCP timers */ +static void dhcp_timeout(struct netif *netif); +static void dhcp_t1_timeout(struct netif *netif); +static void dhcp_t2_timeout(struct netif *netif); + +/* build outgoing messages */ +/* create a DHCP message, fill in common headers */ +static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type); +/* free a DHCP request */ +static void dhcp_delete_msg(struct dhcp *dhcp); +/* add a DHCP option (type, then length in bytes) */ +static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); +/* add option values */ +static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); +static void dhcp_option_short(struct dhcp *dhcp, u16_t value); +static void dhcp_option_long(struct dhcp *dhcp, u32_t value); +#if LWIP_NETIF_HOSTNAME +static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif); +#endif /* LWIP_NETIF_HOSTNAME */ +/* always add the DHCP options trailer to end and pad */ +static void dhcp_option_trailer(struct dhcp *dhcp); + +/** Ensure DHCP PCB is allocated and bound */ +static err_t +dhcp_inc_pcb_refcount(void) +{ + if (dhcp_pcb_refcount == 0) { + LWIP_ASSERT("dhcp_inc_pcb_refcount(): memory leak", dhcp_pcb == NULL); + + /* allocate UDP PCB */ + dhcp_pcb = udp_new(); + + if (dhcp_pcb == NULL) { + return ERR_MEM; + } + + ip_set_option(dhcp_pcb, SOF_BROADCAST); + + /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */ + udp_bind(dhcp_pcb, IP4_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp_pcb, IP4_ADDR_ANY, DHCP_SERVER_PORT); + udp_recv(dhcp_pcb, dhcp_recv, NULL); + } + + dhcp_pcb_refcount++; + + return ERR_OK; +} + +/** Free DHCP PCB if the last netif stops using it */ +static void +dhcp_dec_pcb_refcount(void) +{ + LWIP_ASSERT("dhcp_pcb_refcount(): refcount error", (dhcp_pcb_refcount > 0)); + dhcp_pcb_refcount--; + + if (dhcp_pcb_refcount == 0) { + udp_remove(dhcp_pcb); + dhcp_pcb = NULL; + } +} + +/** + * Back-off the DHCP client (because of a received NAK response). + * + * Back-off the DHCP client because of a received NAK. Receiving a + * NAK means the client asked for something non-sensible, for + * example when it tries to renew a lease obtained on another network. + * + * We clear any existing set IP address and restart DHCP negotiation + * afresh (as per RFC2131 3.2.3). + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_nak(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Change to a defined state - set this before assigning the address + to ensure the callback can use dhcp_supplied_address() */ + dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF); + /* remove IP address from interface (must no longer be used, as per RFC2131) */ + netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4); + /* We can immediately restart discovery */ + dhcp_discover(netif); +} + +#if DHCP_DOES_ARP_CHECK +/** + * Checks if the offered IP address is already in use. + * + * It does so by sending an ARP request for the offered address and + * entering CHECKING state. If no ARP reply is received within a small + * interval, the address is assumed to be free for use by us. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_check(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], + (s16_t)netif->name[1])); + dhcp_set_state(dhcp, DHCP_STATE_CHECKING); + /* create an ARP query for the offered IP address, expecting that no host + responds, as the IP address should not be in use. */ + result = etharp_query(netif, &dhcp->offered_ip_addr, NULL); + if (result != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n")); + } + if (dhcp->tries < 255) { + dhcp->tries++; + } + msecs = 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); +} +#endif /* DHCP_DOES_ARP_CHECK */ + +/** + * Remember the configuration offered by a DHCP server. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_offer(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* obtain the server address */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) { + ip_addr_set_ip4_u32(&dhcp->server_ip_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID))); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", + ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr)))); + /* remember offered address */ + ip4_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif)); + } +} + +/** + * Select a DHCP server offer out of all offers. + * + * Simply select the first offer received. + * + * @param netif the netif under DHCP control + * @return lwIP specific error (see error.h) + */ +static err_t +dhcp_select(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + err_t result; + u16_t msecs; + u8_t i; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + dhcp_set_state(dhcp, DHCP_STATE_REQUESTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + /* MUST request the offered IP address */ + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr)))); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options)); + for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) { + dhcp_option_byte(dhcp, dhcp_discover_request_options[i]); + } + + dhcp_option(dhcp, DHCP_OPTION_US, strlen(TLS_CONFIG_DHCP_OPTION60)); + for (i = 0; i < strlen(TLS_CONFIG_DHCP_OPTION60); i++) + { + dhcp_option_byte(dhcp, *((u8 *)TLS_CONFIG_DHCP_OPTION60 + i)); + } + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + dhcp_option_trailer(dhcp); + /* shrink the pbuf to the actual content length */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* send broadcast to any DHCP server */ + udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n")); + } + if (dhcp->tries < 255) { + dhcp->tries++; + } + msecs = (dhcp->tries < 3 ? 1 << dhcp->tries : 3) * 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * The DHCP timer that checks for lease renewal/rebind timeouts. + * Must be called once a minute (see @ref DHCP_COARSE_TIMER_SECS). + */ +void +dhcp_coarse_tmr(void) +{ + struct netif *netif = netif_list; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n")); + /* iterate through all network interfaces */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + struct dhcp *dhcp = netif_dhcp_data(netif); + if ((dhcp != NULL) && (dhcp->state != DHCP_STATE_OFF)) { + /* compare lease time to expire timeout */ + if (dhcp->t0_timeout && (++dhcp->lease_used == dhcp->t0_timeout)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t0 timeout\n")); + /* this clients' lease time has expired */ + dhcp_release(netif); + dhcp_discover(netif); + /* timer is active (non zero), and triggers (zeroes) now? */ + } else if (dhcp->t2_rebind_time && (dhcp->t2_rebind_time-- == 1)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); + /* this clients' rebind timeout triggered */ + dhcp_t2_timeout(netif); + /* timer is active (non zero), and triggers (zeroes) now */ + } else if (dhcp->t1_renew_time && (dhcp->t1_renew_time-- == 1)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); + /* this clients' renewal timeout triggered */ + dhcp_t1_timeout(netif); + } + } + /* proceed to next netif */ + netif = netif->next; + } +} + +/** + * DHCP transaction timeout handling (this function must be called every 500ms, + * see @ref DHCP_FINE_TIMER_MSECS). + * + * A DHCP server is expected to respond within a short period of time. + * This timer checks whether an outstanding DHCP request is timed out. + */ +void +dhcp_fine_tmr(void) +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + struct dhcp *dhcp = netif_dhcp_data(netif); + /* only act on DHCP configured interfaces */ + if (dhcp != NULL) { + /* timer is active (non zero), and is about to trigger now */ + if (dhcp->request_timeout > 1) { + dhcp->request_timeout--; + } + else if (dhcp->request_timeout == 1) { + dhcp->request_timeout--; + /* { netif->dhcp->request_timeout == 0 } */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); + /* this client's request timeout triggered */ + dhcp_timeout(netif); + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * A DHCP negotiation transaction, or ARP request, has timed out. + * + * The timer that was started with the DHCP or ARP request has + * timed out, indicating no response was received in time. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n")); + /* back-off period has passed, or server selection timed out */ + if ((dhcp->state == DHCP_STATE_BACKING_OFF) || (dhcp->state == DHCP_STATE_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); + dhcp_discover(netif); + /* receiving the requested lease timed out */ + } else if (dhcp->state == DHCP_STATE_REQUESTING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); + if (dhcp->tries <= 5) { + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); + dhcp_release(netif); + dhcp_discover(netif); + } +#if DHCP_DOES_ARP_CHECK + /* received no ARP reply for the offered address (which is good) */ + } else if (dhcp->state == DHCP_STATE_CHECKING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); + if (dhcp->tries <= 1) { + dhcp_check(netif); + /* no ARP replies on the offered address, + looks like the IP address is indeed free */ + } else { + /* bind the interface to the offered address */ + dhcp_bind(netif); + } +#endif /* DHCP_DOES_ARP_CHECK */ + } else if (dhcp->state == DHCP_STATE_REBOOTING) { + if (dhcp->tries < REBOOT_TRIES) { + dhcp_reboot(netif); + } else { + dhcp_discover(netif); + } + }else if (dhcp->state == DHCP_STATE_RENEWING){ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); + dhcp_renew(netif); + }else if (dhcp->state == DHCP_STATE_REBINDING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); + if (dhcp->tries <= 8) { + dhcp_rebind(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + } +} + +/** + * The renewal period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_t1_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n")); + if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) || + (dhcp->state == DHCP_STATE_RENEWING)) { + /* just retry to renew - note that the rebind timer (t2) will + * eventually time-out if renew tries fail. */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t1_timeout(): must renew\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_STATE_RENEWING, not DHCP_STATE_BOUND */ + dhcp_renew(netif); + /* Calculate next timeout */ + if (((dhcp->t2_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS)) + { + dhcp->t1_renew_time = ((dhcp->t2_timeout - dhcp->lease_used) / 2); + } + } +} + +/** + * The rebind period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_t2_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n")); + if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) || + (dhcp->state == DHCP_STATE_RENEWING) || (dhcp->state == DHCP_STATE_REBINDING)) { + /* just retry to rebind */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t2_timeout(): must rebind\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_STATE_REBINDING, not DHCP_STATE_BOUND */ + dhcp_rebind(netif); + /* Calculate next timeout */ + if (((dhcp->t0_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS)) + { + dhcp->t2_rebind_time = ((dhcp->t0_timeout - dhcp->lease_used) / 2); + } + } +} + +/** + * Handle a DHCP ACK packet + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_ack(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + +#if LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV + u8_t n; +#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV */ +#if LWIP_DHCP_GET_NTP_SRV + ip4_addr_t ntp_server_addrs[LWIP_DHCP_MAX_NTP_SERVERS]; +#endif + + /* clear options we might not get from the ACK */ + ip4_addr_set_zero(&dhcp->offered_sn_mask); + ip4_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip4_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* lease time given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) { + /* remember offered lease time */ + dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME); + } + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) { + /* remember given renewal period */ + dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1); + } else { + /* calculate safe periods for renewal */ + dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; + } + + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) { + /* remember given rebind period */ + dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2); + } else { + /* calculate safe periods for rebinding (offered_t0_lease * 0.875 -> 87.5%)*/ + dhcp->offered_t2_rebind = (dhcp->offered_t0_lease * 7U) / 8U; + } + + /* (y)our internet address */ + ip4_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + +#if LWIP_DHCP_BOOTP_FILE + /* copy boot server address, + boot file name copied in dhcp_parse_reply if not overloaded */ + ip4_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* subnet mask given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) { + /* remember given subnet mask */ + ip4_addr_set_u32(&dhcp->offered_sn_mask, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK))); + dhcp->subnet_mask_given = 1; + } else { + dhcp->subnet_mask_given = 0; + } + + /* gateway router */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) { + ip4_addr_set_u32(&dhcp->offered_gw_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER))); + } + +#if LWIP_DHCP_GET_NTP_SRV + /* NTP servers */ + for (n = 0; (n < LWIP_DHCP_MAX_NTP_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n); n++) { + ip4_addr_set_u32(&ntp_server_addrs[n], lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n))); + } + dhcp_set_ntp_servers(n, ntp_server_addrs); +#endif /* LWIP_DHCP_GET_NTP_SRV */ + +#if LWIP_DHCP_PROVIDE_DNS_SERVERS + /* DNS servers */ + for (n = 0; (n < LWIP_DHCP_PROVIDE_DNS_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) { + ip_addr_t dns_addr; + ip_addr_set_ip4_u32(&dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n))); + dns_setserver(n, &dns_addr); + } +#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */ +} + +/** + * @ingroup dhcp4 + * Set a statically allocated struct dhcp to work with. + * Using this prevents dhcp_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct dhcp + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void +dhcp_set_struct(struct netif *netif, struct dhcp *dhcp) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("dhcp != NULL", dhcp != NULL); + LWIP_ASSERT("netif already has a struct dhcp set", netif_dhcp_data(netif) == NULL); + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */ + netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp); +} + +/** + * @ingroup dhcp4 + * Removes a struct dhcp from a netif. + * + * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the + * struct dhcp since the memory is passed back to the heap. + * + * @param netif the netif from which to remove the struct dhcp + */ +void dhcp_cleanup(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + + if (netif_dhcp_data(netif) != NULL) { + mem_free(netif_dhcp_data(netif)); + netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, NULL); + } +} + +/** + * @ingroup dhcp4 + * Start DHCP negotiation for a network interface. + * + * If no DHCP client instance was attached to this interface, + * a new client is created first. If a DHCP client instance + * was already present, it restarts negotiation. + * + * @param netif The lwIP network interface + * @return lwIP error code + * - ERR_OK - No error + * - ERR_MEM - Out of memory + */ +err_t +dhcp_start(struct netif *netif) +{ + struct dhcp *dhcp; + err_t result; + + LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;); + LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;); + dhcp = netif_dhcp_data(netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* check MTU of the netif */ + if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n")); + return ERR_MEM; + } + + /* no DHCP client attached yet? */ + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): mallocing new DHCP client\n")); + dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); + return ERR_MEM; + } + + /* store this dhcp client in the netif */ + netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp")); + /* already has DHCP client attached */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n")); + LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL ); + + if (dhcp->pcb_allocated != 0) { + dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */ + } + /* dhcp is cleared below, no need to reset flag*/ + } + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */ + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); + + if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */ + return ERR_MEM; + } + dhcp->pcb_allocated = 1; + +#if LWIP_DHCP_CHECK_LINK_UP + if (!netif_is_link_up(netif)) { + /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */ + dhcp_set_state(dhcp, DHCP_STATE_INIT); + return ERR_OK; + } +#endif /* LWIP_DHCP_CHECK_LINK_UP */ + + + /* (re)start the DHCP negotiation */ + result = dhcp_discover(netif); + if (result != ERR_OK) { + /* free resources allocated above */ + dhcp_stop(netif); + return ERR_MEM; + } + return result; +} + +/** + * @ingroup dhcp4 + * Inform a DHCP server of our manual configuration. + * + * This informs DHCP servers of our fixed IP address configuration + * by sending an INFORM message. It does not involve DHCP address + * configuration, it is just here to be nice to the network. + * + * @param netif The lwIP network interface + */ +void +dhcp_inform(struct netif *netif) +{ + struct dhcp dhcp; + err_t result = ERR_OK; + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */ + return; + } + + memset(&dhcp, 0, sizeof(struct dhcp)); + dhcp_set_state(&dhcp, DHCP_STATE_INFORMING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM); + if (result == ERR_OK) { + dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option_trailer(&dhcp); + + pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n")); + + udp_sendto_if(dhcp_pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + + dhcp_delete_msg(&dhcp); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n")); + } + + dhcp_dec_pcb_refcount(); /* delete DHCP PCB if not needed any more */ +} + +/** Handle a possible change in the network configuration. + * + * This enters the REBOOTING state to verify that the currently bound + * address is still valid. + */ +void +dhcp_network_changed(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + + if (!dhcp) + return; + switch (dhcp->state) { + case DHCP_STATE_REBINDING: + case DHCP_STATE_RENEWING: + case DHCP_STATE_BOUND: + case DHCP_STATE_REBOOTING: + dhcp->tries = 0; + dhcp_reboot(netif); + break; + case DHCP_STATE_OFF: + /* stay off */ + break; + default: + /* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the + state changes, SELECTING: continue with current 'rid' as we stay in the + same state */ +#if LWIP_DHCP_AUTOIP_COOP + if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + /* ensure we start with short timeouts, even if already discovering */ + dhcp->tries = 0; + dhcp_discover(netif); + break; + } +} + +#if DHCP_DOES_ARP_CHECK +/** + * Match an ARP reply with the offered IP address: + * check whether the offered IP address is not in use using ARP + * + * @param netif the network interface on which the reply was received + * @param addr The IP address we received a reply from + */ +void +dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr) +{ + struct dhcp *dhcp; + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + dhcp = netif_dhcp_data(netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n")); + /* is a DHCP client doing an ARP check? */ + if ((dhcp != NULL) && (dhcp->state == DHCP_STATE_CHECKING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", + ip4_addr_get_u32(addr))); + /* did a host respond with the address we + were offered by the DHCP server? */ + if (ip4_addr_cmp(addr, &dhcp->offered_ip_addr)) { + /* we will not accept the offered address */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); + dhcp_decline(netif); + } + } +} + +/** + * Decline an offered lease. + * + * Tell the DHCP server we do not accept the offered address. + * One reason to decline the lease is when we find out the address + * is already in use by another host (through ARP). + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_decline(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n")); + dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + /* resize pbuf to reflect true size of options */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* per section 4.4.4, broadcast DECLINE messages */ + udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_decline: could not allocate DHCP request\n")); + } + if (dhcp->tries < 255) { + dhcp->tries++; + } + msecs = 10*1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} +#endif /* DHCP_DOES_ARP_CHECK */ + + +static int dhcp_timeout_flag = 0; + +int tls_dhcp_get_ip_timeout_flag(void) +{ + return dhcp_timeout_flag; +} + +/** + * Start the DHCP process, discover a DHCP server. + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_discover(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + err_t result = ERR_OK; + u16_t msecs; + u8_t i; + dhcp_timeout_flag = 0; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n")); +#if !LWIP_DHCP_AUTOIP_COOP + if (dhcp->tries >= 10) { + dhcp_timeout_flag = 1; + NETIF_STATUS_CALLBACK(netif, NETIF_STATE_IPV4); + return ERR_TIMEOUT; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + ip4_addr_set_any(&dhcp->offered_ip_addr); + dhcp_set_state(dhcp, DHCP_STATE_SELECTING); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER); + if (result == ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n")); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options)); + for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) { + dhcp_option_byte(dhcp, dhcp_discover_request_options[i]); + } + + dhcp_option(dhcp, DHCP_OPTION_US, strlen(TLS_CONFIG_DHCP_OPTION60)); + for (i = 0; i < strlen(TLS_CONFIG_DHCP_OPTION60); i++) + { + dhcp_option_byte(dhcp, *((u8 *)TLS_CONFIG_DHCP_OPTION60 + i)); + } + + dhcp_option_trailer(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n")); + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); + udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n")); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n")); + } + + dhcp->tries++; + +#if LWIP_DHCP_AUTOIP_COOP + if (dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) { + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON; + autoip_start(netif); + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + msecs = (dhcp->tries < 3 ? 1 << dhcp->tries : 3) * 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Bind the interface to the offered IP address. + * + * @param netif network interface to bind to the offered address + */ +static void +dhcp_bind(struct netif *netif) +{ + u32_t timeout; + struct dhcp *dhcp; + ip4_addr_t sn_mask, gw_addr; + LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;); + dhcp = netif_dhcp_data(netif); + LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* reset time used of lease */ + dhcp->lease_used = 0; + + if (dhcp->offered_t0_lease != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease)); + timeout = (dhcp->offered_t0_lease + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if (timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t0_timeout = (u16_t)timeout; + if (dhcp->t0_timeout == 0) { + dhcp->t0_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t0_lease*1000)); + } + + /* temporary DHCP lease? */ + if (dhcp->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if (timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t1_timeout = (u16_t)timeout; + if (dhcp->t1_timeout == 0) { + dhcp->t1_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + dhcp->t1_renew_time = dhcp->t1_timeout; + } + /* set renewal period timer */ + if (dhcp->offered_t2_rebind != 0xffffffffUL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if (timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t2_timeout = (u16_t)timeout; + if (dhcp->t2_timeout == 0) { + dhcp->t2_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + dhcp->t2_rebind_time = dhcp->t2_timeout; + } + + /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */ + if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { + dhcp->t1_timeout = 0; + } + + if (dhcp->subnet_mask_given) { + /* copy offered network mask */ + ip4_addr_copy(sn_mask, dhcp->offered_sn_mask); + } else { + /* subnet mask not given, choose a safe subnet mask given the network class */ + u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr); + if (first_octet <= 127) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL)); + } else if (first_octet >= 192) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL)); + } else { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL)); + } + } + + ip4_addr_copy(gw_addr, dhcp->offered_gw_addr); + /* gateway address not given? */ + if (ip4_addr_isany_val(gw_addr)) { + /* copy network address */ + ip4_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask); + /* use first host address on network as gateway */ + ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL)); + } + +#if LWIP_DHCP_AUTOIP_COOP + if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F" SN: 0x%08"X32_F" GW: 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr), ip4_addr_get_u32(&sn_mask), ip4_addr_get_u32(&gw_addr))); + /* netif is now bound to DHCP leased address - set this before assigning the address + to ensure the callback can use dhcp_supplied_address() */ + dhcp_set_state(dhcp, DHCP_STATE_BOUND); + + netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr); + /* interface is used by routing now that an address is set */ +} + +/** + * @ingroup dhcp4 + * Renew an existing DHCP lease at the involved DHCP server. + * + * @param netif network interface which must renew its lease + */ +err_t +dhcp_renew(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + err_t result; + u16_t msecs; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n")); + dhcp_set_state(dhcp, DHCP_STATE_RENEWING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options)); + for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) { + dhcp_option_byte(dhcp, dhcp_discover_request_options[i]); + } + + dhcp_option(dhcp, DHCP_OPTION_US, strlen(TLS_CONFIG_DHCP_OPTION60)); + for (i = 0; i < strlen(TLS_CONFIG_DHCP_OPTION60); i++) + { + dhcp_option_byte(dhcp, *((u8 *)TLS_CONFIG_DHCP_OPTION60 + i)); + } + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + /* append DHCP message trailer */ + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp_pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n")); + } + if (dhcp->tries < 255) { + dhcp->tries++; + } + /* back-off on retries, but to a maximum of 20 seconds */ + msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + * @param netif network interface which must rebind with a DHCP server + */ +static err_t +dhcp_rebind(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + err_t result; + u16_t msecs; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n")); + dhcp_set_state(dhcp, DHCP_STATE_REBINDING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options)); + for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) { + dhcp_option_byte(dhcp, dhcp_discover_request_options[i]); + } + + dhcp_option(dhcp, DHCP_OPTION_US, strlen(TLS_CONFIG_DHCP_OPTION60)); + for (i = 0; i < strlen(TLS_CONFIG_DHCP_OPTION60); i++) + { + dhcp_option_byte(dhcp, *((u8 *)TLS_CONFIG_DHCP_OPTION60 + i)); + } + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n")); + } + if (dhcp->tries < 255) { + dhcp->tries++; + } + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Enter REBOOTING state to verify an existing lease + * + * @param netif network interface which must reboot + */ +static err_t +dhcp_reboot(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + err_t result; + u16_t msecs; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n")); + dhcp_set_state(dhcp, DHCP_STATE_REBOOTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN_MIN_REQUIRED); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options)); + for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) { + dhcp_option_byte(dhcp, dhcp_discover_request_options[i]); + } + + dhcp_option(dhcp, DHCP_OPTION_US, strlen(TLS_CONFIG_DHCP_OPTION60)); + for (i = 0; i < strlen(TLS_CONFIG_DHCP_OPTION60); i++) + { + dhcp_option_byte(dhcp, *((u8 *)TLS_CONFIG_DHCP_OPTION60 + i)); + } + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n")); + } + if (dhcp->tries < 255) { + dhcp->tries++; + } + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * @ingroup dhcp4 + * Release a DHCP lease (usually called before @ref dhcp_stop). + * + * @param netif network interface which must release its lease + */ +err_t +dhcp_release(struct netif *netif) +{ + struct dhcp *dhcp = netif_dhcp_data(netif); + err_t result; + ip_addr_t server_ip_addr; + u8_t is_dhcp_supplied_address; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + if (dhcp == NULL) { + return ERR_ARG; + } + ip_addr_copy(server_ip_addr, dhcp->server_ip_addr); + + is_dhcp_supplied_address = dhcp_supplied_address(netif); + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_STATE_OFF); + /* clean old DHCP offer */ + ip_addr_set_zero_ip4(&dhcp->server_ip_addr); + ip4_addr_set_zero(&dhcp->offered_ip_addr); + ip4_addr_set_zero(&dhcp->offered_sn_mask); + ip4_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip4_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + dhcp->t1_renew_time = dhcp->t2_rebind_time = dhcp->lease_used = dhcp->t0_timeout = 0; + + if (!is_dhcp_supplied_address) { + /* don't issue release message when address is not dhcp-assigned */ + return ERR_OK; + } + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&server_ip_addr)))); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp_pcb, dhcp->p_out, &server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_STATE_OFF\n")); + } else { + /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n")); + } + /* remove IP address from interface (prevents routing from selecting this interface) */ + netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4); + + return result; +} + +/** + * @ingroup dhcp4 + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void +dhcp_stop(struct netif *netif) +{ + struct dhcp *dhcp; + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); + dhcp = netif_dhcp_data(netif); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n")); + /* netif is DHCP configured? */ + if (dhcp != NULL) { +#if LWIP_DHCP_AUTOIP_COOP + if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + dhcp_set_state(dhcp, DHCP_STATE_OFF); + + if (dhcp->pcb_allocated != 0) { + dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */ + dhcp->pcb_allocated = 0; + } + } +} + +/* + * Set the DHCP state of a DHCP client. + * + * If the state changed, reset the number of tries. + */ +static void +dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +{ + if (new_state != dhcp->state) { + dhcp->state = new_state; + dhcp->tries = 0; + dhcp->request_timeout = 0; + } +} + +/* + * Concatenate an option type and length field to the outgoing + * DHCP message. + * + */ +static void +dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) +{ + LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = option_type; + dhcp->msg_out->options[dhcp->options_out_len++] = option_len; +} +/* + * Concatenate a single byte to the outgoing DHCP message. + * + */ +static void +dhcp_option_byte(struct dhcp *dhcp, u8_t value) +{ + LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = value; +} + +static void +dhcp_option_short(struct dhcp *dhcp, u16_t value) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU); +} + +static void +dhcp_option_long(struct dhcp *dhcp, u32_t value) +{ + LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL)); +} + +#if LWIP_NETIF_HOSTNAME +static void +dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif) +{ + if (netif->hostname != NULL) { + size_t namelen = strlen(netif->hostname); + if (namelen > 0) { + size_t len; + const char *p = netif->hostname; + /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME + and 1 byte for trailer) */ + size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3; + LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available); + len = LWIP_MIN(namelen, available); + LWIP_ASSERT("DHCP: hostname is too long!", len <= 0xFF); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, (u8_t)len); + while (len--) { + dhcp_option_byte(dhcp, *p++); + } + } + } +} +#endif /* LWIP_NETIF_HOSTNAME */ + +/** + * Extract the DHCP message and the DHCP options. + * + * Extract the DHCP message and the DHCP options, each into a contiguous + * piece of memory. As a DHCP message is variable sized by its options, + * and also allows overriding some fields for options, the easy approach + * is to first unfold the options into a contiguous piece of memory, and + * use that further on. + * + */ +static err_t +dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p) +{ + u8_t *options; + u16_t offset; + u16_t offset_max; + u16_t options_idx; + u16_t options_idx_max; + struct pbuf *q; + int parse_file_as_options = 0; + int parse_sname_as_options = 0; + + /* clear received options */ + dhcp_clear_all_options(dhcp); + /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */ + if (p->len < DHCP_SNAME_OFS) { + return ERR_BUF; + } + dhcp->msg_in = (struct dhcp_msg *)p->payload; +#if LWIP_DHCP_BOOTP_FILE + /* clear boot file name */ + dhcp->boot_file_name[0] = 0; +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* parse options */ + + /* start with options field */ + options_idx = DHCP_OPTIONS_OFS; + /* parse options to the end of the received packet */ + options_idx_max = p->tot_len; +again: + q = p; + while ((q != NULL) && (options_idx >= q->len)) { + options_idx -= q->len; + options_idx_max -= q->len; + q = q->next; + } + if (q == NULL) { + return ERR_BUF; + } + offset = options_idx; + offset_max = options_idx_max; + options = (u8_t*)q->payload; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while ((q != NULL) && (offset < offset_max) && (options[offset] != DHCP_OPTION_END)) { + u8_t op = options[offset]; + u8_t len; + u8_t decode_len = 0; + int decode_idx = -1; + u16_t val_offset = offset + 2; + /* len byte might be in the next pbuf */ + if ((offset + 1) < q->len) { + len = options[offset + 1]; + } else { + len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0); + } + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + decode_len = len; + switch(op) { + /* case(DHCP_OPTION_END): handled above */ + case(DHCP_OPTION_PAD): + /* special option: no len encoded */ + decode_len = len = 0; + /* will be increased below */ + offset--; + break; + case(DHCP_OPTION_SUBNET_MASK): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; + break; + case(DHCP_OPTION_ROUTER): + decode_len = 4; /* only copy the first given router */ + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_ROUTER; + break; +#if LWIP_DHCP_PROVIDE_DNS_SERVERS + case(DHCP_OPTION_DNS_SERVER): + /* special case: there might be more than one server */ + LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;); + /* limit number of DNS servers */ + decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_DNS_SERVER; + break; +#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */ + case(DHCP_OPTION_LEASE_TIME): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_LEASE_TIME; + break; +#if LWIP_DHCP_GET_NTP_SRV + case(DHCP_OPTION_NTP): + /* special case: there might be more than one server */ + LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;); + /* limit number of NTP servers */ + decode_len = LWIP_MIN(len, 4 * LWIP_DHCP_MAX_NTP_SERVERS); + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_NTP_SERVER; + break; +#endif /* LWIP_DHCP_GET_NTP_SRV*/ + case(DHCP_OPTION_OVERLOAD): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + /* decode overload only in options, not in file/sname: invalid packet */ + LWIP_ERROR("overload in file/sname", options_idx == DHCP_OPTIONS_OFS, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_OVERLOAD; + break; + case(DHCP_OPTION_MESSAGE_TYPE): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_MSG_TYPE; + break; + case(DHCP_OPTION_SERVER_ID): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SERVER_ID; + break; + case(DHCP_OPTION_T1): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_T1; + break; + case(DHCP_OPTION_T2): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_T2; + break; + default: + decode_len = 0; + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", (u16_t)op)); + break; + } + offset += len + 2; + if (decode_len > 0) { + u32_t value = 0; + u16_t copy_len; +decode_next: + LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX); + if (!dhcp_option_given(dhcp, decode_idx)) { + copy_len = LWIP_MIN(decode_len, 4); + if (pbuf_copy_partial(q, &value, copy_len, val_offset) != copy_len) { + return ERR_BUF; + } + if (decode_len > 4) { + /* decode more than one u32_t */ + LWIP_ERROR("decode_len %% 4 == 0", decode_len % 4 == 0, return ERR_VAL;); + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, lwip_htonl(value)); + decode_len -= 4; + val_offset += 4; + decode_idx++; + goto decode_next; + } else if (decode_len == 4) { + value = lwip_ntohl(value); + } else { + LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;); + value = ((u8_t*)&value)[0]; + } + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, value); + } + } + if (offset >= q->len) { + offset -= q->len; + offset_max -= q->len; + if ((offset < offset_max) && offset_max) { + q = q->next; + LWIP_ERROR("next pbuf was null", q != NULL, return ERR_VAL;); + options = (u8_t*)q->payload; + } else { + /* We've run out of bytes, probably no end marker. Don't proceed. */ + break; + } + } + } + /* is this an overloaded message? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) { + u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD); + dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD); + if (overload == DHCP_OVERLOAD_FILE) { + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME) { + parse_sname_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME_FILE) { + parse_sname_as_options = 1; + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload)); + } +#if LWIP_DHCP_BOOTP_FILE + if (!parse_file_as_options) { + /* only do this for ACK messages */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) && + (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK)) + /* copy bootp file name, don't care for sname (server hostname) */ + if (pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS) != (DHCP_FILE_LEN-1)) { + return ERR_BUF; + } + /* make sure the string is really NULL-terminated */ + dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0; + } +#endif /* LWIP_DHCP_BOOTP_FILE */ + } + if (parse_file_as_options) { + /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */ + parse_file_as_options = 0; + options_idx = DHCP_FILE_OFS; + options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN; + goto again; + } else if (parse_sname_as_options) { + parse_sname_as_options = 0; + options_idx = DHCP_SNAME_OFS; + options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN; + goto again; + } + return ERR_OK; +} + +/** + * If an incoming DHCP message is in response to us, then trigger the state machine + */ +static void +dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + struct netif *netif = ip_current_input_netif(); + struct dhcp *dhcp = netif_dhcp_data(netif); + struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; + u8_t msg_type; + u8_t i; + + LWIP_UNUSED_ARG(arg); + + /* Caught DHCP message from netif that does not have DHCP enabled? -> not interested */ + if ((dhcp == NULL) || (dhcp->pcb_allocated == 0)) { + goto free_pbuf_and_return; + } + + LWIP_ASSERT("invalid server address type", IP_IS_V4(addr)); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, + ip4_addr1_16(ip_2_ip4(addr)), ip4_addr2_16(ip_2_ip4(addr)), ip4_addr3_16(ip_2_ip4(addr)), ip4_addr4_16(ip_2_ip4(addr)), port)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); + /* prevent warnings about unused arguments */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + + if (p->len < DHCP_MIN_REPLY_LEN) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n")); + goto free_pbuf_and_return; + } + + if (reply_msg->op != DHCP_BOOTREPLY) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); + goto free_pbuf_and_return; + } + /* iterate through hardware address and match against DHCP message */ + for (i = 0; i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN && i < DHCP_CHADDR_LEN; i++) { + if (netif->hwaddr[i] != reply_msg->chaddr[i]) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", + (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i])); + goto free_pbuf_and_return; + } + } + /* match transaction ID against what we expected */ + if (lwip_ntohl(reply_msg->xid) != dhcp->xid) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",lwip_ntohl(reply_msg->xid),dhcp->xid)); + goto free_pbuf_and_return; + } + /* option fields could be unfold? */ + if (dhcp_parse_reply(dhcp, p) != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("problem unfolding DHCP message - too short on memory?\n")); + goto free_pbuf_and_return; + } + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); + /* obtain pointer to DHCP message type */ + if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); + goto free_pbuf_and_return; + } + + /* read DHCP message type */ + msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE); + /* message type is DHCP ACK? */ + if (msg_type == DHCP_ACK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n")); + /* in requesting state? */ + if (dhcp->state == DHCP_STATE_REQUESTING) { + dhcp_handle_ack(netif); +#if DHCP_DOES_ARP_CHECK + if ((netif->flags & NETIF_FLAG_ETHARP) != 0) { + /* check if the acknowledged lease address is already in use */ + dhcp_check(netif); + } else { + /* bind interface to the acknowledged lease address */ + dhcp_bind(netif); + } +#else + /* bind interface to the acknowledged lease address */ + dhcp_bind(netif); +#endif + } + /* already bound to the given lease address? */ + else if ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REBINDING) || + (dhcp->state == DHCP_STATE_RENEWING)) { + dhcp_handle_ack(netif); + dhcp_bind(netif); + } + } + /* received a DHCP_NAK in appropriate state? */ + else if ((msg_type == DHCP_NAK) && + ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REQUESTING) || + (dhcp->state == DHCP_STATE_REBINDING) || (dhcp->state == DHCP_STATE_RENEWING ))) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n")); + dhcp_handle_nak(netif); + } + /* received a DHCP_OFFER in DHCP_STATE_SELECTING state? */ + else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_STATE_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_STATE_SELECTING state\n")); + dhcp->request_timeout = 0; + /* remember offered lease */ + dhcp_handle_offer(netif); + } + +free_pbuf_and_return: + if (dhcp != NULL) { + dhcp->msg_in = NULL; + } + pbuf_free(p); +} + +/** + * Create a DHCP request, fill in common headers + * + * @param netif the netif under DHCP control + * @param dhcp dhcp control struct + * @param message_type message type of the request + */ +static err_t +dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type) +{ + u16_t i; +#ifndef DHCP_GLOBAL_XID + /** default global transaction identifier starting value (easy to match + * with a packet analyser). We simply increment for each new request. + * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one + * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */ +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + static u32_t xid; +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + static u32_t xid = 0xABCD0000; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ +#else + if (!xid_initialised) { + xid = DHCP_GLOBAL_XID; + xid_initialised = !xid_initialised; + } +#endif + LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;); + LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;); + LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL); + LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL); + dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM); + if (dhcp->p_out == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_create_msg(): could not allocate pbuf\n")); + return ERR_MEM; + } + LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg", + (dhcp->p_out->len >= sizeof(struct dhcp_msg))); + + /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER */ + if ((message_type != DHCP_REQUEST) || (dhcp->state == DHCP_STATE_REBOOTING)) { + /* reuse transaction identifier in retransmissions */ + if (dhcp->tries == 0) { +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + { + extern int tls_wl_get_isr_count(void); + srand(tls_wl_get_isr_count()); /*Using Wi-Fi Rx dataCnt as Random Seed*/ + xid = LWIP_RAND(); + } +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + xid++; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + } + dhcp->xid = xid; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, + ("transaction id xid(%"X32_F")\n", xid)); + + dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload; + + dhcp->msg_out->op = DHCP_BOOTREQUEST; + /* @todo: make link layer independent */ + dhcp->msg_out->htype = DHCP_HTYPE_ETH; + dhcp->msg_out->hlen = netif->hwaddr_len; + dhcp->msg_out->hops = 0; + dhcp->msg_out->xid = lwip_htonl(dhcp->xid); + dhcp->msg_out->secs = 0; + /* we don't need the broadcast flag since we can receive unicast traffic + before being fully configured! */ + dhcp->msg_out->flags = 0; + ip4_addr_set_zero(&dhcp->msg_out->ciaddr); + /* set ciaddr to netif->ip_addr based on message_type and state */ + if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || (message_type == DHCP_RELEASE) || + ((message_type == DHCP_REQUEST) && /* DHCP_STATE_BOUND not used for sending! */ + ((dhcp->state== DHCP_STATE_RENEWING) || dhcp->state== DHCP_STATE_REBINDING))) { + ip4_addr_copy(dhcp->msg_out->ciaddr, *netif_ip4_addr(netif)); + } + ip4_addr_set_zero(&dhcp->msg_out->yiaddr); + ip4_addr_set_zero(&dhcp->msg_out->siaddr); + ip4_addr_set_zero(&dhcp->msg_out->giaddr); + for (i = 0; i < DHCP_CHADDR_LEN; i++) { + /* copy netif hardware address, pad with zeroes */ + dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/; + } + for (i = 0; i < DHCP_SNAME_LEN; i++) { + dhcp->msg_out->sname[i] = 0; + } + for (i = 0; i < DHCP_FILE_LEN; i++) { + dhcp->msg_out->file[i] = 0; + } + dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE); + dhcp->options_out_len = 0; + /* fill options field with an incrementing array (for debugging purposes) */ + for (i = 0; i < DHCP_OPTIONS_LEN; i++) { + dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */ + } + /* Add option MESSAGE_TYPE */ + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, message_type); + return ERR_OK; +} + +/** + * Free previously allocated memory used to send a DHCP request. + * + * @param dhcp the dhcp struct to free the request from + */ +static void +dhcp_delete_msg(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL); + if (dhcp->p_out != NULL) { + pbuf_free(dhcp->p_out); + } + dhcp->p_out = NULL; + dhcp->msg_out = NULL; +} + +/** + * Add a DHCP message trailer + * + * Adds the END option to the DHCP message, and if + * necessary, up to three padding bytes. + * + * @param dhcp DHCP state structure + */ +static void +dhcp_option_trailer(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL); + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; + /* packet is too small, or not 4 byte aligned? */ + while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) && + (dhcp->options_out_len < DHCP_OPTIONS_LEN)) { + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +/** check if DHCP supplied netif->ip_addr + * + * @param netif the netif to check + * @return 1 if DHCP supplied netif->ip_addr (states BOUND or RENEWING), + * 0 otherwise + */ +u8_t +dhcp_supplied_address(const struct netif *netif) +{ + if ((netif != NULL) && (netif_dhcp_data(netif) != NULL)) { + struct dhcp* dhcp = netif_dhcp_data(netif); + return (dhcp->state == DHCP_STATE_BOUND) || (dhcp->state == DHCP_STATE_RENEWING) || + (dhcp->state == DHCP_STATE_REBINDING); + } + return 0; +} + +#endif /* LWIP_IPV4 && LWIP_DHCP */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/etharp.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/etharp.c new file mode 100644 index 0000000..dcbb928 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/etharp.c @@ -0,0 +1,1206 @@ +/** + * @file + * Address Resolution Protocol module for IP over Ethernet + * + * Functionally, ARP is divided into two parts. The first maps an IP address + * to a physical address when sending a packet, and the second part answers + * requests from other machines for our physical address. + * + * This implementation complies with RFC 826 (Ethernet ARP). It supports + * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6 + * if an interface calls etharp_gratuitous(our_netif) upon address change. + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET + +#include "lwip/etharp.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "netif/ethernet.h" + +#include + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** Re-request a used ARP entry 1 minute before it would expire to prevent + * breaking a steadily used connection because the ARP entry timed out. */ +#define ARP_AGE_REREQUEST_USED_UNICAST (ARP_MAXAGE - 30) +#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15) + +/** the time an ARP entry stays pending after first request, + * for ARP_TMR_INTERVAL = 1000, this is + * 10 seconds. + * + * @internal Keep this number at least 2, otherwise it might + * run out instantly if the timeout occurs directly after a request. + */ +#define ARP_MAXPENDING 5 + +/** ARP states */ +enum etharp_state { + ETHARP_STATE_EMPTY = 0, + ETHARP_STATE_PENDING, + ETHARP_STATE_STABLE, + ETHARP_STATE_STABLE_REREQUESTING_1, + ETHARP_STATE_STABLE_REREQUESTING_2 +#if ETHARP_SUPPORT_STATIC_ENTRIES + ,ETHARP_STATE_STATIC +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +}; + +struct etharp_entry { +#if ARP_QUEUEING + /** Pointer to queue of pending outgoing packets on this ARP entry. */ + struct etharp_q_entry *q; +#else /* ARP_QUEUEING */ + /** Pointer to a single pending outgoing packet on this ARP entry. */ + struct pbuf *q; +#endif /* ARP_QUEUEING */ + ip4_addr_t ipaddr; + struct netif *netif; + struct eth_addr ethaddr; + u16_t ctime; + u8_t state; +}; + +static struct etharp_entry arp_table[ARP_TABLE_SIZE]; + +#if !LWIP_NETIF_HWADDRHINT +static u8_t etharp_cached_entry; +#endif /* !LWIP_NETIF_HWADDRHINT */ + +/** Try hard to create a new entry - we want the IP address to appear in + the cache (even if this means removing an active entry or so). */ +#define ETHARP_FLAG_TRY_HARD 1 +#define ETHARP_FLAG_FIND_ONLY 2 +#if ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_FLAG_STATIC_ENTRY 4 +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#if LWIP_NETIF_HWADDRHINT +#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ + *((netif)->addr_hint) = (hint); +#else /* LWIP_NETIF_HWADDRHINT */ +#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint)) +#endif /* LWIP_NETIF_HWADDRHINT */ + + +/* Some checks, instead of etharp_init(): */ +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h" +#endif + + +static err_t etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr); +static err_t etharp_raw(struct netif *netif, + const struct eth_addr *ethsrc_addr, const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr, + const u16_t opcode); + +#if ARP_QUEUEING +/** + * Free a complete queue of etharp entries + * + * @param q a qeueue of etharp_q_entry's to free + */ +static void +free_etharp_q(struct etharp_q_entry *q) +{ + struct etharp_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ARP_QUEUE, r); + } +} +#else /* ARP_QUEUEING */ + +/** Compatibility define: free the queued pbuf */ +#define free_etharp_q(q) pbuf_free(q) + +#endif /* ARP_QUEUEING */ + +/** Clean up ARP table entries */ +static void +etharp_free_entry(int i) +{ + /* remove from SNMP ARP index tree */ + mib2_remove_arp_entry(arp_table[i].netif, &arp_table[i].ipaddr); + /* and empty packet queue */ + if (arp_table[i].q != NULL) { + /* remove all queued packets */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; + } + /* recycle entry for re-use */ + arp_table[i].state = ETHARP_STATE_EMPTY; +#ifdef LWIP_DEBUG + /* for debugging, clean out the complete entry */ + arp_table[i].ctime = 0; + arp_table[i].netif = NULL; + ip4_addr_set_zero(&arp_table[i].ipaddr); + arp_table[i].ethaddr = ethzero; +#endif /* LWIP_DEBUG */ +} + +/** + * Clears expired entries in the ARP table. + * + * This function should be called every ARP_TMR_INTERVAL milliseconds (1 second), + * in order to expire entries in the ARP table. + */ +void +etharp_tmr(void) +{ + u8_t i; + + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n")); + /* remove expired entries from the ARP table */ + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if (state != ETHARP_STATE_EMPTY +#if ETHARP_SUPPORT_STATIC_ENTRIES + && (state != ETHARP_STATE_STATIC) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + ) { + arp_table[i].ctime++; + if ((arp_table[i].ctime >= ARP_MAXAGE) || + ((arp_table[i].state == ETHARP_STATE_PENDING) && + (arp_table[i].ctime >= ARP_MAXPENDING))) { + /* pending or stable entry has become old! */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n", + arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); + /* clean up entries that have just been expired */ + etharp_free_entry(i); + } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) { + /* Don't send more than one request every 2 seconds. */ + arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2; + } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) { + /* Reset state to stable, so that the next transmitted packet will + re-send an ARP request. */ + arp_table[i].state = ETHARP_STATE_STABLE; + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* still pending, resend an ARP query */ + etharp_request(arp_table[i].netif, &arp_table[i].ipaddr); + } + } + } +} + +/** + * Search the ARP table for a matching or new entry. + * + * If an IP address is given, return a pending or stable ARP entry that matches + * the address. If no match is found, create a new entry with this address set, + * but in state ETHARP_EMPTY. The caller must check and possibly change the + * state of the returned entry. + * + * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY. + * + * In all cases, attempt to create new entries from an empty entry. If no + * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle + * old entries. Heuristic choose the least important entry for recycling. + * + * @param ipaddr IP address to find in ARP cache, or to add if not found. + * @param flags See @ref etharp_state + * @param netif netif related to this address (used for NETIF_HWADDRHINT) + * + * @return The ARP entry index that matched or is created, ERR_MEM if no + * entry is found or could be recycled. + */ +static s8_t +etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif) +{ + s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; + s8_t empty = ARP_TABLE_SIZE; + u8_t i = 0; + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u16_t age_queue = 0, age_pending = 0, age_stable = 0; + + LWIP_UNUSED_ARG(netif); + + /** + * a) do a search through the cache, remember candidates + * b) select candidate entry + * c) create new entry + */ + + /* a) in a single search sweep, do all of this + * 1) remember the first empty entry (if any) + * 2) remember the oldest stable entry (if any) + * 3) remember the oldest pending entry without queued packets (if any) + * 4) remember the oldest pending entry with queued packets (if any) + * 5) search for a matching IP entry, either pending or stable + * until 5 matches, or all entries are searched for. + */ + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + /* no empty entry found yet and now we do find one? */ + if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) { + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i)); + /* remember first empty entry */ + empty = i; + } else if (state != ETHARP_STATE_EMPTY) { + LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE", + state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE); + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip4_addr_cmp(ipaddr, &arp_table[i].ipaddr) +#if ETHARP_TABLE_MATCH_NETIF + && ((netif == NULL) || (netif == arp_table[i].netif)) +#endif /* ETHARP_TABLE_MATCH_NETIF */ + ) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ + return i; + } + /* pending entry? */ + if (state == ETHARP_STATE_PENDING) { + /* pending with queued packets? */ + if (arp_table[i].q != NULL) { + if (arp_table[i].ctime >= age_queue) { + old_queue = i; + age_queue = arp_table[i].ctime; + } + } else + /* pending without queued packets? */ + { + if (arp_table[i].ctime >= age_pending) { + old_pending = i; + age_pending = arp_table[i].ctime; + } + } + /* stable entry? */ + } else if (state >= ETHARP_STATE_STABLE) { +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* don't record old_stable for static entries since they never expire */ + if (state < ETHARP_STATE_STATIC) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* remember entry with oldest stable entry in oldest, its age in maxtime */ + if (arp_table[i].ctime >= age_stable) { + old_stable = i; + age_stable = arp_table[i].ctime; + } + } + } + } + } + /* { we have no match } => try to create a new entry */ + + /* don't create new entry, only search? */ + if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || + /* or no empty entry found and not allowed to recycle? */ + ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n")); + return (s8_t)ERR_MEM; + } + + /* b) choose the least destructive entry to recycle: + * 1) empty entry + * 2) oldest stable entry + * 3) oldest pending entry without queued packets + * 4) oldest pending entry with queued packets + * + * { ETHARP_FLAG_TRY_HARD is set at this point } + */ + + /* 1) empty entry available? */ + if (empty < ARP_TABLE_SIZE) { + i = empty; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + } else { + /* 2) found recyclable stable entry? */ + if (old_stable < ARP_TABLE_SIZE) { + /* recycle oldest stable*/ + i = old_stable; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); + /* no queued packets should exist on stable entries */ + LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); + /* 3) found recyclable pending entry without queued packets? */ + } else if (old_pending < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_pending; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); + /* 4) found recyclable pending entry with queued packets? */ + } else if (old_queue < ARP_TABLE_SIZE) { + /* recycle oldest pending (queued packets are free in etharp_free_entry) */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); + /* no empty or recyclable entries found */ + } else { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n")); + return (s8_t)ERR_MEM; + } + + /* { empty or recyclable entry found } */ + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + etharp_free_entry(i); + } + + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY", + arp_table[i].state == ETHARP_STATE_EMPTY); + + /* IP address given? */ + if (ipaddr != NULL) { + /* set IP address */ + ip4_addr_copy(arp_table[i].ipaddr, *ipaddr); + } + arp_table[i].ctime = 0; +#if ETHARP_TABLE_MATCH_NETIF + arp_table[i].netif = netif; +#endif /* ETHARP_TABLE_MATCH_NETIF*/ + return (err_t)i; +} + +/** + * Update (or insert) a IP/MAC address pair in the ARP cache. + * + * If a pending entry is resolved, any queued packets will be sent + * at this point. + * + * @param netif netif related to this entry (used for NETIF_ADDRHINT) + * @param ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags See @ref etharp_state + * + * @return + * - ERR_OK Successfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +err_t +etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) +{ + s8_t i; + LWIP_ASSERT("netif->hwaddr_len == ETH_HWADDR_LEN", netif->hwaddr_len == ETH_HWADDR_LEN); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2], + (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5])); + /* non-unicast address? */ + if (ip4_addr_isany(ipaddr) || + ip4_addr_isbroadcast(ipaddr, netif) || + ip4_addr_ismulticast(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ + i = etharp_find_entry(ipaddr, flags, netif); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + +#if ETHARP_SUPPORT_STATIC_ENTRIES + if (flags & ETHARP_FLAG_STATIC_ENTRY) { + /* record static type */ + arp_table[i].state = ETHARP_STATE_STATIC; + } else if (arp_table[i].state == ETHARP_STATE_STATIC) { + /* found entry is a static type, don't overwrite it */ + return ERR_VAL; + } else +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + } + + /* record network interface */ + arp_table[i].netif = netif; + /* insert in SNMP ARP index tree */ + mib2_add_arp_entry(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + /* update address */ + ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); + /* reset time stamp */ + arp_table[i].ctime = 0; + /* this is where we will send out queued packets! */ +#if ARP_QUEUEING + while (arp_table[i].q != NULL) { + struct pbuf *p; + /* remember remainder of queue */ + struct etharp_q_entry *q = arp_table[i].q; + /* pop first item off the queue */ + arp_table[i].q = q->next; + /* get the packet pointer */ + p = q->p; + /* now queue entry can be freed */ + memp_free(MEMP_ARP_QUEUE, q); +#else /* ARP_QUEUEING */ + if (arp_table[i].q != NULL) { + struct pbuf *p = arp_table[i].q; + arp_table[i].q = NULL; +#endif /* ARP_QUEUEING */ + /* send the queued IP packet */ + ethernet_output(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr, ETHTYPE_IP); + /* free the queued IP packet */ + pbuf_free(p); + } + return ERR_OK; +} + +#if ETHARP_SUPPORT_STATIC_ENTRIES +/** Add a new static entry to the ARP table. If an entry exists for the + * specified IP address, this entry is overwritten. + * If packets are queued for the specified IP address, they are sent out. + * + * @param ipaddr IP address for the new static entry + * @param ethaddr ethernet address for the new static entry + * @return See return values of etharp_add_static_entry + */ +err_t +etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr) +{ + struct netif *netif; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2], + (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5])); + + netif = ip4_route(ipaddr); + if (netif == NULL) { + return ERR_RTE; + } + + return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); +} + +/** Remove a static entry from the ARP table previously added with a call to + * etharp_add_static_entry. + * + * @param ipaddr IP address of the static entry to remove + * @return ERR_OK: entry removed + * ERR_MEM: entry wasn't found + * ERR_ARG: entry wasn't a static entry but a dynamic one + */ +err_t +etharp_remove_static_entry(const ip4_addr_t *ipaddr) +{ + s8_t i; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); + + /* find or create ARP entry */ + i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, NULL); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + + if (arp_table[i].state != ETHARP_STATE_STATIC) { + /* entry wasn't a static entry, cannot remove it */ + return ERR_ARG; + } + /* entry found, free it */ + etharp_free_entry(i); + return ERR_OK; +} +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +/** + * Remove all ARP table entries of the specified netif. + * + * @param netif points to a network interface + */ +void +etharp_cleanup_netif(struct netif *netif) +{ + u8_t i; + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) { + etharp_free_entry(i); + } + } +} + +/** + * Finds (stable) ethernet/IP address pair from ARP table + * using interface and IP address index. + * @note the addresses in the ARP table are in network order! + * + * @param netif points to interface index + * @param ipaddr points to the (network order) IP address index + * @param eth_ret points to return pointer + * @param ip_ret points to return pointer + * @return table index if found, -1 otherwise + */ +s8_t +etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr, + struct eth_addr **eth_ret, const ip4_addr_t **ip_ret) +{ + s8_t i; + + LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL", + eth_ret != NULL && ip_ret != NULL); + + LWIP_UNUSED_ARG(netif); + + i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif); + if ((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) { + *eth_ret = &arp_table[i].ethaddr; + *ip_ret = &arp_table[i].ipaddr; + return i; + } + return -1; +} + +/** + * Possibility to iterate over stable ARP table entries + * + * @param i entry number, 0 to ARP_TABLE_SIZE + * @param ipaddr return value: IP address + * @param netif return value: points to interface + * @param eth_ret return value: ETH address + * @return 1 on valid index, 0 otherwise + */ +u8_t +etharp_get_entry(u8_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret) +{ + LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL); + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("eth_ret != NULL", eth_ret != NULL); + + if((i < ARP_TABLE_SIZE) && (arp_table[i].state >= ETHARP_STATE_STABLE)) { + *ipaddr = &arp_table[i].ipaddr; + *netif = arp_table[i].netif; + *eth_ret = &arp_table[i].ethaddr; + return 1; + } else { + return 0; + } +} + +/** + * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache + * send out queued IP packets. Updates cache with snooped address pairs. + * + * Should be called for incoming ARP packets. The pbuf in the argument + * is freed by this function. + * + * @param p The ARP packet that arrived on netif. Is freed by this function. + * @param netif The lwIP network interface on which the ARP packet pbuf arrived. + * + * @see pbuf_free() + */ +void +etharp_input(struct pbuf *p, struct netif *netif) +{ + struct etharp_hdr *hdr; + /* these are aligned properly, whereas the ARP header fields might not be */ + ip4_addr_t sipaddr, dipaddr; + u8_t for_us; + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + hdr = (struct etharp_hdr *)p->payload; + + /* RFC 826 "Packet Reception": */ + if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) || + (hdr->hwlen != ETH_HWADDR_LEN) || + (hdr->protolen != sizeof(ip4_addr_t)) || + (hdr->proto != PP_HTONS(ETHTYPE_IP))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", + hdr->hwtype, (u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen)); + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + ETHARP_STATS_INC(etharp.recv); + +#if LWIP_AUTOIP + /* We have to check if a host already has configured our random + * created link local address and continuously check if there is + * a host with this IP-address so we can detect collisions */ + autoip_arp_reply(netif, hdr); +#endif /* LWIP_AUTOIP */ + + /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + /* this interface is not configured? */ + if (ip4_addr_isany_val(*netif_ip4_addr(netif))) { + for_us = 0; + } else { + /* ARP packet directed to us? */ + for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif)); + } + + /* ARP message directed to us? + -> add IP address in ARP cache; assume requester wants to talk to us, + can result in directly sending the queued packets for this host. + ARP message not directed to us? + -> update the source IP address in the cache, if present */ + etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), + for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY); + + /* now act on the message itself */ + switch (hdr->opcode) { + /* ARP request? */ + case PP_HTONS(ARP_REQUEST): + /* ARP request. If it asked for our address, we send out a + * reply. In any case, we time-stamp any existing ARP entry, + * and possibly send out an IP packet that was queued on it. */ + + LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP request\n")); + /* ARP request for our address? */ + if (for_us) { + /* send ARP response */ + etharp_raw(netif, + (struct eth_addr *)netif->hwaddr, &hdr->shwaddr, + (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), + &hdr->shwaddr, &sipaddr, + ARP_REPLY); + /* we are not configured? */ + } else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) { + /* { for_us == 0 and netif->ip_addr.addr == 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n")); + /* request was not directed to us */ + } else { + /* { for_us == 0 and netif->ip_addr.addr != 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n")); + } + break; + case PP_HTONS(ARP_REPLY): + /* ARP reply. We already updated the ARP cache earlier. */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP reply\n")); +#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) + /* DHCP wants to know about ARP replies from any host with an + * IP address also offered to us by the DHCP server. We do not + * want to take a duplicate IP address on a single network. + * @todo How should we handle redundant (fail-over) interfaces? */ + dhcp_arp_reply(netif, &sipaddr); +#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */ + break; + default: + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP unknown opcode type %"S16_F"\n", lwip_htons(hdr->opcode))); + ETHARP_STATS_INC(etharp.err); + break; + } + /* free ARP packet */ + pbuf_free(p); +} + +/** Just a small helper function that sends a pbuf to an ethernet address + * in the arp_table specified by the index 'arp_idx'. + */ +static err_t +etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) +{ + LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE", + arp_table[arp_idx].state >= ETHARP_STATE_STABLE); + /* if arp table entry is about to expire: re-request it, + but only if its state is ETHARP_STATE_STABLE to prevent flooding the + network with ARP requests if this address is used frequently. */ + if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) { + if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) { + /* issue a standard request using broadcast */ + if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { + arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1; + } + } else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) { + /* issue a unicast request (for 15 seconds) to prevent unnecessary broadcast */ + if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) { + arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1; + } + } + } + + return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IP packet. + * + * For IP multicast and broadcast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, the packet is submitted to etharp_query(). In + * case the IP address is outside the local network, the IP address of + * the gateway is used. + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ipaddr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or ethernet_output(). + */ +err_t +etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) +{ + const struct eth_addr *dest; + struct eth_addr mcastaddr; + const ip4_addr_t *dst_addr = ipaddr; + + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL); + + /* Determine on destination hardware address. Broadcasts and multicasts + * are special, other IP addresses are looked up in the ARP table. */ + + /* broadcast destination IP address? */ + if (ip4_addr_isbroadcast(ipaddr, netif)) { + /* broadcast on Ethernet also */ + dest = (const struct eth_addr *)ðbroadcast; + /* multicast destination IP address? */ + } else if (ip4_addr_ismulticast(ipaddr)) { + /* Hash IP multicast address to MAC address.*/ + mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0; + mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1; + mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2; + mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; + mcastaddr.addr[4] = ip4_addr3(ipaddr); + mcastaddr.addr[5] = ip4_addr4(ipaddr); + /* destination Ethernet address is multicast */ + dest = &mcastaddr; + /* unicast destination IP address? */ + } else { + s8_t i; + /* outside local network? if so, this can neither be a global broadcast nor + a subnet broadcast. */ + if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) && + !ip4_addr_islinklocal(ipaddr)) { +#if LWIP_AUTOIP + struct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr*, q->payload); + /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with + a link-local source address must always be "directly to its destination + on the same physical link. The host MUST NOT send the packet to any + router for forwarding". */ + if (!ip4_addr_islinklocal(&iphdr->src)) +#endif /* LWIP_AUTOIP */ + { +#ifdef LWIP_HOOK_ETHARP_GET_GW + /* For advanced routing, a single default gateway might not be enough, so get + the IP address of the gateway to handle the current destination address. */ + dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr); + if (dst_addr == NULL) +#endif /* LWIP_HOOK_ETHARP_GET_GW */ + { + /* interface has default gateway? */ + if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) { + /* send to hardware address of default gateway IP address */ + dst_addr = netif_ip4_gw(netif); + /* no default gateway available */ + } else { + /* no route to destination error (default gateway missing) */ + return ERR_RTE; + } + } + } + } +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t etharp_cached_entry = *(netif->addr_hint); + if (etharp_cached_entry < ARP_TABLE_SIZE) { +#endif /* LWIP_NETIF_HWADDRHINT */ + if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) && +#if ETHARP_TABLE_MATCH_NETIF + (arp_table[etharp_cached_entry].netif == netif) && +#endif + (ip4_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { + /* the per-pcb-cached entry is stable and the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_output_to_arp_index(netif, q, etharp_cached_entry); + } +#if LWIP_NETIF_HWADDRHINT + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* find stable entry: do this here since this is a critical path for + throughput and etharp_find_entry() is kind of slow */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if ((arp_table[i].state >= ETHARP_STATE_STABLE) && +#if ETHARP_TABLE_MATCH_NETIF + (arp_table[i].netif == netif) && +#endif + (ip4_addr_cmp(dst_addr, &arp_table[i].ipaddr))) { + /* found an existing, stable entry */ + ETHARP_SET_HINT(netif, i); + return etharp_output_to_arp_index(netif, q, i); + } + } + /* no stable entry found, use the (slower) query function: + queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, dst_addr, q); + } + + /* continuation for multicast/broadcast destinations */ + /* obtain source Ethernet address of the given interface */ + /* send packet directly on the link */ + return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), dest, ETHTYPE_IP); +} + +/** + * Send an ARP request for the given IP address and/or queue a packet. + * + * If the IP address was not yet in the cache, a pending ARP cache entry + * is added and an ARP request is sent for the given address. The packet + * is queued on this entry. + * + * If the IP address was already pending in the cache, a new ARP request + * is sent for the given address. The packet is queued on this entry. + * + * If the IP address was already stable in the cache, and a packet is + * given, it is directly sent and no ARP request is sent out. + * + * If the IP address was already stable in the cache, and no packet is + * given, an ARP request is sent out. + * + * @param netif The lwIP network interface on which ipaddr + * must be queried for. + * @param ipaddr The IP address to be resolved. + * @param q If non-NULL, a pbuf that must be delivered to the IP address. + * q is not freed by this function. + * + * @note q must only be ONE packet, not a packet queue! + * + * @return + * - ERR_BUF Could not make room for Ethernet header. + * - ERR_MEM Hardware address unknown, and no more ARP entries available + * to query for address or queue the packet. + * - ERR_MEM Could not queue packet due to memory shortage. + * - ERR_RTE No route to destination (no gateway to external networks). + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + */ +err_t +etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q) +{ + struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; + err_t result = ERR_MEM; + int is_new_entry = 0; + s8_t i; /* ARP entry index */ + + /* non-unicast address? */ + if (ip4_addr_isbroadcast(ipaddr, netif) || + ip4_addr_ismulticast(ipaddr) || + ip4_addr_isany(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + + /* find entry in ARP cache, ask to create entry if queueing packet */ + i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif); + + /* could not find or create entry? */ + if (i < 0) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n")); + if (q) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n")); + ETHARP_STATS_INC(etharp.memerr); + } + return (err_t)i; + } + + /* mark a fresh entry as pending (we just sent a request) */ + if (arp_table[i].state == ETHARP_STATE_EMPTY) { + is_new_entry = 1; + arp_table[i].state = ETHARP_STATE_PENDING; + /* record network interface for re-sending arp request in etharp_tmr */ + arp_table[i].netif = netif; + } + + /* { i is either a STABLE or (new or existing) PENDING entry } */ + LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", + ((arp_table[i].state == ETHARP_STATE_PENDING) || + (arp_table[i].state >= ETHARP_STATE_STABLE))); + + /* do we have a new entry? or an implicit query request? */ + if (is_new_entry || (q == NULL)) { + /* try to resolve it; send out ARP request */ + result = etharp_request(netif, ipaddr); + if (result != ERR_OK) { + /* ARP request couldn't be sent */ + /* We don't re-send arp request in etharp_tmr, but we still queue packets, + since this failure could be temporary, and the next packet calling + etharp_query again could lead to sending the queued packets. */ + } + if (q == NULL) { + return result; + } + } + + /* packet given? */ + LWIP_ASSERT("q != NULL", q != NULL); + /* stable entry? */ + if (arp_table[i].state >= ETHARP_STATE_STABLE) { + /* we have a valid IP->Ethernet address mapping */ + ETHARP_SET_HINT(netif, i); + /* send the packet */ + result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP); + /* pending entry? (either just created or already pending */ + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* entry is still pending, queue the given packet 'q' */ + struct pbuf *p; + int copy_needed = 0; + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0)); + if (p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if (copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet could be taken over? */ + if (p != NULL) { + /* queue packet ... */ +#if ARP_QUEUEING + struct etharp_q_entry *new_entry; + /* allocate a new arp queue entry */ + new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE); + if (new_entry != NULL) { + unsigned int qlen = 0; + new_entry->next = 0; + new_entry->p = p; + if (arp_table[i].q != NULL) { + /* queue was already existent, append the new entry to the end */ + struct etharp_q_entry *r; + r = arp_table[i].q; + qlen++; + while (r->next != NULL) { + r = r->next; + qlen++; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + arp_table[i].q = new_entry; + } +#if ARP_QUEUE_LEN + if (qlen >= ARP_QUEUE_LEN) { + struct etharp_q_entry *old; + old = arp_table[i].q; + arp_table[i].q = arp_table[i].q->next; + pbuf_free(old->p); + memp_free(MEMP_ARP_QUEUE, old); + } +#endif + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + result = ERR_OK; + } else { + /* the pool MEMP_ARP_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } +#else /* ARP_QUEUEING */ + /* always queue one packet per ARP request only, freeing a previously queued packet */ + if (arp_table[i].q != NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + pbuf_free(arp_table[i].q); + } + arp_table[i].q = p; + result = ERR_OK; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); +#endif /* ARP_QUEUEING */ + } else { + ETHARP_STATS_INC(etharp.memerr); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } + } + return result; +} + +/** + * Send a raw ARP packet (opcode and all addresses can be modified) + * + * @param netif the lwip network interface on which to send the ARP packet + * @param ethsrc_addr the source MAC address for the ethernet header + * @param ethdst_addr the destination MAC address for the ethernet header + * @param hwsrc_addr the source MAC address for the ARP protocol header + * @param ipsrc_addr the source IP address for the ARP protocol header + * @param hwdst_addr the destination MAC address for the ARP protocol header + * @param ipdst_addr the destination IP address for the ARP protocol header + * @param opcode the type of the ARP packet + * @return ERR_OK if the ARP packet has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +static err_t +etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr, + const u16_t opcode) +{ + struct pbuf *p; + err_t result = ERR_OK; + struct etharp_hdr *hdr; + + LWIP_ASSERT("netif != NULL", netif != NULL); + + /* allocate a pbuf for the outgoing ARP request packet */ + p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM); + /* could allocate a pbuf for an ARP request? */ + if (p == NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_raw: could not allocate pbuf for ARP request.\n")); + ETHARP_STATS_INC(etharp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr", + (p->len >= SIZEOF_ETHARP_HDR)); + + hdr = (struct etharp_hdr *)p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n")); + hdr->opcode = lwip_htons(opcode); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETH_HWADDR_LEN)); + + /* Write the ARP MAC-Addresses */ + ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr); + ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr); + /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without + * structure packing. */ + IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr); + IPADDR2_COPY(&hdr->dipaddr, ipdst_addr); + + hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET); + hdr->proto = PP_HTONS(ETHTYPE_IP); + /* set hwlen and protolen */ + hdr->hwlen = ETH_HWADDR_LEN; + hdr->protolen = sizeof(ip4_addr_t); + + /* send ARP query */ +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + if(ip4_addr_islinklocal(ipsrc_addr)) { + ethernet_output(netif, p, ethsrc_addr, ðbroadcast, ETHTYPE_ARP); + } else +#endif /* LWIP_AUTOIP */ + { + ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP); + } + + ETHARP_STATS_INC(etharp.xmit); + /* free ARP query packet */ + pbuf_free(p); + p = NULL; + /* could not allocate pbuf for ARP request */ + + return result; +} + +/** + * Send an ARP request packet asking for ipaddr to a specific eth address. + * Used to send unicast request to refresh the ARP table just before an entry + * times out + * + * @param netif the lwip network interface on which to send the request + * @param ipaddr the IP address for which to ask + * @param hw_dst_addr the ethernet address to send this packet to + * @return ERR_OK if the request has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +static err_t +etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr, + (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), ðzero, + ipaddr, ARP_REQUEST); +} + +/** + * Send an ARP request packet asking for ipaddr. + * + * @param netif the lwip network interface on which to send the request + * @param ipaddr the IP address for which to ask + * @return ERR_OK if the request has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +err_t +etharp_request(struct netif *netif, const ip4_addr_t *ipaddr) +{ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n")); + return etharp_request_dst(netif, ipaddr, ðbroadcast); +} +#endif /* LWIP_IPV4 && LWIP_ARP */ + +#endif /* LWIP_ARP || LWIP_ETHERNET */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/icmp.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/icmp.c new file mode 100644 index 0000000..5ee24ee --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/icmp.c @@ -0,0 +1,397 @@ +/** + * @file + * ICMP - Internet Control Message Protocol + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" + +#include + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be + * used to modify and send a response packet (and to 1 if this is not the case, + * e.g. when link header is stripped of when receiving) */ +#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1 +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + +/* The amount of data from the original packet to return in a dest-unreachable */ +#define ICMP_DEST_UNREACH_DATASIZE 8 + +static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); + +/** + * Processes ICMP input packets, called from ip_input(). + * + * Currently only processes icmp echo requests and sends + * out the echo response. + * + * @param p the icmp echo request packet, p->payload pointing to the icmp header + * @param inp the netif on which this packet was received + */ +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; +#ifdef LWIP_DEBUG + u8_t code; +#endif /* LWIP_DEBUG */ + struct icmp_echo_hdr *iecho; + const struct ip_hdr *iphdr_in; + u16_t hlen; + const ip4_addr_t* src; + + ICMP_STATS_INC(icmp.recv); + MIB2_STATS_INC(mib2.icmpinmsgs); + + iphdr_in = ip4_current_header(); + hlen = IPH_HL(iphdr_in) * 4; + if (hlen < IP_HLEN) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen)); + goto lenerr; + } + if (p->len < sizeof(u16_t)*2) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); + goto lenerr; + } + + type = *((u8_t *)p->payload); +#ifdef LWIP_DEBUG + code = *(((u8_t *)p->payload)+1); +#endif /* LWIP_DEBUG */ + switch (type) { + case ICMP_ER: + /* This is OK, echo reply might have been parsed by a raw PCB + (as obviously, an echo request has been sent, too). */ + MIB2_STATS_INC(mib2.icmpinechoreps); + break; + case ICMP_ECHO: + MIB2_STATS_INC(mib2.icmpinechos); + src = ip4_current_dest_addr(); + /* multicast destination address? */ + if (ip4_addr_ismulticast(ip4_current_dest_addr())) { +#if LWIP_MULTICAST_PING + /* For multicast, use address of receiving interface as source address */ + src = netif_ip4_addr(inp); +#else /* LWIP_MULTICAST_PING */ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast pings\n")); + goto icmperr; +#endif /* LWIP_MULTICAST_PING */ + } + /* broadcast destination address? */ + if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) { +#if LWIP_BROADCAST_PING + /* For broadcast, use address of receiving interface as source address */ + src = netif_ip4_addr(inp); +#else /* LWIP_BROADCAST_PING */ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n")); + goto icmperr; +#endif /* LWIP_BROADCAST_PING */ + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + goto lenerr; + } +#if CHECKSUM_CHECK_ICMP + IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP) { + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.chkerr); + MIB2_STATS_INC(mib2.icmpinerrors); + return; + } + } +#endif +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + if (pbuf_header(p, (s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) { + /* p is not big enough to contain link headers + * allocate a new one and copy p into it + */ + struct pbuf *r; + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len + hlen, PBUF_RAM); + if (r == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); + goto icmperr; + } + if (r->len < hlen + sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header")); + pbuf_free(r); + goto icmperr; + } + /* copy the ip header */ + MEMCPY(r->payload, iphdr_in, hlen); + /* switch r->payload back to icmp header (cannot fail) */ + if (pbuf_header(r, (s16_t)-hlen)) { + LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0); + pbuf_free(r); + goto icmperr; + } + /* copy the rest of the packet without ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("icmp_input: copying to new pbuf failed")); + pbuf_free(r); + goto icmperr; + } + /* free the original p */ + pbuf_free(p); + /* we now have an identical copy of p that has room for link headers */ + p = r; + } else { + /* restore p->payload to point to icmp header (cannot fail) */ + if (pbuf_header(p, -(s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto icmperr; + } + } +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + /* At this point, all checks are OK. */ + /* We generate an answer by switching the dest and src ip addresses, + * setting the icmp type to ECHO_RESPONSE and updating the checksum. */ + iecho = (struct icmp_echo_hdr *)p->payload; + if (pbuf_header(p, (s16_t)hlen)) { + LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet")); + } else { + err_t ret; + struct ip_hdr *iphdr = (struct ip_hdr*)p->payload; + ip4_addr_copy(iphdr->src, *src); + ip4_addr_copy(iphdr->dest, *ip4_current_src_addr()); + ICMPH_TYPE_SET(iecho, ICMP_ER); +#if CHECKSUM_GEN_ICMP + IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) { + /* adjust the checksum */ + if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8); + } + } +#if LWIP_CHECKSUM_CTRL_PER_NETIF + else { + iecho->chksum = 0; + } +#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */ +#else /* CHECKSUM_GEN_ICMP */ + iecho->chksum = 0; +#endif /* CHECKSUM_GEN_ICMP */ + + /* Set the correct TTL and recalculate the header checksum. */ + IPH_TTL_SET(iphdr, ICMP_TTL); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) { + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen)); + } +#endif /* CHECKSUM_GEN_IP */ + + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + MIB2_STATS_INC(mib2.icmpoutmsgs); + /* increase number of echo replies attempted to send */ + MIB2_STATS_INC(mib2.icmpoutechoreps); + + /* send an ICMP packet */ + ret = ip4_output_if(p, src, LWIP_IP_HDRINCL, + ICMP_TTL, 0, IP_PROTO_ICMP, inp); + if (ret != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret))); + } + } + break; + default: + if (type == ICMP_DUR) { + MIB2_STATS_INC(mib2.icmpindestunreachs); + } else if (type == ICMP_TE) { + MIB2_STATS_INC(mib2.icmpintimeexcds); + } else if (type == ICMP_PP) { + MIB2_STATS_INC(mib2.icmpinparmprobs); + } else if (type == ICMP_SQ) { + MIB2_STATS_INC(mib2.icmpinsrcquenchs); + } else if (type == ICMP_RD) { + MIB2_STATS_INC(mib2.icmpinredirects); + } else if (type == ICMP_TS) { + MIB2_STATS_INC(mib2.icmpintimestamps); + } else if (type == ICMP_TSR) { + MIB2_STATS_INC(mib2.icmpintimestampreps); + } else if (type == ICMP_AM) { + MIB2_STATS_INC(mib2.icmpinaddrmasks); + } else if (type == ICMP_AMR) { + MIB2_STATS_INC(mib2.icmpinaddrmaskreps); + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", + (s16_t)type, (s16_t)code)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + pbuf_free(p); + return; +lenerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + MIB2_STATS_INC(mib2.icmpinerrors); + return; +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING +icmperr: + pbuf_free(p); + ICMP_STATS_INC(icmp.err); + MIB2_STATS_INC(mib2.icmpinerrors); + return; +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */ +} + +/** + * Send an icmp 'destination unreachable' packet, called from ip_input() if + * the transport layer protocol is unknown and from udp_input() if the local + * port is not bound. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'unreachable' packet + */ +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + MIB2_STATS_INC(mib2.icmpoutdestunreachs); + icmp_send_response(p, ICMP_DUR, t); +} + +#if IP_FORWARD || IP_REASSEMBLY +/** + * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'time exceeded' packet + */ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + MIB2_STATS_INC(mib2.icmpouttimeexcds); + icmp_send_response(p, ICMP_TE, t); +} + +#endif /* IP_FORWARD || IP_REASSEMBLY */ + +/** + * Send an icmp packet in response to an incoming packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param type Type of the ICMP header + * @param code Code of the ICMP header + */ +static void +icmp_send_response(struct pbuf *p, u8_t type, u8_t code) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + /* we can use the echo header here */ + struct icmp_echo_hdr *icmphdr; + ip4_addr_t iphdr_src; + struct netif *netif; + + /* increase number of messages attempted to send */ + MIB2_STATS_INC(mib2.icmpoutmsgs); + + /* ICMP header + IP header + 8 bytes of data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n")); + MIB2_STATS_INC(mib2.icmpouterrors); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); + + iphdr = (struct ip_hdr *)p->payload; + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src); + LWIP_DEBUGF(ICMP_DEBUG, (" to ")); + ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest); + LWIP_DEBUGF(ICMP_DEBUG, ("\n")); + + icmphdr = (struct icmp_echo_hdr *)q->payload; + icmphdr->type = type; + icmphdr->code = code; + icmphdr->id = 0; + icmphdr->seqno = 0; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); + + ip4_addr_copy(iphdr_src, iphdr->src); +#ifdef LWIP_HOOK_IP4_ROUTE_SRC + { + ip4_addr_t iphdr_dst; + ip4_addr_copy(iphdr_dst, iphdr->dest); + netif = ip4_route_src(&iphdr_src, &iphdr_dst); + } +#else + netif = ip4_route(&iphdr_src); +#endif + if (netif != NULL) { + /* calculate checksum */ + icmphdr->chksum = 0; +#if CHECKSUM_GEN_ICMP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) { + icmphdr->chksum = inet_chksum(icmphdr, q->len); + } +#endif + ICMP_STATS_INC(icmp.xmit); + ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif); + } + pbuf_free(q); +} + +#endif /* LWIP_IPV4 && LWIP_ICMP */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/igmp.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/igmp.c new file mode 100644 index 0000000..ddadbac --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/igmp.c @@ -0,0 +1,800 @@ +/** + * @file + * IGMP - Internet Group Management Protocol + * + * @defgroup igmp IGMP + * @ingroup ip4 + * To be called from TCPIP thread + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 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 CITEL Technologies Ltd 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 CITEL TECHNOLOGIES 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 CITEL TECHNOLOGIES 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 a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +/*------------------------------------------------------------- +Note 1) +Although the rfc requires V1 AND V2 capability +we will only support v2 since now V1 is very old (August 1989) +V1 can be added if required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 2) +A query for a specific group address (as opposed to ALLHOSTS) +has now been implemented as I am unsure if it is required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 3) +The router alert rfc 2113 is implemented in outgoing packets +but not checked rigorously incoming +------------------------------------------------------------- +Steve Reynolds +------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * RFC 988 - Host extensions for IP multicasting - V0 + * RFC 1054 - Host extensions for IP multicasting - + * RFC 1112 - Host extensions for IP multicasting - V1 + * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) + * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 + * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ + * RFC 2113 - IP Router Alert Option - + *----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/igmp.h" +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/stats.h" +#include "lwip/prot/igmp.h" + +#include "string.h" + +static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr); +static err_t igmp_remove_group(struct netif* netif, struct igmp_group *group); +static void igmp_timeout(struct netif *netif, struct igmp_group *group); +static void igmp_start_timer(struct igmp_group *group, u8_t max_time); +static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); +static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif); +static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type); + +static ip4_addr_t allsystems; +static ip4_addr_t allrouters; + +/** + * Initialize the IGMP module + */ +void +igmp_init(void) +{ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); +} + +/** + * Start IGMP processing on interface + * + * @param netif network interface on which start IGMP processing + */ +err_t +igmp_start(struct netif *netif) +{ + struct igmp_group* group; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void*)netif)); + + group = igmp_lookup_group(netif, &allsystems); + + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); + ip4_addr_debug_print_val(IGMP_DEBUG, allsystems); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif)); + netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER); + } + + return ERR_OK; + } + + return ERR_MEM; +} + +/** + * Stop IGMP processing on interface + * + * @param netif network interface on which stop IGMP processing + */ +err_t +igmp_stop(struct netif *netif) +{ + struct igmp_group *group = netif_igmp_data(netif); + + netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL); + + while (group != NULL) { + struct igmp_group *next = group->next; /* avoid use-after-free below */ + + /* disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); + ip4_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif)); + netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER); + } + + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report IGMP memberships for this interface + * + * @param netif network interface on which report IGMP memberships + */ +void +igmp_report_groups(struct netif *netif) +{ + struct igmp_group *group = netif_igmp_data(netif); + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void*)netif)); + + /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ + if(group != NULL) { + group = group->next; + } + + while (group != NULL) { + igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + group = group->next; + } +} + +/** + * Search for a group in the global igmp_group_list + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search for + * @return a struct igmp_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct igmp_group * +igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr) +{ + struct igmp_group *group = netif_igmp_data(ifp); + + while (group != NULL) { + if (ip4_addr_cmp(&(group->group_address), addr)) { + return group; + } + group = group->next; + } + + /* to be clearer, we return NULL here instead of + * 'group' (which is also NULL at this point). + */ + return NULL; +} + +/** + * Search for a specific igmp group and create a new one if not found- + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search + * @return a struct igmp_group*, + * NULL on memory error. + */ +static struct igmp_group * +igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr) +{ + struct igmp_group *group; + struct igmp_group *list_head = netif_igmp_data(ifp); + + /* Search if the group already exists */ + group = igmp_lookfor_group(ifp, addr); + if (group != NULL) { + /* Group already exists. */ + return group; + } + + /* Group doesn't exist yet, create a new one */ + group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + ip4_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + + /* Ensure allsystems group is always first in list */ + if (list_head == NULL) { + /* this is the first entry in linked list */ + LWIP_ASSERT("igmp_lookup_group: first group must be allsystems", + (ip4_addr_cmp(addr, &allsystems) != 0)); + group->next = NULL; + netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group); + } else { + /* append _after_ first entry */ + LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems", + (ip4_addr_cmp(addr, &allsystems) == 0)); + group->next = list_head->next; + list_head->next = group; + } + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); + ip4_addr_debug_print(IGMP_DEBUG, addr); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)ifp)); + + return group; +} + +/** + * Remove a group in the global igmp_group_list, but don't free it yet + * + * @param group the group to remove from the global igmp_group_list + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +igmp_remove_group(struct netif* netif, struct igmp_group *group) +{ + err_t err = ERR_OK; + struct igmp_group *tmp_group; + + /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ + for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) { + if (tmp_group->next == group) { + tmp_group->next = group->next; + break; + } + } + /* Group not found in the global igmp_group_list */ + if (tmp_group == NULL) { + err = ERR_ARG; + } + + return err; +} + +/** + * Called from ip_input() if a new IGMP packet is received. + * + * @param p received igmp packet, p->payload pointing to the igmp header + * @param inp network interface on which the packet was received + * @param dest destination ip address of the igmp packet + */ +void +igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest) +{ + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + IGMP_STATS_INC(igmp.recv); + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + if (p->len < IGMP_MINLEN) { + pbuf_free(p); + IGMP_STATS_INC(igmp.lenerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); + return; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); + ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->dest)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)inp)); + + /* Now calculate and check the checksum */ + igmp = (struct igmp_msg *)p->payload; + if (inet_chksum(igmp, p->len)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.chkerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); + return; + } + + /* Packet is ok so find an existing group */ + group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + IGMP_STATS_INC(igmp.drop); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); + return; + } + + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) { + /* THIS IS THE GENERAL QUERY */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + + if (igmp->igmp_maxresp == 0) { + IGMP_STATS_INC(igmp.rx_v1); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); + igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; + } else { + IGMP_STATS_INC(igmp.rx_general); + } + + groupref = netif_igmp_data(inp); + + /* Do not send messages on the all systems group address! */ + /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */ + if(groupref != NULL) { + groupref = groupref->next; + } + + while (groupref) { + igmp_delaying_member(groupref, igmp->igmp_maxresp); + groupref = groupref->next; + } + } else { + /* IGMP_MEMB_QUERY to a specific group ? */ + if (!ip4_addr_isany(&igmp->igmp_group_address)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip4_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); + if (ip4_addr_cmp(dest, &allsystems)) { + ip4_addr_t groupaddr; + LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + /* we first need to re-look for the group since we used dest last time */ + ip4_addr_copy(groupaddr, igmp->igmp_group_address); + group = igmp_lookfor_group(inp, &groupaddr); + } else { + LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + } + + if (group != NULL) { + IGMP_STATS_INC(igmp.rx_group); + igmp_delaying_member(group, igmp->igmp_maxresp); + } else { + IGMP_STATS_INC(igmp.drop); + } + } else { + IGMP_STATS_INC(igmp.proterr); + } + } + break; + case IGMP_V2_MEMB_REPORT: + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + IGMP_STATS_INC(igmp.rx_report); + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + /* This is on a specific group we have already looked up */ + group->timer = 0; /* stopped */ + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + break; + default: + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", + igmp->igmp_msgtype, group->group_state, (void*)&group, (void*)inp)); + IGMP_STATS_INC(igmp.proterr); + break; + } + + pbuf_free(p); + return; +} + +/** + * @ingroup igmp + * Join a group on one network interface. + * + * @param ifaddr ip address of the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) { + err = igmp_joingroup_netif(netif, groupaddr); + if (err != ERR_OK) { + /* Return an error even if some network interfaces are joined */ + /** @todo undo any other netif already joined */ + return err; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * @ingroup igmp + * Join a group on one network interface. + * + * @param netif the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif, an err_t otherwise + */ +err_t +igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) +{ + struct igmp_group *group; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* make sure it is an igmp-enabled netif */ + LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;); + + /* find group or create a new one if not found */ + group = igmp_lookup_group(netif, groupaddr); + + if (group != NULL) { + /* This should create a new group, check the state to make sure */ + if (group->group_state != IGMP_GROUP_NON_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n")); + } else { + /* OK - it was new group */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: ")); + ip4_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If first use of the group, allow the group at the MAC level */ + if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD ")); + ip4_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif)); + netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER); + } + + IGMP_STATS_INC(igmp.tx_join); + igmp_send(netif, group, IGMP_V2_MEMB_REPORT); + + igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } + /* Increment group use */ + group->use++; + /* Join on this interface */ + return ERR_OK; + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n")); + return ERR_MEM; + } +} + +/** + * @ingroup igmp + * Leave a group on one network interface. + * + * @param ifaddr ip address of the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) { + err_t res = igmp_leavegroup_netif(netif, groupaddr); + if (err != ERR_OK) { + /* Store this result if we have not yet gotten a success */ + err = res; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * @ingroup igmp + * Leave a group on one network interface. + * + * @param netif the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif, an err_t otherwise + */ +err_t +igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) +{ + struct igmp_group *group; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* make sure it is an igmp-enabled netif */ + LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;); + + /* find group */ + group = igmp_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: ")); + ip4_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If there is no other use of the group */ + if (group->use <= 1) { + /* Remove the group from the list */ + igmp_remove_group(netif, group); + + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n")); + IGMP_STATS_INC(igmp.tx_leave); + igmp_send(netif, group, IGMP_LEAVE_GROUP); + } + + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL ")); + ip4_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif)); + netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER); + } + + /* Free group struct */ + memp_free(MEMP_IGMP_GROUP, group); + } else { + /* Decrement group use */ + group->use--; + } + return ERR_OK; + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n")); + return ERR_VAL; + } +} + +/** + * The igmp timer function (both for NO_SYS=1 and =0) + * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). + */ +void +igmp_tmr(void) +{ + struct netif *netif = netif_list; + + while (netif != NULL) { + struct igmp_group *group = netif_igmp_data(netif); + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + igmp_timeout(netif, group); + } + } + group = group->next; + } + netif = netif->next; + } +} + +/** + * Called if a timeout for one group is reached. + * Sends a report for this group. + * + * @param group an igmp_group for which a timeout is reached + */ +static void +igmp_timeout(struct netif *netif, struct igmp_group *group) +{ + /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group + (unless it is the allsystems group) */ + if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && + (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); + ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)netif)); + + group->group_state = IGMP_GROUP_IDLE_MEMBER; + + IGMP_STATS_INC(igmp.tx_report); + igmp_send(netif, group, IGMP_V2_MEMB_REPORT); + } +} + +/** + * Start a timer for an igmp group + * + * @param group the igmp_group for which to start a timer + * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with + * every call to igmp_tmr()) + */ +static void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ +#ifdef LWIP_RAND + group->timer = max_time > 2 ? (LWIP_RAND() % max_time) : 1; +#else /* LWIP_RAND */ + /* ATTENTION: use this only if absolutely necessary! */ + group->timer = max_time / 2; +#endif /* LWIP_RAND */ + + if (group->timer == 0) { + group->timer = 1; + } +} + +/** + * Delaying membership report for a group if necessary + * + * @param group the igmp_group for which "delaying" membership report + * @param maxresp query delay + */ +static void +igmp_delaying_member(struct igmp_group *group, u8_t maxresp) +{ + if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || + ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + igmp_start_timer(group, maxresp); + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } +} + + +/** + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, + * the IP address of the outgoing network interface is filled in as source address. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == LWIP_IP_HDRINCL, p already includes an + IP header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + */ +static err_t +igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif) +{ + /* This is the "router alert" option */ + u16_t ra[2]; + ra[0] = PP_HTONS(ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + IGMP_STATS_INC(igmp.xmit); + return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); +} + +/** + * Send an igmp packet to a specific group. + * + * @param group the group to which to send the packet + * @param type the type of igmp packet to send + */ +static void +igmp_send(struct netif *netif, struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + ip4_addr_t src = *IP4_ADDR_ANY4; + ip4_addr_t* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = (struct igmp_msg *)p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip4_addr_copy(src, *netif_ip4_addr(netif)); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + ip4_addr_copy(igmp->igmp_group_address, group->group_address); + group->last_reporter_flag = 1; /* Remember we were the last to report */ + } else { + if (type == IGMP_LEAVE_GROUP) { + dest = &allrouters; + ip4_addr_copy(igmp->igmp_group_address, group->group_address); + } + } + + if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { + igmp->igmp_msgtype = type; + igmp->igmp_maxresp = 0; + igmp->igmp_checksum = 0; + igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); + + igmp_ip_output_if(p, &src, dest, netif); + } + + pbuf_free(p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + IGMP_STATS_INC(igmp.memerr); + } +} + +#endif /* LWIP_IPV4 && LWIP_IGMP */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4.c new file mode 100644 index 0000000..ccb4944 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4.c @@ -0,0 +1,1087 @@ +/** + * @file + * This is the IPv4 layer implementation for incoming and outgoing IP traffic. + * + * @see ip_frag.c + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 + +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip4_frag.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/igmp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/autoip.h" +#include "lwip/stats.h" +#include "lwip/prot/dhcp.h" + +#include "wm_sockets.h" +#include + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +/** Set this to 0 in the rare case of wanting to call an extra function to + * generate the IP checksum (in contrast to calculating it on-the-fly). */ +#ifndef LWIP_INLINE_IP_CHKSUM +#if LWIP_CHECKSUM_CTRL_PER_NETIF +#define LWIP_INLINE_IP_CHKSUM 0 +#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */ +#define LWIP_INLINE_IP_CHKSUM 1 +#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */ +#endif + +#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP_INLINE 1 +#else +#define CHECKSUM_GEN_IP_INLINE 0 +#endif + +#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT) +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1 + +/** Some defines for DHCP to let link-layer-addressed packets through while the + * netif is down. + * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT(port) + * to return 1 if the port is accepted and 0 if the port is not accepted. + */ +#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) +/* accept DHCP client port and custom port */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \ + || (LWIP_IP_ACCEPT_UDP_PORT(port))) +#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept custom port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port)) +#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept DHCP client port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT)) +#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ + +#else /* LWIP_DHCP */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0 +#endif /* LWIP_DHCP */ + +/** The IP header ID of the next outgoing IP packet */ +static u16_t ip_id; + +#if LWIP_MULTICAST_TX_OPTIONS +/** The default netif used for multicast */ +static struct netif* ip4_default_multicast_netif; + +/** + * @ingroup ip4 + * Set a default netif for IPv4 multicast. */ +void +ip4_set_default_multicast_netif(struct netif* default_multicast_netif) +{ + ip4_default_multicast_netif = default_multicast_netif; +} +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + +#ifdef LWIP_HOOK_IP4_ROUTE_SRC +/** + * Source based IPv4 routing must be fully implemented in + * LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides he parameters. + */ +struct netif * +ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src) +{ + if (src != NULL) { + /* when src==NULL, the hook is called from ip4_route(dest) */ + struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, src); + if (netif != NULL) { + return netif; + } + } + return ip4_route(dest); +} +#endif /* LWIP_HOOK_IP4_ROUTE_SRC */ + +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip4_route(const ip4_addr_t *dest) +{ + struct netif *netif; + +#if LWIP_MULTICAST_TX_OPTIONS + /* Use administratively selected interface for multicast by default */ + if (ip4_addr_ismulticast(dest) && ip4_default_multicast_netif) { + return ip4_default_multicast_netif; + } +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + + /* iterate through netifs */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + /* is the netif up, does it have a link and a valid address? */ + if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) { + /* network mask matches? */ + if (ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) { + /* return netif on which to forward IP packet */ + return netif; + } + /* gateway matches on a non broadcast interface? (i.e. peer in a point to point interface) */ + if (((netif->flags & NETIF_FLAG_BROADCAST) == 0) && ip4_addr_cmp(dest, netif_ip4_gw(netif))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + +#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF + /* loopif is disabled, looopback traffic is passed through any netif */ + if (ip4_addr_isloopback(dest)) { + /* don't check for link on loopback traffic */ + if (netif_default != NULL && netif_is_up(netif_default)) { + return netif_default; + } + /* default netif is not up, just use any netif for loopback traffic */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (netif_is_up(netif)) { + return netif; + } + } + return NULL; + } +#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */ + +#ifdef LWIP_HOOK_IP4_ROUTE_SRC + netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, NULL); + if (netif != NULL) { + return netif; + } +#elif defined(LWIP_HOOK_IP4_ROUTE) + netif = LWIP_HOOK_IP4_ROUTE(dest); + if (netif != NULL) { + return netif; + } +#endif + + if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) || + ip4_addr_isany_val(*netif_ip4_addr(netif_default))) { + /* No matching netif found and default netif is not usable. + If this is not good enough for you, use LWIP_HOOK_IP4_ROUTE() */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + MIB2_STATS_INC(mib2.ipoutnoroutes); + return NULL; + } + + return netif_default; +} + +#if IP_FORWARD +/** + * Determine whether an IP address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + * @param p the packet to forward + * @return 1: can forward 0: discard + */ +static int +ip4_canforward(struct pbuf *p) +{ + u32_t addr = lwip_htonl(ip4_addr_get_u32(ip4_current_dest_addr())); + + if (p->flags & PBUF_FLAG_LLBCAST) { + /* don't route link-layer broadcasts */ + return 0; + } + if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) { + /* don't route link-layer multicasts unless the destination address is an IP + multicast address */ + return 0; + } + if (IP_EXPERIMENTAL(addr)) { + return 0; + } + if (IP_CLASSA(addr)) { + u32_t net = addr & IP_CLASSA_NET; + if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) { + /* don't route loopback packets */ + return 0; + } + } + return 1; +} + +/** + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IP header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + LWIP_UNUSED_ARG(inp); + + if (!ip4_canforward(p)) { + goto return_noroute; + } + + /* RFC3927 2.7: do not forward link-local addresses */ + if (ip4_addr_islinklocal(ip4_current_dest_addr())) { + LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()), + ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr()))); + goto return_noroute; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip4_route_src(ip4_current_dest_addr(), ip4_current_src_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n", + ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()), + ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr()))); + /* @todo: send ICMP_DUR_NET? */ + goto return_noroute; + } +#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not bouncing packets back on incoming interface.\n")); + goto return_noroute; + } +#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */ + + /* decrement TTL */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + /* send ICMP if TTL == 0 */ + if (IPH_TTL(iphdr) == 0) { + MIB2_STATS_INC(mib2.ipinhdrerrors); +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + return; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()), + ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr()))); + + IP_STATS_INC(ip.fw); + MIB2_STATS_INC(mib2.ipforwdatagrams); + IP_STATS_INC(ip.xmit); + + PERF_STOP("ip4_forward"); + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) { +#if IP_FRAG + ip4_frag(p, netif, ip4_current_dest_addr()); +#else /* IP_FRAG */ + /* @todo: send ICMP Destination Unreachable code 13 "Communication administratively prohibited"? */ +#endif /* IP_FRAG */ + } else { +#if LWIP_ICMP + /* send ICMP Destination Unreachable code 4: "Fragmentation Needed and DF Set" */ + icmp_dest_unreach(p, ICMP_DUR_FRAG); +#endif /* LWIP_ICMP */ + } + return; + } + /* transmit pbuf on chosen interface */ + netif->output(netif, p, ip4_current_dest_addr()); + return; +return_noroute: + MIB2_STATS_INC(mib2.ipoutnoroutes); +} +#endif /* IP_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IP packet (p->payload points to IP header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip4_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr; + struct netif *netif; + u16_t iphdr_hlen; + u16_t iphdr_len; +#if IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP + int check_ip_src = 1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */ + + IP_STATS_INC(ip.recv); + MIB2_STATS_INC(mib2.ipinreceives); + + /* identify the IP header */ + iphdr = (struct ip_hdr *)p->payload; + if (IPH_V(iphdr) != 4) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", (u16_t)IPH_V(iphdr))); + ip4_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + MIB2_STATS_INC(mib2.ipinhdrerrors); + return ERR_OK; + } + +#ifdef LWIP_HOOK_IP4_INPUT + if (LWIP_HOOK_IP4_INPUT(p, inp)) { + /* the packet has been eaten */ + return ERR_OK; + } +#endif + + /* obtain IP header length in number of 32-bit words */ + iphdr_hlen = IPH_HL(iphdr); + /* calculate IP header length in bytes */ + iphdr_hlen *= 4; + /* obtain ip length in bytes */ + iphdr_len = lwip_ntohs(IPH_LEN(iphdr)); + + /* Trim pbuf. This is especially required for packets < 60 bytes. */ + if (iphdr_len < p->tot_len) { + pbuf_realloc(p, iphdr_len); + } + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len) || (iphdr_hlen < IP_HLEN)) { + if (iphdr_hlen < IP_HLEN) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("ip4_input: short IP header (%"U16_F" bytes) received, IP packet dropped\n", iphdr_hlen)); + } + if (iphdr_hlen > p->len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_hlen, p->len)); + } + if (iphdr_len > p->tot_len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_len, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.lenerr); + IP_STATS_INC(ip.drop); + MIB2_STATS_INC(mib2.ipindiscards); + return ERR_OK; + } + + /* verify checksum */ +#if CHECKSUM_CHECK_IP + IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) { + if (inet_chksum(iphdr, iphdr_hlen) != 0) { + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); + ip4_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.chkerr); + IP_STATS_INC(ip.drop); + MIB2_STATS_INC(mib2.ipinhdrerrors); + return ERR_OK; + } + } +#endif + + /* copy IP addresses to aligned ip_addr_t */ + ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest); + ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src); + + /* match packet against an interface, i.e. is this packet for us? */ + if (ip4_addr_ismulticast(ip4_current_dest_addr())) { +#if LWIP_IGMP + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip4_current_dest_addr()))) { + /* IGMP snooping switches need 0.0.0.0 to be allowed as source address (RFC 4541) */ + ip4_addr_t allsystems; + IP4_ADDR(&allsystems, 224, 0, 0, 1); + if (ip4_addr_cmp(ip4_current_dest_addr(), &allsystems) && + ip4_addr_isany(ip4_current_src_addr())) { + check_ip_src = 0; + } + netif = inp; + } else { + netif = NULL; + } +#else /* LWIP_IGMP */ + if ((netif_is_up(inp)) && (!ip4_addr_isany_val(*netif_ip4_addr(inp)))) { + netif = inp; + } else { + netif = NULL; + } +#endif /* LWIP_IGMP */ + } else { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", + ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(netif_ip4_addr(netif)), + ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(netif_ip4_netmask(netif)), + ip4_addr_get_u32(netif_ip4_addr(netif)) & ip4_addr_get_u32(netif_ip4_netmask(netif)), + ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(netif_ip4_netmask(netif)))); + + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))) { + /* unicast to this interface address? */ + if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) || + /* or broadcast on this interface network address? */ + ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) +#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF + || (ip4_addr_get_u32(ip4_current_dest_addr()) == PP_HTONL(IPADDR_LOOPBACK)) +#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */ + ) { + LWIP_DEBUGF(IP_DEBUG, ("ip4_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#if LWIP_AUTOIP + /* connections to link-local addresses must persist after changing + the netif's address (RFC3927 ch. 1.9) */ + if (autoip_accept_packet(netif, ip4_current_dest_addr())) { + LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#endif /* LWIP_AUTOIP */ + } + if (first) { +#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF + /* Packets sent to the loopback address must not be accepted on an + * interface that does not have the loopback address assigned to it, + * unless a non-loopback interface is used for loopback traffic. */ + if (ip4_addr_isloopback(ip4_current_dest_addr())) { + netif = NULL; + break; + } +#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */ + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while (netif != NULL); + } + +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed + * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. + * According to RFC 1542 section 3.1.1, referred by RFC 2131). + * + * If you want to accept private broadcast communication while a netif is down, + * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.: + * + * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345)) + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: UDP packet to DHCP client port %"U16_F"\n", + lwip_ntohs(udphdr->dest))); + if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: DHCP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING + if (check_ip_src +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */ + && !ip4_addr_isany_val(*ip4_current_src_addr()) +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + ) +#endif /* LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING */ + { + if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) || + (ip4_addr_ismulticast(ip4_current_src_addr()))) { + /* packet source is not valid */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip4_input: packet source is not valid.\n")); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.drop); + MIB2_STATS_INC(mib2.ipinaddrerrors); + MIB2_STATS_INC(mib2.ipindiscards); + return ERR_OK; + } + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n")); +#if IP_FORWARD + /* non-broadcast packet? */ + if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) { + /* try to forward IP packet on (other) interfaces */ + ip4_forward(p, iphdr, inp); + } else +#endif /* IP_FORWARD */ + { + IP_STATS_INC(ip.drop); + MIB2_STATS_INC(mib2.ipinaddrerrors); + MIB2_STATS_INC(mib2.ipindiscards); + } + pbuf_free(p); + return ERR_OK; + } + /* packet consists of multiple fragments? */ + if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) { +#if IP_REASSEMBLY /* packet fragment reassembly code present? */ + LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip4_reass()\n", + lwip_ntohs(IPH_ID(iphdr)), p->tot_len, lwip_ntohs(IPH_LEN(iphdr)), (u16_t)!!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (u16_t)((lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8))); + /* reassemble the packet*/ + p = ip4_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + return ERR_OK; + } + iphdr = (struct ip_hdr *)p->payload; +#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + lwip_ntohs(IPH_OFFSET(iphdr)))); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + MIB2_STATS_INC(mib2.ipinunknownprotos); + return ERR_OK; +#endif /* IP_REASSEMBLY */ + } + +#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ + +#if LWIP_IGMP + /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ + if ((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { +#else + if (iphdr_hlen > IP_HLEN) { +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); + pbuf_free(p); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + MIB2_STATS_INC(mib2.ipinunknownprotos); + return ERR_OK; + } +#endif /* IP_OPTIONS_ALLOWED == 0 */ + + /* send to upper layers */ + LWIP_DEBUGF(IP_DEBUG, ("ip4_input: \n")); + ip4_debug_print(p); + LWIP_DEBUGF(IP_DEBUG, ("ip4_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + + ip_data.current_netif = netif; + ip_data.current_input_netif = inp; + ip_data.current_ip4_header = iphdr; + ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4; + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */ + + switch (IPH_PROTO(iphdr)) { +#if LWIP_UDP + case IP_PROTO_UDP: +#if LWIP_UDPLITE + case IP_PROTO_UDPLITE: +#endif /* LWIP_UDPLITE */ + MIB2_STATS_INC(mib2.ipindelivers); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP_PROTO_TCP: + MIB2_STATS_INC(mib2.ipindelivers); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP + case IP_PROTO_ICMP: + MIB2_STATS_INC(mib2.ipindelivers); + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ +#if LWIP_IGMP + case IP_PROTO_IGMP: + igmp_input(p, inp, ip4_current_dest_addr()); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) && + !ip4_addr_ismulticast(ip4_current_dest_addr())) { + pbuf_header_force(p, iphdr_hlen); /* Move to ip header, no check necessary. */ + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", (u16_t)IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + MIB2_STATS_INC(mib2.ipinunknownprotos); + } + } + + /* @todo: this is not really necessary... */ + ip_data.current_netif = NULL; + ip_data.current_input_netif = NULL; + ip_data.current_ip4_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip4_addr_set_any(ip4_current_src_addr()); + ip4_addr_set_any(ip4_current_dest_addr()); + + return ERR_OK; +} + +/** + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + * If the destination IP address is LWIP_IP_HDRINCL, p is assumed to already + * include an IP header and p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == LWIP_IP_HDRINCL, p already includes an + IP header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + * + * @note ip_id: RFC791 "some host may be able to simply use + * unique identifiers independent of destination" + */ +err_t +ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ +#if IP_OPTIONS_SEND + return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if() but with the possibility to include IP options: + * + * @ param ip_options pointer to the IP options, copied into the IP header + * @ param optlen length of ip_options + */ +err_t +ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen) +{ +#endif /* IP_OPTIONS_SEND */ + const ip4_addr_t *src_used = src; + if (dest != LWIP_IP_HDRINCL) { + if (ip4_addr_isany(src)) { + src_used = netif_ip4_addr(netif); + } + } + +#if IP_OPTIONS_SEND + return ip4_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif, + ip_options, optlen); +#else /* IP_OPTIONS_SEND */ + return ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif); +#endif /* IP_OPTIONS_SEND */ +} + +/** + * Same as ip_output_if() but 'src' address is not replaced by netif address + * when it is 'any'. + */ +err_t +ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ +#if IP_OPTIONS_SEND + return ip4_output_if_opt_src(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if_opt() but 'src' address is not replaced by netif address + * when it is 'any'. + */ +err_t +ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen) +{ +#endif /* IP_OPTIONS_SEND */ + struct ip_hdr *iphdr; + ip4_addr_t dest_addr; +#if CHECKSUM_GEN_IP_INLINE + u32_t chk_sum = 0; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + MIB2_STATS_INC(mib2.ipoutrequests); + + /* Should the IP header be generated or is it already included in p? */ + if (dest != LWIP_IP_HDRINCL) { + u16_t ip_hlen = IP_HLEN; +#if IP_OPTIONS_SEND + u16_t optlen_aligned = 0; + if (optlen != 0) { +#if CHECKSUM_GEN_IP_INLINE + int i; +#endif /* CHECKSUM_GEN_IP_INLINE */ + /* round up to a multiple of 4 */ + optlen_aligned = ((optlen + 3) & ~3); + ip_hlen += optlen_aligned; + /* First write in the IP options */ + if (pbuf_header(p, optlen_aligned)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n")); + IP_STATS_INC(ip.err); + MIB2_STATS_INC(mib2.ipoutdiscards); + return ERR_BUF; + } + MEMCPY(p->payload, ip_options, optlen); + if (optlen < optlen_aligned) { + /* zero the remaining bytes */ + memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); + } +#if CHECKSUM_GEN_IP_INLINE + for (i = 0; i < optlen_aligned/2; i++) { + chk_sum += ((u16_t*)p->payload)[i]; + } +#endif /* CHECKSUM_GEN_IP_INLINE */ + } +#endif /* IP_OPTIONS_SEND */ + /* generate IP header */ + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: not enough room for IP header in pbuf\n")); + + IP_STATS_INC(ip.err); + MIB2_STATS_INC(mib2.ipoutdiscards); + return ERR_BUF; + } + + iphdr = (struct ip_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", + (p->len >= sizeof(struct ip_hdr))); + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += PP_NTOHS(proto | (ttl << 8)); +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* dest cannot be NULL here */ + ip4_addr_copy(iphdr->dest, *dest); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + IPH_VHL_SET(iphdr, 4, ip_hlen / 4); + IPH_TOS_SET(iphdr, tos); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8)); +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_LEN_SET(iphdr, lwip_htons(p->tot_len)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_len; +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, lwip_htons(ip_id)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_id; +#endif /* CHECKSUM_GEN_IP_INLINE */ + ++ip_id; + + if (src == NULL) { + ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY4); + } else { + /* src cannot be NULL here */ + ip4_addr_copy(iphdr->src, *src); + } + +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16; + chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF); + chk_sum = (chk_sum >> 16) + chk_sum; + chk_sum = ~chk_sum; + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) { + iphdr->_chksum = (u16_t)chk_sum; /* network order */ + } +#if LWIP_CHECKSUM_CTRL_PER_NETIF + else { + IPH_CHKSUM_SET(iphdr, 0); + } +#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/ +#else /* CHECKSUM_GEN_IP_INLINE */ + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) { + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); + } +#endif /* CHECKSUM_GEN_IP */ +#endif /* CHECKSUM_GEN_IP_INLINE */ + } else { + /* IP header already included in p */ + iphdr = (struct ip_hdr *)p->payload; + ip4_addr_copy(dest_addr, iphdr->dest); + dest = &dest_addr; + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num)); + ip4_debug_print(p); + +#if ENABLE_LOOPBACK + if (ip4_addr_cmp(dest, netif_ip4_addr(netif)) +#if !LWIP_HAVE_LOOPIF + || ip4_addr_isloopback(dest) +#endif /* !LWIP_HAVE_LOOPIF */ + ) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); + return netif_loop_output(netif, p); + } +#if LWIP_MULTICAST_TX_OPTIONS + if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { + netif_loop_output(netif, p); + } +#endif /* LWIP_MULTICAST_TX_OPTIONS */ +#endif /* ENABLE_LOOPBACK */ +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + return ip4_frag(p, netif, dest); + } +#endif /* IP_FRAG */ + + LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n")); + return netif->output(netif, p, dest); +} + +/** + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == LWIP_IP_HDRINCL, p already includes an + IP header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + if ((netif = ip4_route_src(dest, src)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip4_output_if(p, src, dest, ttl, tos, proto, netif); +} + +#if LWIP_NETIF_HWADDRHINT +/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == LWIP_IP_HDRINCL, p already includes an + IP header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + if ((netif = ip4_route_src(dest, src)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip4_output_if(p, src, dest, ttl, tos, proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if IP_DEBUG +/* Print an IP header by using LWIP_DEBUGF + * @param p an IP packet, p->payload pointing to the IP header + */ +void +ip4_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", + (u16_t)IPH_V(iphdr), + (u16_t)IPH_HL(iphdr), + (u16_t)IPH_TOS(iphdr), + lwip_ntohs(IPH_LEN(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", + lwip_ntohs(IPH_ID(iphdr)), + (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 15 & 1), + (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 14 & 1), + (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 13 & 1), + (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", + (u16_t)IPH_TTL(iphdr), + (u16_t)IPH_PROTO(iphdr), + lwip_ntohs(IPH_CHKSUM(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", + ip4_addr1_16(&iphdr->src), + ip4_addr2_16(&iphdr->src), + ip4_addr3_16(&iphdr->src), + ip4_addr4_16(&iphdr->src))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", + ip4_addr1_16(&iphdr->dest), + ip4_addr2_16(&iphdr->dest), + ip4_addr3_16(&iphdr->dest), + ip4_addr4_16(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ + +#endif /* LWIP_IPV4 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4_addr.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4_addr.c new file mode 100644 index 0000000..40e9ffa --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4_addr.c @@ -0,0 +1,331 @@ +/** + * @file + * This is the IPv4 address tools implementation. + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 + +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +/* used by IP4_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY); +const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST); + +/** + * Determine if an address is a broadcast address on a network interface + * + * @param addr address to be checked + * @param netif the network interface against which the address is checked + * @return returns non-zero if the address is a broadcast address + */ +u8_t +ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif) +{ + ip4_addr_t ipaddr; + ip4_addr_set_u32(&ipaddr, addr); + + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((~addr == IPADDR_ANY) || + (addr == IPADDR_ANY)) { + return 1; + /* no broadcast support on this network interface? */ + } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) { + /* the given address cannot be a broadcast address + * nor can we check against any broadcast addresses */ + return 0; + /* address matches network interface address exactly? => no broadcast */ + } else if (addr == ip4_addr_get_u32(netif_ip4_addr(netif))) { + return 0; + /* on the same (sub) network... */ + } else if (ip4_addr_netcmp(&ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr & ~ip4_addr_get_u32(netif_ip4_netmask(netif))) == + (IPADDR_BROADCAST & ~ip4_addr_get_u32(netif_ip4_netmask(netif))))) { + /* => network broadcast address */ + return 1; + } else { + return 0; + } +} + +/** Checks if a netmask is valid (starting with ones, then only zeros) + * + * @param netmask the IPv4 netmask to check (in network byte order!) + * @return 1 if the netmask is valid, 0 if it is not + */ +u8_t +ip4_addr_netmask_valid(u32_t netmask) +{ + u32_t mask; + u32_t nm_hostorder = lwip_htonl(netmask); + + /* first, check for the first zero */ + for (mask = 1UL << 31 ; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) == 0) { + break; + } + } + /* then check that there is no one */ + for (; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) != 0) { + /* there is a one after the first zero -> invalid */ + return 0; + } + } + /* no one after the first zero -> valid */ + return 1; +} + +/* Here for now until needed in other places in lwIP */ +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + +/** + * Ascii internet address interpretation routine. + * The value returned is in network order. + * + * @param cp IP address in ascii representation (e.g. "127.0.0.1") + * @return ip address in network order + */ +u32_t +ipaddr_addr(const char *cp) +{ + ip4_addr_t val; + + if (ip4addr_aton(cp, &val)) { + return ip4_addr_get_u32(&val); + } + return (IPADDR_NONE); +} + +/** + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + * + * @param cp IP address in ascii representation (e.g. "127.0.0.1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ip4addr_aton(const char *cp, ip4_addr_t *addr) +{ + u32_t val; + u8_t base; + char c; + u32_t parts[4]; + u32_t *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, 1-9=decimal. + */ + if (!isdigit(c)) { + return 0; + } + val = 0; + base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } else { + base = 8; + } + } + for (;;) { + if (isdigit(c)) { + val = (val * base) + (u32_t)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + val = (val << 4) | (u32_t)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else { + break; + } + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) { + return 0; + } + *pp++ = val; + c = *++cp; + } else { + break; + } + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !isspace(c)) { + return 0; + } + /* + * Concoct the address according to + * the number of parts specified. + */ + switch (pp - parts + 1) { + + case 0: + return 0; /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffffUL) { + return 0; + } + if (parts[0] > 0xff) { + return 0; + } + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) { + return 0; + } + if ((parts[0] > 0xff) || (parts[1] > 0xff)) { + return 0; + } + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) { + return 0; + } + if ((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) { + return 0; + } + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + default: + LWIP_ASSERT("unhandled", 0); + break; + } + if (addr) { + ip4_addr_set_u32(addr, lwip_htonl(val)); + } + return 1; +} + +/** + * Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * representation of addr + */ +char* +ip4addr_ntoa(const ip4_addr_t *addr) +{ + static char str[IP4ADDR_STRLEN_MAX]; + return ip4addr_ntoa_r(addr, str, IP4ADDR_STRLEN_MAX); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char* +ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen) +{ + u32_t s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + int len = 0; + + s_addr = ip4_addr_get_u32(addr); + + rp = buf; + ap = (u8_t *)&s_addr; + for (n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = (char)('0' + rem); + } while (*ap); + while (i--) { + if (len++ >= buflen) { + return NULL; + } + *rp++ = inv[i]; + } + if (len++ >= buflen) { + return NULL; + } + *rp++ = '.'; + ap++; + } + *--rp = 0; + return buf; +} + +#endif /* LWIP_IPV4 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4_frag.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4_frag.c new file mode 100644 index 0000000..fdb2088 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/ip4_frag.c @@ -0,0 +1,864 @@ +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * Simon Goldschmidt + * original reassembly code by Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 + +#include "lwip/ip4_frag.h" +#include "lwip/def.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + +#include + +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! + * + * @todo: work with IP header options + */ + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +#define IP_REASS_VALIDATE_TELEGRAM_FINISHED 1 +#define IP_REASS_VALIDATE_PBUF_QUEUED 0 +#define IP_REASS_VALIDATE_PBUF_DROPPED -1 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IP header, since it replaces + * the IP header in memory in incoming fragments (after copying it) to keep + * track of the various fragments. (-> If the IP header doesn't need packing, + * this struct doesn't need packing, too.) + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). + */ +void +ip_reass_tmr(void) +{ + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + u16_t pbufs_freed = 0; + u16_t clen; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + MIB2_STATS_INC(mib2.ipreasmfails); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev, *oldest_prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + oldest_prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + oldest_prev = prev; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + oldest_prev = prev; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; +#if ! IP_REASS_FREE_OLDEST + LWIP_UNUSED_ARG(clen); +#endif + + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } + } + memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reassdata struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param ipr points to the reassembly state + * @param new_p points to the pbuf for the current fragment + * @param is_last is 1 if this pbuf has MF==0 (ipr->flags not updated yet) + * @return see IP_REASS_VALIDATE_* defines + */ +static int +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p, int is_last) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset, len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find one with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + if (iprh->end > iprh_tmp->start) { + /* fragment overlaps with following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if (iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if (iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no holes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if (is_last || ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0)) { + /* and had no holes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if ((ipr->p == NULL) || (((struct ip_reass_helper*)ipr->p->payload)->start != 0)) { + valid = 0; + } else { + /* and check that there are no holes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid ? IP_REASS_VALIDATE_TELEGRAM_FINISHED : IP_REASS_VALIDATE_PBUF_QUEUED; + } + /* If we come here, not all fragments were received, yet! */ + return IP_REASS_VALIDATE_PBUF_QUEUED; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return IP_REASS_VALIDATE_PBUF_DROPPED; +#endif /* IP_REASS_CHECK_OVERLAP */ +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip4_reass(struct pbuf *p) +{ + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; + u16_t offset, len, clen; + int valid; + int is_last; + + IPFRAG_STATS_INC(ip_frag.recv); + MIB2_STATS_INC(mib2.ipreasmreqds); + + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; + } + + offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; + } + } + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n", + lwip_ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if (ipr == NULL) { + goto nullreturn; + } + } else { + if (((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((lwip_ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + is_last = (IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0; + if (is_last) { + u16_t datagram_len = (u16_t)(offset + len); + if ((datagram_len < offset) || (datagram_len > (0xFFFF - IP_HLEN))) { + /* u16_t overflow, cannot handle this */ + goto nullreturn; + } + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + valid = ip_reass_chain_frag_into_datagram_and_validate(ipr, p, is_last); + if (valid == IP_REASS_VALIDATE_PBUF_DROPPED) { + goto nullreturn; + } + /* if we come here, the pbuf has been enqueued */ + + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time + (overflow checked by testing against IP_REASS_MAX_PBUFS) */ + ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount + clen); + if (is_last) { + u16_t datagram_len = (u16_t)(offset + len); + ipr->datagram_len = datagram_len; + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip4_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + + if (valid == IP_REASS_VALIDATE_TELEGRAM_FINISHED) { + struct ip_reassdata *ipr_prev; + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, lwip_htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set/calculate the correct checksum? */ +#if CHECKSUM_GEN_IP + IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) { + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + } +#endif /* CHECKSUM_GEN_IP */ + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while (r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + + /* find the previous entry in the linked list */ + if (ipr == reassdatagrams) { + ipr_prev = NULL; + } else { + for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { + if (ipr_prev->next == ipr) { + break; + } + } + } + + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + MIB2_STATS_INC(mib2.ipreasmoks); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + +nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: nullreturn\n")); + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if !LWIP_NETIF_TX_SINGLE_PBUF +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ipfrag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip_frag_free_pbuf_custom_ref(pcr); +} +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by pointing PBUF_REFs into p. + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) +{ + struct pbuf *rambuf; +#if !LWIP_NETIF_TX_SINGLE_PBUF + struct pbuf *newpbuf; + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + struct ip_hdr *original_iphdr; + struct ip_hdr *iphdr; + const u16_t nfb = (netif->mtu - IP_HLEN) / 8; + u16_t left, fragsize; + u16_t ofo; + int last; + u16_t poff = IP_HLEN; + u16_t tmp; + + original_iphdr = (struct ip_hdr *)p->payload; + iphdr = original_iphdr; + LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL(iphdr) * 4 == IP_HLEN, return ERR_VAL); + + /* Save original offset */ + tmp = lwip_ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + LWIP_ERROR("ip_frag(): MF already set", (tmp & IP_MF) == 0, return ERR_VAL); + + left = p->tot_len - IP_HLEN; + + while (left) { + /* Fill this fragment */ + fragsize = LWIP_MIN(left, nfb * 8); + +#if LWIP_NETIF_TX_SINGLE_PBUF + rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM); + if (rambuf == NULL) { + goto memerr; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); + poff += pbuf_copy_partial(p, rambuf->payload, fragsize, poff); + /* make room for the IP header */ + if (pbuf_header(rambuf, IP_HLEN)) { + pbuf_free(rambuf); + goto memerr; + } + /* fill in the IP header */ + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr*)rambuf->payload; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + goto memerr; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr *)rambuf->payload; + + left_to_copy = fragsize; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + u16_t plen = p->len - poff; + newpbuflen = LWIP_MIN(left_to_copy, plen); + /* Is this pbuf already empty? */ + if (!newpbuflen) { + poff = 0; + p = p->next; + continue; + } + pcr = ip_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + goto memerr; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, + (u8_t*)p->payload + poff, newpbuflen); + if (newpbuf == NULL) { + ip_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + goto memerr; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + poff = 0; + p = p->next; + } + } + poff += newpbuflen; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + /* Correct header */ + last = (left <= netif->mtu - IP_HLEN); + + /* Set new offset and MF flag */ + tmp = (IP_OFFMASK & (ofo)); + if (!last) { + tmp = tmp | IP_MF; + } + IPH_OFFSET_SET(iphdr, lwip_htons(tmp)); + IPH_LEN_SET(iphdr, lwip_htons(fragsize + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) { + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + } +#endif /* CHECKSUM_GEN_IP */ + + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); + left -= fragsize; + ofo += nfb; + } + MIB2_STATS_INC(mib2.ipfragoks); + return ERR_OK; +memerr: + MIB2_STATS_INC(mib2.ipfragfails); + return ERR_MEM; +} +#endif /* IP_FRAG */ + +#endif /* LWIP_IPV4 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv4/ip_frag.c b/sdk/Src/Network/lwip2.0.3/core/ipv4/ip_frag.c new file mode 100644 index 0000000..5e14f72 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv4/ip_frag.c @@ -0,0 +1,897 @@ +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * Simon Goldschmidt + * original reassembly code by Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV4 + +#include "lwip/ip_frag.h" +#include "lwip/def.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + +#include + +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! + * + * @todo: work with IP header options + */ + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IP header, since it replaces + * the IP header in memory in incoming fragments (after copying it) to keep + * track of the various fragments. (-> If the IP header doesn't need packing, + * this struct doesn't need packing, too.) + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). + */ +void +ip_reass_tmr(void) +{ + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + MIB2_STATS_INC(mib2.ipreasmfails); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev, *oldest_prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + oldest_prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + oldest_prev = prev; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + oldest_prev = prev; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; +#if ! IP_REASS_FREE_OLDEST + LWIP_UNUSED_ARG(clen); +#endif + + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } + } + memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reassdata struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param root_p points to the 'root' pbuf for the current datagram being assembled. + * @param new_p points to the pbuf for the current fragment + * @return 0 if invalid, >0 otherwise + */ +static int +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset,len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find one with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + } else { + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if (iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if (iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no holes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { + /* and had no holes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if ((ipr->p == NULL) || (((struct ip_reass_helper*)ipr->p->payload)->start != 0)) { + valid = 0; + } else { + /* and check that there are no holes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + LWIP_ASSERT("validate_datagram:datagram end!=datagram len", + iprh->end == ipr->datagram_len); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid; + } + /* If we come here, not all fragments were received, yet! */ + return 0; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return 0; +#endif /* IP_REASS_CHECK_OVERLAP */ +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip4_reass(struct pbuf *p) +{ + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; + u16_t offset, len; + u8_t clen; + + IPFRAG_STATS_INC(ip_frag.recv); + MIB2_STATS_INC(mib2.ipreasmreqds); + + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; + } + + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; + } + } + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if (ipr == NULL) { + goto nullreturn; + } + } else { + if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip_reass_pbufcount += clen; + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + ipr->datagram_len = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip4_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { + struct ip_reassdata *ipr_prev; + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set/calculate the correct checksum? */ +#if CHECKSUM_GEN_IP + IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) { + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + } +#endif /* CHECKSUM_GEN_IP */ + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while (r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + + /* find the previous entry in the linked list */ + if (ipr == reassdatagrams) { + ipr_prev = NULL; + } else { + for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { + if (ipr_prev->next == ipr) { + break; + } + } + } + + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + MIB2_STATS_INC(mib2.ipreasmoks); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + +nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: nullreturn\n")); + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if IP_FRAG_USES_STATIC_BUF +static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; +#else /* IP_FRAG_USES_STATIC_BUF */ + +#if !LWIP_NETIF_TX_SINGLE_PBUF +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ipfrag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip_frag_free_pbuf_custom_ref(pcr); +} +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by using a fixed size static memory buffer (PBUF_REF) or + * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) +{ + struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF + struct pbuf *header; +#else +#if !LWIP_NETIF_TX_SINGLE_PBUF + struct pbuf *newpbuf; +#endif + struct ip_hdr *original_iphdr; +#endif + struct ip_hdr *iphdr; + u16_t nfb; + u16_t left, cop; + u16_t mtu = netif->mtu; + u16_t ofo, omf; + u16_t last; + u16_t poff = IP_HLEN; + u16_t tmp; +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + + /* Get a RAM based MTU sized pbuf */ +#if IP_FRAG_USES_STATIC_BUF + /* When using a static buffer, we use a PBUF_REF, which we will + * use to reference the packet (without link header). + * Layer and length is irrelevant. + */ + rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); + if (rambuf == NULL) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); + goto memerr; + } + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = LWIP_MEM_ALIGN((void *)buf); + + /* Copy the IP header in it */ + iphdr = (struct ip_hdr *)rambuf->payload; + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = (struct ip_hdr *)p->payload; + iphdr = original_iphdr; +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Save original offset */ + tmp = ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + omf = tmp & IP_MF; + + left = p->tot_len - IP_HLEN; + + nfb = (mtu - IP_HLEN) / 8; + + while (left) { + last = (left <= mtu - IP_HLEN); + + /* Set new offset and MF flag */ + tmp = omf | (IP_OFFMASK & (ofo)); + if (!last) { + tmp = tmp | IP_MF; + } + + /* Fill this fragment */ + cop = last ? left : nfb * 8; + +#if IP_FRAG_USES_STATIC_BUF + poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); +#else /* IP_FRAG_USES_STATIC_BUF */ +#if LWIP_NETIF_TX_SINGLE_PBUF + rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM); + if (rambuf == NULL) { + goto memerr; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); + poff += pbuf_copy_partial(p, rambuf->payload, cop, poff); + /* make room for the IP header */ + if (pbuf_header(rambuf, IP_HLEN)) { + pbuf_free(rambuf); + goto memerr; + } + /* fill in the IP header */ + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr*)rambuf->payload; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + goto memerr; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr *)rambuf->payload; + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + goto memerr; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + goto memerr; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Correct header */ + IPH_OFFSET_SET(iphdr, htons(tmp)); + IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) { + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + } +#endif /* CHECKSUM_GEN_IP */ + +#if IP_FRAG_USES_STATIC_BUF + if (last) { + pbuf_realloc(rambuf, left + IP_HLEN); + } + + /* This part is ugly: we alloc a RAM based pbuf for + * the link level header for each chunk and then + * free it. A PBUF_ROM style pbuf for which pbuf_header + * worked would make things simpler. + */ + header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (header != NULL) { + pbuf_chain(header, rambuf); + netif->output(netif, header, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + MIB2_STATS_INC(mib2.ipfragcreates); + pbuf_free(header); + } else { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); + pbuf_free(rambuf); + goto memerr; + } +#else /* IP_FRAG_USES_STATIC_BUF */ + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + left -= cop; + ofo += nfb; + } +#if IP_FRAG_USES_STATIC_BUF + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + MIB2_STATS_INC(mib2.ipfragoks); + return ERR_OK; +memerr: + MIB2_STATS_INC(mib2.ipfragfails); + return ERR_MEM; +} +#endif /* IP_FRAG */ + +#endif /* LWIP_IPV4 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/dhcp6.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/dhcp6.c new file mode 100644 index 0000000..015ced1 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/dhcp6.c @@ -0,0 +1,50 @@ +/** + * @file + * + * DHCPv6. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + + +#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/ethip6.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/ethip6.c new file mode 100644 index 0000000..1d3ff9e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/ethip6.c @@ -0,0 +1,118 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET + +#include "lwip/ethip6.h" +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" +#include "lwip/prot/ethernet.h" +#include "netif/ethernet.h" + +#include + +/** + * Resolve and fill-in Ethernet address header for outgoing IPv6 packet. + * + * For IPv6 multicast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, ask the ND6 module what to do. It will either let us + * send the the packet right away, or queue the packet for later itself, unless + * an error occurs. + * + * @todo anycast addresses + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ip6addr The IP address of the packet destination. + * + * @return + * - ERR_OK or the return value of @ref nd6_get_next_hop_addr_or_queue. + */ +err_t +ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) +{ + struct eth_addr dest; + const u8_t *hwaddr; + err_t result; + + /* multicast destination IP address? */ + if (ip6_addr_ismulticast(ip6addr)) { + /* Hash IP multicast address to MAC address.*/ + dest.addr[0] = 0x33; + dest.addr[1] = 0x33; + dest.addr[2] = ((const u8_t *)(&(ip6addr->addr[3])))[0]; + dest.addr[3] = ((const u8_t *)(&(ip6addr->addr[3])))[1]; + dest.addr[4] = ((const u8_t *)(&(ip6addr->addr[3])))[2]; + dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3]; + + /* Send out. */ + return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6); + } + + /* We have a unicast destination IP address */ + /* @todo anycast? */ + + /* Ask ND6 what to do with the packet. */ + result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr); + if (result != ERR_OK) { + return result; + } + + /* If no hardware address is returned, nd6 has queued the packet for later. */ + if (hwaddr == NULL) { + return ERR_OK; + } + + /* Send out the packet using the returned hardware address. */ + SMEMCPY(dest.addr, hwaddr, 6); + return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6); +} + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/icmp6.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/icmp6.c new file mode 100644 index 0000000..c519dbb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/icmp6.c @@ -0,0 +1,350 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp6.h" +#include "lwip/prot/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/ip.h" +#include "lwip/stats.h" + +#include + +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif +#if LWIP_ICMP6_DATASIZE == 0 +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/* Forward declarations */ +static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); + + +/** + * Process an input ICMPv6 message. Called by ip6_input. + * + * Will generate a reply for echo requests. Other messages are forwarded + * to nd6_input, or mld6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +icmp6_input(struct pbuf *p, struct netif *inp) +{ + struct icmp6_hdr *icmp6hdr; + struct pbuf *r; + const ip6_addr_t *reply_src; + + ICMP6_STATS_INC(icmp6.recv); + + /* Check that ICMPv6 header fits in payload */ + if (p->len < sizeof(struct icmp6_hdr)) { + /* drop short packets */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.lenerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + +#if CHECKSUM_CHECK_ICMP6 + IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) { + if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), + ip6_current_dest_addr()) != 0) { + /* Checksum failed */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.chkerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } + } +#endif /* CHECKSUM_CHECK_ICMP6 */ + + switch (icmp6hdr->type) { + case ICMP6_TYPE_NA: /* Neighbor advertisement */ + case ICMP6_TYPE_NS: /* Neighbor solicitation */ + case ICMP6_TYPE_RA: /* Router advertisement */ + case ICMP6_TYPE_RD: /* Redirect */ + case ICMP6_TYPE_PTB: /* Packet too big */ + nd6_input(p, inp); + return; +// break; + case ICMP6_TYPE_RS: +#if LWIP_IPV6_FORWARD + /* @todo implement router functionality */ +#endif + break; +#if LWIP_IPV6_MLD + case ICMP6_TYPE_MLQ: + case ICMP6_TYPE_MLR: + case ICMP6_TYPE_MLD: + mld6_input(p, inp); + return; +// break; +#endif + case ICMP6_TYPE_EREQ: +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_MULTICAST_PING */ + + /* Allocate reply. */ + r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); + if (r == NULL) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + + /* Copy echo request. */ + if (pbuf_copy(r, p) != ERR_OK) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.err); + return; + } + + /* Determine reply source IPv6 address. */ +#if LWIP_MULTICAST_PING + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr())); + if (reply_src == NULL) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else +#endif /* LWIP_MULTICAST_PING */ + { + reply_src = ip6_current_dest_addr(); + } + + /* Set fields in reply. */ + ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; +#if CHECKSUM_GEN_ICMP6 + IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) { + ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, + IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); + } +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Send reply. */ + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(r, reply_src, ip6_current_src_addr(), + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); + pbuf_free(r); + + break; + default: + ICMP6_STATS_INC(icmp6.proterr); + ICMP6_STATS_INC(icmp6.drop); + break; + } + + pbuf_free(p); +} + + +/** + * Send an icmpv6 'destination unreachable' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the unreachable type + */ +void +icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); +} + +/** + * Send an icmpv6 'packet too big' packet. + * + * @param p the input packet for which the 'packet too big' should be sent, + * p->payload pointing to the IPv6 header + * @param mtu the maximum mtu that we can accept + */ +void +icmp6_packet_too_big(struct pbuf *p, u32_t mtu) +{ + icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); +} + +/** + * Send an icmpv6 'time exceeded' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the time exceeded type + */ +void +icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); +} + +/** + * Send an icmpv6 'parameter problem' packet. + * + * @param p the input packet for which the 'param problem' should be sent, + * p->payload pointing to the IP header + * @param c ICMPv6 code for the param problem type + * @param pointer the pointer to the byte where the parameter is found + */ +void +icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) +{ + icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); +} + +/** + * Send an ICMPv6 packet in response to an incoming packet. + * + * @param p the input packet for which the response should be sent, + * p->payload pointing to the IPv6 header + * @param code Code of the ICMPv6 header + * @param data Additional 32-bit parameter in the ICMPv6 header + * @param type Type of the ICMPv6 header + */ +static void +icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) +{ + struct pbuf *q; + struct icmp6_hdr *icmp6hdr; + const ip6_addr_t *reply_src; + ip6_addr_t *reply_dest; + ip6_addr_t reply_src_local, reply_dest_local; + struct ip6_hdr *ip6hdr; + struct netif *netif; + + /* ICMPv6 header + IPv6 header + data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp 6message", + (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); + + icmp6hdr = (struct icmp6_hdr *)q->payload; + icmp6hdr->type = type; + icmp6hdr->code = code; + icmp6hdr->data = data; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, + IP6_HLEN + LWIP_ICMP6_DATASIZE); + + /* Get the destination address and netif for this ICMP message. */ + if ((ip_current_netif() == NULL) || + ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) { + /* Special case, as ip6_current_xxx is either NULL, or points + * to a different packet than the one that expired. + * We must use the addresses that are stored in the expired packet. */ + ip6hdr = (struct ip6_hdr *)p->payload; + /* copy from packed address to aligned address */ + ip6_addr_copy(reply_dest_local, ip6hdr->src); + ip6_addr_copy(reply_src_local, ip6hdr->dest); + reply_dest = &reply_dest_local; + reply_src = &reply_src_local; + netif = ip6_route(reply_src, reply_dest); + if (netif == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else { + netif = ip_current_netif(); + reply_dest = ip6_current_src_addr(); + + /* Select an address to use as source. */ + reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest)); + if (reply_src == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + + /* calculate checksum */ + icmp6hdr->chksum = 0; +#if CHECKSUM_GEN_ICMP6 + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { + icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, + reply_src, reply_dest); + } +#endif /* CHECKSUM_GEN_ICMP6 */ + + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(q); +} + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/inet6.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/inet6.c new file mode 100644 index 0000000..7cb16aa --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/inet6.c @@ -0,0 +1,53 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/inet.h" + +/** This variable is initialized by the system to contain the wildcard IPv6 address. + */ +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; + +#endif /* LWIP_IPV6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6.c new file mode 100644 index 0000000..e354087 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6.c @@ -0,0 +1,1122 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6_frag.h" +#include "lwip/icmp6.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/dhcp6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/debug.h" +#include "lwip/stats.h" + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +/** + * Finds the appropriate network interface for a given IPv6 address. It tries to select + * a netif following a sequence of heuristics: + * 1) if there is only 1 netif, return it + * 2) if the destination is a link-local address, try to match the src address to a netif. + * this is a tricky case because with multiple netifs, link-local addresses only have + * meaning within a particular subnet/link. + * 3) tries to match the destination subnet to a configured address + * 4) tries to find a router + * 5) tries to match the source address to the netif + * 6) returns the default netif, if configured + * + * @param src the source IPv6 address, if known + * @param dest the destination IPv6 address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) +{ + struct netif *netif; + s8_t i; + + /* If single netif configuration, fast return. */ + if ((netif_list != NULL) && (netif_list->next == NULL)) { + if (!netif_is_up(netif_list) || !netif_is_link_up(netif_list)) { + return NULL; + } + return netif_list; + } + + /* Special processing for link-local addresses. */ + if (ip6_addr_islinklocal(dest)) { + if (ip6_addr_isany(src)) { + /* Use default netif, if Up. */ + if (netif_default == NULL || !netif_is_up(netif_default) || + !netif_is_link_up(netif_default)) { + return NULL; + } + return netif_default; + } + + /* Try to find the netif for the source address, checking that link is up. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (!netif_is_up(netif) || !netif_is_link_up(netif)) { + continue; + } + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* netif not found, use default netif, if up */ + if (netif_default == NULL || !netif_is_up(netif_default) || + !netif_is_link_up(netif_default)) { + return NULL; + } + return netif_default; + } + + /* we come here for non-link-local addresses */ +#ifdef LWIP_HOOK_IP6_ROUTE + netif = LWIP_HOOK_IP6_ROUTE(src, dest); + if (netif != NULL) { + return netif; + } +#endif + + /* See if the destination subnet matches a configured address. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (!netif_is_up(netif) || !netif_is_link_up(netif)) { + continue; + } + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* Get the netif for a suitable router. */ + netif = nd6_find_route(dest); + if ((netif != NULL) && netif_is_up(netif) && netif_is_link_up(netif)) { + return netif; + } + + /* try with the netif that matches the source address. */ + if (!ip6_addr_isany(src)) { + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (!netif_is_up(netif) || !netif_is_link_up(netif)) { + continue; + } + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + } + +#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF + /* loopif is disabled, loopback traffic is passed through any netif */ + if (ip6_addr_isloopback(dest)) { + /* don't check for link on loopback traffic */ + if (netif_default != NULL && netif_is_up(netif_default)) { + return netif_default; + } + /* default netif is not up, just use any netif for loopback traffic */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (netif_is_up(netif)) { + return netif; + } + } + return NULL; + } +#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */ + + /* no matching netif found, use default netif, if up */ + if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default)) { + return NULL; + } + return netif_default; +} + +/** + * @ingroup ip6 + * Select the best IPv6 source address for a given destination + * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior + * is assumed. + * + * @param netif the netif on which to send a packet + * @param dest the destination we are trying to reach + * @return the most suitable source address to use, or NULL if no suitable + * source address is found + */ +const ip_addr_t * +ip6_select_source_address(struct netif *netif, const ip6_addr_t *dest) +{ + const ip_addr_t *src = NULL; + u8_t i; + + /* If dest is link-local, choose a link-local source. */ + if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { + return netif_ip_addr6(netif, i); + } + } + } + + /* Choose a site-local with matching prefix. */ + if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_issitelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip_addr6(netif, i); + } + } + } + + /* Choose a unique-local with matching prefix. */ + if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip_addr6(netif, i); + } + } + } + + /* Choose a global with best matching prefix. */ + if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isglobal(netif_ip6_addr(netif, i))) { + if (src == NULL) { + src = netif_ip_addr6(netif, i); + } + else { + /* Replace src only if we find a prefix match. */ + /* @todo find longest matching prefix. */ + if ((!(ip6_addr_netcmp(ip_2_ip6(src), dest))) && + ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) { + src = netif_ip_addr6(netif, i); + } + } + } + } + if (src != NULL) { + return src; + } + } + + /* Last resort: see if arbitrary prefix matches. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip_addr6(netif, i); + } + } + + return NULL; +} + +#if LWIP_IPV6_FORWARD +/** + * Forwards an IPv6 packet. It finds an appropriate route for the + * packet, decrements the HL value of the packet, and outputs + * the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IPv6 header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + /* do not forward link-local or loopback addresses */ + if (ip6_addr_islinklocal(ip6_current_dest_addr()) || + ip6_addr_isloopback(ip6_current_dest_addr())) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip6_route(IP6_ADDR_ANY6, ip6_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* decrement HL */ + IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); + /* send ICMP6 if HL == 0 */ + if (IP6H_HOPLIM(iphdr) == 0) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_time_exceeded(p, ICMP6_TE_HL); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + if (netif->mtu && (p->tot_len > netif->mtu)) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_packet_too_big(p, netif->mtu); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); + + /* transmit pbuf on chosen interface */ + netif->output_ip6(netif, p, ip6_current_dest_addr()); + IP6_STATS_INC(ip6.fw); + IP6_STATS_INC(ip6.xmit); + return; +} +#endif /* LWIP_IPV6_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IPv6 packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip6_forward). + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IPv6 packet (p->payload points to IPv6 header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip6_input(struct pbuf *p, struct netif *inp) +{ + struct ip6_hdr *ip6hdr; + struct netif *netif; + u8_t nexth; + u16_t hlen; /* the current header length */ + u8_t i; +#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/ + @todo + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP6_STATS_INC(ip6.recv); + + /* identify the IP header */ + ip6hdr = (struct ip6_hdr *)p->payload; + if (IP6H_V(ip6hdr) != 6) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n", + IP6H_V(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.err); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + +#ifdef LWIP_HOOK_IP6_INPUT + if (LWIP_HOOK_IP6_INPUT(p, inp)) { + /* the packet has been eaten */ + return ERR_OK; + } +#endif + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) { + if (IP6_HLEN > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + (u16_t)IP6_HLEN, p->len)); + } + if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + (u16_t)(IP6H_PLEN(ip6hdr) + IP6_HLEN), p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr)); + + /* copy IP addresses to aligned ip6_addr_t */ + ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest); + ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src); + + /* Don't accept virtual IPv4 mapped IPv6 addresses. + * Don't accept multicast source addresses. */ + if (ip6_addr_isipv4mappedipv6(ip_2_ip6(&ip_data.current_iphdr_dest)) || + ip6_addr_isipv4mappedipv6(ip_2_ip6(&ip_data.current_iphdr_src)) || + ip6_addr_ismulticast(ip_2_ip6(&ip_data.current_iphdr_src))) { + IP6_STATS_INC(ip6.err); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* current header pointer. */ + ip_data.current_ip6_header = ip6hdr; + + /* In netif, used in case we need to send ICMPv6 packets back. */ + ip_data.current_netif = inp; + ip_data.current_input_netif = inp; + + /* match packet against an interface, i.e. is this packet for us? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* Always joined to multicast if-local and link-local all-nodes group. */ + if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) || + ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) { + netif = inp; + } +#if LWIP_IPV6_MLD + else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) { + netif = inp; + } +#else /* LWIP_IPV6_MLD */ + else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) { + /* Filter solicited node packets when MLD is not enabled + * (for Neighbor discovery). */ + netif = NULL; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) && + ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + netif = inp; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + break; + } + } + } +#endif /* LWIP_IPV6_MLD */ + else { + netif = NULL; + } + } else { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + /* interface is up? */ + if (netif_is_up(netif)) { + /* unicast to this interface address? address configured? */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) { + /* exit outer loop */ + goto netif_found; + } + } + } + if (first) { + if (ip6_addr_islinklocal(ip6_current_dest_addr()) +#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF + || ip6_addr_isloopback(ip6_current_dest_addr()) +#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */ + ) { + /* Do not match link-local addresses to other netifs. The loopback + * address is to be considered link-local and packets to it should be + * dropped on other interfaces, as per RFC 4291 Sec. 2.5.3. This + * requirement cannot be implemented in the case that loopback + * traffic is sent across a non-loopback interface, however. + */ + netif = NULL; + break; + } + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while (netif != NULL); +netif_found: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n", + netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X')); + } + + /* "::" packet source address? (used in duplicate address detection) */ + if (ip6_addr_isany(ip6_current_src_addr()) && + (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) { + /* packet source is not valid */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n")); +#if LWIP_IPV6_FORWARD + /* non-multicast packet? */ + if (!ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* try to forward IP packet on (other) interfaces */ + ip6_forward(p, ip6hdr, inp); + } +#endif /* LWIP_IPV6_FORWARD */ + pbuf_free(p); + goto ip6_input_cleanup; + } + + /* current netif pointer. */ + ip_data.current_netif = netif; + + /* Save next header type. */ + nexth = IP6H_NEXTH(ip6hdr); + + /* Init header length. */ + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + + /* Move to payload. */ + pbuf_header(p, -IP6_HLEN); + + /* Process known option extension headers, if present. */ + while (nexth != IP6_NEXTH_NONE) + { + switch (nexth) { + case IP6_NEXTH_HOPBYHOP: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -(s16_t)hlen); + break; + case IP6_NEXTH_DESTOPTS: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -(s16_t)hlen); + break; + case IP6_NEXTH_ROUTING: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -(s16_t)hlen); + break; + + case IP6_NEXTH_FRAGMENT: + { + struct ip6_frag_hdr *frag_hdr; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n")); + + frag_hdr = (struct ip6_frag_hdr *)p->payload; + + /* Get next header type. */ + nexth = frag_hdr->_nexth; + + /* Fragment Header length. */ + hlen = 8; + ip_data.current_ip_header_tot_len += hlen; + + /* Make sure this header fits in current pbuf. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_FRAG_STATS_INC(ip6_frag.lenerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto ip6_input_cleanup; + } + + /* Offset == 0 and more_fragments == 0? */ + if ((frag_hdr->_fragment_offset & + PP_HTONS(IP6_FRAG_OFFSET_MASK | IP6_FRAG_MORE_FLAG)) == 0) { + /* This is a 1-fragment packet, usually a packet that we have + * already reassembled. Skip this header anc continue. */ + pbuf_header(p, -(s16_t)hlen); + } else { +#if LWIP_IPV6_REASS + + /* reassemble the packet */ + p = ip6_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + goto ip6_input_cleanup; + } + + /* Returned p point to IPv6 header. + * Update all our variables and pointers and continue. */ + ip6hdr = (struct ip6_hdr *)p->payload; + nexth = IP6H_NEXTH(ip6hdr); + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + pbuf_header(p, -IP6_HLEN); + +#else /* LWIP_IPV6_REASS */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.opterr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; +#endif /* LWIP_IPV6_REASS */ + } + break; + } + default: + goto options_done; + break; + } + } +options_done: + + /* p points to IPv6 header again. */ + pbuf_header_force(p, (s16_t)ip_data.current_ip_header_tot_len); + + /* send to upper layers */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n")); + ip6_debug_print(p); + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + switch (nexth) { + case IP6_NEXTH_NONE: + pbuf_free(p); + break; +#if LWIP_UDP + case IP6_NEXTH_UDP: +#if LWIP_UDPLITE + case IP6_NEXTH_UDPLITE: +#endif /* LWIP_UDPLITE */ + /* Point to payload. */ + pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP6_NEXTH_TCP: + /* Point to payload. */ + pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP6 + case IP6_NEXTH_ICMP6: + /* Point to payload. */ + pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len); + icmp6_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP6 + /* send ICMP parameter problem unless it was a multicast or ICMPv6 */ + if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) && + (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) { + icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen); + } +#endif /* LWIP_ICMP */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.proterr); + IP6_STATS_INC(ip6.drop); + break; + } + } + +ip6_input_cleanup: + ip_data.current_netif = NULL; + ip_data.current_input_netif = NULL; + ip_data.current_ip6_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip6_addr_set_zero(ip6_current_src_addr()); + ip6_addr_set_zero(ip6_current_dest_addr()); + + return ERR_OK; +} + + +/** + * Sends an IPv6 packet on a network interface. This function constructs + * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is + * used as source (usually during network startup). If the source IPv6 address it + * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network + * interface is filled in as source address. If the destination IPv6 address is + * LWIP_IP_HDRINCL, p is assumed to already include an IPv6 header and + * p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == LWIP_IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IPv6/LINK headers + * returns errors returned by netif->output + */ +err_t +ip6_output_if(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, + u8_t hl, u8_t tc, + u8_t nexth, struct netif *netif) +{ + const ip6_addr_t *src_used = src; + if (dest != LWIP_IP_HDRINCL) { + if (src != NULL && ip6_addr_isany(src)) { + src_used = ip_2_ip6(ip6_select_source_address(netif, dest)); + if ((src_used == NULL) || ip6_addr_isany(src_used)) { + /* No appropriate source address was found for this packet. */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n")); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + } + } + return ip6_output_if_src(p, src_used, dest, hl, tc, nexth, netif); +} + +/** + * Same as ip6_output_if() but 'src' address is not replaced by netif address + * when it is 'any'. + */ +err_t +ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, + u8_t hl, u8_t tc, + u8_t nexth, struct netif *netif) +{ + struct ip6_hdr *ip6hdr; + ip6_addr_t dest_addr; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + /* Should the IPv6 header be generated or is it already included in p? */ + if (dest != LWIP_IP_HDRINCL) { + /* generate IPv6 header */ + if (pbuf_header(p, IP6_HLEN)) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + ip6hdr = (struct ip6_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", + (p->len >= sizeof(struct ip6_hdr))); + + IP6H_HOPLIM_SET(ip6hdr, hl); + IP6H_NEXTH_SET(ip6hdr, nexth); + + /* dest cannot be NULL here */ + ip6_addr_copy(ip6hdr->dest, *dest); + + IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); + IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); + + if (src == NULL) { + src = IP6_ADDR_ANY6; + } + /* src cannot be NULL here */ + ip6_addr_copy(ip6hdr->src, *src); + + } else { + /* IP header already included in p */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(dest_addr, ip6hdr->dest); + dest = &dest_addr; + } + + IP6_STATS_INC(ip6.xmit); + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num)); + ip6_debug_print(p); + +#if ENABLE_LOOPBACK + { + int i; +#if !LWIP_HAVE_LOOPIF + if (ip6_addr_isloopback(dest)) { + return netif_loop_output(netif, p); + } +#endif /* !LWIP_HAVE_LOOPIF */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(dest, netif_ip6_addr(netif, i))) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP6_DEBUG, ("netif_loop_output()\n")); + return netif_loop_output(netif, p); + } + } + } +#endif /* ENABLE_LOOPBACK */ +#if LWIP_IPV6_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { + return ip6_frag(p, netif, dest); + } +#endif /* LWIP_IPV6_FRAG */ + + LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()\n")); + return netif->output_ip6(netif, p, dest); +} + +/** + * Simple interface to ip6_output_if. It finds the outgoing network + * interface and calls upon ip6_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == LWIP_IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + if (dest != LWIP_IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + return ip6_output_if(p, src, dest, hl, tc, nexth, netif); +} + + +#if LWIP_NETIF_HWADDRHINT +/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip6_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == LWIP_IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + err_t err; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + if (dest != LWIP_IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if LWIP_IPV6_MLD +/** + * Add a hop-by-hop options header with a router alert option and padding. + * + * Used by MLD when sending a Multicast listener report/done message. + * + * @param p the packet to which we will prepend the options header + * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6) + * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD) + * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise + */ +err_t +ip6_options_add_hbh_ra(struct pbuf *p, u8_t nexth, u8_t value) +{ + struct ip6_hbh_hdr *hbh_hdr; + + /* Move pointer to make room for hop-by-hop options header. */ + if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + hbh_hdr = (struct ip6_hbh_hdr *)p->payload; + + /* Set fields. */ + hbh_hdr->_nexth = nexth; + hbh_hdr->_hlen = 0; + hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION; + hbh_hdr->_ra_opt_dlen = 2; + hbh_hdr->_ra_opt_data = value; + hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION; + hbh_hdr->_padn_opt_dlen = 0; + + return ERR_OK; +} +#endif /* LWIP_IPV6_MLD */ + +#if IP6_DEBUG +/* Print an IPv6 header by using LWIP_DEBUGF + * @param p an IPv6 packet, p->payload pointing to the IPv6 header + */ +void +ip6_debug_print(struct pbuf *p) +{ + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; + + LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n")); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n", + IP6H_V(ip6hdr), + IP6H_TC(ip6hdr), + IP6H_FL(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n", + IP6H_PLEN(ip6hdr), + IP6H_NEXTH(ip6hdr), + IP6H_HOPLIM(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->src)), + IP6_ADDR_BLOCK2(&(ip6hdr->src)), + IP6_ADDR_BLOCK3(&(ip6hdr->src)), + IP6_ADDR_BLOCK4(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->src)), + IP6_ADDR_BLOCK6(&(ip6hdr->src)), + IP6_ADDR_BLOCK7(&(ip6hdr->src)), + IP6_ADDR_BLOCK8(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->dest)), + IP6_ADDR_BLOCK2(&(ip6hdr->dest)), + IP6_ADDR_BLOCK3(&(ip6hdr->dest)), + IP6_ADDR_BLOCK4(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->dest)), + IP6_ADDR_BLOCK6(&(ip6hdr->dest)), + IP6_ADDR_BLOCK7(&(ip6hdr->dest)), + IP6_ADDR_BLOCK8(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP6_DEBUG */ + +#endif /* LWIP_IPV6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6_addr.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6_addr.c new file mode 100644 index 0000000..7b6a79a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6_addr.c @@ -0,0 +1,294 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * Functions for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip_addr.h" +#include "lwip/def.h" + +/* used by IP6_ADDR_ANY(6) in ip6_addr.h */ +const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul); + +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif +#ifndef xchar +#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) +#endif + +/** + * Check whether "cp" is a valid ascii representation + * of an IPv6 address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * + * @param cp IPv6 address in ascii representation (e.g. "FF01::1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ip6addr_aton(const char *cp, ip6_addr_t *addr) +{ + u32_t addr_index, zero_blocks, current_block_index, current_block_value; + const char *s; + + /* Count the number of colons, to count the number of blocks in a "::" sequence + zero_blocks may be 1 even if there are no :: sequences */ + zero_blocks = 8; + for (s = cp; *s != 0; s++) { + if (*s == ':') { + zero_blocks--; + } else if (!isxdigit(*s)) { + break; + } + } + + /* parse each block */ + addr_index = 0; + current_block_index = 0; + current_block_value = 0; + for (s = cp; *s != 0; s++) { + if (*s == ':') { + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + current_block_index++; + current_block_value = 0; + if (current_block_index > 7) { + /* address too long! */ + return 0; + } + if (s[1] == ':') { + if (s[2] == ':') { + /* invalid format: three successive colons */ + return 0; + } + s++; + /* "::" found, set zeros */ + while (zero_blocks > 0) { + zero_blocks--; + if (current_block_index & 0x1) { + addr_index++; + } else { + if (addr) { + addr->addr[addr_index] = 0; + } + } + current_block_index++; + if (current_block_index > 7) { + /* address too long! */ + return 0; + } + } + } + } else if (isxdigit(*s)) { + /* add current digit */ + current_block_value = (current_block_value << 4) + + (isdigit(*s) ? (u32_t)(*s - '0') : + (u32_t)(10 + (islower(*s) ? *s - 'a' : *s - 'A'))); + } else { + /* unexpected digit, space? CRLF? */ + break; + } + } + + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + + /* convert to network byte order. */ + if (addr) { + for (addr_index = 0; addr_index < 4; addr_index++) { + addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]); + } + } + + if (current_block_index != 7) { + return 0; + } + + return 1; +} + +/** + * Convert numeric IPv6 address into ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip6 address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * representation of addr + */ +char * +ip6addr_ntoa(const ip6_addr_t *addr) +{ + static char str[40]; + return ip6addr_ntoa_r(addr, str, 40); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip6 address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char * +ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen) +{ + u32_t current_block_index, current_block_value, next_block_value; + s32_t i; + u8_t zero_flag, empty_block_flag; + + i = 0; + empty_block_flag = 0; /* used to indicate a zero chain for "::' */ + + for (current_block_index = 0; current_block_index < 8; current_block_index++) { + /* get the current 16-bit block */ + current_block_value = lwip_htonl(addr->addr[current_block_index >> 1]); + if ((current_block_index & 0x1) == 0) { + current_block_value = current_block_value >> 16; + } + current_block_value &= 0xffff; + + /* Check for empty block. */ + if (current_block_value == 0) { + if (current_block_index == 7 && empty_block_flag == 1) { + /* special case, we must render a ':' for the last block. */ + buf[i++] = ':'; + if (i >= buflen) { + return NULL; + } + break; + } + if (empty_block_flag == 0) { + /* generate empty block "::", but only if more than one contiguous zero block, + * according to current formatting suggestions RFC 5952. */ + next_block_value = lwip_htonl(addr->addr[(current_block_index + 1) >> 1]); + if ((current_block_index & 0x1) == 0x01) { + next_block_value = next_block_value >> 16; + } + next_block_value &= 0xffff; + if (next_block_value == 0) { + empty_block_flag = 1; + buf[i++] = ':'; + if (i >= buflen) { + return NULL; + } + continue; /* move on to next block. */ + } + } else if (empty_block_flag == 1) { + /* move on to next block. */ + continue; + } + } else if (empty_block_flag == 1) { + /* Set this flag value so we don't produce multiple empty blocks. */ + empty_block_flag = 2; + } + + if (current_block_index > 0) { + buf[i++] = ':'; + if (i >= buflen) { + return NULL; + } + } + + if ((current_block_value & 0xf000) == 0) { + zero_flag = 1; + } else { + buf[i++] = xchar(((current_block_value & 0xf000) >> 12)); + zero_flag = 0; + if (i >= buflen) { + return NULL; + } + } + + if (((current_block_value & 0xf00) == 0) && (zero_flag)) { + /* do nothing */ + } else { + buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); + zero_flag = 0; + if (i >= buflen) { + return NULL; + } + } + + if (((current_block_value & 0xf0) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); + zero_flag = 0; + if (i >= buflen) { + return NULL; + } + } + + buf[i++] = xchar((current_block_value & 0xf)); + if (i >= buflen) { + return NULL; + } + } + + buf[i] = 0; + + return buf; +} + +#endif /* LWIP_IPV6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6_frag.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6_frag.c new file mode 100644 index 0000000..2a02d7d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/ip6_frag.c @@ -0,0 +1,805 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" +#include "lwip/ip6_frag.h" +#include "lwip/ip6.h" +#include "lwip/icmp6.h" +#include "lwip/nd6.h" +#include "lwip/ip.h" + +#include "lwip/pbuf.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#if IPV6_FRAG_COPYHEADER +#define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN)) +#endif + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IPv6 header, since it replaces + * the Fragment Header in memory in incoming fragments to keep + * track of the various fragments. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* static variables */ +static struct ip6_reassdata *ip6_reassdatagrams; +static u16_t ip6_reass_pbufcount; + +/* Forward declarations. */ +static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr); +#if IP_REASS_FREE_OLDEST +static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed); +#endif /* IP_REASS_FREE_OLDEST */ + +void +ip6_reass_tmr(void) +{ + struct ip6_reassdata *r, *tmp; + +#if !IPV6_FRAG_COPYHEADER + LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1", + sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN); +#endif /* !IPV6_FRAG_COPYHEADER */ + + r = ip6_reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + r = r->next; + } else { + /* reassembly timed out */ + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip6_reass_free_complete_datagram(tmp); + } + } +} + +/** + * Free a datagram (struct ip6_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), + * sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + */ +static void +ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) +{ + struct ip6_reassdata *prev; + u16_t pbufs_freed = 0; + u16_t clen; + struct pbuf *p; + struct ip6_reass_helper *iprh; + +#if LWIP_ICMP6 + iprh = (struct ip6_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, move back to the original ipv6 header (we are now pointing to Fragment header). + This cannot fail since we already checked when receiving this fragment. */ + if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)IPV6_FRAG_HDRREF(ipr->iphdr)))) { + LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); + } + else { + icmp6_time_exceeded(p, ICMP6_TE_FRAG); + } + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP6 */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip6_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + + /* Then, unchain the struct ip6_reassdata from the list and free it. */ + if (ipr == ip6_reassdatagrams) { + ip6_reassdatagrams = ipr->next; + } else { + prev = ip6_reassdatagrams; + while (prev != NULL) { + if (prev->next == ipr) { + break; + } + prev = prev->next; + } + if (prev != NULL) { + prev->next = ipr->next; + } + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* Finally, update number of pbufs in reassembly queue */ + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); + ip6_reass_pbufcount -= pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram ipr is not freed! + * + * @param ipr ip6_reassdata for the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + */ +static void +ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed) +{ + struct ip6_reassdata *r, *oldest; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the current datagram! */ + do { + r = oldest = ip6_reassdatagrams; + while (r != NULL) { + if (r != ipr) { + if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + r = r->next; + } + if (oldest == ipr) { + /* nothing to free, ipr is the only element on the list */ + return; + } + if (oldest != NULL) { + ip6_reass_free_complete_datagram(oldest); + } + } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (ip6_reassdatagrams != NULL)); +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Reassembles incoming IPv6 fragments into an IPv6 datagram. + * + * @param p points to the IPv6 Fragment Header + * @return NULL if reassembly is incomplete, pbuf pointing to + * IPv6 Header if reassembly is complete + */ +struct pbuf * +ip6_reass(struct pbuf *p) +{ + struct ip6_reassdata *ipr, *ipr_prev; + struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct ip6_frag_hdr *frag_hdr; + u16_t offset, len; + u16_t clen; + u8_t valid = 1; + struct pbuf *q; + + IP6_FRAG_STATS_INC(ip6_frag.recv); + + if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) { + /* ip6_frag_hdr must be in the first pbuf, not chained */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + + frag_hdr = (struct ip6_frag_hdr *) p->payload; + + clen = pbuf_clen(p); + + offset = lwip_ntohs(frag_hdr->_fragment_offset); + + /* Calculate fragment length from IPv6 payload length. + * Adjust for headers before Fragment Header. + * And finally adjust by Fragment Header length. */ + len = lwip_ntohs(ip6_current_header()->_plen); + len -= (u16_t)(((u8_t*)p->payload - (const u8_t*)ip6_current_header()) - IP6_HLEN); + len -= IP6_FRAG_HLEN; + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = ip6_reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if ((frag_hdr->_identification == ipr->identification) && + ip6_addr_cmp(ip6_current_src_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) && + ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->dest))) { + IP6_FRAG_STATS_INC(ip6_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + /* Make room and try again. */ + ip6_reass_remove_oldest_datagram(ipr, clen); + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr != NULL) { + /* re-search ipr_prev since it might have been removed */ + for (ipr_prev = ip6_reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { + if (ipr_prev->next == ipr) { + break; + } + } + } else +#endif /* IP_REASS_FREE_OLDEST */ + { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + memset(ipr, 0, sizeof(struct ip6_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = ip6_reassdatagrams; + ip6_reassdatagrams = ipr; + + /* Use the current IPv6 header for src/dest address reference. + * Eventually, we will replace it when we get the first fragment + * (it might be this one, in any case, it is done later). */ +#if IPV6_FRAG_COPYHEADER + MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN); +#else /* IPV6_FRAG_COPYHEADER */ + /* need to use the none-const pointer here: */ + ipr->iphdr = ip_data.current_ip6_header; +#endif /* IPV6_FRAG_COPYHEADER */ + + /* copy the fragmented packet id. */ + ipr->identification = frag_hdr->_identification; + + /* copy the nexth field */ + ipr->nexth = frag_hdr->_nexth; + } + + /* Check if we are allowed to enqueue more datagrams. */ + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + ip6_reass_remove_oldest_datagram(ipr, clen); + if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) { + /* re-search ipr_prev since it might have been removed */ + for (ipr_prev = ip6_reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { + if (ipr_prev->next == ipr) { + break; + } + } + } else +#endif /* IP_REASS_FREE_OLDEST */ + { + /* @todo: send ICMPv6 time exceeded here? */ + /* drop this pbuf */ + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + /* Overwrite Fragment Header with our own helper struct. */ +#if IPV6_FRAG_COPYHEADER + if (IPV6_FRAG_REQROOM > 0) { + /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4). + This cannot fail since we already checked when receiving this fragment. */ + u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM); + LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); + } +#else /* IPV6_FRAG_COPYHEADER */ + LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1", + sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN); +#endif /* IPV6_FRAG_COPYHEADER */ + iprh = (struct ip6_reass_helper *)p->payload; + iprh->next_pbuf = NULL; + iprh->start = (offset & IP6_FRAG_OFFSET_MASK); + iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; + + /* find the right place to insert this pbuf */ + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip6_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { +#if IP_REASS_CHECK_OVERLAP + if (iprh->end > iprh_tmp->start) { + /* fragment overlaps with following, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + if (iprh_prev != NULL) { + if (iprh->start < iprh_prev->end) { + /* fragment overlaps with previous, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } +#endif /* IP_REASS_CHECK_OVERLAP */ + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ + iprh_prev->next_pbuf = p; + } else { + /* fragment with the lowest offset */ + ipr->p = p; + } + break; + } else if (iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#if IP_REASS_CHECK_OVERLAP + } else if (iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no gaps. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = p; + } + } + + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip6_reass_pbufcount += clen; + + /* Remember IPv6 header if this is the first fragment. */ + if (iprh->start == 0) { +#if IPV6_FRAG_COPYHEADER + if (iprh->next_pbuf != NULL) { + MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN); + } +#else /* IPV6_FRAG_COPYHEADER */ + /* need to use the none-const pointer here: */ + ipr->iphdr = ip_data.current_ip6_header; +#endif /* IPV6_FRAG_COPYHEADER */ + } + + /* If this is the last fragment, calculate total packet length. */ + if ((offset & IP6_FRAG_MORE_FLAG) == 0) { + ipr->datagram_len = iprh->end; + } + + /* Additional validity tests: we have received first and last fragment. */ + iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; + if (iprh_tmp->start != 0) { + valid = 0; + } + if (ipr->datagram_len == 0) { + valid = 0; + } + + /* Final validity test: no gaps between current and last fragment. */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while ((q != NULL) && valid) { + iprh = (struct ip6_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + + if (valid) { + /* All fragments have been received */ + struct ip6_hdr* iphdr_ptr; + + /* chain together the pbufs contained within the ip6_reassdata list. */ + iprh = (struct ip6_reass_helper*) ipr->p->payload; + while (iprh != NULL) { + struct pbuf* next_pbuf = iprh->next_pbuf; + if (next_pbuf != NULL) { + /* Save next helper struct (will be hidden in next step). */ + iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload; + + /* hide the fragment header for every succeeding fragment */ + pbuf_header(next_pbuf, -IP6_FRAG_HLEN); +#if IPV6_FRAG_COPYHEADER + if (IPV6_FRAG_REQROOM > 0) { + /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */ + u8_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM)); + LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); + } +#endif + pbuf_cat(ipr->p, next_pbuf); + } + else { + iprh_tmp = NULL; + } + + iprh = iprh_tmp; + } + +#if IPV6_FRAG_COPYHEADER + if (IPV6_FRAG_REQROOM > 0) { + /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */ + u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM)); + LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); + } + iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN); + MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN); +#else + iphdr_ptr = ipr->iphdr; +#endif + + /* Adjust datagram length by adding header lengths. */ + ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr) + + IP6_FRAG_HLEN + - IP6_HLEN); + + /* Set payload length in ip header. */ + iphdr_ptr->_plen = lwip_htons(ipr->datagram_len); + + /* Get the first pbuf. */ + p = ipr->p; + + /* Restore Fragment Header in first pbuf. Mark as "single fragment" + * packet. Restore nexth. */ + frag_hdr = (struct ip6_frag_hdr *) p->payload; + frag_hdr->_nexth = ipr->nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = 0; + frag_hdr->_identification = 0; + + /* release the sources allocate for the fragment queue entry */ + if (ip6_reassdatagrams == ipr) { + /* it was the first in the list */ + ip6_reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); + ipr_prev->next = ipr->next; + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* adjust the number of pbufs currently queued for reassembly. */ + ip6_reass_pbufcount -= pbuf_clen(p); + + /* Move pbuf back to IPv6 header. + This cannot fail since we already checked when receiving this fragment. */ + if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) { + LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); + pbuf_free(p); + return NULL; + } + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + return NULL; + +nullreturn: + pbuf_free(p); + return NULL; +} + +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG + +#if !LWIP_NETIF_TX_SINGLE_PBUF +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip6_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ip6_frag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip6_frag_free_pbuf_custom_ref(pcr); +} +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ + +/** + * Fragment an IPv6 datagram if too large for the netif or path MTU. + * + * Chop the datagram in MTU sized chunks and send them in order + * by pointing PBUF_REFs into p + * + * @param p ipv6 packet to send + * @param netif the netif on which to send + * @param dest destination ipv6 address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest) +{ + struct ip6_hdr *original_ip6hdr; + struct ip6_hdr *ip6hdr; + struct ip6_frag_hdr *frag_hdr; + struct pbuf *rambuf; +#if !LWIP_NETIF_TX_SINGLE_PBUF + struct pbuf *newpbuf; + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + static u32_t identification; + u16_t nfb; + u16_t left, cop; + u16_t mtu; + u16_t fragment_offset = 0; + u16_t last; + u16_t poff = IP6_HLEN; + + identification++; + + original_ip6hdr = (struct ip6_hdr *)p->payload; + + mtu = nd6_get_destination_mtu(dest, netif); + + /* @todo we assume there are no options in the unfragmentable part (IPv6 header). */ + left = p->tot_len - IP6_HLEN; + + nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK; + + while (left) { + last = (left <= nfb); + + /* Fill this fragment */ + cop = last ? left : nfb; + +#if LWIP_NETIF_TX_SINGLE_PBUF + rambuf = pbuf_alloc(PBUF_IP, cop + IP6_FRAG_HLEN, PBUF_RAM); + if (rambuf == NULL) { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); + poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff); + /* make room for the IP header */ + if (pbuf_header(rambuf, IP6_HLEN)) { + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + /* fill in the IP header */ + SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN); + ip6hdr = (struct ip6_hdr *)rambuf->payload; + frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN); +#else + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM); + if (rambuf == NULL) { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP6_HLEN))); + SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN); + ip6hdr = (struct ip6_hdr *)rambuf->payload; + frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN); + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + p->tot_len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip6_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip6_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + /* Set headers */ + frag_hdr->_nexth = original_ip6hdr->_nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = lwip_htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)); + frag_hdr->_identification = lwip_htonl(identification); + + IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT); + IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN); + + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + IP6_FRAG_STATS_INC(ip6_frag.xmit); + netif->output_ip6(netif, rambuf, dest); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); + left -= cop; + fragment_offset += cop; + } + return ERR_OK; +} + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/mld6.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/mld6.c new file mode 100644 index 0000000..90bcd36 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/mld6.c @@ -0,0 +1,588 @@ +/** + * @file + * Multicast listener discovery + * + * @defgroup mld6 MLD6 + * @ingroup ip6 + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2.\n + * To be called from TCPIP thread + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +/* Based on igmp.c implementation of igmp v2 protocol */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mld6.h" +#include "lwip/prot/mld6.h" +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + + +/* + * MLD constants + */ +#define MLD6_HL 1 +#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500) + +#define MLD6_GROUP_NON_MEMBER 0 +#define MLD6_GROUP_DELAYING_MEMBER 1 +#define MLD6_GROUP_IDLE_MEMBER 2 + +/* Forward declarations. */ +static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr); +static err_t mld6_remove_group(struct netif *netif, struct mld_group *group); +static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); +static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type); + + +/** + * Stop MLD processing on interface + * + * @param netif network interface on which stop MLD processing + */ +err_t +mld6_stop(struct netif *netif) +{ + struct mld_group *group = netif_mld6_data(netif); + + netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL); + + while (group != NULL) { + struct mld_group *next = group->next; /* avoid use-after-free below */ + + /* disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER); + } + + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report MLD memberships for this interface + * + * @param netif network interface on which report MLD memberships + */ +void +mld6_report_groups(struct netif *netif) +{ + struct mld_group *group = netif_mld6_data(netif); + + while (group != NULL) { + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + group = group->next; + } +} + +/** + * Search for a group that is joined on a netif + * + * @param ifp the network interface for which to look + * @param addr the group ipv6 address to search for + * @return a struct mld_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct mld_group * +mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr) +{ + struct mld_group *group = netif_mld6_data(ifp); + + while (group != NULL) { + if (ip6_addr_cmp(&(group->group_address), addr)) { + return group; + } + group = group->next; + } + + return NULL; +} + + +/** + * create a new group + * + * @param ifp the network interface for which to create + * @param addr the new group ipv6 + * @return a struct mld_group*, + * NULL on memory error. + */ +static struct mld_group * +mld6_new_group(struct netif *ifp, const ip6_addr_t *addr) +{ + struct mld_group *group; + + group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); + if (group != NULL) { + ip6_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = netif_mld6_data(ifp); + + netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group); + } + + return group; +} + +/** + * Remove a group from the mld_group_list, but do not free it yet + * + * @param group the group to remove + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +mld6_remove_group(struct netif *netif, struct mld_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (netif_mld6_data(netif) == group) { + netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next); + } else { + /* look for group further down the list */ + struct mld_group *tmpGroup; + for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not find group */ + if (tmpGroup == NULL) { + err = ERR_ARG; + } + } + + return err; +} + + +/** + * Process an input MLD message. Called by icmp6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +mld6_input(struct pbuf *p, struct netif *inp) +{ + struct mld_header *mld_hdr; + struct mld_group *group; + + MLD6_STATS_INC(mld6.recv); + + /* Check that mld header fits in packet. */ + if (p->len < sizeof(struct mld_header)) { + /* @todo debug message */ + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + MLD6_STATS_INC(mld6.drop); + return; + } + + mld_hdr = (struct mld_header *)p->payload; + + switch (mld_hdr->type) { + case ICMP6_TYPE_MLQ: /* Multicast listener query. */ + /* Is it a general query? */ + if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && + ip6_addr_isany(&(mld_hdr->multicast_address))) { + MLD6_STATS_INC(mld6.rx_general); + /* Report all groups, except all nodes group, and if-local groups. */ + group = netif_mld6_data(inp); + while (group != NULL) { + if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && + (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + group = group->next; + } + } else { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_group); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* Schedule a report. */ + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + } + break; /* ICMP6_TYPE_MLQ */ + case ICMP6_TYPE_MLR: /* Multicast listener report. */ + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_report); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* If we are waiting to report, cancel it. */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + group->timer = 0; /* stopped */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + } + break; /* ICMP6_TYPE_MLR */ + case ICMP6_TYPE_MLD: /* Multicast listener done. */ + /* Do nothing, router will query us. */ + break; /* ICMP6_TYPE_MLD */ + default: + MLD6_STATS_INC(mld6.proterr); + MLD6_STATS_INC(mld6.drop); + break; + } + + pbuf_free(p); +} + +/** + * @ingroup mld6 + * Join a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * join a new group. If IP6_ADDR_ANY, join on all netifs + * @param groupaddr the ipv6 address of the group to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct netif *netif; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if (ip6_addr_isany(srcaddr) || + netif_get_ip6_addr_match(netif, srcaddr) >= 0) { + err = mld6_joingroup_netif(netif, groupaddr); + if (err != ERR_OK) { + return err; + } + } + + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * @ingroup mld6 + * Join a group on a network interface. + * + * @param netif the network interface which should join a new group. + * @param groupaddr the ipv6 address of the group to join + * @return ERR_OK if group was joined on the netif, an err_t otherwise + */ +err_t +mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) +{ + struct mld_group *group; + + /* find group or create a new one if not found */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group == NULL) { + /* Joining a new group. Create a new group entry. */ + group = mld6_new_group(netif, groupaddr); + if (group == NULL) { + return ERR_MEM; + } + + /* Activate this address on the MAC layer. */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER); + } + + /* Report our membership. */ + MLD6_STATS_INC(mld6.tx_report); + mld6_send(netif, group, ICMP6_TYPE_MLR); + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + + /* Increment group use */ + group->use++; + return ERR_OK; +} + +/** + * @ingroup mld6 + * Leave a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * leave the group. If IP6_ISANY, leave on all netifs + * @param groupaddr the ipv6 address of the group to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct netif *netif; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if (ip6_addr_isany(srcaddr) || + netif_get_ip6_addr_match(netif, srcaddr) >= 0) { + err_t res = mld6_leavegroup_netif(netif, groupaddr); + if (err != ERR_OK) { + /* Store this result if we have not yet gotten a success */ + err = res; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * @ingroup mld6 + * Leave a group on a network interface. + * + * @param netif the network interface which should leave the group. + * @param groupaddr the ipv6 address of the group to leave + * @return ERR_OK if group was left on the netif, an err_t otherwise + */ +err_t +mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) +{ + struct mld_group *group; + + /* find group */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Leave if there is no other use of the group */ + if (group->use <= 1) { + /* Remove the group from the list */ + mld6_remove_group(netif, group); + + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + MLD6_STATS_INC(mld6.tx_leave); + mld6_send(netif, group, ICMP6_TYPE_MLD); + } + + /* Disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER); + } + + /* free group struct */ + memp_free(MEMP_MLD6_GROUP, group); + } else { + /* Decrement group use */ + group->use--; + } + + /* Left group */ + return ERR_OK; + } + + /* Group not found */ + return ERR_VAL; +} + + +/** + * Periodic timer for mld processing. Must be called every + * MLD6_TMR_INTERVAL milliseconds (100). + * + * When a delaying member expires, a membership report is sent. + */ +void +mld6_tmr(void) +{ + struct netif *netif = netif_list; + + while (netif != NULL) { + struct mld_group *group = netif_mld6_data(netif); + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + MLD6_STATS_INC(mld6.tx_report); + mld6_send(netif, group, ICMP6_TYPE_MLR); + group->group_state = MLD6_GROUP_IDLE_MEMBER; + } + } + } + group = group->next; + } + netif = netif->next; + } +} + +/** + * Schedule a delayed membership report for a group + * + * @param group the mld_group for which "delaying" membership report + * should be sent + * @param maxresp the max resp delay provided in the query + */ +static void +mld6_delayed_report(struct mld_group *group, u16_t maxresp) +{ + /* Convert maxresp from milliseconds to tmr ticks */ + maxresp = maxresp / MLD6_TMR_INTERVAL; + if (maxresp == 0) { + maxresp = 1; + } + +#ifdef LWIP_RAND + /* Randomize maxresp. (if LWIP_RAND is supported) */ + maxresp = LWIP_RAND() % maxresp; + if (maxresp == 0) { + maxresp = 1; + } +#endif /* LWIP_RAND */ + + /* Apply timer value if no report has been scheduled already. */ + if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) || + ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + group->timer = maxresp; + group->group_state = MLD6_GROUP_DELAYING_MEMBER; + } +} + +/** + * Send a MLD message (report or done). + * + * An IPv6 hop-by-hop options header with a router alert option + * is prepended. + * + * @param group the group to report or quit + * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) + */ +static void +mld6_send(struct netif *netif, struct mld_group *group, u8_t type) +{ + struct mld_header *mld_hdr; + struct pbuf *p; + const ip6_addr_t *src_addr; + + /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM); + if (p == NULL) { + MLD6_STATS_INC(mld6.memerr); + return; + } + + /* Move to make room for Hop-by-hop options header. */ + if (pbuf_header(p, -IP6_HBH_HLEN)) { + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + return; + } + + /* Select our source address. */ + if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { + /* This is a special case, when we are performing duplicate address detection. + * We must join the multicast group, but we don't have a valid address yet. */ + src_addr = IP6_ADDR_ANY6; + } else { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(netif, 0); + } + + /* MLD message header pointer. */ + mld_hdr = (struct mld_header *)p->payload; + + /* Set fields. */ + mld_hdr->type = type; + mld_hdr->code = 0; + mld_hdr->chksum = 0; + mld_hdr->max_resp_delay = 0; + mld_hdr->reserved = 0; + ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); + +#if CHECKSUM_GEN_ICMP6 + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { + mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, + src_addr, &(group->group_address)); + } +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Add hop-by-hop headers options: router alert with MLD value. */ + ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); + + if (type == ICMP6_TYPE_MLR) { + /* Remember we were the last to report */ + group->last_reporter_flag = 1; + } + + /* Send the packet out. */ + MLD6_STATS_INC(mld6.xmit); + ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), + MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif); + pbuf_free(p); +} + +#endif /* LWIP_IPV6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/ipv6/nd6.c b/sdk/Src/Network/lwip2.0.3/core/ipv6/nd6.c new file mode 100644 index 0000000..cb02ad9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/ipv6/nd6.c @@ -0,0 +1,2102 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/nd6.h" +#include "lwip/priv/nd6_priv.h" +#include "lwip/prot/nd6.h" +#include "lwip/prot/icmp6.h" +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" +#include "lwip/mld6.h" +#include "lwip/ip.h" +#include "lwip/stats.h" +#include "lwip/dns.h" + +#include + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK +#error LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK +#endif + +/* Router tables. */ +struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; +struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; +struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES]; +struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS]; + +/* Default values, can be updated by a RA message. */ +u32_t reachable_time = LWIP_ND6_REACHABLE_TIME; +u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* @todo implement this value in timer */ + +/* Index for cache entries. */ +static u8_t nd6_cached_neighbor_index; +static u8_t nd6_cached_destination_index; + +/* Multicast address holder. */ +static ip6_addr_t multicast_address; + +/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */ +static u8_t nd6_ra_buffer[sizeof(struct prefix_option)]; + +/* Forward declarations. */ +static s8_t nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr); +static s8_t nd6_new_neighbor_cache_entry(void); +static void nd6_free_neighbor_cache_entry(s8_t i); +static s8_t nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr); +static s8_t nd6_new_destination_cache_entry(void); +static s8_t nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif); +static s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif); +static s8_t nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif); +static s8_t nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif); +static s8_t nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif); +static s8_t nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif); +static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif); +static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q); + +#define ND6_SEND_FLAG_MULTICAST_DEST 0x01 +#define ND6_SEND_FLAG_ALLNODES_DEST 0x02 +static void nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags); +static void nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags); +static void nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags); +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +static err_t nd6_send_rs(struct netif *netif); +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +#if LWIP_ND6_QUEUEING +static void nd6_free_q(struct nd6_q_entry *q); +#else /* LWIP_ND6_QUEUEING */ +#define nd6_free_q(q) pbuf_free(q) +#endif /* LWIP_ND6_QUEUEING */ +static void nd6_send_q(s8_t i); + + +/** + * Process an incoming neighbor discovery message + * + * @param p the nd packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +nd6_input(struct pbuf *p, struct netif *inp) +{ + u8_t msg_type; + s8_t i; + + ND6_STATS_INC(nd6.recv); + + msg_type = *((u8_t *)p->payload); + switch (msg_type) { + case ICMP6_TYPE_NA: /* Neighbor Advertisement. */ + { + struct na_header *na_hdr; + struct lladdr_option *lladdr_opt; + + /* Check that na header fits in packet. */ + if (p->len < (sizeof(struct na_header))) { + /* @todo debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + na_hdr = (struct na_header *)p->payload; + + /* Unsolicited NA?*/ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + ip6_addr_t target_address; + + /* This is an unsolicited NA. + * link-layer changed? + * part of DAD mechanism? */ + + /* Create an aligned copy. */ + ip6_addr_set(&target_address, &(na_hdr->target_address)); + +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* If the target address matches this netif, it is a DAD response. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) && + ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) { + /* We are using a duplicate address. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + +#if LWIP_IPV6_AUTOCONFIG + /* Check to see if this address was autoconfigured. */ + if (!ip6_addr_islinklocal(&target_address)) { + i = nd6_get_onlink_prefix(&target_address, inp); + if (i >= 0) { + /* Mark this prefix as duplicate, so that we don't use it + * to generate this address again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE; + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + + pbuf_free(p); + return; + } + } +#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */ + + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + 2)) { + /* @todo debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) { + /* @todo debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + /* This is an unsolicited NA, most likely there was a LLADDR change. */ + i = nd6_find_neighbor_cache_entry(&target_address); + if (i >= 0) { + if (na_hdr->flags & ND6_FLAG_OVERRIDE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + } + } else { + ip6_addr_t target_address; + + /* This is a solicited NA. + * neighbor address resolution response? + * neighbor unreachability detection response? */ + + /* Create an aligned copy. */ + ip6_addr_set(&target_address, &(na_hdr->target_address)); + + /* Find the cache entry corresponding to this na. */ + i = nd6_find_neighbor_cache_entry(&target_address); + if (i < 0) { + /* We no longer care about this target address. drop it. */ + pbuf_free(p); + return; + } + + /* Update cache entry. */ + if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || + (neighbor_cache[i].state == ND6_INCOMPLETE)) { + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + 2)) { + /* @todo debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) { + /* @todo debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + + neighbor_cache[i].netif = inp; + neighbor_cache[i].state = ND6_REACHABLE; + neighbor_cache[i].counter.reachable_time = reachable_time; + + /* Send queued packets, if any. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + } + + break; /* ICMP6_TYPE_NA */ + } + case ICMP6_TYPE_NS: /* Neighbor solicitation. */ + { + struct ns_header *ns_hdr; + struct lladdr_option *lladdr_opt; + u8_t accepted; + + /* Check that ns header fits in packet. */ + if (p->len < sizeof(struct ns_header)) { + /* @todo debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ns_hdr = (struct ns_header *)p->payload; + + /* Check if there is a link-layer address provided. Only point to it if in this buffer. */ + if (p->len >= (sizeof(struct ns_header) + 2)) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + if (p->len < (sizeof(struct ns_header) + (lladdr_opt->length << 3))) { + lladdr_opt = NULL; + } + } else { + lladdr_opt = NULL; + } + + /* Check if the target address is configured on the receiving netif. */ + accepted = 0; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) || + (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) && + ip6_addr_isany(ip6_current_src_addr()))) && + ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + accepted = 1; + break; + } + } + + /* NS not for us? */ + if (!accepted) { + pbuf_free(p); + return; + } + + /* Check for ANY address in src (DAD algorithm). */ + if (ip6_addr_isany(ip6_current_src_addr())) { + /* Sender is validating this address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) && + ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + /* Send a NA back so that the sender does not use this address. */ + nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST); + if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) { + /* We shouldn't use this address either. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + } + } + } + } else { + ip6_addr_t target_address; + + /* Sender is trying to resolve our address. */ + /* Verify that they included their own link-layer address. */ + if (lladdr_opt == NULL) { + /* Not a valid message. */ + pbuf_free(p); + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + return; + } + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if (i>= 0) { + /* We already have a record for the solicitor. */ + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + + /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; + } + } else { + /* Add their IPv6 address and link-layer address to neighbor cache. + * We will need it at least to send a unicast NA message, but most + * likely we will also be communicating with this node soon. */ + i = nd6_new_neighbor_cache_entry(); + if (i < 0) { + /* We couldn't assign a cache entry for this neighbor. + * we won't be able to reply. drop it. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; + } + + /* Create an aligned copy. */ + ip6_addr_set(&target_address, &(ns_hdr->target_address)); + + /* Send back a NA for us. Allocate the reply pbuf. */ + nd6_send_na(inp, &target_address, ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE); + } + + break; /* ICMP6_TYPE_NS */ + } + case ICMP6_TYPE_RA: /* Router Advertisement. */ + { + struct ra_header *ra_hdr; + u8_t *buffer; /* Used to copy options. */ + u16_t offset; +#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS + /* There can by multiple RDNSS options per RA */ + u8_t rdnss_server_idx = 0; +#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */ + + /* Check that RA header fits in packet. */ + if (p->len < sizeof(struct ra_header)) { + /* @todo debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ra_hdr = (struct ra_header *)p->payload; + + /* If we are sending RS messages, stop. */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* ensure at least one solicitation is sent */ + if ((inp->rs_count < LWIP_ND6_MAX_MULTICAST_SOLICIT) || + (nd6_send_rs(inp) == ERR_OK)) { + inp->rs_count = 0; + } +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + /* Get the matching default router entry. */ + i = nd6_get_router(ip6_current_src_addr(), inp); + if (i < 0) { + /* Create a new router entry. */ + i = nd6_new_router(ip6_current_src_addr(), inp); + } + + if (i < 0) { + /* Could not create a new router entry. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Re-set invalidation timer. */ + default_router_list[i].invalidation_timer = lwip_htons(ra_hdr->router_lifetime); + + /* Re-set default timer values. */ +#if LWIP_ND6_ALLOW_RA_UPDATES + if (ra_hdr->retrans_timer > 0) { + retrans_timer = lwip_htonl(ra_hdr->retrans_timer); + } + if (ra_hdr->reachable_time > 0) { + reachable_time = lwip_htonl(ra_hdr->reachable_time); + } +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + + /* @todo set default hop limit... */ + /* ra_hdr->current_hop_limit;*/ + + /* Update flags in local entry (incl. preference). */ + default_router_list[i].flags = ra_hdr->flags; + + /* Offset to options. */ + offset = sizeof(struct ra_header); + + /* Process each option. */ + while ((p->tot_len - offset) > 0) { + if (p->len == p->tot_len) { + /* no need to copy from contiguous pbuf */ + buffer = &((u8_t*)p->payload)[offset]; + } else { + buffer = nd6_ra_buffer; + if (pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset) != sizeof(struct prefix_option)) { + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + } + if (buffer[1] == 0) { + /* zero-length extension. drop packet */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + switch (buffer[0]) { + case ND6_OPTION_TYPE_SOURCE_LLADDR: + { + struct lladdr_option *lladdr_opt; + lladdr_opt = (struct lladdr_option *)buffer; + if ((default_router_list[i].neighbor_entry != NULL) && + (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) { + SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len); + default_router_list[i].neighbor_entry->state = ND6_REACHABLE; + default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time; + } + break; + } + case ND6_OPTION_TYPE_MTU: + { + struct mtu_option *mtu_opt; + mtu_opt = (struct mtu_option *)buffer; + if (lwip_htonl(mtu_opt->mtu) >= 1280) { +#if LWIP_ND6_ALLOW_RA_UPDATES + inp->mtu = (u16_t)lwip_htonl(mtu_opt->mtu); +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + } + break; + } + case ND6_OPTION_TYPE_PREFIX_INFO: + { + struct prefix_option *prefix_opt; + prefix_opt = (struct prefix_option *)buffer; + + if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) && + (prefix_opt->prefix_length == 64) && + !ip6_addr_islinklocal(&(prefix_opt->prefix))) { + /* Add to on-link prefix list. */ + s8_t prefix; + ip6_addr_t prefix_addr; + + /* Get a memory-aligned copy of the prefix. */ + ip6_addr_set(&prefix_addr, &(prefix_opt->prefix)); + + /* find cache entry for this prefix. */ + prefix = nd6_get_onlink_prefix(&prefix_addr, inp); + if (prefix < 0) { + /* Create a new cache entry. */ + prefix = nd6_new_onlink_prefix(&prefix_addr, inp); + } + if (prefix >= 0) { + prefix_list[prefix].invalidation_timer = lwip_htonl(prefix_opt->valid_lifetime); + +#if LWIP_IPV6_AUTOCONFIG + if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) { + /* Mark prefix as autonomous, so that address autoconfiguration can take place. + * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/ + prefix_list[prefix].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS; + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + break; + } + case ND6_OPTION_TYPE_ROUTE_INFO: + /* @todo implement preferred routes. + struct route_option * route_opt; + route_opt = (struct route_option *)buffer;*/ + + break; +#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS + case ND6_OPTION_TYPE_RDNSS: + { + u8_t num, n; + struct rdnss_option * rdnss_opt; + + rdnss_opt = (struct rdnss_option *)buffer; + num = (rdnss_opt->length - 1) / 2; + for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++) { + ip_addr_t rdnss_address; + + /* Get a memory-aligned copy of the prefix. */ + ip_addr_copy_from_ip6(rdnss_address, rdnss_opt->rdnss_address[n]); + + if (htonl(rdnss_opt->lifetime) > 0) { + /* TODO implement Lifetime > 0 */ + dns_setserver(rdnss_server_idx++, &rdnss_address); + } else { + /* TODO implement DNS removal in dns.c */ + u8_t s; + for (s = 0; s < DNS_MAX_SERVERS; s++) { + const ip_addr_t *addr = dns_getserver(s); + if(ip_addr_cmp(addr, &rdnss_address)) { + dns_setserver(s, NULL); + } + } + } + } + break; + } +#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */ + default: + /* Unrecognized option, abort. */ + ND6_STATS_INC(nd6.proterr); + break; + } + /* option length is checked earlier to be non-zero to make sure loop ends */ + offset += 8 * ((u16_t)buffer[1]); + } + + break; /* ICMP6_TYPE_RA */ + } + case ICMP6_TYPE_RD: /* Redirect */ + { + struct redirect_header *redir_hdr; + struct lladdr_option *lladdr_opt; + ip6_addr_t tmp; + + /* Check that Redir header fits in packet. */ + if (p->len < sizeof(struct redirect_header)) { + /* @todo debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + redir_hdr = (struct redirect_header *)p->payload; + + if (p->len >= (sizeof(struct redirect_header) + 2)) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header)); + if (p->len < (sizeof(struct redirect_header) + (lladdr_opt->length << 3))) { + lladdr_opt = NULL; + } + } else { + lladdr_opt = NULL; + } + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(&tmp, &(redir_hdr->destination_address)); + + /* Find dest address in cache */ + i = nd6_find_destination_cache_entry(&tmp); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Set the new target address. */ + ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address)); + + /* If Link-layer address of other router is given, try to add to neighbor cache. */ + if (lladdr_opt != NULL) { + if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { + /* Copy target address to current source address, to have an aligned copy. */ + ip6_addr_set(&tmp, &(redir_hdr->target_address)); + + i = nd6_find_neighbor_cache_entry(&tmp); + if (i < 0) { + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), &tmp); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; + } + } + if (i >= 0) { + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; + } + } + } + } + break; /* ICMP6_TYPE_RD */ + } + case ICMP6_TYPE_PTB: /* Packet too big */ + { + struct icmp6_hdr *icmp6hdr; /* Packet too big message */ + struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */ + u32_t pmtu; + ip6_addr_t tmp; + + /* Check that ICMPv6 header + IPv6 header fit in payload */ + if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { + /* drop short packets */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(&tmp, &(ip6hdr->dest)); + + /* Look for entry in destination cache. */ + i = nd6_find_destination_cache_entry(&tmp); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Change the Path MTU. */ + pmtu = lwip_htonl(icmp6hdr->data); + destination_cache[i].pmtu = (u16_t)LWIP_MIN(pmtu, 0xFFFF); + + break; /* ICMP6_TYPE_PTB */ + } + + default: + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + break; /* default */ + } + + pbuf_free(p); +} + + +/** + * Periodic timer for Neighbor discovery functions: + * + * - Update neighbor reachability states + * - Update destination cache entries age + * - Update invalidation timers of default routers and on-link prefixes + * - Perform duplicate address detection (DAD) for our addresses + * - Send router solicitations + */ +void +nd6_tmr(void) +{ + s8_t i; + struct netif *netif; + + /* Process neighbor entries. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + switch (neighbor_cache[i].state) { + case ND6_INCOMPLETE: + if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) && + (!neighbor_cache[i].isrouter)) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST); + } + break; + case ND6_REACHABLE: + /* Send queued packets, if any are left. Should have been sent already. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) { + /* Change to stale state. */ + neighbor_cache[i].state = ND6_STALE; + neighbor_cache[i].counter.stale_time = 0; + } else { + neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_STALE: + neighbor_cache[i].counter.stale_time++; + break; + case ND6_DELAY: + if (neighbor_cache[i].counter.delay_time <= 1) { + /* Change to PROBE state. */ + neighbor_cache[i].state = ND6_PROBE; + neighbor_cache[i].counter.probes_sent = 0; + } else { + neighbor_cache[i].counter.delay_time--; + } + break; + case ND6_PROBE: + if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) && + (!neighbor_cache[i].isrouter)) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_neighbor_cache_probe(&neighbor_cache[i], 0); + } + break; + case ND6_NO_ENTRY: + default: + /* Do nothing. */ + break; + } + } + + /* Process destination entries. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + destination_cache[i].age++; + } + + /* Process router entries. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (default_router_list[i].neighbor_entry != NULL) { + /* Active entry. */ + if (default_router_list[i].invalidation_timer > 0) { + default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + } + if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + /* Less than 1 second remaining. Clear this entry. */ + default_router_list[i].neighbor_entry->isrouter = 0; + default_router_list[i].neighbor_entry = NULL; + default_router_list[i].invalidation_timer = 0; + default_router_list[i].flags = 0; + } + } + } + + /* Process prefix entries. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if (prefix_list[i].netif != NULL) { + if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + /* Entry timed out, remove it */ + prefix_list[i].invalidation_timer = 0; + +#if LWIP_IPV6_AUTOCONFIG + /* If any addresses were configured with this prefix, remove them */ + if (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED) { + s8_t j; + + for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) { + if ((netif_ip6_addr_state(prefix_list[i].netif, j) != IP6_ADDR_INVALID) && + ip6_addr_netcmp(&prefix_list[i].prefix, netif_ip6_addr(prefix_list[i].netif, j))) { + netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_INVALID); + prefix_list[i].flags = 0; + + /* Exit loop. */ + break; + } + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + + prefix_list[i].netif = NULL; + prefix_list[i].flags = 0; + } else { + prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + +#if LWIP_IPV6_AUTOCONFIG + /* Initiate address autoconfiguration for this prefix, if conditions are met. */ + if (prefix_list[i].netif->ip6_autoconfig_enabled && + (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) && + !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) { + s8_t j; + /* Try to get an address on this netif that is invalid. + * Skip 0 index (link-local address) */ + for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) { + if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDR_INVALID) { + /* Generate an address using this prefix and interface ID from link-local address. */ + netif_ip6_addr_set_parts(prefix_list[i].netif, j, + prefix_list[i].prefix.addr[0], prefix_list[i].prefix.addr[1], + netif_ip6_addr(prefix_list[i].netif, 0)->addr[2], netif_ip6_addr(prefix_list[i].netif, 0)->addr[3]); + + /* Mark it as tentative (DAD will be performed if configured). */ + netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE); + + /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED; + + /* Exit loop. */ + break; + } + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + } + + + /* Process our own addresses, if DAD configured. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + u8_t addr_state = netif_ip6_addr_state(netif, i); + if (ip6_addr_istentative(addr_state)) { + if ((addr_state & IP6_ADDR_TENTATIVE_COUNT_MASK) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { + /* No NA received in response. Mark address as valid. */ + netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED); + /* @todo implement preferred and valid lifetimes. */ + } else if (netif->flags & NETIF_FLAG_UP) { + /* Send a NS for this address. */ + nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST); + /* tentative: set next state by increasing by one */ + netif_ip6_addr_set_state(netif, i, addr_state + 1); + /* @todo send max 1 NS per tmr call? enable return*/ + /*return;*/ + } + } + } + } + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send router solicitation messages, if necessary. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP) && + (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)))) { + if (nd6_send_rs(netif) == ERR_OK) { + netif->rs_count--; + } + } + } +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +} + +/** Send a neighbor solicitation message for a specific neighbor cache entry + * + * @param entry the neightbor cache entry for wich to send the message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags) +{ + nd6_send_ns(entry->netif, &entry->next_hop_address, flags); +} + +/** + * Send a neighbor solicitation message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) +{ + struct ns_header *ns_hdr; + struct pbuf *p; + const ip6_addr_t *src_addr; + u16_t lladdr_opt_len; + + if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(netif, 0); + /* calculate option length (in 8-byte-blocks) */ + lladdr_opt_len = ((netif->hwaddr_len + 2) + 7) >> 3; + } else { + src_addr = IP6_ADDR_ANY6; + /* Option "MUST NOT be included when the source IP address is the unspecified address." */ + lladdr_opt_len = 0; + } + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + (lladdr_opt_len << 3), PBUF_RAM); + if (p == NULL) { + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + ns_hdr = (struct ns_header *)p->payload; + + ns_hdr->type = ICMP6_TYPE_NS; + ns_hdr->code = 0; + ns_hdr->chksum = 0; + ns_hdr->reserved = 0; + ip6_addr_set(&(ns_hdr->target_address), target_addr); + + if (lladdr_opt_len != 0) { + struct lladdr_option *lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = (u8_t)lladdr_opt_len; + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + } + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + target_addr = &multicast_address; + } + +#if CHECKSUM_GEN_ICMP6 + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { + ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + target_addr); + } +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, target_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +/** + * Send a neighbor advertisement message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) +{ + struct na_header *na_hdr; + struct lladdr_option *lladdr_opt; + struct pbuf *p; + const ip6_addr_t *src_addr; + const ip6_addr_t *dest_addr; + u16_t lladdr_opt_len; + + /* Use link-local address as source address. */ + /* src_addr = netif_ip6_addr(netif, 0); */ + /* Use target address as source address. */ + src_addr = target_addr; + + /* Allocate a packet. */ + lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + (lladdr_opt_len << 3), PBUF_RAM); + if (p == NULL) { + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + na_hdr = (struct na_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + na_hdr->type = ICMP6_TYPE_NA; + na_hdr->code = 0; + na_hdr->chksum = 0; + na_hdr->flags = flags & 0xf0; + na_hdr->reserved[0] = 0; + na_hdr->reserved[1] = 0; + na_hdr->reserved[2] = 0; + ip6_addr_set(&(na_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; + lladdr_opt->length = (u8_t)lladdr_opt_len; + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + dest_addr = &multicast_address; + } else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { + ip6_addr_set_allnodes_linklocal(&multicast_address); + dest_addr = &multicast_address; + } else { + dest_addr = ip6_current_src_addr(); + } + +#if CHECKSUM_GEN_ICMP6 + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { + na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + dest_addr); + } +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, dest_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +/** + * Send a router solicitation message + * + * @param netif the netif on which to send the message + */ +static err_t +nd6_send_rs(struct netif *netif) +{ + struct rs_header *rs_hdr; + struct lladdr_option *lladdr_opt; + struct pbuf *p; + const ip6_addr_t *src_addr; + err_t err; + u16_t lladdr_opt_len = 0; + + /* Link-local source address, or unspecified address? */ + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { + src_addr = netif_ip6_addr(netif, 0); + } else { + src_addr = IP6_ADDR_ANY6; + } + + /* Generate the all routers target address. */ + ip6_addr_set_allrouters_linklocal(&multicast_address); + + /* Allocate a packet. */ + if (src_addr != IP6_ADDR_ANY6) { + lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + } + p = pbuf_alloc(PBUF_IP, sizeof(struct rs_header) + (lladdr_opt_len << 3), PBUF_RAM); + if (p == NULL) { + ND6_STATS_INC(nd6.memerr); + return ERR_BUF; + } + + /* Set fields. */ + rs_hdr = (struct rs_header *)p->payload; + + rs_hdr->type = ICMP6_TYPE_RS; + rs_hdr->code = 0; + rs_hdr->chksum = 0; + rs_hdr->reserved = 0; + + if (src_addr != IP6_ADDR_ANY6) { + /* Include our hw address. */ + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header)); + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = (u8_t)lladdr_opt_len; + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + } + +#if CHECKSUM_GEN_ICMP6 + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { + rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + &multicast_address); + } +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + + err = ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, &multicast_address, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); + + return err; +} +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +/** + * Search for a neighbor cache entry + * + * @param ip6addr the IPv6 address of the neighbor + * @return The neighbor cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) { + return i; + } + } + return -1; +} + +/** + * Create a new neighbor cache entry. + * + * If no unused entry is found, will try to recycle an old entry + * according to ad-hoc "age" heuristic. + * + * @return The neighbor cache entry index that was created, -1 if no + * entry could be created + */ +static s8_t +nd6_new_neighbor_cache_entry(void) +{ + s8_t i; + s8_t j; + u32_t time; + + + /* First, try to find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (neighbor_cache[i].state == ND6_NO_ENTRY) { + return i; + } + } + + /* We need to recycle an entry. in general, do not recycle if it is a router. */ + + /* Next, try to find a Stale entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_STALE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Probe entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_PROBE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Delayed entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_DELAY) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find the oldest reachable entry. */ + time = 0xfffffffful; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_REACHABLE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.reachable_time < time) { + j = i; + time = neighbor_cache[i].counter.reachable_time; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry without queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ( + (neighbor_cache[i].q == NULL) && + (neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry with queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* No more entries to try. */ + return -1; +} + +/** + * Will free any resources associated with a neighbor cache + * entry, and will mark it as unused. + * + * @param i the neighbor cache entry index to free + */ +static void +nd6_free_neighbor_cache_entry(s8_t i) +{ + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + if (neighbor_cache[i].isrouter) { + /* isrouter needs to be cleared before deleting a neighbor cache entry */ + return; + } + + /* Free any queued packets. */ + if (neighbor_cache[i].q != NULL) { + nd6_free_q(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } + + neighbor_cache[i].state = ND6_NO_ENTRY; + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = NULL; + neighbor_cache[i].counter.reachable_time = 0; + ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address)); +} + +/** + * Search for a destination cache entry + * + * @param ip6addr the IPv6 address of the destination + * @return The destination cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { + return i; + } + } + return -1; +} + +/** + * Create a new destination cache entry. If no unused entry is found, + * will recycle oldest entry. + * + * @return The destination cache entry index that was created, -1 if no + * entry was created + */ +static s8_t +nd6_new_destination_cache_entry(void) +{ + s8_t i, j; + u32_t age; + + /* Find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_isany(&(destination_cache[i].destination_addr))) { + return i; + } + } + + /* Find oldest entry. */ + age = 0; + j = LWIP_ND6_NUM_DESTINATIONS - 1; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (destination_cache[i].age > age) { + j = i; + } + } + + return j; +} + +/** + * Clear the destination cache. + * + * This operation may be necessary for consistency in the light of changing + * local addresses and/or use of the gateway hook. + */ +void +nd6_clear_destination_cache(void) +{ + int i; + + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + ip6_addr_set_any(&destination_cache[i].destination_addr); + } +} + +/** + * Determine whether an address matches an on-link prefix. + * + * @param ip6addr the IPv6 address to match + * @return 1 if the address is on-link, 0 otherwise + */ +static s8_t +nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if ((prefix_list[i].netif == netif) && + (prefix_list[i].invalidation_timer > 0) && + ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) { + return 1; + } + } + /* Check to see if address prefix matches a (manually?) configured address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { + return 1; + } + } + return 0; +} + +/** + * Select a default router for a destination. + * + * @param ip6addr the destination address + * @param netif the netif for the outgoing packet, if known + * @return the default router entry index, or -1 if no suitable + * router is found + */ +static s8_t +nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif) +{ + s8_t i; + /* last_router is used for round-robin router selection (as recommended + * in RFC). This is more robust in case one router is not reachable, + * we are not stuck trying to resolve it. */ + static s8_t last_router; + (void)ip6addr; /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */ + + /* @todo: implement default router preference */ + + /* Look for reachable routers. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0) && + (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) { + return i; + } + } + + /* Look for router in other reachability states, but still valid according to timer. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0)) { + return i; + } + } + + /* Look for any router for which we have any information at all. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if (default_router_list[i].neighbor_entry != NULL && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) { + return i; + } + } + + /* no suitable router found. */ + return -1; +} + +/** + * Find a router-announced route to the given destination. + * + * The caller is responsible for checking whether the returned netif, if any, + * is in a suitable state (up, link up) to be used for packet transmission. + * + * @param ip6addr the destination IPv6 address + * @return the netif to use for the destination, or NULL if none found + */ +struct netif * +nd6_find_route(const ip6_addr_t *ip6addr) +{ + s8_t i; + + i = nd6_select_router(ip6addr, NULL); + if (i >= 0) { + if (default_router_list[i].neighbor_entry != NULL) { + return default_router_list[i].neighbor_entry->netif; /* may be NULL */ + } + } + + return NULL; +} + +/** + * Find an entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is found, if known + * @return the index of the router entry, or -1 if not found + */ +static s8_t +nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif) +{ + s8_t i; + + /* Look for router. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if ((default_router_list[i].neighbor_entry != NULL) && + ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) && + ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) { + return i; + } + } + + /* router not found. */ + return -1; +} + +/** + * Create a new entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is connected, if known + * @return the index on the router table, or -1 if could not be created + */ +static s8_t +nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif) +{ + s8_t router_index; + s8_t free_router_index; + s8_t neighbor_index; + + /* Do we have a neighbor entry for this router? */ + neighbor_index = nd6_find_neighbor_cache_entry(router_addr); + if (neighbor_index < 0) { + /* Create a neighbor entry for this router. */ + neighbor_index = nd6_new_neighbor_cache_entry(); + if (neighbor_index < 0) { + /* Could not create neighbor entry for this router. */ + return -1; + } + ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr); + neighbor_cache[neighbor_index].netif = netif; + neighbor_cache[neighbor_index].q = NULL; + neighbor_cache[neighbor_index].state = ND6_INCOMPLETE; + neighbor_cache[neighbor_index].counter.probes_sent = 1; + nd6_send_neighbor_cache_probe(&neighbor_cache[neighbor_index], ND6_SEND_FLAG_MULTICAST_DEST); + } + + /* Mark neighbor as router. */ + neighbor_cache[neighbor_index].isrouter = 1; + + /* Look for empty entry. */ + free_router_index = LWIP_ND6_NUM_ROUTERS; + for (router_index = LWIP_ND6_NUM_ROUTERS - 1; router_index >= 0; router_index--) { + /* check if router already exists (this is a special case for 2 netifs on the same subnet + - e.g. wifi and cable) */ + if(default_router_list[router_index].neighbor_entry == &(neighbor_cache[neighbor_index])){ + return router_index; + } + if (default_router_list[router_index].neighbor_entry == NULL) { + /* remember lowest free index to create a new entry */ + free_router_index = router_index; + } + } + if (free_router_index < LWIP_ND6_NUM_ROUTERS) { + default_router_list[free_router_index].neighbor_entry = &(neighbor_cache[neighbor_index]); + return free_router_index; + } + + /* Could not create a router entry. */ + + /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */ + neighbor_cache[neighbor_index].isrouter = 0; + + /* router not found. */ + return -1; +} + +/** + * Find the cached entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not found + */ +static s8_t +nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif) +{ + s8_t i; + + /* Look for prefix in list. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) && + (prefix_list[i].netif == netif)) { + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Creates a new entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not created + */ +static s8_t +nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif) +{ + s8_t i; + + /* Create new entry. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((prefix_list[i].netif == NULL) || + (prefix_list[i].invalidation_timer == 0)) { + /* Found empty prefix entry. */ + prefix_list[i].netif = netif; + ip6_addr_set(&(prefix_list[i].prefix), prefix); +#if LWIP_IPV6_AUTOCONFIG + prefix_list[i].flags = 0; +#endif /* LWIP_IPV6_AUTOCONFIG */ + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Determine the next hop for a destination. Will determine if the + * destination is on-link, else a suitable on-link router is selected. + * + * The last entry index is cached for fast entry search. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the neighbor cache entry for the next hop, ERR_RTE if no + * suitable next hop was found, ERR_MEM if no cache entry + * could be created + */ +static s8_t +nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif) +{ +#ifdef LWIP_HOOK_ND6_GET_GW + const ip6_addr_t *next_hop_addr; +#endif /* LWIP_HOOK_ND6_GET_GW */ + s8_t i; + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t addr_hint = *(netif->addr_hint); + if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) { + nd6_cached_destination_index = addr_hint; + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look for ip6addr in destination cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + /* the cached entry index is the right one! */ + /* do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + /* Search destination cache. */ + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + /* found destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } else { + /* Not found. Create a new destination entry. */ + i = nd6_new_destination_cache_entry(); + if (i >= 0) { + /* got new destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } else { + /* Could not create a destination cache entry. */ + return ERR_MEM; + } + + /* Copy dest address to destination cache. */ + ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr); + + /* Now find the next hop. is it a neighbor? */ + if (ip6_addr_islinklocal(ip6addr) || + nd6_is_prefix_in_netif(ip6addr, netif)) { + /* Destination in local link. */ + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr); +#ifdef LWIP_HOOK_ND6_GET_GW + } else if ((next_hop_addr = LWIP_HOOK_ND6_GET_GW(netif, ip6addr)) != NULL) { + /* Next hop for destination provided by hook function. */ + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; + ip6_addr_set(&destination_cache[nd6_cached_destination_index].next_hop_addr, next_hop_addr); +#endif /* LWIP_HOOK_ND6_GET_GW */ + } else { + /* We need to select a router. */ + i = nd6_select_router(ip6addr, netif); + if (i < 0) { + /* No router found. */ + ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr)); + return ERR_RTE; + } + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */ + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address); + } + } + } + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + *(netif->addr_hint) = nd6_cached_destination_index; + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look in neighbor cache for the next-hop address. */ + if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr), + &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + /* Cache hit. */ + /* Do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr)); + if (i >= 0) { + /* Found a matching record, make it new cached entry. */ + nd6_cached_neighbor_index = i; + } else { + /* Neighbor not in cache. Make a new entry. */ + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + /* got new neighbor entry. make it our new cached index. */ + nd6_cached_neighbor_index = i; + } else { + /* Could not create a neighbor cache entry. */ + return ERR_MEM; + } + + /* Initialize fields. */ + ip6_addr_copy(neighbor_cache[i].next_hop_address, + destination_cache[nd6_cached_destination_index].next_hop_addr); + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = netif; + neighbor_cache[i].state = ND6_INCOMPLETE; + neighbor_cache[i].counter.probes_sent = 1; + nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST); + } + } + + /* Reset this destination's age. */ + destination_cache[nd6_cached_destination_index].age = 0; + + return nd6_cached_neighbor_index; +} + +/** + * Queue a packet for a neighbor. + * + * @param neighbor_index the index in the neighbor cache table + * @param q packet to be queued + * @return ERR_OK if succeeded, ERR_MEM if out of memory + */ +static err_t +nd6_queue_packet(s8_t neighbor_index, struct pbuf *q) +{ + err_t result = ERR_MEM; + struct pbuf *p; + int copy_needed = 0; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *new_entry, *r; +#endif /* LWIP_ND6_QUEUEING */ + + if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) { + return ERR_ARG; + } + + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + if (p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if (copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ +#if LWIP_ND6_QUEUEING + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); +#else /* LWIP_ND6_QUEUEING */ + pbuf_free(neighbor_cache[neighbor_index].q); + neighbor_cache[neighbor_index].q = NULL; +#endif /* LWIP_ND6_QUEUEING */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + } + if (p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet was copied/ref'd? */ + if (p != NULL) { + /* queue packet ... */ +#if LWIP_ND6_QUEUEING + /* allocate a new nd6 queue entry */ + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + } + if (new_entry != NULL) { + new_entry->next = NULL; + new_entry->p = p; + if (neighbor_cache[neighbor_index].q != NULL) { + /* queue was already existent, append the new entry to the end */ + r = neighbor_cache[neighbor_index].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + neighbor_cache[neighbor_index].q = new_entry; + } + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; + } else { + /* the pool MEMP_ND6_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p)); + /* { result == ERR_MEM } through initialization */ + } +#else /* LWIP_ND6_QUEUEING */ + /* Queue a single packet. If an older packet is already queued, free it as per RFC. */ + if (neighbor_cache[neighbor_index].q != NULL) { + pbuf_free(neighbor_cache[neighbor_index].q); + } + neighbor_cache[neighbor_index].q = p; + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; +#endif /* LWIP_ND6_QUEUEING */ + } else { + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } + + return result; +} + +#if LWIP_ND6_QUEUEING +/** + * Free a complete queue of nd6 q entries + * + * @param q a queue of nd6_q_entry to free + */ +static void +nd6_free_q(struct nd6_q_entry *q) +{ + struct nd6_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ND6_QUEUE, r); + } +} +#endif /* LWIP_ND6_QUEUEING */ + +/** + * Send queued packets for a neighbor + * + * @param i the neighbor to send packets to + */ +static void +nd6_send_q(s8_t i) +{ + struct ip6_hdr *ip6hdr; + ip6_addr_t dest; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *q; +#endif /* LWIP_ND6_QUEUEING */ + + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + +#if LWIP_ND6_QUEUEING + while (neighbor_cache[i].q != NULL) { + /* remember first in queue */ + q = neighbor_cache[i].q; + /* pop first item off the queue */ + neighbor_cache[i].q = q->next; + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(q->p->payload); + /* Create an aligned copy. */ + ip6_addr_set(&dest, &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, &dest); + /* free the queued IP packet */ + pbuf_free(q->p); + /* now queue entry can be freed */ + memp_free(MEMP_ND6_QUEUE, q); + } +#else /* LWIP_ND6_QUEUEING */ + if (neighbor_cache[i].q != NULL) { + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); + /* Create an aligned copy. */ + ip6_addr_set(&dest, &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, &dest); + /* free the queued IP packet */ + pbuf_free(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } +#endif /* LWIP_ND6_QUEUEING */ +} + +/** + * A packet is to be transmitted to a specific IPv6 destination on a specific + * interface. Check if we can find the hardware address of the next hop to use + * for the packet. If so, give the hardware address to the caller, which should + * use it to send the packet right away. Otherwise, enqueue the packet for + * later transmission while looking up the hardware address, if possible. + * + * As such, this function returns one of three different possible results: + * + * - ERR_OK with a non-NULL 'hwaddrp': the caller should send the packet now. + * - ERR_OK with a NULL 'hwaddrp': the packet has been enqueued for later. + * - not ERR_OK: something went wrong; forward the error upward in the stack. + * + * @param netif The lwIP network interface on which the IP packet will be sent. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ip6addr The destination IPv6 address of the packet. + * @param hwaddrp On success, filled with a pointer to a HW address or NULL (meaning + * the packet has been queued). + * @return + * - ERR_OK on success, ERR_RTE if no route was found for the packet, + * or ERR_MEM if low memory conditions prohibit sending the packet at all. + */ +err_t +nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp) +{ + s8_t i; + + /* Get next hop record. */ + i = nd6_get_next_hop_entry(ip6addr, netif); + if (i < 0) { + /* failed to get a next hop neighbor record. */ + return i; + } + + /* Now that we have a destination record, send or queue the packet. */ + if (neighbor_cache[i].state == ND6_STALE) { + /* Switch to delay state. */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; + } + /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */ + if ((neighbor_cache[i].state == ND6_REACHABLE) || + (neighbor_cache[i].state == ND6_DELAY) || + (neighbor_cache[i].state == ND6_PROBE)) { + + /* Tell the caller to send out the packet now. */ + *hwaddrp = neighbor_cache[i].lladdr; + return ERR_OK; + } + + /* We should queue packet on this interface. */ + *hwaddrp = NULL; + return nd6_queue_packet(i, q); +} + + +/** + * Get the Path MTU for a destination. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the Path MTU, if known, or the netif default MTU + */ +u16_t +nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif) +{ + s8_t i; + + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + if (destination_cache[i].pmtu > 0) { + return destination_cache[i].pmtu; + } + } + + if (netif != NULL) { + return netif->mtu; + } + + return 1280; /* Minimum MTU */ +} + + +#if LWIP_ND6_TCP_REACHABILITY_HINTS +/** + * Provide the Neighbor discovery process with a hint that a + * destination is reachable. Called by tcp_receive when ACKs are + * received or sent (as per RFC). This is useful to avoid sending + * NS messages every 30 seconds. + * + * @param ip6addr the destination address which is know to be reachable + * by an upper layer protocol (TCP) + */ +void +nd6_reachability_hint(const ip6_addr_t *ip6addr) +{ + s8_t i; + + /* Find destination in cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + i = nd6_cached_destination_index; + ND6_STATS_INC(nd6.cachehit); + } else { + i = nd6_find_destination_cache_entry(ip6addr); + } + if (i < 0) { + return; + } + + /* Find next hop neighbor in cache. */ + if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + i = nd6_cached_neighbor_index; + ND6_STATS_INC(nd6.cachehit); + } else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr)); + } + if (i < 0) { + return; + } + + /* For safety: don't set as reachable if we don't have a LL address yet. Misuse protection. */ + if (neighbor_cache[i].state == ND6_INCOMPLETE || neighbor_cache[i].state == ND6_NO_ENTRY) { + return; + } + + /* Set reachability state. */ + neighbor_cache[i].state = ND6_REACHABLE; + neighbor_cache[i].counter.reachable_time = reachable_time; +} +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +/** + * Remove all prefix, neighbor_cache and router entries of the specified netif. + * + * @param netif points to a network interface + */ +void +nd6_cleanup_netif(struct netif *netif) +{ + u8_t i; + s8_t router_index; + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if (prefix_list[i].netif == netif) { + prefix_list[i].netif = NULL; + prefix_list[i].flags = 0; + } + } + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (neighbor_cache[i].netif == netif) { + for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) { + if (default_router_list[router_index].neighbor_entry == &neighbor_cache[i]) { + default_router_list[router_index].neighbor_entry = NULL; + default_router_list[router_index].flags = 0; + } + } + neighbor_cache[i].isrouter = 0; + nd6_free_neighbor_cache_entry(i); + } + } +} + +#if LWIP_IPV6_MLD +/** + * The state of a local IPv6 address entry is about to change. If needed, join + * or leave the solicited-node multicast group for the address. + * + * @param netif The netif that owns the address. + * @param addr_idx The index of the address. + * @param new_state The new (IP6_ADDR_) state for the address. + */ +void +nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state) +{ + u8_t old_state, old_member, new_member; + + old_state = netif_ip6_addr_state(netif, addr_idx); + + /* Determine whether we were, and should be, a member of the solicited-node + * multicast group for this address. For tentative addresses, the group is + * not joined until the address enters the TENTATIVE_1 (or VALID) state. */ + old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_TENTATIVE); + new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_TENTATIVE); + + if (old_member != new_member) { + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]); + + if (new_member) { + mld6_joingroup_netif(netif, &multicast_address); + } else { + mld6_leavegroup_netif(netif, &multicast_address); + } + } +} +#endif /* LWIP_IPV6_MLD */ + +#endif /* LWIP_IPV6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/mem.c b/sdk/Src/Network/lwip2.0.3/core/mem.c new file mode 100644 index 0000000..e407458 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/mem.c @@ -0,0 +1,779 @@ +/** + * @file + * Dynamic memory manager + * + * This is a lightweight replacement for the standard C library malloc(). + * + * If you want to use the standard C library malloc() instead, define + * MEM_LIBC_MALLOC to 1 in your lwipopts.h + * + * To let mem_malloc() use pools (prevents fragmentation and is much faster than + * a heap but might waste some memory), define MEM_USE_POOLS to 1, define + * MEMP_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list + * of pools like this (more pools can be added between _START and _END): + * + * Define three pools with sizes 256, 512, and 1512 bytes + * LWIP_MALLOC_MEMPOOL_START + * LWIP_MALLOC_MEMPOOL(20, 256) + * LWIP_MALLOC_MEMPOOL(10, 512) + * LWIP_MALLOC_MEMPOOL(5, 1512) + * LWIP_MALLOC_MEMPOOL_END + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" +#include "lwip/mem.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/err.h" + +#include "wm_mem.h" + +#include + +#if MEM_LIBC_MALLOC +#include /* for malloc()/free() */ +#endif + +#if MEM_LIBC_MALLOC || MEM_USE_POOLS + +/** mem_init is not used when using pools instead of a heap or using + * C library malloc(). + */ +void +mem_init(void) +{ +} + +/** mem_trim is not used when using pools instead of a heap or using + * C library malloc(): we can't free part of a pool element and the stack + * support mem_trim() to return a different pointer + */ +void* +mem_trim(void *mem, mem_size_t size) +{ + LWIP_UNUSED_ARG(size); + return mem; +} +#endif /* MEM_LIBC_MALLOC || MEM_USE_POOLS */ + +#if MEM_LIBC_MALLOC +/* lwIP heap implemented using C library malloc() */ + +/* in case C library malloc() needs extra protection, + * allow these defines to be overridden. + */ +#ifndef mem_clib_free +#define mem_clib_free tls_mem_free +#endif +#ifndef mem_clib_malloc +#define mem_clib_malloc tls_mem_alloc +#endif +#ifndef mem_clib_calloc +#define mem_clib_calloc tls_mem_calloc +#endif + +#if LWIP_STATS && MEM_STATS +#define MEM_LIBC_STATSHELPER_SIZE LWIP_MEM_ALIGN_SIZE(sizeof(mem_size_t)) +#else +#define MEM_LIBC_STATSHELPER_SIZE 0 +#endif + +/** + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value must always be aligned (as defined by MEM_ALIGNMENT). + */ +void * +mem_malloc(mem_size_t size) +{ + void* ret = mem_clib_malloc(size + MEM_LIBC_STATSHELPER_SIZE); + if (ret == NULL) { + MEM_STATS_INC(err); + } else { + LWIP_ASSERT("malloc() must return aligned memory", LWIP_MEM_ALIGN(ret) == ret); +#if LWIP_STATS && MEM_STATS + *(mem_size_t*)ret = size; + ret = (u8_t*)ret + MEM_LIBC_STATSHELPER_SIZE; + MEM_STATS_INC_USED(used, size); +#endif + } + return ret; +} + +/** Put memory back on the heap + * + * @param rmem is the pointer as returned by a previous call to mem_malloc() + */ +void +mem_free(void *rmem) +{ + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); +#if LWIP_STATS && MEM_STATS + rmem = (u8_t*)rmem - MEM_LIBC_STATSHELPER_SIZE; + MEM_STATS_DEC_USED(used, *(mem_size_t*)rmem); +#endif + mem_clib_free(rmem); +} + +#elif MEM_USE_POOLS + +/* lwIP heap implemented with different sized pools */ + +/** + * Allocate memory: determine the smallest pool that is big enough + * to contain an element of 'size' and get an element from that pool. + * + * @param size the size in bytes of the memory needed + * @return a pointer to the allocated memory or NULL if the pool is empty + */ +void * +mem_malloc(mem_size_t size) +{ + void *ret; + struct memp_malloc_helper *element = NULL; + memp_t poolnr; + mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + + for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { + /* is this pool big enough to hold an element of the required size + plus a struct memp_malloc_helper that saves the pool this element came from? */ + if (required_size <= memp_pools[poolnr]->size) { + element = (struct memp_malloc_helper*)memp_malloc(poolnr); + if (element == NULL) { + /* No need to DEBUGF or ASSERT: This error is already taken care of in memp.c */ +#if MEM_USE_POOLS_TRY_BIGGER_POOL + /** Try a bigger pool if this one is empty! */ + if (poolnr < MEMP_POOL_LAST) { + continue; + } +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + MEM_STATS_INC(err); + return NULL; + } + break; + } + } + if (poolnr > MEMP_POOL_LAST) { + LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); + MEM_STATS_INC(err); + return NULL; + } + + /* save the pool number this element came from */ + element->poolnr = poolnr; + /* and return a pointer to the memory directly after the struct memp_malloc_helper */ + ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + +#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) + /* truncating to u16_t is safe because struct memp_desc::size is u16_t */ + element->size = (u16_t)size; + MEM_STATS_INC_USED(used, element->size); +#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */ +#if MEMP_OVERFLOW_CHECK + /* initialize unused memory (diff between requested size and selected pool's size) */ + memset((u8_t*)ret + size, 0xcd, memp_pools[poolnr]->size - size); +#endif /* MEMP_OVERFLOW_CHECK */ + return ret; +} + +/** + * Free memory previously allocated by mem_malloc. Loads the pool number + * and calls memp_free with that pool number to put the element back into + * its pool + * + * @param rmem the memory element to free + */ +void +mem_free(void *rmem) +{ + struct memp_malloc_helper *hmem; + + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); + + /* get the original struct memp_malloc_helper */ + /* cast through void* to get rid of alignment warnings */ + hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); + + LWIP_ASSERT("hmem != NULL", (hmem != NULL)); + LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); + LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); + + MEM_STATS_DEC_USED(used, hmem->size); +#if MEMP_OVERFLOW_CHECK + { + u16_t i; + LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size", + hmem->size <= memp_pools[hmem->poolnr]->size); + /* check that unused memory remained untouched (diff between requested size and selected pool's size) */ + for (i = hmem->size; i < memp_pools[hmem->poolnr]->size; i++) { + u8_t data = *((u8_t*)rmem + i); + LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd); + } + } +#endif /* MEMP_OVERFLOW_CHECK */ + + /* and put it in the pool we saved earlier */ + memp_free(hmem->poolnr, hmem); +} + +#else /* MEM_USE_POOLS */ +/* lwIP replacement for your libc malloc() */ + +/** + * The heap is made up as a list of structs of this type. + * This does not have to be aligned since for getting its size, + * we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns. + */ +struct mem { + /** index (-> ram[next]) of the next struct */ + mem_size_t next; + /** index (-> ram[prev]) of the previous struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ + u8_t used; +}; + +/** All allocated blocks will be MIN_SIZE bytes big, at least! + * MIN_SIZE can be overridden to suit your needs. Smaller values save space, + * larger values could prevent too small blocks to fragment the RAM too much. */ +#ifndef MIN_SIZE +#define MIN_SIZE 12 +#endif /* MIN_SIZE */ +/* some alignment macros: we define them here for better source code layout */ +#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE) +#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) +#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + +/** If you want to relocate the heap to external memory, simply define + * LWIP_RAM_HEAP_POINTER as a void-pointer to that location. + * If so, make sure the memory at that location is big enough (see below on + * how that space is calculated). */ +#ifndef LWIP_RAM_HEAP_POINTER +/** the heap. we need one struct mem at the end and some room for alignment */ +LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U*SIZEOF_STRUCT_MEM)); +#define LWIP_RAM_HEAP_POINTER ram_heap +#endif /* LWIP_RAM_HEAP_POINTER */ + +/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ +static u8_t *ram; +/** the last entry, always unused! */ +static struct mem *ram_end; +/** pointer to the lowest free block, this is used for faster search */ +static struct mem *lfree; + +/** concurrent access protection */ +#if !NO_SYS +static sys_mutex_t mem_mutex; +#endif + +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + +static volatile u8_t mem_free_count; + +/* Allow mem_free from other (e.g. interrupt) context */ +#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) +#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) +#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) +#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) + +#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +/* Protect the heap only by using a semaphore */ +#define LWIP_MEM_FREE_DECL_PROTECT() +#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex) +#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex) +/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ +#define LWIP_MEM_ALLOC_DECL_PROTECT() +#define LWIP_MEM_ALLOC_PROTECT() +#define LWIP_MEM_ALLOC_UNPROTECT() + +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + +/** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist + * one empty struct mem pointing to another empty struct mem. + * + * @param mem this points to a struct mem which just has been freed + * @internal this function is only called by mem_free() and mem_trim() + * + * This assumes access to the heap is protected by the calling function + * already. + */ +static void +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); + LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); + LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); + + nmem = (struct mem *)(void *)&ram[mem->next]; + if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + /* if mem->next is unused and not end of ram, combine mem and mem->next */ + if (lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram); + } + + /* plug hole backward */ + pmem = (struct mem *)(void *)&ram[mem->prev]; + if (pmem != mem && pmem->used == 0) { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram); + } +} + +/** + * Zero the heap and initialize start, end and lowest-free + */ +void +mem_init(void) +{ + struct mem *mem; + + LWIP_ASSERT("Sanity check alignment", + (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); + + /* align the heap */ + ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER); + /* initialize the start of the heap */ + mem = (struct mem *)(void *)ram; + mem->next = MEM_SIZE_ALIGNED; + mem->prev = 0; + mem->used = 0; + /* initialize the end of the heap */ + ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED]; + ram_end->used = 1; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)(void *)ram; + + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); + + if (sys_mutex_new(&mem_mutex) != 0) { + LWIP_ASSERT("failed to create mem_mutex", 0); + } +} + +/** + * Put a struct mem back on the heap + * + * @param rmem is the data portion of a struct mem as returned by a previous + * call to mem_malloc() + */ +void +mem_free(void *rmem) +{ + struct mem *mem; + LWIP_MEM_FREE_DECL_PROTECT(); + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n")); + return; + } + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); + + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return; + } + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ + /* cast through void* to get rid of alignment warnings */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ + LWIP_ASSERT("mem_free: mem->used", mem->used); + /* ... and is now unused. */ + mem->used = 0; + + if (mem < lfree) { + /* the newly freed struct is now the lowest */ + lfree = mem; + } + + MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram))); + + /* finally, see if prev or next are free also */ + plug_holes(mem); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); +} + +/** + * Shrink memory returned by mem_malloc(). + * + * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment + * or NULL if newsize is > old size, in which case rmem is NOT touched + * or freed! + */ +void * +mem_trim(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ + LWIP_MEM_FREE_DECL_PROTECT(); + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + newsize = LWIP_MEM_ALIGN_SIZE(newsize); + + if (newsize < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + newsize = MIN_SIZE_ALIGNED; + } + + if (newsize > MEM_SIZE_ALIGNED) { + return NULL; + } + + LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return rmem; + } + /* Get the corresponding struct mem ... */ + /* cast through void* to get rid of alignment warnings */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... and its offset pointer */ + ptr = (mem_size_t)((u8_t *)mem - ram); + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size); + if (newsize > size) { + /* not supported */ + return NULL; + } + if (newsize == size) { + /* No change in size, simply return */ + return rmem; + } + + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + + mem2 = (struct mem *)(void *)&ram[mem->next]; + if (mem2->used == 0) { + /* The next struct is unused, we can simply move it at little */ + mem_size_t next; + /* remember the old next pointer */ + next = mem2->next; + /* create new struct mem which is moved directly after the shrinked mem */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + if (lfree == mem2) { + lfree = (struct mem *)(void *)&ram[ptr2]; + } + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + /* restore the next pointer */ + mem2->next = next; + /* link it back to mem */ + mem2->prev = ptr; + /* link mem to it */ + mem->next = ptr2; + /* last thing to restore linked list: as we have moved mem2, + * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not + * the end of the heap */ + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* no need to plug holes, we've already done that */ + } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { + /* Next struct is used but there's room for another struct mem with + * at least MIN_SIZE_ALIGNED of data. + * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem + * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)(void *)&ram[ptr2]; + if (mem2 < lfree) { + lfree = mem2; + } + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* the original mem->next is used, so no need to plug holes! */ + } + /* else { + next struct mem is used but size between mem and mem2 is not big enough + to create another struct mem + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); + return rmem; +} + +/** + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). + */ +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + u8_t local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_ALLOC_DECL_PROTECT(); + + if (size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + size = LWIP_MEM_ALIGN_SIZE(size); + + if (size < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + size = MIN_SIZE_ALIGNED; + } + + if (size > MEM_SIZE_ALIGNED) { + return NULL; + } + + /* protect the heap from concurrent access */ + sys_mutex_lock(&mem_mutex); + LWIP_MEM_ALLOC_PROTECT(); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* run as long as a mem_free disturbed mem_malloc or mem_trim */ + do { + local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + /* Scan through the heap searching for a free block that is big enough, + * beginning with the lowest free block. + */ + for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)(void *)&ram[ptr])->next) { + mem = (struct mem *)(void *)&ram[ptr]; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* allow mem_free or mem_trim to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem. */ + local_mem_free_count = 1; + break; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { + /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + /* create mem2 struct */ + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); + } else { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or exact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; + MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram)); + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +mem_malloc_adjust_lfree: +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + if (mem == lfree) { + struct mem *cur = lfree; + /* Find next free block after mem and update lowest free pointer */ + while (cur->used && cur != ram_end) { +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem or lfree. */ + goto mem_malloc_adjust_lfree; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + cur = (struct mem *)(void *)&ram[cur->next]; + } + lfree = cur; + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + LWIP_ASSERT("mem_malloc: sanity check alignment", + (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + + return (u8_t *)mem + SIZEOF_STRUCT_MEM; + } + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* if we got interrupted by a mem_free, try again */ + } while (local_mem_free_count != 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); + MEM_STATS_INC(err); + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + return NULL; +} + +#endif /* MEM_USE_POOLS */ + +#if MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) +void * +mem_calloc(mem_size_t count, mem_size_t size) +{ + return mem_clib_calloc(count, size); +} + +#else /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */ +/** + * Contiguously allocates enough space for count objects that are size bytes + * of memory each and returns a pointer to the allocated memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void * +mem_calloc(mem_size_t count, mem_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = mem_malloc(count * size); + if (p) { + /* zero the memory */ + memset(p, 0, (size_t)count * (size_t)size); + } + return p; +} +#endif /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */ diff --git a/sdk/Src/Network/lwip2.0.3/core/memp.c b/sdk/Src/Network/lwip2.0.3/core/memp.c new file mode 100644 index 0000000..727a0c2 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/memp.c @@ -0,0 +1,496 @@ +/** + * @file + * Dynamic pool memory manager + * + * lwIP has dedicated pools for many structures (netconn, protocol control blocks, + * packet buffers, ...). All these pools are managed here. + * + * @defgroup mempool Memory pools + * @ingroup infrastructure + * Custom memory pools + + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/memp.h" +#include "lwip/sys.h" +#include "lwip/stats.h" + +#include + +/* Make sure we include everything we need for size calculation required by memp_std.h */ +#include "lwip/pbuf.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/ip4_frag.h" +#include "lwip/netbuf.h" +#include "lwip/api.h" +#include "lwip/priv/tcpip_priv.h" +#include "lwip/priv/api_msg.h" +#include "lwip/sockets.h" +#include "lwip/netifapi.h" +#include "lwip/etharp.h" +#include "lwip/igmp.h" +#include "lwip/timeouts.h" +/* needed by default MEMP_NUM_SYS_TIMEOUT */ +#include "netif/ppp/ppp_opts.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" +#include "lwip/priv/nd6_priv.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" + +#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc) +#include "lwip/priv/memp_std.h" + +const struct memp_desc* const memp_pools[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name, +#include "lwip/priv/memp_std.h" +}; + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +#if MEMP_MEM_MALLOC && MEMP_OVERFLOW_CHECK >= 2 +#undef MEMP_OVERFLOW_CHECK +/* MEMP_OVERFLOW_CHECK >= 2 does not work with MEMP_MEM_MALLOC, use 1 instead */ +#define MEMP_OVERFLOW_CHECK 1 +#endif + +#if MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC +/** + * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm". + */ +static int +memp_sanity(const struct memp_desc *desc) +{ + struct memp *t, *h; + + t = *desc->tab; + if (t != NULL) { + for (h = t->next; (t != NULL) && (h != NULL); t = t->next, + h = ((h->next != NULL) ? h->next->next : NULL)) { + if (t == h) { + return 0; + } + } + } + + return 1; +} +#endif /* MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC */ + +#if MEMP_OVERFLOW_CHECK +/** + * Check if a memp element was victim of an overflow + * (e.g. the restricted area after it has been altered) + * + * @param p the memp element to check + * @param desc the pool p comes from + */ +static void +memp_overflow_check_element_overflow(struct memp *p, const struct memp_desc *desc) +{ +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + u16_t k; + u8_t *m; + m = (u8_t*)p + MEMP_SIZE + desc->size; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp overflow in pool "; + strcat(errstr, desc->desc); + LWIP_ASSERT(errstr, 0); + } + } +#else /* MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(desc); +#endif /* MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */ +} + +/** + * Check if a memp element was victim of an underflow + * (e.g. the restricted area before it has been altered) + * + * @param p the memp element to check + * @param desc the pool p comes from + */ +static void +memp_overflow_check_element_underflow(struct memp *p, const struct memp_desc *desc) +{ +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + u16_t k; + u8_t *m; + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp underflow in pool "; + strcat(errstr, desc->desc); + LWIP_ASSERT(errstr, 0); + } + } +#else /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 */ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(desc); +#endif /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 */ +} + +/** + * Initialize the restricted area of on memp element. + */ +static void +memp_overflow_init_element(struct memp *p, const struct memp_desc *desc) +{ +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + u8_t *m; +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + desc->size; + memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); +#endif +#else /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(desc); +#endif /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */ +} + +#if MEMP_OVERFLOW_CHECK >= 2 +/** + * Do an overflow check for all elements in every pool. + * + * @see memp_overflow_check_element for a description of the check + */ +static void +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + + for (i = 0; i < MEMP_MAX; ++i) { + p = (struct memp*)LWIP_MEM_ALIGN(memp_pools[i]->base); + for (j = 0; j < memp_pools[i]->num; ++j) { + memp_overflow_check_element_overflow(p, memp_pools[i]); + memp_overflow_check_element_underflow(p, memp_pools[i]); + p = LWIP_ALIGNMENT_CAST(struct memp*, ((u8_t*)p + MEMP_SIZE + memp_pools[i]->size + MEMP_SANITY_REGION_AFTER_ALIGNED)); + } + } + SYS_ARCH_UNPROTECT(old_level); +} +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +#endif /* MEMP_OVERFLOW_CHECK */ + +/** + * Initialize custom memory pool. + * Related functions: memp_malloc_pool, memp_free_pool + * + * @param desc pool to initialize + */ +void +memp_init_pool(const struct memp_desc *desc) +{ +#if MEMP_MEM_MALLOC + LWIP_UNUSED_ARG(desc); +#else + int i; + struct memp *memp; + + *desc->tab = NULL; + memp = (struct memp*)LWIP_MEM_ALIGN(desc->base); + /* create a linked list of memp elements */ + for (i = 0; i < desc->num; ++i) { + memp->next = *desc->tab; + *desc->tab = memp; +#if MEMP_OVERFLOW_CHECK + memp_overflow_init_element(memp, desc); +#endif /* MEMP_OVERFLOW_CHECK */ + /* cast through void* to get rid of alignment warnings */ + memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size +#if MEMP_OVERFLOW_CHECK + + MEMP_SANITY_REGION_AFTER_ALIGNED +#endif + ); + } +#if MEMP_STATS + desc->stats->avail = desc->num; +#endif /* MEMP_STATS */ +#endif /* !MEMP_MEM_MALLOC */ + +#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) + desc->stats->name = desc->desc; +#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */ +} + +/** + * Initializes lwIP built-in pools. + * Related functions: memp_malloc, memp_free + * + * Carves out memp_memory into linked lists for each pool-type. + */ +void +memp_init(void) +{ + u16_t i; + + /* for every pool: */ + for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) { + memp_init_pool(memp_pools[i]); + +#if LWIP_STATS && MEMP_STATS + lwip_stats.memp[i] = memp_pools[i]->stats; +#endif + } + +#if MEMP_OVERFLOW_CHECK >= 2 + /* check everything a first time to see if it worked */ + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +} + +static void* +#if !MEMP_OVERFLOW_CHECK +do_memp_malloc_pool(const struct memp_desc *desc) +#else +do_memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int line) +#endif +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + +#if MEMP_MEM_MALLOC + memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size)); + SYS_ARCH_PROTECT(old_level); +#else /* MEMP_MEM_MALLOC */ + SYS_ARCH_PROTECT(old_level); + + memp = *desc->tab; +#endif /* MEMP_MEM_MALLOC */ + + if (memp != NULL) { +#if !MEMP_MEM_MALLOC +#if MEMP_OVERFLOW_CHECK == 1 + memp_overflow_check_element_overflow(memp, desc); + memp_overflow_check_element_underflow(memp, desc); +#endif /* MEMP_OVERFLOW_CHECK */ + + *desc->tab = memp->next; +#if MEMP_OVERFLOW_CHECK + memp->next = NULL; +#endif /* MEMP_OVERFLOW_CHECK */ +#endif /* !MEMP_MEM_MALLOC */ +#if MEMP_OVERFLOW_CHECK + memp->file = file; + memp->line = line; +#if MEMP_MEM_MALLOC + memp_overflow_init_element(memp, desc); +#endif /* MEMP_MEM_MALLOC */ +#endif /* MEMP_OVERFLOW_CHECK */ + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); +#if MEMP_STATS + desc->stats->used++; + if (desc->stats->used > desc->stats->max) { + desc->stats->max = desc->stats->used; + } +#endif + SYS_ARCH_UNPROTECT(old_level); + /* cast through u8_t* to get rid of alignment warnings */ + return ((u8_t*)memp + MEMP_SIZE); + } else { + LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc)); +#if MEMP_STATS + desc->stats->err++; +#endif + } + + SYS_ARCH_UNPROTECT(old_level); + return NULL; +} + +/** + * Get an element from a custom pool. + * + * @param desc the pool to get an element from + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ +void * +#if !MEMP_OVERFLOW_CHECK +memp_malloc_pool(const struct memp_desc *desc) +#else +memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int line) +#endif +{ + LWIP_ASSERT("invalid pool desc", desc != NULL); + if (desc == NULL) { + return NULL; + } + +#if !MEMP_OVERFLOW_CHECK + return do_memp_malloc_pool(desc); +#else + return do_memp_malloc_pool_fn(desc, file, line); +#endif +} + +/** + * Get an element from a specific pool. + * + * @param type the pool to get an element from + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ +void * +#if !MEMP_OVERFLOW_CHECK +memp_malloc(memp_t type) +#else +memp_malloc_fn(memp_t type, const char* file, const int line) +#endif +{ + void *memp; + LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); + +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ + +#if !MEMP_OVERFLOW_CHECK + memp = do_memp_malloc_pool(memp_pools[type]); +#else + memp = do_memp_malloc_pool_fn(memp_pools[type], file, line); +#endif + + return memp; +} + +static void +do_memp_free_pool(const struct memp_desc* desc, void *mem) +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ASSERT("memp_free: mem properly aligned", + ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); + + /* cast through void* to get rid of alignment warnings */ + memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE); + + SYS_ARCH_PROTECT(old_level); + +#if MEMP_OVERFLOW_CHECK == 1 + memp_overflow_check_element_overflow(memp, desc); + memp_overflow_check_element_underflow(memp, desc); +#endif /* MEMP_OVERFLOW_CHECK */ + +#if MEMP_STATS + desc->stats->used--; +#endif + +#if MEMP_MEM_MALLOC + LWIP_UNUSED_ARG(desc); + SYS_ARCH_UNPROTECT(old_level); + mem_free(memp); +#else /* MEMP_MEM_MALLOC */ + memp->next = *desc->tab; + *desc->tab = memp; + +#if MEMP_SANITY_CHECK + LWIP_ASSERT("memp sanity", memp_sanity(desc)); +#endif /* MEMP_SANITY_CHECK */ + + SYS_ARCH_UNPROTECT(old_level); +#endif /* !MEMP_MEM_MALLOC */ +} + +/** + * Put a custom pool element back into its pool. + * + * @param desc the pool where to put mem + * @param mem the memp element to free + */ +void +memp_free_pool(const struct memp_desc* desc, void *mem) +{ + LWIP_ASSERT("invalid pool desc", desc != NULL); + if ((desc == NULL) || (mem == NULL)) { + return; + } + + do_memp_free_pool(desc, mem); +} + +/** + * Put an element back into its pool. + * + * @param type the pool where to put mem + * @param mem the memp element to free + */ +void +memp_free(memp_t type, void *mem) +{ +#ifdef LWIP_HOOK_MEMP_AVAILABLE + struct memp *old_first; +#endif + + LWIP_ERROR("memp_free: type < MEMP_MAX", (type < MEMP_MAX), return;); + + if (mem == NULL) { + return; + } + +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ + +#ifdef LWIP_HOOK_MEMP_AVAILABLE + old_first = *memp_pools[type]->tab; +#endif + + do_memp_free_pool(memp_pools[type], mem); + +#ifdef LWIP_HOOK_MEMP_AVAILABLE + if (old_first == NULL) { + LWIP_HOOK_MEMP_AVAILABLE(type); + } +#endif +} diff --git a/sdk/Src/Network/lwip2.0.3/core/netif.c b/sdk/Src/Network/lwip2.0.3/core/netif.c new file mode 100644 index 0000000..3bf86b7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/netif.c @@ -0,0 +1,1274 @@ +/** + * @file + * lwIP network interface abstraction + * + * @defgroup netif Network interface (NETIF) + * @ingroup callbackstyle_api + * + * @defgroup netif_ip4 IPv4 address handling + * @ingroup netif + * + * @defgroup netif_ip6 IPv6 address handling + * @ingroup netif + * + * @defgroup netif_cd Client data handling + * Store data (void*) on a netif for application usage. + * @see @ref LWIP_NUM_NETIF_CLIENT_DATA + * @ingroup netif + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +#include "lwip/opt.h" + +#include + +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/udp.h" +#include "lwip/raw.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "lwip/etharp.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/ip.h" +#if ENABLE_LOOPBACK +#if LWIP_NETIF_LOOPBACK_MULTITHREADING +#include "lwip/tcpip.h" +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#include "netif/ethernet.h" + +#if LWIP_AUTOIP +#include "lwip/autoip.h" +#endif /* LWIP_AUTOIP */ +#if LWIP_DHCP +#include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_IPV6_MLD +#include "lwip/mld6.h" +#endif /* LWIP_IPV6_MLD */ +#if LWIP_IPV6 +#include "lwip/nd6.h" +#endif + + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0) +#else +#define NETIF_LINK_CALLBACK(n) +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; + +static u8_t netif_num; + +#if LWIP_NUM_NETIF_CLIENT_DATA > 0 +static u8_t netif_client_id; +#endif + +#define NETIF_REPORT_TYPE_IPV4 0x01 +#define NETIF_REPORT_TYPE_IPV6 0x02 +static void netif_issue_reports(struct netif* netif, u8_t report_type); + +#if LWIP_IPV6 +static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ + +#if LWIP_HAVE_LOOPIF +#if LWIP_IPV4 +static err_t netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t* addr); +#endif +#if LWIP_IPV6 +static err_t netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t* addr); +#endif + + +static struct netif loop_netif; + +/** + * Initialize a lwip network interface structure for a loopback interface + * + * @param netif the lwip network interface structure for this loopif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + */ +static err_t +netif_loopif_init(struct netif *netif) +{ + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ + MIB2_INIT_NETIF(netif, snmp_ifType_softwareLoopback, 0); + + netif->name[0] = 'l'; + netif->name[1] = 'o'; +#if LWIP_IPV4 + netif->output = netif_loop_output_ipv4; +#endif +#if LWIP_IPV6 + netif->output_ip6 = netif_loop_output_ipv6; +#endif +#if LWIP_LOOPIF_MULTICAST + netif->flags |= NETIF_FLAG_IGMP; +#endif + return ERR_OK; +} +#endif /* LWIP_HAVE_LOOPIF */ + +void +netif_init(void) +{ +#if LWIP_HAVE_LOOPIF +#if LWIP_IPV4 +#define LOOPIF_ADDRINIT &loop_ipaddr, &loop_netmask, &loop_gw, + ip4_addr_t loop_ipaddr, loop_netmask, loop_gw; + IP4_ADDR(&loop_gw, 127,0,0,1); + IP4_ADDR(&loop_ipaddr, 127,0,0,1); + IP4_ADDR(&loop_netmask, 255,0,0,0); +#else /* LWIP_IPV4 */ +#define LOOPIF_ADDRINIT +#endif /* LWIP_IPV4 */ + +#if NO_SYS + netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, ip_input); +#else /* NO_SYS */ + netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, tcpip_input); +#endif /* NO_SYS */ + +#if LWIP_IPV6 + IP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL); + loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID; +#endif /* LWIP_IPV6 */ + + netif_set_link_up(&loop_netif); + netif_set_up(&loop_netif); + +#endif /* LWIP_HAVE_LOOPIF */ +} + +/** + * @ingroup lwip_nosys + * Forwards a received packet for input processing with + * ethernet_input() or ip_input() depending on netif flags. + * Don't call directly, pass to netif_add() and call + * netif->input(). + * Only works if the netif driver correctly sets + * NETIF_FLAG_ETHARP and/or NETIF_FLAG_ETHERNET flag! + */ +err_t +netif_input(struct pbuf *p, struct netif *inp) +{ +#if LWIP_ETHERNET + if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { + return ethernet_input(p, inp); + } else +#endif /* LWIP_ETHERNET */ + return ip_input(p, inp); +} + +/** + * @ingroup netif + * Add a network interface to the list of lwIP netifs. + * + * @param netif a pre-allocated netif structure + * @param ipaddr IP address for the new netif + * @param netmask network mask for the new netif + * @param gw default gateway IP address for the new netif + * @param state opaque data passed to the new netif + * @param init callback function that initializes the interface + * @param input callback function that is called to pass + * ingress packets up in the protocol layer stack.\n + * It is recommended to use a function that passes the input directly + * to the stack (netif_input(), NO_SYS=1 mode) or via sending a + * message to TCPIP thread (tcpip_input(), NO_SYS=0 mode).\n + * These functions use netif flags NETIF_FLAG_ETHARP and NETIF_FLAG_ETHERNET + * to decide whether to forward to ethernet_input() or ip_input(). + * In other words, the functions only work when the netif + * driver is implemented correctly!\n + * Most members of struct netif should be be initialized by the + * netif init function = netif driver (init parameter of this function).\n + * IPv6: Don't forget to call netif_create_ip6_linklocal_address() after + * setting the MAC address in struct netif.hwaddr + * (IPv6 requires a link-local address). + * + * @return netif, or NULL if failed. + */ +struct netif * +netif_add(struct netif *netif, +#if LWIP_IPV4 + const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw, +#endif /* LWIP_IPV4 */ + void *state, netif_init_fn init, netif_input_fn input) +{ +#if LWIP_IPV6 + s8_t i; +#endif + + LWIP_ASSERT("No init function given", init != NULL); + + /* reset new interface configuration state */ +#if LWIP_IPV4 + ip_addr_set_zero_ip4(&netif->ip_addr); + ip_addr_set_zero_ip4(&netif->netmask); + ip_addr_set_zero_ip4(&netif->gw); +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + ip_addr_set_zero_ip6(&netif->ip6_addr[i]); + netif->ip6_addr_state[i] = IP6_ADDR_INVALID; + } + netif->output_ip6 = netif_null_output_ip6; +#endif /* LWIP_IPV6 */ + NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL); + netif->flags = 0; +#ifdef netif_get_client_data + memset(netif->client_data, 0, sizeof(netif->client_data)); +#endif /* LWIP_NUM_NETIF_CLIENT_DATA */ +#if LWIP_IPV6_AUTOCONFIG + /* IPv6 address autoconfiguration not enabled by default */ + netif->ip6_autoconfig_enabled = 0; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_NETIF_STATUS_CALLBACK + netif->status_callback = NULL; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + netif->link_callback = NULL; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_IGMP + netif->igmp_mac_filter = NULL; +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif->mld_mac_filter = NULL; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if ENABLE_LOOPBACK + netif->loop_first = NULL; + netif->loop_last = NULL; +#endif /* ENABLE_LOOPBACK */ + + /* remember netif specific state information data */ + netif->state = state; + netif->num = netif_num++; + netif->input = input; + + NETIF_SET_HWADDRHINT(netif, NULL); +#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS + netif->loop_cnt_current = 0; +#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ + +#if LWIP_IPV4 + netif_set_addr(netif, ipaddr, netmask, gw); +#endif /* LWIP_IPV4 */ + + /* call user specified initialization function for netif */ + if (init(netif) != ERR_OK) { + return NULL; + } + + /* add this netif to the list */ + netif->next = netif_list; + netif_list = netif; + mib2_netif_added(netif); + +#if LWIP_IGMP + /* start IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_start(netif); + } +#endif /* LWIP_IGMP */ + + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP", + netif->name[0], netif->name[1])); +#if LWIP_IPV4 + LWIP_DEBUGF(NETIF_DEBUG, (" addr ")); + ip4_addr_debug_print(NETIF_DEBUG, ipaddr); + LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); + ip4_addr_debug_print(NETIF_DEBUG, netmask); + LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); + ip4_addr_debug_print(NETIF_DEBUG, gw); +#endif /* LWIP_IPV4 */ + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + return netif; +} + +#if LWIP_IPV4 +/** + * @ingroup netif_ip4 + * Change IP address configuration for a network interface (including netmask + * and default gateway). + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * @param netmask the new netmask + * @param gw the new default gateway + */ +void +netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, + const ip4_addr_t *gw) +{ + if (ip4_addr_isany(ipaddr)) { + /* when removing an address, we have to remove it *before* changing netmask/gw + to ensure that tcp RST segment can be sent correctly */ + netif_set_ipaddr(netif, ipaddr); + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); + } else { + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); + /* set ipaddr last to ensure netmask/gw have been set when status callback is called */ + netif_set_ipaddr(netif, ipaddr); + } +} +#endif /* LWIP_IPV4*/ + +/** + * @ingroup netif + * Remove a network interface from the list of lwIP netifs. + * + * @param netif the network interface to remove + */ +void +netif_remove(struct netif *netif) +{ +#if LWIP_IPV6 + int i; +#endif + + if (netif == NULL) { + return; + } + +#if LWIP_IPV4 + if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) { +#if LWIP_TCP + tcp_netif_ip_addr_changed(netif_ip_addr4(netif), NULL); +#endif /* LWIP_TCP */ +#if LWIP_UDP + udp_netif_ip_addr_changed(netif_ip_addr4(netif), NULL); +#endif /* LWIP_UDP */ +#if LWIP_RAW + raw_netif_ip_addr_changed(netif_ip_addr4(netif), NULL); +#endif /* LWIP_RAW */ + } + +#if LWIP_IGMP + /* stop IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_stop(netif); + } +#endif /* LWIP_IGMP */ +#endif /* LWIP_IPV4*/ + +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) { +#if LWIP_TCP + tcp_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL); +#endif /* LWIP_TCP */ +#if LWIP_UDP + udp_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL); +#endif /* LWIP_UDP */ +#if LWIP_RAW + raw_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL); +#endif /* LWIP_RAW */ + } + } +#if LWIP_IPV6_MLD + /* stop MLD processing */ + mld6_stop(netif); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + if (netif_is_up(netif)) { + /* set netif down before removing (call callback function) */ + netif_set_down(netif); + } + + mib2_remove_ip4(netif); + + /* this netif is default? */ + if (netif_default == netif) { + /* reset default netif */ + netif_set_default(NULL); + } + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + } else { + /* look for netif further down the list */ + struct netif * tmp_netif; + for (tmp_netif = netif_list; tmp_netif != NULL; tmp_netif = tmp_netif->next) { + if (tmp_netif->next == netif) { + tmp_netif->next = netif->next; + break; + } + } + if (tmp_netif == NULL) { + return; /* netif is not on the list */ + } + } + mib2_netif_removed(netif); +#if LWIP_NETIF_REMOVE_CALLBACK + if (netif->remove_callback) { + netif->remove_callback(netif); + } +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); +} + +/** + * @ingroup netif + * Find a network interface by searching for its name + * + * @param name the name of the netif (like netif->name) plus concatenated number + * in ascii representation (e.g. 'en0') + */ +struct netif * +netif_find(const char *name) +{ + struct netif *netif; + u8_t num; + + if (name == NULL) { + return NULL; + } + + num = (u8_t)(name[2] - '0'); + + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1])); + return netif; + } + } + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1])); + return NULL; +} + +#if LWIP_IPV4 +/** + * @ingroup netif_ip4 + * Change the IP address of a network interface + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * + * @note call netif_set_addr() if you also want to change netmask and + * default gateway + */ +void +netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr) +{ + ip_addr_t new_addr; + *ip_2_ip4(&new_addr) = (ipaddr ? *ipaddr : *IP4_ADDR_ANY4); + IP_SET_TYPE_VAL(new_addr, IPADDR_TYPE_V4); + + /* address is actually being changed? */ + if (ip4_addr_cmp(ip_2_ip4(&new_addr), netif_ip4_addr(netif)) == 0) { + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); +#if LWIP_TCP + tcp_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr); +#endif /* LWIP_TCP */ +#if LWIP_UDP + udp_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr); +#endif /* LWIP_UDP */ +#if LWIP_RAW + raw_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr); +#endif /* LWIP_RAW */ + + mib2_remove_ip4(netif); + mib2_remove_route_ip4(0, netif); + /* set new IP address to netif */ + ip4_addr_set(ip_2_ip4(&netif->ip_addr), ipaddr); + IP_SET_TYPE_VAL(netif->ip_addr, IPADDR_TYPE_V4); + mib2_add_ip4(netif); + mib2_add_route_ip4(0, netif); + + netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4); + + NETIF_STATUS_CALLBACK(netif, NETIF_STATE_IPV4); + } + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(netif_ip4_addr(netif)), + ip4_addr2_16(netif_ip4_addr(netif)), + ip4_addr3_16(netif_ip4_addr(netif)), + ip4_addr4_16(netif_ip4_addr(netif)))); +} + +/** + * @ingroup netif_ip4 + * Change the default gateway for a network interface + * + * @param netif the network interface to change + * @param gw the new default gateway + * + * @note call netif_set_addr() if you also want to change ip address and netmask + */ +void +netif_set_gw(struct netif *netif, const ip4_addr_t *gw) +{ + ip4_addr_set(ip_2_ip4(&netif->gw), gw); + IP_SET_TYPE_VAL(netif->gw, IPADDR_TYPE_V4); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(netif_ip4_gw(netif)), + ip4_addr2_16(netif_ip4_gw(netif)), + ip4_addr3_16(netif_ip4_gw(netif)), + ip4_addr4_16(netif_ip4_gw(netif)))); +} + +/** + * @ingroup netif_ip4 + * Change the netmask of a network interface + * + * @param netif the network interface to change + * @param netmask the new netmask + * + * @note call netif_set_addr() if you also want to change ip address and + * default gateway + */ +void +netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask) +{ + mib2_remove_route_ip4(0, netif); + /* set new netmask to netif */ + ip4_addr_set(ip_2_ip4(&netif->netmask), netmask); + IP_SET_TYPE_VAL(netif->netmask, IPADDR_TYPE_V4); + mib2_add_route_ip4(0, netif); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(netif_ip4_netmask(netif)), + ip4_addr2_16(netif_ip4_netmask(netif)), + ip4_addr3_16(netif_ip4_netmask(netif)), + ip4_addr4_16(netif_ip4_netmask(netif)))); +} +#endif /* LWIP_IPV4 */ + +/** + * @ingroup netif + * Set a network interface as the default network interface + * (used to output all packets for which no specific route is found) + * + * @param netif the default network interface + */ +void +netif_set_default(struct netif *netif) +{ + if (netif == NULL) { + /* remove default route */ + mib2_remove_route_ip4(1, netif); + } else { + /* install default route */ + mib2_add_route_ip4(1, netif); + } + netif_default = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); +} + +/** + * @ingroup netif + * Bring an interface up, available for processing + * traffic. + */ +void +netif_set_up(struct netif *netif) +{ +//printf("netif_set_up netif %x, netif->flags %x\n", netif, netif->flags); + if (!(netif->flags & NETIF_FLAG_UP)) { + netif->flags |= NETIF_FLAG_UP; + + MIB2_COPY_SYSUPTIME_TO(&netif->ts); +#if 0 + NETIF_STATUS_CALLBACK(netif, NETIF_STATE_IPV4|NETIF_STATE_IPV6); +#else +#if LWIP_IPV6 + netif_create_ip6_linklocal_address(netif, 1); +#endif +#endif + + if (netif->flags & NETIF_FLAG_LINK_UP) { + netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6); + } + } +} + +/** Send ARP/IGMP/MLD/RS events, e.g. on link-up/netif-up or addr-change + */ +static void +netif_issue_reports(struct netif* netif, u8_t report_type) +{ +#if LWIP_IPV4 + if ((report_type & NETIF_REPORT_TYPE_IPV4) && + !ip4_addr_isany_val(*netif_ip4_addr(netif))) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & (NETIF_FLAG_ETHARP)) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups(netif); + } +#endif /* LWIP_IGMP */ + } +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 + if (report_type & NETIF_REPORT_TYPE_IPV6) { +#if LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups(netif); +#endif /* LWIP_IPV6_MLD */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send Router Solicitation messages. */ + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + } +#endif /* LWIP_IPV6 */ +} + +/** + * @ingroup netif + * Bring an interface down, disabling any traffic processing. + */ +void +netif_set_down(struct netif *netif) +{ + if (netif->flags & NETIF_FLAG_UP) { + netif->flags &= ~NETIF_FLAG_UP; + MIB2_COPY_SYSUPTIME_TO(&netif->ts); + + ip_addr_set_zero_ip4(&netif->ip_addr); + ip_addr_set_zero_ip4(&netif->netmask); + ip_addr_set_zero_ip4(&netif->gw); + +#if LWIP_IPV4 && LWIP_ARP + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_cleanup_netif(netif); + } +#endif /* LWIP_IPV4 && LWIP_ARP */ + +#if LWIP_IPV6 + nd6_cleanup_netif(netif); + netif_ip6_addr_set_state(netif, 0, IP6_ADDR_INVALID); +#endif /* LWIP_IPV6 */ + + NETIF_STATUS_CALLBACK(netif, NETIF_STATE_IPV4|NETIF_STATE_IPV6); + } +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * @ingroup netif + * Set callback to be called when interface is brought up/down or address is changed while up + */ +void +netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback) +{ + if (netif) { + netif->status_callback = status_callback; + } +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_REMOVE_CALLBACK +/** + * @ingroup netif + * Set callback to be called when the interface has been removed + */ +void +netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback) +{ + if (netif) { + netif->remove_callback = remove_callback; + } +} +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +/** + * @ingroup netif + * Called by a driver when its link goes up + */ +void +netif_set_link_up(struct netif *netif) +{ + if (!(netif->flags & NETIF_FLAG_LINK_UP)) { + netif->flags |= NETIF_FLAG_LINK_UP; + +#if LWIP_DHCP + dhcp_network_changed(netif); +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP + autoip_network_changed(netif); +#endif /* LWIP_AUTOIP */ + + if (netif->flags & NETIF_FLAG_UP) { + netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6); + } + NETIF_LINK_CALLBACK(netif); + } +} + +/** + * @ingroup netif + * Called by a driver when its link goes down + */ +void +netif_set_link_down(struct netif *netif ) +{ + if (netif->flags & NETIF_FLAG_LINK_UP) { + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); + } +} + +#if LWIP_NETIF_LINK_CALLBACK +/** + * @ingroup netif + * Set callback to be called when link is brought up/down + */ +void +netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback) +{ + if (netif) { + netif->link_callback = link_callback; + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if ENABLE_LOOPBACK +/** + * @ingroup netif + * Send an IP packet to be received on the same netif (loopif-like). + * The pbuf is simply copied and handed back to netif->input. + * In multithreaded mode, this is done directly since netif->input must put + * the packet on a queue. + * In callback mode, the packet is put on an internal queue and is fed to + * netif->input by netif_poll(). + * + * @param netif the lwip network interface structure + * @param p the (IP) packet to 'send' + * @return ERR_OK if the packet has been sent + * ERR_MEM if the pbuf used to copy the packet couldn't be allocated + */ +err_t +netif_loop_output(struct netif *netif, struct pbuf *p) +{ + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u16_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if MIB2_STATS +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* MIB2_STATS */ + SYS_ARCH_DECL_PROTECT(lev); + + /* Allocate a new pbuf */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards); + return ERR_MEM; + } +#if LWIP_LOOPBACK_MAX_PBUFS + clen = pbuf_clen(r); + /* check for overflow or too many pbuf on queue */ + if (((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || + ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards); + return ERR_MEM; + } + netif->loop_cnt_current += clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* Copy the whole pbuf queue p into the single pbuf r */ + if ((err = pbuf_copy(r, p)) != ERR_OK) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards); + return err; + } + + /* Put the packet on a linked list which gets emptied through calling + netif_poll(). */ + + /* let last point to the last pbuf in chain r */ + for (last = r; last->next != NULL; last = last->next) { + /* nothing to do here, just get to the last pbuf */ + } + + SYS_ARCH_PROTECT(lev); + if (netif->loop_first != NULL) { + LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); + netif->loop_last->next = r; + netif->loop_last = last; + } else { + netif->loop_first = r; + netif->loop_last = last; + } + SYS_ARCH_UNPROTECT(lev); + + LINK_STATS_INC(link.xmit); + MIB2_STATS_NETIF_ADD(stats_if, ifoutoctets, p->tot_len); + MIB2_STATS_NETIF_INC(stats_if, ifoutucastpkts); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback_with_block((tcpip_callback_fn)netif_poll, netif, 0); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; +} + +#if LWIP_HAVE_LOOPIF +#if LWIP_IPV4 +static err_t +netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t* addr) +{ + LWIP_UNUSED_ARG(addr); + return netif_loop_output(netif, p); +} +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +static err_t +netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t* addr) +{ + LWIP_UNUSED_ARG(addr); + return netif_loop_output(netif, p); +} +#endif /* LWIP_IPV6 */ +#endif /* LWIP_HAVE_LOOPIF */ + + +/** + * Call netif_poll() in the main loop of your application. This is to prevent + * reentering non-reentrant functions like tcp_input(). Packets passed to + * netif_loop_output() are put on a list that is passed to netif->input() by + * netif_poll(). + */ +void +netif_poll(struct netif *netif) +{ + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if MIB2_STATS +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* MIB2_STATS */ + SYS_ARCH_DECL_PROTECT(lev); + + /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ + SYS_ARCH_PROTECT(lev); + while (netif->loop_first != NULL) { + struct pbuf *in, *in_end; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 1; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + in = in_end = netif->loop_first; + while (in_end->len != in_end->tot_len) { + LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); + in_end = in_end->next; +#if LWIP_LOOPBACK_MAX_PBUFS + clen++; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + } +#if LWIP_LOOPBACK_MAX_PBUFS + /* adjust the number of pbufs on queue */ + LWIP_ASSERT("netif->loop_cnt_current underflow", + ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); + netif->loop_cnt_current -= clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* 'in_end' now points to the last pbuf from 'in' */ + if (in_end == netif->loop_last) { + /* this was the last pbuf in the list */ + netif->loop_first = netif->loop_last = NULL; + } else { + /* pop the pbuf off the list */ + netif->loop_first = in_end->next; + LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); + } + /* De-queue the pbuf from its successors on the 'loop_' list. */ + in_end->next = NULL; + SYS_ARCH_UNPROTECT(lev); + + LINK_STATS_INC(link.recv); + MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len); + MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts); + /* loopback packets are always IP packets! */ + if (ip_input(in, netif) != ERR_OK) { + pbuf_free(in); + } + SYS_ARCH_PROTECT(lev); + } + SYS_ARCH_UNPROTECT(lev); +} + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +/** + * Calls netif_poll() for every netif on the netif_list. + */ +void +netif_poll_all(void) +{ + struct netif *netif = netif_list; + /* loop through netifs */ + while (netif != NULL) { + netif_poll(netif); + /* proceed to next network interface */ + netif = netif->next; + } +} +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_NUM_NETIF_CLIENT_DATA > 0 +/** + * @ingroup netif_cd + * Allocate an index to store data in client_data member of struct netif. + * Returned value is an index in mentioned array. + * @see LWIP_NUM_NETIF_CLIENT_DATA + */ +u8_t +netif_alloc_client_data_id(void) +{ + u8_t result = netif_client_id; + netif_client_id++; + + LWIP_ASSERT("Increase LWIP_NUM_NETIF_CLIENT_DATA in lwipopts.h", result < LWIP_NUM_NETIF_CLIENT_DATA); + return result + LWIP_NETIF_CLIENT_DATA_INDEX_MAX; +} +#endif + +#if LWIP_IPV6 +/** + * @ingroup netif_ip6 + * Change an IPv6 address of a network interface + * + * @param netif the network interface to change + * @param addr_idx index of the IPv6 address + * @param addr6 the new IPv6 address + * + * @note call netif_ip6_addr_set_state() to set the address valid/temptative + */ +void +netif_ip6_addr_set(struct netif *netif, s8_t addr_idx, const ip6_addr_t *addr6) +{ + LWIP_ASSERT("addr6 != NULL", addr6 != NULL); + netif_ip6_addr_set_parts(netif, addr_idx, addr6->addr[0], addr6->addr[1], + addr6->addr[2], addr6->addr[3]); +} + +/* + * Change an IPv6 address of a network interface (internal version taking 4 * u32_t) + * + * @param netif the network interface to change + * @param addr_idx index of the IPv6 address + * @param i0 word0 of the new IPv6 address + * @param i1 word1 of the new IPv6 address + * @param i2 word2 of the new IPv6 address + * @param i3 word3 of the new IPv6 address + */ +void +netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3) +{ + const ip6_addr_t *old_addr; + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES); + + old_addr = netif_ip6_addr(netif, addr_idx); + /* address is actually being changed? */ + if ((old_addr->addr[0] != i0) || (old_addr->addr[1] != i1) || + (old_addr->addr[2] != i2) || (old_addr->addr[3] != i3)) { + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set: netif address being changed\n")); + + if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) { +#if LWIP_TCP || LWIP_UDP + ip_addr_t new_ipaddr; + IP_ADDR6(&new_ipaddr, i0, i1, i2, i3); +#endif /* LWIP_TCP || LWIP_UDP */ +#if LWIP_TCP + tcp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr); +#endif /* LWIP_TCP */ +#if LWIP_UDP + udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr); +#endif /* LWIP_UDP */ +#if LWIP_RAW + raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr); +#endif /* LWIP_RAW */ + } + /* @todo: remove/readd mib2 ip6 entries? */ + + IP6_ADDR(ip_2_ip6(&(netif->ip6_addr[addr_idx])), i0, i1, i2, i3); + IP_SET_TYPE_VAL(netif->ip6_addr[addr_idx], IPADDR_TYPE_V6); + + if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) { + netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6); + NETIF_STATUS_CALLBACK(netif, NETIF_STATE_IPV6); + } + } + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n", + addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)), + netif_ip6_addr_state(netif, addr_idx))); +} + +/** + * @ingroup netif_ip6 + * Change the state of an IPv6 address of a network interface + * (INVALID, TEMPTATIVE, PREFERRED, DEPRECATED, where TEMPTATIVE + * includes the number of checks done, see ip6_addr.h) + * + * @param netif the network interface to change + * @param addr_idx index of the IPv6 address + * @param state the new IPv6 address state + */ +void +netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state) +{ + u8_t old_state; + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES); + + old_state = netif_ip6_addr_state(netif, addr_idx); + //printf("old_state %d, state %d\n", old_state, state); + /* state is actually being changed? */ + if (old_state != state) { + u8_t old_valid = old_state & IP6_ADDR_VALID; + u8_t new_valid = state & IP6_ADDR_VALID; + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set_state: netif address state being changed\n")); + +#if LWIP_IPV6_MLD + /* Reevaluate solicited-node multicast group membership. */ + if (netif->flags & NETIF_FLAG_MLD6) { + nd6_adjust_mld_membership(netif, addr_idx, state); + } +#endif /* LWIP_IPV6_MLD */ + + if (old_valid && !new_valid) { + /* address about to be removed by setting invalid */ +#if LWIP_TCP + tcp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL); +#endif /* LWIP_TCP */ +#if LWIP_UDP + udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL); +#endif /* LWIP_UDP */ +#if LWIP_RAW + raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL); +#endif /* LWIP_RAW */ + /* @todo: remove mib2 ip6 entries? */ + } + netif->ip6_addr_state[addr_idx] = state; + + if (!old_valid && new_valid) { + /* address added by setting valid */ + /* @todo: add mib2 ip6 entries? */ + netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6); + } + if ((old_state & IP6_ADDR_PREFERRED) != (state & IP6_ADDR_PREFERRED)) { + /* address state has changed (valid flag changed or switched between + preferred and deprecated) -> call the callback function */ + NETIF_STATUS_CALLBACK(netif, NETIF_STATE_IPV6); + } + } + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n", + addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)), + netif_ip6_addr_state(netif, addr_idx))); +} + +/** + * Checks if a specific address is assigned to the netif and returns its + * index. + * + * @param netif the netif to check + * @param ip6addr the IPv6 address to find + * @return >= 0: address found, this is its index + * -1: address not found on this netif + */ +s8_t +netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) { + return i; + } + } + return -1; +} + +/** + * @ingroup netif_ip6 + * Create a link-local IPv6 address on a netif (stored in slot 0) + * + * @param netif the netif to create the address on + * @param from_mac_48bit if != 0, assume hwadr is a 48-bit MAC address (std conversion) + * if == 0, use hwaddr directly as interface ID + */ +void +netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit) +{ + u8_t i, addr_index; + + /* Link-local prefix. */ + ip_2_ip6(&netif->ip6_addr[0])->addr[0] = PP_HTONL(0xfe800000ul); + ip_2_ip6(&netif->ip6_addr[0])->addr[1] = 0; + + /* Generate interface ID. */ + if (from_mac_48bit) { + /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ + ip_2_ip6(&netif->ip6_addr[0])->addr[2] = lwip_htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) | + ((u32_t)(netif->hwaddr[1]) << 16) | + ((u32_t)(netif->hwaddr[2]) << 8) | + (0xff)); + ip_2_ip6(&netif->ip6_addr[0])->addr[3] = lwip_htonl((0xfeul << 24) | + ((u32_t)(netif->hwaddr[3]) << 16) | + ((u32_t)(netif->hwaddr[4]) << 8) | + (netif->hwaddr[5])); + } else { + /* Use hwaddr directly as interface ID. */ + ip_2_ip6(&netif->ip6_addr[0])->addr[2] = 0; + ip_2_ip6(&netif->ip6_addr[0])->addr[3] = 0; + + addr_index = 3; + for (i = 0; (i < 8) && (i < netif->hwaddr_len); i++) { + if (i == 4) { + addr_index--; + } + ip_2_ip6(&netif->ip6_addr[0])->addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)); + } + } + + /* Set address state. */ +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* Will perform duplicate address detection (DAD). */ + netif_ip6_addr_set_state(netif, 0, IP6_ADDR_TENTATIVE); +#else + /* Consider address valid. */ + netif_ip6_addr_set_state(netif, 0, IP6_ADDR_PREFERRED); +#endif /* LWIP_IPV6_AUTOCONFIG */ +} + +/** + * @ingroup netif_ip6 + * This function allows for the easy addition of a new IPv6 address to an interface. + * It takes care of finding an empty slot and then sets the address tentative + * (to make sure that all the subsequent processing happens). + * + * @param netif netif to add the address on + * @param ip6addr address to add + * @param chosen_idx if != NULL, the chosen IPv6 address index will be stored here + */ +err_t +netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx) +{ + s8_t i; + + i = netif_get_ip6_addr_match(netif, ip6addr); + if (i >= 0) { + /* Address already added */ + if (chosen_idx != NULL) { + *chosen_idx = i; + } + return ERR_OK; + } + + /* Find a free slot -- musn't be the first one (reserved for link local) */ + for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) { + ip_addr_copy_from_ip6(netif->ip6_addr[i], *ip6addr); + netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE); + if (chosen_idx != NULL) { + *chosen_idx = i; + } + return ERR_OK; + } + } + + if (chosen_idx != NULL) { + *chosen_idx = -1; + } + return ERR_VAL; +} + +/** Dummy IPv6 output function for netifs not supporting IPv6 + */ +static err_t +netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(netif); + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(ipaddr); + + return ERR_IF; +} +#endif /* LWIP_IPV6 */ diff --git a/sdk/Src/Network/lwip2.0.3/core/pbuf.c b/sdk/Src/Network/lwip2.0.3/core/pbuf.c new file mode 100644 index 0000000..95e762f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/pbuf.c @@ -0,0 +1,1442 @@ +/** + * @file + * Packet buffer management + */ + +/** + * @defgroup pbuf Packet buffers (PBUF) + * @ingroup infrastructure + * + * Packets are built from the pbuf data structure. It supports dynamic + * memory allocation for packet contents or can reference externally + * managed packet contents both in RAM and ROM. Quick allocation for + * incoming packets is provided through pools with fixed sized pbufs. + * + * A packet may span over multiple pbufs, chained as a singly linked + * list. This is called a "pbuf chain". + * + * Multiple packets may be queued, also using this singly linked list. + * This is called a "packet queue". + * + * So, a packet queue consists of one or more pbuf chains, each of + * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE + * NOT SUPPORTED!!! Use helper structs to queue multiple packets. + * + * The differences between a pbuf chain and a packet queue are very + * precise but subtle. + * + * The last pbuf of a packet has a ->tot_len field that equals the + * ->len field. It can be found by traversing the list. If the last + * pbuf of a packet has a ->next field other than NULL, more packets + * are on the queue. + * + * Therefore, looping through a pbuf of a single packet, has an + * loop end condition (tot_len == p->len), NOT (next == NULL). + * + * Example of custom pbuf usage for zero-copy RX: + @code{.c} +typedef struct my_custom_pbuf +{ + struct pbuf_custom p; + void* dma_descriptor; +} my_custom_pbuf_t; + +LWIP_MEMPOOL_DECLARE(RX_POOL, 10, sizeof(my_custom_pbuf_t), "Zero-copy RX PBUF pool"); + +void my_pbuf_free_custom(void* p) +{ + my_custom_pbuf_t* my_puf = (my_custom_pbuf_t*)p; + + LOCK_INTERRUPTS(); + free_rx_dma_descriptor(my_pbuf->dma_descriptor); + LWIP_MEMPOOL_FREE(RX_POOL, my_pbuf); + UNLOCK_INTERRUPTS(); +} + +void eth_rx_irq() +{ + dma_descriptor* dma_desc = get_RX_DMA_descriptor_from_ethernet(); + my_custom_pbuf_t* my_pbuf = (my_custom_pbuf_t*)LWIP_MEMPOOL_ALLOC(RX_POOL); + + my_pbuf->p.custom_free_function = my_pbuf_free_custom; + my_pbuf->dma_descriptor = dma_desc; + + invalidate_cpu_cache(dma_desc->rx_data, dma_desc->rx_length); + + struct pbuf* p = pbuf_alloced_custom(PBUF_RAW, + dma_desc->rx_length, + PBUF_REF, + &my_pbuf->p, + dma_desc->rx_data, + dma_desc->max_buffer_size); + + if(netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } +} + @endcode + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#if LWIP_TCP && TCP_QUEUE_OOSEQ +#include "lwip/priv/tcp_priv.h" +#endif +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) +/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically + aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ +#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) + +#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_IS_EMPTY() +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +#if !NO_SYS +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL +#include "lwip/tcpip.h" +#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ + if (tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \ + SYS_ARCH_PROTECT(old_level); \ + pbuf_free_ooseq_pending = 0; \ + SYS_ARCH_UNPROTECT(old_level); \ + } } while(0) +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +#endif /* !NO_SYS */ + +volatile u8_t pbuf_free_ooseq_pending; +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() + +/** + * Attempt to reclaim some memory from queued out-of-sequence TCP segments + * if we run out of pool pbufs. It's better to give priority to new packets + * if we're running out. + * + * This must be done in the correct thread context therefore this function + * can only be used with NO_SYS=0 and through tcpip_callback. + */ +#if !NO_SYS +static +#endif /* !NO_SYS */ +void +pbuf_free_ooseq(void) +{ + struct tcp_pcb* pcb; + SYS_ARCH_SET(pbuf_free_ooseq_pending, 0); + + for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { + if (NULL != pcb->ooseq) { + /** Free the ooseq pbufs of one PCB only */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + return; + } + } +} + +#if !NO_SYS +/** + * Just a callback function for tcpip_callback() that calls pbuf_free_ooseq(). + */ +static void +pbuf_free_ooseq_callback(void *arg) +{ + LWIP_UNUSED_ARG(arg); + pbuf_free_ooseq(); +} +#endif /* !NO_SYS */ + +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void +pbuf_pool_is_empty(void) +{ +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL + SYS_ARCH_SET(pbuf_free_ooseq_pending, 1); +#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ + u8_t queued; + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_pending; + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); + + if (!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); + } +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +} +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +/** + * @ingroup pbuf + * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). + * + * The actual memory allocated for the pbuf is determined by the + * layer at which the pbuf is allocated and the requested size + * (from the size parameter). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type this parameter decides how and where the pbuf + * should be allocated as follows: + * + * - PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. It is assumed that the memory used is really + * similar to ROM in that it is immutable and will not be + * changed. Memory which is dynamic should generally not + * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. + * - PBUF_REF: no buffer memory is allocated for the pbuf, even for + * protocol headers. It is assumed that the pbuf is only + * being used in a single thread. If the pbuf gets queued, + * then pbuf_take should be called to copy the buffer. + * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + * + * @return the allocated pbuf. If multiple pbufs where allocated, this + * is the first pbuf of a pbuf chain. + */ +struct pbuf * +pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) +{ + struct pbuf *p, *q, *r; + u16_t offset; + s32_t rem_len; /* remaining length */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); + + /* determine header offset */ + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN; + break; + case PBUF_RAW_TX: + /* add room for encapsulating link layer headers (e.g. 802.11) */ + offset = PBUF_LINK_ENCAPSULATION_HLEN; + break; + case PBUF_RAW: + /* no offset (e.g. RX buffers or chain successors) */ + offset = 0; + break; + default: + LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch (type) { + case PBUF_POOL: + /* allocate head of pbuf chain into p */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + if (p == NULL) { + PBUF_POOL_IS_EMPTY(); + return NULL; + } + p->type = type; + p->next = NULL; + + /* make the payload pointer point 'offset' bytes into pbuf data memory */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); + LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + /* the total length of the pbuf chain is the requested size */ + p->tot_len = length; + /* set the length of the first pbuf in the chain */ + p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", + (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); + /* set reference count (needed here in case we fail) */ + p->ref = 1; + + /* now allocate the tail of the pbuf chain */ + + /* remember first pbuf for linkage in next iteration */ + r = p; + /* remaining length to be allocated */ + rem_len = length - p->len; + /* any remaining pbufs to be allocated? */ + while (rem_len > 0) { + q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + if (q == NULL) { + PBUF_POOL_IS_EMPTY(); + /* free chain so far allocated */ + pbuf_free(p); + /* bail out unsuccessfully */ + return NULL; + } + q->type = type; + q->flags = 0; + q->next = NULL; + /* make previous pbuf point to this pbuf */ + r->next = q; + /* set total length of this pbuf and next in chain */ + LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); + q->tot_len = (u16_t)rem_len; + /* this pbuf length is pool size, unless smaller sized tail */ + q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); + q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); + LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", + ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + q->ref = 1; + /* calculate remaining length to be allocated */ + rem_len -= q->len; + /* remember this pbuf for linkage in next iteration */ + r = q; + } + /* end of chain */ + /*r->next = NULL;*/ + + break; + case PBUF_RAM: + { + mem_size_t alloc_len = LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length); + + /* bug #50040: Check for integer overflow when calculating alloc_len */ + if (alloc_len < LWIP_MEM_ALIGN_SIZE(length)) { + return NULL; + } + + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = (struct pbuf*)mem_malloc(alloc_len); + } + + if (p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + + LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + break; + /* pbuf references existing (non-volatile static constant) ROM payload? */ + case PBUF_ROM: + /* pbuf references existing (externally allocated) RAM payload? */ + case PBUF_REF: + /* only allocate memory for the pbuf structure */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF); + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", + (type == PBUF_ROM) ? "ROM" : "REF")); + return NULL; + } + /* caller must set this field properly, afterwards */ + p->payload = NULL; + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + break; + default: + LWIP_ASSERT("pbuf_alloc: erroneous type", 0); + return NULL; + } + /* set reference count */ + p->ref = 1; + /* set flags */ + p->flags = 0; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + return p; +} + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** + * @ingroup pbuf + * Initialize a custom pbuf (already allocated). + * + * @param l flag to define header size + * @param length size of the pbuf's payload + * @param type type of the pbuf (only used to treat the pbuf accordingly, as + * this function allocates no memory) + * @param p pointer to the custom pbuf to initialize (already allocated) + * @param payload_mem pointer to the buffer that is used for payload and headers, + * must be at least big enough to hold 'length' plus the header size, + * may be NULL if set later. + * ATTENTION: The caller is responsible for correct alignment of this buffer!! + * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least + * big enough to hold 'length' plus the header size + */ +struct pbuf* +pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, + void *payload_mem, u16_t payload_mem_len) +{ + u16_t offset; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); + + /* determine header offset */ + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN; + break; + case PBUF_RAW_TX: + /* add room for encapsulating link layer headers (e.g. 802.11) */ + offset = PBUF_LINK_ENCAPSULATION_HLEN; + break; + case PBUF_RAW: + offset = 0; + break; + default: + LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0); + return NULL; + } + + if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); + return NULL; + } + + p->pbuf.next = NULL; + if (payload_mem != NULL) { + p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); + } else { + p->pbuf.payload = NULL; + } + p->pbuf.flags = PBUF_FLAG_IS_CUSTOM; + p->pbuf.len = p->pbuf.tot_len = length; + p->pbuf.type = type; + p->pbuf.ref = 1; + return &p->pbuf; +} +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +/** + * @ingroup pbuf + * Shrink a pbuf chain to a desired length. + * + * @param p pbuf to shrink. + * @param new_len desired new length of pbuf chain + * + * Depending on the desired length, the first few pbufs in a chain might + * be skipped and left unchanged. The new last pbuf in the chain will be + * resized, and any remaining pbufs will be freed. + * + * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. + * @note May not be called on a packet queue. + * + * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). + */ +void +pbuf_realloc(struct pbuf *p, u16_t new_len) +{ + struct pbuf *q; + u16_t rem_len; /* remaining length */ + s32_t grow; + + LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); + LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL || + p->type == PBUF_ROM || + p->type == PBUF_RAM || + p->type == PBUF_REF); + + /* desired length larger than current length? */ + if (new_len >= p->tot_len) { + /* enlarging not yet supported */ + return; + } + + /* the pbuf chain grows by (new_len - p->tot_len) bytes + * (which may be negative in case of shrinking) */ + grow = new_len - p->tot_len; + + /* first, step over any pbufs that should remain in the chain */ + rem_len = new_len; + q = p; + /* should this pbuf be kept? */ + while (rem_len > q->len) { + /* decrease remaining length by pbuf length */ + rem_len -= q->len; + /* decrease total length indicator */ + LWIP_ASSERT("grow < max_u16_t", grow < 0xffff); + q->tot_len += (u16_t)grow; + /* proceed to next pbuf in chain */ + q = q->next; + LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); + } + /* we have now reached the new last pbuf (in q) */ + /* rem_len == desired length for pbuf q */ + + /* shrink allocated memory for PBUF_RAM */ + /* (other types merely adjust their length fields */ + if ((q->type == PBUF_RAM) && (rem_len != q->len) +#if LWIP_SUPPORT_CUSTOM_PBUF + && ((q->flags & PBUF_FLAG_IS_CUSTOM) == 0) +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + ) { + /* reallocate and adjust the length of the pbuf that will be split */ + q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len); + LWIP_ASSERT("mem_trim returned q == NULL", q != NULL); + } + /* adjust length fields for new last pbuf */ + q->len = rem_len; + q->tot_len = q->len; + + /* any remaining pbufs in chain? */ + if (q->next != NULL) { + /* free remaining pbufs in chain */ + pbuf_free(q->next); + } + /* q is last packet in chain */ + q->next = NULL; + +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * @see pbuf_header. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size. + * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types + * + * @return non-zero on failure, zero on success. + * + */ +static u8_t +pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force) +{ + u16_t type; + void *payload; + u16_t increment_magnitude; + + LWIP_ASSERT("p != NULL", p != NULL); + if ((header_size_increment == 0) || (p == NULL)) { + return 0; + } + + if (header_size_increment < 0) { + increment_magnitude = (u16_t)-header_size_increment; + /* Check that we aren't going to move off the end of the pbuf */ + LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); + } else { + increment_magnitude = (u16_t)header_size_increment; +#if 0 + /* Can't assert these as some callers speculatively call + pbuf_header() to see if it's OK. Will return 1 below instead. */ + /* Check that we've got the correct type of pbuf to work with */ + LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", + p->type == PBUF_RAM || p->type == PBUF_POOL); + /* Check that we aren't going to move off the beginning of the pbuf */ + LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF", + (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF); +#endif + } + + type = p->type; + /* remember current payload pointer */ + payload = p->payload; + + /* pbuf types containing payloads? */ + if (type == PBUF_RAM || type == PBUF_POOL) { + /* set new payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + /* boundary check fails? */ + if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, + ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF))); + /* restore old payload pointer */ + p->payload = payload; + /* bail out unsuccessfully */ + return 1; + } + /* pbuf types referring to external payloads? */ + } else if (type == PBUF_REF || type == PBUF_ROM) { + /* hide a header in the payload? */ + if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else if ((header_size_increment > 0) && force) { + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccessfully */ + return 1; + } + } else { + /* Unknown type */ + LWIP_ASSERT("bad pbuf type", 0); + return 1; + } + /* modify pbuf length fields */ + p->len += header_size_increment; + p->tot_len += header_size_increment; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n", + (void *)payload, (void *)p->payload, header_size_increment)); + + return 0; +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns successful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + return pbuf_header_impl(p, header_size_increment, 0); +} + +/** + * Same as pbuf_header but does not check if 'header_size > 0' is allowed. + * This is used internally only, to allow PBUF_REF for RX. + */ +u8_t +pbuf_header_force(struct pbuf *p, s16_t header_size_increment) +{ + return pbuf_header_impl(p, header_size_increment, 1); +} + +/** + * @ingroup pbuf + * Dereference a pbuf chain or queue and deallocate any no-longer-used + * pbufs at the head of this chain or queue. + * + * Decrements the pbuf reference count. If it reaches zero, the pbuf is + * deallocated. + * + * For a pbuf chain, this is repeated for each pbuf in the chain, + * up to the first pbuf which has a non-zero reference count after + * decrementing. So, when all reference counts are one, the whole + * chain is free'd. + * + * @param p The pbuf (chain) to be dereferenced. + * + * @return the number of pbufs that were de-allocated + * from the head of the chain. + * + * @note MUST NOT be called on a packet queue (Not verified to work yet). + * @note the reference counter of a pbuf equals the number of pointers + * that refer to the pbuf (or into the pbuf). + * + * @internal examples: + * + * Assuming existing chains a->b->c with the following reference + * counts, calling pbuf_free(a) results in: + * + * 1->2->3 becomes ...1->3 + * 3->3->3 becomes 2->3->3 + * 1->1->2 becomes ......1 + * 2->1->1 becomes 1->1->1 + * 1->1->1 becomes ....... + * + */ +u8_t +pbuf_free(struct pbuf *p) +{ + u16_t type; + struct pbuf *q; + u8_t count; + + if (p == NULL) { + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_free(p == NULL) was called.\n")); + return 0; + } + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); + + PERF_START; + + LWIP_ASSERT("pbuf_free: sane type", + p->type == PBUF_RAM || p->type == PBUF_ROM || + p->type == PBUF_REF || p->type == PBUF_POOL); + + count = 0; + /* de-allocate all consecutive pbufs from the head of the chain that + * obtain a zero reference count after decrementing*/ + while (p != NULL) { + u16_t ref; + SYS_ARCH_DECL_PROTECT(old_level); + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. We put the new ref into a local variable to prevent + * further protection. */ + SYS_ARCH_PROTECT(old_level); + /* all pbufs in a chain are referenced at least once */ + LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + /* decrease reference count (number of pointers to pbuf) */ + ref = --(p->ref); + SYS_ARCH_UNPROTECT(old_level); + /* this pbuf is no longer referenced to? */ + if (ref == 0) { + /* remember next pbuf in chain for next iteration */ + q = p->next; + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); + type = p->type; +#if LWIP_SUPPORT_CUSTOM_PBUF + /* is this a custom pbuf? */ + if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) { + struct pbuf_custom *pc = (struct pbuf_custom*)p; + LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL); + pc->custom_free_function(p); + } else +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + { + /* is this a pbuf from the pool? */ + if (type == PBUF_POOL) { + memp_free(MEMP_PBUF_POOL, p); + /* is this a ROM or RAM referencing pbuf? */ + } else if (type == PBUF_ROM || type == PBUF_REF) { + memp_free(MEMP_PBUF, p); + /* type == PBUF_RAM */ + } else { + mem_free(p); + } + } + count++; + /* proceed to next pbuf */ + p = q; + /* p->ref > 0, this pbuf is still referenced to */ + /* (and so the remaining pbufs in chain as well) */ + } else { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); + /* stop walking through the chain */ + p = NULL; + } + } + PERF_STOP("pbuf_free"); + /* return number of de-allocated pbufs */ + return count; +} + +/** + * Count number of pbufs in a chain + * + * @param p first pbuf of chain + * @return the number of pbufs in a chain + */ +u16_t +pbuf_clen(const struct pbuf *p) +{ + u16_t len; + + len = 0; + while (p != NULL) { + ++len; + p = p->next; + } + return len; +} + +/** + * @ingroup pbuf + * Increment the reference count of the pbuf. + * + * @param p pbuf to increase reference counter of + * + */ +void +pbuf_ref(struct pbuf *p) +{ + /* pbuf given? */ + if (p != NULL) { + SYS_ARCH_INC(p->ref, 1); + LWIP_ASSERT("pbuf ref overflow", p->ref > 0); + } +} + +/** + * @ingroup pbuf + * Concatenate two pbufs (each may be a pbuf chain) and take over + * the caller's reference of the tail pbuf. + * + * @note The caller MAY NOT reference the tail pbuf afterwards. + * Use pbuf_chain() for that purpose. + * + * @see pbuf_chain() + */ +void +pbuf_cat(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", + ((h != NULL) && (t != NULL)), return;); + + /* proceed to last pbuf of chain */ + for (p = h; p->next != NULL; p = p->next) { + /* add total length of second chain to all totals of first chain */ + p->tot_len += t->tot_len; + } + /* { p is last pbuf of first h chain, p->next == NULL } */ + LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); + LWIP_ASSERT("p->next == NULL", p->next == NULL); + /* add total length of second chain to last pbuf total of first chain */ + p->tot_len += t->tot_len; + /* chain last pbuf of head (p) with first of tail (t) */ + p->next = t; + /* p->next now references t, but the caller will drop its reference to t, + * so netto there is no change to the reference count of t. + */ +} + +/** + * @ingroup pbuf + * Chain two pbufs (or pbuf chains) together. + * + * The caller MUST call pbuf_free(t) once it has stopped + * using it. Use pbuf_cat() instead if you no longer use t. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * @note The pbufs MUST belong to the same packet. + * @note MAY NOT be called on a packet queue. + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * The ->ref field of the first pbuf of the tail chain is adjusted. + * + */ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + pbuf_cat(h, t); + /* t is now referenced by h */ + pbuf_ref(t); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); +} + +/** + * Dechains the first pbuf from its succeeding pbufs in the chain. + * + * Makes p->tot_len field equal to p->len. + * @param p pbuf to dechain + * @return remainder of the pbuf chain, or NULL if it was de-allocated. + * @note May not be called on a packet queue. + */ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + /* tail */ + q = p->next; + /* pbuf has successor in chain? */ + if (q != NULL) { + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); + /* enforce invariant if assertion is disabled */ + q->tot_len = p->tot_len - p->len; + /* decouple pbuf from remainder */ + p->next = NULL; + /* total length of pbuf p is its own length only */ + p->tot_len = p->len; + /* q is no longer referenced by p, free it */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + tail_gone = pbuf_free(q); + if (tail_gone > 0) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, + ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); + } + /* return remaining tail or NULL if deallocated */ + } + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); + return ((tail_gone > 0) ? NULL : q); +} + +/** + * @ingroup pbuf + * Create PBUF_RAM copies of pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * @note You MUST explicitly use p = pbuf_take(p); + * + * @note Only one packet is copied, no packet queue! + * + * @param p_to pbuf destination of the copy + * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big + * enough to hold p_from + */ +err_t +pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from) +{ + u16_t offset_to=0, offset_from=0, len; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", + (const void*)p_to, (const void*)p_from)); + + /* is the target big enough to hold the source? */ + LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && + (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); + + /* iterate through pbuf chain */ + do + { + /* copy one part of the original chain */ + if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { + /* complete current p_from fits into current p_to */ + len = p_from->len - offset_from; + } else { + /* current p_from does not fit into current p_to */ + len = p_to->len - offset_to; + } + MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len); + offset_to += len; + offset_from += len; + LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); + LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); + if (offset_from >= p_from->len) { + /* on to next p_from (if any) */ + offset_from = 0; + p_from = p_from->next; + } + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;); + } + + if ((p_from != NULL) && (p_from->len == p_from->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!", + (p_from->next == NULL), return ERR_VAL;); + } + if ((p_to != NULL) && (p_to->len == p_to->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!", + (p_to->next == NULL), return ERR_VAL;); + } + } while (p_from); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n")); + return ERR_OK; +} + +/** + * @ingroup pbuf + * Copy (part of) the contents of a packet buffer + * to an application supplied buffer. + * + * @param buf the pbuf from which to copy data + * @param dataptr the application supplied buffer + * @param len length of data to copy (dataptr must be big enough). No more + * than buf->tot_len will be copied, irrespective of len + * @param offset offset into the packet buffer from where to begin copying len bytes + * @return the number of bytes copied, or 0 on failure + */ +u16_t +pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) +{ + const struct pbuf *p; + u16_t left; + u16_t buf_copy_len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); + + left = 0; + + if ((buf == NULL) || (dataptr == NULL)) { + return 0; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for (p = buf; len != 0 && p != NULL; p = p->next) { + if ((offset != 0) && (offset >= p->len)) { + /* don't copy from this buffer -> on to the next */ + offset -= p->len; + } else { + /* copy from this buffer. maybe only partially. */ + buf_copy_len = p->len - offset; + if (buf_copy_len > len) { + buf_copy_len = len; + } + /* copy the necessary parts of the buffer */ + MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len); + copied_total += buf_copy_len; + left += buf_copy_len; + len -= buf_copy_len; + offset = 0; + } + } + return copied_total; +} + +#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE +/** + * This method modifies a 'pbuf chain', so that its total length is + * smaller than 64K. The remainder of the original pbuf chain is stored + * in *rest. + * This function never creates new pbufs, but splits an existing chain + * in two parts. The tot_len of the modified packet queue will likely be + * smaller than 64K. + * 'packet queues' are not supported by this function. + * + * @param p the pbuf queue to be split + * @param rest pointer to store the remainder (after the first 64K) + */ +void pbuf_split_64k(struct pbuf *p, struct pbuf **rest) +{ + *rest = NULL; + if ((p != NULL) && (p->next != NULL)) { + u16_t tot_len_front = p->len; + struct pbuf *i = p; + struct pbuf *r = p->next; + + /* continue until the total length (summed up as u16_t) overflows */ + while ((r != NULL) && ((u16_t)(tot_len_front + r->len) > tot_len_front)) { + tot_len_front += r->len; + i = r; + r = r->next; + } + /* i now points to last packet of the first segment. Set next + pointer to NULL */ + i->next = NULL; + + if (r != NULL) { + /* Update the tot_len field in the first part */ + for (i = p; i != NULL; i = i->next) { + i->tot_len -= r->tot_len; + LWIP_ASSERT("tot_len/len mismatch in last pbuf", + (i->next != NULL) || (i->tot_len == i->len)); + } + if (p->flags & PBUF_FLAG_TCP_FIN) { + r->flags |= PBUF_FLAG_TCP_FIN; + } + + /* tot_len field in rest does not need modifications */ + /* reference counters do not need modifications */ + *rest = r; + } + } +} +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + +/* Actual implementation of pbuf_skip() but returning const pointer... */ +static const struct pbuf* +pbuf_skip_const(const struct pbuf* in, u16_t in_offset, u16_t* out_offset) +{ + u16_t offset_left = in_offset; + const struct pbuf* q = in; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= offset_left)) { + offset_left -= q->len; + q = q->next; + } + if (out_offset != NULL) { + *out_offset = offset_left; + } + return q; +} + +/** + * @ingroup pbuf + * Skip a number of bytes at the start of a pbuf + * + * @param in input pbuf + * @param in_offset offset to skip + * @param out_offset resulting offset in the returned pbuf + * @return the pbuf in the queue where the offset is + */ +struct pbuf* +pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset) +{ + const struct pbuf* out = pbuf_skip_const(in, in_offset, out_offset); + return LWIP_CONST_CAST(struct pbuf*, out); +} + +/** + * @ingroup pbuf + * Copy application supplied data into a pbuf. + * This function can only be used to copy the equivalent of buf->tot_len data. + * + * @param buf pbuf to fill with data + * @param dataptr application supplied data buffer + * @param len length of the application supplied data buffer + * + * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough + */ +err_t +pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) +{ + struct pbuf *p; + u16_t buf_copy_len; + u16_t total_copy_len = len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;); + LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;); + LWIP_ERROR("pbuf_take: buf not large enough", (buf->tot_len >= len), return ERR_MEM;); + + if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { + return ERR_ARG; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for (p = buf; total_copy_len != 0; p = p->next) { + LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); + buf_copy_len = total_copy_len; + if (buf_copy_len > p->len) { + /* this pbuf cannot hold all remaining data */ + buf_copy_len = p->len; + } + /* copy the necessary parts of the buffer */ + MEMCPY(p->payload, &((const char*)dataptr)[copied_total], buf_copy_len); + total_copy_len -= buf_copy_len; + copied_total += buf_copy_len; + } + LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); + return ERR_OK; +} + +/** + * @ingroup pbuf + * Same as pbuf_take() but puts data at an offset + * + * @param buf pbuf to fill with data + * @param dataptr application supplied data buffer + * @param len length of the application supplied data buffer + * @param offset offset in pbuf where to copy dataptr to + * + * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough + */ +err_t +pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset) +{ + u16_t target_offset; + struct pbuf* q = pbuf_skip(buf, offset, &target_offset); + + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->tot_len >= target_offset + len)) { + u16_t remaining_len = len; + const u8_t* src_ptr = (const u8_t*)dataptr; + /* copy the part that goes into the first pbuf */ + u16_t first_copy_len = LWIP_MIN(q->len - target_offset, len); + MEMCPY(((u8_t*)q->payload) + target_offset, dataptr, first_copy_len); + remaining_len -= first_copy_len; + src_ptr += first_copy_len; + if (remaining_len > 0) { + return pbuf_take(q->next, src_ptr, remaining_len); + } + return ERR_OK; + } + return ERR_MEM; +} + +/** + * @ingroup pbuf + * Creates a single pbuf out of a queue of pbufs. + * + * @remark: Either the source pbuf 'p' is freed by this function or the original + * pbuf 'p' is returned, therefore the caller has to check the result! + * + * @param p the source pbuf + * @param layer pbuf_layer of the new pbuf + * + * @return a new, single pbuf (p->next is NULL) + * or the old pbuf if allocation fails + */ +struct pbuf* +pbuf_coalesce(struct pbuf *p, pbuf_layer layer) +{ + struct pbuf *q; + err_t err; + if (p->next == NULL) { + return p; + } + q = pbuf_alloc(layer, p->tot_len, PBUF_RAM); + if (q == NULL) { + /* @todo: what do we do now? */ + return p; + } + err = pbuf_copy(q, p); + LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ + LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); + pbuf_free(p); + return q; +} + +#if LWIP_CHECKSUM_ON_COPY +/** + * Copies data into a single pbuf (*not* into a pbuf queue!) and updates + * the checksum while copying + * + * @param p the pbuf to copy data into + * @param start_offset offset of p->payload where to copy the data to + * @param dataptr data to copy into the pbuf + * @param len length of data to copy into the pbuf + * @param chksum pointer to the checksum which is updated + * @return ERR_OK if successful, another error if the data does not fit + * within the (first) pbuf (no pbuf queues!) + */ +err_t +pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum) +{ + u32_t acc; + u16_t copy_chksum; + char *dst_ptr; + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("dataptr != NULL", dataptr != NULL); + LWIP_ASSERT("chksum != NULL", chksum != NULL); + LWIP_ASSERT("len != 0", len != 0); + + if ((start_offset >= p->len) || (start_offset + len > p->len)) { + return ERR_ARG; + } + + dst_ptr = ((char*)p->payload) + start_offset; + copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len); + if ((start_offset & 1) != 0) { + copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum); + } + acc = *chksum; + acc += copy_chksum; + *chksum = FOLD_U32T(acc); + return ERR_OK; +} +#endif /* LWIP_CHECKSUM_ON_COPY */ + +/** + * @ingroup pbuf + * Get one byte from the specified position in a pbuf + * WARNING: returns zero for offset >= p->tot_len + * + * @param p pbuf to parse + * @param offset offset into p of the byte to return + * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len + */ +u8_t +pbuf_get_at(const struct pbuf* p, u16_t offset) +{ + int ret = pbuf_try_get_at(p, offset); + if (ret >= 0) { + return (u8_t)ret; + } + return 0; +} + +/** + * @ingroup pbuf + * Get one byte from the specified position in a pbuf + * + * @param p pbuf to parse + * @param offset offset into p of the byte to return + * @return byte at an offset into p [0..0xFF] OR negative if 'offset' >= p->tot_len + */ +int +pbuf_try_get_at(const struct pbuf* p, u16_t offset) +{ + u16_t q_idx; + const struct pbuf* q = pbuf_skip_const(p, offset, &q_idx); + + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > q_idx)) { + return ((u8_t*)q->payload)[q_idx]; + } + return -1; +} + +/** + * @ingroup pbuf + * Put one byte to the specified position in a pbuf + * WARNING: silently ignores offset >= p->tot_len + * + * @param p pbuf to fill + * @param offset offset into p of the byte to write + * @param data byte to write at an offset into p + */ +void +pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data) +{ + u16_t q_idx; + struct pbuf* q = pbuf_skip(p, offset, &q_idx); + + /* write requested data if pbuf is OK */ + if ((q != NULL) && (q->len > q_idx)) { + ((u8_t*)q->payload)[q_idx] = data; + } +} + +/** + * @ingroup pbuf + * Compare pbuf contents at specified offset with memory s2, both of length n + * + * @param p pbuf to compare + * @param offset offset into p at which to start comparing + * @param s2 buffer to compare + * @param n length of buffer to compare + * @return zero if equal, nonzero otherwise + * (0xffff if p is too short, diffoffset+1 otherwise) + */ +u16_t +pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n) +{ + u16_t start = offset; + const struct pbuf* q = p; + u16_t i; + + /* pbuf long enough to perform check? */ + if(p->tot_len < (offset + n)) { + return 0xffff; + } + + /* get the correct pbuf from chain. We know it succeeds because of p->tot_len check above. */ + while ((q != NULL) && (q->len <= start)) { + start -= q->len; + q = q->next; + } + + /* return requested data if pbuf is OK */ + for (i = 0; i < n; i++) { + /* We know pbuf_get_at() succeeds because of p->tot_len check above. */ + u8_t a = pbuf_get_at(q, start + i); + u8_t b = ((const u8_t*)s2)[i]; + if (a != b) { + return i+1; + } + } + return 0; +} + +/** + * @ingroup pbuf + * Find occurrence of mem (with length mem_len) in pbuf p, starting at offset + * start_offset. + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param mem search for the contents of this buffer + * @param mem_len length of 'mem' + * @param start_offset offset into p at which to start searching + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_memfind(const struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset) +{ + u16_t i; + u16_t max = p->tot_len - mem_len; + if (p->tot_len >= mem_len + start_offset) { + for (i = start_offset; i <= max; i++) { + u16_t plus = pbuf_memcmp(p, i, mem, mem_len); + if (plus == 0) { + return i; + } + } + } + return 0xFFFF; +} + +/** + * Find occurrence of substr with length substr_len in pbuf p, start at offset + * start_offset + * WARNING: in contrast to strstr(), this one does not stop at the first \0 in + * the pbuf/source string! + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param substr string to search for in p, maximum length is 0xFFFE + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_strstr(const struct pbuf* p, const char* substr) +{ + size_t substr_len; + if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) { + return 0xFFFF; + } + substr_len = strlen(substr); + if (substr_len >= 0xFFFF) { + return 0xFFFF; + } + return pbuf_memfind(p, substr, (u16_t)substr_len, 0); +} diff --git a/sdk/Src/Network/lwip2.0.3/core/raw.c b/sdk/Src/Network/lwip2.0.3/core/raw.c new file mode 100644 index 0000000..4ee7457 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/raw.c @@ -0,0 +1,521 @@ +/** + * @file + * Implementation of raw protocol PCBs for low-level handling of + * different types of protocols besides (or overriding) those + * already available in lwIP.\n + * See also @ref raw_raw + * + * @defgroup raw_raw RAW + * @ingroup callbackstyle_api + * Implementation of raw protocol PCBs for low-level handling of + * different types of protocols besides (or overriding) those + * already available in lwIP.\n + * @see @ref raw_api + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/raw.h" +#include "lwip/stats.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" + +#include + +/** The list of RAW PCBs */ +static struct raw_pcb *raw_pcbs; + +static u8_t +raw_input_match(struct raw_pcb *pcb, u8_t broadcast) +{ + LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */ + +#if LWIP_IPV4 && LWIP_IPV6 + /* Dual-stack: PCBs listening to any IP type also listen to any IP address */ + if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { +#if IP_SOF_BROADCAST_RECV + if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) { + return 0; + } +#endif /* IP_SOF_BROADCAST_RECV */ + return 1; + } +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + + /* Only need to check PCB if incoming IP version matches PCB IP version */ + if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) { +#if LWIP_IPV4 + /* Special case: IPv4 broadcast: receive all broadcasts + * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */ + if (broadcast != 0) { +#if IP_SOF_BROADCAST_RECV + if (ip_get_option(pcb, SOF_BROADCAST)) +#endif /* IP_SOF_BROADCAST_RECV */ + { + if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip))) { + return 1; + } + } + } else +#endif /* LWIP_IPV4 */ + /* Handle IPv4 and IPv6: catch all or exact match */ + if (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) { + return 1; + } + } + + return 0; +} + +/** + * Determine if in incoming IP packet is covered by a RAW PCB + * and if so, pass it to a user-provided receive callback function. + * + * Given an incoming IP datagram (as a chain of pbufs) this function + * finds a corresponding RAW PCB and calls the corresponding receive + * callback function. + * + * @param p pbuf to be demultiplexed to a RAW PCB. + * @param inp network interface on which the datagram was received. + * @return - 1 if the packet has been eaten by a RAW PCB receive + * callback function. The caller MAY NOT not reference the + * packet any longer, and MAY NOT call pbuf_free(). + * @return - 0 if packet is not eaten (pbuf is still referenced by the + * caller). + * + */ +u8_t +raw_input(struct pbuf *p, struct netif *inp) +{ + struct raw_pcb *pcb, *prev; + s16_t proto; + u8_t eaten = 0; + u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()); + + LWIP_UNUSED_ARG(inp); + +#if LWIP_IPV6 +#if LWIP_IPV4 + if (IP_HDR_GET_VERSION(p->payload) == 6) +#endif /* LWIP_IPV4 */ + { + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; + proto = IP6H_NEXTH(ip6hdr); + } +#if LWIP_IPV4 + else +#endif /* LWIP_IPV4 */ +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 + { + proto = IPH_PROTO((struct ip_hdr *)p->payload); + } +#endif /* LWIP_IPV4 */ + + prev = NULL; + pcb = raw_pcbs; + /* loop through all raw pcbs until the packet is eaten by one */ + /* this allows multiple pcbs to match against the packet by design */ + while ((eaten == 0) && (pcb != NULL)) { + if ((pcb->protocol == proto) && raw_input_match(pcb, broadcast)) { + /* receive callback function available? */ + if (pcb->recv != NULL) { +#ifndef LWIP_NOASSERT + void* old_payload = p->payload; +#endif + /* the receive callback function did not eat the packet? */ + eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()); + if (eaten != 0) { + /* receive function ate the packet */ + p = NULL; + eaten = 1; + if (prev != NULL) { + /* move the pcb to the front of raw_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + } else { + /* sanity-check that the receive callback did not alter the pbuf */ + LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet", + p->payload == old_payload); + } + } + /* no receive callback function was set for this raw PCB */ + } + /* drop the packet */ + prev = pcb; + pcb = pcb->next; + } + return eaten; +} + +/** + * @ingroup raw_raw + * Bind a RAW PCB. + * + * @param pcb RAW PCB to be bound with a local address ipaddr. + * @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to + * bind to all local interfaces. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occurred. + * - ERR_USE. The specified IP address is already bound to by + * another RAW PCB. + * + * @see raw_disconnect() + */ +err_t +raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr) +{ + if ((pcb == NULL) || (ipaddr == NULL)) { + return ERR_VAL; + } + ip_addr_set_ipaddr(&pcb->local_ip, ipaddr); + return ERR_OK; +} + +/** + * @ingroup raw_raw + * Connect an RAW PCB. This function is required by upper layers + * of lwip. Using the raw api you could use raw_sendto() instead + * + * This will associate the RAW PCB with the remote address. + * + * @param pcb RAW PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * + * @return lwIP error code + * + * @see raw_disconnect() and raw_sendto() + */ +err_t +raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr) +{ + if ((pcb == NULL) || (ipaddr == NULL)) { + return ERR_VAL; + } + ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr); + return ERR_OK; +} + +/** + * @ingroup raw_raw + * Set the callback function for received packets that match the + * raw PCB's protocol and binding. + * + * The callback function MUST either + * - eat the packet by calling pbuf_free() and returning non-zero. The + * packet will not be passed to other raw PCBs or other protocol layers. + * - not free the packet, and return zero. The packet will be matched + * against further PCBs and/or forwarded to another protocol layers. + */ +void +raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * @ingroup raw_raw + * Send the raw IP packet to the given address. Note that actually you cannot + * modify the IP headers (this is inconsistent with the receive callback where + * you actually get the IP headers), you can only specify the IP payload here. + * It requires some more changes in lwIP. (there will be a raw_send() function + * then.) + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * @param ipaddr the destination address of the IP packet + * + */ +err_t +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr) +{ + err_t err; + struct netif *netif; + const ip_addr_t *src_ip; + struct pbuf *q; /* q will be sent down the stack */ + s16_t header_size; + + if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) { + return ERR_VAL; + } + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); + + header_size = ( +#if LWIP_IPV4 && LWIP_IPV6 + IP_IS_V6(ipaddr) ? IP6_HLEN : IP_HLEN); +#elif LWIP_IPV4 + IP_HLEN); +#else + IP6_HLEN); +#endif + + /* not enough space to add an IP header to first pbuf in given p chain? */ + if (pbuf_header(p, header_size)) { + /* allocate header in new pbuf */ + q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + } + /* { first pbuf q points to header pbuf } */ + LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* first pbuf q equals given pbuf */ + q = p; + if (pbuf_header(q, -header_size)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } + } + + if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { + /* Don't call ip_route() with IP_ANY_TYPE */ + netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(ipaddr)), ipaddr); + } else { + netif = ip_route(&pcb->local_ip, ipaddr); + } + + if (netif == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); + ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + +#if IP_SOF_BROADCAST + if (IP_IS_V4(ipaddr)) + { + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_VAL; + } + } +#endif /* IP_SOF_BROADCAST */ + + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = ip_netif_get_local_ip(netif, ipaddr); +#if LWIP_IPV6 + if (src_ip == NULL) { + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } +#endif /* LWIP_IPV6 */ + } else { + /* use RAW PCB local IP address as source address */ + src_ip = &pcb->local_ip; + } + +#if LWIP_IPV6 + /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542, + compute the checksum and update the checksum in the payload. */ + if (IP_IS_V6(ipaddr) && pcb->chksum_reqd) { + u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(ipaddr)); + LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2)); + SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t)); + } +#endif + + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); + err = ip_output_if(q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* did we chain a header earlier? */ + if (q != p) { + /* free the header */ + pbuf_free(q); + } + return err; +} + +/** + * @ingroup raw_raw + * Send the raw IP packet to the address given by raw_connect() + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * + */ +err_t +raw_send(struct raw_pcb *pcb, struct pbuf *p) +{ + return raw_sendto(pcb, p, &pcb->remote_ip); +} + +/** + * @ingroup raw_raw + * Remove an RAW PCB. + * + * @param pcb RAW PCB to be removed. The PCB is removed from the list of + * RAW PCB's and the data structure is freed from memory. + * + * @see raw_new() + */ +void +raw_remove(struct raw_pcb *pcb) +{ + struct raw_pcb *pcb2; + /* pcb to be removed is first in list? */ + if (raw_pcbs == pcb) { + /* make list start at 2nd pcb */ + raw_pcbs = raw_pcbs->next; + /* pcb not 1st in list */ + } else { + for (pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in raw_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + break; + } + } + } + memp_free(MEMP_RAW_PCB, pcb); +} + +/** + * @ingroup raw_raw + * Create a RAW PCB. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new(u8_t proto) +{ + struct raw_pcb *pcb; + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n")); + + pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB); + /* could allocate RAW PCB? */ + if (pcb != NULL) { + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct raw_pcb)); + pcb->protocol = proto; + pcb->ttl = RAW_TTL; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + return pcb; +} + +/** + * @ingroup raw_raw + * Create a RAW PCB for specific IP type. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param type IP address type, see @ref lwip_ip_addr_type definitions. + * If you want to listen to IPv4 and IPv6 (dual-stack) packets, + * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE. + * @param proto the protocol number (next header) of the IPv6 packet payload + * (e.g. IP6_NEXTH_ICMP6) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new_ip_type(u8_t type, u8_t proto) +{ + struct raw_pcb *pcb; + pcb = raw_new(proto); +#if LWIP_IPV4 && LWIP_IPV6 + if (pcb != NULL) { + IP_SET_TYPE_VAL(pcb->local_ip, type); + IP_SET_TYPE_VAL(pcb->remote_ip, type); + } +#else /* LWIP_IPV4 && LWIP_IPV6 */ + LWIP_UNUSED_ARG(type); +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + return pcb; +} + +/** This function is called from netif.c when address is changed + * + * @param old_addr IP address of the netif before change + * @param new_addr IP address of the netif after change + */ +void raw_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr) +{ + struct raw_pcb* rpcb; + + if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) { + for (rpcb = raw_pcbs; rpcb != NULL; rpcb = rpcb->next) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&rpcb->local_ip, old_addr)) { + /* The PCB is bound to the old ipaddr and + * is set to bound to the new one instead */ + ip_addr_copy(rpcb->local_ip, *new_addr); + } + } + } +} + +#endif /* LWIP_RAW */ diff --git a/sdk/Src/Network/lwip2.0.3/core/stats.c b/sdk/Src/Network/lwip2.0.3/core/stats.c new file mode 100644 index 0000000..32981a6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/stats.c @@ -0,0 +1,169 @@ +/** + * @file + * Statistics module + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/debug.h" + +#include + +struct stats_ lwip_stats; + +void +stats_init(void) +{ +#ifdef LWIP_DEBUG +#if MEM_STATS + lwip_stats.mem.name = "MEM"; +#endif /* MEM_STATS */ +#endif /* LWIP_DEBUG */ +} + +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); +} + +#if IGMP_STATS || MLD6_STATS +void +stats_display_igmp(struct stats_igmp *igmp, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); + LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); + LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group)); + LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general)); + LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); + LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); + LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); + LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n", igmp->tx_report)); +} +#endif /* IGMP_STATS || MLD6_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, const char *name) +{ + LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); + LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); +} + +#if MEMP_STATS +void +stats_display_memp(struct stats_mem *mem, int index) +{ + if (index < MEMP_MAX) { + stats_display_mem(mem, mem->name); + } +} +#endif /* MEMP_STATS */ +#endif /* MEM_STATS || MEMP_STATS */ + +#if SYS_STATS +void +stats_display_sys(struct stats_sys *sys) +{ + LWIP_PLATFORM_DIAG(("\nSYS\n\t")); + LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); + LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); + LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); + LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used)); + LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max)); + LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err)); + LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); + LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); + LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n", (u32_t)sys->mbox.err)); +} +#endif /* SYS_STATS */ + +void +stats_display(void) +{ + s16_t i; + + LINK_STATS_DISPLAY(); + ETHARP_STATS_DISPLAY(); + IPFRAG_STATS_DISPLAY(); + IP6_FRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + ND6_STATS_DISPLAY(); + IP6_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + MLD6_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + ICMP6_STATS_DISPLAY(); + UDP_STATS_DISPLAY(); + TCP_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); + for (i = 0; i < MEMP_MAX; i++) { + MEMP_STATS_DISPLAY(i); + } + SYS_STATS_DISPLAY(); +} +#endif /* LWIP_STATS_DISPLAY */ + +#endif /* LWIP_STATS */ + diff --git a/sdk/Src/Network/lwip2.0.3/core/sys.c b/sdk/Src/Network/lwip2.0.3/core/sys.c new file mode 100644 index 0000000..b309df5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/sys.c @@ -0,0 +1,107 @@ +/** + * @file + * lwIP Operating System abstraction + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/** + * @defgroup sys_layer Porting (system abstraction layer) + * @ingroup lwip + * @verbinclude "sys_arch.txt" + * + * @defgroup sys_os OS abstraction layer + * @ingroup sys_layer + * No need to implement functions in this section in NO_SYS mode. + * + * @defgroup sys_sem Semaphores + * @ingroup sys_os + * + * @defgroup sys_mutex Mutexes + * @ingroup sys_os + * Mutexes are recommended to correctly handle priority inversion, + * especially if you use LWIP_CORE_LOCKING . + * + * @defgroup sys_mbox Mailboxes + * @ingroup sys_os + * + * @defgroup sys_time Time + * @ingroup sys_layer + * + * @defgroup sys_prot Critical sections + * @ingroup sys_layer + * Used to protect short regions of code against concurrent access. + * - Your system is a bare-metal system (probably with an RTOS) + * and interrupts are under your control: + * Implement this as LockInterrupts() / UnlockInterrupts() + * - Your system uses an RTOS with deferred interrupt handling from a + * worker thread: Implement as a global mutex or lock/unlock scheduler + * - Your system uses a high-level OS with e.g. POSIX signals: + * Implement as a global mutex + * + * @defgroup sys_misc Misc + * @ingroup sys_os + */ + +#include "lwip/opt.h" + +#include "lwip/sys.h" +#include "wm_socket.h" + +/* Most of the functions defined in sys.h must be implemented in the + * architecture-dependent file sys_arch.c */ + +#if !NO_SYS + +#ifndef sys_msleep +/** + * Sleep for some ms. Timeouts are NOT processed while sleeping. + * + * @param ms number of milliseconds to sleep + */ +void +sys_msleep(u32_t ms) +{ + if (ms > 0) { + sys_sem_t delaysem; + err_t err = sys_sem_new(&delaysem, 0); + if (err == ERR_OK) { + sys_arch_sem_wait(&delaysem, ms); + sys_sem_free(&delaysem); + } + } +} +#endif /* sys_msleep */ + +#endif /* !NO_SYS */ diff --git a/sdk/Src/Network/lwip2.0.3/core/tcp.c b/sdk/Src/Network/lwip2.0.3/core/tcp.c new file mode 100644 index 0000000..c0a05ff --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/tcp.c @@ -0,0 +1,2296 @@ +/** + * @file + * Transmission Control Protocol for IP + * See also @ref tcp_raw + * + * @defgroup tcp_raw TCP + * @ingroup callbackstyle_api + * Transmission Control Protocol for IP\n + * @see @ref raw_api and @ref netconn + * + * Common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_in.c and tcp_out.c respectively.\n + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/tcp.h" +#include "lwip/priv/tcp_priv.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/nd6.h" +#include "lwip/alg.h" + +#include + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +#ifndef TCP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define TCP_LOCAL_PORT_RANGE_START 0xc000 +#define TCP_LOCAL_PORT_RANGE_END 0xffff +#define TCP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START)) +#endif + +#if LWIP_TCP_KEEPALIVE +#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl) +#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl) +#else /* LWIP_TCP_KEEPALIVE */ +#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE +#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT +#endif /* LWIP_TCP_KEEPALIVE */ + +/* As initial send MSS, we use TCP_MSS but limit it to 536. */ +#if TCP_MSS > 536 +#define INITIAL_MSS 536 +#else +#define INITIAL_MSS TCP_MSS +#endif + +static const char * const tcp_state_str[] = { + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED", + "FIN_WAIT_1", + "FIN_WAIT_2", + "CLOSE_WAIT", + "CLOSING", + "LAST_ACK", + "TIME_WAIT" +}; + +/* last local TCP port */ +static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; + +/* Incremented every coarse grained timer shot (typically every 500 ms). */ +u32_t tcp_ticks; +static const u8_t tcp_backoff[13] = + { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3}; + /* Times per slowtmr hits */ +static const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; + +/* The TCP PCB lists. */ + +/** List of all TCP PCBs bound but not yet (connected || listening) */ +struct tcp_pcb *tcp_bound_pcbs; +/** List of all TCP PCBs in LISTEN state */ +union tcp_listen_pcbs_t tcp_listen_pcbs; +/** List of all TCP PCBs that are in a state in which + * they accept or send data. */ +struct tcp_pcb *tcp_active_pcbs; +/** List of all TCP PCBs in TIME-WAIT state */ +struct tcp_pcb *tcp_tw_pcbs; + +/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ +struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, + &tcp_active_pcbs, &tcp_tw_pcbs}; + +u8_t tcp_active_pcbs_changed; + +#if SNDBUF_SHARE +s32 sndbuf_len = TCP_SND_BUF; +#endif + +/** Timer counter to handle calling slow-timer from tcp_tmr() */ +static u8_t tcp_timer; +static u8_t tcp_timer_ctr; +static u16_t tcp_new_port(void); + +static err_t tcp_close_shutdown_fin(struct tcp_pcb *pcb); + +/** + * Initialize this module. + */ +void +tcp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Called periodically to dispatch TCP timers. + */ +void +tcp_tmr(void) +{ + /* Call tcp_fasttmr() every 250 ms */ + tcp_fasttmr(); + + if (++tcp_timer & 1) { + /* Call tcp_slowtmr() every 500 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} + +#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG +/** Called when a listen pcb is closed. Iterates one pcb list and removes the + * closed listener pcb from pcb->listener if matching. + */ +static void +tcp_remove_listener(struct tcp_pcb *list, struct tcp_pcb_listen *lpcb) +{ + struct tcp_pcb *pcb; + for (pcb = list; pcb != NULL; pcb = pcb->next) { + if (pcb->listener == lpcb) { + pcb->listener = NULL; + } + } +} +#endif + +/** Called when a listen pcb is closed. Iterates all pcb lists and removes the + * closed listener pcb from pcb->listener if matching. + */ +static void +tcp_listen_closed(struct tcp_pcb *pcb) +{ +#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG + size_t i; + LWIP_ASSERT("pcb != NULL", pcb != NULL); + LWIP_ASSERT("pcb->state == LISTEN", pcb->state == LISTEN); + for (i = 1; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) { + tcp_remove_listener(*tcp_pcb_lists[i], (struct tcp_pcb_listen*)pcb); + } +#endif + LWIP_UNUSED_ARG(pcb); +} + +#if TCP_LISTEN_BACKLOG +/** @ingroup tcp_raw + * Delay accepting a connection in respect to the listen backlog: + * the number of outstanding connections is increased until + * tcp_backlog_accepted() is called. + * + * ATTENTION: the caller is responsible for calling tcp_backlog_accepted() + * or else the backlog feature will get out of sync! + * + * @param pcb the connection pcb which is not fully accepted yet + */ +void +tcp_backlog_delayed(struct tcp_pcb* pcb) +{ + LWIP_ASSERT("pcb != NULL", pcb != NULL); + if ((pcb->flags & TF_BACKLOGPEND) == 0) { + if (pcb->listener != NULL) { + pcb->listener->accepts_pending++; + LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0); + pcb->flags |= TF_BACKLOGPEND; + } + } +} + +/** @ingroup tcp_raw + * A delayed-accept a connection is accepted (or closed/aborted): decreases + * the number of outstanding connections after calling tcp_backlog_delayed(). + * + * ATTENTION: the caller is responsible for calling tcp_backlog_accepted() + * or else the backlog feature will get out of sync! + * + * @param pcb the connection pcb which is now fully accepted (or closed/aborted) + */ +void +tcp_backlog_accepted(struct tcp_pcb* pcb) +{ + LWIP_ASSERT("pcb != NULL", pcb != NULL); + if ((pcb->flags & TF_BACKLOGPEND) != 0) { + if (pcb->listener != NULL) { + LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0); + if(pcb->listener->accepts_pending>0) + pcb->listener->accepts_pending--; + pcb->flags &= ~TF_BACKLOGPEND; + } + } +} +#endif /* TCP_LISTEN_BACKLOG */ + +/** + * Closes the TX side of a connection held by the PCB. + * For tcp_close(), a RST is sent if the application didn't receive all data + * (tcp_recved() not called for all data passed to recv callback). + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it. + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +static err_t +tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) +{ + if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { + if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND_MAX(pcb))) { + /* Not all data received by application, send RST to tell the remote + side about this. */ + LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); + + /* don't call tcp_abort here: we must not deallocate the pcb since + that might not be expected when calling tcp_close */ + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + if (pcb->state == ESTABLISHED) { + /* move to TIME_WAIT since we close actively */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */ + if (tcp_input_pcb == pcb) { + /* prevent using a deallocated pcb: free it from tcp_input later */ + tcp_trigger_input_pcb_close(); + } else { + memp_free(MEMP_TCP_PCB, pcb); + } + } + return ERR_OK; + } + } + + /* - states which free the pcb are handled here, + - states which send FIN and change state are handled in tcp_close_shutdown_fin() */ + switch (pcb->state) { + case CLOSED: + /* Closing a pcb in the CLOSED state might seem erroneous, + * however, it is in this state once allocated and as yet unused + * and the user needs some way to free it should the need arise. + * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) + * or for a pcb that has been used and then entered the CLOSED state + * is erroneous, but this should never happen as the pcb has in those cases + * been freed, and so any remaining handles are bogus. */ + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); + break; + case LISTEN: + tcp_listen_closed(pcb); + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + break; + case SYN_SENT: + TCP_PCB_REMOVE_ACTIVE(pcb); + memp_free(MEMP_TCP_PCB, pcb); + MIB2_STATS_INC(mib2.tcpattemptfails); + break; + default: + return tcp_close_shutdown_fin(pcb); + } + return ERR_OK; +} + +static err_t +tcp_close_shutdown_fin(struct tcp_pcb *pcb) +{ + err_t err; + LWIP_ASSERT("pcb != NULL", pcb != NULL); + + switch (pcb->state) { + case SYN_RCVD: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + tcp_backlog_accepted(pcb); + MIB2_STATS_INC(mib2.tcpattemptfails); + pcb->state = FIN_WAIT_1; + } + break; + case ESTABLISHED: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + MIB2_STATS_INC(mib2.tcpestabresets); + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + MIB2_STATS_INC(mib2.tcpestabresets); + pcb->state = LAST_ACK; + } + break; + default: + /* Has already been closed, do nothing. */ + return ERR_OK; + } + + if (err == ERR_OK) { + /* To ensure all data has been sent when tcp_close returns, we have + to make sure tcp_output doesn't fail. + Since we don't really have to ensure all data has been sent when tcp_close + returns (unsent data is sent from tcp timer functions, also), we don't care + for the return value of tcp_output for now. */ + tcp_output(pcb); + } else if (err == ERR_MEM) { + /* Mark this pcb for closing. Closing is retried from tcp_tmr. */ + pcb->flags |= TF_CLOSEPEND; + /* We have to return ERR_OK from here to indicate to the callers that this + pcb should not be used any more as it will be freed soon via tcp_tmr. + This is OK here since sending FIN does not guarantee a time frime for + actually freeing the pcb, either (it is left in closure states for + remote ACK or timeout) */ + return ERR_OK; + } + return err; +} + +/** + * @ingroup tcp_raw + * Closes the connection held by the PCB. + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it (unless an error is returned). + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); + tcp_debug_print_state(pcb->state); + + if (pcb->state != LISTEN) { + /* Set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + } + /* ... and close */ + return tcp_close_shutdown(pcb, 1); +} + +/** + * @ingroup tcp_raw + * Causes all or part of a full-duplex connection of this PCB to be shut down. + * This doesn't deallocate the PCB unless shutting down both sides! + * Shutting down both sides is the same as calling tcp_close, so if it succeds + * (i.e. returns ER_OK), the PCB must not be referenced any more! + * + * @param pcb PCB to shutdown + * @param shut_rx shut down receive side if this is != 0 + * @param shut_tx shut down send side if this is != 0 + * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down) + * another err_t on error. + */ +err_t +tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx) +{ + if (pcb->state == LISTEN) { + return ERR_CONN; + } + if (shut_rx) { + /* shut down the receive side: set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + if (shut_tx) { + /* shutting down the tx AND rx side is the same as closing for the raw API */ + return tcp_close_shutdown(pcb, 1); + } + /* ... and free buffered data */ + if (pcb->refused_data != NULL) { + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + } + if (shut_tx) { + /* This can't happen twice since if it succeeds, the pcb's state is changed. + Only close in these states as the others directly deallocate the PCB */ + switch (pcb->state) { + case SYN_RCVD: + case ESTABLISHED: + case CLOSE_WAIT: + return tcp_close_shutdown(pcb, (u8_t)shut_rx); + default: + /* Not (yet?) connected, cannot shutdown the TX side as that would bring us + into CLOSED state, where the PCB is deallocated. */ + return ERR_CONN; + } + } + return ERR_OK; +} + +/** + * Abandons a connection and optionally sends a RST to the remote + * host. Deletes the local protocol control block. This is done when + * a connection is killed because of shortage of memory. + * + * @param pcb the tcp_pcb to abort + * @param reset boolean to indicate whether a reset should be sent + */ +void +tcp_abandon(struct tcp_pcb *pcb, int reset) +{ + u32_t seqno, ackno; +#if LWIP_CALLBACK_API + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs", + pcb->state != LISTEN); + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if (pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + int send_rst = 0; + u16_t local_port = 0; + enum tcp_state last_state; + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + if (pcb->state == CLOSED) { + if (pcb->local_port != 0) { + /* bound, not yet opened */ + TCP_RMV(&tcp_bound_pcbs, pcb); + } + } else { + send_rst = reset; + local_port = pcb->local_port; + TCP_PCB_REMOVE_ACTIVE(pcb); + } + if (pcb->unacked != NULL) { + tcp_segs_free(pcb->unacked); + } + if (pcb->unsent != NULL) { + tcp_segs_free(pcb->unsent); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + tcp_segs_free(pcb->ooseq); + } +#endif /* TCP_QUEUE_OOSEQ */ + tcp_backlog_accepted(pcb); + if (send_rst) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, local_port, pcb->remote_port); + } + #if SNDBUF_SHARE + if(pcb->snd_buf < TCP_SND_BUF) + { + sndbuf_len += (TCP_SND_BUF - pcb->snd_buf); + pcb->snd_buf = TCP_SND_BUF; + } +#endif + last_state = pcb->state; + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(last_state, errf, errf_arg, ERR_ABRT); + } +} + +/** + * @ingroup tcp_raw + * Aborts the connection by sending a RST (reset) segment to the remote + * host. The pcb is deallocated. This function never fails. + * + * ATTENTION: When calling this from one of the TCP callbacks, make + * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise + * or you will risk accessing deallocated memory or memory leaks! + * + * @param pcb the tcp pcb to abort + */ +void +tcp_abort(struct tcp_pcb *pcb) +{ + tcp_abandon(pcb, 1); +} + +/** + * @ingroup tcp_raw + * Binds the connection to a local port number and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + * @param pcb the tcp_pcb to bind (no check is done whether this pcb is + * already bound!) + * @param ipaddr the local ip address to bind to (use IP4_ADDR_ANY to bind + * to any local address + * @param port the local port to bind to + * @return ERR_USE if the port is already in use + * ERR_VAL if bind failed because the PCB is not in a valid state + * ERR_OK if bound + */ +err_t +tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) +{ + int i; + int max_pcb_list = NUM_TCP_PCB_LISTS; + struct tcp_pcb *cpcb; + +#if LWIP_IPV4 + /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */ + if (ipaddr == NULL) { + ipaddr = IP4_ADDR_ANY; + } +#endif /* LWIP_IPV4 */ + +#if TLS_CONFIG_AP_OPT_FWD + if (alg_napt_port_is_used(port)) + { + return ERR_USE; + } +#endif + + /* still need to check for ipaddr == NULL in IPv6 only case */ + if ((pcb == NULL) || (ipaddr == NULL)) { + return ERR_VAL; + } + + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL); + +#if SO_REUSE + /* Unless the REUSEADDR flag is set, + we have to check the pcbs in TIME-WAIT state, also. + We do not dump TIME_WAIT pcb's; they can still be matched by incoming + packets using both local and remote IP addresses and ports to distinguish. + */ + if (ip_get_option(pcb, SOF_REUSEADDR)) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ + + if (port == 0) { + port = tcp_new_port(); + if (port == 0) { + return ERR_BUF; + } + } else { + /* Check if the address already is in use (on all lists) */ + for (i = 0; i < max_pcb_list; i++) { + for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { +#if SO_REUSE + /* Omit checking for the same port if both pcbs have REUSEADDR set. + For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in + tcp_connect. */ + if (!ip_get_option(pcb, SOF_REUSEADDR) || + !ip_get_option(cpcb, SOF_REUSEADDR)) +#endif /* SO_REUSE */ + { + /* @todo: check accept_any_ip_version */ + if (i == (NUM_TCP_PCB_LISTS - 1) ){ + if ((IP_IS_V6(ipaddr) == IP_IS_V6_VAL(cpcb->local_ip)) && + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + else + { + if ((IP_IS_V6(ipaddr) == IP_IS_V6_VAL(cpcb->local_ip)) && + (ip_addr_isany(&cpcb->local_ip) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&cpcb->local_ip, ipaddr))) { + return ERR_USE; + } + } + } + } + } + } + } + + if (!ip_addr_isany(ipaddr)) { + ip_addr_set(&pcb->local_ip, ipaddr); + } + pcb->local_port = port; + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); + return ERR_OK; +} +#if LWIP_CALLBACK_API +/** + * Default accept callback if no accept callback is specified by the user. + */ +static err_t +tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + + tcp_abort(pcb); + + return ERR_ABRT; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * @ingroup tcp_raw + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + * + * @param pcb the original tcp_pcb + * @param backlog the incoming connections queue limit + * @return tcp_pcb used for listening, consumes less memory. + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen_with_backlog(tpcb, backlog); + */ +struct tcp_pcb * +tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + return tcp_listen_with_backlog_and_err(pcb, backlog, NULL); +} + +/** + * @ingroup tcp_raw + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + * + * @param pcb the original tcp_pcb + * @param backlog the incoming connections queue limit + * @param err when NULL is returned, this contains the error reason + * @return tcp_pcb used for listening, consumes less memory. + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen_with_backlog_and_err(tpcb, backlog, &err); + */ +struct tcp_pcb * +tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err) +{ + struct tcp_pcb_listen *lpcb = NULL; + err_t res; + + LWIP_UNUSED_ARG(backlog); + LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, res = ERR_CLSD; goto done); + + /* already listening? */ + if (pcb->state == LISTEN) { + lpcb = (struct tcp_pcb_listen*)pcb; + res = ERR_ALREADY; + goto done; + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR)) { + /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage + is declared (listen-/connection-pcb), we have to make sure now that + this port is only used once for every local IP. */ + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((lpcb->local_port == pcb->local_port) && + ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) { + /* this address/port is already used */ + lpcb = NULL; + res = ERR_USE; + goto done; + } + } + } +#endif /* SO_REUSE */ + lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN); + if (lpcb == NULL) { + res = ERR_MEM; + goto done; + } + lpcb->callback_arg = pcb->callback_arg; + lpcb->local_port = pcb->local_port; + lpcb->state = LISTEN; + lpcb->prio = pcb->prio; + lpcb->so_options = pcb->so_options; + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; +#if LWIP_IPV4 && LWIP_IPV6 + IP_SET_TYPE_VAL(lpcb->remote_ip, pcb->local_ip.type); +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + ip_addr_copy(lpcb->local_ip, pcb->local_ip); + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); +#if LWIP_CALLBACK_API + lpcb->accept = tcp_accept_null; +#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + lpcb->accepts_pending = 0; + tcp_backlog_set(lpcb, backlog); +#endif /* TCP_LISTEN_BACKLOG */ + TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb); + res = ERR_OK; +done: + if (err != NULL) { + *err = res; + } + return (struct tcp_pcb *)lpcb; +} + +/** + * Update the state that tracks the available window space to advertise. + * + * Returns how much extra window would be advertised if we sent an + * update now. + */ +u32_t +tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) +{ + u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; + + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + /* we can advertise more window */ + pcb->rcv_ann_wnd = pcb->rcv_wnd; + return new_right_edge - pcb->rcv_ann_right_edge; + } else { + if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) { + /* Can happen due to other end sending out of advertised window, + * but within actual available (but not yet advertised) window */ + pcb->rcv_ann_wnd = 0; + } else { + /* keep the right edge of window constant */ + u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt; +#if !LWIP_WND_SCALE + LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); +#endif + pcb->rcv_ann_wnd = (tcpwnd_size_t)new_rcv_ann_wnd; + } + return 0; + } +} + +/** + * @ingroup tcp_raw + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + * + * @param pcb the tcp_pcb for which data is read + * @param len the amount of bytes that have been read by the application + */ +void +tcp_recved(struct tcp_pcb *pcb, u16_t len) +{ + int wnd_inflation; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_recved for listen-pcbs", + pcb->state != LISTEN); + + pcb->rcv_wnd += len; + if (pcb->rcv_wnd > TCP_WND_MAX(pcb)) { + pcb->rcv_wnd = TCP_WND_MAX(pcb); + } else if (pcb->rcv_wnd == 0) { + /* rcv_wnd overflowed */ + if ((pcb->state == CLOSE_WAIT) || (pcb->state == LAST_ACK)) { + /* In passive close, we allow this, since the FIN bit is added to rcv_wnd + by the stack itself, since it is not mandatory for an application + to call tcp_recved() for the FIN bit, but e.g. the netconn API does so. */ + pcb->rcv_wnd = TCP_WND_MAX(pcb); + } else { + LWIP_ASSERT("tcp_recved: len wrapped rcv_wnd\n", 0); + } + } + + wnd_inflation = tcp_update_rcv_ann_wnd(pcb); + + /* If the change in the right edge of window is significant (default + * watermark is TCP_WND/4), then send an explicit update now. + * Otherwise wait for a packet to be sent in the normal course of + * events (or more window to be available later) */ + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) { + tcp_ack_now(pcb); + tcp_output(pcb); + } + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: received %"U16_F" bytes, wnd %"TCPWNDSIZE_F" (%"TCPWNDSIZE_F").\n", + len, pcb->rcv_wnd, (u16_t)(TCP_WND_MAX(pcb) - pcb->rcv_wnd))); +} + +/** + * Allocate a new local TCP port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + u8_t i; + u16_t n = 0; + struct tcp_pcb *pcb; + static u8 firstflag = 1; + + if(firstflag) + { + extern int tls_wl_get_isr_count(void); + firstflag = 0; + srand(tls_wl_get_isr_count() + tcp_port); /*Using Wi-Fi Rx dataCnt as Random Seed*/ + tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); + } + + +again: + if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) { + tcp_port = TCP_LOCAL_PORT_RANGE_START; + } + /* Check all PCB lists. */ + for (i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for (pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == tcp_port) { + if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + } + return tcp_port; +} + +/** + * @ingroup tcp_raw + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @param port the remote tcp port to connect to + * @param connected callback function to call when connected (on error, + the err calback will be called) + * @return ERR_VAL if invalid arguments are given + * ERR_OK if connect request has been sent + * other err_t values if connect request couldn't be sent + */ +err_t +tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, + tcp_connected_fn connected) +{ + err_t ret; + u32_t iss; + u16_t old_local_port; + + if ((pcb == NULL) || (ipaddr == NULL)) { + return ERR_VAL; + } + + LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + ip_addr_set(&pcb->remote_ip, ipaddr); + pcb->remote_port = port; + + /* check if we have a route to the remote host */ + if (ip_addr_isany(&pcb->local_ip)) { + /* no local IP address set, yet. */ + struct netif *netif; + const ip_addr_t *local_ip; + ip_route_get_local_ip(&pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Use the address as local address of the pcb. */ + ip_addr_copy(pcb->local_ip, *local_ip); + } + + old_local_port = pcb->local_port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + if (pcb->local_port == 0) { + return ERR_BUF; + } + } else { +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR)) { + /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure + now that the 5-tuple is unique. */ + struct tcp_pcb *cpcb; + int i; + /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */ + for (i = 2; i < NUM_TCP_PCB_LISTS; i++) { + for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if ((cpcb->local_port == pcb->local_port) && + (cpcb->remote_port == port) && + ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) && + ip_addr_cmp(&cpcb->remote_ip, ipaddr)) { + /* linux returns EISCONN here, but ERR_USE should be OK for us */ + return ERR_USE; + } + } + } + } +#endif /* SO_REUSE */ + } + + iss = tcp_next_iss(pcb); + pcb->rcv_nxt = 0; + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_wl2 = iss - 1; + pcb->snd_lbb = iss - 1; + /* Start with a window that does not need scaling. When window scaling is + enabled and used, the window is enlarged when both sides agree on scaling. */ + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND); + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->snd_wnd = TCP_WND; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = INITIAL_MSS; +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + pcb->cwnd = 1; +#if LWIP_CALLBACK_API + pcb->connected = connected; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(connected); +#endif /* LWIP_CALLBACK_API */ + + /* Send a SYN together with the MSS option. */ + ret = tcp_enqueue_flags(pcb, TCP_SYN); + if (ret == ERR_OK) { + /* SYN segment was enqueued, changed the pcbs state now */ + pcb->state = SYN_SENT; + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG_ACTIVE(pcb); + MIB2_STATS_INC(mib2.tcpactiveopens); + + tcp_output(pcb); + } + return ret; +} + +/** + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_slowtmr(void) +{ + struct tcp_pcb *pcb, *prev; + tcpwnd_size_t eff_wnd; + u8_t pcb_remove; /* flag if a PCB should be removed */ + u8_t pcb_reset; /* flag if a RST should be sent when removing */ + err_t err; + + err = ERR_OK; + + ++tcp_ticks; + ++tcp_timer_ctr; + +tcp_slowtmr_start: + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + if (pcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); + } + while (pcb != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + if (pcb->last_timer == tcp_timer_ctr) { + /* skip this pcb, we have already processed it */ + pcb = pcb->next; + continue; + } + pcb->last_timer = tcp_timer_ctr; + + pcb_remove = 0; + pcb_reset = 0; + + if (pcb->state == SYN_SENT && pcb->nrtx >= TCP_SYNMAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); + } + else if (pcb->nrtx >= TCP_MAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); + } else { + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1]; + if (pcb->persist_cnt < backoff_cnt) { + pcb->persist_cnt++; + } + if (pcb->persist_cnt >= backoff_cnt) { + if (tcp_zero_window_probe(pcb) == ERR_OK) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + } + } + } else { + /* Increase the retransmission timer if it is running */ + if (pcb->rtime >= 0) { + ++pcb->rtime; + } + + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + u8_t backoff_idx = LWIP_MIN(pcb->nrtx, sizeof(tcp_backoff)-1); + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx]; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < (tcpwnd_size_t)(pcb->mss << 1)) { + pcb->ssthresh = (pcb->mss << 1); + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"TCPWNDSIZE_F + " ssthresh %"TCPWNDSIZE_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); + } + } + } + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if (pcb->state == FIN_WAIT_2) { + /* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */ + if (pcb->flags & TF_RXCLOSED) { + /* PCB was fully closed (either through close() or SHUT_RDWR): + normal FIN-WAIT timeout handling. */ + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } + } + } + + /* Check if KEEPALIVE should be sent */ + if (ip_get_option(pcb, SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to ")); + ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + ++pcb_remove; + ++pcb_reset; + } else if ((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) + / TCP_SLOW_INTERVAL) + { + err = tcp_keepalive(pcb); + if (err == ERR_OK) { + pcb->keep_cnt_sent++; + } + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if (pcb->state == SYN_RCVD) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); + } + } + + /* Check if this PCB has stayed too long in LAST-ACK */ + if (pcb->state == LAST_ACK) { + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); + } + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; +#if LWIP_CALLBACK_API + tcp_err_fn err_fn = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + void *err_arg; + enum tcp_state last_state; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + if (pcb_reset) { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + } + + err_arg = pcb->callback_arg; + last_state = pcb->state; + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + + tcp_active_pcbs_changed = 0; + TCP_EVENT_ERR(last_state, err_fn, err_arg, ERR_ABRT); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + } else { + /* get the 'next' element now and work with 'prev' below (in case of abort) */ + prev = pcb; + pcb = pcb->next; + + /* We check if we should poll the connection. */ + ++prev->polltmr; + if (prev->polltmr >= prev->pollinterval) { + prev->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + tcp_active_pcbs_changed = 0; + TCP_EVENT_POLL(prev, err); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + /* if err == ERR_ABRT, 'prev' is already deallocated */ + if (err == ERR_OK) { + tcp_output(prev); + } + } + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while (pcb != NULL) { + LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +/** + * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously + * "refused" by upper layer (application) and sends delayed ACKs. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb; + + ++tcp_timer_ctr; + +tcp_fasttmr_start: + pcb = tcp_active_pcbs; + + while (pcb != NULL) { + if (pcb->last_timer != tcp_timer_ctr) { + struct tcp_pcb *next; + pcb->last_timer = tcp_timer_ctr; + /* send delayed ACKs */ + if (pcb->flags & TF_ACK_DELAY) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + tcp_output(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + /* send pending FIN */ + if (pcb->flags & TF_CLOSEPEND) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: pending FIN\n")); + pcb->flags &= ~(TF_CLOSEPEND); + tcp_close_shutdown_fin(pcb); + } + + next = pcb->next; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + tcp_active_pcbs_changed = 0; + tcp_process_refused_data(pcb); + if (tcp_active_pcbs_changed) { + /* application callback has changed the pcb list: restart the loop */ + goto tcp_fasttmr_start; + } + } + pcb = next; + } else { + pcb = pcb->next; + } + } +} + +/** Call tcp_output for all active pcbs that have TF_NAGLEMEMERR set */ +void +tcp_txnow(void) +{ + struct tcp_pcb *pcb; + + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->flags & TF_NAGLEMEMERR) { + tcp_output(pcb); + } + } +} + +/** Pass pcb->refused_data to the recv callback */ +err_t +tcp_process_refused_data(struct tcp_pcb *pcb) +{ +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + struct pbuf *rest; + while (pcb->refused_data != NULL) +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + { + err_t err; + u8_t refused_flags = pcb->refused_data->flags; + /* set pcb->refused_data to NULL in case the callback frees it and then + closes the pcb */ + struct pbuf *refused_data = pcb->refused_data; +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + pbuf_split_64k(refused_data, &rest); + pcb->refused_data = rest; +#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + pcb->refused_data = NULL; +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err); + if (err == ERR_OK) { + /* did refused_data include a FIN? */ + if (refused_flags & PBUF_FLAG_TCP_FIN +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + && (rest == NULL) +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + ) { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + } + } else if (err == ERR_ABRT) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + /* Drop incoming packets because pcb is "full" (only if the incoming + segment contains data). */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + return ERR_ABRT; + } else { + /* data is still refused, pbuf is still valid (go on for ACK-only packets) */ +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + if (rest != NULL) { + pbuf_cat(refused_data, rest); + } +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + pcb->refused_data = refused_data; + return ERR_INPROGRESS; + } + } + return ERR_OK; +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + * @param seg tcp_seg list of TCP segments to free + */ +void +tcp_segs_free(struct tcp_seg *seg) +{ + while (seg != NULL) { + struct tcp_seg *next = seg->next; + tcp_seg_free(seg); + seg = next; + } +} + +/** + * Frees a TCP segment (tcp_seg structure). + * + * @param seg single tcp_seg to free + */ +void +tcp_seg_free(struct tcp_seg *seg) +{ + if (seg != NULL) { + if (seg->p != NULL) { + pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } +} + +/** + * Sets the priority of a connection. + * + * @param pcb the tcp_pcb to manipulate + * @param prio new priority + */ +void +tcp_setprio(struct tcp_pcb *pcb, u8_t prio) +{ + pcb->prio = prio; +} + +#if TCP_QUEUE_OOSEQ +/** + * Returns a copy of the given TCP segment. + * The pbuf and data are not copied, only the pointers + * + * @param seg the old tcp_seg + * @return a copy of seg + */ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG); + if (cseg == NULL) { + return NULL; + } + SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +#endif /* TCP_QUEUE_OOSEQ */ + +#if LWIP_CALLBACK_API +/** + * Default receive callback that is called if the user didn't register + * a recv callback for the pcb. + */ +err_t +tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + LWIP_UNUSED_ARG(arg); + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } else if (err == ERR_OK) { + return tcp_close(pcb); + } + return ERR_OK; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Kills the oldest active connection that has the same or lower priority than + * 'prio'. + * + * @param prio minimum priority + */ +static void +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + u8_t mprio; + + mprio = LWIP_MIN(TCP_PRIO_MAX, prio); + + /* We kill the oldest active connection that has lower priority than prio. */ + inactivity = 0; + inactive = NULL; + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->prio <= mprio && + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + mprio = pcb->prio; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Kills the oldest connection that is in specific state. + * Called from tcp_alloc() for LAST_ACK and CLOSING if no more connections are available. + */ +static void +tcp_kill_state(enum tcp_state state) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK)); + + inactivity = 0; + inactive = NULL; + /* Go through the list of active pcbs and get the oldest pcb that is in state + CLOSING/LAST_ACK. */ + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->state == state) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_closing: killing oldest %s PCB %p (%"S32_F")\n", + tcp_state_str[state], (void *)inactive, inactivity)); + /* Don't send a RST, since no data is lost. */ + tcp_abandon(inactive, 0); + } +} + +/** + * Kills the oldest connection that is in TIME_WAIT state. + * Called from tcp_alloc() if no more connections are available. + */ +static void +tcp_kill_timewait(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +static void +tcp_kill_finwait2(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->state == FIN_WAIT_1 || pcb->state == FIN_WAIT_2) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + } + if (inactive != NULL) { + tcp_pcb_remove(&tcp_active_pcbs, inactive); + memp_free(MEMP_TCP_PCB, inactive); + } +} + +static void +tcp_kill_lastack(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->state == LAST_ACK ) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + } + if (inactive != NULL) { + tcp_pcb_remove(&tcp_active_pcbs, inactive); + memp_free(MEMP_TCP_PCB, inactive); + } +} + + +/** + * Allocate a new tcp_pcb structure. + * + * @param prio priority for the new pcb + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_alloc(u8_t prio) +{ + struct tcp_pcb *pcb; + struct tcp_pcb *pcb_tw; + u32_t n = 0; + + pcb_tw = tcp_tw_pcbs; + while (pcb_tw != NULL) { + n++; + pcb_tw = pcb_tw->next; + } + if (n > TCP_DEFAULT_LISTEN_BACKLOG) + tcp_kill_timewait(); + + pcb_tw = tcp_active_pcbs; + while (pcb_tw != NULL) { + if (pcb_tw->state == FIN_WAIT_2 || pcb_tw->state == FIN_WAIT_1) + { + n++; + } + pcb_tw = pcb_tw->next; + } + + if (n > MEMP_NUM_TCP_PCB) + tcp_kill_finwait2(); + + pcb_tw = tcp_active_pcbs; + while (pcb_tw != NULL) { + if (pcb_tw->state == LAST_ACK) + { + n++; + } + pcb_tw = pcb_tw->next; + } + if (n > MEMP_NUM_TCP_PCB) + tcp_kill_lastack(); + + + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing oldest connection in TIME-WAIT. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); + tcp_kill_timewait(); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing oldest connection in LAST-ACK (these wouldn't go to TIME-WAIT). */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest LAST-ACK connection\n")); + tcp_kill_state(LAST_ACK); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing oldest connection in CLOSING. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest CLOSING connection\n")); + tcp_kill_state(CLOSING); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing active connections with lower priority than the new one. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio)); + tcp_kill_prio(prio); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed multiple times before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed multiple times before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed multiple times before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed above */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* zero out the whole pcb, so there is no need to initialize members to zero */ + memset(pcb, 0, sizeof(struct tcp_pcb)); + pcb->prio = prio; + pcb->snd_buf = TCP_SND_BUF; + /* Start with a window that does not need scaling. When window scaling is + enabled and used, the window is enlarged when both sides agree on scaling. */ + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND); + pcb->ttl = TCP_TTL; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = INITIAL_MSS; + pcb->rto = 3000 / TCP_SLOW_INTERVAL; + pcb->sv = 3000 / TCP_SLOW_INTERVAL; + pcb->rtime = -1; + pcb->cwnd = 1; + pcb->tmr = tcp_ticks; + pcb->last_timer = tcp_timer_ctr; + + /* RFC 5681 recommends setting ssthresh abritrarily high and gives an example + of using the largest advertised receive window. We've seen complications with + receiving TCPs that use window scaling and/or window auto-tuning where the + initial advertised window is very small and then grows rapidly once the + connection is established. To avoid these complications, we set ssthresh to the + largest effective cwnd (amount of in-flight data) that the sender can have. */ + pcb->ssthresh = TCP_SND_BUF; + +#if LWIP_CALLBACK_API + pcb->recv = tcp_recv_null; +#endif /* LWIP_CALLBACK_API */ + + /* Init KEEPALIVE timer */ + pcb->keep_idle = TCP_KEEPIDLE_DEFAULT; + +#if LWIP_TCP_KEEPALIVE + pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT; + pcb->keep_cnt = TCP_KEEPCNT_DEFAULT; +#endif /* LWIP_TCP_KEEPALIVE */ + } + return pcb; +} + +/** + * @ingroup tcp_raw + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @internal: Maybe there should be a idle TCP PCB list where these + * PCBs are put on. Port reservation using tcp_bind() is implemented but + * allocated pcbs that are not bound can't be killed automatically if wanting + * to allocate a pcb with higher prio (@see tcp_kill_prio()) + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new(void) +{ + return tcp_alloc(TCP_PRIO_NORMAL); +} + +/** + * @ingroup tcp_raw + * Creates a new TCP protocol control block but doesn't + * place it on any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @param type IP address type, see @ref lwip_ip_addr_type definitions. + * If you want to listen to IPv4 and IPv6 (dual-stack) connections, + * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE. + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new_ip_type(u8_t type) +{ + struct tcp_pcb * pcb; + pcb = tcp_alloc(TCP_PRIO_NORMAL); +#if LWIP_IPV4 && LWIP_IPV6 + if (pcb != NULL) { + IP_SET_TYPE_VAL(pcb->local_ip, type); + IP_SET_TYPE_VAL(pcb->remote_ip, type); + } +#else + LWIP_UNUSED_ARG(type); +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + return pcb; +} + +/** + * @ingroup tcp_raw + * Used to specify the argument that should be passed callback + * functions. + * + * @param pcb tcp_pcb to set the callback argument + * @param arg void pointer argument to pass to callback functions + */ +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + if (pcb != NULL) { + pcb->callback_arg = arg; + } +} +#if LWIP_CALLBACK_API + +/** + * @ingroup tcp_raw + * Used to specify the function that should be called when a TCP + * connection receives data. + * + * @param pcb tcp_pcb to set the recv callback + * @param recv callback function to call for this pcb when data is received + */ +void +tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) +{ + if (pcb != NULL) { + LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN); + pcb->recv = recv; + } +} + +/** + * @ingroup tcp_raw + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + * @param pcb tcp_pcb to set the sent callback + * @param sent callback function to call for this pcb when data is successfully sent + */ +void +tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) +{ + if (pcb != NULL) { + LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN); + pcb->sent = sent; + } +} + +/** + * @ingroup tcp_raw + * Used to specify the function that should be called when a fatal error + * has occurred on the connection. + * + * @note The corresponding pcb is already freed when this callback is called! + * + * @param pcb tcp_pcb to set the err callback + * @param err callback function to call for this pcb when a fatal error + * has occurred on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) +{ + if (pcb != NULL) { + LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN); + pcb->errf = err; + } +} + +/** + * @ingroup tcp_raw + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + * @param pcb tcp_pcb to set the accept callback + * @param accept callback function to call for this pcb when LISTENing + * connection has been connected to another host + */ +void +tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) +{ + if ((pcb != NULL) && (pcb->state == LISTEN)) { + struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen*)pcb; + lpcb->accept = accept; + } +} +#endif /* LWIP_CALLBACK_API */ + + +/** + * @ingroup tcp_raw + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) +{ + LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN); +#if LWIP_CALLBACK_API + pcb->poll = poll; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(poll); +#endif /* LWIP_CALLBACK_API */ + pcb->pollinterval = interval; +} + +/** + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory + * (pcb->ooseq, pcb->unsent and pcb->unacked are freed). + * + * @param pcb tcp_pcb to purge. The pcb itself is not deallocated! + */ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if (pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); + + tcp_backlog_accepted(pcb); + + if (pcb->refused_data != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n")); + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + if (pcb->unsent != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if (pcb->unacked != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; +#if TCP_OVERSIZE + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ +#if SNDBUF_SHARE + if(pcb->snd_buf < TCP_SND_BUF) + { + sndbuf_len += (TCP_SND_BUF - pcb->snd_buf); + pcb->snd_buf = TCP_SND_BUF; + } +#endif + } +} + +/** + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + * @param pcblist PCB list to purge. + * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated! + */ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if (pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + + if (pcb->state != LISTEN) { + LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL); + LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL); +#if TCP_QUEUE_OOSEQ + LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL); +#endif /* TCP_QUEUE_OOSEQ */ + } + + pcb->state = CLOSED; + /* reset the local port to prevent the pcb from being 'bound' */ + pcb->local_port = 0; + + LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} + +/** + * Calculates a new initial sequence number for new connections. + * + * @return u32_t pseudo random sequence number + */ +u32_t +tcp_next_iss(struct tcp_pcb *pcb) +{ +#ifdef LWIP_HOOK_TCP_ISN + return LWIP_HOOK_TCP_ISN(&pcb->local_ip, pcb->local_port, &pcb->remote_ip, pcb->remote_port); +#else /* LWIP_HOOK_TCP_ISN */ + static u32_t iss = 6510; + + LWIP_UNUSED_ARG(pcb); + + iss += tcp_ticks; /* XXX */ + return iss; +#endif /* LWIP_HOOK_TCP_ISN */ +} + +#if TCP_CALCULATE_EFF_SEND_MSS +/** + * Calculates the effective send mss that can be used for a specific IP address + * by using ip_route to determine the netif used to send to the address and + * calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss_impl(u16_t sendmss, const ip_addr_t *dest +#if LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING + , const ip_addr_t *src +#endif /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */ + ) +{ + u16_t mss_s; + struct netif *outif; + s16_t mtu; + + outif = ip_route(src, dest); +#if LWIP_IPV6 +#if LWIP_IPV4 + if (IP_IS_V6(dest)) +#endif /* LWIP_IPV4 */ + { + /* First look in destination cache, to see if there is a Path MTU. */ + mtu = nd6_get_destination_mtu(ip_2_ip6(dest), outif); + } +#if LWIP_IPV4 + else +#endif /* LWIP_IPV4 */ +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 + { + if (outif == NULL) { + return sendmss; + } + mtu = outif->mtu; + } +#endif /* LWIP_IPV4 */ + + if (mtu != 0) { +#if LWIP_IPV6 +#if LWIP_IPV4 + if (IP_IS_V6(dest)) +#endif /* LWIP_IPV4 */ + { + mss_s = mtu - IP6_HLEN - TCP_HLEN; + } +#if LWIP_IPV4 + else +#endif /* LWIP_IPV4 */ +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 + { + mss_s = mtu - IP_HLEN - TCP_HLEN; + } +#endif /* LWIP_IPV4 */ + /* RFC 1122, chap 4.2.2.6: + * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize + * We correct for TCP options in tcp_write(), and don't support IP options. + */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +/** Helper function for tcp_netif_ip_addr_changed() that iterates a pcb list */ +static void +tcp_netif_ip_addr_changed_pcblist(const ip_addr_t* old_addr, struct tcp_pcb* pcb_list) +{ + struct tcp_pcb *pcb; + pcb = pcb_list; + while (pcb != NULL) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&pcb->local_ip, old_addr) +#if LWIP_AUTOIP + /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ + && (!IP_IS_V4_VAL(pcb->local_ip) || !ip4_addr_islinklocal(ip_2_ip4(&pcb->local_ip))) +#endif /* LWIP_AUTOIP */ + ) { + /* this connection must be aborted */ + struct tcp_pcb *next = pcb->next; + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + tcp_abort(pcb); + pcb = next; + } else { + pcb = pcb->next; + } + } +} + +/** This function is called from netif.c when address is changed or netif is removed + * + * @param old_addr IP address of the netif before change + * @param new_addr IP address of the netif after change or NULL if netif has been removed + */ +void +tcp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr) +{ + struct tcp_pcb_listen *lpcb, *next; + + if (!ip_addr_isany(old_addr)) { + tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_active_pcbs); + tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_bound_pcbs); + + if (!ip_addr_isany(new_addr)) { + /* PCB bound to current local interface address? */ + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = next) { + next = lpcb->next; + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&lpcb->local_ip, old_addr)) { + /* The PCB is listening to the old ipaddr and + * is set to listen to the new one instead */ + ip_addr_copy(lpcb->local_ip, *new_addr); + } + } + } + } +} + +const char* +tcp_debug_state_str(enum tcp_state s) +{ + return tcp_state_str[s]; +} + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +/** + * Print a tcp header for debugging purposes. + * + * @param tcphdr pointer to a struct tcp_hdr + */ +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + lwip_ntohs(tcphdr->src), lwip_ntohs(tcphdr->dest))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n", + lwip_ntohl(tcphdr->seqno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n", + lwip_ntohl(tcphdr->ackno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (", + TCPH_HDRLEN(tcphdr), + (u16_t)(TCPH_FLAGS(tcphdr) >> 5 & 1), + (u16_t)(TCPH_FLAGS(tcphdr) >> 4 & 1), + (u16_t)(TCPH_FLAGS(tcphdr) >> 3 & 1), + (u16_t)(TCPH_FLAGS(tcphdr) >> 2 & 1), + (u16_t)(TCPH_FLAGS(tcphdr) >> 1 & 1), + (u16_t)(TCPH_FLAGS(tcphdr) & 1), + lwip_ntohs(tcphdr->wnd))); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_DEBUG, ("), win)\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n", + lwip_ntohs(tcphdr->chksum), lwip_ntohs(tcphdr->urgp))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} + +/** + * Print a tcp state for debugging purposes. + * + * @param s enum tcp_state to print + */ +void +tcp_debug_print_state(enum tcp_state s) +{ + LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s])); +} + +/** + * Print tcp flags for debugging purposes. + * + * @param flags tcp flags, all active flags are printed + */ +void +tcp_debug_print_flags(u8_t flags) +{ + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("FIN ")); + } + if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("SYN ")); + } + if (flags & TCP_RST) { + LWIP_DEBUGF(TCP_DEBUG, ("RST ")); + } + if (flags & TCP_PSH) { + LWIP_DEBUGF(TCP_DEBUG, ("PSH ")); + } + if (flags & TCP_ACK) { + LWIP_DEBUGF(TCP_DEBUG, ("ACK ")); + } + if (flags & TCP_URG) { + LWIP_DEBUGF(TCP_DEBUG, ("URG ")); + } + if (flags & TCP_ECE) { + LWIP_DEBUGF(TCP_DEBUG, ("ECE ")); + } + if (flags & TCP_CWR) { + LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); + } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); +} + +/** + * Print all tcp_pcbs in every list for debugging purposes. + */ +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + struct tcp_pcb_listen *pcbl; + + LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + + LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for (pcbl = tcp_listen_pcbs.listen_pcbs; pcbl != NULL; pcbl = pcbl->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F" ", pcbl->local_port)); + tcp_debug_print_state(pcbl->state); + } + + LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} + +/** + * Check state consistency of the tcp_pcb lists. + */ +s16_t +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + return 1; +} +#endif /* TCP_DEBUG */ + +#endif /* LWIP_TCP */ diff --git a/sdk/Src/Network/lwip2.0.3/core/tcp_in.c b/sdk/Src/Network/lwip2.0.3/core/tcp_in.c new file mode 100644 index 0000000..6782995 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/tcp_in.c @@ -0,0 +1,1840 @@ +/** + * @file + * Transmission Control Protocol, incoming traffic + * + * The input processing functions of the TCP layer. + * + * These functions are generally called in the order (ip_input() ->) + * tcp_input() -> * tcp_process() -> tcp_receive() (-> application). + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/priv/tcp_priv.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#if LWIP_ND6_TCP_REACHABILITY_HINTS +#include "lwip/nd6.h" +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +/** Initial CWND calculation as defined RFC 2581 */ +#define LWIP_TCP_CALC_INITIAL_CWND(mss) LWIP_MIN((4U * (mss)), LWIP_MAX((2U * (mss)), 4380U)); + +/* These variables are global to all functions involved in the input + processing of TCP segments. They are set by the tcp_input() + function. */ +static struct tcp_seg inseg; +static struct tcp_hdr *tcphdr; +static u16_t tcphdr_optlen; +static u16_t tcphdr_opt1len; +static u8_t* tcphdr_opt2; +static u16_t tcp_optidx; +static u32_t seqno, ackno; +static tcpwnd_size_t recv_acked; +static u16_t tcplen; +static u8_t flags; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static void tcp_listen_input(struct tcp_pcb_listen *pcb); +static void tcp_timewait_input(struct tcp_pcb *pcb); + +static int tcp_input_delayed_close(struct tcp_pcb *pcb); + +/** + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + * + * @param p received TCP segment to process (p->payload pointing to the TCP header) + * @param inp network interface on which this segment was received + */ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; +#if SO_REUSE + struct tcp_pcb *lpcb_prev = NULL; + struct tcp_pcb_listen *lpcb_any = NULL; +#endif /* SO_REUSE */ + u8_t hdrlen_bytes; + err_t err; + + LWIP_UNUSED_ARG(inp); + + PERF_START; + + TCP_STATS_INC(tcp.recv); + MIB2_STATS_INC(mib2.tcpinsegs); + + tcphdr = (struct tcp_hdr *)p->payload; + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* Check that TCP header fits in payload */ + if (p->len < TCP_HLEN) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if (ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()) || + ip_addr_ismulticast(ip_current_dest_addr())) { + TCP_STATS_INC(tcp.proterr); + goto dropped; + } + +#if CHECKSUM_CHECK_TCP + IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_TCP) { + /* Verify TCP checksum. */ + u16_t chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, + ip_current_src_addr(), ip_current_dest_addr()); + if (chksum != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + chksum)); + tcp_debug_print(tcphdr); + TCP_STATS_INC(tcp.chkerr); + goto dropped; + } + } +#endif /* CHECKSUM_CHECK_TCP */ + + /* sanity-check header length */ + hdrlen_bytes = TCPH_HDRLEN(tcphdr) * 4; + if ((hdrlen_bytes < TCP_HLEN) || (hdrlen_bytes > p->tot_len)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: invalid header length (%"U16_F")\n", (u16_t)hdrlen_bytes)); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + tcphdr_optlen = hdrlen_bytes - TCP_HLEN; + tcphdr_opt2 = NULL; + if (p->len >= hdrlen_bytes) { + /* all options are in the first pbuf */ + tcphdr_opt1len = tcphdr_optlen; + pbuf_header(p, -(s16_t)hdrlen_bytes); /* cannot fail */ + } else { + u16_t opt2len; + /* TCP header fits into first pbuf, options don't - data is in the next pbuf */ + /* there must be a next pbuf, due to hdrlen_bytes sanity check above */ + LWIP_ASSERT("p->next != NULL", p->next != NULL); + + /* advance over the TCP header (cannot fail) */ + pbuf_header(p, -TCP_HLEN); + + /* determine how long the first and second parts of the options are */ + tcphdr_opt1len = p->len; + opt2len = tcphdr_optlen - tcphdr_opt1len; + + /* options continue in the next pbuf: set p to zero length and hide the + options in the next pbuf (adjusting p->tot_len) */ + pbuf_header(p, -(s16_t)tcphdr_opt1len); + + /* check that the options fit in the second pbuf */ + if (opt2len > p->next->len) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: options overflow second pbuf (%"U16_F" bytes)\n", p->next->len)); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* remember the pointer to the second part of the options */ + tcphdr_opt2 = (u8_t*)p->next->payload; + + /* advance p->next to point after the options, and manually + adjust p->tot_len to keep it consistent with the changed p->next */ + pbuf_header(p->next, -(s16_t)opt2len); + p->tot_len -= opt2len; + + LWIP_ASSERT("p->len == 0", p->len == 0); + LWIP_ASSERT("p->tot_len == p->next->tot_len", p->tot_len == p->next->tot_len); + } + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = lwip_ntohs(tcphdr->src); + tcphdr->dest = lwip_ntohs(tcphdr->dest); + seqno = tcphdr->seqno = lwip_ntohl(tcphdr->seqno); + ackno = tcphdr->ackno = lwip_ntohl(tcphdr->ackno); + tcphdr->wnd = lwip_ntohs(tcphdr->wnd); + + flags = TCPH_FLAGS(tcphdr); + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0); + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. */ + prev = NULL; + + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) && + ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if (prev != NULL) { + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb; + } else { + TCP_STATS_INC(tcp.cachehit); + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb; + } + + if (pcb == NULL) { + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) && + ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb); + pbuf_free(p); + return; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incoming connections. */ + prev = NULL; + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if (lpcb->local_port == tcphdr->dest) { + if (IP_IS_ANY_TYPE_VAL(lpcb->local_ip)) { + /* found an ANY TYPE (IPv4/IPv6) match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; +#endif /* SO_REUSE */ + } else if (IP_ADDR_PCB_VERSION_MATCH_EXACT(lpcb, ip_current_dest_addr())) { + if (ip_addr_cmp(&lpcb->local_ip, ip_current_dest_addr())) { + /* found an exact match */ + break; + } else if (ip_addr_isany(&lpcb->local_ip)) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; + #endif /* SO_REUSE */ + } + } + } + prev = (struct tcp_pcb *)lpcb; + } +#if SO_REUSE + /* first try specific local IP */ + if (lpcb == NULL) { + /* only pass to ANY if no specific local IP has been found */ + lpcb = lpcb_any; + prev = lpcb_prev; + } +#endif /* SO_REUSE */ + if (lpcb != NULL) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if (prev != NULL) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } else { + TCP_STATS_INC(tcp.cachehit); + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb); + pbuf_free(p); + return; + } + } + +#if TCP_INPUT_DEBUG + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + + if (pcb != NULL) { + /* The incoming segment belongs to a connection. */ +#if TCP_INPUT_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.p = p; + inseg.tcphdr = tcphdr; + + recv_data = NULL; + recv_flags = 0; + recv_acked = 0; + + if (flags & TCP_PSH) { + p->flags |= PBUF_FLAG_PUSH; + } + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + if ((tcp_process_refused_data(pcb) == ERR_ABRT) || + ((pcb->refused_data != NULL) && (tcplen > 0))) { + /* pcb has been aborted or refused data is still refused and the new + segment contains data */ + if (pcb->rcv_ann_wnd == 0) { + /* this is a zero-window probe, we respond to it with current RCV.NXT + and drop the data segment */ + tcp_send_empty_ack(pcb); + } + TCP_STATS_INC(tcp.drop); + MIB2_STATS_INC(mib2.tcpinerrs); + goto aborted; + } + } + tcp_input_pcb = pcb; + err = tcp_process(pcb); + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. If so, we don't do anything. */ + if (err != ERR_ABRT) { + if (recv_flags & TF_RESET) { + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (recv_acked > 0) { + u16_t acked16; +#if LWIP_WND_SCALE + /* recv_acked is u32_t but the sent callback only takes a u16_t, + so we might have to call it multiple times. */ + u32_t acked = recv_acked; + while (acked > 0) { + acked16 = (u16_t)LWIP_MIN(acked, 0xffffu); + acked -= acked16; +#else + { + acked16 = recv_acked; +#endif + TCP_EVENT_SENT(pcb, (u16_t)acked16, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + recv_acked = 0; + } + if (tcp_input_delayed_close(pcb)) { + goto aborted; + } +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + while (recv_data != NULL) { + struct pbuf *rest = NULL; + pbuf_split_64k(recv_data, &rest); +#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + if (recv_data != NULL) { +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + + LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL); + if (pcb->flags & TF_RXCLOSED) { + /* received data although already closed -> abort (send RST) to + notify the remote host that not all data has been processed */ + pbuf_free(recv_data); +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + if (rest != NULL) { + pbuf_free(rest); + } +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + tcp_abort(pcb); + goto aborted; + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + if (err == ERR_ABRT) { +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + if (rest != NULL) { + pbuf_free(rest); + } +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + goto aborted; + } + + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + if (rest != NULL) { + pbuf_cat(recv_data, rest); + } +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + break; + } else { + /* Upper layer received the data, go on with the rest if > 64K */ + recv_data = rest; +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + } + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + if (pcb->refused_data != NULL) { + /* Delay this if we have refused data. */ + pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; + } else { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + } + + tcp_input_pcb = NULL; + if (tcp_input_delayed_close(pcb)) { + goto aborted; + } + /* Try to send something out. */ + tcp_output(pcb); +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + } + } + /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()). + Below this line, 'pcb' may not be dereferenced! */ +aborted: + tcp_input_pcb = NULL; + recv_data = NULL; + + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p); + inseg.p = NULL; + } + } else { + + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { + TCP_STATS_INC(tcp.proterr); + TCP_STATS_INC(tcp.drop); + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), + ip_current_src_addr(), tcphdr->dest, tcphdr->src); + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); + return; +dropped: + TCP_STATS_INC(tcp.drop); + MIB2_STATS_INC(mib2.tcpinerrs); + pbuf_free(p); +} + +/** Called from tcp_input to check for TF_CLOSED flag. This results in closing + * and deallocating a pcb at the correct place to ensure noone references it + * any more. + * @returns 1 if the pcb has been closed and deallocated, 0 otherwise + */ +static int +tcp_input_delayed_close(struct tcp_pcb *pcb) +{ + if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + if (!(pcb->flags & TF_RXCLOSED)) { + /* Connection closed although the application has only shut down the + tx side: call the PCB's err callback and indicate the closure to + ensure the application doesn't continue using the PCB. */ + TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_CLSD); + } + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + return 1; + } + return 0; +} + +/** + * Called by tcp_input() when a segment arrives for a listening + * connection (from tcp_input()). + * + * @param pcb the tcp_pcb_listen for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static void +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + u32_t iss; + err_t rc; + + if (flags & TCP_RST) { + /* An incoming RST should be ignored. Return. */ + return; + } + + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if (flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), + ip_current_src_addr(), tcphdr->dest, tcphdr->src); + } else if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); +#if TCP_LISTEN_BACKLOG + if (pcb->accepts_pending >= pcb->backlog) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest)); + return; + } +#endif /* TCP_LISTEN_BACKLOG */ + npcb = tcp_alloc(pcb->prio); + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory available. */ + if (npcb == NULL) { + err_t err; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); + TCP_STATS_INC(tcp.memerr); + TCP_EVENT_ACCEPT(pcb, NULL, pcb->callback_arg, ERR_MEM, err); + LWIP_UNUSED_ARG(err); /* err not useful here */ + return; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; + npcb->flags |= TF_BACKLOGPEND; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ + ip_addr_copy(npcb->local_ip, *ip_current_dest_addr()); + ip_addr_copy(npcb->remote_ip, *ip_current_src_addr()); + npcb->local_port = pcb->local_port; + npcb->remote_port = tcphdr->src; + npcb->state = SYN_RCVD; + npcb->rcv_nxt = seqno + 1; + npcb->rcv_ann_right_edge = npcb->rcv_nxt; + iss = tcp_next_iss(npcb); + npcb->snd_wl2 = iss; + npcb->snd_nxt = iss; + npcb->lastack = iss; + npcb->snd_lbb = iss; + npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ + npcb->callback_arg = pcb->callback_arg; +#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG + npcb->listener = pcb; +#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */ + /* inherit socket options */ + npcb->so_options = pcb->so_options & SOF_INHERITED; + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG_ACTIVE(npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); + npcb->snd_wnd = tcphdr->wnd; + npcb->snd_wnd_max = npcb->snd_wnd; + +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, &npcb->remote_ip); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + MIB2_STATS_INC(mib2.tcppassiveopens); + + /* Send a SYN|ACK together with the MSS option. */ + rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK); + if (rc != ERR_OK) { + tcp_abandon(npcb, 0); + return; + } + tcp_output(npcb); + } + return; +} + +/** + * Called by tcp_input() when a segment arrives for a connection in + * TIME_WAIT. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static void +tcp_timewait_input(struct tcp_pcb *pcb) +{ + /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ + /* RFC 793 3.9 Event Processing - Segment Arrives: + * - first check sequence number - we skip that one in TIME_WAIT (always + * acceptable since we only send ACKs) + * - second check the RST bit (... return) */ + if (flags & TCP_RST) { + return; + } + /* - fourth, check the SYN bit, */ + if (flags & TCP_SYN) { + /* If an incoming segment is not acceptable, an acknowledgment + should be sent in reply */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd)) { + /* If the SYN is in the window it is an error, send a reset */ + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), + ip_current_src_addr(), tcphdr->dest, tcphdr->src); + return; + } + } else if (flags & TCP_FIN) { + /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. + Restart the 2 MSL time-wait timeout.*/ + pcb->tmr = tcp_ticks; + } + + if ((tcplen > 0)) { + /* Acknowledge data, FIN or out-of-window SYN */ + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + return; +} + +/** + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_seg *rseg; + u8_t acceptable = 0; + err_t err; + + err = ERR_OK; + + /* Process incoming RST segments. */ + if (flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if (pcb->state == SYN_SENT) { + /* "In the SYN-SENT state (a RST received in response to an initial SYN), + the RST is acceptable if the ACK field acknowledges the SYN." */ + if (ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + /* "In all states except SYN-SENT, all reset (RST) segments are validated + by checking their SEQ-fields." */ + if (seqno == pcb->rcv_nxt) { + acceptable = 1; + } else if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd)) { + /* If the sequence number is inside the window, we only send an ACK + and wait for a re-send with matching sequence number. + This violates RFC 793, but is required to protection against + CVE-2004-0230 (RST spoofing attack). */ + tcp_ack_now(pcb); + } + } + + if (acceptable) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + recv_flags |= TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + return ERR_RST; + } else { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + return ERR_OK; + } + } + + if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { + /* Cope with new connection attempt after remote end crashed */ + tcp_ack_now(pcb); + return ERR_OK; + } + + if ((pcb->flags & TF_RXCLOSED) == 0) { + /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */ + pcb->tmr = tcp_ticks; + } + pcb->keep_cnt_sent = 0; + + tcp_parseopt(pcb); + + /* Do different things depending on the TCP state. */ + switch (pcb->state) { + case SYN_SENT: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, + pcb->snd_nxt, lwip_ntohl(pcb->unacked->tcphdr->seqno))); + /* received SYN ACK with expected sequence number? */ + if ((flags & TCP_ACK) && (flags & TCP_SYN) + && (ackno == pcb->lastack + 1)) { + pcb->rcv_nxt = seqno + 1; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->lastack = ackno; + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wnd_max = pcb->snd_wnd; + pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ + pcb->state = ESTABLISHED; + +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss); + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SENT): cwnd %"TCPWNDSIZE_F + " ssthresh %"TCPWNDSIZE_F"\n", + pcb->cwnd, pcb->ssthresh)); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + if (rseg == NULL) { + /* might happen if tcp_output fails in tcp_rexmit_rto() + in which case the segment is on the unsent list */ + rseg = pcb->unsent; + LWIP_ASSERT("no segment to free", rseg != NULL); + pcb->unsent = rseg->next; + } else { + pcb->unacked = rseg->next; + } + tcp_seg_free(rseg); + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if (pcb->unacked == NULL) { + pcb->rtime = -1; + } else { + pcb->rtime = 0; + pcb->nrtx = 0; + } + + /* Call the user specified function to call when successfully + * connected. */ + TCP_EVENT_CONNECTED(pcb, ERR_OK, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + tcp_ack_now(pcb); + } + /* received ACK? possibly a half-open connection */ + else if (flags & TCP_ACK) { + /* send a RST to bring the other side in a non-synchronized state. */ + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), + ip_current_src_addr(), tcphdr->dest, tcphdr->src); + /* Resend SYN immediately (don't wait for rto timeout) to establish + connection faster, but do not send more SYNs than we otherwise would + have, or we might get caught in a loop on loopback interfaces. */ + if (pcb->nrtx < TCP_SYNMAXRTX) { + pcb->rtime = 0; + tcp_rexmit_rto(pcb); + } + } + break; + case SYN_RCVD: + if (flags & TCP_ACK) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + pcb->state = ESTABLISHED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); +#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG +#if LWIP_CALLBACK_API + LWIP_ASSERT("pcb->listener->accept != NULL", + (pcb->listener == NULL) || (pcb->listener->accept != NULL)); +#endif + if (pcb->listener == NULL) { + /* listen pcb might be closed by now */ + err = ERR_VAL; + } else +#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */ + { + tcp_backlog_accepted(pcb); + /* Call the accept function. */ + TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err); + } + if (err != ERR_OK) { + /* If the accept function returns with an error, we abort + * the connection. */ + /* Already aborted? */ + if (err != ERR_ABRT) { + tcp_abort(pcb); + } + return ERR_ABRT; + } + /* If there was any data contained within this ACK, + * we'd better pass it on to the application as well. */ + tcp_receive(pcb); + + /* Prevent ACK for SYN to generate a sent event */ + if (recv_acked != 0) { + recv_acked--; + } + + pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss); + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SYN_RCVD): cwnd %"TCPWNDSIZE_F + " ssthresh %"TCPWNDSIZE_F"\n", + pcb->cwnd, pcb->ssthresh)); + + if (recv_flags & TF_GOT_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + } else { + /* incorrect ACK number, send RST */ + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), + ip_current_src_addr(), tcphdr->dest, tcphdr->src); + } + } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { + /* Looks like another copy of the SYN - retransmit our SYN-ACK */ + tcp_rexmit(pcb); + } + break; + case CLOSE_WAIT: + /* FALLTHROUGH */ + case ESTABLISHED: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { /* passive close */ + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) && + pcb->unsent == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) && + pcb->unsent == NULL) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ + recv_flags |= TF_CLOSED; + } + break; + default: + break; + } + return ERR_OK; +} + +#if TCP_QUEUE_OOSEQ +/** + * Insert segment into the list (segments covered with new one will be deleted) + * + * Called from tcp_receive() + */ +static void +tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) +{ + struct tcp_seg *old_seg; + + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + /* received segment overlaps all following segments */ + tcp_segs_free(next); + next = NULL; + } else { + /* delete some following segments + oos queue may have segments with FIN flag */ + while (next && + TCP_SEQ_GEQ((seqno + cseg->len), + (next->tcphdr->seqno + next->len))) { + /* cseg with FIN already processed */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + if (next && + TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + cseg->next = next; +} +#endif /* TCP_QUEUE_OOSEQ */ + +/** + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, it places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + * + * Called from tcp_process(). + */ +static void +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next; +#if TCP_QUEUE_OOSEQ + struct tcp_seg *prev, *cseg; +#endif /* TCP_QUEUE_OOSEQ */ + s32_t off; + s16_t m; + u32_t right_wnd_edge; + u16_t new_tot_len; + int found_dupack = 0; +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + u32_t ooseq_blen; + u16_t ooseq_qlen; +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ + + LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED); + + if (flags & TCP_ACK) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; + + /* Update window. */ + if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && (u32_t)SND_WND_SCALE(pcb, tcphdr->wnd) > pcb->snd_wnd)) { + pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd); + /* keep track of the biggest window announced by the remote host to calculate + the maximum segment size */ + if (pcb->snd_wnd_max < pcb->snd_wnd) { + pcb->snd_wnd_max = pcb->snd_wnd; + } + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd == 0) { + if (pcb->persist_backoff == 0) { + /* start persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + } else if (pcb->persist_backoff > 0) { + /* stop persist timer */ + pcb->persist_backoff = 0; + } + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"TCPWNDSIZE_F"\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if (pcb->snd_wnd != (tcpwnd_size_t)SND_WND_SCALE(pcb, tcphdr->wnd)) { + LWIP_DEBUGF(TCP_WND_DEBUG, + ("tcp_receive: no window update lastack %"U32_F" ackno %" + U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a + * duplicate ack if: + * 1) It doesn't ACK new data + * 2) length of received packet is zero (i.e. no payload) + * 3) the advertised window hasn't changed + * 4) There is outstanding unacknowledged data (retransmission timer running) + * 5) The ACK is == biggest ACK sequence number so far seen (snd_una) + * + * If it passes all five, should process as a dupack: + * a) dupacks < 3: do nothing + * b) dupacks == 3: fast retransmit + * c) dupacks > 3: increase cwnd + * + * If it only passes 1-3, should reset dupack counter (and add to + * stats, which we don't do in lwIP) + * + * If it only passes 1, should reset dupack counter + * + */ + + /* Clause 1 */ + if (TCP_SEQ_LEQ(ackno, pcb->lastack)) { + /* Clause 2 */ + if (tcplen == 0) { + /* Clause 3 */ + if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge) { + /* Clause 4 */ + if (pcb->rtime >= 0) { + /* Clause 5 */ + if (pcb->lastack == ackno) { + found_dupack = 1; + if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) { + ++pcb->dupacks; + } + if (pcb->dupacks > 3) { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } else if (pcb->dupacks == 3) { + /* Do fast retransmit */ + tcp_rexmit_fast(pcb); + } + } + } + } + } + /* If Clause (1) or more is true, but not a duplicate ack, reset + * count of consecutive duplicate acks */ + if (!found_dupack) { + pcb->dupacks = 0; + } + } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if (pcb->state >= ESTABLISHED) { + if (pcb->cwnd < pcb->ssthresh) { + if ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd)); + } else { + tcpwnd_size_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); + if (new_cwnd > pcb->cwnd) { + pcb->cwnd = new_cwnd; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd)); + } + } + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n", + ackno, + pcb->unacked != NULL? + lwip_ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + lwip_ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowledges them. */ + while (pcb->unacked != NULL && + TCP_SEQ_LEQ(lwip_ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n", + lwip_ntohl(pcb->unacked->tcphdr->seqno), + lwip_ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked; + pcb->unacked = pcb->unacked->next; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + + pcb->snd_queuelen -= pbuf_clen(next->p); + recv_acked += next->len; + tcp_seg_free(next); + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unacked)\n", (tcpwnd_size_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + } + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if (pcb->unacked == NULL) { + pcb->rtime = -1; + } else { + pcb->rtime = 0; + } + + pcb->polltmr = 0; + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (ip_current_is_v6()) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + } else { + /* Out of sequence ACK, didn't really ack anything */ + tcp_send_empty_ack(pcb); + } + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact have been sent once. */ + while (pcb->unsent != NULL && + TCP_SEQ_BETWEEN(ackno, lwip_ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", + lwip_ntohl(pcb->unsent->tcphdr->seqno), lwip_ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent; + pcb->unsent = pcb->unsent->next; +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + pcb->snd_queuelen -= pbuf_clen(next->p); + recv_acked += next->len; + tcp_seg_free(next); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unsent)\n", (tcpwnd_size_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + } + pcb->snd_buf += recv_acked; +#if SNDBUF_SHARE + sndbuf_len += recv_acked; +#endif + /* End of ACK for new data processing. */ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { + /* diff between this shouldn't exceed 32K since this are tcp timer ticks + and a round-trip shouldn't be that long... */ + m = (s16_t)(tcp_ticks - pcb->rttest); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", + m, (u16_t)(m * TCP_SLOW_INTERVAL))); + + /* This is taken directly from VJs original code in his paper */ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if (m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", + pcb->rto, (u16_t)(pcb->rto * TCP_SLOW_INTERVAL))); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further unless the pcb already received a FIN. + (RFC 793, chapter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING, + LAST-ACK and TIME-WAIT: "Ignore the segment text.") */ + if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) { + if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ + if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)) { + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + + struct pbuf *p = inseg.p; + off = pcb->rcv_nxt - seqno; + LWIP_ASSERT("inseg.p != NULL", inseg.p); + LWIP_ASSERT("insane offset!", (off < 0x7fff)); + if (inseg.p->len < off) { + LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off)); + new_tot_len = (u16_t)(inseg.p->tot_len - off); + while (p->len < off) { + off -= p->len; + /* KJM following line changed (with addition of new_tot_len var) + to fix bug #9076 + inseg.p->tot_len -= p->len; */ + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + if (pbuf_header(p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } else { + if (pbuf_header(inseg.p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) { + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); + tcp_ack_now(pcb); + } + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd - 1)) { + if (pcb->rcv_nxt == seqno) { + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ + tcplen = TCP_TCPLEN(&inseg); + + if (tcplen > pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) & ~(unsigned int)TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + TCPWND_CHECK16(pcb->rcv_wnd); + inseg.len = (u16_t)pcb->rcv_wnd; + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } +#if TCP_QUEUE_OOSEQ + /* Received in-sequence data, adjust ooseq data if: + - FIN has been received or + - inseq overlaps with ooseq */ + if (pcb->ooseq != NULL) { + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: received in-order FIN, binning ooseq queue\n")); + /* Received in-order FIN means anything that was received + * out of order must now have been received in-order, so + * bin the ooseq queue */ + while (pcb->ooseq != NULL) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + tcp_seg_free(old_ooseq); + } + } else { + next = pcb->ooseq; + /* Remove all segments on ooseq that are covered by inseg already. + * FIN is copied from ooseq to inseg if present. */ + while (next && + TCP_SEQ_GEQ(seqno + tcplen, + next->tcphdr->seqno + next->len)) { + /* inseg cannot have FIN here (already processed above) */ + if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 && + (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) { + TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN); + tcplen = TCP_TCPLEN(&inseg); + } + prev = next; + next = next->next; + tcp_seg_free(prev); + } + /* Now trim right side of inseg if it overlaps with the first + * segment on ooseq */ + if (next && + TCP_SEQ_GT(seqno + tcplen, + next->tcphdr->seqno)) { + /* inseg cannot have FIN here (already processed above) */ + inseg.len = (u16_t)(next->tcphdr->seqno - seqno); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n", + (seqno + tcplen) == next->tcphdr->seqno); + } + pcb->ooseq = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + pcb->rcv_nxt = seqno + tcplen; + + /* Update the receiver's (our) window. */ + LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen); + pcb->rcv_wnd -= tcplen; + + tcp_update_rcv_ann_wnd(pcb); + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if (inseg.p->tot_len > 0) { + recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); + recv_flags |= TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + are now in sequence. */ + while (pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n", + pcb->rcv_wnd >= TCP_TCPLEN(cseg)); + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + + tcp_update_rcv_ann_wnd(pcb); + + if (cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + /* With window scaling, this can overflow recv_data->tot_len, but + that's not a problem since we explicitly fix that before passing + recv_data to the application. */ + if (recv_data) { + pbuf_cat(recv_data, cseg->p); + } else { + recv_data = cseg->p; + } + cseg->p = NULL; + } + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); + recv_flags |= TF_GOT_FIN; + if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */ + pcb->state = CLOSE_WAIT; + } + } + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (ip_current_is_v6()) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_send_empty_ack(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if (pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for (next = pcb->ooseq; next != NULL; next = next->next) { + if (seqno == next->tcphdr->seqno) { + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if (inseg.len > next->len) { + /* The incoming segment is larger than the old + segment. We replace some segments with the new + one. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + tcp_oos_insert_segment(cseg, next); + } + break; + } else { + /* Either the lengths are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if (prev == NULL) { + if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + pcb->ooseq = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } else { + /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ + if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim trim the previous + segment, delete next segments that included in received segment + and trim received, if needed. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + prev->next = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if (next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + /* segment "next" already contains all data */ + break; + } + next->next = tcp_seg_copy(&inseg); + if (next->next != NULL) { + if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = (u16_t)(seqno - next->tcphdr->seqno); + pbuf_realloc(next->p, next->len); + } + /* check if the remote side overruns our receive window */ + if (TCP_SEQ_GT((u32_t)tcplen + seqno, pcb->rcv_nxt + (u32_t)pcb->rcv_wnd)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) & ~TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + next->next->len = (u16_t)(pcb->rcv_nxt + pcb->rcv_wnd - seqno); + pbuf_realloc(next->next->p, next->next->len); + tcplen = TCP_TCPLEN(next->next); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } + } + break; + } + } + prev = next; + } + } +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + /* Check that the data on ooseq doesn't exceed one of the limits + and throw away everything above that limit. */ + ooseq_blen = 0; + ooseq_qlen = 0; + prev = NULL; + for (next = pcb->ooseq; next != NULL; prev = next, next = next->next) { + struct pbuf *p = next->p; + ooseq_blen += p->tot_len; + ooseq_qlen += pbuf_clen(p); + if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) || + (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) { + /* too much ooseq data, dump this and everything after it */ + tcp_segs_free(next); + if (prev == NULL) { + /* first ooseq segment is too much, dump the whole queue */ + pcb->ooseq = NULL; + } else { + /* just dump 'next' and everything after it */ + prev->next = NULL; + } + break; + } + } +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ +#endif /* TCP_QUEUE_OOSEQ */ + } + } else { + /* The incoming segment is not within the window. */ + tcp_send_empty_ack(pcb); + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + if (!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)) { + tcp_ack_now(pcb); + } + } +} + +static u8_t +tcp_getoptbyte(void) +{ + if ((tcphdr_opt2 == NULL) || (tcp_optidx < tcphdr_opt1len)) { + u8_t* opts = (u8_t *)tcphdr + TCP_HLEN; + return opts[tcp_optidx++]; + } else { + u8_t idx = (u8_t)(tcp_optidx++ - tcphdr_opt1len); + return tcphdr_opt2[idx]; + } +} + +/** + * Parses the options contained in the incoming segment. + * + * Called from tcp_listen_input() and tcp_process(). + * Currently, only the MSS option is supported! + * + * @param pcb the tcp_pcb for which a segment arrived + */ +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + u8_t data; + u16_t mss; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif + + /* Parse the TCP MSS option, if present. */ + if (tcphdr_optlen != 0) { + for (tcp_optidx = 0; tcp_optidx < tcphdr_optlen; ) { + u8_t opt = tcp_getoptbyte(); + switch (opt) { + case LWIP_TCP_OPT_EOL: + /* End of options. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case LWIP_TCP_OPT_NOP: + /* NOP option. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n")); + break; + case LWIP_TCP_OPT_MSS: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n")); + if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_MSS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_MSS) > tcphdr_optlen) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* An MSS option with the right option length. */ + mss = (tcp_getoptbyte() << 8); + mss |= tcp_getoptbyte(); + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + break; +#if LWIP_WND_SCALE + case LWIP_TCP_OPT_WS: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: WND_SCALE\n")); + if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_WS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_WS) > tcphdr_optlen) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* An WND_SCALE option with the right option length. */ + data = tcp_getoptbyte(); + /* If syn was received with wnd scale option, + activate wnd scale opt, but only if this is not a retransmission */ + if ((flags & TCP_SYN) && !(pcb->flags & TF_WND_SCALE)) { + pcb->snd_scale = data; + if (pcb->snd_scale > 14U) { + pcb->snd_scale = 14U; + } + pcb->rcv_scale = TCP_RCV_SCALE; + pcb->flags |= TF_WND_SCALE; + /* window scaling is enabled, we can use the full receive window */ + LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND)); + LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND)); + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND; + } + break; +#endif +#if LWIP_TCP_TIMESTAMPS + case LWIP_TCP_OPT_TS: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n")); + if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_TS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_TS) > tcphdr_optlen) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* TCP timestamp option with valid length */ + tsval = tcp_getoptbyte(); + tsval |= (tcp_getoptbyte() << 8); + tsval |= (tcp_getoptbyte() << 16); + tsval |= (tcp_getoptbyte() << 24); + if (flags & TCP_SYN) { + pcb->ts_recent = lwip_ntohl(tsval); + /* Enable sending timestamps in every segment now that we know + the remote host supports it. */ + pcb->flags |= TF_TIMESTAMP; + } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) { + pcb->ts_recent = lwip_ntohl(tsval); + } + /* Advance to next option (6 bytes already read) */ + tcp_optidx += LWIP_TCP_OPT_LEN_TS - 6; + break; +#endif + default: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n")); + data = tcp_getoptbyte(); + if (data < 2) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + /* If the length field is zero, the options are malformed + and we don't process them further. */ + return; + } + /* All other options have a length field, so that we easily + can skip past them. */ + tcp_optidx += data - 2; + } + } + } +} + +void +tcp_trigger_input_pcb_close(void) +{ + recv_flags |= TF_CLOSED; +} + +#endif /* LWIP_TCP */ diff --git a/sdk/Src/Network/lwip2.0.3/core/tcp_out.c b/sdk/Src/Network/lwip2.0.3/core/tcp_out.c new file mode 100644 index 0000000..182725e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/tcp_out.c @@ -0,0 +1,1680 @@ +/** + * @file + * Transmission Control Protocol, outgoing traffic + * + * The output functions of TCP. + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/priv/tcp_priv.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#if LWIP_TCP_TIMESTAMPS +#include "lwip/sys.h" +#endif + +#include + +/* Define some copy-macros for checksum-on-copy so that the code looks + nicer by preventing too many ifdef's. */ +#if TCP_CHECKSUM_ON_COPY +#define TCP_DATA_COPY(dst, src, len, seg) do { \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \ + len, &seg->chksum, &seg->chksum_swapped); \ + seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped); +#else /* TCP_CHECKSUM_ON_COPY*/ +#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len) +#endif /* TCP_CHECKSUM_ON_COPY*/ + +/** Define this to 1 for an extra check that the output checksum is valid + * (usefule when the checksum is generated by the application, not the stack) */ +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0 +#endif +/* Allow to override the failure of sanity check from warning to e.g. hard failure */ +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(msg) LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, msg) +#endif +#endif + +#if TCP_OVERSIZE +/** The size of segment pbufs created when TCP_OVERSIZE is enabled */ +#ifndef TCP_OVERSIZE_CALC_LENGTH +#define TCP_OVERSIZE_CALC_LENGTH(length) ((length) + TCP_OVERSIZE) +#endif +#endif + +/* Forward declarations.*/ +static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif); + +/** Allocate a pbuf and create a tcphdr at p->payload, used for output + * functions other than the default tcp_output -> tcp_output_segment + * (e.g. tcp_send_empty_ack, etc.) + * + * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr) + * @param optlen length of header-options + * @param datalen length of tcp data to reserve in pbuf + * @param seqno_be seqno in network byte order (big-endian) + * @return pbuf with p->payload being the tcp_hdr + */ +static struct pbuf * +tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen, + u32_t seqno_be /* already in network byte order */) +{ + struct tcp_hdr *tcphdr; + struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= TCP_HLEN + optlen)); + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = lwip_htons(pcb->local_port); + tcphdr->dest = lwip_htons(pcb->remote_port); + tcphdr->seqno = seqno_be; + tcphdr->ackno = lwip_htonl(pcb->rcv_nxt); + TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK); + tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd))); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + /* If we're sending a packet, update the announced right window edge */ + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + } + return p; +} + +/** + * Called by tcp_close() to send a segment including FIN flag but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @return ERR_OK if sent, another err_t otherwise + */ +err_t +tcp_send_fin(struct tcp_pcb *pcb) +{ + /* first, try to add the fin to the last unsent segment */ + if (pcb->unsent != NULL) { + struct tcp_seg *last_unsent; + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) { + /* no SYN/FIN/RST flag in the header, we can add the FIN flag */ + TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN); + pcb->flags |= TF_FIN; + return ERR_OK; + } + } + /* no data, no length, flags, copy=1, no optdata */ + return tcp_enqueue_flags(pcb, TCP_FIN); +} + +/** + * Create a TCP segment with prefilled header. + * + * Called by tcp_write and tcp_enqueue_flags. + * + * @param pcb Protocol control block for the TCP connection. + * @param p pbuf that is used to hold the TCP header. + * @param flags TCP flags for header. + * @param seqno TCP sequence number of this packet + * @param optflags options to include in TCP header + * @return a new tcp_seg pointing to p, or NULL. + * The TCP header is filled in except ackno and wnd. + * p is freed on failure. + */ +static struct tcp_seg * +tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags) +{ + struct tcp_seg *seg; + u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags); + + if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no memory.\n")); + pbuf_free(p); + return NULL; + } + seg->flags = optflags; + seg->next = NULL; + seg->p = p; + LWIP_ASSERT("p->tot_len >= optlen", p->tot_len >= optlen); + seg->len = p->tot_len - optlen; +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = 0; + seg->chksum_swapped = 0; + /* check optflags */ + LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", + (optflags & TF_SEG_DATA_CHECKSUMMED) == 0); +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* build TCP header */ + if (pbuf_header(p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + tcp_seg_free(seg); + return NULL; + } + seg->tcphdr = (struct tcp_hdr *)seg->p->payload; + seg->tcphdr->src = lwip_htons(pcb->local_port); + seg->tcphdr->dest = lwip_htons(pcb->remote_port); + seg->tcphdr->seqno = lwip_htonl(seqno); + /* ackno is set in tcp_output */ + TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags); + /* wnd and chksum are set in tcp_output */ + seg->tcphdr->urgp = 0; + return seg; +} + +/** + * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. + * + * This function is like pbuf_alloc(layer, length, PBUF_RAM) except + * there may be extra bytes available at the end. + * + * @param layer flag to define header size. + * @param length size of the pbuf's payload. + * @param max_length maximum usable size of payload+oversize. + * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. + * @param pcb The TCP connection that will enqueue the pbuf. + * @param apiflags API flags given to tcp_write. + * @param first_seg true when this pbuf will be used in the first enqueued segment. + */ +#if TCP_OVERSIZE +static struct pbuf * +tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, + u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, + u8_t first_seg) +{ + struct pbuf *p; + u16_t alloc = length; + +#if LWIP_NETIF_TX_SINGLE_PBUF + LWIP_UNUSED_ARG(max_length); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(apiflags); + LWIP_UNUSED_ARG(first_seg); + alloc = max_length; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (length < max_length) { + /* Should we allocate an oversized pbuf, or just the minimum + * length required? If tcp_write is going to be called again + * before this segment is transmitted, we want the oversized + * buffer. If the segment will be transmitted immediately, we can + * save memory by allocating only length. We use a simple + * heuristic based on the following information: + * + * Did the user set TCP_WRITE_FLAG_MORE? + * + * Will the Nagle algorithm defer transmission of this segment? + */ + if ((apiflags & TCP_WRITE_FLAG_MORE) || + (!(pcb->flags & TF_NODELAY) && + (!first_seg || + pcb->unsent != NULL || + pcb->unacked != NULL))) { + alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(TCP_OVERSIZE_CALC_LENGTH(length))); + } + } +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(layer, alloc, PBUF_RAM); + if (p == NULL) { + return NULL; + } + LWIP_ASSERT("need unchained pbuf", p->next == NULL); + *oversize = p->len - length; + /* trim p->len to the currently used size */ + p->len = p->tot_len = length; + return p; +} +#else /* TCP_OVERSIZE */ +#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM) +#endif /* TCP_OVERSIZE */ + +#if TCP_CHECKSUM_ON_COPY +/** Add a checksum of newly added data to the segment */ +static void +tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum, + u8_t *seg_chksum_swapped) +{ + u32_t helper; + /* add chksum to old chksum and fold to u16_t */ + helper = chksum + *seg_chksum; + chksum = FOLD_U32T(helper); + if ((len & 1) != 0) { + *seg_chksum_swapped = 1 - *seg_chksum_swapped; + chksum = SWAP_BYTES_IN_WORD(chksum); + } + *seg_chksum = chksum; +} +#endif /* TCP_CHECKSUM_ON_COPY */ + +/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). + * + * @param pcb the tcp pcb to check for + * @param len length of data to send (checked agains snd_buf) + * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise + */ +static err_t +tcp_write_checks(struct tcp_pcb *pcb, u16_t len) +{ + /* connection is in invalid state for data transmission? */ + if ((pcb->state != ESTABLISHED) && + (pcb->state != CLOSE_WAIT) && + (pcb->state != SYN_SENT) && + (pcb->state != SYN_RCVD)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } else if (len == 0) { + return ERR_OK; + } + + /* fail on too much data */ +#if SNDBUF_SHARE + if(len > sndbuf_len){ + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"TCPWNDSIZE_F")\n", + len, sndbuf_len)); +#else + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"TCPWNDSIZE_F")\n", + len, pcb->snd_buf)); +#endif + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen)); + + /* If total number of pbufs on the unsent/unacked queues exceeds the + * configured maximum, return an error */ + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + return ERR_OK; +} + +/** + * @ingroup tcp_raw + * Write data for sending (but does not send it immediately). + * + * It waits in the expectation of more data being sent soon (as + * it can send them more efficiently by combining them together). + * To prompt the system to send data now, call tcp_output() after + * calling tcp_write(). + * + * @param pcb Protocol control block for the TCP connection to enqueue data for. + * @param arg Pointer to the data to be enqueued for sending. + * @param len Data length in bytes + * @param apiflags combination of following flags : + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent, + * @return ERR_OK if enqueued, another err_t on error + */ +err_t +tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) +{ + struct pbuf *concat_p = NULL; + struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL; + u16_t pos = 0; /* position in 'arg' data */ + u16_t queuelen; + u8_t optlen = 0; + u8_t optflags = 0; +#if TCP_OVERSIZE + u16_t oversize = 0; + u16_t oversize_used = 0; +#if TCP_OVERSIZE_DBGCHECK + u16_t oversize_add = 0; +#endif /* TCP_OVERSIZE_DBGCHECK*/ +#endif /* TCP_OVERSIZE */ + u16_t extendlen = 0; +#if TCP_CHECKSUM_ON_COPY + u16_t concat_chksum = 0; + u8_t concat_chksum_swapped = 0; + u16_t concat_chksummed = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + err_t err; + /* don't allocate segments bigger than half the maximum window we ever received */ + u16_t mss_local = LWIP_MIN(pcb->mss, TCPWND_MIN16(pcb->snd_wnd_max/2)); + mss_local = mss_local ? mss_local : pcb->mss; + +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Always copy to try to create single pbufs for TX */ + apiflags |= TCP_WRITE_FLAG_COPY; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)apiflags)); + LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", + arg != NULL, return ERR_ARG;); + + err = tcp_write_checks(pcb, len); + if (err != ERR_OK) { + return err; + } + queuelen = pcb->snd_queuelen; + +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + /* Make sure the timestamp option is only included in data segments if we + agreed about it with the remote host. */ + optflags = TF_SEG_OPTS_TS; + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + /* ensure that segments can hold at least one data byte... */ + mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1); + } +#endif /* LWIP_TCP_TIMESTAMPS */ + + + /* + * TCP segmentation is done in three phases with increasing complexity: + * + * 1. Copy data directly into an oversized pbuf. + * 2. Chain a new pbuf to the end of pcb->unsent. + * 3. Create new segments. + * + * We may run out of memory at any point. In that case we must + * return ERR_MEM and not change anything in pcb. Therefore, all + * changes are recorded in local variables and committed at the end + * of the function. Some pcb fields are maintained in local copies: + * + * queuelen = pcb->snd_queuelen + * oversize = pcb->unsent_oversize + * + * These variables are set consistently by the phases: + * + * seg points to the last segment tampered with. + * + * pos records progress as data is segmented. + */ + + /* Find the tail of the unsent queue. */ + if (pcb->unsent != NULL) { + u16_t space; + u16_t unsent_optlen; + + /* @todo: this could be sped up by keeping last_unsent in the pcb */ + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + /* Usable space at the end of the last unsent segment */ + unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags); + LWIP_ASSERT("mss_local is too small", mss_local >= last_unsent->len + unsent_optlen); + space = mss_local - (last_unsent->len + unsent_optlen); + + /* + * Phase 1: Copy data directly into an oversized pbuf. + * + * The number of bytes copied is recorded in the oversize_used + * variable. The actual copying is done at the bottom of the + * function. + */ +#if TCP_OVERSIZE +#if TCP_OVERSIZE_DBGCHECK + /* check that pcb->unsent_oversize matches last_unsent->oversize_left */ + LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)", + pcb->unsent_oversize == last_unsent->oversize_left); +#endif /* TCP_OVERSIZE_DBGCHECK */ + oversize = pcb->unsent_oversize; + if (oversize > 0) { + LWIP_ASSERT("inconsistent oversize vs. space", oversize <= space); + seg = last_unsent; + oversize_used = LWIP_MIN(space, LWIP_MIN(oversize, len)); + pos += oversize_used; + oversize -= oversize_used; + space -= oversize_used; + } + /* now we are either finished or oversize is zero */ + LWIP_ASSERT("inconsistent oversize vs. len", (oversize == 0) || (pos == len)); +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: Chain a new pbuf to the end of pcb->unsent. + * + * As an exception when NOT copying the data, if the given data buffer + * directly follows the last unsent data buffer in memory, extend the last + * ROM pbuf reference to the buffer, thus saving a ROM pbuf allocation. + * + * We don't extend segments containing SYN/FIN flags or options + * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at + * the end. + */ + if ((pos < len) && (space > 0) && (last_unsent->len > 0)) { + u16_t seglen = LWIP_MIN(space, len - pos); + seg = last_unsent; + + /* Create a pbuf with a copy or reference to seglen bytes. We + * can use PBUF_RAW here since the data appears in the middle of + * a segment. A header will never be prepended. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* Data is copied */ + if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", + seglen)); + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + oversize_add = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ + TCP_DATA_COPY2(concat_p->payload, (const u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); +#if TCP_CHECKSUM_ON_COPY + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + queuelen += pbuf_clen(concat_p); + } else { + /* Data is not copied */ + /* If the last unsent pbuf is of type PBUF_ROM, try to extend it. */ + struct pbuf *p; + for (p = last_unsent->p; p->next != NULL; p = p->next); + if (p->type == PBUF_ROM && (const u8_t *)p->payload + p->len == (const u8_t *)arg) { + LWIP_ASSERT("tcp_write: ROM pbufs cannot be oversized", pos == 0); + extendlen = seglen; + } else { + if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } + /* reference the non-volatile payload data */ + ((struct pbuf_rom*)concat_p)->payload = (const u8_t*)arg + pos; + queuelen += pbuf_clen(concat_p); + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + tcp_seg_add_chksum(~inet_chksum((const u8_t*)arg + pos, seglen), seglen, + &concat_chksum, &concat_chksum_swapped); + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + } + + pos += seglen; + } + } else { +#if TCP_OVERSIZE + LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)", + pcb->unsent_oversize == 0); +#endif /* TCP_OVERSIZE */ + } + + /* + * Phase 3: Create new segments. + * + * The new segments are chained together in the local 'queue' + * variable, ready to be appended to pcb->unsent. + */ + while (pos < len) { + struct pbuf *p; + u16_t left = len - pos; + u16_t max_len = mss_local - optlen; + u16_t seglen = LWIP_MIN(left, max_len); +#if TCP_CHECKSUM_ON_COPY + u16_t chksum = 0; + u8_t chksum_swapped = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* If copy is set, memory should be allocated and data copied + * into pbuf */ + if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen", + (p->len >= seglen)); + TCP_DATA_COPY2((char *)p->payload + optlen, (const u8_t*)arg + pos, seglen, &chksum, &chksum_swapped); + } else { + /* Copy is not set: First allocate a pbuf for holding the data. + * Since the referenced data is available at least until it is + * sent out on the link (as it has to be ACKed by the remote + * party) we can safely use PBUF_ROM instead of PBUF_REF here. + */ + struct pbuf *p2; +#if TCP_OVERSIZE + LWIP_ASSERT("oversize == 0", oversize == 0); +#endif /* TCP_OVERSIZE */ + if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + chksum = ~inet_chksum((const u8_t*)arg + pos, seglen); + if (seglen & 1) { + chksum_swapped = 1; + chksum = SWAP_BYTES_IN_WORD(chksum); + } +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + ((struct pbuf_rom*)p2)->payload = (const u8_t*)arg + pos; + + /* Second, allocate a pbuf for the headers. */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + /* If allocation fails, we have to deallocate the data pbuf as + * well. */ + pbuf_free(p2); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for header pbuf\n")); + goto memerr; + } + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(p/*header*/, p2/*data*/); + } + + queuelen += pbuf_clen(p); + + /* Now that there are more segments queued, we check again if the + * length of the queue exceeds the configured maximum or + * overflows. */ + if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: queue too long %"U16_F" (%d)\n", + queuelen, (int)TCP_SND_QUEUELEN)); + pbuf_free(p); + goto memerr; + } + + if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) { + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = chksum; + seg->chksum_swapped = chksum_swapped; + seg->flags |= TF_SEG_DATA_CHECKSUMMED; +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL); + prev_seg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + prev_seg = seg; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n", + lwip_ntohl(seg->tcphdr->seqno), + lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg))); + + pos += seglen; + } + + /* + * All three segmentation phases were successful. We can commit the + * transaction. + */ +#if TCP_OVERSIZE_DBGCHECK + if ((last_unsent != NULL) && (oversize_add != 0)) { + last_unsent->oversize_left += oversize_add; + } +#endif /* TCP_OVERSIZE_DBGCHECK */ + + /* + * Phase 1: If data has been added to the preallocated tail of + * last_unsent, we update the length fields of the pbuf chain. + */ +#if TCP_OVERSIZE + if (oversize_used > 0) { + struct pbuf *p; + /* Bump tot_len of whole chain, len of tail */ + for (p = last_unsent->p; p; p = p->next) { + p->tot_len += oversize_used; + if (p->next == NULL) { + TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent); + p->len += oversize_used; + } + } + last_unsent->len += oversize_used; +#if TCP_OVERSIZE_DBGCHECK + LWIP_ASSERT("last_unsent->oversize_left >= oversize_used", + last_unsent->oversize_left >= oversize_used); + last_unsent->oversize_left -= oversize_used; +#endif /* TCP_OVERSIZE_DBGCHECK */ + } + pcb->unsent_oversize = oversize; +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: concat_p can be concatenated onto last_unsent->p, unless we + * determined that the last ROM pbuf can be extended to include the new data. + */ + if (concat_p != NULL) { + LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty", + (last_unsent != NULL)); + pbuf_cat(last_unsent->p, concat_p); + last_unsent->len += concat_p->tot_len; + } else if (extendlen > 0) { + struct pbuf *p; + LWIP_ASSERT("tcp_write: extension of reference requires reference", + last_unsent != NULL && last_unsent->p != NULL); + for (p = last_unsent->p; p->next != NULL; p = p->next) { + p->tot_len += extendlen; + } + p->tot_len += extendlen; + p->len += extendlen; + last_unsent->len += extendlen; + } + +#if TCP_CHECKSUM_ON_COPY + if (concat_chksummed) { + LWIP_ASSERT("tcp_write: concat checksum needs concatenated data", + concat_p != NULL || extendlen > 0); + /*if concat checksumm swapped - swap it back */ + if (concat_chksum_swapped) { + concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum); + } + tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum, + &last_unsent->chksum_swapped); + last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED; + } +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* + * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that + * is harmless + */ + if (last_unsent == NULL) { + pcb->unsent = queue; + } else { + last_unsent->next = queue; + } + + /* + * Finally update the pcb state. + */ + pcb->snd_lbb += len; + pcb->snd_buf -= len; +#if SNDBUF_SHARE + sndbuf_len -= len; +#endif + pcb->snd_queuelen = queuelen; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n", + pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued. */ + if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + return ERR_OK; +memerr: + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + + if (concat_p != NULL) { + pbuf_free(concat_p); + } + if (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + +/** + * Enqueue TCP options for transmission. + * + * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl(). + * + * @param pcb Protocol control block for the TCP connection. + * @param flags TCP header flags to set in the outgoing segment. + */ +err_t +tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) +{ + struct pbuf *p; + struct tcp_seg *seg; + u8_t optflags = 0; + u8_t optlen = 0; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)", + (flags & (TCP_SYN | TCP_FIN)) != 0); + + /* check for configured max queuelen and possible overflow (FIN flag should always come through!) */ + if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) && + ((flags & TCP_FIN) == 0)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + if (flags & TCP_SYN) { + optflags = TF_SEG_OPTS_MSS; +#if LWIP_WND_SCALE + if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_WND_SCALE)) { + /* In a (sent in state SYN_RCVD), the window scale option may only + be sent if we received a window scale option from the remote host. */ + optflags |= TF_SEG_OPTS_WND_SCALE; + } +#endif /* LWIP_WND_SCALE */ + } +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + /* Make sure the timestamp option is only included in data segments if we + agreed about it with the remote host. */ + optflags |= TF_SEG_OPTS_TS; + } +#endif /* LWIP_TCP_TIMESTAMPS */ + optlen = LWIP_TCP_OPT_LENGTH(optflags); + + /* Allocate pbuf with room for TCP header + options */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen", + (p->len >= optlen)); + + /* Allocate memory for tcp_seg, and fill in fields. */ + if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % LWIP_MIN(MEM_ALIGNMENT, 4)) == 0); + LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0); + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, + ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + lwip_ntohl(seg->tcphdr->seqno), + lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + /* Now append seg to pcb->unsent queue */ + if (pcb->unsent == NULL) { + pcb->unsent = seg; + } else { + struct tcp_seg *useg; + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + useg->next = seg; + } +#if TCP_OVERSIZE + /* The new unsent tail has no space */ + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + + /* SYN and FIN bump the sequence number */ + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + pcb->snd_lbb++; + /* optlen does not influence snd_buf */ + } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } + + /* update number of segments on the queues */ + pcb->snd_queuelen += pbuf_clen(seg->p); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue_flags: invalid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + return ERR_OK; +} + +#if LWIP_TCP_TIMESTAMPS +/* Build a timestamp option (12 bytes long) at the specified options pointer) + * + * @param pcb tcp_pcb + * @param opts option pointer where to store the timestamp option + */ +static void +tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) +{ + /* Pad with two NOP options to make everything nicely aligned */ + opts[0] = PP_HTONL(0x0101080A); + opts[1] = lwip_htonl(sys_now()); + opts[2] = lwip_htonl(pcb->ts_recent); +} +#endif + +#if LWIP_WND_SCALE +/** Build a window scale option (3 bytes long) at the specified options pointer) + * + * @param opts option pointer where to store the window scale option + */ +static void +tcp_build_wnd_scale_option(u32_t *opts) +{ + /* Pad with one NOP option to make everything nicely aligned */ + opts[0] = PP_HTONL(0x01030300 | TCP_RCV_SCALE); +} +#endif + +/** + * Send an ACK without data. + * + * @param pcb Protocol control block for the TCP connection to send the ACK + */ +err_t +tcp_send_empty_ack(struct tcp_pcb *pcb) +{ + err_t err; + struct pbuf *p; + u8_t optlen = 0; + struct netif *netif; +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + +#if LWIP_TCP_TIMESTAMPS + if (pcb->flags & TF_TIMESTAMP) { + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif + + p = tcp_output_alloc_header(pcb, optlen, 0, lwip_htonl(pcb->snd_nxt)); + if (p == NULL) { + /* let tcp_fasttmr retry sending this ACK */ + pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + + /* NB. MSS and window scale options are only sent on SYNs, so ignore them here */ +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (pcb->flags & TF_TIMESTAMP) { + tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1)); + } +#endif + + netif = ip_route(&pcb->local_ip, &pcb->remote_ip); + if (netif == NULL) { + err = ERR_RTE; + } else { +#if CHECKSUM_GEN_TCP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) { + tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); + } +#endif + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, pcb->tos, IP_PROTO_TCP, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + } + pbuf_free(p); + + if (err != ERR_OK) { + /* let tcp_fasttmr retry sending this ACK */ + pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW); + } else { + /* remove ACK flags from the PCB, as we sent an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + return err; +} + +/** + * @ingroup tcp_raw + * Find out what we can send and send it + * + * @param pcb Protocol control block for the TCP connection to send data + * @return ERR_OK if data has been sent or nothing to send + * another err_t on error + */ +err_t +tcp_output(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg, *useg; + u32_t wnd, snd_nxt; + err_t err; + struct netif *netif; +#if TCP_CWND_DEBUG + s16_t i = 0; +#endif /* TCP_CWND_DEBUG */ + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_output for listen-pcbs", + pcb->state != LISTEN); + + /* First, check if we are invoked by the TCP input processing + code. If so, we do not output anything. Instead, we rely on the + input processing code to call us when input processing is done + with. */ + if (tcp_input_pcb == pcb) { + return ERR_OK; + } + + wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); + + seg = pcb->unsent; + + /* If the TF_ACK_NOW flag is set and no data will be sent (either + * because the ->unsent queue is empty or because the window does + * not allow it), construct an empty ACK segment and send it. + * + * If data is to be sent, we will just piggyback the ACK (see below). + */ + if (pcb->flags & TF_ACK_NOW && + (seg == NULL || + lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { + return tcp_send_empty_ack(pcb); + } + + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next); + } + + netif = ip_route(&pcb->local_ip, &pcb->remote_ip); + if (netif == NULL) { + return ERR_RTE; + } + + /* If we don't have a local IP address, we get one from netif */ + if (ip_addr_isany(&pcb->local_ip)) { + const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, &pcb->remote_ip); + if (local_ip == NULL) { + return ERR_RTE; + } + ip_addr_copy(pcb->local_ip, *local_ip); + } + +#if TCP_OUTPUT_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F + ", cwnd %"TCPWNDSIZE_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); + } else { + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + lwip_ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + /* Check if we need to start the persistent timer when the next unsent segment + * does not fit within the remaining send window and RTO timer is not running (we + * have no in-flight data). A traditional approach would fill the remaining window + * with part of the unsent segment (which will engage zero-window probing upon + * reception of the zero window update from the receiver). This ensures the + * subsequent window update is reliably received. With the goal of being lightweight, + * we avoid splitting the unsent segment and treat the window as already zero. + */ + if (seg != NULL && + lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd && + wnd > 0 && wnd == pcb->snd_wnd && pcb->unacked == NULL) { + /* Start the persist timer */ + if (pcb->persist_backoff == 0) { + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + goto output_done; + } + /* data available and window allows it to be sent? */ + while (seg != NULL && + lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + /* Stop sending if the nagle algorithm would prevent it + * Don't stop: + * - if tcp_write had a memory error before (prevent delayed ACK timeout) or + * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - + * either seg->next != NULL or pcb->unacked == NULL; + * RST is no sent using tcp_write/tcp_output. + */ + if ((tcp_do_output_nagle(pcb) == 0) && + ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)) { + break; + } +#if TCP_CWND_DEBUG + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + lwip_ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + lwip_ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + if (pcb->state != SYN_SENT) { + TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); + } + +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ + err = tcp_output_segment(seg, pcb, netif); + if (err != ERR_OK) { + /* segment could not be sent, for whatever reason */ + pcb->flags |= TF_NAGLEMEMERR; + return err; + } + pcb->unsent = seg->next; + if (pcb->state != SYN_SENT) { + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + if (TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + /* unacked list is empty? */ + if (pcb->unacked == NULL) { + pcb->unacked = seg; + useg = seg; + /* unacked list is not empty? */ + } else { + /* In the case of fast retransmit, the packet should not go to the tail + * of the unacked queue, but rather somewhere before it. We need to check for + * this case. -STJ Jul 27, 2004 */ + if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) { + /* add segment to before tail of unacked list, keeping the list sorted */ + struct tcp_seg **cur_seg = &(pcb->unacked); + while (*cur_seg && + TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = (*cur_seg); + (*cur_seg) = seg; + } else { + /* add segment to tail of unacked list */ + useg->next = seg; + useg = useg->next; + } + } + /* do not queue empty segments on the unacked list */ + } else { + tcp_seg_free(seg); + } + seg = pcb->unsent; + } +output_done: +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + /* last unsent has been removed, reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + pcb->flags &= ~TF_NAGLEMEMERR; + return ERR_OK; +} + +/** + * Called by tcp_output() to actually send a TCP segment over IP. + * + * @param seg the tcp_seg to send + * @param pcb the tcp_pcb for the TCP connection used to send the segment + * @param netif the netif used to send the segment + */ +static err_t +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif) +{ + err_t err; + u16_t len; + u32_t *opts; + + if (seg->p->ref != 1) { + /* This can happen if the pbuf of this segment is still referenced by the + netif driver due to deferred transmission. Since this function modifies + p->len, we must not continue in this case. */ + return ERR_OK; + } + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = lwip_htonl(pcb->rcv_nxt); + + /* advertise our receive window size in this TCP segment */ +#if LWIP_WND_SCALE + if (seg->flags & TF_SEG_OPTS_WND_SCALE) { + /* The Window field in a SYN segment itself (the only type where we send + the window scale option) is never scaled. */ + seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(pcb->rcv_ann_wnd)); + } else +#endif /* LWIP_WND_SCALE */ + { + seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd))); + } + + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + + /* Add any requested options. NB MSS option is only set on SYN + packets, so ignore it here */ + /* cast through void* to get rid of alignment warnings */ + opts = (u32_t *)(void *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + u16_t mss; +#if TCP_CALCULATE_EFF_SEND_MSS + mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip); +#else /* TCP_CALCULATE_EFF_SEND_MSS */ + mss = TCP_MSS; +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + *opts = TCP_BUILD_MSS_OPTION(mss); + opts += 1; + } +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (seg->flags & TF_SEG_OPTS_TS) { + tcp_build_timestamp_option(pcb, opts); + opts += 3; + } +#endif +#if LWIP_WND_SCALE + if (seg->flags & TF_SEG_OPTS_WND_SCALE) { + tcp_build_wnd_scale_option(opts); + opts += 1; + } +#endif + + /* Set retransmission timer running if it is not currently enabled + This must be set before checking the route. */ + if (pcb->rtime < 0) { + pcb->rtime = 0; + } + + if (pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = lwip_ntohl(seg->tcphdr->seqno); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", + lwip_htonl(seg->tcphdr->seqno), lwip_htonl(seg->tcphdr->seqno) + + seg->len)); + + len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); + if (len == 0) { + /** Exclude retransmitted segments from this count. */ + MIB2_STATS_INC(mib2.tcpoutsegs); + } + + seg->p->len -= len; + seg->p->tot_len -= len; + + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) { +#if TCP_CHECKSUM_ON_COPY + u32_t acc; +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + u16_t chksum_slow = ip_chksum_pseudo(seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { + LWIP_ASSERT("data included but not checksummed", + seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4)); + } + + /* rebuild TCP header checksum (TCP header changes for retransmissions!) */ + acc = ip_chksum_pseudo_partial(seg->p, IP_PROTO_TCP, + seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip); + /* add payload checksum */ + if (seg->chksum_swapped) { + seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); + seg->chksum_swapped = 0; + } + acc += (u16_t)~(seg->chksum); + seg->tcphdr->chksum = FOLD_U32T(acc); +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + if (chksum_slow != seg->tcphdr->chksum) { + TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL( + ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n", + seg->tcphdr->chksum, chksum_slow)); + seg->tcphdr->chksum = chksum_slow; + } +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ +#else /* TCP_CHECKSUM_ON_COPY */ + seg->tcphdr->chksum = ip_chksum_pseudo(seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* TCP_CHECKSUM_ON_COPY */ + } +#endif /* CHECKSUM_GEN_TCP */ + TCP_STATS_INC(tcp.xmit); + + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ip_output_if(seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + pcb->tos, IP_PROTO_TCP, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + return err; +} + +/** + * Send a TCP RESET packet (empty segment with RST flag set) either to + * abort a connection or to show that there is no matching local connection + * for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip the local IP address to send the segment from + * @param remote_ip the remote IP address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ +void +tcp_rst(u32_t seqno, u32_t ackno, + const ip_addr_t *local_ip, const ip_addr_t *remote_ip, + u16_t local_port, u16_t remote_port) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct netif *netif; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = lwip_htons(local_port); + tcphdr->dest = lwip_htons(remote_port); + tcphdr->seqno = lwip_htonl(seqno); + tcphdr->ackno = lwip_htonl(ackno); + TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); +#if LWIP_WND_SCALE + tcphdr->wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF)); +#else + tcphdr->wnd = PP_HTONS(TCP_WND); +#endif + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + TCP_STATS_INC(tcp.xmit); + MIB2_STATS_INC(mib2.tcpoutrsts); + + netif = ip_route(local_ip, remote_ip); + if (netif != NULL) { +#if CHECKSUM_GEN_TCP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) { + tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, + local_ip, remote_ip); + } +#endif + /* Send output with hardcoded TTL/HL since we have no access to the pcb */ + ip_output_if(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP, netif); + } + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} + +/** + * Requeue all unacked segments for retransmission + * + * Called by tcp_slowtmr() for slow retransmission. + * + * @param pcb the tcp_pcb for which to re-enqueue all unacked segments + */ +void +tcp_rexmit_rto(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move all unacked segments to the head of the unsent queue */ + for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); + /* concatenate unsent queue after unacked queue */ + seg->next = pcb->unsent; +#if TCP_OVERSIZE_DBGCHECK + /* if last unsent changed, we need to update unsent_oversize */ + if (pcb->unsent == NULL) { + pcb->unsent_oversize = seg->oversize_left; + } +#endif /* TCP_OVERSIZE_DBGCHECK */ + /* unsent queue is the concatenated queue (of unacked, unsent) */ + pcb->unsent = pcb->unacked; + /* unacked queue is now empty */ + pcb->unacked = NULL; + + /* increment number of retransmissions */ + if (pcb->nrtx < 0xFF) { + ++pcb->nrtx; + } + + /* Don't take any RTT measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission */ + tcp_output(pcb); +} + +/** + * Requeue the first unacked segment for retransmission + * + * Called by tcp_receive() for fast retransmit. + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + struct tcp_seg **cur_seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move the first unacked segment to the unsent queue */ + /* Keep the unsent queue sorted. */ + seg = pcb->unacked; + pcb->unacked = seg->next; + + cur_seg = &(pcb->unsent); + while (*cur_seg && + TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = *cur_seg; + *cur_seg = seg; +#if TCP_OVERSIZE + if (seg->next == NULL) { + /* the retransmitted segment is last in unsent, so reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + if (pcb->nrtx < 0xFF) { + ++pcb->nrtx; + } + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission. */ + MIB2_STATS_INC(mib2.tcpretranssegs); + /* No need to call tcp_output: we are always called from tcp_input() + and thus tcp_output directly returns. */ +} + + +/** + * Handle retransmission after three dupacks received + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit_fast(struct tcp_pcb *pcb) +{ + if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: dupacks %"U16_F" (%"U32_F + "), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + lwip_ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + + /* Set ssthresh to half of the minimum of the current + * cwnd and the advertised window */ + pcb->ssthresh = LWIP_MIN(pcb->cwnd, pcb->snd_wnd) / 2; + + /* The minimum value for ssthresh should be 2 MSS */ + if (pcb->ssthresh < (2U * pcb->mss)) { + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: The minimum value for ssthresh %"TCPWNDSIZE_F + " should be min 2 mss %"U16_F"...\n", + pcb->ssthresh, (u16_t)(2*pcb->mss))); + pcb->ssthresh = 2*pcb->mss; + } + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + + /* Reset the retransmission timer to prevent immediate rto retransmissions */ + pcb->rtime = 0; + } +} + + +/** + * Send keepalive packets to keep a connection active although + * no data is sent over it. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a keepalive packet + */ +err_t +tcp_keepalive(struct tcp_pcb *pcb) +{ + err_t err; + struct pbuf *p; + struct netif *netif; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to ")); + ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent)); + + p = tcp_output_alloc_header(pcb, 0, 0, lwip_htonl(pcb->snd_nxt - 1)); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: could not allocate memory for pbuf\n")); + return ERR_MEM; + } + netif = ip_route(&pcb->local_ip, &pcb->remote_ip); + if (netif == NULL) { + err = ERR_RTE; + } else { +#if CHECKSUM_GEN_TCP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) { + struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); + } +#endif /* CHECKSUM_GEN_TCP */ + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + } + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n", + pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err)); + return err; +} + + +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +err_t +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + err_t err; + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + u16_t len; + u8_t is_fin; + u32_t snd_nxt; + struct netif *netif; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to ")); + ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if (seg == NULL) { + seg = pcb->unsent; + } + if (seg == NULL) { + /* nothing to send, zero window probe not needed */ + return ERR_OK; + } + + is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); + /* we want to send one seqno: either FIN or data (no options) */ + len = is_fin ? 0 : 1; + + p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return ERR_MEM; + } + tcphdr = (struct tcp_hdr *)p->payload; + + if (is_fin) { + /* FIN segment, no data */ + TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); + } else { + /* Data segment, copy in one byte from the head of the unacked queue */ + char *d = ((char *)p->payload + TCP_HLEN); + /* Depending on whether the segment has already been sent (unacked) or not + (unsent), seg->p->payload points to the IP header or TCP header. + Ensure we copy the first TCP data byte: */ + pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len); + } + + /* The byte may be acknowledged without the window being opened. */ + snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + 1; + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; + } + + netif = ip_route(&pcb->local_ip, &pcb->remote_ip); + if (netif == NULL) { + err = ERR_RTE; + } else { +#if CHECKSUM_GEN_TCP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) { + tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); + } +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + } + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F" err %d.\n", + pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err)); + return err; +} +#endif /* LWIP_TCP */ diff --git a/sdk/Src/Network/lwip2.0.3/core/timeouts.c b/sdk/Src/Network/lwip2.0.3/core/timeouts.c new file mode 100644 index 0000000..bcaa80d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/timeouts.c @@ -0,0 +1,437 @@ +/** + * @file + * Stack-internal timers implementation. + * This file includes timer callbacks for stack-internal timers as well as + * functions to set up or stop timers and check for expired timers. + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#include "lwip/timeouts.h" +#include "lwip/priv/tcp_priv.h" + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/priv/tcpip_priv.h" + +#include "lwip/ip4_frag.h" +#include "lwip/etharp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" +#include "lwip/sys.h" +#include "lwip/pbuf.h" +#include "lwip/alg.h" + +#if LWIP_DEBUG_TIMERNAMES +#define HANDLER(x) x, #x +#else /* LWIP_DEBUG_TIMERNAMES */ +#define HANDLER(x) x +#endif /* LWIP_DEBUG_TIMERNAMES */ + +/** This array contains all stack-internal cyclic timers. To get the number of + * timers, use LWIP_ARRAYSIZE() */ +const struct lwip_cyclic_timer lwip_cyclic_timers[] = { +#if LWIP_TCP + /* The TCP timer is a special case: it does not have to run always and + is triggered to start from TCP using tcp_timer_needed() */ + {TCP_TMR_INTERVAL, HANDLER(tcp_tmr)}, +#endif /* LWIP_TCP */ +#if LWIP_IPV4 +#if IP_REASSEMBLY + {IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)}, +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + {ARP_TMR_INTERVAL, HANDLER(etharp_tmr)}, +#endif /* LWIP_ARP */ +#if LWIP_DHCP + {DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)}, + {DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)}, +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + {AUTOIP_TMR_INTERVAL, HANDLER(autoip_tmr)}, +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + {IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)}, +#endif /* LWIP_IGMP */ +#endif /* LWIP_IPV4 */ +#if LWIP_DNS + {DNS_TMR_INTERVAL, HANDLER(dns_tmr)}, +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + {ND6_TMR_INTERVAL, HANDLER(nd6_tmr)}, +#if LWIP_IPV6_REASS + {IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)}, +#endif /* LWIP_IPV6_REASS */ +#if LWIP_IPV6_MLD + {MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)}, +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ +}; + +#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM +#define MAX_TIMEO_SIZE 5 +/** The one and only timeout list */ +static struct sys_timeo *next_timeout[MAX_TIMEO_SIZE]; +static u32_t timeouts_last_time; + +#if LWIP_TCP +/** global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +tcpip_tcp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + /* call TCP timer handler */ + tcp_tmr(); + /* timer still needed? */ + if (tcp_active_pcbs || tcp_tw_pcbs) { + /* restart timer */ + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } else { + /* disable timer */ + tcpip_tcp_timer_active = 0; + } +} + +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ +void +tcp_timer_needed(void) +{ + /* timer is off but needed again? */ + if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + /* enable and start timer */ + tcpip_tcp_timer_active = 1; + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } +} +#endif /* LWIP_TCP */ + +/** + * Timer callback function that calls mld6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +cyclic_timer(void *arg) +{ + const struct lwip_cyclic_timer* cyclic = (const struct lwip_cyclic_timer*)arg; +#if LWIP_DEBUG_TIMERNAMES + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name)); +#endif + cyclic->handler(); + sys_timeout(cyclic->interval_ms, cyclic_timer, arg); +} + +/** Initialize this module */ +void sys_timeouts_init(void) +{ + size_t i; + /* tcp_tmr() at index 0 is started on demand */ + for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) { + /* we have to cast via size_t to get rid of const warning + (this is OK as cyclic_timer() casts back to const* */ + sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, LWIP_CONST_CAST(void*, &lwip_cyclic_timers[i])); + } + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = sys_now(); +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_timeouts_mbox_fetch() + * - by calling sys_check_timeouts() (NO_SYS==1 only) + * + * @param msecs time in milliseconds after that the timer should expire + * @param handler callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +#if LWIP_DEBUG_TIMERNAMES +void +sys_timeout_debug_p(u8 timeo_assigned, u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) +#else /* LWIP_DEBUG_TIMERNAMES */ +void +sys_timeout_p(u8 timeo_assigned, u32_t msecs, sys_timeout_handler handler, void *arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ +{ + struct sys_timeo *timeout, *t; + u32_t now, diff; + struct sys_timeo **timeo = &next_timeout[timeo_assigned]; + timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); + return; + } + + now = sys_now(); + if (*timeo == NULL) { + diff = 0; + timeouts_last_time = now; + } else { + diff = now - timeouts_last_time; + } + + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; + timeout->time = msecs + diff; +#if LWIP_DEBUG_TIMERNAMES + timeout->handler_name = handler_name; + LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", + (void *)timeout, msecs, handler_name, (void *)arg)); +#endif /* LWIP_DEBUG_TIMERNAMES */ + + if (*timeo == NULL) { + *timeo = timeout; + return; + } + + if ((*timeo)->time > msecs) { + (*timeo)->time -= msecs; + timeout->next = *timeo; + *timeo = timeout; + } else { + for(t = *timeo; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } else if (timeout->time > msecs) { + /* If this is the case, 'timeouts_last_time' and 'now' differs too much. + This can be due to sys_check_timeouts() not being called at the right + times, but also when stopping in a breakpoint. Anyway, let's assume + this is not wanted, so add the first timer's time instead of 'diff' */ + timeout->time = msecs + (*timeo)->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * Go through timeout list (for this task only) and remove the first matching + * entry (subsequent entries remain untouched), even though the timeout has not + * triggered yet. + * + * @param handler callback function that would be called by the timeout + * @param arg callback argument that would be passed to handler +*/ +void +sys_untimeout_p(u8 timeo_assigned, sys_timeout_handler handler, void *arg) +{ + struct sys_timeo *prev_t, *t; + struct sys_timeo **timeo = &next_timeout[timeo_assigned]; + + if (*timeo == NULL) { + return; + } + + for (t = *timeo, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == handler) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + *timeo = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + +/** + * @ingroup lwip_nosys + * Handle timeouts for NO_SYS==1 (i.e. without using + * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout + * handler functions when timeouts expire. + * + * Must be called periodically from your main loop. + */ +#if !NO_SYS && !defined __DOXYGEN__ +static +#endif /* !NO_SYS */ +void +sys_check_timeouts(void) +{ + struct sys_timeo **timeo = &next_timeout[0]; + if (*timeo) { + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + u8_t had_one; + u32_t now; + + now = sys_now(); + /* this cares for wraparounds */ + diff = now - timeouts_last_time; + do { + PBUF_CHECK_FREE_OOSEQ(); + had_one = 0; + tmptimeout = *timeo; + if (tmptimeout && (tmptimeout->time <= diff)) { + /* timeout has expired */ + had_one = 1; + timeouts_last_time += tmptimeout->time; + diff -= tmptimeout->time; + *timeo = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { +#if !NO_SYS + /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the + timeout handler function. */ + LOCK_TCPIP_CORE(); +#endif /* !NO_SYS */ + handler(arg); +#if !NO_SYS + UNLOCK_TCPIP_CORE(); +#endif /* !NO_SYS */ + } + LWIP_TCPIP_THREAD_ALIVE(); + } + /* repeat until all expired timers have been called */ + } while (had_one); + } +} + +/** Set back the timestamp of the last call to sys_check_timeouts() + * This is necessary if sys_check_timeouts() hasn't been called for a long + * time (e.g. while saving energy) to prevent all timer functions of that + * period being called. + */ +void +sys_restart_timeouts(void) +{ + timeouts_last_time = sys_now(); +} + +/** Return the time left before the next timeout is due. If no timeouts are + * enqueued, returns 0xffffffff + */ +#if !NO_SYS +static +#endif /* !NO_SYS */ +u32_t +sys_timeouts_sleeptime_p(u8 timeo_assigned) +{ + u32_t diff; + struct sys_timeo **timeo = &next_timeout[timeo_assigned]; + if (*timeo == NULL) { + return 0xffffffff; + } + diff = sys_now() - timeouts_last_time; + if (diff > (*timeo)->time) { + return 0; + } else { + return (*timeo)->time - diff; + } +} + +#if !NO_SYS + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_timeouts_mbox_fetch_p(u8 timeo_assigned, sys_mbox_t *mbox, void **msg) +{ + u32_t sleeptime; + + struct sys_timeo **timeo = &next_timeout[timeo_assigned]; +again: + if (!(*timeo)) { + sys_arch_mbox_fetch(mbox, msg, 0); + return; + } + + sleeptime = sys_timeouts_sleeptime_p(timeo_assigned); + if (sleeptime == 0 || sys_arch_mbox_fetch(mbox, msg, sleeptime) == SYS_ARCH_TIMEOUT) { + /* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred + before a message could be fetched. */ + sys_check_timeouts(); + /* We try again to fetch a message from the mbox. */ + goto again; + } +} + +#endif /* NO_SYS */ + +#else /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */ +/* Satisfy the TCP code which calls this function */ +void +tcp_timer_needed(void) +{ +} +#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */ diff --git a/sdk/Src/Network/lwip2.0.3/core/timers.c b/sdk/Src/Network/lwip2.0.3/core/timers.c new file mode 100644 index 0000000..1b42d11 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/timers.c @@ -0,0 +1,572 @@ +/** + * @file + * Stack-internal timers implementation. + * This file includes timer callbacks for stack-internal timers as well as + * functions to set up or stop timers and check for expired timers. + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#include "lwip/timers.h" +#include "lwip/priv/tcp_priv.h" + +#if LWIP_TIMERS + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/priv/tcpip_priv.h" + +#include "lwip/ip_frag.h" +#include "netif/etharp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" +#include "lwip/sys.h" +#include "lwip/pbuf.h" +#include "lwip/alg.h" + +#define MAX_TIMEO_SIZE 5 +/** The one and only timeout list */ +static struct sys_timeo *next_timeout[MAX_TIMEO_SIZE]; +#if NO_SYS +static u32_t timeouts_last_time; +#endif /* NO_SYS */ + +#if LWIP_TCP +/** global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +tcpip_tcp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + /* call TCP timer handler */ + tcp_tmr(); + /* timer still needed? */ + if (tcp_active_pcbs || tcp_tw_pcbs) { + /* restart timer */ + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } else { + /* disable timer */ + tcpip_tcp_timer_active = 0; + } +} + +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ +void +tcp_timer_needed(void) +{ + /* timer is off but needed again? */ + if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + /* enable and start timer */ + tcpip_tcp_timer_active = 1; + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } +} +#endif /* LWIP_TCP */ + +#if LWIP_IPV4 +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n")); + ip_reass_tmr(); + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +} +#endif /* IP_REASSEMBLY */ + +#if LWIP_ARP +/** + * Timer callback function that calls etharp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n")); + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif /* LWIP_ARP */ + +#if LWIP_DHCP +/** + * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_coarse_tmr(); + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); +} + +/** + * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_fine(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); + dhcp_fine_tmr(); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +} +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP +/** + * Timer callback function that calls autoip_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +autoip_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n")); + autoip_tmr(); + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +} +#endif /* LWIP_AUTOIP */ + +#if LWIP_IGMP +/** + * Timer callback function that calls igmp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +igmp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n")); + igmp_tmr(); + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +} +#endif /* LWIP_IGMP */ +#endif /* LWIP_IPV4 */ + +#if LWIP_DNS +/** + * Timer callback function that calls dns_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dns_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +#if LWIP_IPV6 +/** + * Timer callback function that calls nd6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +nd6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nd6_tmr()\n")); + nd6_tmr(); + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +} + +#if LWIP_IPV6_REASS +/** + * Timer callback function that calls ip6_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip6_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip6_reass_tmr()\n")); + ip6_reass_tmr(); + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +} +#endif /* LWIP_IPV6_REASS */ + +#if LWIP_IPV6_MLD +/** + * Timer callback function that calls mld6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +mld6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: mld6_tmr()\n")); + mld6_tmr(); + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +} +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +/** Initialize this module */ +void sys_timeouts_init(void) +{ +#if LWIP_IPV4 +#if IP_REASSEMBLY + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif /* LWIP_ARP */ +#if LWIP_DHCP + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +#endif /* LWIP_IGMP */ +#endif /* LWIP_IPV4 */ +#if LWIP_DNS + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +#if LWIP_IPV6_REASS + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +#endif /* LWIP_IPV6_REASS */ +#if LWIP_IPV6_MLD + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +#if NO_SYS + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = sys_now(); +#endif +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_timeouts_mbox_fetch() + * - by calling sys_check_timeouts() (NO_SYS==1 only) + * + * @param msecs time in milliseconds after that the timer should expire + * @param handler callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +#if LWIP_DEBUG_TIMERNAMES +void +sys_timeout_debug_p(u8 timeo_assigned, u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) +#else /* LWIP_DEBUG_TIMERNAMES */ +void +sys_timeout_p(u8 timeo_assigned, u32_t msecs, sys_timeout_handler handler, void *arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ +{ + struct sys_timeo *timeout, *t; + struct sys_timeo **timeo = &next_timeout[timeo_assigned]; + timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); + return; + } + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; + timeout->time = msecs; +#if LWIP_DEBUG_TIMERNAMES + timeout->handler_name = handler_name; + LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", + (void *)timeout, msecs, handler_name, (void *)arg)); +#endif /* LWIP_DEBUG_TIMERNAMES */ + + if (*timeo == NULL) { + *timeo = timeout; + return; + } + + if ((*timeo)->time > msecs) { + (*timeo)->time -= msecs; + timeout->next = *timeo; + *timeo = timeout; + } else { + for(t = *timeo; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * Go through timeout list (for this task only) and remove the first matching + * entry (subsequent entries remain untouched), even though the timeout has not + * triggered yet. + * + * @param handler callback function that would be called by the timeout + * @param arg callback argument that would be passed to handler +*/ +void +sys_untimeout_p(u8 timeo_assigned, sys_timeout_handler handler, void *arg) +{ + struct sys_timeo *prev_t, *t; + struct sys_timeo **timeo = &next_timeout[timeo_assigned]; + + if (*timeo == NULL) { + return; + } + + for (t = *timeo, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == handler) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + *timeo = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + +#if NO_SYS + +/** Handle timeouts for NO_SYS==1 (i.e. without using + * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout + * handler functions when timeouts expire. + * + * Must be called periodically from your main loop. + */ +void +sys_check_timeouts(void) +{ + struct sys_timeo **timeo = &next_timeout[0]; + if (*timeo) { + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + u8_t had_one; + u32_t now; + + now = sys_now(); + /* this cares for wraparounds */ + diff = now - timeouts_last_time; + do + { +#if PBUF_POOL_FREE_OOSEQ + PBUF_CHECK_FREE_OOSEQ(); +#endif /* PBUF_POOL_FREE_OOSEQ */ + had_one = 0; + tmptimeout = *timeo; + if (tmptimeout && (tmptimeout->time <= diff)) { + /* timeout has expired */ + had_one = 1; + timeouts_last_time = now; + diff -= tmptimeout->time; + *timeo = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + handler(arg); + } + } + /* repeat until all expired timers have been called */ + }while(had_one); + } +} + +/** Set back the timestamp of the last call to sys_check_timeouts() + * This is necessary if sys_check_timeouts() hasn't been called for a long + * time (e.g. while saving energy) to prevent all timer functions of that + * period being called. + */ +void +sys_restart_timeouts(void) +{ + timeouts_last_time = sys_now(); +} + +/** Return the time left before the next timeout is due. If no timeouts are + * enqueued, returns 0xffffffff + */ +u32_t +sys_timeouts_sleeptime(void) +{ + u32_t diff; + if (next_timeout == NULL) { + return 0xffffffff; + } + diff = sys_now() - timeouts_last_time; + if (diff > next_timeout->time) { + return 0; + } else { + return next_timeout->time - diff; + } +} + +#else /* NO_SYS */ + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_timeouts_mbox_fetch_p(u8 timeo_assigned, sys_mbox_t mbox, void **msg) +{ + u32_t time_needed; + struct sys_timeo *tmptimeout; + sys_timeout_handler handler; + void *arg; + + struct sys_timeo **timeo = &next_timeout[timeo_assigned]; + + again: + if (!(*timeo)) { + time_needed = sys_arch_mbox_fetch(&mbox, msg, 0); + } else { + if ((*timeo)->time > 0) { + time_needed = sys_arch_mbox_fetch(&mbox, msg, (*timeo)->time); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = *timeo; + *timeo = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the + timeout handler function. */ + LOCK_TCPIP_CORE(); + handler(arg); + UNLOCK_TCPIP_CORE(); + } + LWIP_TCPIP_THREAD_ALIVE(); + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < (*timeo)->time) { + (*timeo)->time -= time_needed; + } else { + (*timeo)->time = 0; + } + } + } +} + +#endif /* NO_SYS */ + +#else /* LWIP_TIMERS */ +/* Satisfy the TCP code which calls this function */ +void +tcp_timer_needed(void) +{ +} +#endif /* LWIP_TIMERS */ diff --git a/sdk/Src/Network/lwip2.0.3/core/udp.c b/sdk/Src/Network/lwip2.0.3/core/udp.c new file mode 100644 index 0000000..06df759 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/core/udp.c @@ -0,0 +1,1208 @@ +/** + * @file + * User Datagram Protocol module\n + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).\n + * See also @ref udp_raw + * + * @defgroup udp_raw UDP + * @ingroup callbackstyle_api + * User Datagram Protocol module\n + * @see @ref raw_api and @ref netconn + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/icmp6.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/alg.h" + +#include + +#ifndef UDP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define UDP_LOCAL_PORT_RANGE_START 0xc000 +#define UDP_LOCAL_PORT_RANGE_END 0xffff +#define UDP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START)) +#endif + +/* last local UDP port */ +static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START; + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Initialize this module. + */ +void +udp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Allocate a new local UDP port. + * + * @return a new (free) local UDP port number + */ +static u16_t +udp_new_port(void) +{ + u16_t n = 0; + struct udp_pcb *pcb; + +again: + if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) { + udp_port = UDP_LOCAL_PORT_RANGE_START; + } + /* Check all PCBs. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == udp_port) { + if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + return udp_port; +} + +/** Common code to see if the current input packet matches the pcb + * (current input packet is accessed via ip(4/6)_current_* macros) + * + * @param pcb pcb to check + * @param inp network interface on which the datagram was received (only used for IPv4) + * @param broadcast 1 if his is an IPv4 broadcast (global or subnet-only), 0 otherwise (only used for IPv4) + * @return 1 on match, 0 otherwise + */ +static u8_t +udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast) +{ + LWIP_UNUSED_ARG(inp); /* in IPv6 only case */ + LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */ + + /* Dual-stack: PCBs listening to any IP type also listen to any IP address */ + if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { +#if LWIP_IPV4 && IP_SOF_BROADCAST_RECV + if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) { + return 0; + } +#endif /* LWIP_IPV4 && IP_SOF_BROADCAST_RECV */ + return 1; + } + + /* Only need to check PCB if incoming IP version matches PCB IP version */ + if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) { +#if LWIP_IPV4 + /* Special case: IPv4 broadcast: all or broadcasts in my subnet + * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */ + if (broadcast != 0) { +#if IP_SOF_BROADCAST_RECV + if (ip_get_option(pcb, SOF_BROADCAST)) +#endif /* IP_SOF_BROADCAST_RECV */ + { + if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) || + ((ip4_current_dest_addr()->addr == IPADDR_BROADCAST)) || + ip4_addr_netcmp(ip_2_ip4(&pcb->local_ip), ip4_current_dest_addr(), netif_ip4_netmask(inp))) { + return 1; + } + } + } else +#endif /* LWIP_IPV4 */ + /* Handle IPv4 and IPv6: all or exact match */ + if (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) { + return 1; + } + } + + return 0; +} + +/** + * Process an incoming UDP datagram. + * + * Given an incoming UDP datagram (as a chain of pbufs) this function + * finds a corresponding UDP PCB and hands over the pbuf to the pcbs + * recv function. If no pcb is found or the datagram is incorrect, the + * pbuf is freed. + * + * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) + * @param inp network interface on which the datagram was received. + * + */ +void +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb, *prev; + struct udp_pcb *uncon_pcb; + u16_t src, dest; + u8_t broadcast; + u8_t for_us = 0; + + LWIP_UNUSED_ARG(inp); + + PERF_START; + + UDP_STATS_INC(udp.recv); + + /* Check minimum length (UDP header) */ + if (p->len < UDP_HLEN) { + /* drop short packets */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + UDP_STATS_INC(udp.lenerr); + UDP_STATS_INC(udp.drop); + MIB2_STATS_INC(mib2.udpinerrors); + pbuf_free(p); + goto end; + } + + udphdr = (struct udp_hdr *)p->payload; + + /* is broadcast packet ? */ + broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()); + + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + + /* convert src and dest ports to host byte order */ + src = lwip_ntohs(udphdr->src); + dest = lwip_ntohs(udphdr->dest); + + udp_debug_print(udphdr); + + /* print the UDP source and destination */ + LWIP_DEBUGF(UDP_DEBUG, ("udp (")); + ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest))); + ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src))); + + pcb = NULL; + prev = NULL; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb. + * 'Perfect match' pcbs (connected to the remote port & ip address) are + * preferred. If no perfect match is found, the first unconnected pcb that + * matches the local port and ip address gets the datagram. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, ("pcb (")); + ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); + ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if ((pcb->local_port == dest) && + (udp_input_local_match(pcb, inp, broadcast) != 0)) { + if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) && + ((uncon_pcb == NULL) +#if SO_REUSE + /* prefer specific IPs over cath-all */ + || !ip_addr_isany(&pcb->local_ip) +#endif /* SO_REUSE */ + )) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + + /* compare PCB remote addr+port to UDP source addr+port */ + if ((pcb->remote_port == src) && + (ip_addr_isany_val(pcb->remote_ip) || + ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) { + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } else { + UDP_STATS_INC(udp.cachehit); + } + break; + } + } + + prev = pcb; + } + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } + + /* Check checksum if this is a match or if it was directed at us. */ + if (pcb != NULL) { + for_us = 1; + } else { +#if LWIP_IPV6 + if (ip_current_is_v6()) { + for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0; + } +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 + if (!ip_current_is_v6()) { + for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr()); + } +#endif /* LWIP_IPV4 */ + } + + if (for_us) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if CHECKSUM_CHECK_UDP + IF__NETIF_CHECKSUM_ENABLED(inp, CHECKSUM_CHECK_UDP) { +#if LWIP_UDPLITE + if (ip_current_header_proto() == IP_PROTO_UDPLITE) { + /* Do the UDP Lite checksum */ + u16_t chklen = lwip_ntohs(udphdr->len); + if (chklen < sizeof(struct udp_hdr)) { + if (chklen == 0) { + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet (See RFC 3828 chap. 3.1) */ + chklen = p->tot_len; + } else { + /* At least the UDP-Lite header must be covered by the + checksum! (Again, see RFC 3828 chap. 3.1) */ + goto chkerr; + } + } + if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE, + p->tot_len, chklen, + ip_current_src_addr(), ip_current_dest_addr()) != 0) { + goto chkerr; + } + } else +#endif /* LWIP_UDPLITE */ + { + if (udphdr->chksum != 0) { + if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len, + ip_current_src_addr(), + ip_current_dest_addr()) != 0) { + goto chkerr; + } + } + } + } +#endif /* CHECKSUM_CHECK_UDP */ + if (pbuf_header(p, -UDP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + UDP_STATS_INC(udp.drop); + MIB2_STATS_INC(mib2.udpinerrors); + pbuf_free(p); + goto end; + } + + if (pcb != NULL) { + MIB2_STATS_INC(mib2.udpindatagrams); +#if SO_REUSE && SO_REUSE_RXTOALL + if (ip_get_option(pcb, SOF_REUSEADDR) && + (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) { + /* pass broadcast- or multicast packets to all multicast pcbs + if SOF_REUSEADDR is set on the first match */ + struct udp_pcb *mpcb; + u8_t p_header_changed = 0; + s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); + for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { + if (mpcb != pcb) { + /* compare PCB local addr+port to UDP destination addr+port */ + if ((mpcb->local_port == dest) && + (udp_input_local_match(mpcb, inp, broadcast) != 0)) { + /* pass a copy of the packet to all local matches */ + if (mpcb->recv != NULL) { + struct pbuf *q; + /* for that, move payload to IP header again */ + if (p_header_changed == 0) { + pbuf_header_force(p, hdrs_len); + p_header_changed = 1; + } + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + err_t err = pbuf_copy(q, p); + if (err == ERR_OK) { + /* move payload to UDP data */ + pbuf_header(q, -hdrs_len); + mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + } + } + } + } + } + } + if (p_header_changed) { + /* and move payload to UDP data again */ + pbuf_header(p, -hdrs_len); + } + } +#endif /* SO_REUSE && SO_REUSE_RXTOALL */ + /* callback */ + if (pcb->recv != NULL) { + /* now the recv function is responsible for freeing p */ + pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); + } else { + /* no recv function registered? then we have to free the pbuf! */ + pbuf_free(p); + goto end; + } + } else { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); + +#if LWIP_ICMP || LWIP_ICMP6 + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) { + /* move payload pointer back to ip header */ + pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN)); + icmp_port_unreach(ip_current_is_v6(), p); + } +#endif /* LWIP_ICMP || LWIP_ICMP6 */ + UDP_STATS_INC(udp.proterr); + UDP_STATS_INC(udp.drop); + MIB2_STATS_INC(mib2.udpnoports); + pbuf_free(p); + } + } else { + pbuf_free(p); + } +end: + PERF_STOP("udp_input"); + return; +#if CHECKSUM_CHECK_UDP +chkerr: + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + MIB2_STATS_INC(mib2.udpinerrors); + pbuf_free(p); + PERF_STOP("udp_input"); +#endif /* CHECKSUM_CHECK_UDP */ +} + +/** + * @ingroup udp_raw + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * + * The datagram will be sent to the current remote_ip & remote_port + * stored in pcb. If the pcb is not bound to a port, it will + * automatically be bound to a random port. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occurred. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * - ERR_VAL. No PCB or PCB is dual-stack + * - More errors could be returned by lower protocol layers. + * + * @see udp_disconnect() udp_sendto() + */ +err_t +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + if ((pcb == NULL) || IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) { + return ERR_VAL; + } + + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); +} + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +/** @ingroup udp_raw + * Same as udp_send() but with checksum + */ +err_t +udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum) +{ + if ((pcb == NULL) || IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) { + return ERR_VAL; + } + + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port, + have_chksum, chksum); +} +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +/** + * @ingroup udp_raw + * Send data to a specified address using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * If the PCB already has a remote address association, it will + * be restored after the data is sent. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); +} + +/** @ingroup udp_raw + * Same as udp_sendto(), but with checksum */ +err_t +udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, + u16_t dst_port, u8_t have_chksum, u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct netif *netif; + const ip_addr_t *dst_ip_route = dst_ip; + + if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) { + return ERR_VAL; + } + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); + +#if LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) + if (ip_addr_ismulticast(dst_ip_route)) { +#if LWIP_IPV6 + if (IP_IS_V6(dst_ip)) { + /* For multicast, find a netif based on source address. */ + dst_ip_route = &pcb->local_ip; + } else +#endif /* LWIP_IPV6 */ + { +#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS + /* IPv4 does not use source-based routing by default, so we use an + administratively selected interface for multicast by default. + However, this can be overridden by setting an interface address + in pcb->multicast_ip that is used for routing. */ + if (!ip_addr_isany_val(pcb->multicast_ip) && + !ip4_addr_cmp(ip_2_ip4(&pcb->multicast_ip), IP4_ADDR_BROADCAST)) { + dst_ip_route = &pcb->multicast_ip; + } +#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */ + } + } +#endif /* LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) */ + + /* find the outgoing network interface for this packet */ + if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { + /* Don't call ip_route() with IP_ANY_TYPE */ + netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(dst_ip_route)), dst_ip_route); + } else { + netif = ip_route(&pcb->local_ip, dst_ip_route); + } + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to ")); + ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, dst_ip); + LWIP_DEBUGF(UDP_DEBUG, ("\n")); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +} + +/** + * @ingroup udp_raw + * Send data to a specified address using UDP. + * The netif used for sending can be specified. + * + * This function exists mainly for DHCP, to be able to send UDP packets + * on a netif that is still down. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * @param netif the netif used for sending. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); +} + +/** Same as udp_sendto_if(), but with checksum */ +err_t +udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, + u16_t dst_port, struct netif *netif, u8_t have_chksum, + u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + const ip_addr_t *src_ip; + + if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) { + return ERR_VAL; + } + + /* PCB local address is IP_ANY_ADDR? */ +#if LWIP_IPV6 + if (IP_IS_V6(dst_ip)) { + if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip))) { + src_ip = ip6_select_source_address(netif, ip_2_ip6(dst_ip)); + if (src_ip == NULL) { + /* No suitable source address was found. */ + return ERR_RTE; + } + } else { + /* use UDP PCB local IPv6 address as source address, if still valid. */ + if (netif_get_ip6_addr_match(netif, ip_2_ip6(&pcb->local_ip)) < 0) { + /* Address isn't valid anymore. */ + return ERR_RTE; + } + src_ip = &pcb->local_ip; + } + } +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 && LWIP_IPV6 + else +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +#if LWIP_IPV4 + if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) || + ip4_addr_ismulticast(ip_2_ip4(&pcb->local_ip))) { + /* if the local_ip is any or multicast + * use the outgoing network interface IP address as source address */ + src_ip = netif_ip_addr4(netif); + } else { + /* check if UDP PCB local IP address is correct + * this could be an old address if netif->ip_addr has changed */ + if (!ip4_addr_cmp(ip_2_ip4(&(pcb->local_ip)), netif_ip4_addr(netif))) { + /* local_ip doesn't match, drop the packet */ + return ERR_RTE; + } + /* use UDP PCB local IP address as source address */ + src_ip = &pcb->local_ip; + } +#endif /* LWIP_IPV4 */ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum, src_ip); +#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + return udp_sendto_if_src(pcb, p, dst_ip, dst_port, netif, src_ip); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +} + +/** @ingroup udp_raw + * Same as @ref udp_sendto_if, but with source address */ +err_t +udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0, src_ip); +} + +/** Same as udp_sendto_if_src(), but with checksum */ +err_t +udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, + u16_t dst_port, struct netif *netif, u8_t have_chksum, + u16_t chksum, const ip_addr_t *src_ip) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct udp_hdr *udphdr; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + u8_t ip_proto; + u8_t ttl; + + if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) || + !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) { + return ERR_VAL; + } + +#if LWIP_IPV4 && IP_SOF_BROADCAST + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && +#if LWIP_IPV6 + IP_IS_V4(dst_ip) && +#endif /* LWIP_IPV6 */ + ip_addr_isbroadcast(dst_ip, netif)) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + return ERR_VAL; + } +#endif /* LWIP_IPV4 && IP_SOF_BROADCAST */ + + /* if the PCB is not yet bound to a port, bind it here */ + if (pcb->local_port == 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); + err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); + return err; + } + } + + /* not enough space to add an UDP header to first pbuf in given p chain? */ + if (pbuf_header(p, UDP_HLEN)) { + /* allocate header in a separate new pbuf */ + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p (only if p contains data) */ + pbuf_chain(q, p); + } + /* first pbuf q points to header pbuf */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* adding space for header within p succeeded */ + /* first pbuf q equals given pbuf */ + q = p; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); + } + LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", + (q->len >= sizeof(struct udp_hdr))); + /* q now represents the packet to be sent */ + udphdr = (struct udp_hdr *)q->payload; + udphdr->src = lwip_htons(pcb->local_port); + udphdr->dest = lwip_htons(dst_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* Multicast Loop? */ +#if (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) + if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) { + q->flags |= PBUF_FLAG_MCASTLOOP; + } +#endif /* (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); + +#if LWIP_UDPLITE + /* UDP Lite protocol? */ + if (pcb->flags & UDP_FLAGS_UDPLITE) { + u16_t chklen, chklen_hdr; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); + /* set UDP message length in UDP header */ + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { + if (chklen != 0) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); + } + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet. (See RFC 3828 chap. 3.1) + At least the UDP-Lite header must be covered by the + checksum, therefore, if chksum_len has an illegal + value, we generate the checksum over the complete + packet to be safe. */ + chklen_hdr = 0; + chklen = q->tot_len; + } + udphdr->len = lwip_htons(chklen_hdr); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) { +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + chklen = UDP_HLEN; + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + udphdr->chksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDPLITE, + q->tot_len, chklen, src_ip, dst_ip); +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + acc = udphdr->chksum + (u16_t)~(chksum); + udphdr->chksum = FOLD_U32T(acc); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } + } +#endif /* CHECKSUM_GEN_UDP */ + + ip_proto = IP_PROTO_UDPLITE; + } else +#endif /* LWIP_UDPLITE */ + { /* UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); + udphdr->len = lwip_htons(q->tot_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) { + /* Checksum is mandatory over IPv6. */ + if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + u16_t udpchksum; +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + udpchksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDP, + q->tot_len, UDP_HLEN, src_ip, dst_ip); + acc = udpchksum + (u16_t)~(chksum); + udpchksum = FOLD_U32T(acc); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len, + src_ip, dst_ip); + } + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udpchksum == 0x0000) { + udpchksum = 0xffff; + } + udphdr->chksum = udpchksum; + } + } +#endif /* CHECKSUM_GEN_UDP */ + ip_proto = IP_PROTO_UDP; + } + + /* Determine TTL to use */ +#if LWIP_MULTICAST_TX_OPTIONS + ttl = (ip_addr_ismulticast(dst_ip) ? udp_get_multicast_ttl(pcb) : pcb->ttl); +#else /* LWIP_MULTICAST_TX_OPTIONS */ + ttl = pcb->ttl; +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto)); + /* output to IP */ + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* @todo: must this be increased even if error occurred? */ + MIB2_STATS_INC(mib2.udpoutdatagrams); + + /* did we chain a separate header pbuf earlier? */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + + UDP_STATS_INC(udp.xmit); + return err; +} + +/** + * @ingroup udp_raw + * Bind an UDP PCB. + * + * @param pcb UDP PCB to be bound with a local address ipaddr and port. + * @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to + * bind to all local interfaces. + * @param port local UDP port to bind with. Use 0 to automatically bind + * to a random port between UDP_LOCAL_PORT_RANGE_START and + * UDP_LOCAL_PORT_RANGE_END. + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occurred. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t +udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + u8_t rebind; + +#if LWIP_IPV4 + /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */ + if (ipaddr == NULL) { + ipaddr = IP4_ADDR_ANY; + } +#endif /* LWIP_IPV4 */ + + /* still need to check for ipaddr == NULL in IPv6 only case */ + if ((pcb == NULL) || (ipaddr == NULL)) { + return ERR_VAL; + } + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); + ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE, ipaddr); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); + +#if TLS_CONFIG_AP_OPT_FWD + if (alg_napt_port_is_used(port)) + { + return ERR_USE; + } +#endif + + rebind = 0; + /* Check for double bind and rebind of the same pcb */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + /* is this UDP PCB already on active list? */ + if (pcb == ipcb) { + rebind = 1; + break; + } + } + + /* no port specified? */ + if (port == 0) { + port = udp_new_port(); + if (port == 0) { + /* no more ports available in local range */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); + return ERR_USE; + } + } else { + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb != ipcb) { + /* By default, we don't allow to bind to a port that any other udp + PCB is already bound to, unless *all* PCBs with that port have tha + REUSEADDR flag set. */ +#if SO_REUSE + if (!ip_get_option(pcb, SOF_REUSEADDR) || + !ip_get_option(ipcb, SOF_REUSEADDR)) +#endif /* SO_REUSE */ + { + /* port matches that of PCB in list and REUSEADDR not set -> reject */ + if ((ipcb->local_port == port) && + /* IP address matches? */ + ip_addr_cmp(&ipcb->local_ip, ipaddr)) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } + } + } + } + } + + ip_addr_set_ipaddr(&pcb->local_ip, ipaddr); + + pcb->local_port = port; + mib2_udp_bind(pcb); + /* pcb not active yet? */ + if (rebind == 0) { + /* place the PCB on the active list if not already there */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to ")); + ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port)); + return ERR_OK; +} + +/* udp has broadcast frame and has two netif, use this interface to bind netif*/ +err_t +udp_bind_multicast_netif(struct udp_pcb *pcb, ip_addr_t *ipaddr) +{ + pcb->multicast_ip.addr = ipaddr->addr; + return ERR_OK; +} + + +/** + * @ingroup udp_raw + * Connect an UDP PCB. + * + * This will associate the UDP PCB with the remote address. + * + * @param pcb UDP PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * @param port remote UDP port to connect with. + * + * @return lwIP error code + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * The udp pcb is bound to a random local port if not already bound. + * + * @see udp_disconnect() + */ +err_t +udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + + if ((pcb == NULL) || (ipaddr == NULL)) { + return ERR_VAL; + } + + if (pcb->local_port == 0) { + err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) { + return err; + } + } + + ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to ")); + ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port)); + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb == ipcb) { + /* already on the list, just return */ + return ERR_OK; + } + } + /* PCB not yet on the list, add PCB now */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} + +/** + * @ingroup udp_raw + * Disconnect a UDP PCB + * + * @param pcb the udp pcb to disconnect. + */ +void +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ +#if LWIP_IPV4 && LWIP_IPV6 + if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { + ip_addr_copy(pcb->remote_ip, *IP_ANY_TYPE); + } else { +#endif + ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip); +#if LWIP_IPV4 && LWIP_IPV6 + } +#endif + pcb->remote_port = 0; + /* mark PCB as unconnected */ + pcb->flags &= ~UDP_FLAGS_CONNECTED; +} + +/** + * @ingroup udp_raw + * Set a receive callback for a UDP PCB + * + * This callback will be called when receiving a datagram for the pcb. + * + * @param pcb the pcb for which to set the recv callback + * @param recv function pointer of the callback function + * @param recv_arg additional argument to pass to the callback function + */ +void +udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * @ingroup udp_raw + * Remove an UDP PCB. + * + * @param pcb UDP PCB to be removed. The PCB is removed from the list of + * UDP PCB's and the data structure is freed from memory. + * + * @see udp_new() + */ +void +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + + mib2_udp_unbind(pcb); + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { + /* make list start at 2nd pcb */ + udp_pcbs = udp_pcbs->next; + /* pcb not 1st in list */ + } else { + for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + break; + } + } + } + memp_free(MEMP_UDP_PCB, pcb); +} + +/** + * @ingroup udp_raw + * Create a UDP PCB. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new(void) +{ + struct udp_pcb *pcb; + pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 + * which means checksum is generated over the whole datagram per default + * (recommended as default by RFC 3828). */ + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct udp_pcb)); + pcb->ttl = UDP_TTL; +#if LWIP_MULTICAST_TX_OPTIONS + udp_set_multicast_ttl(pcb, UDP_TTL); +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + } + return pcb; +} + +/** + * @ingroup udp_raw + * Create a UDP PCB for specific IP type. + * + * @param type IP address type, see @ref lwip_ip_addr_type definitions. + * If you want to listen to IPv4 and IPv6 (dual-stack) packets, + * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE. + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new_ip_type(u8_t type) +{ + struct udp_pcb *pcb; + pcb = udp_new(); +#if LWIP_IPV4 && LWIP_IPV6 + if (pcb != NULL) { + IP_SET_TYPE_VAL(pcb->local_ip, type); + IP_SET_TYPE_VAL(pcb->remote_ip, type); + } +#else + LWIP_UNUSED_ARG(type); +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + return pcb; +} + +/** This function is called from netif.c when address is changed + * + * @param old_addr IP address of the netif before change + * @param new_addr IP address of the netif after change + */ +void udp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr) +{ + struct udp_pcb* upcb; + + if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) { + for (upcb = udp_pcbs; upcb != NULL; upcb = upcb->next) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&upcb->local_ip, old_addr)) { + /* The PCB is bound to the old ipaddr and + * is set to bound to the new one instead */ + ip_addr_copy(upcb->local_ip, *new_addr); + } + } + } +} + +#if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ +void +udp_debug_print(struct udp_hdr *udphdr) +{ + LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + lwip_ntohs(udphdr->src), lwip_ntohs(udphdr->dest))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", + lwip_ntohs(udphdr->len), lwip_ntohs(udphdr->chksum))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* UDP_DEBUG */ + +#endif /* LWIP_UDP */ diff --git a/sdk/Src/Network/lwip2.0.3/include/arch/cc.h b/sdk/Src/Network/lwip2.0.3/include/arch/cc.h new file mode 100644 index 0000000..1ef404d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/arch/cc.h @@ -0,0 +1,78 @@ + +#ifndef CC_H_INCLUDED +#define CC_H_INCLUDED + +#include + +/* Define platform endianness */ +#define BYTE_ORDER LITTLE_ENDIAN +#include "wm_type_def.h" +#if 0 // remove by kevin 140612 +/* The unsigned data types */ +typedef unsigned char u8_t; +typedef unsigned short u16_t; +typedef unsigned int u32_t; + +/* The signed counterparts */ +typedef signed char s8_t; +typedef signed short s16_t; +typedef signed int s32_t; + +/* A generic pointer type */ +typedef u32_t mem_ptr_t; +#endif +/* Display name of types */ +#define U16_F "hu" +#define S16_F "hd" +#define X16_F "hx" +#define U32_F "u" +#define S32_F "d" +#define X32_F "x" + +/* Compiler hints for packing lwip's structures */ +#if defined(__CC_ARM) + /* Setup PACKing macros for MDK Tools */ +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((packed)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#elif defined (__ICCARM__) + /* Setup PACKing macros for EWARM Tools */ +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#elif defined (__GNUC__) + /* Setup PACKing macros for GCC Tools */ +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((packed)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#else +#error "This compiler does not support." +#endif + +/* define LWIP_COMPAT_MUTEX + to let sys.h use binary semaphores instead of mutexes - as before in 1.3.2 + Refer CHANGELOG +*/ +#define LWIP_COMPAT_MUTEX 1 + +#ifndef LWIP_PLATFORM_ASSERT +#define LWIP_PLATFORM_ASSERT(x) \ + do \ + { printf("Assertion \"%s\" failed at line %d in %s\n", x, __LINE__, __FILE__); \ + } while(0) +#else +#define LWIP_PLATFORM_ASSERT(x) +#endif + +#ifdef LWIP_PLATFORM_DIAG +#define LWIP_PLATFORM_DIAG(x) do { printf x;} while(0) +#else +#define LWIP_PLATFORM_DIAG(x) do { printf x;} while(0) +#endif + +// #define LWIP_PROVIDE_ERRNO /* Make lwip/arch.h define the codes which are used throughout */ + +#endif /* CC_H_INCLUDED */ diff --git a/sdk/Src/Network/lwip2.0.3/include/arch/perf.h b/sdk/Src/Network/lwip2.0.3/include/arch/perf.h new file mode 100644 index 0000000..b306037 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/arch/perf.h @@ -0,0 +1,8 @@ + +#ifndef PERF_H_INCLUDED +#define PERF_H_INCLUDED + +#define PERF_START /* NULL definition */ +#define PERF_STOP(x) /* NULL definition */ + +#endif /* PERF_H_INCLUDED */ diff --git a/sdk/Src/Network/lwip2.0.3/include/arch/sys_arch.h b/sdk/Src/Network/lwip2.0.3/include/arch/sys_arch.h new file mode 100644 index 0000000..7057de7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/arch/sys_arch.h @@ -0,0 +1,49 @@ + +#ifndef __ARCH_SYS_ARCH_H__ +#define __ARCH_SYS_ARCH_H__ + +#include "wm_osal.h" +#include "wm_config.h" + +#define LWIP_STK_SIZE 2048 +#define LWIP_TASK_MAX 1 +/* The user can change this priority level. + * It is important that there was no crossing with other levels. + */ +#define LWIP_TSK_PRIO TLS_LWIP_TASK_PRIO +#define LWIP_TASK_START_PRIO LWIP_TSK_PRIO +#define LWIP_TASK_END_PRIO LWIP_TSK_PRIO +LWIP_TASK_MAX + +/* the max size of each mailbox */ +#define MAX_QUEUE_ENTRIES 20 + +#define SYS_MBOX_NULL (void *)0 +#define SYS_SEM_NULL (void *)0 + +#define sys_arch_mbox_tryfetch(mbox,msg) \ + sys_arch_mbox_fetch(mbox,msg,1) + +typedef tls_os_sem_t * sys_sem_t; +typedef tls_os_queue_t * sys_mbox_t; +typedef u8_t sys_thread_t; + +typedef unsigned long int sys_prot_t; + +/* This optional function does a "fast" critical region protection and returns +the previous protection level. This function is only called during very short +critical regions. An embedded system which supports ISR-based drivers might want +to implement this function by disabling interrupts. Task-based systems might want +to implement this by using a mutex or disabling tasking. This function should +support recursive calls from the same task or interrupt. In other words, +sys_arch_protect() could be called while already protected. In that case the +return value indicates that it is already protected. */ +extern sys_prot_t sys_arch_protect(void); + +/* This optional function does a "fast" set of critical region protection to the +value specified by pval. See the documentation for sys_arch_protect() for more +information. This function is only required if your port is supporting an +operating system. */ +extern void sys_arch_unprotect(sys_prot_t pval); + +#endif /* __ARCH_SYS_ARCH_H__ */ + diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/alg.h b/sdk/Src/Network/lwip2.0.3/include/lwip/alg.h new file mode 100644 index 0000000..415040d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/alg.h @@ -0,0 +1,66 @@ +/****************************************************************************** + + Copyright (C) 2015 Winner Micro electronics Co., Ltd. + + ****************************************************************************** + File Name : alg.h + Version : Initial Draft + Author : Li Limin, lilm@winnermicro.com + Created : 2015/3/7 + Last Modified : + Description : Application layer gateway, (alg) only for apsta + + History : + Date : 2015/3/7 + Author : Li Limin, lilm@winnermicro.com + Modification : Created file + +******************************************************************************/ +#ifndef __ALG_H__ +#define __ALG_H__ + + +#ifdef __cplusplus +#if __cplusplus +extern "C"{ +#endif +#endif /* __cplusplus */ + + +/* ============================== configure ===================== */ +/* napt age time (second) */ +#define NAPT_TABLE_TIMEOUT 60 + +/* napt port range: 15000~25000 */ +#define NAPT_LOCAL_PORT_RANGE_START 0x3A98 +#define NAPT_LOCAL_PORT_RANGE_END 0x61A8 + +/* napt icmp id range: 3000-65535 */ +#define NAPT_ICMP_ID_RANGE_START 0xBB8 +#define NAPT_ICMP_ID_RANGE_END 0xFFFF + + +/* napt table size */ +//#define NAPT_TABLE_LIMIT +#ifdef NAPT_TABLE_LIMIT +#define NAPT_TABLE_SIZE_MAX 3000 +#endif +/* ============================================================ */ + + +#define NAPT_TMR_INTERVAL ((NAPT_TABLE_TIMEOUT / 2) * 1000UL) + +extern bool alg_napt_port_is_used(u16 port); + +extern int alg_napt_init(void); + +extern int alg_input(const u8 *bssid, u8 *pkt_body, u32 pkt_len); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* __ALG_H__ */ + diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/api.h b/sdk/Src/Network/lwip2.0.3/include/lwip/api.h new file mode 100644 index 0000000..4b658c6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/api.h @@ -0,0 +1,400 @@ +/** + * @file + * netconn API (to be used from non-TCPIP threads) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_API_H +#define LWIP_HDR_API_H + +#include "lwip/opt.h" + +#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ +/* Note: Netconn API is always available when sockets are enabled - + * sockets are implemented on top of them */ + +#include "lwip/arch.h" +#include "lwip/netbuf.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ + +/* Flags for netconn_write (u8_t) */ +#define NETCONN_NOFLAG 0x00 +#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */ +#define NETCONN_COPY 0x01 +#define NETCONN_MORE 0x02 +#define NETCONN_DONTBLOCK 0x04 + +/* Flags for struct netconn.flags (u8_t) */ +/** Should this netconn avoid blocking? */ +#define NETCONN_FLAG_NON_BLOCKING 0x02 +/** Was the last connect action a non-blocking one? */ +#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04 +/** If a nonblocking write has been rejected before, poll_tcp needs to + check if the netconn is writable again */ +#define NETCONN_FLAG_CHECK_WRITESPACE 0x10 +#if LWIP_IPV6 +/** If this flag is set then only IPv6 communication is allowed on the + netconn. As per RFC#3493 this features defaults to OFF allowing + dual-stack usage by default. */ +#define NETCONN_FLAG_IPV6_V6ONLY 0x20 +#endif /* LWIP_IPV6 */ + + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) ((t)&0xF0) +#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0) +#if LWIP_IPV6 +#define NETCONN_TYPE_IPV6 0x08 +#define NETCONNTYPE_ISIPV6(t) (((t)&NETCONN_TYPE_IPV6) != 0) +#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF3) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM) +#else /* LWIP_IPV6 */ +#define NETCONNTYPE_ISIPV6(t) (0) +#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM) +#endif /* LWIP_IPV6 */ + +/** @ingroup netconn_common + * Protocol family and type of the netconn + */ +enum netconn_type { + NETCONN_INVALID = 0, + /** TCP IPv4 */ + NETCONN_TCP = 0x10, +#if LWIP_IPV6 + /** TCP IPv6 */ + NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */, +#endif /* LWIP_IPV6 */ + /** UDP IPv4 */ + NETCONN_UDP = 0x20, + /** UDP IPv4 lite */ + NETCONN_UDPLITE = 0x21, + /** UDP IPv4 no checksum */ + NETCONN_UDPNOCHKSUM = 0x22, + +#if LWIP_IPV6 + /** UDP IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */ + NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */, + /** UDP IPv6 lite (dual-stack by default, unless you call @ref netconn_set_ipv6only) */ + NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */, + /** UDP IPv6 no checksum (dual-stack by default, unless you call @ref netconn_set_ipv6only) */ + NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */, +#endif /* LWIP_IPV6 */ + + /** Raw connection IPv4 */ + NETCONN_RAW = 0x40 +#if LWIP_IPV6 + /** Raw connection IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */ + , NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */ +#endif /* LWIP_IPV6 */ +}; + +/** Current state of the netconn. Non-TCP netconns are always + * in state NETCONN_NONE! */ +enum netconn_state { + NETCONN_NONE, + NETCONN_WRITE, + NETCONN_LISTEN, + NETCONN_CONNECT, + NETCONN_CLOSE +}; + +/** Used to inform the callback function about changes + * + * Event explanation: + * + * In the netconn implementation, there are three ways to block a client: + * + * - accept mbox (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0); in netconn_accept()) + * - receive mbox (sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); in netconn_recv_data()) + * - send queue is full (sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); in lwip_netconn_do_write()) + * + * The events have to be seen as events signaling the state of these mboxes/semaphores. For non-blocking + * connections, you need to know in advance whether a call to a netconn function call would block or not, + * and these events tell you about that. + * + * RCVPLUS events say: Safe to perform a potentially blocking call call once more. + * They are counted in sockets - three RCVPLUS events for accept mbox means you are safe + * to call netconn_accept 3 times without being blocked. + * Same thing for receive mbox. + * + * RCVMINUS events say: Your call to to a possibly blocking function is "acknowledged". + * Socket implementation decrements the counter. + * + * For TX, there is no need to count, its merely a flag. SENDPLUS means you may send something. + * SENDPLUS occurs when enough data was delivered to peer so netconn_send() can be called again. + * A SENDMINUS event occurs when the next call to a netconn_send() would be blocking. + */ +enum netconn_evt { + NETCONN_EVT_RCVPLUS, + NETCONN_EVT_RCVMINUS, + NETCONN_EVT_SENDPLUS, + NETCONN_EVT_SENDMINUS, + NETCONN_EVT_ERROR +}; + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** Used for netconn_join_leave_group() */ +enum netconn_igmp { + NETCONN_JOIN, + NETCONN_LEAVE +}; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +/* Used for netconn_gethostbyname_addrtype(), these should match the DNS_ADDRTYPE defines in dns.h */ +#define NETCONN_DNS_DEFAULT NETCONN_DNS_IPV4_IPV6 +#define NETCONN_DNS_IPV4 0 +#define NETCONN_DNS_IPV6 1 +#define NETCONN_DNS_IPV4_IPV6 2 /* try to resolve IPv4 first, try IPv6 if IPv4 fails only */ +#define NETCONN_DNS_IPV6_IPV4 3 /* try to resolve IPv6 first, try IPv4 if IPv6 fails only */ +#endif /* LWIP_DNS */ + +/* forward-declare some structs to avoid to include their headers */ +struct ip_pcb; +struct tcp_pcb; +struct udp_pcb; +struct raw_pcb; +struct netconn; +struct api_msg; + +/** A callback prototype to inform about events for a netconn */ +typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len); + +/** A netconn descriptor */ +struct netconn { + /** type of the netconn (TCP, UDP or RAW) */ + enum netconn_type type; + /** current state of the netconn */ + enum netconn_state state; + /** the lwIP internal protocol control block */ + union { + struct ip_pcb *ip; + struct tcp_pcb *tcp; + struct udp_pcb *udp; + struct raw_pcb *raw; + } pcb; + /** the last error this netconn had */ + err_t last_err; +#if !LWIP_NETCONN_SEM_PER_THREAD + /** sem that is used to synchronously execute functions in the core context */ + sys_sem_t op_completed; +#endif + /** mbox where received packets are stored until they are fetched + by the netconn application thread (can grow quite big) */ + sys_mbox_t recvmbox; +#if LWIP_TCP + /** mbox where new connections are stored until processed + by the application thread */ + sys_mbox_t acceptmbox; +#endif /* LWIP_TCP */ + /** only used for socket layer */ +#if LWIP_SOCKET + int socket; +#endif /* LWIP_SOCKET */ +#if LWIP_SO_SNDTIMEO + /** timeout to wait for sending data (which means enqueueing data for sending + in internal buffers) in milliseconds */ + s32_t send_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVTIMEO + /** timeout in milliseconds to wait for new data to be received + (or connections to arrive for listening netconns) */ + int recv_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + /** maximum amount of bytes queued in recvmbox + not used for TCP: adjust TCP_WND instead! */ + int recv_bufsize; + /** number of bytes currently in recvmbox to be received, + tested against recv_bufsize to limit bytes on recvmbox + for UDP and RAW, used for FIONREAD */ + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_SO_LINGER + /** values <0 mean linger is disabled, values > 0 are seconds to linger */ + s16_t linger; +#endif /* LWIP_SO_LINGER */ + /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ + u8_t flags; +#if LWIP_TCP + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores how much is already sent. */ + size_t write_offset; + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. + Also used during connect and close. */ + struct api_msg *current_msg; +#endif /* LWIP_TCP */ + /** A callback function that is informed about events for this netconn */ + netconn_callback callback; +}; + +/** Register an Network connection event */ +#define API_EVENT(c,e,l) if (c->callback) { \ + (*c->callback)(c, e, l); \ + } + +/** Set conn->last_err to err but don't overwrite fatal errors */ +#define NETCONN_SET_SAFE_ERR(conn, err) do { if ((conn) != NULL) { \ + SYS_ARCH_DECL_PROTECT(netconn_set_safe_err_lev); \ + SYS_ARCH_PROTECT(netconn_set_safe_err_lev); \ + if (!ERR_IS_FATAL((conn)->last_err)) { \ + (conn)->last_err = err; \ + } \ + SYS_ARCH_UNPROTECT(netconn_set_safe_err_lev); \ +}} while(0); + +/* Network connection functions: */ + +/** @ingroup netconn_common + * Create new netconn connection + * @param t @ref netconn_type */ +#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL) +#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c) +struct netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, + netconn_callback callback); +err_t netconn_delete(struct netconn *conn); +/** Get the type of a netconn (as enum netconn_type). */ +#define netconn_type(conn) (conn->type) + +err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr, + u16_t *port, u8_t local); +/** @ingroup netconn_common */ +#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0) +/** @ingroup netconn_common */ +#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1) + +err_t netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port); +err_t netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port); +err_t netconn_disconnect (struct netconn *conn); +err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog); +/** @ingroup netconn_tcp */ +#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG) +err_t netconn_accept(struct netconn *conn, struct netconn **new_conn); +err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf); +err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf); +err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, + const ip_addr_t *addr, u16_t port); +err_t netconn_send(struct netconn *conn, struct netbuf *buf); +err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written); +/** @ingroup netconn_tcp */ +#define netconn_write(conn, dataptr, size, apiflags) \ + netconn_write_partly(conn, dataptr, size, apiflags, NULL) +err_t netconn_close(struct netconn *conn); +err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +err_t netconn_join_leave_group(struct netconn *conn, const ip_addr_t *multiaddr, + const ip_addr_t *netif_addr, enum netconn_igmp join_or_leave); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if LWIP_DNS +#if LWIP_IPV4 && LWIP_IPV6 +err_t netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype); +#define netconn_gethostbyname(name, addr) netconn_gethostbyname_addrtype(name, addr, NETCONN_DNS_DEFAULT) +#else /* LWIP_IPV4 && LWIP_IPV6 */ +err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); +#define netconn_gethostbyname_addrtype(name, addr, dns_addrtype) netconn_gethostbyname(name, addr) +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +#endif /* LWIP_DNS */ + +#define netconn_err(conn) ((conn)->last_err) +#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) + +/** Set the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_set_nonblocking(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0) +/** Get the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0) + +#if LWIP_IPV6 +/** @ingroup netconn_common + * TCP: Set the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY) + */ +#define netconn_set_ipv6only(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_IPV6_V6ONLY; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_IPV6_V6ONLY; }} while(0) +/** @ingroup netconn_common + * TCP: Get the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY) + */ +#define netconn_get_ipv6only(conn) (((conn)->flags & NETCONN_FLAG_IPV6_V6ONLY) != 0) +#endif /* LWIP_IPV6 */ + +#if LWIP_SO_SNDTIMEO +/** Set the send timeout in milliseconds */ +#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout)) +/** Get the send timeout in milliseconds */ +#define netconn_get_sendtimeout(conn) ((conn)->send_timeout) +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO +/** Set the receive timeout in milliseconds */ +#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout)) +/** Get the receive timeout in milliseconds */ +#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout) +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF +/** Set the receive buffer in bytes */ +#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize)) +/** Get the receive buffer in bytes */ +#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize) +#endif /* LWIP_SO_RCVBUF*/ + +#if LWIP_NETCONN_SEM_PER_THREAD +void netconn_thread_init(void); +void netconn_thread_cleanup(void); +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define netconn_thread_init() +#define netconn_thread_cleanup() +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#endif /* LWIP_HDR_API_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/FILES b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/FILES new file mode 100644 index 0000000..c983076 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/FILES @@ -0,0 +1,2 @@ +This directory contains application headers. +Every application shall provide one api file APP.h and optionally one options file APP_opts.h diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/fs.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/fs.h new file mode 100644 index 0000000..0283a0b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/fs.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2001-2003 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_APPS_FS_H +#define LWIP_HDR_APPS_FS_H + +#include "httpd_opts.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FS_READ_EOF -1 +#define FS_READ_DELAYED -2 + +#if HTTPD_PRECALCULATED_CHECKSUM +struct fsdata_chksum { + u32_t offset; + u16_t chksum; + u16_t len; +}; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ + +#define FS_FILE_FLAGS_HEADER_INCLUDED 0x01 +#define FS_FILE_FLAGS_HEADER_PERSISTENT 0x02 + +struct fs_file { + const char *data; + int len; + int index; + void *pextension; +#if HTTPD_PRECALCULATED_CHECKSUM + const struct fsdata_chksum *chksum; + u16_t chksum_count; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ + u8_t flags; +#if LWIP_HTTPD_CUSTOM_FILES + u8_t is_custom_file; +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#if LWIP_HTTPD_FILE_STATE + void *state; +#endif /* LWIP_HTTPD_FILE_STATE */ +}; + +#if LWIP_HTTPD_FS_ASYNC_READ +typedef void (*fs_wait_cb)(void *arg); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +err_t fs_open(struct fs_file *file, const char *name); +void fs_close(struct fs_file *file); +#if LWIP_HTTPD_DYNAMIC_FILE_READ +#if LWIP_HTTPD_FS_ASYNC_READ +int fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int fs_read(struct fs_file *file, char *buffer, int count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +#if LWIP_HTTPD_FS_ASYNC_READ +int fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +int fs_bytes_left(struct fs_file *file); + +#if LWIP_HTTPD_FILE_STATE +/** This user-defined function is called when a file is opened. */ +void *fs_state_init(struct fs_file *file, const char *name); +/** This user-defined function is called when a file is closed. */ +void fs_state_free(struct fs_file *file, void *state); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_FS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/httpd.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/httpd.h new file mode 100644 index 0000000..57831dd --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/httpd.h @@ -0,0 +1,236 @@ +/** + * @file + * HTTP server + */ + +/* + * Copyright (c) 2001-2003 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * This version of the file has been modified by Texas Instruments to offer + * simple server-side-include (SSI) and Common Gateway Interface (CGI) + * capability. + */ + +#ifndef LWIP_HDR_APPS_HTTPD_H +#define LWIP_HDR_APPS_HTTPD_H + +#include "httpd_opts.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_HTTPD_CGI + +/* + * Function pointer for a CGI script handler. + * + * This function is called each time the HTTPD server is asked for a file + * whose name was previously registered as a CGI function using a call to + * http_set_cgi_handler. The iIndex parameter provides the index of the + * CGI within the ppcURLs array passed to http_set_cgi_handler. Parameters + * pcParam and pcValue provide access to the parameters provided along with + * the URI. iNumParams provides a count of the entries in the pcParam and + * pcValue arrays. Each entry in the pcParam array contains the name of a + * parameter with the corresponding entry in the pcValue array containing the + * value for that parameter. Note that pcParam may contain multiple elements + * with the same name if, for example, a multi-selection list control is used + * in the form generating the data. + * + * The function should return a pointer to a character string which is the + * path and filename of the response that is to be sent to the connected + * browser, for example "/thanks.htm" or "/response/error.ssi". + * + * The maximum number of parameters that will be passed to this function via + * iNumParams is defined by LWIP_HTTPD_MAX_CGI_PARAMETERS. Any parameters in the incoming + * HTTP request above this number will be discarded. + * + * Requests intended for use by this CGI mechanism must be sent using the GET + * method (which encodes all parameters within the URI rather than in a block + * later in the request). Attempts to use the POST method will result in the + * request being ignored. + * + */ +typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[], + char *pcValue[]); + +/* + * Structure defining the base filename (URL) of a CGI and the associated + * function which is to be called when that URL is requested. + */ +typedef struct +{ + const char *pcCGIName; + tCGIHandler pfnCGIHandler; +} tCGI; + +void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers); + +#endif /* LWIP_HTTPD_CGI */ + +#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI + +#if LWIP_HTTPD_CGI_SSI +/** Define this generic CGI handler in your application. + * It is called once for every URI with parameters. + * The parameters can be stored to + */ +extern void httpd_cgi_handler(const char* uri, int iNumParams, char **pcParam, char **pcValue +#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE + , void *connection_state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); +#endif /* LWIP_HTTPD_CGI_SSI */ + +#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */ + +#if LWIP_HTTPD_SSI + +/* + * Function pointer for the SSI tag handler callback. + * + * This function will be called each time the HTTPD server detects a tag of the + * form in a .shtml, .ssi or .shtm file where "name" appears as + * one of the tags supplied to http_set_ssi_handler in the ppcTags array. The + * returned insert string, which will be appended after the the string + * "" in file sent back to the client,should be written to pointer + * pcInsert. iInsertLen contains the size of the buffer pointed to by + * pcInsert. The iIndex parameter provides the zero-based index of the tag as + * found in the ppcTags array and identifies the tag that is to be processed. + * + * The handler returns the number of characters written to pcInsert excluding + * any terminating NULL or a negative number to indicate a failure (tag not + * recognized, for example). + * + * Note that the behavior of this SSI mechanism is somewhat different from the + * "normal" SSI processing as found in, for example, the Apache web server. In + * this case, the inserted text is appended following the SSI tag rather than + * replacing the tag entirely. This allows for an implementation that does not + * require significant additional buffering of output data yet which will still + * offer usable SSI functionality. One downside to this approach is when + * attempting to use SSI within JavaScript. The SSI tag is structured to + * resemble an HTML comment but this syntax does not constitute a comment + * within JavaScript and, hence, leaving the tag in place will result in + * problems in these cases. To work around this, any SSI tag which needs to + * output JavaScript code must do so in an encapsulated way, sending the whole + * HTML section as a single include. + */ +typedef u16_t (*tSSIHandler)( +#if LWIP_HTTPD_SSI_RAW + const char* ssi_tag_name, +#else /* LWIP_HTTPD_SSI_RAW */ + int iIndex, +#endif /* LWIP_HTTPD_SSI_RAW */ + char *pcInsert, int iInsertLen +#if LWIP_HTTPD_SSI_MULTIPART + , u16_t current_tag_part, u16_t *next_tag_part +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE + , void *connection_state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); + +/** Set the SSI handler function + * (if LWIP_HTTPD_SSI_RAW==1, only the first argument is used) + */ +void http_set_ssi_handler(tSSIHandler pfnSSIHandler, + const char **ppcTags, int iNumTags); + +/** For LWIP_HTTPD_SSI_RAW==1, return this to indicate the tag is unknown. + * In this case, the webserver writes a warning into the page. + * You can also just return 0 to write nothing for unknown tags. + */ +#define HTTPD_SSI_TAG_UNKNOWN 0xFFFF + +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_SUPPORT_POST + +/* These functions must be implemented by the application */ + +/** Called when a POST request has been received. The application can decide + * whether to accept it or not. + * + * @param connection Unique connection identifier, valid until httpd_post_end + * is called. + * @param uri The HTTP header URI receiving the POST request. + * @param http_request The raw HTTP request (the first packet, normally). + * @param http_request_len Size of 'http_request'. + * @param content_len Content-Length from HTTP header. + * @param response_uri Filename of response file, to be filled when denying the + * request + * @param response_uri_len Size of the 'response_uri' buffer. + * @param post_auto_wnd Set this to 0 to let the callback code handle window + * updates by calling 'httpd_post_data_recved' (to throttle rx speed) + * default is 1 (httpd handles window updates automatically) + * @return ERR_OK: Accept the POST request, data may be passed in + * another err_t: Deny the POST request, send back 'bad request'. + */ +err_t httpd_post_begin(void *connection, const char *uri, const char *http_request, + u16_t http_request_len, int content_len, char *response_uri, + u16_t response_uri_len, u8_t *post_auto_wnd); + +/** Called for each pbuf of data that has been received for a POST. + * ATTENTION: The application is responsible for freeing the pbufs passed in! + * + * @param connection Unique connection identifier. + * @param p Received data. + * @return ERR_OK: Data accepted. + * another err_t: Data denied, http_post_get_response_uri will be called. + */ +err_t httpd_post_receive_data(void *connection, struct pbuf *p); + +/** Called when all data is received or when the connection is closed. + * The application must return the filename/URI of a file to send in response + * to this POST request. If the response_uri buffer is untouched, a 404 + * response is returned. + * + * @param connection Unique connection identifier. + * @param response_uri Filename of response file, to be filled when denying the request + * @param response_uri_len Size of the 'response_uri' buffer. + */ +void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len); + +#if LWIP_HTTPD_POST_MANUAL_WND +void httpd_post_data_recved(void *connection, u16_t recved_len); +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + +#endif /* LWIP_HTTPD_SUPPORT_POST */ + +void httpd_init(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HTTPD_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/httpd_opts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/httpd_opts.h new file mode 100644 index 0000000..79ba474 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/httpd_opts.h @@ -0,0 +1,323 @@ +/** + * @file + * HTTP server options list + */ + +/* + * Copyright (c) 2001-2003 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * This version of the file has been modified by Texas Instruments to offer + * simple server-side-include (SSI) and Common Gateway Interface (CGI) + * capability. + */ + +#ifndef LWIP_HDR_APPS_HTTPD_OPTS_H +#define LWIP_HDR_APPS_HTTPD_OPTS_H + +#include "lwip/opt.h" + +/** + * @defgroup httpd_opts Options + * @ingroup httpd + * @{ + */ + +/** Set this to 1 to support CGI (old style) */ +#if !defined LWIP_HTTPD_CGI || defined __DOXYGEN__ +#define LWIP_HTTPD_CGI 1 +#endif + +/** Set this to 1 to support CGI (new style) */ +#if !defined LWIP_HTTPD_CGI_SSI || defined __DOXYGEN__ +#define LWIP_HTTPD_CGI_SSI 0 +#endif + +/** Set this to 1 to support SSI (Server-Side-Includes) */ +#if !defined LWIP_HTTPD_SSI || defined __DOXYGEN__ +#define LWIP_HTTPD_SSI 0 +#endif + +/** Set this to 1 to implement an SSI tag handler callback that gets a const char* + * to the tag (instead of an index into a pre-registered array of known tags) */ +#if !defined LWIP_HTTPD_SSI_RAW || defined __DOXYGEN__ +#define LWIP_HTTPD_SSI_RAW 0 +#endif + +/** Set this to 1 to support HTTP POST */ +#if !defined LWIP_HTTPD_SUPPORT_POST || defined __DOXYGEN__ +#define LWIP_HTTPD_SUPPORT_POST 1 +#endif + +/* The maximum number of parameters that the CGI handler can be sent. */ +#if !defined LWIP_HTTPD_MAX_CGI_PARAMETERS || defined __DOXYGEN__ +#define LWIP_HTTPD_MAX_CGI_PARAMETERS 16 +#endif + +/** LWIP_HTTPD_SSI_MULTIPART==1: SSI handler function is called with 2 more + * arguments indicating a counter for insert string that are too long to be + * inserted at once: the SSI handler function must then set 'next_tag_part' + * which will be passed back to it in the next call. */ +#if !defined LWIP_HTTPD_SSI_MULTIPART || defined __DOXYGEN__ +#define LWIP_HTTPD_SSI_MULTIPART 0 +#endif + +/* The maximum length of the string comprising the tag name */ +#if !defined LWIP_HTTPD_MAX_TAG_NAME_LEN || defined __DOXYGEN__ +#define LWIP_HTTPD_MAX_TAG_NAME_LEN 8 +#endif + +/* The maximum length of string that can be returned to replace any given tag */ +#if !defined LWIP_HTTPD_MAX_TAG_INSERT_LEN || defined __DOXYGEN__ +#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 192 +#endif + +#if !defined LWIP_HTTPD_POST_MANUAL_WND || defined __DOXYGEN__ +#define LWIP_HTTPD_POST_MANUAL_WND 0 +#endif + +/** This string is passed in the HTTP header as "Server: " */ +#if !defined HTTPD_SERVER_AGENT || defined __DOXYGEN__ +#define HTTPD_SERVER_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)" +#endif + +/** Set this to 1 if you want to include code that creates HTTP headers + * at runtime. Default is off: HTTP headers are then created statically + * by the makefsdata tool. Static headers mean smaller code size, but + * the (readonly) fsdata will grow a bit as every file includes the HTTP + * header. */ +#if !defined LWIP_HTTPD_DYNAMIC_HEADERS || defined __DOXYGEN__ +#define LWIP_HTTPD_DYNAMIC_HEADERS 0 +#endif + +#if !defined HTTPD_DEBUG || defined __DOXYGEN__ +#define HTTPD_DEBUG LWIP_DBG_ON +#endif + +/** Set this to 1 to use a memp pool for allocating + * struct http_state instead of the heap. + */ +#if !defined HTTPD_USE_MEM_POOL || defined __DOXYGEN__ +#define HTTPD_USE_MEM_POOL 0 +#endif + +/** The server port for HTTPD to use */ +#if !defined HTTPD_SERVER_PORT || defined __DOXYGEN__ +#define HTTPD_SERVER_PORT 80 +#endif + +/** Maximum retries before the connection is aborted/closed. + * - number of times pcb->poll is called -> default is 4*500ms = 2s; + * - reset when pcb->sent is called + */ +#if !defined HTTPD_MAX_RETRIES || defined __DOXYGEN__ +#define HTTPD_MAX_RETRIES 4 +#endif + +/** The poll delay is X*500ms */ +#if !defined HTTPD_POLL_INTERVAL || defined __DOXYGEN__ +#define HTTPD_POLL_INTERVAL 4 +#endif + +/** Priority for tcp pcbs created by HTTPD (very low by default). + * Lower priorities get killed first when running out of memory. + */ +#if !defined HTTPD_TCP_PRIO || defined __DOXYGEN__ +#define HTTPD_TCP_PRIO TCP_PRIO_MIN +#endif + +/** Set this to 1 to enable timing each file sent */ +#if !defined LWIP_HTTPD_TIMING || defined __DOXYGEN__ +#define LWIP_HTTPD_TIMING 0 +#endif +/** Set this to 1 to enable timing each file sent */ +#if !defined HTTPD_DEBUG_TIMING || defined __DOXYGEN__ +#define HTTPD_DEBUG_TIMING LWIP_DBG_OFF +#endif + +/** Set this to one to show error pages when parsing a request fails instead + of simply closing the connection. */ +#if !defined LWIP_HTTPD_SUPPORT_EXTSTATUS || defined __DOXYGEN__ +#define LWIP_HTTPD_SUPPORT_EXTSTATUS 0 +#endif + +/** Set this to 0 to drop support for HTTP/0.9 clients (to save some bytes) */ +#if !defined LWIP_HTTPD_SUPPORT_V09 || defined __DOXYGEN__ +#define LWIP_HTTPD_SUPPORT_V09 1 +#endif + +/** Set this to 1 to enable HTTP/1.1 persistent connections. + * ATTENTION: If the generated file system includes HTTP headers, these must + * include the "Connection: keep-alive" header (pass argument "-11" to makefsdata). + */ +#if !defined LWIP_HTTPD_SUPPORT_11_KEEPALIVE || defined __DOXYGEN__ +#define LWIP_HTTPD_SUPPORT_11_KEEPALIVE 0 +#endif + +/** Set this to 1 to support HTTP request coming in in multiple packets/pbufs */ +#if !defined LWIP_HTTPD_SUPPORT_REQUESTLIST || defined __DOXYGEN__ +#define LWIP_HTTPD_SUPPORT_REQUESTLIST 1 +#endif + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST +/** Number of rx pbufs to enqueue to parse an incoming request (up to the first + newline) */ +#if !defined LWIP_HTTPD_REQ_QUEUELEN || defined __DOXYGEN__ +#define LWIP_HTTPD_REQ_QUEUELEN 5 +#endif + +/** Number of (TCP payload-) bytes (in pbufs) to enqueue to parse and incoming + request (up to the first double-newline) */ +#if !defined LWIP_HTTPD_REQ_BUFSIZE || defined __DOXYGEN__ +#define LWIP_HTTPD_REQ_BUFSIZE LWIP_HTTPD_MAX_REQ_LENGTH +#endif + +/** Defines the maximum length of a HTTP request line (up to the first CRLF, + copied from pbuf into this a global buffer when pbuf- or packet-queues + are received - otherwise the input pbuf is used directly) */ +#if !defined LWIP_HTTPD_MAX_REQ_LENGTH || defined __DOXYGEN__ +#define LWIP_HTTPD_MAX_REQ_LENGTH LWIP_MIN(1023, (LWIP_HTTPD_REQ_QUEUELEN * PBUF_POOL_BUFSIZE)) +#endif +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +/** This is the size of a static buffer used when URIs end with '/'. + * In this buffer, the directory requested is concatenated with all the + * configured default file names. + * Set to 0 to disable checking default filenames on non-root directories. + */ +#if !defined LWIP_HTTPD_MAX_REQUEST_URI_LEN || defined __DOXYGEN__ +#define LWIP_HTTPD_MAX_REQUEST_URI_LEN 63 +#endif + +/** Maximum length of the filename to send as response to a POST request, + * filled in by the application when a POST is finished. + */ +#if !defined LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN || defined __DOXYGEN__ +#define LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN 63 +#endif + +/** Set this to 0 to not send the SSI tag (default is on, so the tag will + * be sent in the HTML page */ +#if !defined LWIP_HTTPD_SSI_INCLUDE_TAG || defined __DOXYGEN__ +#define LWIP_HTTPD_SSI_INCLUDE_TAG 1 +#endif + +/** Set this to 1 to call tcp_abort when tcp_close fails with memory error. + * This can be used to prevent consuming all memory in situations where the + * HTTP server has low priority compared to other communication. */ +#if !defined LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR || defined __DOXYGEN__ +#define LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR 0 +#endif + +/** Set this to 1 to kill the oldest connection when running out of + * memory for 'struct http_state' or 'struct http_ssi_state'. + * ATTENTION: This puts all connections on a linked list, so may be kind of slow. + */ +#if !defined LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED || defined __DOXYGEN__ +#define LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 0 +#endif + +/** Set this to 1 to send URIs without extension without headers + * (who uses this at all??) */ +#if !defined LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI || defined __DOXYGEN__ +#define LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI 0 +#endif + +/** Default: Tags are sent from struct http_state and are therefore volatile */ +#if !defined HTTP_IS_TAG_VOLATILE || defined __DOXYGEN__ +#define HTTP_IS_TAG_VOLATILE(ptr) TCP_WRITE_FLAG_COPY +#endif + +/* By default, the httpd is limited to send 2*pcb->mss to keep resource usage low + when http is not an important protocol in the device. */ +#if !defined HTTPD_LIMIT_SENDING_TO_2MSS || defined __DOXYGEN__ +#define HTTPD_LIMIT_SENDING_TO_2MSS 1 +#endif + +/* Define this to a function that returns the maximum amount of data to enqueue. + The function have this signature: u16_t fn(struct tcp_pcb* pcb); */ +#if !defined HTTPD_MAX_WRITE_LEN || defined __DOXYGEN__ +#if HTTPD_LIMIT_SENDING_TO_2MSS +#define HTTPD_MAX_WRITE_LEN(pcb) (2 * tcp_mss(pcb)) +#endif +#endif + +/*------------------- FS OPTIONS -------------------*/ + +/** Set this to 1 and provide the functions: + * - "int fs_open_custom(struct fs_file *file, const char *name)" + * Called first for every opened file to allow opening files + * that are not included in fsdata(_custom).c + * - "void fs_close_custom(struct fs_file *file)" + * Called to free resources allocated by fs_open_custom(). + */ +#if !defined LWIP_HTTPD_CUSTOM_FILES || defined __DOXYGEN__ +#define LWIP_HTTPD_CUSTOM_FILES 0 +#endif + +/** Set this to 1 to support fs_read() to dynamically read file data. + * Without this (default=off), only one-block files are supported, + * and the contents must be ready after fs_open(). + */ +#if !defined LWIP_HTTPD_DYNAMIC_FILE_READ || defined __DOXYGEN__ +#define LWIP_HTTPD_DYNAMIC_FILE_READ 0 +#endif + +/** Set this to 1 to include an application state argument per file + * that is opened. This allows to keep a state per connection/file. + */ +#if !defined LWIP_HTTPD_FILE_STATE || defined __DOXYGEN__ +#define LWIP_HTTPD_FILE_STATE 0 +#endif + +/** HTTPD_PRECALCULATED_CHECKSUM==1: include precompiled checksums for + * predefined (MSS-sized) chunks of the files to prevent having to calculate + * the checksums at runtime. */ +#if !defined HTTPD_PRECALCULATED_CHECKSUM || defined __DOXYGEN__ +#define HTTPD_PRECALCULATED_CHECKSUM 0 +#endif + +/** LWIP_HTTPD_FS_ASYNC_READ==1: support asynchronous read operations + * (fs_read_async returns FS_READ_DELAYED and calls a callback when finished). + */ +#if !defined LWIP_HTTPD_FS_ASYNC_READ || defined __DOXYGEN__ +#define LWIP_HTTPD_FS_ASYNC_READ 0 +#endif + +/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the + * file system (to prevent changing the file included in CVS) */ +#if !defined HTTPD_USE_CUSTOM_FSDATA || defined __DOXYGEN__ +#define HTTPD_USE_CUSTOM_FSDATA 0 +#endif + +/** + * @} + */ + +#endif /* LWIP_HDR_APPS_HTTPD_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/lwiperf.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/lwiperf.h new file mode 100644 index 0000000..7dbebb0 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/lwiperf.h @@ -0,0 +1,84 @@ +/** + * @file + * lwIP iPerf server implementation + */ + +/* + * Copyright (c) 2014 Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef LWIP_HDR_APPS_LWIPERF_H +#define LWIP_HDR_APPS_LWIPERF_H + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LWIPERF_TCP_PORT_DEFAULT 5001 + +/** lwIPerf test results */ +enum lwiperf_report_type +{ + /** The server side test is done */ + LWIPERF_TCP_DONE_SERVER, + /** The client side test is done */ + LWIPERF_TCP_DONE_CLIENT, + /** Local error lead to test abort */ + LWIPERF_TCP_ABORTED_LOCAL, + /** Data check error lead to test abort */ + LWIPERF_TCP_ABORTED_LOCAL_DATAERROR, + /** Transmit error lead to test abort */ + LWIPERF_TCP_ABORTED_LOCAL_TXERROR, + /** Remote side aborted the test */ + LWIPERF_TCP_ABORTED_REMOTE +}; + +/** Prototype of a report function that is called when a session is finished. + This report function can show the test results. + @param report_type contains the test result */ +typedef void (*lwiperf_report_fn)(void *arg, enum lwiperf_report_type report_type, + const ip_addr_t* local_addr, u16_t local_port, const ip_addr_t* remote_addr, u16_t remote_port, + u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec); + + +void* lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port, + lwiperf_report_fn report_fn, void* report_arg); +void* lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg); +void lwiperf_abort(void* lwiperf_session); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_LWIPERF_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns.h new file mode 100644 index 0000000..d036816 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns.h @@ -0,0 +1,69 @@ +/** + * @file + * MDNS responder + */ + + /* + * Copyright (c) 2015 Verisure Innovation 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Erik Ekman + * + */ +#ifndef LWIP_HDR_MDNS_H +#define LWIP_HDR_MDNS_H + +#include "lwip/apps/mdns_opts.h" +#include "lwip/netif.h" + +#if LWIP_MDNS_RESPONDER + +enum mdns_sd_proto { + DNSSD_PROTO_UDP = 0, + DNSSD_PROTO_TCP = 1 +}; + +#define MDNS_LABEL_MAXLEN 63 + +struct mdns_host; +struct mdns_service; + +/** Callback function to add text to a reply, called when generating the reply */ +typedef void (*service_get_txt_fn_t)(struct mdns_service *service, void *txt_userdata); + +void mdns_resp_init(void); + +err_t mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl); +err_t mdns_resp_remove_netif(struct netif *netif); + +err_t mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_userdata); +err_t mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len); +void mdns_resp_netif_settings_changed(struct netif *netif); + +#endif /* LWIP_MDNS_RESPONDER */ + +#endif /* LWIP_HDR_MDNS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns_opts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns_opts.h new file mode 100644 index 0000000..d8058e4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns_opts.h @@ -0,0 +1,74 @@ +/** + * @file + * MDNS responder + */ + + /* + * Copyright (c) 2015 Verisure Innovation 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Erik Ekman + * + */ + +#ifndef LWIP_HDR_APPS_MDNS_OPTS_H +#define LWIP_HDR_APPS_MDNS_OPTS_H + +#include "lwip/opt.h" + +/** + * @defgroup mdns_opts Options + * @ingroup mdns + * @{ + */ + +/** + * LWIP_MDNS_RESPONDER==1: Turn on multicast DNS module. UDP must be available for MDNS + * transport. IGMP is needed for IPv4 multicast. + */ +#ifndef LWIP_MDNS_RESPONDER +#define LWIP_MDNS_RESPONDER 1 +#endif /* LWIP_MDNS_RESPONDER */ + +/** The maximum number of services per netif */ +#ifndef MDNS_MAX_SERVICES +#define MDNS_MAX_SERVICES 1 +#endif + +/** + * MDNS_DEBUG: Enable debugging for multicast DNS. + */ +#ifndef MDNS_DEBUG +#define MDNS_DEBUG LWIP_DBG_OFF +#endif + +/** + * @} + */ + +#endif /* LWIP_HDR_APPS_MDNS_OPTS_H */ + diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns_priv.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns_priv.h new file mode 100644 index 0000000..c29e3dc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mdns_priv.h @@ -0,0 +1,66 @@ +/** + * @file + * MDNS responder private definitions + */ + + /* + * Copyright (c) 2015 Verisure Innovation 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Erik Ekman + * + */ +#ifndef LWIP_HDR_MDNS_PRIV_H +#define LWIP_HDR_MDNS_PRIV_H + +#include "lwip/apps/mdns_opts.h" +#include "lwip/pbuf.h" + +#if LWIP_MDNS_RESPONDER + +/* Domain struct and methods - visible for unit tests */ + +#define MDNS_DOMAIN_MAXLEN 512 +#define MDNS_READNAME_ERROR 0xFFFF + +struct mdns_domain { + /* Encoded domain name */ + u8_t name[MDNS_DOMAIN_MAXLEN]; + /* Total length of domain name, including zero */ + u16_t length; + /* Set if compression of this domain is not allowed */ + u8_t skip_compression; +}; + +err_t mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len); +u16_t mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain); +int mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b); +u16_t mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain); + +#endif /* LWIP_MDNS_RESPONDER */ + +#endif /* LWIP_HDR_MDNS_PRIV_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mqtt.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mqtt.h new file mode 100644 index 0000000..34b230b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mqtt.h @@ -0,0 +1,244 @@ +/** + * @file + * MQTT client + */ + +/* + * Copyright (c) 2016 Erik Andersson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Erik Andersson + * + */ +#ifndef LWIP_HDR_APPS_MQTT_CLIENT_H +#define LWIP_HDR_APPS_MQTT_CLIENT_H + +#include "lwip/apps/mqtt_opts.h" +#include "lwip/err.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct mqtt_client_t mqtt_client_t; + +/** @ingroup mqtt + * Default MQTT port */ +#define MQTT_PORT 1883 + +/*---------------------------------------------------------------------------------------------- */ +/* Connection with server */ + +/** + * @ingroup mqtt + * Client information and connection parameters */ +struct mqtt_connect_client_info_t { + /** Client identifier, must be set by caller */ + const char *client_id; + /** User name and password, set to NULL if not used */ + const char* client_user; + const char* client_pass; + /** keep alive time in seconds, 0 to disable keep alive functionality*/ + u16_t keep_alive; + /** will topic, set to NULL if will is not to be used, + will_msg, will_qos and will retain are then ignored */ + const char* will_topic; + const char* will_msg; + u8_t will_qos; + u8_t will_retain; +}; + +/** + * @ingroup mqtt + * Connection status codes */ +typedef enum +{ + MQTT_CONNECT_ACCEPTED = 0, + MQTT_CONNECT_REFUSED_PROTOCOL_VERSION = 1, + MQTT_CONNECT_REFUSED_IDENTIFIER = 2, + MQTT_CONNECT_REFUSED_SERVER = 3, + MQTT_CONNECT_REFUSED_USERNAME_PASS = 4, + MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_ = 5, + MQTT_CONNECT_DISCONNECTED = 256, + MQTT_CONNECT_TIMEOUT = 257 +} mqtt_connection_status_t; + +/** + * @ingroup mqtt + * Function prototype for mqtt connection status callback. Called when + * client has connected to the server after initiating a mqtt connection attempt by + * calling mqtt_connect() or when connection is closed by server or an error + * + * @param client MQTT client itself + * @param arg Additional argument to pass to the callback function + * @param status Connect result code or disconnection notification @see mqtt_connection_status_t + * + */ +typedef void (*mqtt_connection_cb_t)(mqtt_client_t *client, void *arg, mqtt_connection_status_t status); + + +/** + * @ingroup mqtt + * Data callback flags */ +enum { + /** Flag set when last fragment of data arrives in data callback */ + MQTT_DATA_FLAG_LAST = 1 +}; + +/** + * @ingroup mqtt + * Function prototype for MQTT incoming publish data callback function. Called when data + * arrives to a subscribed topic @see mqtt_subscribe + * + * @param arg Additional argument to pass to the callback function + * @param data User data, pointed object, data may not be referenced after callback return, + NULL is passed when all publish data are delivered + * @param len Length of publish data fragment + * @param flags MQTT_DATA_FLAG_LAST set when this call contains the last part of data from publish message + * + */ +typedef void (*mqtt_incoming_data_cb_t)(void *arg, const u8_t *data, u16_t len, u8_t flags); + + +/** + * @ingroup mqtt + * Function prototype for MQTT incoming publish function. Called when an incoming publish + * arrives to a subscribed topic @see mqtt_subscribe + * + * @param arg Additional argument to pass to the callback function + * @param topic Zero terminated Topic text string, topic may not be referenced after callback return + * @param tot_len Total length of publish data, if set to 0 (no publish payload) data callback will not be invoked + */ +typedef void (*mqtt_incoming_publish_cb_t)(void *arg, const char *topic, u32_t tot_len); + + +/** + * @ingroup mqtt + * Function prototype for mqtt request callback. Called when a subscribe, unsubscribe + * or publish request has completed + * @param arg Pointer to user data supplied when invoking request + * @param err ERR_OK on success + * ERR_TIMEOUT if no response was received within timeout, + * ERR_ABRT if (un)subscribe was denied + */ +typedef void (*mqtt_request_cb_t)(void *arg, err_t err); + + +/** + * Pending request item, binds application callback to pending server requests + */ +struct mqtt_request_t +{ + /** Next item in list, NULL means this is the last in chain, + next pointing at itself means request is unallocated */ + struct mqtt_request_t *next; + /** Callback to upper layer */ + mqtt_request_cb_t cb; + void *arg; + /** MQTT packet identifier */ + u16_t pkt_id; + /** Expire time relative to element before this */ + u16_t timeout_diff; +}; + +/** Ring buffer */ +struct mqtt_ringbuf_t { + u16_t put; + u16_t get; + u8_t buf[MQTT_OUTPUT_RINGBUF_SIZE]; +}; + +/** MQTT client */ +struct mqtt_client_t +{ + /** Timers and timeouts */ + u16_t cyclic_tick; + u16_t keep_alive; + u16_t server_watchdog; + /** Packet identifier generator*/ + u16_t pkt_id_seq; + /** Packet identifier of pending incoming publish */ + u16_t inpub_pkt_id; + /** Connection state */ + u8_t conn_state; + struct tcp_pcb *conn; + /** Connection callback */ + void *connect_arg; + mqtt_connection_cb_t connect_cb; + /** Pending requests to server */ + struct mqtt_request_t *pend_req_queue; + struct mqtt_request_t req_list[MQTT_REQ_MAX_IN_FLIGHT]; + void *inpub_arg; + /** Incoming data callback */ + mqtt_incoming_data_cb_t data_cb; + mqtt_incoming_publish_cb_t pub_cb; + /** Input */ + u32_t msg_idx; + u8_t rx_buffer[MQTT_VAR_HEADER_BUFFER_LEN]; + /** Output ring-buffer */ + struct mqtt_ringbuf_t output; +}; + + +/** Connect to server */ +err_t mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ipaddr, u16_t port, mqtt_connection_cb_t cb, void *arg, + const struct mqtt_connect_client_info_t *client_info); + +/** Disconnect from server */ +void mqtt_disconnect(mqtt_client_t *client); + +/** Create new client */ +mqtt_client_t *mqtt_client_new(void); + +/** Check connection status */ +u8_t mqtt_client_is_connected(mqtt_client_t *client); + +/** Set callback to call for incoming publish */ +void mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t, + mqtt_incoming_data_cb_t data_cb, void *arg); + +/** Common function for subscribe and unsubscribe */ +err_t mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub); + +/** @ingroup mqtt + *Subscribe to topic */ +#define mqtt_subscribe(client, topic, qos, cb, arg) mqtt_sub_unsub(client, topic, qos, cb, arg, 1) +/** @ingroup mqtt + * Unsubscribe to topic */ +#define mqtt_unsubscribe(client, topic, cb, arg) mqtt_sub_unsub(client, topic, 0, cb, arg, 0) + + +/** Publish data to topic */ +err_t mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain, + mqtt_request_cb_t cb, void *arg); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_MQTT_CLIENT_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mqtt_opts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mqtt_opts.h new file mode 100644 index 0000000..7c72cc0 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/mqtt_opts.h @@ -0,0 +1,103 @@ +/** + * @file + * MQTT client options + */ + +/* + * Copyright (c) 2016 Erik Andersson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Erik Andersson + * + */ +#ifndef LWIP_HDR_APPS_MQTT_OPTS_H +#define LWIP_HDR_APPS_MQTT_OPTS_H + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup mqtt_opts Options + * @ingroup mqtt + * @{ + */ + +/** + * Output ring-buffer size, must be able to fit largest outgoing publish message topic+payloads + */ +#ifndef MQTT_OUTPUT_RINGBUF_SIZE +#define MQTT_OUTPUT_RINGBUF_SIZE 2048 +#endif + +/** + * Number of bytes in receive buffer, must be at least the size of the longest incoming topic + 8 + * If one wants to avoid fragmented incoming publish, set length to max incoming topic length + max payload length + 8 + */ +#ifndef MQTT_VAR_HEADER_BUFFER_LEN +#define MQTT_VAR_HEADER_BUFFER_LEN 1024 +#endif + +/** + * Maximum number of pending subscribe, unsubscribe and publish requests to server . + */ +#ifndef MQTT_REQ_MAX_IN_FLIGHT +#define MQTT_REQ_MAX_IN_FLIGHT 16 +#endif + +/** + * Seconds between each cyclic timer call. + */ +#ifndef MQTT_CYCLIC_TIMER_INTERVAL +#define MQTT_CYCLIC_TIMER_INTERVAL 5 +#endif + +/** + * Publish, subscribe and unsubscribe request timeout in seconds. + */ +#ifndef MQTT_REQ_TIMEOUT +#define MQTT_REQ_TIMEOUT 30 +#endif + +/** + * Seconds for MQTT connect response timeout after sending connect request + */ +#ifndef MQTT_CONNECT_TIMOUT +#define MQTT_CONNECT_TIMOUT 100 +#endif + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_MQTT_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/netbiosns.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/netbiosns.h new file mode 100644 index 0000000..4126606 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/netbiosns.h @@ -0,0 +1,43 @@ +/** + * @file + * NETBIOS name service responder + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ +#ifndef LWIP_HDR_APPS_NETBIOS_H +#define LWIP_HDR_APPS_NETBIOS_H + +#include "lwip/apps/netbiosns_opts.h" + +void netbiosns_init(void); +#ifndef NETBIOS_LWIP_NAME +void netbiosns_set_name(const char* hostname); +#endif +void netbiosns_stop(void); + +#endif /* LWIP_HDR_APPS_NETBIOS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/netbiosns_opts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/netbiosns_opts.h new file mode 100644 index 0000000..0909ef7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/netbiosns_opts.h @@ -0,0 +1,59 @@ +/** + * @file + * NETBIOS name service responder options + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ +#ifndef LWIP_HDR_APPS_NETBIOS_OPTS_H +#define LWIP_HDR_APPS_NETBIOS_OPTS_H + +#include "lwip/opt.h" + +/** + * @defgroup netbiosns_opts Options + * @ingroup netbiosns + * @{ + */ + +/** NetBIOS name of lwip device + * This must be uppercase until NETBIOS_STRCMP() is defined to a string + * comparision function that is case insensitive. + * If you want to use the netif's hostname, use this (with LWIP_NETIF_HOSTNAME): + * (ip_current_netif() != NULL ? ip_current_netif()->hostname != NULL ? ip_current_netif()->hostname : "" : "") + * + * If this is not defined, netbiosns_set_name() can be called at runtime to change the name. + */ +#ifdef __DOXYGEN__ +#define NETBIOS_LWIP_NAME "NETBIOSLWIPDEV" +#endif + +/** + * @} + */ + +#endif /* LWIP_HDR_APPS_NETBIOS_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp.h new file mode 100644 index 0000000..10e8ff4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp.h @@ -0,0 +1,128 @@ +/** + * @file + * SNMP server main API - start and basic configuration + */ + +/* + * Copyright (c) 2001, 2002 Leon Woestenberg + * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Leon Woestenberg + * Martin Hentschel + * + */ +#ifndef LWIP_HDR_APPS_SNMP_H +#define LWIP_HDR_APPS_SNMP_H + +#include "lwip/apps/snmp_opts.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/err.h" +#include "lwip/apps/snmp_core.h" + +/** SNMP variable binding descriptor (publically needed for traps) */ +struct snmp_varbind +{ + /** pointer to next varbind, NULL for last in list */ + struct snmp_varbind *next; + /** pointer to previous varbind, NULL for first in list */ + struct snmp_varbind *prev; + + /** object identifier */ + struct snmp_obj_id oid; + + /** value ASN1 type */ + u8_t type; + /** object value length */ + u16_t value_len; + /** object value */ + void *value; +}; + +/** + * @ingroup snmp_core + * Agent setup, start listening to port 161. + */ +void snmp_init(void); +void snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs); + +void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid); +const struct snmp_obj_id* snmp_get_device_enterprise_oid(void); + +void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable); +void snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst); + +/** Generic trap: cold start */ +#define SNMP_GENTRAP_COLDSTART 0 +/** Generic trap: warm start */ +#define SNMP_GENTRAP_WARMSTART 1 +/** Generic trap: link down */ +#define SNMP_GENTRAP_LINKDOWN 2 +/** Generic trap: link up */ +#define SNMP_GENTRAP_LINKUP 3 +/** Generic trap: authentication failure */ +#define SNMP_GENTRAP_AUTH_FAILURE 4 +/** Generic trap: EGP neighbor lost */ +#define SNMP_GENTRAP_EGP_NEIGHBOR_LOSS 5 +/** Generic trap: enterprise specific */ +#define SNMP_GENTRAP_ENTERPRISE_SPECIFIC 6 + +err_t snmp_send_trap_generic(s32_t generic_trap); +err_t snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds); +err_t snmp_send_trap(const struct snmp_obj_id* oid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds); + +#define SNMP_AUTH_TRAPS_DISABLED 0 +#define SNMP_AUTH_TRAPS_ENABLED 1 +void snmp_set_auth_traps_enabled(u8_t enable); +u8_t snmp_get_auth_traps_enabled(void); + +const char * snmp_get_community(void); +const char * snmp_get_community_write(void); +const char * snmp_get_community_trap(void); +void snmp_set_community(const char * const community); +void snmp_set_community_write(const char * const community); +void snmp_set_community_trap(const char * const community); + +void snmp_coldstart_trap(void); +void snmp_authfail_trap(void); + +typedef void (*snmp_write_callback_fct)(const u32_t* oid, u8_t oid_len, void* callback_arg); +void snmp_set_write_callback(snmp_write_callback_fct write_callback, void* callback_arg); + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_SNMP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_core.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_core.h new file mode 100644 index 0000000..e781c53 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_core.h @@ -0,0 +1,364 @@ +/** + * @file + * SNMP core API for implementing MIBs + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + * Martin Hentschel + */ + +#ifndef LWIP_HDR_APPS_SNMP_CORE_H +#define LWIP_HDR_APPS_SNMP_CORE_H + +#include "lwip/apps/snmp_opts.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* basic ASN1 defines */ +#define SNMP_ASN1_CLASS_UNIVERSAL 0x00 +#define SNMP_ASN1_CLASS_APPLICATION 0x40 +#define SNMP_ASN1_CLASS_CONTEXT 0x80 +#define SNMP_ASN1_CLASS_PRIVATE 0xC0 + +#define SNMP_ASN1_CONTENTTYPE_PRIMITIVE 0x00 +#define SNMP_ASN1_CONTENTTYPE_CONSTRUCTED 0x20 + +/* universal tags (from ASN.1 spec.) */ +#define SNMP_ASN1_UNIVERSAL_END_OF_CONTENT 0 +#define SNMP_ASN1_UNIVERSAL_INTEGER 2 +#define SNMP_ASN1_UNIVERSAL_OCTET_STRING 4 +#define SNMP_ASN1_UNIVERSAL_NULL 5 +#define SNMP_ASN1_UNIVERSAL_OBJECT_ID 6 +#define SNMP_ASN1_UNIVERSAL_SEQUENCE_OF 16 + +/* application specific (SNMP) tags (from SNMPv2-SMI) */ +#define SNMP_ASN1_APPLICATION_IPADDR 0 /* [APPLICATION 0] IMPLICIT OCTET STRING (SIZE (4)) */ +#define SNMP_ASN1_APPLICATION_COUNTER 1 /* [APPLICATION 1] IMPLICIT INTEGER (0..4294967295) => u32_t */ +#define SNMP_ASN1_APPLICATION_GAUGE 2 /* [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) => u32_t */ +#define SNMP_ASN1_APPLICATION_TIMETICKS 3 /* [APPLICATION 3] IMPLICIT INTEGER (0..4294967295) => u32_t */ +#define SNMP_ASN1_APPLICATION_OPAQUE 4 /* [APPLICATION 4] IMPLICIT OCTET STRING */ +#define SNMP_ASN1_APPLICATION_COUNTER64 6 /* [APPLICATION 6] IMPLICIT INTEGER (0..18446744073709551615) */ + +/* context specific (SNMP) tags (from RFC 1905) */ +#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_INSTANCE 1 + +/* full ASN1 type defines */ +#define SNMP_ASN1_TYPE_END_OF_CONTENT (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_END_OF_CONTENT) +#define SNMP_ASN1_TYPE_INTEGER (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_INTEGER) +#define SNMP_ASN1_TYPE_OCTET_STRING (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_OCTET_STRING) +#define SNMP_ASN1_TYPE_NULL (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_NULL) +#define SNMP_ASN1_TYPE_OBJECT_ID (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_OBJECT_ID) +#define SNMP_ASN1_TYPE_SEQUENCE (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_UNIVERSAL_SEQUENCE_OF) +#define SNMP_ASN1_TYPE_IPADDR (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_IPADDR) +#define SNMP_ASN1_TYPE_IPADDRESS SNMP_ASN1_TYPE_IPADDR +#define SNMP_ASN1_TYPE_COUNTER (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER) +#define SNMP_ASN1_TYPE_COUNTER32 SNMP_ASN1_TYPE_COUNTER +#define SNMP_ASN1_TYPE_GAUGE (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_GAUGE) +#define SNMP_ASN1_TYPE_GAUGE32 SNMP_ASN1_TYPE_GAUGE +#define SNMP_ASN1_TYPE_UNSIGNED32 SNMP_ASN1_TYPE_GAUGE +#define SNMP_ASN1_TYPE_TIMETICKS (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_TIMETICKS) +#define SNMP_ASN1_TYPE_OPAQUE (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_OPAQUE) +#define SNMP_ASN1_TYPE_COUNTER64 (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER64) + +#define SNMP_VARBIND_EXCEPTION_OFFSET 0xF0 +#define SNMP_VARBIND_EXCEPTION_MASK 0x0F + +/** error codes predefined by SNMP prot. */ +typedef enum { + SNMP_ERR_NOERROR = 0, +/* +outdated v1 error codes. do not use anmore! +#define SNMP_ERR_NOSUCHNAME 2 use SNMP_ERR_NOSUCHINSTANCE instead +#define SNMP_ERR_BADVALUE 3 use SNMP_ERR_WRONGTYPE,SNMP_ERR_WRONGLENGTH,SNMP_ERR_WRONGENCODING or SNMP_ERR_WRONGVALUE instead +#define SNMP_ERR_READONLY 4 use SNMP_ERR_NOTWRITABLE instead +*/ + SNMP_ERR_GENERROR = 5, + SNMP_ERR_NOACCESS = 6, + SNMP_ERR_WRONGTYPE = 7, + SNMP_ERR_WRONGLENGTH = 8, + SNMP_ERR_WRONGENCODING = 9, + SNMP_ERR_WRONGVALUE = 10, + SNMP_ERR_NOCREATION = 11, + SNMP_ERR_INCONSISTENTVALUE = 12, + SNMP_ERR_RESOURCEUNAVAILABLE = 13, + SNMP_ERR_COMMITFAILED = 14, + SNMP_ERR_UNDOFAILED = 15, + SNMP_ERR_NOTWRITABLE = 17, + SNMP_ERR_INCONSISTENTNAME = 18, + + SNMP_ERR_NOSUCHINSTANCE = SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_INSTANCE +} snmp_err_t; + +/** internal object identifier representation */ +struct snmp_obj_id +{ + u8_t len; + u32_t id[SNMP_MAX_OBJ_ID_LEN]; +}; + +struct snmp_obj_id_const_ref +{ + u8_t len; + const u32_t* id; +}; + +extern const struct snmp_obj_id_const_ref snmp_zero_dot_zero; /* administrative identifier from SNMPv2-SMI */ + +/** SNMP variant value, used as reference in struct snmp_node_instance and table implementation */ +union snmp_variant_value +{ + void* ptr; + const void* const_ptr; + u32_t u32; + s32_t s32; +}; + + +/** +SNMP MIB node types + tree node is the only node the stack can process in order to walk the tree, + all other nodes are assumed to be leaf nodes. + This cannot be an enum because users may want to define their own node types. +*/ +#define SNMP_NODE_TREE 0x00 +/* predefined leaf node types */ +#define SNMP_NODE_SCALAR 0x01 +#define SNMP_NODE_SCALAR_ARRAY 0x02 +#define SNMP_NODE_TABLE 0x03 +#define SNMP_NODE_THREADSYNC 0x04 + +/** node "base class" layout, the mandatory fields for a node */ +struct snmp_node +{ + /** one out of SNMP_NODE_TREE or any leaf node type (like SNMP_NODE_SCALAR) */ + u8_t node_type; + /** the number assigned to this node which used as part of the full OID */ + u32_t oid; +}; + +/** SNMP node instance access types */ +typedef enum { + SNMP_NODE_INSTANCE_ACCESS_READ = 1, + SNMP_NODE_INSTANCE_ACCESS_WRITE = 2, + SNMP_NODE_INSTANCE_READ_ONLY = SNMP_NODE_INSTANCE_ACCESS_READ, + SNMP_NODE_INSTANCE_READ_WRITE = (SNMP_NODE_INSTANCE_ACCESS_READ | SNMP_NODE_INSTANCE_ACCESS_WRITE), + SNMP_NODE_INSTANCE_WRITE_ONLY = SNMP_NODE_INSTANCE_ACCESS_WRITE, + SNMP_NODE_INSTANCE_NOT_ACCESSIBLE = 0 +} snmp_access_t; + +struct snmp_node_instance; + +typedef s16_t (*node_instance_get_value_method)(struct snmp_node_instance*, void*); +typedef snmp_err_t (*node_instance_set_test_method)(struct snmp_node_instance*, u16_t, void*); +typedef snmp_err_t (*node_instance_set_value_method)(struct snmp_node_instance*, u16_t, void*); +typedef void (*node_instance_release_method)(struct snmp_node_instance*); + +#define SNMP_GET_VALUE_RAW_DATA 0x8000 + +/** SNMP node instance */ +struct snmp_node_instance +{ + /** prefilled with the node, get_instance() is called on; may be changed by user to any value to pass an arbitrary node between calls to get_instance() and get_value/test_value/set_value */ + const struct snmp_node* node; + /** prefilled with the instance id requested; for get_instance() this is the exact oid requested; for get_next_instance() this is the relative starting point, stack expects relative oid of next node here */ + struct snmp_obj_id instance_oid; + + /** ASN type for this object (see snmp_asn1.h for definitions) */ + u8_t asn1_type; + /** one out of instance access types defined above (SNMP_NODE_INSTANCE_READ_ONLY,...) */ + snmp_access_t access; + + /** returns object value for the given object identifier. Return values <0 to indicate an error */ + node_instance_get_value_method get_value; + /** tests length and/or range BEFORE setting */ + node_instance_set_test_method set_test; + /** sets object value, only called when set_test() was successful */ + node_instance_set_value_method set_value; + /** called in any case when the instance is not required anymore by stack (useful for freeing memory allocated in get_instance/get_next_instance methods) */ + node_instance_release_method release_instance; + + /** reference to pass arbitrary value between calls to get_instance() and get_value/test_value/set_value */ + union snmp_variant_value reference; + /** see reference (if reference is a pointer, the length of underlying data may be stored here or anything else) */ + u32_t reference_len; +}; + + +/** SNMP tree node */ +struct snmp_tree_node +{ + /** inherited "base class" members */ + struct snmp_node node; + u16_t subnode_count; + const struct snmp_node* const *subnodes; +}; + +#define SNMP_CREATE_TREE_NODE(oid, subnodes) \ + {{ SNMP_NODE_TREE, (oid) }, \ + (u16_t)LWIP_ARRAYSIZE(subnodes), (subnodes) } + +#define SNMP_CREATE_EMPTY_TREE_NODE(oid) \ + {{ SNMP_NODE_TREE, (oid) }, \ + 0, NULL } + +/** SNMP leaf node */ +struct snmp_leaf_node +{ + /** inherited "base class" members */ + struct snmp_node node; + snmp_err_t (*get_instance)(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); + snmp_err_t (*get_next_instance)(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); +}; + +/** represents a single mib with its base oid and root node */ +struct snmp_mib +{ + const u32_t *base_oid; + u8_t base_oid_len; + const struct snmp_node *root_node; +}; + +#define SNMP_MIB_CREATE(oid_list, root_node) { (oid_list), (u8_t)LWIP_ARRAYSIZE(oid_list), root_node } + +/** OID range structure */ +struct snmp_oid_range +{ + u32_t min; + u32_t max; +}; + +/** checks if incoming OID length and values are in allowed ranges */ +u8_t snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len); + +typedef enum { + SNMP_NEXT_OID_STATUS_SUCCESS, + SNMP_NEXT_OID_STATUS_NO_MATCH, + SNMP_NEXT_OID_STATUS_BUF_TO_SMALL +} snmp_next_oid_status_t; + +/** state for next_oid_init / next_oid_check functions */ +struct snmp_next_oid_state +{ + const u32_t* start_oid; + u8_t start_oid_len; + + u32_t* next_oid; + u8_t next_oid_len; + u8_t next_oid_max_len; + + snmp_next_oid_status_t status; + void* reference; +}; + +void snmp_next_oid_init(struct snmp_next_oid_state *state, + const u32_t *start_oid, u8_t start_oid_len, + u32_t *next_oid_buf, u8_t next_oid_max_len); +u8_t snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len); +u8_t snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference); + +void snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len); +void snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len); +void snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len); +void snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len); +u8_t snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len); +s8_t snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len); + +#if LWIP_IPV4 +u8_t snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip); +void snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid); +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 +u8_t snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip); +void snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid); +#endif /* LWIP_IPV6 */ +#if LWIP_IPV4 || LWIP_IPV6 +u8_t snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid); +u8_t snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid); + +u8_t snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip); +u8_t snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port); +#endif /* LWIP_IPV4 || LWIP_IPV6 */ + +struct netif; +u8_t netif_to_num(const struct netif *netif); + +snmp_err_t snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value); /* generic function which can be used if test is always successful */ + +err_t snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value); +err_t snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value); +u8_t snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count); +u8_t snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value); + +struct snmp_statistics +{ + u32_t inpkts; + u32_t outpkts; + u32_t inbadversions; + u32_t inbadcommunitynames; + u32_t inbadcommunityuses; + u32_t inasnparseerrs; + u32_t intoobigs; + u32_t innosuchnames; + u32_t inbadvalues; + u32_t inreadonlys; + u32_t ingenerrs; + u32_t intotalreqvars; + u32_t intotalsetvars; + u32_t ingetrequests; + u32_t ingetnexts; + u32_t insetrequests; + u32_t ingetresponses; + u32_t intraps; + u32_t outtoobigs; + u32_t outnosuchnames; + u32_t outbadvalues; + u32_t outgenerrs; + u32_t outgetrequests; + u32_t outgetnexts; + u32_t outsetrequests; + u32_t outgetresponses; + u32_t outtraps; +}; + +extern struct snmp_statistics snmp_stats; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* LWIP_HDR_APPS_SNMP_CORE_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_mib2.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_mib2.h new file mode 100644 index 0000000..2f4a689 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_mib2.h @@ -0,0 +1,78 @@ +/** + * @file + * SNMP MIB2 API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Dirk Ziegelmeier + * + */ +#ifndef LWIP_HDR_APPS_SNMP_MIB2_H +#define LWIP_HDR_APPS_SNMP_MIB2_H + +#include "lwip/apps/snmp_opts.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ +#if SNMP_LWIP_MIB2 + +#include "lwip/apps/snmp_core.h" + +extern const struct snmp_mib mib2; + +#if SNMP_USE_NETCONN +#include "lwip/apps/snmp_threadsync.h" +void snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg); +extern struct snmp_threadsync_instance snmp_mib2_lwip_locks; +#endif + +#ifndef SNMP_SYSSERVICES +#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2)) +#endif + +void snmp_mib2_set_sysdescr(const u8_t* str, const u16_t* len); /* read-only be defintion */ +void snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize); +void snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen); +void snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize); +void snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen); +void snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize); +void snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen); + +#endif /* SNMP_LWIP_MIB2 */ +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_SNMP_MIB2_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_opts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_opts.h new file mode 100644 index 0000000..6c9ba7b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_opts.h @@ -0,0 +1,293 @@ +/** + * @file + * SNMP server options list + */ + +/* + * Copyright (c) 2015 Dirk Ziegelmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Dirk Ziegelmeier + * + */ +#ifndef LWIP_HDR_SNMP_OPTS_H +#define LWIP_HDR_SNMP_OPTS_H + +#include "lwip/opt.h" + +/** + * @defgroup snmp_opts Options + * @ingroup snmp + * @{ + */ + +/** + * LWIP_SNMP==1: This enables the lwIP SNMP agent. UDP must be available + * for SNMP transport. + * If you want to use your own SNMP agent, leave this disabled. + * To integrate MIB2 of an external agent, you need to enable + * LWIP_MIB2_CALLBACKS and MIB2_STATS. This will give you the callbacks + * and statistics counters you need to get MIB2 working. + */ +#if !defined LWIP_SNMP || defined __DOXYGEN__ +#define LWIP_SNMP 0 +#endif + +/** + * SNMP_USE_NETCONN: Use netconn API instead of raw API. + * Makes SNMP agent run in a worker thread, so blocking operations + * can be done in MIB calls. + */ +#if !defined SNMP_USE_NETCONN || defined __DOXYGEN__ +#define SNMP_USE_NETCONN 0 +#endif + +/** + * SNMP_USE_RAW: Use raw API. + * SNMP agent does not run in a worker thread, so blocking operations + * should not be done in MIB calls. + */ +#if !defined SNMP_USE_RAW || defined __DOXYGEN__ +#define SNMP_USE_RAW 1 +#endif + +#if SNMP_USE_NETCONN && SNMP_USE_RAW +#error SNMP stack can use only one of the APIs {raw, netconn} +#endif + +#if LWIP_SNMP && !SNMP_USE_NETCONN && !SNMP_USE_RAW +#error SNMP stack needs a receive API and UDP {raw, netconn} +#endif + +#if SNMP_USE_NETCONN +/** + * SNMP_STACK_SIZE: Stack size of SNMP netconn worker thread + */ +#if !defined SNMP_STACK_SIZE || defined __DOXYGEN__ +#define SNMP_STACK_SIZE DEFAULT_THREAD_STACKSIZE +#endif + +/** + * SNMP_THREAD_PRIO: SNMP netconn worker thread priority + */ +#if !defined SNMP_THREAD_PRIO || defined __DOXYGEN__ +#define SNMP_THREAD_PRIO DEFAULT_THREAD_PRIO +#endif +#endif /* SNMP_USE_NETCONN */ + +/** + * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap + * destination is required + */ +#if !defined SNMP_TRAP_DESTINATIONS || defined __DOXYGEN__ +#define SNMP_TRAP_DESTINATIONS 1 +#endif + +/** + * Only allow SNMP write actions that are 'safe' (e.g. disabling netifs is not + * a safe action and disabled when SNMP_SAFE_REQUESTS = 1). + * Unsafe requests are disabled by default! + */ +#if !defined SNMP_SAFE_REQUESTS || defined __DOXYGEN__ +#define SNMP_SAFE_REQUESTS 1 +#endif + +/** + * The maximum length of strings used. + */ +#if !defined SNMP_MAX_OCTET_STRING_LEN || defined __DOXYGEN__ +#define SNMP_MAX_OCTET_STRING_LEN 127 +#endif + +/** + * The maximum number of Sub ID's inside an object identifier. + * Indirectly this also limits the maximum depth of SNMP tree. + */ +#if !defined SNMP_MAX_OBJ_ID_LEN || defined __DOXYGEN__ +#define SNMP_MAX_OBJ_ID_LEN 50 +#endif + +#if !defined SNMP_MAX_VALUE_SIZE || defined __DOXYGEN__ +/** + * The maximum size of a value. + */ +#define SNMP_MIN_VALUE_SIZE (2 * sizeof(u32_t*)) /* size required to store the basic types (8 bytes for counter64) */ +/** + * The minimum size of a value. + */ +#define SNMP_MAX_VALUE_SIZE LWIP_MAX(LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN), sizeof(u32_t)*(SNMP_MAX_OBJ_ID_LEN)), SNMP_MIN_VALUE_SIZE) +#endif + +/** + * The snmp read-access community. Used for write-access and traps, too + * unless SNMP_COMMUNITY_WRITE or SNMP_COMMUNITY_TRAP are enabled, respectively. + */ +#if !defined SNMP_COMMUNITY || defined __DOXYGEN__ +#define SNMP_COMMUNITY "public" +#endif + +/** + * The snmp write-access community. + * Set this community to "" in order to disallow any write access. + */ +#if !defined SNMP_COMMUNITY_WRITE || defined __DOXYGEN__ +#define SNMP_COMMUNITY_WRITE "private" +#endif + +/** + * The snmp community used for sending traps. + */ +#if !defined SNMP_COMMUNITY_TRAP || defined __DOXYGEN__ +#define SNMP_COMMUNITY_TRAP "public" +#endif + +/** + * The maximum length of community string. + * If community names shall be adjusted at runtime via snmp_set_community() calls, + * enter here the possible maximum length (+1 for terminating null character). + */ +#if !defined SNMP_MAX_COMMUNITY_STR_LEN || defined __DOXYGEN__ +#define SNMP_MAX_COMMUNITY_STR_LEN LWIP_MAX(LWIP_MAX(sizeof(SNMP_COMMUNITY), sizeof(SNMP_COMMUNITY_WRITE)), sizeof(SNMP_COMMUNITY_TRAP)) +#endif + +/** + * The OID identifiying the device. This may be the enterprise OID itself or any OID located below it in tree. + */ +#if !defined SNMP_DEVICE_ENTERPRISE_OID || defined __DOXYGEN__ +#define SNMP_LWIP_ENTERPRISE_OID 26381 +/** + * IANA assigned enterprise ID for lwIP is 26381 + * @see http://www.iana.org/assignments/enterprise-numbers + * + * @note this enterprise ID is assigned to the lwIP project, + * all object identifiers living under this ID are assigned + * by the lwIP maintainers! + * @note don't change this define, use snmp_set_device_enterprise_oid() + * + * If you need to create your own private MIB you'll need + * to apply for your own enterprise ID with IANA: + * http://www.iana.org/numbers.html + */ +#define SNMP_DEVICE_ENTERPRISE_OID {1, 3, 6, 1, 4, 1, SNMP_LWIP_ENTERPRISE_OID} +/** + * Length of SNMP_DEVICE_ENTERPRISE_OID + */ +#define SNMP_DEVICE_ENTERPRISE_OID_LEN 7 +#endif + +/** + * SNMP_DEBUG: Enable debugging for SNMP messages. + */ +#if !defined SNMP_DEBUG || defined __DOXYGEN__ +#define SNMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs. + */ +#if !defined SNMP_MIB_DEBUG || defined __DOXYGEN__ +#define SNMP_MIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * Indicates if the MIB2 implementation of LWIP SNMP stack is used. + */ +#if !defined SNMP_LWIP_MIB2 || defined __DOXYGEN__ +#define SNMP_LWIP_MIB2 LWIP_SNMP +#endif + +/** + * Value return for sysDesc field of MIB2. + */ +#if !defined SNMP_LWIP_MIB2_SYSDESC || defined __DOXYGEN__ +#define SNMP_LWIP_MIB2_SYSDESC "lwIP" +#endif + +/** + * Value return for sysName field of MIB2. + * To make sysName field settable, call snmp_mib2_set_sysname() to provide the necessary buffers. + */ +#if !defined SNMP_LWIP_MIB2_SYSNAME || defined __DOXYGEN__ +#define SNMP_LWIP_MIB2_SYSNAME "FQDN-unk" +#endif + +/** + * Value return for sysContact field of MIB2. + * To make sysContact field settable, call snmp_mib2_set_syscontact() to provide the necessary buffers. + */ +#if !defined SNMP_LWIP_MIB2_SYSCONTACT || defined __DOXYGEN__ +#define SNMP_LWIP_MIB2_SYSCONTACT "" +#endif + +/** + * Value return for sysLocation field of MIB2. + * To make sysLocation field settable, call snmp_mib2_set_syslocation() to provide the necessary buffers. + */ +#if !defined SNMP_LWIP_MIB2_SYSLOCATION || defined __DOXYGEN__ +#define SNMP_LWIP_MIB2_SYSLOCATION "" +#endif + +/** + * This value is used to limit the repetitions processed in GetBulk requests (value == 0 means no limitation). + * This may be useful to limit the load for a single request. + * According to SNMP RFC 1905 it is allowed to not return all requested variables from a GetBulk request if system load would be too high. + * so the effect is that the client will do more requests to gather all data. + * For the stack this could be useful in case that SNMP processing is done in TCP/IP thread. In this situation a request with many + * repetitions could block the thread for a longer time. Setting limit here will keep the stack more responsive. + */ +#if !defined SNMP_LWIP_GETBULK_MAX_REPETITIONS || defined __DOXYGEN__ +#define SNMP_LWIP_GETBULK_MAX_REPETITIONS 0 +#endif + +/** + * @} + */ + +/* + ------------------------------------ + ---------- SNMPv3 options ---------- + ------------------------------------ +*/ + +/** + * LWIP_SNMP_V3==1: This enables EXPERIMENTAL SNMPv3 support. LWIP_SNMP must + * also be enabled. + * THIS IS UNDER DEVELOPMENT AND SHOULD NOT BE ENABLED IN PRODUCTS. + */ +#ifndef LWIP_SNMP_V3 +#define LWIP_SNMP_V3 0 +#endif + +#ifndef LWIP_SNMP_V3_CRYPTO +#define LWIP_SNMP_V3_CRYPTO LWIP_SNMP_V3 +#endif + +#ifndef LWIP_SNMP_V3_MBEDTLS +#define LWIP_SNMP_V3_MBEDTLS LWIP_SNMP_V3 +#endif + +#endif /* LWIP_HDR_SNMP_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_scalar.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_scalar.h new file mode 100644 index 0000000..40a060c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_scalar.h @@ -0,0 +1,113 @@ +/** + * @file + * SNMP server MIB API to implement scalar nodes + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +#ifndef LWIP_HDR_APPS_SNMP_SCALAR_H +#define LWIP_HDR_APPS_SNMP_SCALAR_H + +#include "lwip/apps/snmp_opts.h" +#include "lwip/apps/snmp_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +/** basic scalar node */ +struct snmp_scalar_node +{ + /** inherited "base class" members */ + struct snmp_leaf_node node; + u8_t asn1_type; + snmp_access_t access; + node_instance_get_value_method get_value; + node_instance_set_test_method set_test; + node_instance_set_value_method set_value; +}; + + +snmp_err_t snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); +snmp_err_t snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); + +#define SNMP_SCALAR_CREATE_NODE(oid, access, asn1_type, get_value_method, set_test_method, set_value_method) \ + {{{ SNMP_NODE_SCALAR, (oid) }, \ + snmp_scalar_get_instance, \ + snmp_scalar_get_next_instance }, \ + (asn1_type), (access), (get_value_method), (set_test_method), (set_value_method) } + +#define SNMP_SCALAR_CREATE_NODE_READONLY(oid, asn1_type, get_value_method) SNMP_SCALAR_CREATE_NODE(oid, SNMP_NODE_INSTANCE_READ_ONLY, asn1_type, get_value_method, NULL, NULL) + +/** scalar array node - a tree node which contains scalars only as children */ +struct snmp_scalar_array_node_def +{ + u32_t oid; + u8_t asn1_type; + snmp_access_t access; +}; + +typedef s16_t (*snmp_scalar_array_get_value_method)(const struct snmp_scalar_array_node_def*, void*); +typedef snmp_err_t (*snmp_scalar_array_set_test_method)(const struct snmp_scalar_array_node_def*, u16_t, void*); +typedef snmp_err_t (*snmp_scalar_array_set_value_method)(const struct snmp_scalar_array_node_def*, u16_t, void*); + +/** basic scalar array node */ +struct snmp_scalar_array_node +{ + /** inherited "base class" members */ + struct snmp_leaf_node node; + u16_t array_node_count; + const struct snmp_scalar_array_node_def* array_nodes; + snmp_scalar_array_get_value_method get_value; + snmp_scalar_array_set_test_method set_test; + snmp_scalar_array_set_value_method set_value; +}; + +snmp_err_t snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); +snmp_err_t snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); + +#define SNMP_SCALAR_CREATE_ARRAY_NODE(oid, array_nodes, get_value_method, set_test_method, set_value_method) \ + {{{ SNMP_NODE_SCALAR_ARRAY, (oid) }, \ + snmp_scalar_array_get_instance, \ + snmp_scalar_array_get_next_instance }, \ + (u16_t)LWIP_ARRAYSIZE(array_nodes), (array_nodes), (get_value_method), (set_test_method), (set_value_method) } + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_SNMP_SCALAR_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_table.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_table.h new file mode 100644 index 0000000..4988b51 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_table.h @@ -0,0 +1,134 @@ +/** + * @file + * SNMP server MIB API to implement table nodes + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Martin Hentschel + * + */ + +#ifndef LWIP_HDR_APPS_SNMP_TABLE_H +#define LWIP_HDR_APPS_SNMP_TABLE_H + +#include "lwip/apps/snmp_opts.h" +#include "lwip/apps/snmp_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +/** default (customizable) read/write table */ +struct snmp_table_col_def +{ + u32_t index; + u8_t asn1_type; + snmp_access_t access; +}; + +/** table node */ +struct snmp_table_node +{ + /** inherited "base class" members */ + struct snmp_leaf_node node; + u16_t column_count; + const struct snmp_table_col_def* columns; + snmp_err_t (*get_cell_instance)(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance); + snmp_err_t (*get_next_cell_instance)(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance); + /** returns object value for the given object identifier */ + node_instance_get_value_method get_value; + /** tests length and/or range BEFORE setting */ + node_instance_set_test_method set_test; + /** sets object value, only called when set_test() was successful */ + node_instance_set_value_method set_value; +}; + +snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); +snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); + +#define SNMP_TABLE_CREATE(oid, columns, get_cell_instance_method, get_next_cell_instance_method, get_value_method, set_test_method, set_value_method) \ + {{{ SNMP_NODE_TABLE, (oid) }, \ + snmp_table_get_instance, \ + snmp_table_get_next_instance }, \ + (u16_t)LWIP_ARRAYSIZE(columns), (columns), \ + (get_cell_instance_method), (get_next_cell_instance_method), \ + (get_value_method), (set_test_method), (set_value_method)} + +#define SNMP_TABLE_GET_COLUMN_FROM_OID(oid) ((oid)[1]) /* first array value is (fixed) row entry (fixed to 1) and 2nd value is column, follow3ed by instance */ + + +/** simple read-only table */ +typedef enum { + SNMP_VARIANT_VALUE_TYPE_U32, + SNMP_VARIANT_VALUE_TYPE_S32, + SNMP_VARIANT_VALUE_TYPE_PTR, + SNMP_VARIANT_VALUE_TYPE_CONST_PTR +} snmp_table_column_data_type_t; + +struct snmp_table_simple_col_def +{ + u32_t index; + u8_t asn1_type; + snmp_table_column_data_type_t data_type; /* depending of what union member is used to store the value*/ +}; + +/** simple read-only table node */ +struct snmp_table_simple_node +{ + /* inherited "base class" members */ + struct snmp_leaf_node node; + u16_t column_count; + const struct snmp_table_simple_col_def* columns; + snmp_err_t (*get_cell_value)(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len); + snmp_err_t (*get_next_cell_instance_and_value)(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len); +}; + +snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); +snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); + +#define SNMP_TABLE_CREATE_SIMPLE(oid, columns, get_cell_value_method, get_next_cell_instance_and_value_method) \ + {{{ SNMP_NODE_TABLE, (oid) }, \ + snmp_table_simple_get_instance, \ + snmp_table_simple_get_next_instance }, \ + (u16_t)LWIP_ARRAYSIZE(columns), (columns), (get_cell_value_method), (get_next_cell_instance_and_value_method) } + +s16_t snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value); +s16_t snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value); +s16_t snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value); + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_SNMP_TABLE_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_threadsync.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_threadsync.h new file mode 100644 index 0000000..a25dbf2 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmp_threadsync.h @@ -0,0 +1,114 @@ +/** + * @file + * SNMP server MIB API to implement thread synchronization + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Dirk Ziegelmeier + * + */ + +#ifndef LWIP_HDR_APPS_SNMP_THREADSYNC_H +#define LWIP_HDR_APPS_SNMP_THREADSYNC_H + +#include "lwip/apps/snmp_opts.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/apps/snmp_core.h" +#include "lwip/sys.h" + +typedef void (*snmp_threadsync_called_fn)(void* arg); +typedef void (*snmp_threadsync_synchronizer_fn)(snmp_threadsync_called_fn fn, void* arg); + + +/** Thread sync runtime data. For internal usage only. */ +struct threadsync_data +{ + union { + snmp_err_t err; + s16_t s16; + } retval; + union { + const u32_t *root_oid; + void *value; + } arg1; + union { + u8_t root_oid_len; + u16_t len; + } arg2; + const struct snmp_threadsync_node *threadsync_node; + struct snmp_node_instance proxy_instance; +}; + +/** Thread sync instance. Needed EXCATLY once for every thread to be synced into. */ +struct snmp_threadsync_instance +{ + sys_sem_t sem; + sys_mutex_t sem_usage_mutex; + snmp_threadsync_synchronizer_fn sync_fn; + struct threadsync_data data; +}; + +/** SNMP thread sync proxy leaf node */ +struct snmp_threadsync_node +{ + /* inherited "base class" members */ + struct snmp_leaf_node node; + + const struct snmp_leaf_node *target; + struct snmp_threadsync_instance *instance; +}; + +snmp_err_t snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); +snmp_err_t snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance); + +/** Create thread sync proxy node */ +#define SNMP_CREATE_THREAD_SYNC_NODE(oid, target_leaf_node, threadsync_instance) \ + {{{ SNMP_NODE_THREADSYNC, (oid) }, \ + snmp_threadsync_get_instance, \ + snmp_threadsync_get_next_instance }, \ + (target_leaf_node), \ + (threadsync_instance) } + +/** Create thread sync instance data */ +void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn); + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_SNMP_THREADSYNC_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmpv3.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmpv3.h new file mode 100644 index 0000000..c99fed4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/snmpv3.h @@ -0,0 +1,90 @@ +/** + * @file + * Additional SNMPv3 functionality RFC3414 and RFC3826. + */ + +/* + * Copyright (c) 2016 Elias Oenal. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Elias Oenal + */ + +#ifndef LWIP_HDR_APPS_SNMP_V3_H +#define LWIP_HDR_APPS_SNMP_V3_H + +#include "lwip/apps/snmp_opts.h" +#include "lwip/err.h" + +#if LWIP_SNMP && LWIP_SNMP_V3 + +#define SNMP_V3_AUTH_ALGO_INVAL 0 +#define SNMP_V3_AUTH_ALGO_MD5 1 +#define SNMP_V3_AUTH_ALGO_SHA 2 + +#define SNMP_V3_PRIV_ALGO_INVAL 0 +#define SNMP_V3_PRIV_ALGO_DES 1 +#define SNMP_V3_PRIV_ALGO_AES 2 + +#define SNMP_V3_PRIV_MODE_DECRYPT 0 +#define SNMP_V3_PRIV_MODE_ENCRYPT 1 + +/* + * The following callback functions must be implemented by the application. + * There is a dummy implementation in snmpv3_dummy.c. + */ + +void snmpv3_get_engine_id(const char **id, u8_t *len); +err_t snmpv3_set_engine_id(const char* id, u8_t len); + +u32_t snmpv3_get_engine_boots(void); +void snmpv3_set_engine_boots(u32_t boots); + +u32_t snmpv3_get_engine_time(void); +void snmpv3_reset_engine_time(void); + +err_t snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key); + +/* The following functions are provided by the SNMPv3 agent */ + +void snmpv3_engine_id_changed(void); + +void snmpv3_password_to_key_md5( + const u8_t *password, /* IN */ + u8_t passwordlen, /* IN */ + const u8_t *engineID, /* IN - pointer to snmpEngineID */ + u8_t engineLength, /* IN - length of snmpEngineID */ + u8_t *key); /* OUT - pointer to caller 16-octet buffer */ + +void snmpv3_password_to_key_sha( + const u8_t *password, /* IN */ + u8_t passwordlen, /* IN */ + const u8_t *engineID, /* IN - pointer to snmpEngineID */ + u8_t engineLength, /* IN - length of snmpEngineID */ + u8_t *key); /* OUT - pointer to caller 20-octet buffer */ + +#endif + +#endif /* LWIP_HDR_APPS_SNMP_V3_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/sntp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/sntp.h new file mode 100644 index 0000000..40df9cc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/sntp.h @@ -0,0 +1,76 @@ +/** + * @file + * SNTP client API + */ + +/* + * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Frédéric Bernon, Simon Goldschmidt + * + */ +#ifndef LWIP_HDR_APPS_SNTP_H +#define LWIP_HDR_APPS_SNTP_H + +#include "lwip/apps/sntp_opts.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* SNTP operating modes: default is to poll using unicast. + The mode has to be set before calling sntp_init(). */ +#define SNTP_OPMODE_POLL 0 +#define SNTP_OPMODE_LISTENONLY 1 +void sntp_setoperatingmode(u8_t operating_mode); +u8_t sntp_getoperatingmode(void); + +void sntp_init(void); +void sntp_stop(void); +u8_t sntp_enabled(void); + +void sntp_setserver(u8_t idx, const ip_addr_t *addr); +const ip_addr_t* sntp_getserver(u8_t idx); + +#if SNTP_SERVER_DNS +void sntp_setservername(u8_t idx, char *server); +char *sntp_getservername(u8_t idx); +#endif /* SNTP_SERVER_DNS */ + +#if SNTP_GET_SERVERS_FROM_DHCP +void sntp_servermode_dhcp(int set_servers_from_dhcp); +#else /* SNTP_GET_SERVERS_FROM_DHCP */ +#define sntp_servermode_dhcp(x) +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_SNTP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/sntp_opts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/sntp_opts.h new file mode 100644 index 0000000..f3651f9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/sntp_opts.h @@ -0,0 +1,173 @@ +/** + * @file + * SNTP client options list + */ + +/* + * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Frédéric Bernon, Simon Goldschmidt + * + */ +#ifndef LWIP_HDR_APPS_SNTP_OPTS_H +#define LWIP_HDR_APPS_SNTP_OPTS_H + +#include "lwip/opt.h" + +/** + * @defgroup sntp_opts Options + * @ingroup sntp + * @{ + */ + +/** SNTP macro to change system time in seconds + * Define SNTP_SET_SYSTEM_TIME_US(sec, us) to set the time in microseconds instead of this one + * if you need the additional precision. + */ +#if !defined SNTP_SET_SYSTEM_TIME || defined __DOXYGEN__ +#define SNTP_SET_SYSTEM_TIME(sec) LWIP_UNUSED_ARG(sec) +#endif + +/** The maximum number of SNTP servers that can be set */ +#if !defined SNTP_MAX_SERVERS || defined __DOXYGEN__ +#define SNTP_MAX_SERVERS LWIP_DHCP_MAX_NTP_SERVERS +#endif + +/** Set this to 1 to implement the callback function called by dhcp when + * NTP servers are received. */ +#if !defined SNTP_GET_SERVERS_FROM_DHCP || defined __DOXYGEN__ +#define SNTP_GET_SERVERS_FROM_DHCP LWIP_DHCP_GET_NTP_SRV +#endif + +/** Set this to 1 to support DNS names (or IP address strings) to set sntp servers + * One server address/name can be defined as default if SNTP_SERVER_DNS == 1: + * \#define SNTP_SERVER_ADDRESS "pool.ntp.org" + */ +#if !defined SNTP_SERVER_DNS || defined __DOXYGEN__ +#define SNTP_SERVER_DNS 0 +#endif + +/** + * SNTP_DEBUG: Enable debugging for SNTP. + */ +#if !defined SNTP_DEBUG || defined __DOXYGEN__ +#define SNTP_DEBUG LWIP_DBG_OFF +#endif + +/** SNTP server port */ +#if !defined SNTP_PORT || defined __DOXYGEN__ +#define SNTP_PORT 123 +#endif + +/** Set this to 1 to allow config of SNTP server(s) by DNS name */ +#if !defined SNTP_SERVER_DNS || defined __DOXYGEN__ +#define SNTP_SERVER_DNS 0 +#endif + +/** Sanity check: + * Define this to + * - 0 to turn off sanity checks (default; smaller code) + * - >= 1 to check address and port of the response packet to ensure the + * response comes from the server we sent the request to. + * - >= 2 to check returned Originate Timestamp against Transmit Timestamp + * sent to the server (to ensure response to older request). + * - >= 3 @todo: discard reply if any of the LI, Stratum, or Transmit Timestamp + * fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast). + * - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each + * greater than or equal to 0 and less than infinity, where infinity is + * currently a cozy number like one second. This check avoids using a + * server whose synchronization source has expired for a very long time. + */ +#if !defined SNTP_CHECK_RESPONSE || defined __DOXYGEN__ +#define SNTP_CHECK_RESPONSE 0 +#endif + +/** According to the RFC, this shall be a random delay + * between 1 and 5 minutes (in milliseconds) to prevent load peaks. + * This can be defined to a random generation function, + * which must return the delay in milliseconds as u32_t. + * Turned off by default. + */ +#if !defined SNTP_STARTUP_DELAY || defined __DOXYGEN__ +#define SNTP_STARTUP_DELAY 0 +#endif + +/** If you want the startup delay to be a function, define this + * to a function (including the brackets) and define SNTP_STARTUP_DELAY to 1. + */ +#if !defined SNTP_STARTUP_DELAY_FUNC || defined __DOXYGEN__ +#define SNTP_STARTUP_DELAY_FUNC SNTP_STARTUP_DELAY +#endif + +/** SNTP receive timeout - in milliseconds + * Also used as retry timeout - this shouldn't be too low. + * Default is 3 seconds. + */ +#if !defined SNTP_RECV_TIMEOUT || defined __DOXYGEN__ +#define SNTP_RECV_TIMEOUT 3000 +#endif + +/** SNTP update delay - in milliseconds + * Default is 1 hour. Must not be beolw 15 seconds by specification (i.e. 15000) + */ +#if !defined SNTP_UPDATE_DELAY || defined __DOXYGEN__ +#define SNTP_UPDATE_DELAY 3600000 +#endif + +/** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2 + * to send in request and compare in response. + */ +#if !defined SNTP_GET_SYSTEM_TIME || defined __DOXYGEN__ +#define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0) +#endif + +/** Default retry timeout (in milliseconds) if the response + * received is invalid. + * This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached. + */ +#if !defined SNTP_RETRY_TIMEOUT || defined __DOXYGEN__ +#define SNTP_RETRY_TIMEOUT SNTP_RECV_TIMEOUT +#endif + +/** Maximum retry timeout (in milliseconds). */ +#if !defined SNTP_RETRY_TIMEOUT_MAX || defined __DOXYGEN__ +#define SNTP_RETRY_TIMEOUT_MAX (SNTP_RETRY_TIMEOUT * 10) +#endif + +/** Increase retry timeout with every retry sent + * Default is on to conform to RFC. + */ +#if !defined SNTP_RETRY_TIMEOUT_EXP || defined __DOXYGEN__ +#define SNTP_RETRY_TIMEOUT_EXP 1 +#endif + +/** + * @} + */ + +#endif /* LWIP_HDR_APPS_SNTP_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/tftp_opts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/tftp_opts.h new file mode 100644 index 0000000..6968a80 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/tftp_opts.h @@ -0,0 +1,105 @@ +/****************************************************************//** + * + * @file tftp_opts.h + * + * @author Logan Gunthorpe + * + * @brief Trivial File Transfer Protocol (RFC 1350) implementation options + * + * Copyright (c) Deltatee Enterprises Ltd. 2013 + * All rights reserved. + * + ********************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification,are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Logan Gunthorpe + * + */ + +#ifndef LWIP_HDR_APPS_TFTP_OPTS_H +#define LWIP_HDR_APPS_TFTP_OPTS_H + +#include "lwip/opt.h" + +/** + * @defgroup tftp_opts Options + * @ingroup tftp + * @{ + */ + +/** + * Enable TFTP debug messages + */ +#if !defined TFTP_DEBUG || defined __DOXYGEN__ +#define TFTP_DEBUG LWIP_DBG_ON +#endif + +/** + * TFTP server port + */ +#if !defined TFTP_PORT || defined __DOXYGEN__ +#define TFTP_PORT 69 +#endif + +/** + * TFTP timeout + */ +#if !defined TFTP_TIMEOUT_MSECS || defined __DOXYGEN__ +#define TFTP_TIMEOUT_MSECS 10000 +#endif + +/** + * Max. number of retries when a file is read from server + */ +#if !defined TFTP_MAX_RETRIES || defined __DOXYGEN__ +#define TFTP_MAX_RETRIES 5 +#endif + +/** + * TFTP timer cyclic interval + */ +#if !defined TFTP_TIMER_MSECS || defined __DOXYGEN__ +#define TFTP_TIMER_MSECS 50 +#endif + +/** + * Max. length of TFTP filename + */ +#if !defined TFTP_MAX_FILENAME_LEN || defined __DOXYGEN__ +#define TFTP_MAX_FILENAME_LEN 20 +#endif + +/** + * Max. length of TFTP mode + */ +#if !defined TFTP_MAX_MODE_LEN || defined __DOXYGEN__ +#define TFTP_MAX_MODE_LEN 7 +#endif + +/** + * @} + */ + +#endif /* LWIP_HDR_APPS_TFTP_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/apps/tftp_server.h b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/tftp_server.h new file mode 100644 index 0000000..3fbe701 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/apps/tftp_server.h @@ -0,0 +1,94 @@ +/****************************************************************//** + * + * @file tftp_server.h + * + * @author Logan Gunthorpe + * + * @brief Trivial File Transfer Protocol (RFC 1350) + * + * Copyright (c) Deltatee Enterprises Ltd. 2013 + * All rights reserved. + * + ********************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification,are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Logan Gunthorpe + * + */ + +#ifndef LWIP_HDR_APPS_TFTP_SERVER_H +#define LWIP_HDR_APPS_TFTP_SERVER_H + +#include "lwip/apps/tftp_opts.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @ingroup tftp + * TFTP context containing callback functions for TFTP transfers + */ +struct tftp_context { + /** + * Open file for read/write. + * @param fname Filename + * @param mode Mode string from TFTP RFC 1350 (netascii, octet, mail) + * @param write Flag indicating read (0) or write (!= 0) access + * @returns File handle supplied to other functions + */ + void* (*open)(const char* fname, const char* mode, u8_t write); + /** + * Close file handle + * @param handle File handle returned by open() + */ + void (*close)(void* handle); + /** + * Read from file + * @param handle File handle returned by open() + * @param buf Target buffer to copy read data to + * @param bytes Number of bytes to copy to buf + * @returns >= 0: Success; < 0: Error + */ + int (*read)(void* handle, void* buf, int bytes); + /** + * Write to file + * @param handle File handle returned by open() + * @param pbuf PBUF adjusted such that payload pointer points + * to the beginning of write data. In other words, + * TFTP headers are stripped off. + * @returns >= 0: Success; < 0: Error + */ + int (*write)(void* handle, struct pbuf* p); +}; + +err_t tftp_init(const struct tftp_context* ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_TFTP_SERVER_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/arch.h b/sdk/Src/Network/lwip2.0.3/include/lwip/arch.h new file mode 100644 index 0000000..fa93bd4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/arch.h @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_ARCH_H +#define LWIP_HDR_ARCH_H + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + +#include "arch/cc.h" +#include + +/** Temporary: define format string for size_t if not defined in cc.h */ +#ifndef SZT_F +#define SZT_F U32_F +#endif /* SZT_F */ +/** Temporary upgrade helper: define format string for u8_t as hex if not + defined in cc.h */ +#ifndef X8_F +#define X8_F "02x" +#endif /* X8_F */ + +/** Define this to 1 in arch/cc.h of your port if your compiler does not provide + * the limits.h header. You need to define the type limits yourself in this case + * (e.g. INT_MAX). + */ +#ifndef LWIP_NO_LIMITS_H +#define LWIP_NO_LIMITS_H 0 +#endif + +/* Include limits.h? */ +#if !LWIP_NO_LIMITS_H +#include +#endif + +/** C++ const_cast(val) equivalent to remove constness from a value (GCC -Wcast-qual) */ +#ifndef LWIP_CONST_CAST +#define LWIP_CONST_CAST(target_type, val) ((target_type)(val)) +#endif + +/** Get rid of alignment cast warnings (GCC -Wcast-align) */ +#ifndef LWIP_ALIGNMENT_CAST +#define LWIP_ALIGNMENT_CAST(target_type, val) LWIP_CONST_CAST(target_type, val) +#endif + +/** Get rid of warnings related to pointer-to-numeric and vice-versa casts, + * e.g. "conversion from 'u8_t' to 'void *' of greater size" + */ +#ifndef LWIP_PTR_NUMERIC_CAST +#define LWIP_PTR_NUMERIC_CAST(target_type, val) LWIP_CONST_CAST(target_type, val) +#endif + +/** Allocates a memory buffer of specified size that is of sufficient size to align + * its start address using LWIP_MEM_ALIGN. + * You can declare your own version here e.g. to enforce alignment without adding + * trailing padding bytes (see LWIP_MEM_ALIGN_BUFFER) or your own section placement + * requirements.\n + * e.g. if you use gcc and need 32 bit alignment:\n + * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] \_\_attribute\_\_((aligned(4)))\n + * or more portable:\n + * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u32_t variable_name[(size + sizeof(u32_t) - 1) / sizeof(u32_t)] + */ +#ifndef LWIP_DECLARE_MEMORY_ALIGNED +#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)] +#endif + +/** Calculate memory size for an aligned buffer - returns the next highest + * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and + * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4). + */ +#ifndef LWIP_MEM_ALIGN_SIZE +#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U)) +#endif + +/** Calculate safe memory size for an aligned buffer when using an unaligned + * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the + * start (e.g. if buffer is u8_t[] and actual data will be u32_t*) + */ +#ifndef LWIP_MEM_ALIGN_BUFFER +#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1U)) +#endif + +/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT + * so that ADDR % MEM_ALIGNMENT == 0 + */ +#ifndef LWIP_MEM_ALIGN +#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) +#endif +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PACK_STRUCT_BEGIN +#define PACK_STRUCT_BEGIN +#endif /* PACK_STRUCT_BEGIN */ + +#ifndef PACK_STRUCT_END +#define PACK_STRUCT_END +#endif /* PACK_STRUCT_END */ + +#ifndef PACK_STRUCT_STRUCT +#define PACK_STRUCT_STRUCT +#endif /* PACK_STRUCT_STRUCT */ + +#ifndef PACK_STRUCT_FIELD +#define PACK_STRUCT_FIELD(x) x +#endif /* PACK_STRUCT_FIELD */ + +/* Used for struct fields of u8_t, + * where some compilers warn that packing is not necessary */ +#ifndef PACK_STRUCT_FLD_8 +#define PACK_STRUCT_FLD_8(x) PACK_STRUCT_FIELD(x) +#endif /* PACK_STRUCT_FLD_8 */ + +/* Used for struct fields of that are packed structs themself, + * where some compilers warn that packing is not necessary */ +#ifndef PACK_STRUCT_FLD_S +#define PACK_STRUCT_FLD_S(x) PACK_STRUCT_FIELD(x) +#endif /* PACK_STRUCT_FLD_S */ + + +#ifndef LWIP_UNUSED_ARG +#define LWIP_UNUSED_ARG(x) (void)x +#endif /* LWIP_UNUSED_ARG */ + + +#ifdef LWIP_PROVIDE_ERRNO + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#ifdef ENOMEM +#undef ENOMEM +#endif +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#ifdef EINVAL +#undef EINVAL +#endif +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#ifdef EDOM +#undef EDOM +#endif +#define EDOM 33 /* Math argument out of domain of func */ +#ifdef ERANGE +#undef ERANGE +#endif +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#ifdef EILSEQ +#undef EILSEQ +#endif +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#ifndef errno +extern int errno; +#endif + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_ARCH_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/autoip.h b/sdk/Src/Network/lwip2.0.3/include/lwip/autoip.h new file mode 100644 index 0000000..5c6d13e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/autoip.h @@ -0,0 +1,99 @@ +/** + * @file + * + * AutoIP Automatic LinkLocal IP Configuration + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + */ + +#ifndef LWIP_HDR_AUTOIP_H +#define LWIP_HDR_AUTOIP_H + +#include "lwip/opt.h" + +#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +/* #include "lwip/udp.h" */ +#include "lwip/etharp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** AutoIP Timing */ +#define AUTOIP_TMR_INTERVAL 100 +#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL) + +/** AutoIP state information per netif */ +struct autoip +{ + /** the currently selected, probed, announced or used LL IP-Address */ + ip4_addr_t llipaddr; + /** current AutoIP state machine state */ + u8_t state; + /** sent number of probes or announces, dependent on state */ + u8_t sent_num; + /** ticks to wait, tick is AUTOIP_TMR_INTERVAL long */ + u16_t ttw; + /** ticks until a conflict can be solved by defending */ + u8_t lastconflict; + /** total number of probed/used Link Local IP-Addresses */ + u8_t tried_llipaddr; +}; + + +void autoip_set_struct(struct netif *netif, struct autoip *autoip); +/** Remove a struct autoip previously set to the netif using autoip_set_struct() */ +#define autoip_remove_struct(netif) do { (netif)->autoip = NULL; } while (0) +err_t autoip_start(struct netif *netif); +err_t autoip_stop(struct netif *netif); +void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr); +void autoip_tmr(void); +void autoip_network_changed(struct netif *netif); +u8_t autoip_supplied_address(const struct netif *netif); + +/* for lwIP internal use by ip4.c */ +u8_t autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr); + +#define netif_autoip_data(netif) ((struct autoip*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP)) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV4 && LWIP_AUTOIP */ + +#endif /* LWIP_HDR_AUTOIP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/debug.h b/sdk/Src/Network/lwip2.0.3/include/lwip/debug.h new file mode 100644 index 0000000..fc5755f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/debug.h @@ -0,0 +1,170 @@ +/** + * @file + * Debug messages infrastructure + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_DEBUG_H +#define LWIP_HDR_DEBUG_H + +#include "lwip/arch.h" +#include "lwip/opt.h" + +// #define LWIP_DEBUG +//#define LWIP_NOASSERT + +/** + * @defgroup debugging_levels LWIP_DBG_MIN_LEVEL and LWIP_DBG_TYPES_ON values + * @ingroup lwip_opts_debugmsg + * @{ + */ + +/** @name Debug level (LWIP_DBG_MIN_LEVEL) + * @{ + */ +/** Debug level: ALL messages*/ +#define LWIP_DBG_LEVEL_ALL 0x00 +/** Debug level: Warnings. bad checksums, dropped packets, ... */ +#define LWIP_DBG_LEVEL_WARNING 0x01 +/** Debug level: Serious. memory allocation failures, ... */ +#define LWIP_DBG_LEVEL_SERIOUS 0x02 +/** Debug level: Severe */ +#define LWIP_DBG_LEVEL_SEVERE 0x03 +/** + * @} + */ + +#define LWIP_DBG_MASK_LEVEL 0x03 +/* compatibility define only */ +#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL + +/** @name Enable/disable debug messages completely (LWIP_DBG_TYPES_ON) + * @{ + */ +/** flag for LWIP_DEBUGF to enable that debug message */ +#define LWIP_DBG_ON 0x80U +/** flag for LWIP_DEBUGF to disable that debug message */ +#define LWIP_DBG_OFF 0x00U +/** + * @} + */ + +/** @name Debug message types (LWIP_DBG_TYPES_ON) + * @{ + */ +/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */ +#define LWIP_DBG_TRACE 0x40U +/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */ +#define LWIP_DBG_STATE 0x20U +/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */ +#define LWIP_DBG_FRESH 0x10U +/** flag for LWIP_DEBUGF to halt after printing this debug message */ +#define LWIP_DBG_HALT 0x08U +/** + * @} + */ + +/** + * @} + */ + +/** + * @defgroup lwip_assertions Assertion handling + * @ingroup lwip_opts_debug + * @{ + */ +/** + * LWIP_NOASSERT: Disable LWIP_ASSERT checks: + * To disable assertions define LWIP_NOASSERT in arch/cc.h. + */ +#ifdef __DOXYGEN__ +#define LWIP_NOASSERT +#undef LWIP_NOASSERT +#endif +/** + * @} + */ + +#ifndef LWIP_NOASSERT +#define LWIP_ASSERT(message, assertion) do { if (!(assertion)) { \ + LWIP_PLATFORM_ASSERT(message); }} while(0) +#ifndef LWIP_PLATFORM_ASSERT +#error "If you want to use LWIP_ASSERT, LWIP_PLATFORM_ASSERT(message) needs to be defined in your arch/cc.h" +#endif +#else /* LWIP_NOASSERT */ +#define LWIP_ASSERT(message, assertion) +#endif /* LWIP_NOASSERT */ + +#ifndef LWIP_ERROR +#ifndef LWIP_NOASSERT +#define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_ASSERT(message) +#elif defined LWIP_DEBUG +#define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_DIAG((message)) +#else +#define LWIP_PLATFORM_ERROR(message) +#endif + +/* if "expression" isn't true, then print "message" and execute "handler" expression */ +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + LWIP_PLATFORM_ERROR(message); handler;}} while(0) +#endif /* LWIP_ERROR */ + +/** Enable debug message printing, but only if debug message type is enabled + * AND is of correct type AND is at least LWIP_DBG_LEVEL. + */ +#ifdef __DOXYGEN__ +#define LWIP_DEBUG +#undef LWIP_DEBUG +#endif + +#ifdef LWIP_DEBUG +#ifndef LWIP_PLATFORM_DIAG +#error "If you want to use LWIP_DEBUG, LWIP_PLATFORM_DIAG(message) needs to be defined in your arch/cc.h" +#endif +#define LWIP_DEBUGF(debug, message) do { \ + if ( \ + ((debug) & LWIP_DBG_ON) && \ + ((debug) & LWIP_DBG_TYPES_ON) && \ + ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \ + LWIP_PLATFORM_DIAG(message); \ + if ((debug) & LWIP_DBG_HALT) { \ + while(1); \ + } \ + } \ + } while(0) + +#else /* LWIP_DEBUG */ +#define LWIP_DEBUGF(debug, message) +#endif /* LWIP_DEBUG */ + +#endif /* LWIP_HDR_DEBUG_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/def.h b/sdk/Src/Network/lwip2.0.3/include/lwip/def.h new file mode 100644 index 0000000..9a649b4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/def.h @@ -0,0 +1,153 @@ +/** + * @file + * various utility macros + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_DEF_H +#define LWIP_HDR_DEF_H + +/* arch.h might define NULL already */ +#include "lwip/arch.h" +#include "lwip/opt.h" +#if LWIP_PERF +#include "arch/perf.h" +#else /* LWIP_PERF */ +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ +#endif /* LWIP_PERF */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y)) + +/* Get the number of entries in an array ('x' must NOT be a pointer!) */ +#define LWIP_ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0])) + +/** Create u32_t value from bytes */ +#define LWIP_MAKEU32(a,b,c,d) (((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff)) + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#if BYTE_ORDER == BIG_ENDIAN +#define lwip_htons(x) (x) +#define lwip_ntohs(x) (x) +#define lwip_htonl(x) (x) +#define lwip_ntohl(x) (x) +#define PP_HTONS(x) (x) +#define PP_NTOHS(x) (x) +#define PP_HTONL(x) (x) +#define PP_NTOHL(x) (x) +#else /* BYTE_ORDER != BIG_ENDIAN */ +#ifndef lwip_htons +u16_t lwip_htons(u16_t x); +#endif +#define lwip_ntohs(x) lwip_htons(x) + +#ifndef lwip_htonl +u32_t lwip_htonl(u32_t x); +#endif +#define lwip_ntohl(x) lwip_htonl(x) + +/* These macros should be calculated by the preprocessor and are used + with compile-time constants only (so that there is no little-endian + overhead at runtime). */ +#define PP_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8)) +#define PP_NTOHS(x) PP_HTONS(x) +#define PP_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \ + (((x) & 0x0000ff00UL) << 8) | \ + (((x) & 0x00ff0000UL) >> 8) | \ + (((x) & 0xff000000UL) >> 24)) +#define PP_NTOHL(x) PP_HTONL(x) +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +/* Provide usual function names as macros for users, but this can be turned off */ +#ifndef LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS +#ifdef htons +#undef htons +#endif /* htons */ +#ifdef htonl +#undef htonl +#endif /* htonl */ +#ifdef ntohs +#undef ntohs +#endif /* ntohs */ +#ifdef ntohl +#undef ntohl +#endif /* ntohl */ +#define htons(x) lwip_htons(x) +#define ntohs(x) lwip_ntohs(x) +#define htonl(x) lwip_htonl(x) +#define ntohl(x) lwip_ntohl(x) +#endif + +/* Functions that are not available as standard implementations. + * In cc.h, you can #define these to implementations available on + * your platform to save some code bytes if you use these functions + * in your application, too. + */ + +#ifndef lwip_itoa +/* This can be #defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform */ +void lwip_itoa(char* result, size_t bufsize, int number); +#endif +#ifndef lwip_strnicmp +/* This can be #defined to strnicmp() or strncasecmp() depending on your platform */ +int lwip_strnicmp(const char* str1, const char* str2, size_t len); +#endif +#ifndef lwip_stricmp +/* This can be #defined to stricmp() or strcasecmp() depending on your platform */ +int lwip_stricmp(const char* str1, const char* str2); +#endif +#ifndef lwip_strnstr +/* This can be #defined to strnstr() depending on your platform */ +char* lwip_strnstr(const char* buffer, const char* token, size_t n); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_DEF_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/dhcp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/dhcp.h new file mode 100644 index 0000000..e0e59f6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/dhcp.h @@ -0,0 +1,143 @@ +/** + * @file + * DHCP client API + */ + +/* + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Leon Woestenberg + * + */ +#ifndef LWIP_HDR_DHCP_H +#define LWIP_HDR_DHCP_H + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** period (in seconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_SECS 10//60 +/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL) +/** period (in milliseconds) of the application calling dhcp_fine_tmr() */ +#define DHCP_FINE_TIMER_MSECS 500 + +#define DHCP_BOOT_FILE_LEN 128U + +/* AutoIP cooperation flags (struct dhcp.autoip_coop_state) */ +typedef enum { + DHCP_AUTOIP_COOP_STATE_OFF = 0, + DHCP_AUTOIP_COOP_STATE_ON = 1 +} dhcp_autoip_coop_state_enum_t; + +struct dhcp +{ + /** transaction identifier of last sent request */ + u32_t xid; + /** incoming msg */ + struct dhcp_msg *msg_in; + /** track PCB allocation state */ + u8_t pcb_allocated; + /** current DHCP state machine state */ + u8_t state; + /** retries of current request */ + u8_t tries; +#if LWIP_DHCP_AUTOIP_COOP + u8_t autoip_coop_state; +#endif + u8_t subnet_mask_given; + + struct pbuf *p_out; /* pbuf of outcoming msg */ + struct dhcp_msg *msg_out; /* outgoing msg */ + u16_t options_out_len; /* outgoing msg options length */ + u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */ + u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */ + u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */ + u16_t t1_renew_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next renew try */ + u16_t t2_rebind_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next rebind try */ + u16_t lease_used; /* #ticks with period DHCP_COARSE_TIMER_SECS since last received DHCP ack */ + u16_t t0_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for lease time */ + ip_addr_t server_ip_addr; /* dhcp server address that offered this lease (ip_addr_t because passed to UDP) */ + ip4_addr_t offered_ip_addr; + ip4_addr_t offered_sn_mask; + ip4_addr_t offered_gw_addr; + + u32_t offered_t0_lease; /* lease period (in seconds) */ + u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */ + u32_t offered_t2_rebind; /* recommended rebind time (usually 87.5 of lease period) */ +#if LWIP_DHCP_BOOTP_FILE + ip4_addr_t offered_si_addr; + char boot_file_name[DHCP_BOOT_FILE_LEN]; +#endif /* LWIP_DHCP_BOOTPFILE */ +}; + + +void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp); +/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */ +#define dhcp_remove_struct(netif) netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, NULL) +void dhcp_cleanup(struct netif *netif); +err_t dhcp_start(struct netif *netif); +err_t dhcp_renew(struct netif *netif); +err_t dhcp_release(struct netif *netif); +void dhcp_stop(struct netif *netif); +void dhcp_inform(struct netif *netif); +void dhcp_network_changed(struct netif *netif); +#if DHCP_DOES_ARP_CHECK +void dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr); +#endif +u8_t dhcp_supplied_address(const struct netif *netif); +/* to be called every minute */ +void dhcp_coarse_tmr(void); +/* to be called every half second */ +void dhcp_fine_tmr(void); + +#if LWIP_DHCP_GET_NTP_SRV +/** This function must exist, in other to add offered NTP servers to + * the NTP (or SNTP) engine. + * See LWIP_DHCP_MAX_NTP_SERVERS */ +extern void dhcp_set_ntp_servers(u8_t num_ntp_servers, const ip4_addr_t* ntp_server_addrs); +#endif /* LWIP_DHCP_GET_NTP_SRV */ + +#define netif_dhcp_data(netif) ((struct dhcp*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP)) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DHCP */ + +#endif /*LWIP_HDR_DHCP_H*/ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/dhcp6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/dhcp6.h new file mode 100644 index 0000000..a6a4412 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/dhcp6.h @@ -0,0 +1,58 @@ +/** + * @file + * + * IPv6 address autoconfiguration as per RFC 4862. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * IPv6 address autoconfiguration as per RFC 4862. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_IP6_DHCP6_H +#define LWIP_HDR_IP6_DHCP6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + + +struct dhcp6 +{ + /*@todo: implement DHCP6*/ +}; + +#endif /* LWIP_IPV6_DHCP6 */ + +#endif /* LWIP_HDR_IP6_DHCP6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/dns.h b/sdk/Src/Network/lwip2.0.3/include/lwip/dns.h new file mode 100644 index 0000000..23fd4f5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/dns.h @@ -0,0 +1,130 @@ +/** + * @file + * DNS API + */ + +/** + * lwip DNS resolver header file. + + * Author: Jim Pettinato + * April 2007 + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_HDR_DNS_H +#define LWIP_HDR_DNS_H + +#include "lwip/opt.h" + +#if LWIP_DNS + +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** DNS timer period */ +#define DNS_TMR_INTERVAL 1000 + +/* DNS resolve types: */ +#define LWIP_DNS_ADDRTYPE_IPV4 0 +#define LWIP_DNS_ADDRTYPE_IPV6 1 +#define LWIP_DNS_ADDRTYPE_IPV4_IPV6 2 /* try to resolve IPv4 first, try IPv6 if IPv4 fails only */ +#define LWIP_DNS_ADDRTYPE_IPV6_IPV4 3 /* try to resolve IPv6 first, try IPv4 if IPv6 fails only */ +#if LWIP_IPV4 && LWIP_IPV6 +#ifndef LWIP_DNS_ADDRTYPE_DEFAULT +#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV4_IPV6 +#endif +#elif LWIP_IPV4 +#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV4 +#else +#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV6 +#endif + +#if DNS_LOCAL_HOSTLIST +/** struct used for local host-list */ +struct local_hostlist_entry { + /** static hostname */ + const char *name; + /** static host address in network byteorder */ + ip_addr_t addr; + struct local_hostlist_entry *next; +}; +#define DNS_LOCAL_HOSTLIST_ELEM(name, addr_init) {name, addr_init, NULL} +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN +#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN DNS_MAX_NAME_LENGTH +#endif +#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1)) +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#endif /* DNS_LOCAL_HOSTLIST */ + +#if LWIP_IPV4 +extern const ip_addr_t dns_mquery_v4group; +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 +extern const ip_addr_t dns_mquery_v6group; +#endif /* LWIP_IPV6 */ + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +typedef void (*dns_found_callback)(const char *name, const ip_addr_t *ipaddr, void *callback_arg); + +void dns_init(void); +void dns_tmr(void); +void dns_setserver(u8_t numdns, const ip_addr_t *dnsserver); +const ip_addr_t* dns_getserver(u8_t numdns); +err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr, + dns_found_callback found, void *callback_arg); +err_t dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, + dns_found_callback found, void *callback_arg, + u8_t dns_addrtype); + + +#if DNS_LOCAL_HOSTLIST +size_t dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg); +err_t dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype); +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +int dns_local_removehost(const char *hostname, const ip_addr_t *addr); +err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr); +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#endif /* DNS_LOCAL_HOSTLIST */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS */ + +#endif /* LWIP_HDR_DNS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/err.h b/sdk/Src/Network/lwip2.0.3/include/lwip/err.h new file mode 100644 index 0000000..c4c5b37 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/err.h @@ -0,0 +1,119 @@ +/** + * @file + * lwIP Error codes + */ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_ERR_H +#define LWIP_HDR_ERR_H + +#include "lwip/opt.h" +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup infrastructure_errors Error codes + * @ingroup infrastructure + * @{ + */ + +/** Define LWIP_ERR_T in cc.h if you want to use + * a different type for your platform (must be signed). */ +#ifdef LWIP_ERR_T +typedef LWIP_ERR_T err_t; +#else /* LWIP_ERR_T */ +typedef s8_t err_t; +#endif /* LWIP_ERR_T*/ +#if 0 +/** Definitions for error constants. */ +typedef enum { +/** No error, everything OK. */ + ERR_OK = 0, +/** Out of memory error. */ + ERR_MEM = -1, +/** Buffer error. */ + ERR_BUF = -2, +/** Timeout. */ + ERR_TIMEOUT = -3, +/** Routing problem. */ + ERR_RTE = -4, +/** Operation in progress */ + ERR_INPROGRESS = -5, +/** Illegal value. */ + ERR_VAL = -6, +/** Operation would block. */ + ERR_WOULDBLOCK = -7, +/** Address in use. */ + ERR_USE = -8, +/** Already connecting. */ + ERR_ALREADY = -9, +/** Conn already established.*/ + ERR_ISCONN = -10, +/** Not connected. */ + ERR_CONN = -11, +/** Low-level netif error */ + ERR_IF = -12, + +/** Connection aborted. */ + ERR_ABRT = -13, +/** Connection reset. */ + ERR_RST = -14, +/** Connection closed. */ + ERR_CLSD = -15, +/** Illegal argument. */ + ERR_ARG = -16 +} err_enum_t; +#endif +#define ERR_IS_FATAL(e) ((e) <= ERR_ABRT) + +/** + * @} + */ + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err); +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#if !NO_SYS +int err_to_errno(err_t err); +#endif /* !NO_SYS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_ERR_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/errno.h b/sdk/Src/Network/lwip2.0.3/include/lwip/errno.h new file mode 100644 index 0000000..641cffb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/errno.h @@ -0,0 +1,193 @@ +/** + * @file + * Posix Errno defines + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_ERRNO_H +#define LWIP_HDR_ERRNO_H + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LWIP_PROVIDE_ERRNO + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#ifndef errno +extern int errno; +#endif + +#else /* LWIP_PROVIDE_ERRNO */ + +/* Define LWIP_ERRNO_INCLUDE to to include the error defines here */ +#ifdef LWIP_ERRNO_INCLUDE +#include LWIP_ERRNO_INCLUDE +#endif /* LWIP_ERRNO_INCLUDE */ + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_ERRNO_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/etharp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/etharp.h new file mode 100644 index 0000000..0292284 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/etharp.h @@ -0,0 +1,128 @@ +/** + * @file + * Ethernet output function - handles OUTGOING ethernet level traffic, implements + * ARP resolving. + * To be used in most low-level netif implementations + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef LWIP_HDR_NETIF_ETHARP_H +#define LWIP_HDR_NETIF_ETHARP_H + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip4_addr.h" +#include "lwip/netif.h" +#include "lwip/ip4.h" +#include "lwip/prot/ethernet.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/prot/etharp.h" + +/** 1 seconds period */ +#define ARP_TMR_INTERVAL 1000 + +#if ARP_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct etharp_q_entry { + struct etharp_q_entry *next; + struct pbuf *p; +}; +#endif /* ARP_QUEUEING */ + +#define etharp_init() /* Compatibility define, no init needed. */ +void etharp_tmr(void); +s8_t etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr, + struct eth_addr **eth_ret, const ip4_addr_t **ip_ret); +u8_t etharp_get_entry(u8_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret); +err_t etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr); +err_t etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q); +err_t etharp_request(struct netif *netif, const ip4_addr_t *ipaddr); +/** For Ethernet network interfaces, we might want to send "gratuitous ARP"; + * this is an ARP packet sent by a node in order to spontaneously cause other + * nodes to update an entry in their ARP cache. + * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ +#define etharp_gratuitous(netif) etharp_request((netif), netif_ip4_addr(netif)) +void etharp_cleanup_netif(struct netif *netif); + +/** + * Update (or insert) a IP/MAC address pair in the ARP cache. + * + * If a pending entry is resolved, any queued packets will be sent + * at this point. + * + * @param netif netif related to this entry (used for NETIF_ADDRHINT) + * @param ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags See @ref etharp_state + * + * @return + * - ERR_OK Successfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +err_t +etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags); + + +#if ETHARP_SUPPORT_STATIC_ENTRIES +err_t etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr); +err_t etharp_remove_static_entry(const ip4_addr_t *ipaddr); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#endif /* LWIP_IPV4 && LWIP_ARP */ + +void etharp_input(struct pbuf *p, struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ARP || LWIP_ETHERNET */ + +#endif /* LWIP_HDR_NETIF_ETHARP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ethip6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ethip6.h new file mode 100644 index 0000000..f393c9c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ethip6.h @@ -0,0 +1,68 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_ETHIP6_H +#define LWIP_HDR_ETHIP6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +err_t ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ + +#endif /* LWIP_HDR_ETHIP6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/icmp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/icmp.h new file mode 100644 index 0000000..d0d67b6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/icmp.h @@ -0,0 +1,110 @@ +/** + * @file + * ICMP API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_ICMP_H +#define LWIP_HDR_ICMP_H + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/prot/icmp.h" + +#if LWIP_IPV6 && LWIP_ICMP6 +#include "lwip/icmp6.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** ICMP destination unreachable codes */ +enum icmp_dur_type { + /** net unreachable */ + ICMP_DUR_NET = 0, + /** host unreachable */ + ICMP_DUR_HOST = 1, + /** protocol unreachable */ + ICMP_DUR_PROTO = 2, + /** port unreachable */ + ICMP_DUR_PORT = 3, + /** fragmentation needed and DF set */ + ICMP_DUR_FRAG = 4, + /** source route failed */ + ICMP_DUR_SR = 5 +}; + +/** ICMP time exceeded codes */ +enum icmp_te_type { + /** time to live exceeded in transit */ + ICMP_TE_TTL = 0, + /** fragment reassembly time exceeded */ + ICMP_TE_FRAG = 1 +}; + +#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +void icmp_input(struct pbuf *p, struct netif *inp); +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#endif /* LWIP_IPV4 && LWIP_ICMP */ + +#if LWIP_IPV4 && LWIP_IPV6 +#if LWIP_ICMP && LWIP_ICMP6 +#define icmp_port_unreach(isipv6, pbuf) ((isipv6) ? \ + icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) : \ + icmp_dest_unreach(pbuf, ICMP_DUR_PORT)) +#elif LWIP_ICMP +#define icmp_port_unreach(isipv6, pbuf) do{ if(!(isipv6)) { icmp_dest_unreach(pbuf, ICMP_DUR_PORT);}}while(0) +#elif LWIP_ICMP6 +#define icmp_port_unreach(isipv6, pbuf) do{ if(isipv6) { icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT);}}while(0) +#else +#define icmp_port_unreach(isipv6, pbuf) +#endif +#elif LWIP_IPV6 && LWIP_ICMP6 +#define icmp_port_unreach(isipv6, pbuf) icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) +#elif LWIP_IPV4 && LWIP_ICMP +#define icmp_port_unreach(isipv6, pbuf) icmp_dest_unreach(pbuf, ICMP_DUR_PORT) +#else /* (LWIP_IPV6 && LWIP_ICMP6) || (LWIP_IPV4 && LWIP_ICMP) */ +#define icmp_port_unreach(isipv6, pbuf) +#endif /* (LWIP_IPV6 && LWIP_ICMP6) || (LWIP_IPV4 && LWIP_ICMP) LWIP_IPV4*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_ICMP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/icmp6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/icmp6.h new file mode 100644 index 0000000..a5f812f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/icmp6.h @@ -0,0 +1,70 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_ICMP6_H +#define LWIP_HDR_ICMP6_H + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" +#include "lwip/prot/icmp6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +void icmp6_input(struct pbuf *p, struct netif *inp); +void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c); +void icmp6_packet_too_big(struct pbuf *p, u32_t mtu); +void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c); +void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer); + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* LWIP_HDR_ICMP6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/igmp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/igmp.h new file mode 100644 index 0000000..ca52a2d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/igmp.h @@ -0,0 +1,115 @@ +/** + * @file + * IGMP API + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 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 CITEL Technologies Ltd 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 CITEL TECHNOLOGIES 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 CITEL TECHNOLOGIES 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 a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +#ifndef LWIP_HDR_IGMP_H +#define LWIP_HDR_IGMP_H + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/pbuf.h" + +#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* IGMP timer */ +#define IGMP_TMR_INTERVAL 100 /* Milliseconds */ +#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL) +#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL) + +/* Compatibility defines (don't use for new code) */ +#define IGMP_DEL_MAC_FILTER NETIF_DEL_MAC_FILTER +#define IGMP_ADD_MAC_FILTER NETIF_ADD_MAC_FILTER + +/** + * igmp group structure - there is + * a list of groups for each interface + * these should really be linked from the interface, but + * if we keep them separate we will not affect the lwip original code + * too much + * + * There will be a group for the all systems group address but this + * will not run the state machine as it is used to kick off reports + * from all the other groups + */ +struct igmp_group { + /** next link */ + struct igmp_group *next; + /** multicast address */ + ip4_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting, negative is OFF */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/* Prototypes */ +void igmp_init(void); +err_t igmp_start(struct netif *netif); +err_t igmp_stop(struct netif *netif); +void igmp_report_groups(struct netif *netif); +struct igmp_group *igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr); +void igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest); +err_t igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr); +err_t igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr); +err_t igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr); +err_t igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr); +void igmp_tmr(void); + +/** @ingroup igmp + * Get list head of IGMP groups for netif. + * Note: The allsystems group IP is contained in the list as first entry. + * @see @ref netif_set_igmp_mac_filter() + */ +#define netif_igmp_data(netif) ((struct igmp_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP)) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV4 && LWIP_IGMP */ + +#endif /* LWIP_HDR_IGMP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/inet.h b/sdk/Src/Network/lwip2.0.3/include/lwip/inet.h new file mode 100644 index 0000000..960c72d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/inet.h @@ -0,0 +1,176 @@ +/** + * @file + * This file (together with sockets.h) aims to provide structs and functions from + * - arpa/inet.h + * - netinet/in.h + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_INET_H +#define LWIP_HDR_INET_H + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" +#include "wm_sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +/* If your port already typedef's in_addr_t, define IN_ADDR_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(in_addr_t) && !defined(IN_ADDR_T_DEFINED) +typedef u32_t in_addr_t; +#endif + +struct in_addr { + in_addr_t s_addr; +}; + +struct in6_addr { + union { + u32_t u32_addr[4]; + u8_t u8_addr[16]; + } un; +#define s6_addr un.u8_addr +}; + +/** 255.255.255.255 */ +#define INADDR_NONE IPADDR_NONE +/** 127.0.0.1 */ +#define INADDR_LOOPBACK IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define INADDR_ANY IPADDR_ANY +/** 255.255.255.255 */ +#define INADDR_BROADCAST IPADDR_BROADCAST + +/** This macro can be used to initialize a variable of type struct in6_addr + to the IPv6 wildcard address. */ +#define IN6ADDR_ANY_INIT {{{0,0,0,0}}} +/** This macro can be used to initialize a variable of type struct in6_addr + to the IPv6 loopback address. */ +#define IN6ADDR_LOOPBACK_INIT {{{0,0,0,PP_HTONL(1)}}} +/** This variable is initialized by the system to contain the wildcard IPv6 address. */ +extern const struct in6_addr in6addr_any; +#endif +/* Definitions of the bits in an (IPv4) Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IN_CLASSA(a) IP_CLASSA(a) +#define IN_CLASSA_NET IP_CLASSA_NET +#define IN_CLASSA_NSHIFT IP_CLASSA_NSHIFT +#define IN_CLASSA_HOST IP_CLASSA_HOST +#define IN_CLASSA_MAX IP_CLASSA_MAX + +#define IN_CLASSB(b) IP_CLASSB(b) +#define IN_CLASSB_NET IP_CLASSB_NET +#define IN_CLASSB_NSHIFT IP_CLASSB_NSHIFT +#define IN_CLASSB_HOST IP_CLASSB_HOST +#define IN_CLASSB_MAX IP_CLASSB_MAX + +#define IN_CLASSC(c) IP_CLASSC(c) +#define IN_CLASSC_NET IP_CLASSC_NET +#define IN_CLASSC_NSHIFT IP_CLASSC_NSHIFT +#define IN_CLASSC_HOST IP_CLASSC_HOST +#define IN_CLASSC_MAX IP_CLASSC_MAX + +#define IN_CLASSD(d) IP_CLASSD(d) +#define IN_CLASSD_NET IP_CLASSD_NET /* These ones aren't really */ +#define IN_CLASSD_NSHIFT IP_CLASSD_NSHIFT /* net and host fields, but */ +#define IN_CLASSD_HOST IP_CLASSD_HOST /* routing needn't know. */ +#define IN_CLASSD_MAX IP_CLASSD_MAX + +#define IN_MULTICAST(a) IP_MULTICAST(a) + +#define IN_EXPERIMENTAL(a) IP_EXPERIMENTAL(a) +#define IN_BADCLASS(a) IP_BADCLASS(a) + +#define IN_LOOPBACKNET IP_LOOPBACKNET + +#if 0 +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN IP4ADDR_STRLEN_MAX +#endif +#if LWIP_IPV6 +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN IP6ADDR_STRLEN_MAX +#endif +#endif +#endif + +#if LWIP_IPV4 + +#define inet_addr_from_ip4addr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr)) +#define inet_addr_to_ip4addr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr)) +/* ATTENTION: the next define only works because both s_addr and ip4_addr_t are an u32_t effectively! */ +#define inet_addr_to_ip4addr_p(target_ip4addr_p, source_inaddr) ((target_ip4addr_p) = (ip4_addr_t*)&((source_inaddr)->s_addr)) + +#if 0 +/* directly map this to the lwip internal functions */ +#define inet_addr(cp) ipaddr_addr(cp) +#define inet_aton(cp, addr) ip4addr_aton(cp, (ip4_addr_t*)addr) +#define inet_ntoa(addr) ip4addr_ntoa((const ip4_addr_t*)&(addr)) +#endif +#define inet_ntoa_r(addr, buf, buflen) ip4addr_ntoa_r((const ip4_addr_t*)&(addr), buf, buflen) + +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \ + (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \ + (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \ + (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];} +#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \ + (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \ + (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \ + (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];} +/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */ +#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr)) + +/* directly map this to the lwip internal functions */ +#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr) +#define inet6_ntoa(addr) ip6addr_ntoa((const ip6_addr_t*)&(addr)) +#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((const ip6_addr_t*)&(addr), buf, buflen) + +#endif /* LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_INET_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/inet_chksum.h b/sdk/Src/Network/lwip2.0.3/include/lwip/inet_chksum.h new file mode 100644 index 0000000..799cab5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/inet_chksum.h @@ -0,0 +1,105 @@ +/** + * @file + * IP checksum calculation functions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_INET_CHKSUM_H +#define LWIP_HDR_INET_CHKSUM_H + +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +/** Swap the bytes in an u16_t: much like lwip_htons() for little-endian */ +#ifndef SWAP_BYTES_IN_WORD +#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8) +#endif /* SWAP_BYTES_IN_WORD */ + +/** Split an u32_t in two u16_ts and add them up */ +#ifndef FOLD_U32T +#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL)) +#endif + +#if LWIP_CHECKSUM_ON_COPY +/** Function-like macro: same as MEMCPY but returns the checksum of copied data + as u16_t */ +# ifndef LWIP_CHKSUM_COPY +# define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len) +# ifndef LWIP_CHKSUM_COPY_ALGORITHM +# define LWIP_CHKSUM_COPY_ALGORITHM 1 +# endif /* LWIP_CHKSUM_COPY_ALGORITHM */ +# else /* LWIP_CHKSUM_COPY */ +# define LWIP_CHKSUM_COPY_ALGORITHM 0 +# endif /* LWIP_CHKSUM_COPY */ +#else /* LWIP_CHECKSUM_ON_COPY */ +# define LWIP_CHKSUM_COPY_ALGORITHM 0 +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(const void *dataptr, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +#if LWIP_CHKSUM_COPY_ALGORITHM +u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len); +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ + +#if LWIP_IPV4 +u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + const ip4_addr_t *src, const ip4_addr_t *dest); +u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, + u16_t proto_len, u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest); +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + const ip6_addr_t *src, const ip6_addr_t *dest); +u16_t ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest); +#endif /* LWIP_IPV6 */ + + +u16_t ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + const ip_addr_t *src, const ip_addr_t *dest); +u16_t ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_INET_H */ + diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/init.h b/sdk/Src/Network/lwip2.0.3/include/lwip/init.h new file mode 100644 index 0000000..721bb57 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/init.h @@ -0,0 +1,100 @@ +/** + * @file + * lwIP initialization API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_INIT_H +#define LWIP_HDR_INIT_H + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup lwip_version Version + * @ingroup lwip + * @{ + */ + +/** X.x.x: Major version of the stack */ +#define LWIP_VERSION_MAJOR 2 +/** x.X.x: Minor version of the stack */ +#define LWIP_VERSION_MINOR 0 +/** x.x.X: Revision of the stack */ +#define LWIP_VERSION_REVISION 3 +/** For release candidates, this is set to 1..254 + * For official releases, this is set to 255 (LWIP_RC_RELEASE) + * For development versions (Git), this is set to 0 (LWIP_RC_DEVELOPMENT) */ +#define LWIP_VERSION_RC LWIP_RC_RELEASE + +/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */ +#define LWIP_RC_RELEASE 255 +/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for Git versions */ +#define LWIP_RC_DEVELOPMENT 0 + +#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE) +#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT) +#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT)) + +/* Some helper defines to get a version string */ +#define LWIP_VERSTR2(x) #x +#define LWIP_VERSTR(x) LWIP_VERSTR2(x) +#if LWIP_VERSION_IS_RELEASE +#define LWIP_VERSION_STRING_SUFFIX "" +#elif LWIP_VERSION_IS_DEVELOPMENT +#define LWIP_VERSION_STRING_SUFFIX "d" +#else +#define LWIP_VERSION_STRING_SUFFIX "rc" LWIP_VERSTR(LWIP_VERSION_RC) +#endif + +/** Provides the version of the stack */ +#define LWIP_VERSION (((u32_t)LWIP_VERSION_MAJOR) << 24 | ((u32_t)LWIP_VERSION_MINOR) << 16 | \ + ((u32_t)LWIP_VERSION_REVISION) << 8 | ((u32_t)LWIP_VERSION_RC)) +/** Provides the version of the stack as string */ +#define LWIP_VERSION_STRING LWIP_VERSTR(LWIP_VERSION_MAJOR) "." LWIP_VERSTR(LWIP_VERSION_MINOR) "." LWIP_VERSTR(LWIP_VERSION_REVISION) LWIP_VERSION_STRING_SUFFIX + +/** + * @} + */ + +/* Modules initialization */ +void lwip_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_INIT_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip.h new file mode 100644 index 0000000..90c1a39 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip.h @@ -0,0 +1,319 @@ +/** + * @file + * IP API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_IP_H +#define LWIP_HDR_IP_H + +#include "lwip/opt.h" + +#include "wm_sockets.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" +#include "lwip/ip4.h" +#include "lwip/ip6.h" +#include "lwip/prot/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is passed as the destination address to ip_output_if (not + to ip_output), meaning that an IP header already is constructed + in the pbuf. This is used when TCP retransmits. */ +#define LWIP_IP_HDRINCL NULL + +/** pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ +#ifndef LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX +#define LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p) LWIP_ASSERT("p->ref == 1", (p)->ref == 1) +#endif + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +/** This is the common part of all PCB types. It needs to be at the + beginning of a PCB type definition. It is located here so that + changes to this common part are made in one location instead of + having to change all PCB structs. */ +#define IP_PCB \ + /* ip addresses in network byte order */ \ + ip_addr_t local_ip; \ + ip_addr_t remote_ip; \ + /* Socket options */ \ + u8_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + +struct ip_pcb { +/* Common members of all PCB types */ + IP_PCB; +}; + +/* + * Option flags per-socket. These are the same like SO_XXX in sockets.h + */ +#define SOF_REUSEADDR 0x04U /* allow local address reuse */ +#define SOF_KEEPALIVE 0x08U /* keep connections alive */ +#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ + +/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */ +#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE) + +/** Global variables of this module, kept in a struct for efficient access using base+index. */ +struct ip_globals +{ + /** The interface that accepted the packet for the current callback invocation. */ + struct netif *current_netif; + /** The interface that received the packet for the current callback invocation. */ + struct netif *current_input_netif; +#if LWIP_IPV4 + /** Header of the input packet currently being processed. */ + struct ip_hdr *current_ip4_header; +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 + /** Header of the input IPv6 packet currently being processed. */ + struct ip6_hdr *current_ip6_header; +#endif /* LWIP_IPV6 */ + /** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */ + u16_t current_ip_header_tot_len; + /** Source IP address of current_header */ + ip_addr_t current_iphdr_src; + /** Destination IP address of current_header */ + ip_addr_t current_iphdr_dest; +}; +extern struct ip_globals ip_data; + + +/** Get the interface that accepted the current packet. + * This may or may not be the receiving netif, depending on your netif/network setup. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_netif() (ip_data.current_netif) +/** Get the interface that received the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_input_netif() (ip_data.current_input_netif) +/** Total header length of ip(6)_current_header() (i.e. after this, the UDP/TCP header starts) */ +#define ip_current_header_tot_len() (ip_data.current_ip_header_tot_len) +/** Source IP address of current_header */ +#define ip_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP address of current_header */ +#define ip_current_dest_addr() (&ip_data.current_iphdr_dest) + +#if LWIP_IPV4 && LWIP_IPV6 +/** Get the IPv4 header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip4_current_header() ((const struct ip_hdr*)(ip_data.current_ip4_header)) +/** Get the IPv6 header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip6_current_header() ((const struct ip6_hdr*)(ip_data.current_ip6_header)) +/** Returns TRUE if the current IP input packet is IPv6, FALSE if it is IPv4 */ +#define ip_current_is_v6() (ip6_current_header() != NULL) +/** Source IPv6 address of current_header */ +#define ip6_current_src_addr() (ip_2_ip6(&ip_data.current_iphdr_src)) +/** Destination IPv6 address of current_header */ +#define ip6_current_dest_addr() (ip_2_ip6(&ip_data.current_iphdr_dest)) +/** Get the transport layer protocol */ +#define ip_current_header_proto() (ip_current_is_v6() ? \ + IP6H_NEXTH(ip6_current_header()) :\ + IPH_PROTO(ip4_current_header())) +/** Get the transport layer header */ +#define ip_next_header_ptr() ((const void*)((ip_current_is_v6() ? \ + (const u8_t*)ip6_current_header() : (const u8_t*)ip4_current_header()) + ip_current_header_tot_len())) + +/** Source IP4 address of current_header */ +#define ip4_current_src_addr() (ip_2_ip4(&ip_data.current_iphdr_src)) +/** Destination IP4 address of current_header */ +#define ip4_current_dest_addr() (ip_2_ip4(&ip_data.current_iphdr_dest)) + +#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */ + +/** Get the IPv4 header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip4_current_header() ((const struct ip_hdr*)(ip_data.current_ip4_header)) +/** Always returns FALSE when only supporting IPv4 only */ +#define ip_current_is_v6() 0 +/** Get the transport layer protocol */ +#define ip_current_header_proto() IPH_PROTO(ip4_current_header()) +/** Get the transport layer header */ +#define ip_next_header_ptr() ((const void*)((const u8_t*)ip4_current_header() + ip_current_header_tot_len())) +/** Source IP4 address of current_header */ +#define ip4_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP4 address of current_header */ +#define ip4_current_dest_addr() (&ip_data.current_iphdr_dest) + +#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */ + +/** Get the IPv6 header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip6_current_header() ((const struct ip6_hdr*)(ip_data.current_ip6_header)) +/** Always returns TRUE when only supporting IPv6 only */ +#define ip_current_is_v6() 1 +/** Get the transport layer protocol */ +#define ip_current_header_proto() IP6H_NEXTH(ip6_current_header()) +/** Get the transport layer header */ +#define ip_next_header_ptr() ((const void*)((const u8_t*)ip6_current_header())) +/** Source IP6 address of current_header */ +#define ip6_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP6 address of current_header */ +#define ip6_current_dest_addr() (&ip_data.current_iphdr_dest) + +#endif /* LWIP_IPV6 */ + +/** Union source address of current_header */ +#define ip_current_src_addr() (&ip_data.current_iphdr_src) +/** Union destination address of current_header */ +#define ip_current_dest_addr() (&ip_data.current_iphdr_dest) + +/** Gets an IP pcb option (SOF_* flags) */ +#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt)) +/** Sets an IP pcb option (SOF_* flags) */ +#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt)) +/** Resets an IP pcb option (SOF_* flags) */ +#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt)) +#if LWIP_IPV4 && LWIP_IPV6 +/** + * @ingroup ip + * Output IP packet, netif is selected by source address + */ +#define ip_output(p, src, dest, ttl, tos, proto) \ + (IP_IS_V6(dest) ? \ + ip6_output(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto) : \ + ip4_output(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto)) +/** + * @ingroup ip + * Output IP packet to specified interface + */ +#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \ + (IP_IS_V6(dest) ? \ + ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \ + ip4_output_if(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif)) +/** + * @ingroup ip + * Output IP packet to interface specifying source address + */ +#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \ + (IP_IS_V6(dest) ? \ + ip6_output_if_src(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \ + ip4_output_if_src(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif)) +/** Output IP packet with addr_hint */ +#define ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) \ + (IP_IS_V6(dest) ? \ + ip6_output_hinted(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, addr_hint) : \ + ip4_output_hinted(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, addr_hint)) +/** + * @ingroup ip + * Get netif for address combination. See \ref ip6_route and \ref ip4_route + */ +#define ip_route(src, dest) \ + (IP_IS_V6(dest) ? \ + ip6_route(ip_2_ip6(src), ip_2_ip6(dest)) : \ + ip4_route_src(ip_2_ip4(dest), ip_2_ip4(src))) +/** + * @ingroup ip + * Get netif for IP. + */ +#define ip_netif_get_local_ip(netif, dest) (IP_IS_V6(dest) ? \ + ip6_netif_get_local_ip(netif, ip_2_ip6(dest)) : \ + ip4_netif_get_local_ip(netif)) +#define ip_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip4_debug_print(p)) + +err_t ip_input(struct pbuf *p, struct netif *inp); + +#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */ + +#define ip_output(p, src, dest, ttl, tos, proto) \ + ip4_output(p, src, dest, ttl, tos, proto) +#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \ + ip4_output_if(p, src, dest, ttl, tos, proto, netif) +#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \ + ip4_output_if_src(p, src, dest, ttl, tos, proto, netif) +#define ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) \ + ip4_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) +#define ip_route(src, dest) \ + ip4_route_src(dest, src) +#define ip_netif_get_local_ip(netif, dest) \ + ip4_netif_get_local_ip(netif) +#define ip_debug_print(is_ipv6, p) ip4_debug_print(p) + +#define ip_input ip4_input + +#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */ + +#define ip_output(p, src, dest, ttl, tos, proto) \ + ip6_output(p, src, dest, ttl, tos, proto) +#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \ + ip6_output_if(p, src, dest, ttl, tos, proto, netif) +#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \ + ip6_output_if_src(p, src, dest, ttl, tos, proto, netif) +#define ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) \ + ip6_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) +#define ip_route(src, dest) \ + ip6_route(src, dest) +#define ip_netif_get_local_ip(netif, dest) \ + ip6_netif_get_local_ip(netif, dest) +#define ip_debug_print(is_ipv6, p) ip6_debug_print(p) + +#define ip_input ip6_input + +#endif /* LWIP_IPV6 */ + +#define ip_route_get_local_ip(src, dest, netif, ipaddr) do { \ + (netif) = ip_route(src, dest); \ + (ipaddr) = ip_netif_get_local_ip(netif, dest); \ +}while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP_H */ + + diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip4.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip4.h new file mode 100644 index 0000000..e8ac2bc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip4.h @@ -0,0 +1,111 @@ +/** + * @file + * IPv4 API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_IP4_H +#define LWIP_HDR_IP4_H + +#include "lwip/opt.h" + +#if LWIP_IPV4 + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip4_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" +#include "lwip/prot/ip4.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LWIP_HOOK_IP4_ROUTE_SRC +#define LWIP_IPV4_SRC_ROUTING 1 +#else +#define LWIP_IPV4_SRC_ROUTING 0 +#endif + +/** Currently, the function ip_output_if_opt() is only used with IGMP */ +#define IP_OPTIONS_SEND (LWIP_IPV4 && LWIP_IGMP) + +#define ip_init() /* Compatibility define, no init needed. */ +struct netif *ip4_route(const ip4_addr_t *dest); +#if LWIP_IPV4_SRC_ROUTING +struct netif *ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src); +#else /* LWIP_IPV4_SRC_ROUTING */ +#define ip4_route_src(dest, src) ip4_route(dest) +#endif /* LWIP_IPV4_SRC_ROUTING */ +err_t ip4_input(struct pbuf *p, struct netif *inp); +err_t ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto); +err_t ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif); +err_t ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if IP_OPTIONS_SEND +err_t ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen); +err_t ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen); +#endif /* IP_OPTIONS_SEND */ + +#if LWIP_MULTICAST_TX_OPTIONS +void ip4_set_default_multicast_netif(struct netif* default_multicast_netif); +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + +#define ip4_netif_get_local_ip(netif) (((netif) != NULL) ? netif_ip_addr4(netif) : NULL) + +#if IP_DEBUG +void ip4_debug_print(struct pbuf *p); +#else +#define ip4_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV4 */ + +#endif /* LWIP_HDR_IP_H */ + + diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip4_addr.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip4_addr.h new file mode 100644 index 0000000..6d0ee86 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip4_addr.h @@ -0,0 +1,227 @@ +/** + * @file + * IPv4 address API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_IP4_ADDR_H +#define LWIP_HDR_IP4_ADDR_H + +#include "lwip/opt.h" +#include "lwip/def.h" + +#if LWIP_IPV4 + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +/** This is the aligned version of ip4_addr_t, + used as local variable, on the stack, etc. */ +struct ip4_addr { + u32_t addr; +}; + +/** ip4_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip4_addr_t as well as on ip4_addr_p_t. */ +typedef struct ip4_addr ip4_addr_t; +#endif +/** + * struct ipaddr2 is used in the definition of the ARP packet format in + * order to support compilers that don't have structure packing. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip4_addr2 { + PACK_STRUCT_FIELD(u16_t addrw[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Forward declaration to not include netif.h */ +struct netif; +#if 0 +/** 255.255.255.255 */ +#define IPADDR_NONE ((u32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((u32_t)0x00000000UL) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ((u32_t)0xffffffffUL) +#endif +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IP_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0) +#define IP_CLASSA_NET 0xff000000 +#define IP_CLASSA_NSHIFT 24 +#define IP_CLASSA_HOST (0xffffffff & ~IP_CLASSA_NET) +#define IP_CLASSA_MAX 128 + +#define IP_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL) +#define IP_CLASSB_NET 0xffff0000 +#define IP_CLASSB_NSHIFT 16 +#define IP_CLASSB_HOST (0xffffffff & ~IP_CLASSB_NET) +#define IP_CLASSB_MAX 65536 + +#define IP_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL) +#define IP_CLASSC_NET 0xffffff00 +#define IP_CLASSC_NSHIFT 8 +#define IP_CLASSC_HOST (0xffffffff & ~IP_CLASSC_NET) + +#define IP_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL) +#define IP_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IP_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IP_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IP_MULTICAST(a) IP_CLASSD(a) + +#define IP_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) +#define IP_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) + +#define IP_LOOPBACKNET 127 /* official! */ +#if 0 +/** Set an IP address given by the four byte-parts */ +#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->addr = PP_HTONL(LWIP_MAKEU32(a,b,c,d)) + +/** MEMCPY-like copying of IP addresses where addresses are known to be + * 16-bit-aligned if the port is correctly configured (so a port could define + * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */ +#ifndef IPADDR2_COPY +#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip4_addr_t)) +#endif + +/** Copy IP address - faster than ip4_addr_set: no NULL check */ +#define ip4_addr_copy(dest, src) ((dest).addr = (src).addr) +/** Safely copy one IP address to another (src may be NULL) */ +#define ip4_addr_set(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0 : \ + (src)->addr)) +/** Set complete address to zero */ +#define ip4_addr_set_zero(ipaddr) ((ipaddr)->addr = 0) +/** Set address to IPADDR_ANY (no need for lwip_htonl()) */ +#define ip4_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY) +/** Set address to loopback address */ +#define ip4_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK)) +/** Check if an address is in the loopback region */ +#define ip4_addr_isloopback(ipaddr) (((ipaddr)->addr & PP_HTONL(IP_CLASSA_NET)) == PP_HTONL(((u32_t)IP_LOOPBACKNET) << 24)) +/** Safely copy one IP address to another and change byte order + * from host- to network-order. */ +#define ip4_addr_set_hton(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0:\ + lwip_htonl((src)->addr))) +/** IPv4 only: set the IP address given as an u32_t */ +#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32)) +/** IPv4 only: get the IP address as an u32_t */ +#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr) + +/** Get the network address by combining host address with netmask */ +#define ip4_addr_get_network(target, host, netmask) do { ((target)->addr = ((host)->addr) & ((netmask)->addr)); } while(0) + +/** + * Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip4_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) +#define ip4_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr) + +#define ip4_addr_isany_val(addr1) ((addr1).addr == IPADDR_ANY) +#define ip4_addr_isany(addr1) ((addr1) == NULL || ip4_addr_isany_val(*(addr1))) +#endif +#define ip4_addr_isbroadcast(addr1, netif) ip4_addr_isbroadcast_u32((addr1)->addr, netif) +u8_t ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif); +#if 0 +#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr) +u8_t ip4_addr_netmask_valid(u32_t netmask); + +#define ip4_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL)) + +#define ip4_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL)) + +#define ip4_addr_debug_print_parts(debug, a, b, c, d) \ + LWIP_DEBUGF(debug, ("%" U16_F ".%" U16_F ".%" U16_F ".%" U16_F, a, b, c, d)) +#define ip4_addr_debug_print(debug, ipaddr) \ + ip4_addr_debug_print_parts(debug, \ + (u16_t)((ipaddr) != NULL ? ip4_addr1_16(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? ip4_addr2_16(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? ip4_addr3_16(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? ip4_addr4_16(ipaddr) : 0)) +#define ip4_addr_debug_print_val(debug, ipaddr) \ + ip4_addr_debug_print_parts(debug, \ + ip4_addr1_16(&(ipaddr)), \ + ip4_addr2_16(&(ipaddr)), \ + ip4_addr3_16(&(ipaddr)), \ + ip4_addr4_16(&(ipaddr))) + +/* Get one byte from the 4-byte address */ +#define ip4_addr1(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[0]) +#define ip4_addr2(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[1]) +#define ip4_addr3(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[2]) +#define ip4_addr4(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[3]) +/* These are cast to u16_t, with the intent that they are often arguments + * to printf using the U16_F format from cc.h. */ +#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr)) +#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr)) +#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr)) +#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr)) + +#define IP4ADDR_STRLEN_MAX 16 + +/** For backwards compatibility */ +#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr) + +u32_t ipaddr_addr(const char *cp); +int ip4addr_aton(const char *cp, ip4_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip4addr_ntoa(const ip4_addr_t *addr); +char *ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen); + +#endif +#ifdef __cplusplus +} +#endif +#endif /* LWIP_IPV4 */ + +#endif /* LWIP_HDR_IP_ADDR_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip4_frag.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip4_frag.h new file mode 100644 index 0000000..ed5bf14 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip4_frag.h @@ -0,0 +1,100 @@ +/** + * @file + * IP fragmentation/reassembly + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * + */ + +#ifndef LWIP_HDR_IP4_FRAG_H +#define LWIP_HDR_IP4_FRAG_H + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#if LWIP_IPV4 + +#ifdef __cplusplus +extern "C" { +#endif + +#if IP_REASSEMBLY +/* The IP reassembly timer interval in milliseconds. */ +#define IP_TMR_INTERVAL 1000 + +/** IP reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip_reassdata { + struct ip_reassdata *next; + struct pbuf *p; + struct ip_hdr iphdr; + u16_t datagram_len; + u8_t flags; + u8_t timer; +}; + +void ip_reass_init(void); +void ip_reass_tmr(void); +struct pbuf * ip4_reass(struct pbuf *p); +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if !LWIP_NETIF_TX_SINGLE_PBUF +#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED +#define LWIP_PBUF_CUSTOM_REF_DEFINED +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */ +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ + +err_t ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV4 */ + +#endif /* LWIP_HDR_IP4_FRAG_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip6.h new file mode 100644 index 0000000..9c6d593 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip6.h @@ -0,0 +1,93 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_IP6_H +#define LWIP_HDR_IP6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/prot/ip6.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct netif *ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest); +const ip_addr_t *ip6_select_source_address(struct netif *netif, const ip6_addr_t * dest); +err_t ip6_input(struct pbuf *p, struct netif *inp); +err_t ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth); +err_t ip6_output_if(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, struct netif *netif); +err_t ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if LWIP_IPV6_MLD +err_t ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value); +#endif /* LWIP_IPV6_MLD */ + +#define ip6_netif_get_local_ip(netif, dest) (((netif) != NULL) ? \ + ip6_select_source_address(netif, dest) : NULL) + +#if IP6_DEBUG +void ip6_debug_print(struct pbuf *p); +#else +#define ip6_debug_print(p) +#endif /* IP6_DEBUG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_IP6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip6_addr.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip6_addr.h new file mode 100644 index 0000000..4411d9a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip6_addr.h @@ -0,0 +1,285 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * Structs and macros for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_IP6_ADDR_H +#define LWIP_HDR_IP6_ADDR_H + +#include "lwip/opt.h" +#include "def.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#if 0 +/** This is the aligned version of ip6_addr_t, + used as local variable, on the stack, etc. */ +struct ip6_addr { + u32_t addr[4]; +}; + +/** IPv6 address */ +typedef struct ip6_addr ip6_addr_t; + +/** Set an IPv6 partial address given by byte-parts */ +#define IP6_ADDR_PART(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = PP_HTONL(LWIP_MAKEU32(a,b,c,d)) + +/** Set a full IPv6 address by passing the 4 u32_t indices in network byte order + (use PP_HTONL() for constants) */ +#define IP6_ADDR(ip6addr, idx0, idx1, idx2, idx3) do { \ + (ip6addr)->addr[0] = idx0; \ + (ip6addr)->addr[1] = idx1; \ + (ip6addr)->addr[2] = idx2; \ + (ip6addr)->addr[3] = idx3; } while(0) + +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0])) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[1]) >> 16) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[1])) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[2]) >> 16) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[2])) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[3]) >> 16) & 0xffff)) +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[3])) & 0xffff)) + +/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */ +#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3];}while(0) +/** Safely copy one IPv6 address to another (src may be NULL) */ +#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \ + (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \ + (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \ + (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0) + +/** Set complete address to zero */ +#define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = 0;}while(0) + +/** Set address to ipv6 'any' (no need for lwip_htonl()) */ +#define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr) +/** Set address to ipv6 loopback address */ +#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) +/** Safely copy one IPv6 address to another and change byte order + * from host- to network-order. */ +#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : lwip_htonl((src)->addr[0]); \ + (dest)->addr[1] = (src) == NULL ? 0 : lwip_htonl((src)->addr[1]); \ + (dest)->addr[2] = (src) == NULL ? 0 : lwip_htonl((src)->addr[2]); \ + (dest)->addr[3] = (src) == NULL ? 0 : lwip_htonl((src)->addr[3]);}while(0) + + +/** + * Determine if two IPv6 address are on the same network. + * + * @arg addr1 IPv6 address 1 + * @arg addr2 IPv6 address 2 + * @return !0 if the network identifiers of both address match + */ +#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1])) + +#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3])) + +#define ip6_get_subnet_id(ip6addr) (lwip_htonl((ip6addr)->addr[2]) & 0x0000ffffUL) + +#define ip6_addr_isany_val(ip6addr) (((ip6addr).addr[0] == 0) && \ + ((ip6addr).addr[1] == 0) && \ + ((ip6addr).addr[2] == 0) && \ + ((ip6addr).addr[3] == 0)) +#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || ip6_addr_isany_val(*(ip6addr))) + +#define ip6_addr_isloopback(ip6addr) (((ip6addr)->addr[0] == 0UL) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL)) + +#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL)) + +#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL)) + +#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL)) + +#define ip6_addr_isipv4mappedipv6(ip6addr) (((ip6addr)->addr[0] == 0) && ((ip6addr)->addr[1] == 0) && (((ip6addr)->addr[2]) == PP_HTONL(0x0000FFFFUL))) + +#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) +#define ip6_addr_multicast_transient_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL)) +#define ip6_addr_multicast_prefix_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL)) +#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL)) +#define ip6_addr_multicast_scope(ip6addr) ((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xf) +#define IP6_MULTICAST_SCOPE_RESERVED 0x0 +#define IP6_MULTICAST_SCOPE_RESERVED0 0x0 +#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL 0x1 +#define IP6_MULTICAST_SCOPE_LINK_LOCAL 0x2 +#define IP6_MULTICAST_SCOPE_RESERVED3 0x3 +#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL 0x4 +#define IP6_MULTICAST_SCOPE_SITE_LOCAL 0x5 +#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL 0x8 +#define IP6_MULTICAST_SCOPE_GLOBAL 0xe +#define IP6_MULTICAST_SCOPE_RESERVEDF 0xf +#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff010000UL)) +#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff020000UL)) +#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff040000UL)) +#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff050000UL)) +#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff080000UL)) +#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff0e0000UL)) + +/* @todo define get/set for well-know multicast addresses, e.g. ff02::1 */ +#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) +#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + +#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL))) +#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0) + +#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) ) + +#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \ + (ip6addr)->addr[3] = (PP_HTONL(0xff000000UL) | (if_id));}while(0) + +#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + ((ip6addr)->addr[3] == (PP_HTONL(0xff000000UL) | (sn_addr)->addr[3]))) + +/* IPv6 address states. */ +#define IP6_ADDR_INVALID 0x00 +#define IP6_ADDR_TENTATIVE 0x08 +#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */ +#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */ +#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */ +#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */ +#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */ +#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */ +#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */ +#define IP6_ADDR_VALID 0x10 /* This bit marks an address as valid (preferred or deprecated) */ +#define IP6_ADDR_PREFERRED 0x30 +#define IP6_ADDR_DEPRECATED 0x10 /* Same as VALID (valid but not preferred) */ + +#define IP6_ADDR_TENTATIVE_COUNT_MASK 0x07 /* 1-7 probes sent */ + +#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID) +#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE) +#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */ +#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED) +#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED) + +#define ip6_addr_debug_print_parts(debug, a, b, c, d, e, f, g, h) \ + LWIP_DEBUGF(debug, ("%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F, \ + a, b, c, d, e, f, g, h)) +#define ip6_addr_debug_print(debug, ipaddr) \ + ip6_addr_debug_print_parts(debug, \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0), \ + (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0)) +#define ip6_addr_debug_print_val(debug, ipaddr) \ + ip6_addr_debug_print_parts(debug, \ + IP6_ADDR_BLOCK1(&(ipaddr)), \ + IP6_ADDR_BLOCK2(&(ipaddr)), \ + IP6_ADDR_BLOCK3(&(ipaddr)), \ + IP6_ADDR_BLOCK4(&(ipaddr)), \ + IP6_ADDR_BLOCK5(&(ipaddr)), \ + IP6_ADDR_BLOCK6(&(ipaddr)), \ + IP6_ADDR_BLOCK7(&(ipaddr)), \ + IP6_ADDR_BLOCK8(&(ipaddr))) + +#define IP6ADDR_STRLEN_MAX 46 + +int ip6addr_aton(const char *cp, ip6_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip6addr_ntoa(const ip6_addr_t *addr); +char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_IP6_ADDR_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip6_frag.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip6_frag.h new file mode 100644 index 0000000..2f94ee6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip6_frag.h @@ -0,0 +1,120 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_IP6_FRAG_H +#define LWIP_HDR_IP6_FRAG_H + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + +/** IP6_FRAG_COPYHEADER==1: for platforms where sizeof(void*) > 4, this needs to + * be enabled (to not overwrite part of the data). When enabled, the IPv6 header + * is copied instead of referencing it, which gives more room for struct ip6_reass_helper */ +#ifndef IPV6_FRAG_COPYHEADER +#define IPV6_FRAG_COPYHEADER 0 +#endif + +/** The IPv6 reassembly timer interval in milliseconds. */ +#define IP6_REASS_TMR_INTERVAL 1000 + +/* Copy the complete header of the first fragment to struct ip6_reassdata + or just point to its original location in the first pbuf? */ +#if IPV6_FRAG_COPYHEADER +#define IPV6_FRAG_HDRPTR +#define IPV6_FRAG_HDRREF(hdr) (&(hdr)) +#else /* IPV6_FRAG_COPYHEADER */ +#define IPV6_FRAG_HDRPTR * +#define IPV6_FRAG_HDRREF(hdr) (hdr) +#endif /* IPV6_FRAG_COPYHEADER */ + +/** IPv6 reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip6_reassdata { + struct ip6_reassdata *next; + struct pbuf *p; + struct ip6_hdr IPV6_FRAG_HDRPTR iphdr; + u32_t identification; + u16_t datagram_len; + u8_t nexth; + u8_t timer; +}; + +#define ip6_reass_init() /* Compatibility define */ +void ip6_reass_tmr(void); +struct pbuf *ip6_reass(struct pbuf *p); + +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG /* don't build if not configured for use in lwipopts.h */ + +#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED +#define LWIP_PBUF_CUSTOM_REF_DEFINED +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */ + +err_t ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest); + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP6_FRAG_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip_addr.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip_addr.h new file mode 100644 index 0000000..c92e33a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip_addr.h @@ -0,0 +1,410 @@ +/** + * @file + * IP address API (common IPv4 and IPv6) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_IP_ADDR_H +#define LWIP_HDR_IP_ADDR_H + +#include "lwip/opt.h" +#include "lwip/def.h" + +#include "lwip/ip4_addr.h" +#include "lwip/ip6_addr.h" +#include "wm_sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if 0 + +/** @ingroup ipaddr + * IP address types for use in ip_addr_t.type member. + * @see tcp_new_ip_type(), udp_new_ip_type(), raw_new_ip_type(). + */ +enum lwip_ip_addr_type { + /** IPv4 */ + IPADDR_TYPE_V4 = 0U, + /** IPv6 */ + IPADDR_TYPE_V6 = 6U, + /** IPv4+IPv6 ("dual-stack") */ + IPADDR_TYPE_ANY = 46U +}; +#if LWIP_IPV4 && LWIP_IPV6 +/** + * @ingroup ipaddr + * A union struct for both IP version's addresses. + * ATTENTION: watch out for its size when adding IPv6 address scope! + */ +typedef struct ip_addr { + union { + ip6_addr_t ip6; + ip4_addr_t ip4; + } u_addr; + /** @ref lwip_ip_addr_type */ + u8_t type; +} ip_addr_t; + +extern const ip_addr_t ip_addr_any_type; + +/** @ingroup ip4addr */ +#define IPADDR4_INIT(u32val) { { { { u32val, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_V4 } +/** @ingroup ip4addr */ +#define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d))) +/** @ingroup ip6addr */ +#define IPADDR6_INIT(a, b, c, d) { { { { a, b, c, d } } }, IPADDR_TYPE_V6 } +/** @ingroup ip6addr */ +#define IPADDR6_INIT_HOST(a, b, c, d) { { { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } }, IPADDR_TYPE_V6 } + +/** @ingroup ipaddr */ +#define IP_IS_ANY_TYPE_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_ANY) +/** @ingroup ipaddr */ +#define IPADDR_ANY_TYPE_INIT { { { { 0ul, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_ANY } + +/** @ingroup ip4addr */ +#define IP_IS_V4_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V4) +/** @ingroup ip6addr */ +#define IP_IS_V6_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V6) +/** @ingroup ip4addr */ +#define IP_IS_V4(ipaddr) (((ipaddr) == NULL) || IP_IS_V4_VAL(*(ipaddr))) +/** @ingroup ip6addr */ +#define IP_IS_V6(ipaddr) (((ipaddr) != NULL) && IP_IS_V6_VAL(*(ipaddr))) + +#define IP_SET_TYPE_VAL(ipaddr, iptype) do { (ipaddr).type = (iptype); }while(0) +#define IP_SET_TYPE(ipaddr, iptype) do { if((ipaddr) != NULL) { IP_SET_TYPE_VAL(*(ipaddr), iptype); }}while(0) +#define IP_GET_TYPE(ipaddr) ((ipaddr)->type) + +#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) (IP_GET_TYPE(&pcb->local_ip) == IP_GET_TYPE(ipaddr)) +#define IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr) (IP_IS_ANY_TYPE_VAL(pcb->local_ip) || IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)) + +/** @ingroup ip6addr + * Convert generic ip address to specific protocol version + */ +#define ip_2_ip6(ipaddr) (&((ipaddr)->u_addr.ip6)) +/** @ingroup ip4addr + * Convert generic ip address to specific protocol version + */ +#define ip_2_ip4(ipaddr) (&((ipaddr)->u_addr.ip4)) + +/** @ingroup ip4addr */ +#define IP_ADDR4(ipaddr,a,b,c,d) do { IP4_ADDR(ip_2_ip4(ipaddr),a,b,c,d); \ + IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V4); } while(0) +/** @ingroup ip6addr */ +#define IP_ADDR6(ipaddr,i0,i1,i2,i3) do { IP6_ADDR(ip_2_ip6(ipaddr),i0,i1,i2,i3); \ + IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V6); } while(0) +/** @ingroup ip6addr */ +#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3) IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3)) + +/** @ingroup ipaddr */ +#define ip_addr_copy(dest, src) do{ IP_SET_TYPE_VAL(dest, IP_GET_TYPE(&src)); if(IP_IS_V6_VAL(src)){ \ + ip6_addr_copy(*ip_2_ip6(&(dest)), *ip_2_ip6(&(src))); }else{ \ + ip4_addr_copy(*ip_2_ip4(&(dest)), *ip_2_ip4(&(src))); }}while(0) +/** @ingroup ip6addr */ +#define ip_addr_copy_from_ip6(dest, src) do{ \ + ip6_addr_copy(*ip_2_ip6(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V6); }while(0) +/** @ingroup ip4addr */ +#define ip_addr_copy_from_ip4(dest, src) do{ \ + ip4_addr_copy(*ip_2_ip4(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V4); }while(0) +/** @ingroup ip4addr */ +#define ip_addr_set_ip4_u32(ipaddr, val) do{if(ipaddr){ip4_addr_set_u32(ip_2_ip4(ipaddr), val); \ + IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ip4addr */ +#define ip_addr_get_ip4_u32(ipaddr) (((ipaddr) && IP_IS_V4(ipaddr)) ? \ + ip4_addr_get_u32(ip_2_ip4(ipaddr)) : 0) +/** @ingroup ipaddr */ +#define ip_addr_set(dest, src) do{ IP_SET_TYPE(dest, IP_GET_TYPE(src)); if(IP_IS_V6(src)){ \ + ip6_addr_set(ip_2_ip6(dest), ip_2_ip6(src)); }else{ \ + ip4_addr_set(ip_2_ip4(dest), ip_2_ip4(src)); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_set_ipaddr(dest, src) ip_addr_set(dest, src) +/** @ingroup ipaddr */ +#define ip_addr_set_zero(ipaddr) do{ \ + ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, 0); }while(0) +/** @ingroup ip5addr */ +#define ip_addr_set_zero_ip4(ipaddr) do{ \ + ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }while(0) +/** @ingroup ip6addr */ +#define ip_addr_set_zero_ip6(ipaddr) do{ \ + ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }while(0) +/** @ingroup ipaddr */ +#define ip_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_any(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }else{ \ + ip4_addr_set_any(ip_2_ip4(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_loopback(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }else{ \ + ip4_addr_set_loopback(ip_2_ip4(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_set_hton(dest, src) do{if(IP_IS_V6(src)){ \ + ip6_addr_set_hton(ip_2_ip6(ipaddr), (src)); IP_SET_TYPE(dest, IPADDR_TYPE_V6); }else{ \ + ip4_addr_set_hton(ip_2_ip4(ipaddr), (src)); IP_SET_TYPE(dest, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_get_network(target, host, netmask) do{if(IP_IS_V6(host)){ \ + ip4_addr_set_zero(ip_2_ip4(target)); IP_SET_TYPE(target, IPADDR_TYPE_V6); } else { \ + ip4_addr_get_network(ip_2_ip4(target), ip_2_ip4(host), ip_2_ip4(netmask)); IP_SET_TYPE(target, IPADDR_TYPE_V4); }}while(0) +/** @ingroup ipaddr */ +#define ip_addr_netcmp(addr1, addr2, mask) ((IP_IS_V6(addr1) && IP_IS_V6(addr2)) ? \ + 0 : \ + ip4_addr_netcmp(ip_2_ip4(addr1), ip_2_ip4(addr2), mask)) +/** @ingroup ipaddr */ +#define ip_addr_cmp(addr1, addr2) ((IP_GET_TYPE(addr1) != IP_GET_TYPE(addr2)) ? 0 : (IP_IS_V6_VAL(*(addr1)) ? \ + ip6_addr_cmp(ip_2_ip6(addr1), ip_2_ip6(addr2)) : \ + ip4_addr_cmp(ip_2_ip4(addr1), ip_2_ip4(addr2)))) +/** @ingroup ipaddr */ +#define ip_addr_isany(ipaddr) ((IP_IS_V6(ipaddr)) ? \ + ip6_addr_isany(ip_2_ip6(ipaddr)) : \ + ip4_addr_isany(ip_2_ip4(ipaddr))) +/** @ingroup ipaddr */ +#define ip_addr_isany_val(ipaddr) ((IP_IS_V6_VAL(ipaddr)) ? \ + ip6_addr_isany_val(*ip_2_ip6(&(ipaddr))) : \ + ip4_addr_isany_val(*ip_2_ip4(&(ipaddr)))) +/** @ingroup ipaddr */ +#define ip_addr_isbroadcast(ipaddr, netif) ((IP_IS_V6(ipaddr)) ? \ + 0 : \ + ip4_addr_isbroadcast(ip_2_ip4(ipaddr), netif)) +/** @ingroup ipaddr */ +#define ip_addr_ismulticast(ipaddr) ((IP_IS_V6(ipaddr)) ? \ + ip6_addr_ismulticast(ip_2_ip6(ipaddr)) : \ + ip4_addr_ismulticast(ip_2_ip4(ipaddr))) +/** @ingroup ipaddr */ +#define ip_addr_isloopback(ipaddr) ((IP_IS_V6(ipaddr)) ? \ + ip6_addr_isloopback(ip_2_ip6(ipaddr)) : \ + ip4_addr_isloopback(ip_2_ip4(ipaddr))) +/** @ingroup ipaddr */ +#define ip_addr_islinklocal(ipaddr) ((IP_IS_V6(ipaddr)) ? \ + ip6_addr_islinklocal(ip_2_ip6(ipaddr)) : \ + ip4_addr_islinklocal(ip_2_ip4(ipaddr))) +#define ip_addr_debug_print(debug, ipaddr) do { if(IP_IS_V6(ipaddr)) { \ + ip6_addr_debug_print(debug, ip_2_ip6(ipaddr)); } else { \ + ip4_addr_debug_print(debug, ip_2_ip4(ipaddr)); }}while(0) +#define ip_addr_debug_print_val(debug, ipaddr) do { if(IP_IS_V6_VAL(ipaddr)) { \ + ip6_addr_debug_print_val(debug, *ip_2_ip6(&(ipaddr))); } else { \ + ip4_addr_debug_print_val(debug, *ip_2_ip4(&(ipaddr))); }}while(0) +/** @ingroup ipaddr */ +#define ipaddr_ntoa(addr) (((addr) == NULL) ? "NULL" : \ + ((IP_IS_V6(addr)) ? ip6addr_ntoa(ip_2_ip6(addr)) : ip4addr_ntoa(ip_2_ip4(addr)))) +/** @ingroup ipaddr */ +#define ipaddr_ntoa_r(addr, buf, buflen) (((addr) == NULL) ? "NULL" : \ + ((IP_IS_V6(addr)) ? ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen) : ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen))) +int ipaddr_aton(const char *cp, ip_addr_t *addr); + +/** @ingroup ipaddr */ +#define IPADDR_STRLEN_MAX IP6ADDR_STRLEN_MAX + +/** @ingroup ipaddr */ +#define ip4_2_ipv4_mapped_ipv6(ip6addr, ip4addr) do { \ + (ip6addr)->addr[3] = (ip4addr)->addr; \ + (ip6addr)->addr[2] = PP_HTONL(0x0000FFFFUL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[0] = 0; } while(0); + +/** @ingroup ipaddr */ +#define unmap_ipv4_mapped_ipv6(ip4addr, ip6addr) \ + (ip4addr)->addr = (ip6addr)->addr[3]; + +#define IP46_ADDR_ANY(type) (((type) == IPADDR_TYPE_V6)? IP6_ADDR_ANY : IP4_ADDR_ANY) + +#else /* LWIP_IPV4 && LWIP_IPV6 */ + +#define IP_ADDR_PCB_VERSION_MATCH(addr, pcb) 1 +#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) 1 + +#if LWIP_IPV4 + +typedef ip4_addr_t ip_addr_t; +#define IPADDR4_INIT(u32val) { u32val } +#define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d))) +#define IP_IS_V4_VAL(ipaddr) 1 +#define IP_IS_V6_VAL(ipaddr) 0 +#define IP_IS_V4(ipaddr) 1 +#define IP_IS_V6(ipaddr) 0 +#define IP_IS_ANY_TYPE_VAL(ipaddr) 0 +#define IP_SET_TYPE_VAL(ipaddr, iptype) +#define IP_SET_TYPE(ipaddr, iptype) +#define IP_GET_TYPE(ipaddr) IPADDR_TYPE_V4 +#define ip_2_ip4(ipaddr) (ipaddr) +#define IP_ADDR4(ipaddr,a,b,c,d) IP4_ADDR(ipaddr,a,b,c,d) + +#define ip_addr_copy(dest, src) ip4_addr_copy(dest, src) +#define ip_addr_copy_from_ip4(dest, src) ip4_addr_copy(dest, src) +#define ip_addr_set_ip4_u32(ipaddr, val) ip4_addr_set_u32(ip_2_ip4(ipaddr), val) +#define ip_addr_get_ip4_u32(ipaddr) ip4_addr_get_u32(ip_2_ip4(ipaddr)) +#define ip_addr_set(dest, src) ip4_addr_set(dest, src) +#define ip_addr_set_ipaddr(dest, src) ip4_addr_set(dest, src) +#define ip_addr_set_zero(ipaddr) ip4_addr_set_zero(ipaddr) +#define ip_addr_set_zero_ip4(ipaddr) ip4_addr_set_zero(ipaddr) +#define ip_addr_set_any(is_ipv6, ipaddr) ip4_addr_set_any(ipaddr) +#define ip_addr_set_loopback(is_ipv6, ipaddr) ip4_addr_set_loopback(ipaddr) +#define ip_addr_set_hton(dest, src) ip4_addr_set_hton(dest, src) +#define ip_addr_get_network(target, host, mask) ip4_addr_get_network(target, host, mask) +#define ip_addr_netcmp(addr1, addr2, mask) ip4_addr_netcmp(addr1, addr2, mask) +#define ip_addr_cmp(addr1, addr2) ip4_addr_cmp(addr1, addr2) +#define ip_addr_isany(ipaddr) ip4_addr_isany(ipaddr) +#define ip_addr_isany_val(ipaddr) ip4_addr_isany_val(ipaddr) +#define ip_addr_isloopback(ipaddr) ip4_addr_isloopback(ipaddr) +#define ip_addr_islinklocal(ipaddr) ip4_addr_islinklocal(ipaddr) +#define ip_addr_isbroadcast(addr, netif) ip4_addr_isbroadcast(addr, netif) +#define ip_addr_ismulticast(ipaddr) ip4_addr_ismulticast(ipaddr) +#define ip_addr_debug_print(debug, ipaddr) ip4_addr_debug_print(debug, ipaddr) +#define ip_addr_debug_print_val(debug, ipaddr) ip4_addr_debug_print_val(debug, ipaddr) +#define ipaddr_ntoa(ipaddr) ip4addr_ntoa(ipaddr) +#define ipaddr_ntoa_r(ipaddr, buf, buflen) ip4addr_ntoa_r(ipaddr, buf, buflen) +#define ipaddr_aton(cp, addr) ip4addr_aton(cp, addr) + +#define IPADDR_STRLEN_MAX IP4ADDR_STRLEN_MAX + +#define IP46_ADDR_ANY(type) (IP4_ADDR_ANY) + +#else /* LWIP_IPV4 */ + +typedef ip6_addr_t ip_addr_t; +#define IPADDR6_INIT(a, b, c, d) { { a, b, c, d } } +#define IPADDR6_INIT_HOST(a, b, c, d) { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } +#define IP_IS_V4_VAL(ipaddr) 0 +#define IP_IS_V6_VAL(ipaddr) 1 +#define IP_IS_V4(ipaddr) 0 +#define IP_IS_V6(ipaddr) 1 +#define IP_IS_ANY_TYPE_VAL(ipaddr) 0 +#define IP_SET_TYPE_VAL(ipaddr, iptype) +#define IP_SET_TYPE(ipaddr, iptype) +#define IP_GET_TYPE(ipaddr) IPADDR_TYPE_V6 +#define ip_2_ip6(ipaddr) (ipaddr) +#define IP_ADDR6(ipaddr,i0,i1,i2,i3) IP6_ADDR(ipaddr,i0,i1,i2,i3) +#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3) IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3)) + +#define ip_addr_copy(dest, src) ip6_addr_copy(dest, src) +#define ip_addr_copy_from_ip6(dest, src) ip6_addr_copy(dest, src) +#define ip_addr_set(dest, src) ip6_addr_set(dest, src) +#define ip_addr_set_ipaddr(dest, src) ip6_addr_set(dest, src) +#define ip_addr_set_zero(ipaddr) ip6_addr_set_zero(ipaddr) +#define ip_addr_set_zero_ip6(ipaddr) ip6_addr_set_zero(ipaddr) +#define ip_addr_set_any(is_ipv6, ipaddr) ip6_addr_set_any(ipaddr) +#define ip_addr_set_loopback(is_ipv6, ipaddr) ip6_addr_set_loopback(ipaddr) +#define ip_addr_set_hton(dest, src) ip6_addr_set_hton(dest, src) +#define ip_addr_get_network(target, host, mask) ip6_addr_set_zero(target) +#define ip_addr_netcmp(addr1, addr2, mask) 0 +#define ip_addr_cmp(addr1, addr2) ip6_addr_cmp(addr1, addr2) +#define ip_addr_isany(ipaddr) ip6_addr_isany(ipaddr) +#define ip_addr_isany_val(ipaddr) ip6_addr_isany_val(ipaddr) +#define ip_addr_isloopback(ipaddr) ip6_addr_isloopback(ipaddr) +#define ip_addr_islinklocal(ipaddr) ip6_addr_islinklocal(ipaddr) +#define ip_addr_isbroadcast(addr, netif) 0 +#define ip_addr_ismulticast(ipaddr) ip6_addr_ismulticast(ipaddr) +#define ip_addr_debug_print(debug, ipaddr) ip6_addr_debug_print(debug, ipaddr) +#define ip_addr_debug_print_val(debug, ipaddr) ip6_addr_debug_print_val(debug, ipaddr) +#define ipaddr_ntoa(ipaddr) ip6addr_ntoa(ipaddr) +#define ipaddr_ntoa_r(ipaddr, buf, buflen) ip6addr_ntoa_r(ipaddr, buf, buflen) +#define ipaddr_aton(cp, addr) ip6addr_aton(cp, addr) + +#define IPADDR_STRLEN_MAX IP6ADDR_STRLEN_MAX + +#define IP46_ADDR_ANY(type) (IP6_ADDR_ANY) + +#endif /* LWIP_IPV4 */ +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +#endif + +#if LWIP_IPV4 + +extern const ip_addr_t ip_addr_any; +extern const ip_addr_t ip_addr_broadcast; + +/** + * @ingroup ip4addr + * Can be used as a fixed/const ip_addr_t + * for the IP wildcard. + * Defined to @ref IP4_ADDR_ANY when IPv4 is enabled. + * Defined to @ref IP6_ADDR_ANY in IPv6 only systems. + * Use this if you can handle IPv4 _AND_ IPv6 addresses. + * Use @ref IP4_ADDR_ANY or @ref IP6_ADDR_ANY when the IP + * type matters. + */ +#define IP_ADDR_ANY IP4_ADDR_ANY +/** + * @ingroup ip4addr + * Can be used as a fixed/const ip_addr_t + * for the IPv4 wildcard and the broadcast address + */ +#define IP4_ADDR_ANY (&ip_addr_any) +/** + * @ingroup ip4addr + * Can be used as a fixed/const ip4_addr_t + * for the wildcard and the broadcast address + */ +#define IP4_ADDR_ANY4 (ip_2_ip4(&ip_addr_any)) + +/** @ingroup ip4addr */ +#define IP_ADDR_BROADCAST (&ip_addr_broadcast) +/** @ingroup ip4addr */ +#define IP4_ADDR_BROADCAST (ip_2_ip4(&ip_addr_broadcast)) + +#endif /* LWIP_IPV4*/ + +#if LWIP_IPV6 + +extern const ip_addr_t ip6_addr_any; + +/** + * @ingroup ip6addr + * IP6_ADDR_ANY can be used as a fixed ip_addr_t + * for the IPv6 wildcard address + */ +#define IP6_ADDR_ANY (&ip6_addr_any) +/** + * @ingroup ip6addr + * IP6_ADDR_ANY6 can be used as a fixed ip6_addr_t + * for the IPv6 wildcard address + */ +#define IP6_ADDR_ANY6 (ip_2_ip6(&ip6_addr_any)) + +#if !LWIP_IPV4 +/** IPv6-only configurations */ +#define IP_ADDR_ANY IP6_ADDR_ANY +#endif /* !LWIP_IPV4 */ + +#endif + +#if LWIP_IPV4 && LWIP_IPV6 +/** @ingroup ipaddr */ +#define IP_ANY_TYPE (&ip_addr_any_type) +#else +#define IP_ANY_TYPE IP_ADDR_ANY +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP_ADDR_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/ip_frag.h b/sdk/Src/Network/lwip2.0.3/include/lwip/ip_frag.h new file mode 100644 index 0000000..e3f5a82 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/ip_frag.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * + */ + +#ifndef LWIP_HDR_IP_FRAG_H +#define LWIP_HDR_IP_FRAG_H + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#if LWIP_IPV4 + +#ifdef __cplusplus +extern "C" { +#endif + +#if IP_REASSEMBLY +/* The IP reassembly timer interval in milliseconds. */ +#define IP_TMR_INTERVAL 1000 + +/* IP reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip_reassdata { + struct ip_reassdata *next; + struct pbuf *p; + struct ip_hdr iphdr; + u16_t datagram_len; + u8_t flags; + u8_t timer; +}; + +void ip_reass_init(void); +void ip_reass_tmr(void); +struct pbuf * ip4_reass(struct pbuf *p); +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED +#define LWIP_PBUF_CUSTOM_REF_DEFINED +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */ +#endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +err_t ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV4 */ + +#endif /* LWIP_HDR_IP_FRAG_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/lwipopts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/lwipopts.h new file mode 100644 index 0000000..3142353 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/lwipopts.h @@ -0,0 +1,134 @@ + + +#ifndef __LWIP_OPTS_H +#define __LWIP_OPTS_H + +#include +#include "wm_config.h" +#include "wm_mem.h" +//#include "wm_sockets.h" +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#define MEM_LIBC_MALLOC 1 +#define MEMP_MEM_MALLOC 1 +#define MEM_USE_POOLS 0 +#define MEMP_USE_CUSTOM_POOLS 0 + +#define LWIP_COMPAT_SOCKETS 1 + +#define LWIP_SOCKET TLS_CONFIG_SOCKET_STD +#define LWIP_NETCONN TLS_CONFIG_SOCKET_STD + +#define MEM_SIZE 30000 + +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well + */ +#define TCP_WND (6 * TCP_MSS) + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#define TCP_MSS 1024 +//#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + * To achieve good performance, this should be at least 2 * TCP_MSS. + */ +#define TCP_SND_BUF (15*TCP_MSS) + +/** + * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. + */ +//#define TCP_SND_QUEUELEN ((2 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#define TCP_SND_QUEUELEN 30 + +#define MEMP_NUM_TCP_SEG 32 + +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#define TCPIP_MBOX_SIZE 128 + +/** + * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#define LWIP_TCPIP_CORE_LOCKING 0 + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 + +#define LWIP_DHCP 1 + +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 + +#define LWIP_NETIF_API 1 +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + */ +#define MEMP_NUM_NETCONN 8 +#if 0 + +#define TCP_DEBUG LWIP_DBG_ON +//#define TCP_INPUT_DEBUG LWIP_DBG_ON +#define TCP_CWND_DEBUG LWIP_DBG_ON + +#define TCP_WND_DEBUG LWIP_DBG_ON +//#define TCP_OUTPUT_DEBUG LWIP_DBG_ON +#define TCP_QLEN_DEBUG LWIP_DBG_ON +#define TCP_RTO_DEBUG LWIP_DBG_ON + +#endif + +#define LWIP_IGMP TLS_CONFIG_IGMP +#define LWIP_RAND rand +//#define LWIP_SO_RCVTIMEO 1 + +#define DHCP_DOES_ARP_CHECK 0 + +#define LWIP_IPV4 TLS_CONFIG_IPV4 +#define LWIP_IPV6 TLS_CONFIG_IPV6 +#define LWIP_DNS 1 +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define TCP_LISTEN_BACKLOG 1 +#define TCP_DEFAULT_LISTEN_BACKLOG 8 +#define SO_REUSE 1 +#define LWIP_ND6_MAX_MULTICAST_SOLICIT 10 + +#define LWIP_HAVE_LOOPIF 1 +#define ETHARP_SUPPORT_STATIC_ENTRIES 1 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_TCP_KEEPALIVE 1 + +// #define LWIP_HOOK_IP4_ROUTE_SRC wm_ip4_route_src + +// #define LWIP_DEBUG 0 +// #define HTTPD_DEBUG LWIP_DBG_ON +// #define NETIF_DEBUG LWIP_DBG_ON +// #define DHCP_DEBUG LWIP_DBG_OFF +// #define ETHARP_DEBUG LWIP_DBG_OFF +// #define IP_DEBUG LWIP_DBG_OFF +// #define TCP_DEBUG LWIP_DBG_OFF + +// #include +// #define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0) +// #define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", +// x, __LINE__, __FILE__); fflush(NULL); abort();} while(0) + +#endif /* end of __LWIP_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/mem.h b/sdk/Src/Network/lwip2.0.3/include/lwip/mem.h new file mode 100644 index 0000000..0d27adc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/mem.h @@ -0,0 +1,83 @@ +/** + * @file + * Heap API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_MEM_H +#define LWIP_HDR_MEM_H + +#include "lwip/opt.h" +#include "wm_mem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include "lwip/arch.h" + +typedef size_t mem_size_t; +#define MEM_SIZE_F SZT_F + +#elif MEM_USE_POOLS + +typedef u16_t mem_size_t; +#define MEM_SIZE_F U16_F + +#else + +/* MEM_SIZE would have to be aligned, but using 64000 here instead of + * 65535 leaves some room for alignment... + */ +#if MEM_SIZE > 64000L +typedef u32_t mem_size_t; +#define MEM_SIZE_F U32_F +#else +typedef u16_t mem_size_t; +#define MEM_SIZE_F U16_F +#endif /* MEM_SIZE > 64000 */ +#endif + +void mem_init(void); +void *mem_trim(void *mem, mem_size_t size); +void *mem_malloc(mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_MEM_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/memp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/memp.h new file mode 100644 index 0000000..c45aca8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/memp.h @@ -0,0 +1,155 @@ +/** + * @file + * Memory pool API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef LWIP_HDR_MEMP_H +#define LWIP_HDR_MEMP_H + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* run once with empty definition to handle all custom includes in lwippools.h */ +#define LWIP_MEMPOOL(name,num,size,desc) +#include "lwip/priv/memp_std.h" + +/** Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */ +typedef enum { +#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, +#include "lwip/priv/memp_std.h" + MEMP_MAX +} memp_t; + +#include "lwip/priv/memp_priv.h" +#include "lwip/stats.h" + +extern const struct memp_desc* const memp_pools[MEMP_MAX]; + +/** + * @ingroup mempool + * Declare prototype for private memory pool if it is used in multiple files + */ +#define LWIP_MEMPOOL_PROTOTYPE(name) extern const struct memp_desc memp_ ## name + +#if MEMP_MEM_MALLOC + +#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \ + LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \ + const struct memp_desc memp_ ## name = { \ + DECLARE_LWIP_MEMPOOL_DESC(desc) \ + LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \ + LWIP_MEM_ALIGN_SIZE(size) \ + }; + +#else /* MEMP_MEM_MALLOC */ + +/** + * @ingroup mempool + * Declare a private memory pool + * Private mempools example: + * .h: only when pool is used in multiple .c files: LWIP_MEMPOOL_PROTOTYPE(my_private_pool); + * .c: + * - in global variables section: LWIP_MEMPOOL_DECLARE(my_private_pool, 10, sizeof(foo), "Some description") + * - call ONCE before using pool (e.g. in some init() function): LWIP_MEMPOOL_INIT(my_private_pool); + * - allocate: void* my_new_mem = LWIP_MEMPOOL_ALLOC(my_private_pool); + * - free: LWIP_MEMPOOL_FREE(my_private_pool, my_new_mem); + * + * To relocate a pool, declare it as extern in cc.h. Example for GCC: + * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_my_private_pool[]; + */ +#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \ + LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \ + \ + LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \ + \ + static struct memp *memp_tab_ ## name; \ + \ + const struct memp_desc memp_ ## name = { \ + DECLARE_LWIP_MEMPOOL_DESC(desc) \ + LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \ + LWIP_MEM_ALIGN_SIZE(size), \ + (num), \ + memp_memory_ ## name ## _base, \ + &memp_tab_ ## name \ + }; + +#endif /* MEMP_MEM_MALLOC */ + +/** + * @ingroup mempool + * Initialize a private memory pool + */ +#define LWIP_MEMPOOL_INIT(name) memp_init_pool(&memp_ ## name) +/** + * @ingroup mempool + * Allocate from a private memory pool + */ +#define LWIP_MEMPOOL_ALLOC(name) memp_malloc_pool(&memp_ ## name) +/** + * @ingroup mempool + * Free element from a private memory pool + */ +#define LWIP_MEMPOOL_FREE(name, x) memp_free_pool(&memp_ ## name, (x)) + +#if MEM_USE_POOLS +/** This structure is used to save the pool one element came from. + * This has to be defined here as it is required for pool size calculation. */ +struct memp_malloc_helper +{ + memp_t poolnr; +#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) + u16_t size; +#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */ +}; +#endif /* MEM_USE_POOLS */ + +void memp_init(void); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type); +#endif +void memp_free(memp_t type, void *mem); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_MEMP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/mld6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/mld6.h new file mode 100644 index 0000000..f8c99d9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/mld6.h @@ -0,0 +1,99 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_MLD6_H +#define LWIP_HDR_MLD6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6_MLD && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** MLD group */ +struct mld_group { + /** next link */ + struct mld_group *next; + /** multicast address */ + ip6_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +#define MLD6_TMR_INTERVAL 100 /* Milliseconds */ + +err_t mld6_stop(struct netif *netif); +void mld6_report_groups(struct netif *netif); +void mld6_tmr(void); +struct mld_group *mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr); +void mld6_input(struct pbuf *p, struct netif *inp); +err_t mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr); +err_t mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr); +err_t mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr); +err_t mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr); + +/** @ingroup mld6 + * Get list head of MLD6 groups for netif. + * Note: The allnodes group IP is NOT in the list, since it must always + * be received for correct IPv6 operation. + * @see @ref netif_set_mld_mac_filter() + */ +#define netif_mld6_data(netif) ((struct mld_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6)) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */ + +#endif /* LWIP_HDR_MLD6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/nd6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/nd6.h new file mode 100644 index 0000000..4c85b83 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/nd6.h @@ -0,0 +1,85 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_ND6_H +#define LWIP_HDR_ND6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/err.h" +#include "wm_sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** 1 second period */ +#define ND6_TMR_INTERVAL 1000 + +struct pbuf; +struct netif; + +void nd6_tmr(void); +void nd6_input(struct pbuf *p, struct netif *inp); +void nd6_clear_destination_cache(void); +struct netif *nd6_find_route(const ip6_addr_t *ip6addr); +err_t nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp); +u16_t nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif); +#if LWIP_ND6_TCP_REACHABILITY_HINTS +void nd6_reachability_hint(const ip6_addr_t *ip6addr); +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ +void nd6_cleanup_netif(struct netif *netif); +#if LWIP_IPV6_MLD +void nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state); +#endif /* LWIP_IPV6_MLD */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_ND6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/netbuf.h b/sdk/Src/Network/lwip2.0.3/include/lwip/netbuf.h new file mode 100644 index 0000000..bfd7242 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/netbuf.h @@ -0,0 +1,118 @@ +/** + * @file + * netbuf API (for netconn API) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_NETBUF_H +#define LWIP_HDR_NETBUF_H + +#include "lwip/opt.h" + +#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ +/* Note: Netconn API is always available when sockets are enabled - + * sockets are implemented on top of them */ + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This netbuf has dest-addr/port set */ +#define NETBUF_FLAG_DESTADDR 0x01 +/** This netbuf includes a checksum */ +#define NETBUF_FLAG_CHKSUM 0x02 + +/** "Network buffer" - contains data and addressing info */ +struct netbuf { + struct pbuf *p, *ptr; + ip_addr_t addr; + u16_t port; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + u8_t flags; +#endif /* LWIP_CHECKSUM_ON_COPY */ + u16_t toport_chksum; +#if LWIP_NETBUF_RECVINFO + ip_addr_t toaddr; +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ +}; + +/* Network buffer functions: */ +struct netbuf * netbuf_new (void); +void netbuf_delete (struct netbuf *buf); +void * netbuf_alloc (struct netbuf *buf, u16_t size); +void netbuf_free (struct netbuf *buf); +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +void netbuf_chain (struct netbuf *head, struct netbuf *tail); + +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +s8_t netbuf_next (struct netbuf *buf); +void netbuf_first (struct netbuf *buf); + + +#define netbuf_copy_partial(buf, dataptr, len, offset) \ + pbuf_copy_partial((buf)->p, (dataptr), (len), (offset)) +#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0) +#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len) +#define netbuf_len(buf) ((buf)->p->tot_len) +#define netbuf_fromaddr(buf) (&((buf)->addr)) +#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(&((buf)->addr), fromaddr) +#define netbuf_fromport(buf) ((buf)->port) +#if LWIP_NETBUF_RECVINFO +#define netbuf_destaddr(buf) (&((buf)->toaddr)) +#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(&((buf)->toaddr), destaddr) +#if LWIP_CHECKSUM_ON_COPY +#define netbuf_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0) +#else /* LWIP_CHECKSUM_ON_COPY */ +#define netbuf_destport(buf) ((buf)->toport_chksum) +#endif /* LWIP_CHECKSUM_ON_COPY */ +#endif /* LWIP_NETBUF_RECVINFO */ +#if LWIP_CHECKSUM_ON_COPY +#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \ + (buf)->toport_chksum = chksum; } while(0) +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#endif /* LWIP_HDR_NETBUF_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/netdb.h b/sdk/Src/Network/lwip2.0.3/include/lwip/netdb.h new file mode 100644 index 0000000..160f259 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/netdb.h @@ -0,0 +1,153 @@ +/** + * @file + * NETDB API (sockets) + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef LWIP_HDR_NETDB_H +#define LWIP_HDR_NETDB_H + +#include "lwip/opt.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/arch.h" +#include "lwip/inet.h" +#include "lwip/sockets.h" +#include "wm_sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* some rarely used options */ +#ifndef LWIP_DNS_API_DECLARE_H_ERRNO +#define LWIP_DNS_API_DECLARE_H_ERRNO 1 +#endif + +#ifndef LWIP_DNS_API_DEFINE_ERRORS +#define LWIP_DNS_API_DEFINE_ERRORS 1 +#endif + +#ifndef LWIP_DNS_API_DEFINE_FLAGS +#define LWIP_DNS_API_DEFINE_FLAGS 1 +#endif + +#ifndef LWIP_DNS_API_DECLARE_STRUCTS +#define LWIP_DNS_API_DECLARE_STRUCTS 1 +#endif + +#if LWIP_DNS_API_DEFINE_ERRORS +/** Errors used by the DNS API functions, h_errno can be one of them */ +#define EAI_NONAME 200 +#define EAI_SERVICE 201 +#define EAI_FAIL 202 +#define EAI_MEMORY 203 +#define EAI_FAMILY 204 + +#define HOST_NOT_FOUND 210 +#define NO_DATA 211 +#define NO_RECOVERY 212 +#define TRY_AGAIN 213 +#endif /* LWIP_DNS_API_DEFINE_ERRORS */ + +#if LWIP_DNS_API_DEFINE_FLAGS +/* input flags for struct addrinfo */ +#define AI_PASSIVE 0x01 +#define AI_CANONNAME 0x02 +#define AI_NUMERICHOST 0x04 +#define AI_NUMERICSERV 0x08 +#define AI_V4MAPPED 0x10 +#define AI_ALL 0x20 +#define AI_ADDRCONFIG 0x40 +#endif /* LWIP_DNS_API_DEFINE_FLAGS */ + +#if LWIP_DNS_API_DECLARE_STRUCTS +#if 0 +struct hostent { + char *h_name; /* Official name of the host. */ + char **h_aliases; /* A pointer to an array of pointers to alternative host names, + terminated by a null pointer. */ + int h_addrtype; /* Address type. */ + int h_length; /* The length, in bytes, of the address. */ + char **h_addr_list; /* A pointer to an array of pointers to network addresses (in + network byte order) for the host, terminated by a null pointer. */ +#define h_addr h_addr_list[0] /* for backward compatibility */ +}; +#endif + +struct addrinfo { + int ai_flags; /* Input flags. */ + int ai_family; /* Address family of socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol of socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address of socket. */ + char *ai_canonname; /* Canonical name of service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; +#endif /* LWIP_DNS_API_DECLARE_STRUCTS */ + +#define NETDB_ELEM_SIZE (sizeof(struct addrinfo) + sizeof(struct sockaddr_storage) + DNS_MAX_NAME_LENGTH + 1) + +#if LWIP_DNS_API_DECLARE_H_ERRNO +/* application accessible error code set by the DNS API functions */ +extern int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/ + +struct hostent *lwip_gethostbyname(const char *name); +int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop); +void lwip_freeaddrinfo(struct addrinfo *ai); +int lwip_getaddrinfo(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); + +#if LWIP_COMPAT_SOCKETS +/** @ingroup netdbapi */ +#define gethostbyname(name) lwip_gethostbyname(name) +/** @ingroup netdbapi */ +#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \ + lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop) +/** @ingroup netdbapi */ +#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo) +/** @ingroup netdbapi */ +#define getaddrinfo(nodname, servname, hints, res) \ + lwip_getaddrinfo(nodname, servname, hints, res) +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS && LWIP_SOCKET */ + +#endif /* LWIP_HDR_NETDB_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/netif.h b/sdk/Src/Network/lwip2.0.3/include/lwip/netif.h new file mode 100644 index 0000000..01f3248 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/netif.h @@ -0,0 +1,490 @@ +/** + * @file + * netif API (to be used from TCPIP thread) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_NETIF_H +#define LWIP_HDR_NETIF_H + +#include "lwip/opt.h" + +#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) + +#include "lwip/err.h" + +#include "lwip/ip_addr.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_NETIF_STATUS_CALLBACK +#define NETIF_STATUS_CALLBACK(n, s) do{ if (n->status_callback) { (n->status_callback)(n, s); }}while(0) +#else +#define NETIF_STATUS_CALLBACK(n, s) +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +/* Throughout this file, IP addresses are expected to be in + * the same byte order as in IP_PCB. */ + +/** Must be the maximum of all used hardware address lengths + across all types of interfaces in use. + This does not have to be changed, normally. */ +#ifndef NETIF_MAX_HWADDR_LEN +#define NETIF_MAX_HWADDR_LEN 6U +#endif + +/** + * @defgroup netif_flags Flags + * @ingroup netif + * @{ + */ + +/** Whether the network interface is 'up'. This is + * a software flag used to control whether this network + * interface is enabled and processes traffic. + * It must be set by the startup code before this netif can be used + * (also for dhcp/autoip). + */ +#define NETIF_FLAG_UP 0x01U +/** If set, the netif has broadcast capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_BROADCAST 0x02U +/** If set, the interface has an active link + * (set by the network interface driver). + * Either set by the netif driver in its init function (if the link + * is up at that time) or at a later point once the link comes up + * (if link detection is supported by the hardware). */ +#define NETIF_FLAG_LINK_UP 0x04U +/** If set, the netif is an ethernet device using ARP. + * Set by the netif driver in its init function. + * Used to check input packet types and use of DHCP. */ +#define NETIF_FLAG_ETHARP 0x08U +/** If set, the netif is an ethernet device. It might not use + * ARP or TCP/IP if it is used for PPPoE only. + */ +#define NETIF_FLAG_ETHERNET 0x10U +/** If set, the netif has IGMP capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_IGMP 0x20U +/** If set, the netif has MLD6 capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_MLD6 0x40U + +#define NETIF_STATE_IPV4 0x01U +#define NETIF_STATE_IPV6 0x02U + +/** + * @} + */ + +enum lwip_internal_netif_client_data_index +{ +#if LWIP_DHCP + LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, +#endif +#if LWIP_AUTOIP + LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, +#endif +#if LWIP_IGMP + LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, +#endif +#if LWIP_IPV6_MLD + LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, +#endif + LWIP_NETIF_CLIENT_DATA_INDEX_MAX +}; + +#if LWIP_CHECKSUM_CTRL_PER_NETIF +#define NETIF_CHECKSUM_GEN_IP 0x0001 +#define NETIF_CHECKSUM_GEN_UDP 0x0002 +#define NETIF_CHECKSUM_GEN_TCP 0x0004 +#define NETIF_CHECKSUM_GEN_ICMP 0x0008 +#define NETIF_CHECKSUM_GEN_ICMP6 0x0010 +#define NETIF_CHECKSUM_CHECK_IP 0x0100 +#define NETIF_CHECKSUM_CHECK_UDP 0x0200 +#define NETIF_CHECKSUM_CHECK_TCP 0x0400 +#define NETIF_CHECKSUM_CHECK_ICMP 0x0800 +#define NETIF_CHECKSUM_CHECK_ICMP6 0x1000 +#define NETIF_CHECKSUM_ENABLE_ALL 0xFFFF +#define NETIF_CHECKSUM_DISABLE_ALL 0x0000 +#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */ + +struct netif; + +/** MAC Filter Actions, these are passed to a netif's igmp_mac_filter or + * mld_mac_filter callback function. */ +enum netif_mac_filter_action { + /** Delete a filter entry */ + NETIF_DEL_MAC_FILTER = 0, + /** Add a filter entry */ + NETIF_ADD_MAC_FILTER = 1 +}; + +/** Function prototype for netif init functions. Set up flags and output/linkoutput + * callback functions in this function. + * + * @param netif The netif to initialize + */ +typedef err_t (*netif_init_fn)(struct netif *netif); +/** Function prototype for netif->input functions. This function is saved as 'input' + * callback function in the netif struct. Call it when a packet has been received. + * + * @param p The received packet, copied into a pbuf + * @param inp The netif which received the packet + */ +typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp); + +#if LWIP_IPV4 +/** Function prototype for netif->output functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'etharp_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IP address to which the packet shall be sent + */ +typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, + const ip4_addr_t *ipaddr); +#endif /* LWIP_IPV4*/ + +#if LWIP_IPV6 +/** Function prototype for netif->output_ip6 functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'ethip6_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IPv6 address to which the packet shall be sent + */ +typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p, + const ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ + +/** Function prototype for netif->linkoutput functions. Only used for ethernet + * netifs. This function is called by ARP when a packet shall be sent. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (raw ethernet packet) + */ +typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p); +/** Function prototype for netif status- or link-callback functions. */ +typedef void (*netif_status_callback_fn)(struct netif *netif, u8_t netif_state); +#if LWIP_IPV4 && LWIP_IGMP +/** Function prototype for netif igmp_mac_filter functions */ +typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif, + const ip4_addr_t *group, enum netif_mac_filter_action action); +#endif /* LWIP_IPV4 && LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD +/** Function prototype for netif mld_mac_filter functions */ +typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif, + const ip6_addr_t *group, enum netif_mac_filter_action action); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +#if TLS_CONFIG_AP +typedef err_t (*netif_tcpipoutput_fn)(struct pbuf *p, struct netif *inp, ip_addr_t ipaddr); +#endif + +#if LWIP_DHCP || LWIP_AUTOIP || LWIP_IGMP || LWIP_IPV6_MLD || (LWIP_NUM_NETIF_CLIENT_DATA > 0) +u8_t netif_alloc_client_data_id(void); +/** @ingroup netif_cd + * Set client data. Obtain ID from netif_alloc_client_data_id(). + */ +#define netif_set_client_data(netif, id, data) netif_get_client_data(netif, id) = (data) +/** @ingroup netif_cd + * Get client data. Obtain ID from netif_alloc_client_data_id(). + */ +#define netif_get_client_data(netif, id) (netif)->client_data[(id)] +#endif /* LWIP_DHCP || LWIP_AUTOIP || (LWIP_NUM_NETIF_CLIENT_DATA > 0) */ + +/** Generic data structure used for all lwIP network interfaces. + * The following fields should be filled in by the initialization + * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */ +struct netif { + /** pointer to next in linked list */ + struct netif *next; + +#if LWIP_IPV4 + /** IP address configuration in network byte order */ + ip_addr_t ip_addr; + ip_addr_t netmask; + ip_addr_t gw; +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 + /** Array of IPv6 addresses for this netif. */ + ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; + /** The state of each IPv6 address (Tentative, Preferred, etc). + * @see ip6_addr.h */ + u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES]; +#endif /* LWIP_IPV6 */ + /** This function is called by the network device driver + * to pass a packet up the TCP/IP stack. */ + netif_input_fn input; +#if LWIP_IPV4 + /** This function is called by the IP module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. + * For ethernet physical layer, this is usually etharp_output() */ + netif_output_fn output; +#endif /* LWIP_IPV4 */ + /** This function is called by ethernet_output() when it wants + * to send a packet on the interface. This function outputs + * the pbuf as-is on the link medium. */ + netif_linkoutput_fn linkoutput; +#if LWIP_IPV6 + /** This function is called by the IPv6 module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. + * For ethernet physical layer, this is usually ethip6_output() */ + netif_output_ip6_fn output_ip6; +#endif /* LWIP_IPV6 */ +#if TLS_CONFIG_AP_OPT_FWD + netif_tcpipoutput_fn ipfwd_output; +#endif +#if LWIP_NETIF_STATUS_CALLBACK + /** This function is called when the netif state is set to up or down + */ + netif_status_callback_fn status_callback; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + /** This function is called when the netif link is set to up or down + */ + netif_status_callback_fn link_callback; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK + /** This function is called when the netif has been removed */ + netif_status_callback_fn remove_callback; +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + /** This field can be set by the device driver and could point + * to state information for the device. */ + void *state; +#ifdef netif_get_client_data + void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA]; +#endif +#if LWIP_IPV6_AUTOCONFIG + /** is this netif enabled for IPv6 autoconfiguration */ + u8_t ip6_autoconfig_enabled; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /** Number of Router Solicitation messages that remain to be sent. */ + u8_t rs_count; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_NETIF_HOSTNAME + /* the hostname for this netif, NULL is a valid value */ + const char* hostname; +#endif /* LWIP_NETIF_HOSTNAME */ +#if LWIP_CHECKSUM_CTRL_PER_NETIF + u16_t chksum_flags; +#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/ + /** maximum transfer unit (in bytes) */ + u16_t mtu; + /** number of bytes used in hwaddr */ + u8_t hwaddr_len; + /** link level hardware address of this interface */ + u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; + /** flags (@see @ref netif_flags) */ + u8_t flags; + /** descriptive abbreviation */ + char name[2]; + /** number of this interface */ + u8_t num; +#if MIB2_STATS + /** link type (from "snmp_ifType" enum from snmp_mib2.h) */ + u8_t link_type; + /** (estimate) link speed */ + u32_t link_speed; + /** timestamp at last change made (up/down) */ + u32_t ts; + /** counters */ + struct stats_mib2_netif_ctrs mib2_counters; +#endif /* MIB2_STATS */ +#if LWIP_IPV4 && LWIP_IGMP + /** This function could be called to add or delete an entry in the multicast + filter table of the ethernet MAC.*/ + netif_igmp_mac_filter_fn igmp_mac_filter; +#endif /* LWIP_IPV4 && LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /** This function could be called to add or delete an entry in the IPv6 multicast + filter table of the ethernet MAC. */ + netif_mld_mac_filter_fn mld_mac_filter; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if ENABLE_LOOPBACK + /* List of packets to be queued for ourselves. */ + struct pbuf *loop_first; + struct pbuf *loop_last; +#if LWIP_LOOPBACK_MAX_PBUFS + u16_t loop_cnt_current; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ +#endif /* ENABLE_LOOPBACK */ +}; + +#if LWIP_CHECKSUM_CTRL_PER_NETIF +#define NETIF_SET_CHECKSUM_CTRL(netif, chksumflags) do { \ + (netif)->chksum_flags = chksumflags; } while(0) +#define IF__NETIF_CHECKSUM_ENABLED(netif, chksumflag) if (((netif) == NULL) || (((netif)->chksum_flags & (chksumflag)) != 0)) +#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */ +#define NETIF_SET_CHECKSUM_CTRL(netif, chksumflags) +#define IF__NETIF_CHECKSUM_ENABLED(netif, chksumflag) +#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */ + +/** The list of network interfaces. */ +extern struct netif *netif_list; +/** The default network interface. */ +extern struct netif *netif_default; + +void netif_init(void); + +struct netif *netif_add(struct netif *netif, +#if LWIP_IPV4 + const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw, +#endif /* LWIP_IPV4 */ + void *state, netif_init_fn init, netif_input_fn input); +#if LWIP_IPV4 +void netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, + const ip4_addr_t *gw); +#endif /* LWIP_IPV4 */ +void netif_remove(struct netif * netif); + +/* Returns a network interface given its name. The name is of the form + "et0", where the first two letters are the "name" field in the + netif structure, and the digit is in the num field in the same + structure. */ +struct netif *netif_find(const char *name); + +void netif_set_default(struct netif *netif); + +#if LWIP_IPV4 +void netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr); +void netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask); +void netif_set_gw(struct netif *netif, const ip4_addr_t *gw); +/** @ingroup netif_ip4 */ +#define netif_ip4_addr(netif) ((const ip4_addr_t*)ip_2_ip4(&((netif)->ip_addr))) +/** @ingroup netif_ip4 */ +#define netif_ip4_netmask(netif) ((const ip4_addr_t*)ip_2_ip4(&((netif)->netmask))) +/** @ingroup netif_ip4 */ +#define netif_ip4_gw(netif) ((const ip4_addr_t*)ip_2_ip4(&((netif)->gw))) +/** @ingroup netif_ip4 */ +#define netif_ip_addr4(netif) ((const ip_addr_t*)&((netif)->ip_addr)) +/** @ingroup netif_ip4 */ +#define netif_ip_netmask4(netif) ((const ip_addr_t*)&((netif)->netmask)) +/** @ingroup netif_ip4 */ +#define netif_ip_gw4(netif) ((const ip_addr_t*)&((netif)->gw)) +#endif /* LWIP_IPV4 */ + +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +/** @ingroup netif + * Ask if an interface is up + */ +#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_STATUS_CALLBACK +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK +void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback); +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +/** Ask if a link is up */ +#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_LINK_CALLBACK +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if LWIP_NETIF_HOSTNAME +/** @ingroup netif */ +#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0) +/** @ingroup netif */ +#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL) +#endif /* LWIP_NETIF_HOSTNAME */ + +#if LWIP_IGMP +/** @ingroup netif */ +#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0) +#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL) +#endif /* LWIP_IGMP */ + +#if LWIP_IPV6 && LWIP_IPV6_MLD +/** @ingroup netif */ +#define netif_set_mld_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->mld_mac_filter = function; }}while(0) +#define netif_get_mld_mac_filter(netif) (((netif) != NULL) ? ((netif)->mld_mac_filter) : NULL) +#define netif_mld_mac_filter(netif, addr, action) do { if((netif) && (netif)->mld_mac_filter) { (netif)->mld_mac_filter((netif), (addr), (action)); }}while(0) +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +#if ENABLE_LOOPBACK +err_t netif_loop_output(struct netif *netif, struct pbuf *p); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +err_t netif_input(struct pbuf *p, struct netif *inp); + +#if LWIP_IPV6 +/** @ingroup netif_ip6 */ +#define netif_ip_addr6(netif, i) ((const ip_addr_t*)(&((netif)->ip6_addr[i]))) +/** @ingroup netif_ip6 */ +#define netif_ip6_addr(netif, i) ((const ip6_addr_t*)ip_2_ip6(&((netif)->ip6_addr[i]))) +void netif_ip6_addr_set(struct netif *netif, s8_t addr_idx, const ip6_addr_t *addr6); +void netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3); +#define netif_ip6_addr_state(netif, i) ((netif)->ip6_addr_state[i]) +void netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state); +s8_t netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr); +void netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit); +err_t netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx); +#define netif_set_ip6_autoconfig_enabled(netif, action) do { if(netif) { (netif)->ip6_autoconfig_enabled = (action); }}while(0) +#endif /* LWIP_IPV6 */ + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint)) +#else /* LWIP_NETIF_HWADDRHINT */ +#define NETIF_SET_HWADDRHINT(netif, hint) +#endif /* LWIP_NETIF_HWADDRHINT */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_NETIF_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/netifapi.h b/sdk/Src/Network/lwip2.0.3/include/lwip/netifapi.h new file mode 100644 index 0000000..6c1b56b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/netifapi.h @@ -0,0 +1,140 @@ +/** + * @file + * netif API (to be used from non-TCPIP threads) + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ +#ifndef LWIP_HDR_NETIFAPI_H +#define LWIP_HDR_NETIFAPI_H + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/priv/tcpip_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_MPU_COMPATIBLE +#define NETIFAPI_IPADDR_DEF(type, m) type m +#else /* LWIP_MPU_COMPATIBLE */ +#define NETIFAPI_IPADDR_DEF(type, m) const type * m +#endif /* LWIP_MPU_COMPATIBLE */ + +typedef void (*netifapi_void_fn)(struct netif *netif); +typedef err_t (*netifapi_errt_fn)(struct netif *netif); + +struct netifapi_msg { + struct tcpip_api_call_data call; + struct netif *netif; + union { + struct { +#if LWIP_IPV4 + NETIFAPI_IPADDR_DEF(ip4_addr_t, ipaddr); + NETIFAPI_IPADDR_DEF(ip4_addr_t, netmask); + NETIFAPI_IPADDR_DEF(ip4_addr_t, gw); +#endif /* LWIP_IPV4 */ + void *state; + netif_init_fn init; + netif_input_fn input; + } add; + struct { + netifapi_void_fn voidfunc; + netifapi_errt_fn errtfunc; + } common; + } msg; +}; + + +/* API for application */ +err_t netifapi_netif_add(struct netif *netif, +#if LWIP_IPV4 + const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw, +#endif /* LWIP_IPV4 */ + void *state, netif_init_fn init, netif_input_fn input); + +#if LWIP_IPV4 +err_t netifapi_netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, + const ip4_addr_t *netmask, const ip4_addr_t *gw); +#endif /* LWIP_IPV4*/ + +err_t netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc); + +/** @ingroup netifapi_netif */ +#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL) +/** @ingroup netifapi_netif */ +#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL) +/** @ingroup netifapi_netif */ +#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL) +/** @ingroup netifapi_netif */ +#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL) +/** @ingroup netifapi_netif */ +#define netifapi_netif_set_link_up(n) netifapi_netif_common(n, netif_set_link_up, NULL) +/** @ingroup netifapi_netif */ +#define netifapi_netif_set_link_down(n) netifapi_netif_common(n, netif_set_link_down, NULL) + +/** + * @defgroup netifapi_dhcp4 DHCPv4 + * @ingroup netifapi + * To be called from non-TCPIP threads + */ +/** @ingroup netifapi_dhcp4 */ +#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start) +/** @ingroup netifapi_dhcp4 */ +#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL) +/** @ingroup netifapi_dhcp4 */ +#define netifapi_dhcp_inform(n) netifapi_netif_common(n, dhcp_inform, NULL) +/** @ingroup netifapi_dhcp4 */ +#define netifapi_dhcp_renew(n) netifapi_netif_common(n, NULL, dhcp_renew) +/** @ingroup netifapi_dhcp4 */ +#define netifapi_dhcp_release(n) netifapi_netif_common(n, NULL, dhcp_release) + +/** + * @defgroup netifapi_autoip AUTOIP + * @ingroup netifapi + * To be called from non-TCPIP threads + */ +/** @ingroup netifapi_autoip */ +#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start) +/** @ingroup netifapi_autoip */ +#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETIF_API */ + +#endif /* LWIP_HDR_NETIFAPI_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/opt.h b/sdk/Src/Network/lwip2.0.3/include/lwip/opt.h new file mode 100644 index 0000000..702e3bb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/opt.h @@ -0,0 +1,2877 @@ +/** + * @file + * + * lwIP Options Configuration + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * NOTE: || defined __DOXYGEN__ is a workaround for doxygen bug - + * without this, doxygen does not see the actual #define + */ + +#if !defined LWIP_HDR_OPT_H +#define LWIP_HDR_OPT_H + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you don't like! + */ +#include "lwipopts.h" +#include "lwip/debug.h" + +/** + * @defgroup lwip_opts Options (lwipopts.h) + * @ingroup lwip + * + * @defgroup lwip_opts_debug Debugging + * @ingroup lwip_opts + * + * @defgroup lwip_opts_infrastructure Infrastructure + * @ingroup lwip_opts + * + * @defgroup lwip_opts_callback Callback-style APIs + * @ingroup lwip_opts + * + * @defgroup lwip_opts_threadsafe_apis Thread-safe APIs + * @ingroup lwip_opts + */ + + /* + ------------------------------------ + -------------- NO SYS -------------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_nosys NO_SYS + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * NO_SYS==1: Use lwIP without OS-awareness (no thread, semaphores, mutexes or + * mboxes). This means threaded APIs cannot be used (socket, netconn, + * i.e. everything in the 'api' folder), only the callback-style raw API is + * available (and you have to watch out for yourself that you don't access + * lwIP functions/structures from more than one context at a time!) + */ +#if !defined NO_SYS || defined __DOXYGEN__ +#define NO_SYS 0 +#endif +/** + * @} + */ + +/** + * @defgroup lwip_opts_timers Timers + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_TIMERS==0: Drop support for sys_timeout and lwip-internal cyclic timers. + * (the array of lwip-internal cyclic timers is still provided) + * (check NO_SYS_NO_TIMERS for compatibility to old versions) + */ +#if !defined LWIP_TIMERS || defined __DOXYGEN__ +#ifdef NO_SYS_NO_TIMERS +#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) +#else +#define LWIP_TIMERS 1 +#endif +#endif + +/** + * LWIP_TIMERS_CUSTOM==1: Provide your own timer implementation. + * Function prototypes in timeouts.h and the array of lwip-internal cyclic timers + * are still included, but the implementation is not. The following functions + * will be required: sys_timeouts_init(), sys_timeout(), sys_untimeout(), + * sys_timeouts_mbox_fetch() + */ +#if !defined LWIP_TIMERS_CUSTOM || defined __DOXYGEN__ +#define LWIP_TIMERS_CUSTOM 0 +#endif +/** + * @} + */ + +/** + * @defgroup lwip_opts_memcpy memcpy + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#if !defined MEMCPY || defined __DOXYGEN__ +#define MEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#if !defined SMEMCPY || defined __DOXYGEN__ +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) +#endif +/** + * @} + */ + +/* + ------------------------------------ + ----------- Core locking ----------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_lock Core locking and MPU + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_MPU_COMPATIBLE: enables special memory management mechanism + * which makes lwip able to work on MPU (Memory Protection Unit) system + * by not passing stack-pointers to other threads + * (this decreases performance as memory is allocated from pools instead + * of keeping it on the stack) + */ +#if !defined LWIP_MPU_COMPATIBLE || defined __DOXYGEN__ +#define LWIP_MPU_COMPATIBLE 0 +#endif + +/** + * LWIP_TCPIP_CORE_LOCKING + * Creates a global mutex that is held during TCPIP thread operations. + * Can be locked by client code to perform lwIP operations without changing + * into TCPIP thread using callbacks. See LOCK_TCPIP_CORE() and + * UNLOCK_TCPIP_CORE(). + * Your system should provide mutexes supporting priority inversion to use this. + */ +#if !defined LWIP_TCPIP_CORE_LOCKING || defined __DOXYGEN__ +#define LWIP_TCPIP_CORE_LOCKING 1 +#endif + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: when LWIP_TCPIP_CORE_LOCKING is enabled, + * this lets tcpip_input() grab the mutex for input packets as well, + * instead of allocating a message and passing it to tcpip_thread. + * + * ATTENTION: this does not work when tcpip_input() is called from + * interrupt context! + */ +#if !defined LWIP_TCPIP_CORE_LOCKING_INPUT || defined __DOXYGEN__ +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 +#endif + +/** + * SYS_LIGHTWEIGHT_PROT==1: enable inter-task protection (and task-vs-interrupt + * protection) for certain critical regions during buffer allocation, deallocation + * and memory allocation and deallocation. + * ATTENTION: This is required when using lwIP from more than one context! If + * you disable this, you must be sure what you are doing! + */ +#if !defined SYS_LIGHTWEIGHT_PROT || defined __DOXYGEN__ +#define SYS_LIGHTWEIGHT_PROT 1 +#endif +/** + * @} + */ + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_mem Heap and memory pools + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#if !defined MEM_LIBC_MALLOC || defined __DOXYGEN__ +#define MEM_LIBC_MALLOC 0 +#endif + +/** + * MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator. + * Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution + * speed (heap alloc can be much slower than pool alloc) and usage from interrupts + * (especially if your netif driver allocates PBUF_POOL pbufs for received frames + * from interrupt)! + * ATTENTION: Currently, this uses the heap for ALL pools (also for private pools, + * not only for internal pools defined in memp_std.h)! + */ +#if !defined MEMP_MEM_MALLOC || defined __DOXYGEN__ +#define MEMP_MEM_MALLOC 0 +#endif + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> \#define MEM_ALIGNMENT 4 + * 2 byte alignment -> \#define MEM_ALIGNMENT 2 + */ +#if !defined MEM_ALIGNMENT || defined __DOXYGEN__ +#define MEM_ALIGNMENT 1 +#endif + +/** + * MEM_SIZE: the size of the heap memory. If the application will send + * a lot of data that needs to be copied, this should be set high. + */ +#if !defined MEM_SIZE || defined __DOXYGEN__ +#define MEM_SIZE 1600 +#endif + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#if !defined MEMP_OVERFLOW_CHECK || defined __DOXYGEN__ +#define MEMP_OVERFLOW_CHECK 1 +#endif + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ +#if !defined MEMP_SANITY_CHECK || defined __DOXYGEN__ +#define MEMP_SANITY_CHECK 1 +#endif + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the length needed is returned. + * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled. + */ +#if !defined MEM_USE_POOLS || defined __DOXYGEN__ +#define MEM_USE_POOLS 0 +#endif + +/** + * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next + * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more + * reliable. */ +#if !defined MEM_USE_POOLS_TRY_BIGGER_POOL || defined __DOXYGEN__ +#define MEM_USE_POOLS_TRY_BIGGER_POOL 0 +#endif + +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * include path somewhere. + */ +#if !defined MEMP_USE_CUSTOM_POOLS || defined __DOXYGEN__ +#define MEMP_USE_CUSTOM_POOLS 0 +#endif + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#if !defined LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT || defined __DOXYGEN__ +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 +#endif +/** + * @} + */ + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * @defgroup lwip_opts_memp Internal memory pools + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ +#if !defined MEMP_NUM_PBUF || defined __DOXYGEN__ +#define MEMP_NUM_PBUF 16 +#endif + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#if !defined MEMP_NUM_RAW_PCB || defined __DOXYGEN__ +#define MEMP_NUM_RAW_PCB 4 +#endif + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#if !defined MEMP_NUM_UDP_PCB || defined __DOXYGEN__ +#define MEMP_NUM_UDP_PCB 4 +#endif + +/** + * MEMP_NUM_TCP_PCB: the number of simultaneously active TCP connections. + * (requires the LWIP_TCP option) + */ +#if !defined MEMP_NUM_TCP_PCB || defined __DOXYGEN__ +#define MEMP_NUM_TCP_PCB 5 +#endif + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#if !defined MEMP_NUM_TCP_PCB_LISTEN || defined __DOXYGEN__ +#define MEMP_NUM_TCP_PCB_LISTEN 8 +#endif + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ +#if !defined MEMP_NUM_TCP_SEG || defined __DOXYGEN__ +#define MEMP_NUM_TCP_SEG 16 +#endif + +/** + * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for + * reassembly (whole packets, not fragments!) + */ +#if !defined MEMP_NUM_REASSDATA || defined __DOXYGEN__ +#define MEMP_NUM_REASSDATA 5 +#endif + +/** + * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent + * (fragments, not whole packets!). + * This is only used with LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 + * with DMA-enabled MACs where the packet is not yet sent when netif->output + * returns. + */ +#if !defined MEMP_NUM_FRAG_PBUF || defined __DOXYGEN__ +#define MEMP_NUM_FRAG_PBUF 15 +#endif + +/** + * MEMP_NUM_ARP_QUEUE: the number of simultaneously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#if !defined MEMP_NUM_ARP_QUEUE || defined __DOXYGEN__ +#define MEMP_NUM_ARP_QUEUE 30 +#endif + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members at the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#if !defined MEMP_NUM_IGMP_GROUP || defined __DOXYGEN__ +#define MEMP_NUM_IGMP_GROUP 8 +#endif + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active timeouts. + * The default number of timeouts is calculated here for all enabled modules. + * The formula expects settings to be either '0' or '1'. + */ +#if !defined MEMP_NUM_SYS_TIMEOUT || defined __DOXYGEN__ +#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + (PPP_SUPPORT*6*MEMP_NUM_PPP_PCB) + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)) +#endif + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ +#if !defined MEMP_NUM_NETBUF || defined __DOXYGEN__ +#define MEMP_NUM_NETBUF 2 +#endif + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ +#if !defined MEMP_NUM_NETCONN || defined __DOXYGEN__ +#define MEMP_NUM_NETCONN 4 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#if !defined MEMP_NUM_TCPIP_MSG_API || defined __DOXYGEN__ +#define MEMP_NUM_TCPIP_MSG_API 8 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#if !defined MEMP_NUM_TCPIP_MSG_INPKT || defined __DOXYGEN__ +#define MEMP_NUM_TCPIP_MSG_INPKT 8 +#endif + +/** + * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls + * (before freeing the corresponding memory using lwip_freeaddrinfo()). + */ +#if !defined MEMP_NUM_NETDB || defined __DOXYGEN__ +#define MEMP_NUM_NETDB 1 +#endif + +/** + * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list + * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1. + */ +#if !defined MEMP_NUM_LOCALHOSTLIST || defined __DOXYGEN__ +#define MEMP_NUM_LOCALHOSTLIST 1 +#endif + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#if !defined PBUF_POOL_SIZE || defined __DOXYGEN__ +#define PBUF_POOL_SIZE 16 +#endif + +/** MEMP_NUM_API_MSG: the number of concurrently active calls to various + * socket, netconn, and tcpip functions + */ +#if !defined MEMP_NUM_API_MSG || defined __DOXYGEN__ +#define MEMP_NUM_API_MSG MEMP_NUM_TCPIP_MSG_API +#endif + +/** MEMP_NUM_DNS_API_MSG: the number of concurrently active calls to netconn_gethostbyname + */ +#if !defined MEMP_NUM_DNS_API_MSG || defined __DOXYGEN__ +#define MEMP_NUM_DNS_API_MSG MEMP_NUM_TCPIP_MSG_API +#endif + +/** MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA: the number of concurrently active calls + * to getsockopt/setsockopt + */ +#if !defined MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA || defined __DOXYGEN__ +#define MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA MEMP_NUM_TCPIP_MSG_API +#endif + +/** MEMP_NUM_NETIFAPI_MSG: the number of concurrently active calls to the + * netifapi functions + */ +#if !defined MEMP_NUM_NETIFAPI_MSG || defined __DOXYGEN__ +#define MEMP_NUM_NETIFAPI_MSG MEMP_NUM_TCPIP_MSG_API +#endif +/** + * @} + */ + +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * @defgroup lwip_opts_arp ARP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#if !defined LWIP_ARP || defined __DOXYGEN__ +#define LWIP_ARP 1 +#endif + +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ +#if !defined ARP_TABLE_SIZE || defined __DOXYGEN__ +#define ARP_TABLE_SIZE 10 +#endif + +/** the time an ARP entry stays valid after its last update, + * for ARP_TMR_INTERVAL = 1000, this is + * (60 * 5) seconds = 5 minutes. + */ +#if !defined ARP_MAXAGE || defined __DOXYGEN__ +#define ARP_MAXAGE 300 +#endif + +/** + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. + */ +#if !defined ARP_QUEUEING || defined __DOXYGEN__ +#define ARP_QUEUEING 0 +#endif + +/** The maximum number of packets which may be queued for each + * unresolved address by other network layers. Defaults to 3, 0 means disabled. + * Old packets are dropped, new packets are queued. + */ +#if !defined ARP_QUEUE_LEN || defined __DOXYGEN__ +#define ARP_QUEUE_LEN 3 +#endif + +/** + * ETHARP_SUPPORT_VLAN==1: support receiving and sending ethernet packets with + * VLAN header. See the description of LWIP_HOOK_VLAN_CHECK and + * LWIP_HOOK_VLAN_SET hooks to check/set VLAN headers. + * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. + * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. + * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) + * that returns 1 to accept a packet or 0 to drop a packet. + */ +#if !defined ETHARP_SUPPORT_VLAN || defined __DOXYGEN__ +#define ETHARP_SUPPORT_VLAN 0 +#endif + +/** LWIP_ETHERNET==1: enable ethernet support even though ARP might be disabled + */ +#if !defined LWIP_ETHERNET || defined __DOXYGEN__ +#define LWIP_ETHERNET LWIP_ARP +#endif + +/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure + * alignment of payload after that header. Since the header is 14 bytes long, + * without this padding e.g. addresses in the IP header will not be aligned + * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms. + */ +#if !defined ETH_PAD_SIZE || defined __DOXYGEN__ +#define ETH_PAD_SIZE 0 +#endif + +/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table + * entries (using etharp_add_static_entry/etharp_remove_static_entry). + */ +#if !defined ETHARP_SUPPORT_STATIC_ENTRIES || defined __DOXYGEN__ +#define ETHARP_SUPPORT_STATIC_ENTRIES 0 +#endif + +/** ETHARP_TABLE_MATCH_NETIF==1: Match netif for ARP table entries. + * If disabled, duplicate IP address on multiple netifs are not supported + * (but this should only occur for AutoIP). + */ +#if !defined ETHARP_TABLE_MATCH_NETIF || defined __DOXYGEN__ +#define ETHARP_TABLE_MATCH_NETIF 0 +#endif +/** + * @} + */ + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * @defgroup lwip_opts_ipv4 IPv4 + * @ingroup lwip_opts + * @{ + */ +/** + * LWIP_IPV4==1: Enable IPv4 + */ +#if !defined LWIP_IPV4 || defined __DOXYGEN__ +#define LWIP_IPV4 1 +#endif + +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ +#if !defined IP_FORWARD || defined __DOXYGEN__ +#define IP_FORWARD 0 +#endif + +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#if !defined IP_REASSEMBLY || defined __DOXYGEN__ +#define IP_REASSEMBLY 1 +#endif + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#if !defined IP_FRAG || defined __DOXYGEN__ +#define IP_FRAG 1 +#endif + +#if !LWIP_IPV4 +/* disable IPv4 extensions when IPv4 is disabled */ +#undef IP_FORWARD +#define IP_FORWARD 0 +#undef IP_REASSEMBLY +#define IP_REASSEMBLY 0 +#undef IP_FRAG +#define IP_FRAG 0 +#endif /* !LWIP_IPV4 */ + +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#if !defined IP_OPTIONS_ALLOWED || defined __DOXYGEN__ +#define IP_OPTIONS_ALLOWED 1 +#endif + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#if !defined IP_REASS_MAXAGE || defined __DOXYGEN__ +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#if !defined IP_REASS_MAX_PBUFS || defined __DOXYGEN__ +#define IP_REASS_MAX_PBUFS 10 +#endif + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#if !defined IP_DEFAULT_TTL || defined __DOXYGEN__ +#define IP_DEFAULT_TTL 255 +#endif + +/** + * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. To enable broadcast filter + * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1. + */ +#if !defined IP_SOF_BROADCAST || defined __DOXYGEN__ +#define IP_SOF_BROADCAST 0 +#endif + +/** + * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast + * filter on recv operations. + */ +#if !defined IP_SOF_BROADCAST_RECV || defined __DOXYGEN__ +#define IP_SOF_BROADCAST_RECV 0 +#endif + +/** + * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back + * out on the netif where it was received. This should only be used for + * wireless networks. + * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming + * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags! + */ +#if !defined IP_FORWARD_ALLOW_TX_ON_RX_NETIF || defined __DOXYGEN__ +#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0 +#endif + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#if !defined LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS || defined __DOXYGEN__ +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0 +#endif +/** + * @} + */ + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_icmp ICMP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#if !defined LWIP_ICMP || defined __DOXYGEN__ +#define LWIP_ICMP 1 +#endif + +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#if !defined ICMP_TTL || defined __DOXYGEN__ +#define ICMP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only) + */ +#if !defined LWIP_BROADCAST_PING || defined __DOXYGEN__ +#define LWIP_BROADCAST_PING 0 +#endif + +/** + * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only) + */ +#if !defined LWIP_MULTICAST_PING || defined __DOXYGEN__ +#define LWIP_MULTICAST_PING 0 +#endif +/** + * @} + */ + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * @defgroup lwip_opts_raw RAW + * @ingroup lwip_opts_callback + * @{ + */ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#if !defined LWIP_RAW || defined __DOXYGEN__ +#define LWIP_RAW 1 +#endif + +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#if !defined RAW_TTL || defined __DOXYGEN__ +#define RAW_TTL (IP_DEFAULT_TTL) +#endif +/** + * @} + */ + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_dhcp DHCP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#if !defined LWIP_DHCP || defined __DOXYGEN__ +#define LWIP_DHCP 0 +#endif +#if !LWIP_IPV4 +/* disable DHCP when IPv4 is disabled */ +#undef LWIP_DHCP +#define LWIP_DHCP 0 +#endif /* !LWIP_IPV4 */ + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#if !defined DHCP_DOES_ARP_CHECK || defined __DOXYGEN__ +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +/** + * LWIP_DHCP_CHECK_LINK_UP==1: dhcp_start() only really starts if the netif has + * NETIF_FLAG_LINK_UP set in its flags. As this is only an optimization and + * netif drivers might not set this flag, the default is off. If enabled, + * netif_set_link_up() must be called to continue dhcp starting. + */ +#if !defined LWIP_DHCP_CHECK_LINK_UP +#define LWIP_DHCP_CHECK_LINK_UP 0 +#endif + +/** + * LWIP_DHCP_BOOTP_FILE==1: Store offered_si_addr and boot_file_name. + */ +#if !defined LWIP_DHCP_BOOTP_FILE || defined __DOXYGEN__ +#define LWIP_DHCP_BOOTP_FILE 0 +#endif + +/** + * LWIP_DHCP_GETS_NTP==1: Request NTP servers with discover/select. For each + * response packet, an callback is called, which has to be provided by the port: + * void dhcp_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs); +*/ +#if !defined LWIP_DHCP_GET_NTP_SRV || defined __DOXYGEN__ +#define LWIP_DHCP_GET_NTP_SRV 0 +#endif + +/** + * The maximum of NTP servers requested + */ +#if !defined LWIP_DHCP_MAX_NTP_SERVERS || defined __DOXYGEN__ +#define LWIP_DHCP_MAX_NTP_SERVERS 1 +#endif + +/** + * LWIP_DHCP_MAX_DNS_SERVERS > 0: Request DNS servers with discover/select. + * DHCP servers received in the response are passed to DNS via @ref dns_setserver() + * (up to the maximum limit defined here). + */ +#if !defined LWIP_DHCP_MAX_DNS_SERVERS || defined __DOXYGEN__ +#define LWIP_DHCP_MAX_DNS_SERVERS DNS_MAX_SERVERS +#endif +/** + * @} + */ + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_autoip AUTOIP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#if !defined LWIP_AUTOIP || defined __DOXYGEN__ +#define LWIP_AUTOIP 0 +#endif +#if !LWIP_IPV4 +/* disable AUTOIP when IPv4 is disabled */ +#undef LWIP_AUTOIP +#define LWIP_AUTOIP 0 +#endif /* !LWIP_IPV4 */ + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#if !defined LWIP_DHCP_AUTOIP_COOP || defined __DOXYGEN__ +#define LWIP_DHCP_AUTOIP_COOP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes + * that should be sent before falling back on AUTOIP (the DHCP client keeps + * running in this case). This can be set as low as 1 to get an AutoIP address + * very quickly, but you should be prepared to handle a changing IP address + * when DHCP overrides AutoIP. + */ +#if !defined LWIP_DHCP_AUTOIP_COOP_TRIES || defined __DOXYGEN__ +#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 +#endif +/** + * @} + */ + +/* + ---------------------------------- + ----- SNMP MIB2 support ----- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_mib2 SNMP MIB2 callbacks + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_MIB2_CALLBACKS==1: Turn on SNMP MIB2 callbacks. + * Turn this on to get callbacks needed to implement MIB2. + * Usually MIB2_STATS should be enabled, too. + */ +#if !defined LWIP_MIB2_CALLBACKS || defined __DOXYGEN__ +#define LWIP_MIB2_CALLBACKS 0 +#endif +/** + * @} + */ + +/* + ---------------------------------- + ----- Multicast/IGMP options ----- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_igmp IGMP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#if !defined LWIP_IGMP || defined __DOXYGEN__ +#define LWIP_IGMP 0 +#endif +#if !LWIP_IPV4 +#undef LWIP_IGMP +#define LWIP_IGMP 0 +#endif + +/** + * LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options + * IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP + */ +#if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__ +#define LWIP_MULTICAST_TX_OPTIONS (LWIP_IGMP && LWIP_UDP) +#endif +/** + * @} + */ + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_dns DNS + * @ingroup lwip_opts_callback + * @{ + */ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#if !defined LWIP_DNS || defined __DOXYGEN__ +#define LWIP_DNS 0 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#if !defined DNS_TABLE_SIZE || defined __DOXYGEN__ +#define DNS_TABLE_SIZE 4 +#endif + +/** DNS maximum host name length supported in the name table. */ +#if !defined DNS_MAX_NAME_LENGTH || defined __DOXYGEN__ +#define DNS_MAX_NAME_LENGTH 256 +#endif + +/** The maximum of DNS servers + * The first server can be initialized automatically by defining + * DNS_SERVER_ADDRESS(ipaddr), where 'ipaddr' is an 'ip_addr_t*' + */ +#if !defined DNS_MAX_SERVERS || defined __DOXYGEN__ +#define DNS_MAX_SERVERS 2 +#endif + +/** DNS do a name checking between the query and the response. */ +#if !defined DNS_DOES_NAME_CHECK || defined __DOXYGEN__ +#define DNS_DOES_NAME_CHECK 1 +#endif + +/** LWIP_DNS_SECURE: controls the security level of the DNS implementation + * Use all DNS security features by default. + * This is overridable but should only be needed by very small targets + * or when using against non standard DNS servers. */ +#if !defined LWIP_DNS_SECURE || defined __DOXYGEN__ +#define LWIP_DNS_SECURE (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT) +#endif + +/* A list of DNS security features follows */ +#define LWIP_DNS_SECURE_RAND_XID 1 +#define LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING 2 +#define LWIP_DNS_SECURE_RAND_SRC_PORT 4 + +/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, you have to define an initializer: + * \#define DNS_LOCAL_HOSTLIST_INIT {DNS_LOCAL_HOSTLIST_ELEM("host_ip4", IPADDR4_INIT_BYTES(1,2,3,4)), \ + * DNS_LOCAL_HOSTLIST_ELEM("host_ip6", IPADDR6_INIT_HOST(123, 234, 345, 456)} + * + * Instead, you can also use an external function: + * \#define DNS_LOOKUP_LOCAL_EXTERN(x) extern err_t my_lookup_function(const char *name, ip_addr_t *addr, u8_t dns_addrtype) + * that looks up the IP address and returns ERR_OK if found (LWIP_DNS_ADDRTYPE_xxx is passed in dns_addrtype). + */ +#if !defined DNS_LOCAL_HOSTLIST || defined __DOXYGEN__ +#define DNS_LOCAL_HOSTLIST 0 +#endif /* DNS_LOCAL_HOSTLIST */ + +/** If this is turned on, the local host-list can be dynamically changed + * at runtime. */ +#if !defined DNS_LOCAL_HOSTLIST_IS_DYNAMIC || defined __DOXYGEN__ +#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/** Set this to 1 to enable querying ".local" names via mDNS + * using a One-Shot Multicast DNS Query */ +#if !defined LWIP_DNS_SUPPORT_MDNS_QUERIES || defined __DOXYGEN__ +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 0 +#endif +/** + * @} + */ + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * @defgroup lwip_opts_udp UDP + * @ingroup lwip_opts_callback + * @{ + */ +/** + * LWIP_UDP==1: Turn on UDP. + */ +#if !defined LWIP_UDP || defined __DOXYGEN__ +#define LWIP_UDP 1 +#endif + +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#if !defined LWIP_UDPLITE || defined __DOXYGEN__ +#define LWIP_UDPLITE 0 +#endif + +/** + * UDP_TTL: Default Time-To-Live value. + */ +#if !defined UDP_TTL || defined __DOXYGEN__ +#define UDP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf. + */ +#if !defined LWIP_NETBUF_RECVINFO || defined __DOXYGEN__ +#define LWIP_NETBUF_RECVINFO 0 +#endif +/** + * @} + */ + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * @defgroup lwip_opts_tcp TCP + * @ingroup lwip_opts_callback + * @{ + */ +/** + * LWIP_TCP==1: Turn on TCP. + */ +#if !defined LWIP_TCP || defined __DOXYGEN__ +#define LWIP_TCP 1 +#endif + +/** + * TCP_TTL: Default Time-To-Live value. + */ +#if !defined TCP_TTL || defined __DOXYGEN__ +#define TCP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well. + * ATTENTION: when using TCP_RCV_SCALE, TCP_WND is the total size + * with scaling applied. Maximum window value in the TCP header + * will be TCP_WND >> TCP_RCV_SCALE + */ +#if !defined TCP_WND || defined __DOXYGEN__ +#define TCP_WND (4 * TCP_MSS) +#endif + +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#if !defined TCP_MAXRTX || defined __DOXYGEN__ +#define TCP_MAXRTX 12 +#endif + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#if !defined TCP_SYNMAXRTX || defined __DOXYGEN__ +#define TCP_SYNMAXRTX 6 +#endif + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#if !defined TCP_QUEUE_OOSEQ || defined __DOXYGEN__ +#define TCP_QUEUE_OOSEQ (LWIP_TCP) +#endif + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#if !defined TCP_MSS || defined __DOXYGEN__ +#define TCP_MSS 536 +#endif + +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#if !defined TCP_CALCULATE_EFF_SEND_MSS || defined __DOXYGEN__ +#define TCP_CALCULATE_EFF_SEND_MSS 1 +#endif + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + * To achieve good performance, this should be at least 2 * TCP_MSS. + */ +#if !defined TCP_SND_BUF || defined __DOXYGEN__ +#define TCP_SND_BUF (2 * TCP_MSS) +#endif + +/** + * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. + */ +#if !defined TCP_SND_QUEUELEN || defined __DOXYGEN__ +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than + * TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). + */ +#if !defined TCP_SNDLOWAT || defined __DOXYGEN__ +#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#endif + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less + * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below + * this number, select returns writable (combined with TCP_SNDLOWAT). + */ +#if !defined TCP_SNDQUEUELOWAT || defined __DOXYGEN__ +#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) +#endif + +/** + * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1. + */ +#if !defined TCP_OOSEQ_MAX_BYTES || defined __DOXYGEN__ +#define TCP_OOSEQ_MAX_BYTES 0 +#endif + +/** + * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1. + */ +#if !defined TCP_OOSEQ_MAX_PBUFS || defined __DOXYGEN__ +#define TCP_OOSEQ_MAX_PBUFS 0 +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#if !defined TCP_LISTEN_BACKLOG || defined __DOXYGEN__ +#define TCP_LISTEN_BACKLOG 0 +#endif + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#if !defined TCP_DEFAULT_LISTEN_BACKLOG || defined __DOXYGEN__ +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff +#endif + +/** + * TCP_OVERSIZE: The maximum number of bytes that tcp_write may + * allocate ahead of time in an attempt to create shorter pbuf chains + * for transmission. The meaningful range is 0 to TCP_MSS. Some + * suggested values are: + * + * 0: Disable oversized allocation. Each tcp_write() allocates a new + pbuf (old behaviour). + * 1: Allocate size-aligned pbufs with minimal excess. Use this if your + * scatter-gather DMA requires aligned fragments. + * 128: Limit the pbuf/memory overhead to 20%. + * TCP_MSS: Try to create unfragmented TCP packets. + * TCP_MSS/4: Try to create 4 fragments or less per TCP packet. + */ +#if !defined TCP_OVERSIZE || defined __DOXYGEN__ +#define TCP_OVERSIZE TCP_MSS +#endif + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + * The timestamp option is currently only used to help remote hosts, it is not + * really used locally. Therefore, it is only enabled when a TS option is + * received in the initial SYN packet from a remote host. + */ +#if !defined LWIP_TCP_TIMESTAMPS || defined __DOXYGEN__ +#define LWIP_TCP_TIMESTAMPS 0 +#endif + +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#if !defined TCP_WND_UPDATE_THRESHOLD || defined __DOXYGEN__ +#define TCP_WND_UPDATE_THRESHOLD LWIP_MIN((TCP_WND / 4), (TCP_MSS * 4)) +#endif + +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. This is the default. + */ +#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) || defined __DOXYGEN__ +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 +#else +#ifndef LWIP_EVENT_API +#define LWIP_EVENT_API 0 +#endif +#ifndef LWIP_CALLBACK_API +#define LWIP_CALLBACK_API 0 +#endif +#endif + +/** + * LWIP_WND_SCALE and TCP_RCV_SCALE: + * Set LWIP_WND_SCALE to 1 to enable window scaling. + * Set TCP_RCV_SCALE to the desired scaling factor (shift count in the + * range of [0..14]). + * When LWIP_WND_SCALE is enabled but TCP_RCV_SCALE is 0, we can use a large + * send window while having a small receive window only. + */ +#if !defined LWIP_WND_SCALE || defined __DOXYGEN__ +#define LWIP_WND_SCALE 0 +#define TCP_RCV_SCALE 0 +#endif +/** + * @} + */ + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_pbuf PBUF + * @ingroup lwip_opts + * @{ + */ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#if !defined PBUF_LINK_HLEN || defined __DOXYGEN__ +#if defined LWIP_HOOK_VLAN_SET && !defined __DOXYGEN__ +#define PBUF_LINK_HLEN (18 + ETH_PAD_SIZE) +#else /* LWIP_HOOK_VLAN_SET */ +#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE) +#endif /* LWIP_HOOK_VLAN_SET */ +#endif + +/** + * PBUF_LINK_ENCAPSULATION_HLEN: the number of bytes that should be allocated + * for an additional encapsulation header before ethernet headers (e.g. 802.11) + */ +#if !defined PBUF_LINK_ENCAPSULATION_HLEN || defined __DOXYGEN__ +#define PBUF_LINK_ENCAPSULATION_HLEN 0u +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accommodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#if !defined PBUF_POOL_BUFSIZE || defined __DOXYGEN__ +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN) +#endif +/** + * @} + */ + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * @defgroup lwip_opts_netif NETIF + * @ingroup lwip_opts + * @{ + */ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#if !defined LWIP_NETIF_HOSTNAME || defined __DOXYGEN__ +#define LWIP_NETIF_HOSTNAME 0 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#if !defined LWIP_NETIF_API || defined __DOXYGEN__ +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquisition) + */ +#if !defined LWIP_NETIF_STATUS_CALLBACK || defined __DOXYGEN__ +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#if !defined LWIP_NETIF_LINK_CALLBACK || defined __DOXYGEN__ +#define LWIP_NETIF_LINK_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called + * when a netif has been removed + */ +#if !defined LWIP_NETIF_REMOVE_CALLBACK || defined __DOXYGEN__ +#define LWIP_NETIF_REMOVE_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#if !defined LWIP_NETIF_HWADDRHINT || defined __DOXYGEN__ +#define LWIP_NETIF_HWADDRHINT 0 +#endif + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + * + * @todo: TCP and IP-frag do not work with this, yet: + */ +#if !defined LWIP_NETIF_TX_SINGLE_PBUF || defined __DOXYGEN__ +#define LWIP_NETIF_TX_SINGLE_PBUF 0 +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + +/** + * LWIP_NUM_NETIF_CLIENT_DATA: Number of clients that may store + * data in client_data member array of struct netif. + */ +#if !defined LWIP_NUM_NETIF_CLIENT_DATA || defined __DOXYGEN__ +#define LWIP_NUM_NETIF_CLIENT_DATA 1 +#endif +/** + * @} + */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_loop Loopback interface + * @ingroup lwip_opts_netif + * @{ + */ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1). + * This is only needed when no real netifs are available. If at least one other + * netif is available, loopback traffic uses this netif. + */ +#if !defined LWIP_HAVE_LOOPIF || defined __DOXYGEN__ +#define LWIP_HAVE_LOOPIF LWIP_NETIF_LOOPBACK +#endif + +/** + * LWIP_LOOPIF_MULTICAST==1: Support multicast/IGMP on loop interface (127.0.0.1). + */ +#if !defined LWIP_LOOPIF_MULTICAST || defined __DOXYGEN__ +#define LWIP_LOOPIF_MULTICAST 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#if !defined LWIP_NETIF_LOOPBACK || defined __DOXYGEN__ +#define LWIP_NETIF_LOOPBACK 0 +#endif + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#if !defined LWIP_LOOPBACK_MAX_PBUFS || defined __DOXYGEN__ +#define LWIP_LOOPBACK_MAX_PBUFS 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#if !defined LWIP_NETIF_LOOPBACK_MULTITHREADING || defined __DOXYGEN__ +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +#endif +/** + * @} + */ + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_thread Threading + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#if !defined TCPIP_THREAD_NAME || defined __DOXYGEN__ +#define TCPIP_THREAD_NAME "tcpip_thread" +#endif + +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#if !defined TCPIP_THREAD_STACKSIZE || defined __DOXYGEN__ +#define TCPIP_THREAD_STACKSIZE 0 +#endif + +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#if !defined TCPIP_THREAD_PRIO || defined __DOXYGEN__ +#define TCPIP_THREAD_PRIO 1 +#endif + +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#if !defined TCPIP_MBOX_SIZE || defined __DOXYGEN__ +#define TCPIP_MBOX_SIZE 0 +#endif + +/** + * Define this to something that triggers a watchdog. This is called from + * tcpip_thread after processing a message. + */ +#if !defined LWIP_TCPIP_THREAD_ALIVE || defined __DOXYGEN__ +#define LWIP_TCPIP_THREAD_ALIVE() +#endif + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#if !defined SLIPIF_THREAD_NAME || defined __DOXYGEN__ +#define SLIPIF_THREAD_NAME "slipif_loop" +#endif + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#if !defined SLIPIF_THREAD_STACKSIZE || defined __DOXYGEN__ +#define SLIPIF_THREAD_STACKSIZE 0 +#endif + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#if !defined SLIPIF_THREAD_PRIO || defined __DOXYGEN__ +#define SLIPIF_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#if !defined DEFAULT_THREAD_NAME || defined __DOXYGEN__ +#define DEFAULT_THREAD_NAME "lwIP" +#endif + +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#if !defined DEFAULT_THREAD_STACKSIZE || defined __DOXYGEN__ +#define DEFAULT_THREAD_STACKSIZE 0 +#endif + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#if !defined DEFAULT_THREAD_PRIO || defined __DOXYGEN__ +#define DEFAULT_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#if !defined DEFAULT_RAW_RECVMBOX_SIZE || defined __DOXYGEN__ +#define DEFAULT_RAW_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#if !defined DEFAULT_UDP_RECVMBOX_SIZE || defined __DOXYGEN__ +#define DEFAULT_UDP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#if !defined DEFAULT_TCP_RECVMBOX_SIZE || defined __DOXYGEN__ +#define DEFAULT_TCP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#if !defined DEFAULT_ACCEPTMBOX_SIZE || defined __DOXYGEN__ +#define DEFAULT_ACCEPTMBOX_SIZE 0 +#endif +/** + * @} + */ + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * @defgroup lwip_opts_netconn Netconn + * @ingroup lwip_opts_threadsafe_apis + * @{ + */ +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#if !defined LWIP_NETCONN || defined __DOXYGEN__ +#define LWIP_NETCONN 1 +#endif + +/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout to create + * timers running in tcpip_thread from another thread. + */ +#if !defined LWIP_TCPIP_TIMEOUT || defined __DOXYGEN__ +#define LWIP_TCPIP_TIMEOUT 0 +#endif + +/** LWIP_NETCONN_SEM_PER_THREAD==1: Use one (thread-local) semaphore per + * thread calling socket/netconn functions instead of allocating one + * semaphore per netconn (and per select etc.) + * ATTENTION: a thread-local semaphore for API calls is needed: + * - LWIP_NETCONN_THREAD_SEM_GET() returning a sys_sem_t* + * - LWIP_NETCONN_THREAD_SEM_ALLOC() creating the semaphore + * - LWIP_NETCONN_THREAD_SEM_FREE() freeing the semaphore + * The latter 2 can be invoked up by calling netconn_thread_init()/netconn_thread_cleanup(). + * Ports may call these for threads created with sys_thread_new(). + */ +#if !defined LWIP_NETCONN_SEM_PER_THREAD || defined __DOXYGEN__ +#define LWIP_NETCONN_SEM_PER_THREAD 0 +#endif + +/** LWIP_NETCONN_FULLDUPLEX==1: Enable code that allows reading from one thread, + * writing from a 2nd thread and closing from a 3rd thread at the same time. + * ATTENTION: This is currently really alpha! Some requirements: + * - LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from + * multiple threads at once + * - sys_mbox_free() has to unblock receive tasks waiting on recvmbox/acceptmbox + * and prevent a task pending on this during/after deletion + */ +#if !defined LWIP_NETCONN_FULLDUPLEX || defined __DOXYGEN__ +#define LWIP_NETCONN_FULLDUPLEX 0 +#endif +/** + * @} + */ + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_socket Sockets + * @ingroup lwip_opts_threadsafe_apis + * @{ + */ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#if !defined LWIP_SOCKET || defined __DOXYGEN__ +#define LWIP_SOCKET 1 +#endif + +/* LWIP_SOCKET_SET_ERRNO==1: Set errno when socket functions cannot complete + * successfully, as required by POSIX. Default is POSIX-compliant. + */ +#if !defined LWIP_SOCKET_SET_ERRNO || defined __DOXYGEN__ +#define LWIP_SOCKET_SET_ERRNO 1 +#endif + +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names through defines. + * LWIP_COMPAT_SOCKETS==2: Same as ==1 but correctly named functions are created. + * While this helps code completion, it might conflict with existing libraries. + * (only used if you use sockets.c) + */ +#if !defined LWIP_COMPAT_SOCKETS || defined __DOXYGEN__ +#define LWIP_COMPAT_SOCKETS 1 +#endif + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#if !defined LWIP_POSIX_SOCKETS_IO_NAMES || defined __DOXYGEN__ +#define LWIP_POSIX_SOCKETS_IO_NAMES 1 +#endif + +/** + * LWIP_SOCKET_OFFSET==n: Increases the file descriptor number created by LwIP with n. + * This can be useful when there are multiple APIs which create file descriptors. + * When they all start with a different offset and you won't make them overlap you can + * re implement read/write/close/ioctl/fnctl to send the requested action to the right + * library (sharing select will need more work though). + */ +#if !defined LWIP_SOCKET_OFFSET || defined __DOXYGEN__ +#define LWIP_SOCKET_OFFSET 0 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#if !defined LWIP_TCP_KEEPALIVE || defined __DOXYGEN__ +#define LWIP_TCP_KEEPALIVE 0 +#endif + +/** + * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#if !defined LWIP_SO_SNDTIMEO || defined __DOXYGEN__ +#define LWIP_SO_SNDTIMEO 1 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. + */ +#if !defined LWIP_SO_RCVTIMEO || defined __DOXYGEN__ +#define LWIP_SO_RCVTIMEO 1 +#endif + +/** + * LWIP_SO_SNDRCVTIMEO_NONSTANDARD==1: SO_RCVTIMEO/SO_SNDTIMEO take an int + * (milliseconds, much like winsock does) instead of a struct timeval (default). + */ +#if !defined LWIP_SO_SNDRCVTIMEO_NONSTANDARD || defined __DOXYGEN__ +#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 0 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#if !defined LWIP_SO_RCVBUF || defined __DOXYGEN__ +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * LWIP_SO_LINGER==1: Enable SO_LINGER processing. + */ +#if !defined LWIP_SO_LINGER || defined __DOXYGEN__ +#define LWIP_SO_LINGER 0 +#endif + +/** + * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize. + */ +#if !defined RECV_BUFSIZE_DEFAULT || defined __DOXYGEN__ +#define RECV_BUFSIZE_DEFAULT INT_MAX +#endif + +/** + * By default, TCP socket/netconn close waits 20 seconds max to send the FIN + */ +#if !defined LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT || defined __DOXYGEN__ +#define LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT 5000 +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR option. + */ +#if !defined SO_REUSE || defined __DOXYGEN__ +#define SO_REUSE 0 +#endif + +/** + * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets + * to all local matches if SO_REUSEADDR is turned on. + * WARNING: Adds a memcpy for every packet if passing to more than one pcb! + */ +#if !defined SO_REUSE_RXTOALL || defined __DOXYGEN__ +#define SO_REUSE_RXTOALL 0 +#endif + +/** + * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of + * pending data in the network buffer. This is the way windows does it. It's + * the default for lwIP since it is smaller. + * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next + * pending datagram in bytes. This is the way linux does it. This code is only + * here for compatibility. + */ +#if !defined LWIP_FIONREAD_LINUXMODE || defined __DOXYGEN__ +#define LWIP_FIONREAD_LINUXMODE 0 +#endif +/** + * @} + */ + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * @defgroup lwip_opts_stats Statistics + * @ingroup lwip_opts_debug + * @{ + */ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#if !defined LWIP_STATS || defined __DOXYGEN__ +#define LWIP_STATS 1 +#endif + +#if LWIP_STATS + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#if !defined LWIP_STATS_DISPLAY || defined __DOXYGEN__ +#define LWIP_STATS_DISPLAY 0 +#endif + +/** + * LINK_STATS==1: Enable link stats. + */ +#if !defined LINK_STATS || defined __DOXYGEN__ +#define LINK_STATS 1 +#endif + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#if !defined ETHARP_STATS || defined __DOXYGEN__ +#define ETHARP_STATS (LWIP_ARP) +#endif + +/** + * IP_STATS==1: Enable IP stats. + */ +#if !defined IP_STATS || defined __DOXYGEN__ +#define IP_STATS 1 +#endif + +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ +#if !defined IPFRAG_STATS || defined __DOXYGEN__ +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) +#endif + +/** + * ICMP_STATS==1: Enable ICMP stats. + */ +#if !defined ICMP_STATS || defined __DOXYGEN__ +#define ICMP_STATS 1 +#endif + +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#if !defined IGMP_STATS || defined __DOXYGEN__ +#define IGMP_STATS (LWIP_IGMP) +#endif + +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#if !defined UDP_STATS || defined __DOXYGEN__ +#define UDP_STATS (LWIP_UDP) +#endif + +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#if !defined TCP_STATS || defined __DOXYGEN__ +#define TCP_STATS (LWIP_TCP) +#endif + +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#if !defined MEM_STATS || defined __DOXYGEN__ +#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0)) +#endif + +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#if !defined MEMP_STATS || defined __DOXYGEN__ +#define MEMP_STATS (MEMP_MEM_MALLOC == 0) +#endif + +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#if !defined SYS_STATS || defined __DOXYGEN__ +#define SYS_STATS (NO_SYS == 0) +#endif + +/** + * IP6_STATS==1: Enable IPv6 stats. + */ +#if !defined IP6_STATS || defined __DOXYGEN__ +#define IP6_STATS (LWIP_IPV6) +#endif + +/** + * ICMP6_STATS==1: Enable ICMP for IPv6 stats. + */ +#if !defined ICMP6_STATS || defined __DOXYGEN__ +#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6) +#endif + +/** + * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats. + */ +#if !defined IP6_FRAG_STATS || defined __DOXYGEN__ +#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS)) +#endif + +/** + * MLD6_STATS==1: Enable MLD for IPv6 stats. + */ +#if !defined MLD6_STATS || defined __DOXYGEN__ +#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD) +#endif + +/** + * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats. + */ +#if !defined ND6_STATS || defined __DOXYGEN__ +#define ND6_STATS (LWIP_IPV6) +#endif + +/** + * MIB2_STATS==1: Stats for SNMP MIB2. + */ +#if !defined MIB2_STATS || defined __DOXYGEN__ +#define MIB2_STATS 0 +#endif + +#else + +#define LINK_STATS 0 +#define ETHARP_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 +#define IP6_STATS 0 +#define ICMP6_STATS 0 +#define IP6_FRAG_STATS 0 +#define MLD6_STATS 0 +#define ND6_STATS 0 +#define MIB2_STATS 0 + +#endif /* LWIP_STATS */ +/** + * @} + */ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * @defgroup lwip_opts_checksum Checksum + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_CHECKSUM_CTRL_PER_NETIF==1: Checksum generation/check can be enabled/disabled + * per netif. + * ATTENTION: if enabled, the CHECKSUM_GEN_* and CHECKSUM_CHECK_* defines must be enabled! + */ +#if !defined LWIP_CHECKSUM_CTRL_PER_NETIF || defined __DOXYGEN__ +#define LWIP_CHECKSUM_CTRL_PER_NETIF 0 +#endif + +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ +#if !defined CHECKSUM_GEN_IP || defined __DOXYGEN__ +#define CHECKSUM_GEN_IP 1 +#endif + +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ +#if !defined CHECKSUM_GEN_UDP || defined __DOXYGEN__ +#define CHECKSUM_GEN_UDP 1 +#endif + +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ +#if !defined CHECKSUM_GEN_TCP || defined __DOXYGEN__ +#define CHECKSUM_GEN_TCP 1 +#endif + +/** + * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets. + */ +#if !defined CHECKSUM_GEN_ICMP || defined __DOXYGEN__ +#define CHECKSUM_GEN_ICMP 1 +#endif + +/** + * CHECKSUM_GEN_ICMP6==1: Generate checksums in software for outgoing ICMP6 packets. + */ +#if !defined CHECKSUM_GEN_ICMP6 || defined __DOXYGEN__ +#define CHECKSUM_GEN_ICMP6 1 +#endif + +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ +#if !defined CHECKSUM_CHECK_IP || defined __DOXYGEN__ +#define CHECKSUM_CHECK_IP 1 +#endif + +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ +#if !defined CHECKSUM_CHECK_UDP || defined __DOXYGEN__ +#define CHECKSUM_CHECK_UDP 1 +#endif + +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ +#if !defined CHECKSUM_CHECK_TCP || defined __DOXYGEN__ +#define CHECKSUM_CHECK_TCP 1 +#endif + +/** + * CHECKSUM_CHECK_ICMP==1: Check checksums in software for incoming ICMP packets. + */ +#if !defined CHECKSUM_CHECK_ICMP || defined __DOXYGEN__ +#define CHECKSUM_CHECK_ICMP 1 +#endif + +/** + * CHECKSUM_CHECK_ICMP6==1: Check checksums in software for incoming ICMPv6 packets + */ +#if !defined CHECKSUM_CHECK_ICMP6 || defined __DOXYGEN__ +#define CHECKSUM_CHECK_ICMP6 1 +#endif + +/** + * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from + * application buffers to pbufs. + */ +#if !defined LWIP_CHECKSUM_ON_COPY || defined __DOXYGEN__ +#define LWIP_CHECKSUM_ON_COPY 0 +#endif +/** + * @} + */ + +/* + --------------------------------------- + ---------- IPv6 options --------------- + --------------------------------------- +*/ +/** + * @defgroup lwip_opts_ipv6 IPv6 + * @ingroup lwip_opts + * @{ + */ +/** + * LWIP_IPV6==1: Enable IPv6 + */ +#if !defined LWIP_IPV6 || defined __DOXYGEN__ +#define LWIP_IPV6 0 +#endif + +/** + * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif. + */ +#if !defined LWIP_IPV6_NUM_ADDRESSES || defined __DOXYGEN__ +#define LWIP_IPV6_NUM_ADDRESSES 3 +#endif + +/** + * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs + */ +#if !defined LWIP_IPV6_FORWARD || defined __DOXYGEN__ +#define LWIP_IPV6_FORWARD 0 +#endif + +/** + * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big. + */ +#if !defined LWIP_IPV6_FRAG || defined __DOXYGEN__ +#define LWIP_IPV6_FRAG 0 +#endif + +/** + * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented + */ +#if !defined LWIP_IPV6_REASS || defined __DOXYGEN__ +#define LWIP_IPV6_REASS (LWIP_IPV6) +#endif + +/** + * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during + * network startup. + */ +#if !defined LWIP_IPV6_SEND_ROUTER_SOLICIT || defined __DOXYGEN__ +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 +#endif + +/** + * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862. + */ +#if !defined LWIP_IPV6_AUTOCONFIG || defined __DOXYGEN__ +#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6) +#endif + +/** + * LWIP_IPV6_DUP_DETECT_ATTEMPTS=[0..7]: Number of duplicate address detection attempts. + */ +#if !defined LWIP_IPV6_DUP_DETECT_ATTEMPTS || defined __DOXYGEN__ +#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1 +#endif +/** + * @} + */ + +/** + * @defgroup lwip_opts_icmp6 ICMP6 + * @ingroup lwip_opts_ipv6 + * @{ + */ +/** + * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC) + */ +#if !defined LWIP_ICMP6 || defined __DOXYGEN__ +#define LWIP_ICMP6 (LWIP_IPV6) +#endif + +/** + * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in + * ICMPv6 error messages. + */ +#if !defined LWIP_ICMP6_DATASIZE || defined __DOXYGEN__ +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/** + * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages + */ +#if !defined LWIP_ICMP6_HL || defined __DOXYGEN__ +#define LWIP_ICMP6_HL 255 +#endif +/** + * @} + */ + +/** + * @defgroup lwip_opts_mld6 Multicast listener discovery + * @ingroup lwip_opts_ipv6 + * @{ + */ +/** + * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol. + * If LWIP_IPV6 is enabled but this setting is disabled, the MAC layer must + * indiscriminately pass all inbound IPv6 multicast traffic to lwIP. + */ +#if !defined LWIP_IPV6_MLD || defined __DOXYGEN__ +#define LWIP_IPV6_MLD (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast groups that can be joined. + * There must be enough groups so that each netif can join the solicited-node + * multicast group for each of its local addresses, plus one for MDNS if + * applicable, plus any number of groups to be joined on UDP sockets. + */ +#if !defined MEMP_NUM_MLD6_GROUP || defined __DOXYGEN__ +#define MEMP_NUM_MLD6_GROUP 4 +#endif +/** + * @} + */ + +/** + * @defgroup lwip_opts_nd6 Neighbor discovery + * @ingroup lwip_opts_ipv6 + * @{ + */ +/** + * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address + * is being resolved. + */ +#if !defined LWIP_ND6_QUEUEING || defined __DOXYGEN__ +#define LWIP_ND6_QUEUEING (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution. + */ +#if !defined MEMP_NUM_ND6_QUEUE || defined __DOXYGEN__ +#define MEMP_NUM_ND6_QUEUE 20 +#endif + +/** + * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache + */ +#if !defined LWIP_ND6_NUM_NEIGHBORS || defined __DOXYGEN__ +#define LWIP_ND6_NUM_NEIGHBORS 10 +#endif + +/** + * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache + */ +#if !defined LWIP_ND6_NUM_DESTINATIONS || defined __DOXYGEN__ +#define LWIP_ND6_NUM_DESTINATIONS 10 +#endif + +/** + * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache + */ +#if !defined LWIP_ND6_NUM_PREFIXES || defined __DOXYGEN__ +#define LWIP_ND6_NUM_PREFIXES 5 +#endif + +/** + * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache + */ +#if !defined LWIP_ND6_NUM_ROUTERS || defined __DOXYGEN__ +#define LWIP_ND6_NUM_ROUTERS 3 +#endif + +/** + * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send + * (neighbor solicit and router solicit) + */ +#if !defined LWIP_ND6_MAX_MULTICAST_SOLICIT || defined __DOXYGEN__ +#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3 +#endif + +/** + * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages + * to send during neighbor reachability detection. + */ +#if !defined LWIP_ND6_MAX_UNICAST_SOLICIT || defined __DOXYGEN__ +#define LWIP_ND6_MAX_UNICAST_SOLICIT 3 +#endif + +/** + * Unused: See ND RFC (time in milliseconds). + */ +#if !defined LWIP_ND6_MAX_ANYCAST_DELAY_TIME || defined __DOXYGEN__ +#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000 +#endif + +/** + * Unused: See ND RFC + */ +#if !defined LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT || defined __DOXYGEN__ +#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3 +#endif + +/** + * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds). + * May be updated by router advertisement messages. + */ +#if !defined LWIP_ND6_REACHABLE_TIME || defined __DOXYGEN__ +#define LWIP_ND6_REACHABLE_TIME 30000 +#endif + +/** + * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages + */ +#if !defined LWIP_ND6_RETRANS_TIMER || defined __DOXYGEN__ +#define LWIP_ND6_RETRANS_TIMER 1000 +#endif + +/** + * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation + * message is sent, during neighbor reachability detection. + */ +#if !defined LWIP_ND6_DELAY_FIRST_PROBE_TIME || defined __DOXYGEN__ +#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000 +#endif + +/** + * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update + * Reachable time and retransmission timers, and netif MTU. + */ +#if !defined LWIP_ND6_ALLOW_RA_UPDATES || defined __DOXYGEN__ +#define LWIP_ND6_ALLOW_RA_UPDATES 1 +#endif + +/** + * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery + * with reachability hints for connected destinations. This helps avoid sending + * unicast neighbor solicitation messages. + */ +#if !defined LWIP_ND6_TCP_REACHABILITY_HINTS || defined __DOXYGEN__ +#define LWIP_ND6_TCP_REACHABILITY_HINTS 1 +#endif + +/** + * LWIP_ND6_RDNSS_MAX_DNS_SERVERS > 0: Use IPv6 Router Advertisement Recursive + * DNS Server Option (as per RFC 6106) to copy a defined maximum number of DNS + * servers to the DNS module. + */ +#if !defined LWIP_ND6_RDNSS_MAX_DNS_SERVERS || defined __DOXYGEN__ +#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 0 +#endif +/** + * @} + */ + +/** + * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration. + */ +#if !defined LWIP_IPV6_DHCP6 || defined __DOXYGEN__ +#define LWIP_IPV6_DHCP6 0 +#endif + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/** + * @defgroup lwip_opts_hooks Hooks + * @ingroup lwip_opts_infrastructure + * Hooks are undefined by default, define them to a function if you need them. + * @{ + */ + +/** + * LWIP_HOOK_FILENAME: Custom filename to #include in files that provide hooks. + * Declare your hook function prototypes in there, you may also #include all headers + * providing data types that are need in this file. + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_FILENAME "path/to/my/lwip_hooks.h" +#endif + +/** + * LWIP_HOOK_TCP_ISN: + * Hook for generation of the Initial Sequence Number (ISN) for a new TCP + * connection. The default lwIP ISN generation algorithm is very basic and may + * allow for TCP spoofing attacks. This hook provides the means to implement + * the standardized ISN generation algorithm from RFC 6528 (see contrib/adons/tcp_isn), + * or any other desired algorithm as a replacement. + * Called from tcp_connect() and tcp_listen_input() when an ISN is needed for + * a new TCP connection, if TCP support (@ref LWIP_TCP) is enabled.\n + * Signature: u32_t my_hook_tcp_isn(const ip_addr_t* local_ip, u16_t local_port, const ip_addr_t* remote_ip, u16_t remote_port); + * - it may be necessary to use "struct ip_addr" (ip4_addr, ip6_addr) instead of "ip_addr_t" in function declarations\n + * Arguments: + * - local_ip: pointer to the local IP address of the connection + * - local_port: local port number of the connection (host-byte order) + * - remote_ip: pointer to the remote IP address of the connection + * - remote_port: remote port number of the connection (host-byte order)\n + * Return value: + * - the 32-bit Initial Sequence Number to use for the new TCP connection. + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_TCP_ISN(local_ip, local_port, remote_ip, remote_port) +#endif + +/** + * LWIP_HOOK_IP4_INPUT(pbuf, input_netif): + * - called from ip_input() (IPv4) + * - pbuf: received struct pbuf passed to ip_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_IP4_INPUT(pbuf, input_netif) +#endif + +/** + * LWIP_HOOK_IP4_ROUTE(dest): + * - called from ip_route() (IPv4) + * - dest: destination IPv4 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip_route() continues as normal. + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_IP4_ROUTE() +#endif + +/** + * LWIP_HOOK_IP4_ROUTE_SRC(dest, src): + * - source-based routing for IPv4 (see LWIP_HOOK_IP4_ROUTE(), src may be NULL) + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_IP4_ROUTE_SRC(dest, src) +#endif + +/** + * LWIP_HOOK_ETHARP_GET_GW(netif, dest): + * - called from etharp_output() (IPv4) + * - netif: the netif used for sending + * - dest: the destination IPv4 address + * Returns the IPv4 address of the gateway to handle the specified destination + * IPv4 address. If NULL is returned, the netif's default gateway is used. + * The returned address MUST be directly reachable on the specified netif! + * This function is meant to implement advanced IPv4 routing together with + * LWIP_HOOK_IP4_ROUTE(). The actual routing/gateway table implementation is + * not part of lwIP but can e.g. be hidden in the netif's state argument. +*/ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_ETHARP_GET_GW(netif, dest) +#endif + +/** + * LWIP_HOOK_IP6_INPUT(pbuf, input_netif): + * - called from ip6_input() (IPv6) + * - pbuf: received struct pbuf passed to ip6_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_IP6_INPUT(pbuf, input_netif) +#endif + +/** + * LWIP_HOOK_IP6_ROUTE(src, dest): + * - called from ip6_route() (IPv6) + * - src: sourc IPv6 address + * - dest: destination IPv6 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip6_route() continues as normal. + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_IP6_ROUTE(src, dest) +#endif + +/** + * LWIP_HOOK_ND6_GET_GW(netif, dest): + * - called from nd6_get_next_hop_entry() (IPv6) + * - netif: the netif used for sending + * - dest: the destination IPv6 address + * Returns the IPv6 address of the next hop to handle the specified destination + * IPv6 address. If NULL is returned, a NDP-discovered router is used instead. + * The returned address MUST be directly reachable on the specified netif! + * This function is meant to implement advanced IPv6 routing together with + * LWIP_HOOK_IP6_ROUTE(). The actual routing/gateway table implementation is + * not part of lwIP but can e.g. be hidden in the netif's state argument. +*/ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_ND6_GET_GW(netif, dest) +#endif + +/** + * LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr): + * - called from ethernet_input() if VLAN support is enabled + * - netif: struct netif on which the packet has been received + * - eth_hdr: struct eth_hdr of the packet + * - vlan_hdr: struct eth_vlan_hdr of the packet + * Return values: + * - 0: Packet must be dropped. + * - != 0: Packet must be accepted. + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr) +#endif + +/** + * LWIP_HOOK_VLAN_SET: + * Hook can be used to set prio_vid field of vlan_hdr. If you need to store data + * on per-netif basis to implement this callback, see @ref netif_cd. + * Called from ethernet_output() if VLAN support (@ref ETHARP_SUPPORT_VLAN) is enabled.\n + * Signature: s32_t my_hook_vlan_set(struct netif* netif, struct pbuf* pbuf, const struct eth_addr* src, const struct eth_addr* dst, u16_t eth_type);\n + * Arguments: + * - netif: struct netif that the packet will be sent through + * - p: struct pbuf packet to be sent + * - src: source eth address + * - dst: destination eth address + * - eth_type: ethernet type to packet to be sent\n + * + * + * Return values: + * - <0: Packet shall not contain VLAN header. + * - 0 <= return value <= 0xFFFF: Packet shall contain VLAN header. Return value is prio_vid in host byte order. + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type) +#endif + +/** + * LWIP_HOOK_MEMP_AVAILABLE(memp_t_type): + * - called from memp_free() when a memp pool was empty and an item is now available + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_MEMP_AVAILABLE(memp_t_type) +#endif + +/** + * LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif): + * Called from ethernet_input() when an unknown eth type is encountered. + * Return ERR_OK if packet is accepted, any error code otherwise. + * Payload points to ethernet header! + */ +#ifdef __DOXYGEN__ +#define LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif) +#endif +/** + * @} + */ + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +/** + * @defgroup lwip_opts_debugmsg Debug messages + * @ingroup lwip_opts_debug + * @{ + */ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + * @see debugging_levels + */ +#if !defined LWIP_DBG_MIN_LEVEL || defined __DOXYGEN__ +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL +#endif + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + * @see debugging_levels + */ +#if !defined LWIP_DBG_TYPES_ON || defined __DOXYGEN__ +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON +#endif + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#if !defined ETHARP_DEBUG || defined __DOXYGEN__ +#define ETHARP_DEBUG LWIP_DBG_OFF +#endif + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#if !defined NETIF_DEBUG || defined __DOXYGEN__ +#define NETIF_DEBUG LWIP_DBG_OFF +#endif + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#if !defined PBUF_DEBUG || defined __DOXYGEN__ +#define PBUF_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#if !defined API_LIB_DEBUG || defined __DOXYGEN__ +#define API_LIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#if !defined API_MSG_DEBUG || defined __DOXYGEN__ +#define API_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#if !defined SOCKETS_DEBUG || defined __DOXYGEN__ +#define SOCKETS_DEBUG LWIP_DBG_ON +#endif + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#if !defined ICMP_DEBUG || defined __DOXYGEN__ +#define ICMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#if !defined IGMP_DEBUG || defined __DOXYGEN__ +#define IGMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#if !defined INET_DEBUG || defined __DOXYGEN__ +#define INET_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#if !defined IP_DEBUG || defined __DOXYGEN__ +#define IP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#if !defined IP_REASS_DEBUG || defined __DOXYGEN__ +#define IP_REASS_DEBUG LWIP_DBG_OFF +#endif + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#if !defined RAW_DEBUG || defined __DOXYGEN__ +#define RAW_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#if !defined MEM_DEBUG || defined __DOXYGEN__ +#define MEM_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#if !defined MEMP_DEBUG || defined __DOXYGEN__ +#define MEMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#if !defined SYS_DEBUG || defined __DOXYGEN__ +#define SYS_DEBUG LWIP_DBG_ON +#endif + +/** + * TIMERS_DEBUG: Enable debugging in timers.c. + */ +#if !defined TIMERS_DEBUG || defined __DOXYGEN__ +#define TIMERS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#if !defined TCP_DEBUG || defined __DOXYGEN__ +#define TCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#if !defined TCP_INPUT_DEBUG || defined __DOXYGEN__ +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#if !defined TCP_FR_DEBUG || defined __DOXYGEN__ +#define TCP_FR_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#if !defined TCP_RTO_DEBUG || defined __DOXYGEN__ +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#if !defined TCP_CWND_DEBUG || defined __DOXYGEN__ +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#if !defined TCP_WND_DEBUG || defined __DOXYGEN__ +#define TCP_WND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#if !defined TCP_OUTPUT_DEBUG || defined __DOXYGEN__ +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#if !defined TCP_RST_DEBUG || defined __DOXYGEN__ +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#if !defined TCP_QLEN_DEBUG || defined __DOXYGEN__ +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#endif + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#if !defined UDP_DEBUG || defined __DOXYGEN__ +#define UDP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#if !defined TCPIP_DEBUG || defined __DOXYGEN__ +#define TCPIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#if !defined SLIP_DEBUG || defined __DOXYGEN__ +#define SLIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#if !defined DHCP_DEBUG || defined __DOXYGEN__ +#define DHCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#if !defined AUTOIP_DEBUG || defined __DOXYGEN__ +#define AUTOIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#if !defined DNS_DEBUG || defined __DOXYGEN__ +#define DNS_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#if !defined IP6_DEBUG || defined __DOXYGEN__ +#define IP6_DEBUG LWIP_DBG_OFF +#endif + +/** + * @} + */ + +/* + -------------------------------------------------- + ---------- Performance tracking options ---------- + -------------------------------------------------- +*/ +/** + * @defgroup lwip_opts_perf Performance + * @ingroup lwip_opts_debug + * @{ + */ +/** + * LWIP_PERF: Enable performance testing for lwIP + * (if enabled, arch/perf.h is included) + */ +#if !defined LWIP_PERF || defined __DOXYGEN__ +#define LWIP_PERF 0 +#endif +/** + * @} + */ + +#endif /* LWIP_HDR_OPT_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/pbuf.h b/sdk/Src/Network/lwip2.0.3/include/lwip/pbuf.h new file mode 100644 index 0000000..765dcdd --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/pbuf.h @@ -0,0 +1,265 @@ +/** + * @file + * pbuf API + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef LWIP_HDR_PBUF_H +#define LWIP_HDR_PBUF_H + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "wm_socket.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** LWIP_SUPPORT_CUSTOM_PBUF==1: Custom pbufs behave much like their pbuf type + * but they are allocated by external code (initialised by calling + * pbuf_alloced_custom()) and when pbuf_free gives up their last reference, they + * are freed by calling pbuf_custom->custom_free_function(). + * Currently, the pbuf_custom code is only needed for one specific configuration + * of IP_FRAG, unless required by external driver/application code. */ +#ifndef LWIP_SUPPORT_CUSTOM_PBUF +#define LWIP_SUPPORT_CUSTOM_PBUF ((IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG)) +#endif + +/* @todo: We need a mechanism to prevent wasting memory in every pbuf + (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */ + +#define PBUF_TRANSPORT_HLEN 20 +#if LWIP_IPV6 +#define PBUF_IP_HLEN 40 +#else +#define PBUF_IP_HLEN 20 +#endif + +#if 0 +/** + * @ingroup pbuf + * Enumeration of pbuf layers + */ +typedef enum { + /** Includes spare room for transport layer header, e.g. UDP header. + * Use this if you intend to pass the pbuf to functions like udp_send(). + */ + PBUF_TRANSPORT, + /** Includes spare room for IP header. + * Use this if you intend to pass the pbuf to functions like raw_send(). + */ + PBUF_IP, + /** Includes spare room for link layer header (ethernet header). + * Use this if you intend to pass the pbuf to functions like ethernet_output(). + * @see PBUF_LINK_HLEN + */ + PBUF_LINK, + /** Includes spare room for additional encapsulation header before ethernet + * headers (e.g. 802.11). + * Use this if you intend to pass the pbuf to functions like netif->linkoutput(). + * @see PBUF_LINK_ENCAPSULATION_HLEN + */ + PBUF_RAW_TX, + /** Use this for input packets in a netif driver when calling netif->input() + * in the most common case - ethernet-layer netif driver. */ + PBUF_RAW +} pbuf_layer; + +/** + * @ingroup pbuf + * Enumeration of pbuf types + */ +typedef enum { + /** pbuf data is stored in RAM, used for TX mostly, struct pbuf and its payload + are allocated in one piece of contiguous memory (so the first payload byte + can be calculated from struct pbuf). + pbuf_alloc() allocates PBUF_RAM pbufs as unchained pbufs (although that might + change in future versions). + This should be used for all OUTGOING packets (TX).*/ + PBUF_RAM, + /** pbuf data is stored in ROM, i.e. struct pbuf and its payload are located in + totally different memory areas. Since it points to ROM, payload does not + have to be copied when queued for transmission. */ + PBUF_ROM, + /** pbuf comes from the pbuf pool. Much like PBUF_ROM but payload might change + so it has to be duplicated when queued before transmitting, depending on + who has a 'ref' to it. */ + PBUF_REF, + /** pbuf payload refers to RAM. This one comes from a pool and should be used + for RX. Payload can be chained (scatter-gather RX) but like PBUF_RAM, struct + pbuf and its payload are allocated in one piece of contiguous memory (so + the first payload byte can be calculated from struct pbuf). + Don't use this for TX, if the pool becomes empty e.g. because of TCP queuing, + you are unable to receive TCP acks! */ + PBUF_POOL +} pbuf_type; +#endif + +/** indicates this packet's data should be immediately passed to the application */ +#define PBUF_FLAG_PUSH 0x01U +/** indicates this is a custom pbuf: pbuf_free calls pbuf_custom->custom_free_function() + when the last reference is released (plus custom PBUF_RAM cannot be trimmed) */ +#define PBUF_FLAG_IS_CUSTOM 0x02U +/** indicates this pbuf is UDP multicast to be looped back */ +#define PBUF_FLAG_MCASTLOOP 0x04U +/** indicates this pbuf was received as link-level broadcast */ +#define PBUF_FLAG_LLBCAST 0x08U +/** indicates this pbuf was received as link-level multicast */ +#define PBUF_FLAG_LLMCAST 0x10U +/** indicates this pbuf includes a TCP FIN flag */ +#define PBUF_FLAG_TCP_FIN 0x20U +#if 0 +/** Main packet buffer struct */ +struct pbuf { + /** next pbuf in singly linked pbuf chain */ + struct pbuf *next; + + /** pointer to the actual data in the buffer */ + void *payload; + + /** + * total length of this buffer and all next buffers in chain + * belonging to the same packet. + * + * For non-queue packet chains this is the invariant: + * p->tot_len == p->len + (p->next? p->next->tot_len: 0) + */ + u16_t tot_len; + + /** length of this buffer */ + u16_t len; + + /** pbuf_type as u8_t instead of enum to save space */ + u8_t /*pbuf_type*/ type; + + /** misc flags */ + u8_t flags; + + /** + * the reference count always equals the number of pointers + * that refer to this pbuf. This can be pointers from an application, + * the stack itself, or pbuf->next pointers from a chain. + */ + u16_t ref; +}; +#endif + +/** Helper struct for const-correctness only. + * The only meaning of this one is to provide a const payload pointer + * for PBUF_ROM type. + */ +struct pbuf_rom { + /** next pbuf in singly linked pbuf chain */ + struct pbuf *next; + + /** pointer to the actual data in the buffer */ + const void *payload; +}; + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Prototype for a function to free a custom pbuf */ +typedef void (*pbuf_free_custom_fn)(struct pbuf *p); + +/** A custom pbuf: like a pbuf, but following a function pointer to free it. */ +struct pbuf_custom { + /** The actual pbuf */ + struct pbuf pbuf; + /** This function is called when pbuf_free deallocates this pbuf(_custom) */ + pbuf_free_custom_fn custom_free_function; +}; +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ +#ifndef PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_FREE_OOSEQ 1 +#endif /* PBUF_POOL_FREE_OOSEQ */ +#if LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ +extern volatile u8_t pbuf_free_ooseq_pending; +void pbuf_free_ooseq(void); +/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level to check if ooseq pbufs need to be + freed! */ +#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \ + /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \ + ooseq queued pbufs now */ \ + pbuf_free_ooseq(); }}while(0) +#else /* LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ */ + /* Otherwise declare an empty PBUF_CHECK_FREE_OOSEQ */ + #define PBUF_CHECK_FREE_OOSEQ() +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ*/ + +/* Initializes the pbuf module. This call is empty for now, but may not be in future. */ +#define pbuf_init() + +//struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type); +#if LWIP_SUPPORT_CUSTOM_PBUF +struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, + struct pbuf_custom *p, void *payload_mem, + u16_t payload_mem_len); +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ +void pbuf_realloc(struct pbuf *p, u16_t size); +u8_t pbuf_header(struct pbuf *p, s16_t header_size); +u8_t pbuf_header_force(struct pbuf *p, s16_t header_size); +void pbuf_ref(struct pbuf *p); +//u8_t pbuf_free(struct pbuf *p); +u16_t pbuf_clen(const struct pbuf *p); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); +struct pbuf *pbuf_dechain(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from); +//u16_t pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); +err_t pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset); +struct pbuf *pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset); +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer); +#if LWIP_CHECKSUM_ON_COPY +err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum); +#endif /* LWIP_CHECKSUM_ON_COPY */ +#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE +void pbuf_split_64k(struct pbuf *p, struct pbuf **rest); +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + +u8_t pbuf_get_at(const struct pbuf* p, u16_t offset); +int pbuf_try_get_at(const struct pbuf* p, u16_t offset); +void pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data); +u16_t pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n); +u16_t pbuf_memfind(const struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset); +u16_t pbuf_strstr(const struct pbuf* p, const char* substr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PBUF_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/pppapi.h b/sdk/Src/Network/lwip2.0.3/include/lwip/pppapi.h new file mode 100644 index 0000000..e7de489 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/pppapi.h @@ -0,0 +1,151 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef __LWIP_PPPAPI_H__ +#define __LWIP_PPPAPI_H__ + +#include "lwip/opt.h" + +#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "netif/ppp/ppp.h" +#if PPPOS_SUPPORT +#include "netif/ppp/pppos.h" +#endif /* PPPOS_SUPPORT */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct pppapi_msg_msg { +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_t sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + err_t err; + ppp_pcb *ppp; + union { + struct { + u8_t authtype; + const char *user; + const char *passwd; + } setauth; +#if PPP_NOTIFY_PHASE + struct { + ppp_notify_phase_cb_fn notify_phase_cb; + } setnotifyphasecb; +#endif /* PPP_NOTIFY_PHASE */ +#if PPPOS_SUPPORT + struct { + struct netif *pppif; + pppos_output_cb_fn output_cb; + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } serialcreate; +#endif /* PPPOS_SUPPORT */ +#if PPPOE_SUPPORT + struct { + struct netif *pppif; + struct netif *ethif; + const char *service_name; + const char *concentrator_name; + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } ethernetcreate; +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT + struct { + struct netif *pppif; + struct netif *netif; + ip_addr_t *ipaddr; + u16_t port; +#if PPPOL2TP_AUTH_SUPPORT + const u8_t *secret; + u8_t secret_len; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } l2tpcreate; +#endif /* PPPOL2TP_SUPPORT */ + struct { + u16_t holdoff; + } connect; +#if PPP_SERVER + struct { + struct ppp_addrs *addrs; + } listen; +#endif /* PPP_SERVER */ + struct { + u8_t nocarrier; + } close; + struct { + u8_t cmd; + void *arg; + } ioctl; + } msg; +}; + +struct pppapi_msg { + void (* function)(struct pppapi_msg_msg *msg); + struct pppapi_msg_msg msg; +}; + +/* API for application */ +void pppapi_set_default(ppp_pcb *pcb); +void pppapi_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd); +#if PPP_NOTIFY_PHASE +void pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb); +#endif /* PPP_NOTIFY_PHASE */ +#if PPPOS_SUPPORT +ppp_pcb *pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb, ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOS_SUPPORT */ +#if PPPOE_SUPPORT +ppp_pcb *pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name, + const char *concentrator_name, ppp_link_status_cb_fn link_status_cb, + void *ctx_cb); +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT +ppp_pcb *pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port, + const u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOL2TP_SUPPORT */ +err_t pppapi_connect(ppp_pcb *pcb, u16_t holdoff); +#if PPP_SERVER +err_t pppapi_listen(ppp_pcb *pcb, struct ppp_addrs *addrs); +#endif /* PPP_SERVER */ +err_t pppapi_close(ppp_pcb *pcb, u8_t nocarrier); +err_t pppapi_free(ppp_pcb *pcb); +err_t pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_PPP_API */ + +#endif /* __LWIP_PPPAPI_H__ */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/priv/api_msg.h b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/api_msg.h new file mode 100644 index 0000000..f12b8b7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/api_msg.h @@ -0,0 +1,216 @@ +/** + * @file + * netconn API lwIP internal implementations (do not use in application code) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_API_MSG_H +#define LWIP_HDR_API_MSG_H + +#include "lwip/opt.h" + +#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ +/* Note: Netconn API is always available when sockets are enabled - + * sockets are implemented on top of them */ + +#include "lwip/arch.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/api.h" +#include "lwip/priv/tcpip_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_MPU_COMPATIBLE +#if LWIP_NETCONN_SEM_PER_THREAD +#define API_MSG_M_DEF_SEM(m) *m +#else +#define API_MSG_M_DEF_SEM(m) API_MSG_M_DEF(m) +#endif +#else /* LWIP_MPU_COMPATIBLE */ +#define API_MSG_M_DEF_SEM(m) API_MSG_M_DEF(m) +#endif /* LWIP_MPU_COMPATIBLE */ + +/* For the netconn API, these values are use as a bitmask! */ +#define NETCONN_SHUT_RD 1 +#define NETCONN_SHUT_WR 2 +#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR) + +/* IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ +/** This struct includes everything that is necessary to execute a function + for a netconn in another thread context (mainly used to process netconns + in the tcpip_thread context to be thread safe). */ +struct api_msg { + /** The netconn which to process - always needed: it includes the semaphore + which is used to block the application thread until the function finished. */ + struct netconn *conn; + /** The return value of the function executed in tcpip_thread. */ + err_t err; + /** Depending on the executed function, one of these union members is used */ + union { + /** used for lwip_netconn_do_send */ + struct netbuf *b; + /** used for lwip_netconn_do_newconn */ + struct { + u8_t proto; + } n; + /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */ + struct { + API_MSG_M_DEF_C(ip_addr_t, ipaddr); + u16_t port; + } bc; + /** used for lwip_netconn_do_getaddr */ + struct { + ip_addr_t API_MSG_M_DEF(ipaddr); + u16_t API_MSG_M_DEF(port); + u8_t local; + } ad; + /** used for lwip_netconn_do_write */ + struct { + const void *dataptr; + size_t len; + u8_t apiflags; +#if LWIP_SO_SNDTIMEO + u32_t time_started; +#endif /* LWIP_SO_SNDTIMEO */ + } w; + /** used for lwip_netconn_do_recv */ + struct { + u32_t len; + } r; +#if LWIP_TCP + /** used for lwip_netconn_do_close (/shutdown) */ + struct { + u8_t shut; +#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER + u32_t time_started; +#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ + u8_t polls_left; +#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */ + } sd; +#endif /* LWIP_TCP */ +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) + /** used for lwip_netconn_do_join_leave_group */ + struct { + API_MSG_M_DEF_C(ip_addr_t, multiaddr); + API_MSG_M_DEF_C(ip_addr_t, netif_addr); + enum netconn_igmp join_or_leave; + } jl; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if TCP_LISTEN_BACKLOG + struct { + u8_t backlog; + } lb; +#endif /* TCP_LISTEN_BACKLOG */ + } msg; +#if LWIP_NETCONN_SEM_PER_THREAD + sys_sem_t* op_completed_sem; +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ +}; + +#if LWIP_NETCONN_SEM_PER_THREAD +#define LWIP_API_MSG_SEM(msg) ((msg)->op_completed_sem) +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define LWIP_API_MSG_SEM(msg) (&(msg)->conn->op_completed) +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + +#if LWIP_DNS +/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn, + it has its own struct (to avoid struct api_msg getting bigger than necessary). + lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg + (see netconn_gethostbyname). */ +struct dns_api_msg { + /** Hostname to query or dotted IP address string */ +#if LWIP_MPU_COMPATIBLE + char name[DNS_MAX_NAME_LENGTH]; +#else /* LWIP_MPU_COMPATIBLE */ + const char *name; +#endif /* LWIP_MPU_COMPATIBLE */ + /** The resolved address is stored here */ + ip_addr_t API_MSG_M_DEF(addr); +#if LWIP_IPV4 && LWIP_IPV6 + /** Type of resolve call */ + u8_t dns_addrtype; +#endif /* LWIP_IPV4 && LWIP_IPV6 */ + /** This semaphore is posted when the name is resolved, the application thread + should wait on it. */ + sys_sem_t API_MSG_M_DEF_SEM(sem); + /** Errors are given back here */ + err_t API_MSG_M_DEF(err); +}; +#endif /* LWIP_DNS */ + +#if LWIP_TCP +extern u8_t netconn_aborted; +#endif /* LWIP_TCP */ + +void lwip_netconn_do_newconn (void *m); +void lwip_netconn_do_delconn (void *m); +void lwip_netconn_do_bind (void *m); +void lwip_netconn_do_connect (void *m); +void lwip_netconn_do_disconnect (void *m); +void lwip_netconn_do_listen (void *m); +void lwip_netconn_do_send (void *m); +void lwip_netconn_do_recv (void *m); +#if TCP_LISTEN_BACKLOG +void lwip_netconn_do_accepted (void *m); +#endif /* TCP_LISTEN_BACKLOG */ +void lwip_netconn_do_write (void *m); +void lwip_netconn_do_getaddr (void *m); +void lwip_netconn_do_close (void *m); +void lwip_netconn_do_shutdown (void *m); +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +void lwip_netconn_do_join_leave_group(void *m); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +void lwip_netconn_do_gethostbyname(void *arg); +#endif /* LWIP_DNS */ + +struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); +void netconn_free(struct netconn *conn); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#endif /* LWIP_HDR_API_MSG_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/priv/memp_priv.h b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/memp_priv.h new file mode 100644 index 0000000..f246061 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/memp_priv.h @@ -0,0 +1,183 @@ +/** + * @file + * memory pools lwIP internal implementations (do not use in application code) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef LWIP_HDR_MEMP_PRIV_H +#define LWIP_HDR_MEMP_PRIV_H + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/mem.h" + +#if MEMP_OVERFLOW_CHECK +/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning + * and at the end of each element, initialize them as 0xcd and check + * them later. */ +/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, + * every single element in each pool is checked! + * This is VERY SLOW but also very helpful. */ +/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in + * lwipopts.h to change the amount reserved for checking. */ +#ifndef MEMP_SANITY_REGION_BEFORE +#define MEMP_SANITY_REGION_BEFORE 16 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#if MEMP_SANITY_REGION_BEFORE > 0 +#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) +#else +#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#ifndef MEMP_SANITY_REGION_AFTER +#define MEMP_SANITY_REGION_AFTER 16 +#endif /* MEMP_SANITY_REGION_AFTER*/ +#if MEMP_SANITY_REGION_AFTER > 0 +#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) +#else +#define MEMP_SANITY_REGION_AFTER_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_AFTER*/ + +/* MEMP_SIZE: save space for struct memp and for sanity check */ +#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED) +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) + +#else /* MEMP_OVERFLOW_CHECK */ + +/* No sanity checks + * We don't need to preserve the struct memp while not allocated, so we + * can save a little space and set MEMP_SIZE to 0. + */ +#define MEMP_SIZE 0 +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_OVERFLOW_CHECK */ + +#if !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK +struct memp { + struct memp *next; +#if MEMP_OVERFLOW_CHECK + const char *file; + int line; +#endif /* MEMP_OVERFLOW_CHECK */ +}; +#endif /* !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK */ + +#if MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS +/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */ +typedef enum { + /* Get the first (via: + MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/ + MEMP_POOL_HELPER_FIRST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START 1 +#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0 +#define LWIP_MALLOC_MEMPOOL_END +#include "lwip/priv/memp_std.h" + ) , + /* Get the last (via: + MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */ + MEMP_POOL_HELPER_LAST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size * +#define LWIP_MALLOC_MEMPOOL_END 1 +#include "lwip/priv/memp_std.h" + ) +} memp_pool_helper_t; + +/* The actual start and stop values are here (cast them over) + We use this helper type and these defines so we can avoid using const memp_t values */ +#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST) +#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST) +#endif /* MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS */ + +/** Memory pool descriptor */ +struct memp_desc { +#if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY + /** Textual description */ + const char *desc; +#endif /* LWIP_DEBUG || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY */ +#if MEMP_STATS + /** Statistics */ + struct stats_mem *stats; +#endif + + /** Element size */ + u16_t size; + +#if !MEMP_MEM_MALLOC + /** Number of elements */ + u16_t num; + + /** Base address */ + u8_t *base; + + /** First free element of each pool. Elements form a linked list. */ + struct memp **tab; +#endif /* MEMP_MEM_MALLOC */ +}; + +#if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY +#define DECLARE_LWIP_MEMPOOL_DESC(desc) (desc), +#else +#define DECLARE_LWIP_MEMPOOL_DESC(desc) +#endif + +#if MEMP_STATS +#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name) static struct stats_mem name; +#define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name) &name, +#else +#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name) +#define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name) +#endif + +void memp_init_pool(const struct memp_desc *desc); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_pool_fn(const struct memp_desc* desc, const char* file, const int line); +#define memp_malloc_pool(d) memp_malloc_pool_fn((d), __FILE__, __LINE__) +#else +void *memp_malloc_pool(const struct memp_desc *desc); +#endif +void memp_free_pool(const struct memp_desc* desc, void *mem); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_MEMP_PRIV_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/priv/memp_std.h b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/memp_std.h new file mode 100644 index 0000000..ce9fd50 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/memp_std.h @@ -0,0 +1,146 @@ +/** + * @file + * lwIP internal memory pools (do not use in application code) + * This file is deliberately included multiple times: once with empty + * definition of LWIP_MEMPOOL() to handle all includes and multiple times + * to build up various lists of mem pools. + */ + +/* + * SETUP: Make sure we define everything we will need. + * + * We have create three types of pools: + * 1) MEMPOOL - standard pools + * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c + * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct + * + * If the include'r doesn't require any special treatment of each of the types + * above, then will declare #2 & #3 to be just standard mempools. + */ +#ifndef LWIP_MALLOC_MEMPOOL +/* This treats "malloc pools" just like any other pool. + The pools are a little bigger to provide 'size' as the amount of user data. */ +#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))), "MALLOC_"#size) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL_END +#endif /* LWIP_MALLOC_MEMPOOL */ + +#ifndef LWIP_PBUF_MEMPOOL +/* This treats "pbuf pools" just like any other pool. + * Allocates buffers for a pbuf struct AND a payload size */ +#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#endif /* LWIP_PBUF_MEMPOOL */ + + +/* + * A list of internal pools used by LWIP. + * + * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + */ +#if LWIP_RAW +LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if LWIP_IPV4 && IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#endif /* LWIP_IPV4 && IP_REASSEMBLY */ +#if (IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG) +LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF") +#endif /* IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF || (LWIP_IPV6 && LWIP_IPV6_FRAG) */ + +#if LWIP_NETCONN || LWIP_SOCKET +LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") +LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#if NO_SYS==0 +LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") +#if LWIP_MPU_COMPATIBLE +LWIP_MEMPOOL(API_MSG, MEMP_NUM_API_MSG, sizeof(struct api_msg), "API_MSG") +#if LWIP_DNS +LWIP_MEMPOOL(DNS_API_MSG, MEMP_NUM_DNS_API_MSG, sizeof(struct dns_api_msg), "DNS_API_MSG") +#endif +#if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING +LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA") +#endif +#if LWIP_NETIF_API +LWIP_MEMPOOL(NETIFAPI_MSG, MEMP_NUM_NETIFAPI_MSG, sizeof(struct netifapi_msg), "NETIFAPI_MSG") +#endif +#endif /* LWIP_MPU_COMPATIBLE */ +#if !LWIP_TCPIP_CORE_LOCKING_INPUT +LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") +#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */ +#endif /* NO_SYS==0 */ + +#if LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#endif /* LWIP_IGMP */ + +#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM +LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT") +#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */ + +#if LWIP_DNS && LWIP_SOCKET +LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB") +#endif /* LWIP_DNS && LWIP_SOCKET */ +#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST") +#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +#if LWIP_IPV6 && LWIP_ND6_QUEUEING +LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE") +#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */ + +#if LWIP_IPV6 && LWIP_IPV6_REASS +LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA") +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_MLD +LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP") +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + +/* + * A list of pools of pbuf's used by LWIP. + * + * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + * This allocates enough space for the pbuf struct and a payload. + * (Example: pbuf_payload_size=0 allocates only size for the struct) + */ +LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * Allow for user-defined pools; this must be explicitly set in lwipopts.h + * since the default is to NOT look for lwippools.h + */ +#if MEMP_USE_CUSTOM_POOLS +#include "lwippools.h" +#endif /* MEMP_USE_CUSTOM_POOLS */ + +/* + * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later + * (#undef is ignored for something that is not defined) + */ +#undef LWIP_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL_START +#undef LWIP_MALLOC_MEMPOOL_END +#undef LWIP_PBUF_MEMPOOL diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/priv/nd6_priv.h b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/nd6_priv.h new file mode 100644 index 0000000..4bda0b7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/nd6_priv.h @@ -0,0 +1,144 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_ND6_PRIV_H +#define LWIP_HDR_ND6_PRIV_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_ND6_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct nd6_q_entry { + struct nd6_q_entry *next; + struct pbuf *p; +}; +#endif /* LWIP_ND6_QUEUEING */ + +/** Struct for tables. */ +struct nd6_neighbor_cache_entry { + ip6_addr_t next_hop_address; + struct netif *netif; + u8_t lladdr[NETIF_MAX_HWADDR_LEN]; + /*u32_t pmtu;*/ +#if LWIP_ND6_QUEUEING + /** Pointer to queue of pending outgoing packets on this entry. */ + struct nd6_q_entry *q; +#else /* LWIP_ND6_QUEUEING */ + /** Pointer to a single pending outgoing packet on this entry. */ + struct pbuf *q; +#endif /* LWIP_ND6_QUEUEING */ + u8_t state; + u8_t isrouter; + union { + u32_t reachable_time; /* in ms since value may originate from network packet */ + u32_t delay_time; /* ticks (ND6_TMR_INTERVAL) */ + u32_t probes_sent; + u32_t stale_time; /* ticks (ND6_TMR_INTERVAL) */ + } counter; +}; + +struct nd6_destination_cache_entry { + ip6_addr_t destination_addr; + ip6_addr_t next_hop_addr; + u16_t pmtu; + u32_t age; +}; + +struct nd6_prefix_list_entry { + ip6_addr_t prefix; + struct netif *netif; + u32_t invalidation_timer; /* in ms since value may originate from network packet */ +#if LWIP_IPV6_AUTOCONFIG + u8_t flags; +#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04 +#endif /* LWIP_IPV6_AUTOCONFIG */ +}; + +struct nd6_router_list_entry { + struct nd6_neighbor_cache_entry *neighbor_entry; + u32_t invalidation_timer; /* in ms since value may originate from network packet */ + u8_t flags; +}; + +enum nd6_neighbor_cache_entry_state { + ND6_NO_ENTRY = 0, + ND6_INCOMPLETE, + ND6_REACHABLE, + ND6_STALE, + ND6_DELAY, + ND6_PROBE +}; + +/* Router tables. */ +/* @todo make these static? and entries accessible through API? */ +extern struct nd6_neighbor_cache_entry neighbor_cache[]; +extern struct nd6_destination_cache_entry destination_cache[]; +extern struct nd6_prefix_list_entry prefix_list[]; +extern struct nd6_router_list_entry default_router_list[]; + +/* Default values, can be updated by a RA message. */ +extern u32_t reachable_time; +extern u32_t retrans_timer; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_ND6_PRIV_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/priv/tcp_priv.h b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/tcp_priv.h new file mode 100644 index 0000000..2b77fcd --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/tcp_priv.h @@ -0,0 +1,507 @@ +/** + * @file + * TCP internal implementations (do not use in application code) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_TCP_PRIV_H +#define LWIP_HDR_TCP_PRIV_H + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/prot/tcp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Functions for interfacing with TCP: */ + +/* Lower layer interface to TCP: */ +void tcp_init (void); /* Initialize this module. */ +void tcp_tmr (void); /* Must be called every + TCP_TMR_INTERVAL + ms. (Typically 250 ms). */ +/* It is also possible to call these two functions at the right + intervals (instead of calling tcp_tmr()). */ +void tcp_slowtmr (void); +void tcp_fasttmr (void); + +/* Call this from a netif driver (watch out for threading issues!) that has + returned a memory error on transmit and now has free buffers to send more. + This iterates all active pcbs that had an error and tries to call + tcp_output, so use this with care as it might slow down the system. */ +void tcp_txnow (void); + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp); +/* Used within the TCP code only: */ +struct tcp_pcb * tcp_alloc (u8_t prio); +void tcp_abandon (struct tcp_pcb *pcb, int reset); +err_t tcp_send_empty_ack(struct tcp_pcb *pcb); +void tcp_rexmit (struct tcp_pcb *pcb); +void tcp_rexmit_rto (struct tcp_pcb *pcb); +void tcp_rexmit_fast (struct tcp_pcb *pcb); +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); +err_t tcp_process_refused_data(struct tcp_pcb *pcb); + +/** + * This is the Nagle algorithm: try to combine user data to send as few TCP + * segments as possible. Only send if + * - no previously transmitted data on the connection remains unacknowledged or + * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or + * - the only unsent segment is at least pcb->mss bytes long (or there is more + * than one unsent segment - with lwIP, this can happen although unsent->len < mss) + * - or if we are in fast-retransmit (TF_INFR) + */ +#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ + ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ + (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ + ((tpcb)->unsent->len >= (tpcb)->mss))) || \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ + ) ? 1 : 0) +#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) + + +#define TCP_SEQ_LT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0) +/* is b<=a<=c? */ +#if 0 /* see bug #10548 */ +#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b)) +#endif +#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) + +#ifndef TCP_TMR_INTERVAL +#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */ +#endif /* TCP_TMR_INTERVAL */ + +#ifndef TCP_FAST_INTERVAL +#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */ +#endif /* TCP_FAST_INTERVAL */ + +#ifndef TCP_SLOW_INTERVAL +#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */ +#endif /* TCP_SLOW_INTERVAL */ + +#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */ +#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */ + +#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */ + +#ifndef TCP_MSL +#define TCP_MSL 1000UL//60000UL /* The maximum segment lifetime in milliseconds */ +#endif + +/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */ +#ifndef TCP_KEEPIDLE_DEFAULT +#define TCP_KEEPIDLE_DEFAULT 200000UL //7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */ +#endif + +#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */ + +#define TCP_TCPLEN(seg) ((seg)->len + (((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0) ? 1U : 0U)) + +/** Flags used on input processing, not on pcb->flags +*/ +#define TF_RESET (u8_t)0x08U /* Connection was reset. */ +#define TF_CLOSED (u8_t)0x10U /* Connection was successfully closed. */ +#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ + + +#if LWIP_EVENT_API + +#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) ret = lwip_tcp_event(arg, (pcb),\ + LWIP_EVENT_ACCEPT, NULL, 0, err) +#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_SENT, NULL, space, ERR_OK) +#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, (p), 0, (err)) +#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, NULL, 0, ERR_OK) +#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_CONNECTED, NULL, 0, (err)) +#define TCP_EVENT_POLL(pcb,ret) do { if ((pcb)->state != SYN_RCVD) { \ + ret = lwip_tcp_event((pcb)->callback_arg, (pcb), LWIP_EVENT_POLL, NULL, 0, ERR_OK); \ + } else { \ + ret = ERR_ARG; } } while(0) +#define TCP_EVENT_ERR(last_state,errf,arg,err) do { if (last_state != SYN_RCVD) { \ + lwip_tcp_event((arg), NULL, LWIP_EVENT_ERR, NULL, 0, (err)); } } while(0) + +#else /* LWIP_EVENT_API */ + +#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) \ + do { \ + if((lpcb)->accept != NULL) \ + (ret) = (lpcb)->accept((arg),(pcb),(err)); \ + else (ret) = ERR_ARG; \ + } while (0) + +#define TCP_EVENT_SENT(pcb,space,ret) \ + do { \ + if((pcb)->sent != NULL) \ + (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_RECV(pcb,p,err,ret) \ + do { \ + if((pcb)->recv != NULL) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\ + } else { \ + (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \ + } \ + } while (0) + +#define TCP_EVENT_CLOSED(pcb,ret) \ + do { \ + if(((pcb)->recv != NULL)) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\ + } else { \ + (ret) = ERR_OK; \ + } \ + } while (0) + +#define TCP_EVENT_CONNECTED(pcb,err,ret) \ + do { \ + if((pcb)->connected != NULL) \ + (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_POLL(pcb,ret) \ + do { \ + if((pcb)->poll != NULL) \ + (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_ERR(last_state,errf,arg,err) \ + do { \ + LWIP_UNUSED_ARG(last_state); \ + if((errf) != NULL) \ + (errf)((arg),(err)); \ + } while (0) + +#endif /* LWIP_EVENT_API */ + +/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */ +#if TCP_OVERSIZE && defined(LWIP_DEBUG) +#define TCP_OVERSIZE_DBGCHECK 1 +#else +#define TCP_OVERSIZE_DBGCHECK 0 +#endif + +/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */ +#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP) + +/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */ +struct tcp_seg { + struct tcp_seg *next; /* used when putting segments on a queue */ + struct pbuf *p; /* buffer containing data + TCP header */ + u16_t len; /* the TCP length of this segment */ +#if TCP_OVERSIZE_DBGCHECK + u16_t oversize_left; /* Extra bytes available at the end of the last + pbuf in unsent (used for asserting vs. + tcp_pcb.unsent_oversize only) */ +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + u16_t chksum; + u8_t chksum_swapped; +#endif /* TCP_CHECKSUM_ON_COPY */ + u8_t flags; +#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */ +#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */ +#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is + checksummed into 'chksum' */ +#define TF_SEG_OPTS_WND_SCALE (u8_t)0x08U /* Include WND SCALE option */ + struct tcp_hdr *tcphdr; /* the TCP header */ +}; + +#define LWIP_TCP_OPT_EOL 0 +#define LWIP_TCP_OPT_NOP 1 +#define LWIP_TCP_OPT_MSS 2 +#define LWIP_TCP_OPT_WS 3 +#define LWIP_TCP_OPT_TS 8 + +#define LWIP_TCP_OPT_LEN_MSS 4 +#if LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_OPT_LEN_TS 10 +#define LWIP_TCP_OPT_LEN_TS_OUT 12 /* aligned for output (includes NOP padding) */ +#else +#define LWIP_TCP_OPT_LEN_TS_OUT 0 +#endif +#if LWIP_WND_SCALE +#define LWIP_TCP_OPT_LEN_WS 3 +#define LWIP_TCP_OPT_LEN_WS_OUT 4 /* aligned for output (includes NOP padding) */ +#else +#define LWIP_TCP_OPT_LEN_WS_OUT 0 +#endif + +#define LWIP_TCP_OPT_LENGTH(flags) \ + (flags & TF_SEG_OPTS_MSS ? LWIP_TCP_OPT_LEN_MSS : 0) + \ + (flags & TF_SEG_OPTS_TS ? LWIP_TCP_OPT_LEN_TS_OUT : 0) + \ + (flags & TF_SEG_OPTS_WND_SCALE ? LWIP_TCP_OPT_LEN_WS_OUT : 0) + +/** This returns a TCP header option for MSS in an u32_t */ +#define TCP_BUILD_MSS_OPTION(mss) lwip_htonl(0x02040000 | ((mss) & 0xFFFF)) + +#if LWIP_WND_SCALE +#define TCPWNDSIZE_F U32_F +#define TCPWND_MAX 0xFFFFFFFFU +#define TCPWND_CHECK16(x) LWIP_ASSERT("window size > 0xFFFF", (x) <= 0xFFFF) +#define TCPWND_MIN16(x) ((u16_t)LWIP_MIN((x), 0xFFFF)) +#else /* LWIP_WND_SCALE */ +#define TCPWNDSIZE_F U16_F +#define TCPWND_MAX 0xFFFFU +#define TCPWND_CHECK16(x) +#define TCPWND_MIN16(x) x +#endif /* LWIP_WND_SCALE */ + +/* Global variables: */ +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; +extern u8_t tcp_active_pcbs_changed; + +/* The TCP PCB lists. */ +union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */ + struct tcp_pcb_listen *listen_pcbs; + struct tcp_pcb *pcbs; +}; +extern struct tcp_pcb *tcp_bound_pcbs; +extern union tcp_listen_pcbs_t tcp_listen_pcbs; +extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a + state in which they accept or send + data. */ +extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ + +#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3 +#define NUM_TCP_PCB_LISTS 4 +extern struct tcp_pcb ** const tcp_pcb_lists[NUM_TCP_PCB_LISTS]; + +/* Axioms about the above lists: + 1) Every TCP PCB that is not CLOSED is in one of the lists. + 2) A PCB is only in one of the lists. + 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state. + 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state. +*/ +/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB + with a PCB list or removes a PCB from a list, respectively. */ +#ifndef TCP_DEBUG_PCB_LISTS +#define TCP_DEBUG_PCB_LISTS 0 +#endif +#if TCP_DEBUG_PCB_LISTS +#define TCP_REG(pcbs, npcb) do {\ + struct tcp_pcb *tcp_tmp_pcb; \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \ + for (tcp_tmp_pcb = *(pcbs); \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \ + } \ + LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \ + (npcb)->next = *(pcbs); \ + LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \ + *(pcbs) = (npcb); \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + tcp_timer_needed(); \ + } while(0) +#define TCP_RMV(pcbs, npcb) do { \ + struct tcp_pcb *tcp_tmp_pcb; \ + LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \ + if(*(pcbs) == (npcb)) { \ + *(pcbs) = (*pcbs)->next; \ + } else for (tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + (npcb)->next = NULL; \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \ + } while(0) + +#else /* LWIP_DEBUG */ + +#define TCP_REG(pcbs, npcb) \ + do { \ + (npcb)->next = *pcbs; \ + *(pcbs) = (npcb); \ + tcp_timer_needed(); \ + } while (0) + +#define TCP_RMV(pcbs, npcb) \ + do { \ + if(*(pcbs) == (npcb)) { \ + (*(pcbs)) = (*pcbs)->next; \ + } \ + else { \ + struct tcp_pcb *tcp_tmp_pcb; \ + for (tcp_tmp_pcb = *pcbs; \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + } \ + (npcb)->next = NULL; \ + } while(0) + +#endif /* LWIP_DEBUG */ + +#define TCP_REG_ACTIVE(npcb) \ + do { \ + TCP_REG(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_RMV_ACTIVE(npcb) \ + do { \ + TCP_RMV(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_PCB_REMOVE_ACTIVE(pcb) \ + do { \ + tcp_pcb_remove(&tcp_active_pcbs, pcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + + +/* Internal functions: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); +void tcp_pcb_purge(struct tcp_pcb *pcb); +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); + +void tcp_segs_free(struct tcp_seg *seg); +void tcp_seg_free(struct tcp_seg *seg); +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); + +#define tcp_ack(pcb) \ + do { \ + if((pcb)->flags & TF_ACK_DELAY) { \ + (pcb)->flags &= ~TF_ACK_DELAY; \ + (pcb)->flags |= TF_ACK_NOW; \ + } \ + else { \ + (pcb)->flags |= TF_ACK_DELAY; \ + } \ + } while (0) + +#define tcp_ack_now(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + } while (0) + +err_t tcp_send_fin(struct tcp_pcb *pcb); +err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags); + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); + +void tcp_rst(u32_t seqno, u32_t ackno, + const ip_addr_t *local_ip, const ip_addr_t *remote_ip, + u16_t local_port, u16_t remote_port); + +u32_t tcp_next_iss(struct tcp_pcb *pcb); + +err_t tcp_keepalive(struct tcp_pcb *pcb); +err_t tcp_zero_window_probe(struct tcp_pcb *pcb); +void tcp_trigger_input_pcb_close(void); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss_impl(u16_t sendmss, const ip_addr_t *dest +#if LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING + , const ip_addr_t *src +#endif /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */ + ); +#if LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING +#define tcp_eff_send_mss(sendmss, src, dest) tcp_eff_send_mss_impl(sendmss, dest, src) +#else /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */ +#define tcp_eff_send_mss(sendmss, src, dest) tcp_eff_send_mss_impl(sendmss, dest) +#endif /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */ +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +#if LWIP_CALLBACK_API +err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +#endif /* LWIP_CALLBACK_API */ + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr); +void tcp_debug_print_flags(u8_t flags); +void tcp_debug_print_state(enum tcp_state s); +void tcp_debug_print_pcbs(void); +s16_t tcp_pcbs_sane(void); +#else +# define tcp_debug_print(tcphdr) +# define tcp_debug_print_flags(flags) +# define tcp_debug_print_state(s) +# define tcp_debug_print_pcbs() +# define tcp_pcbs_sane() 1 +#endif /* TCP_DEBUG */ + +/** External function (implemented in timers.c), called when TCP detects + * that a timer is needed (i.e. active- or time-wait-pcb found). */ +void tcp_timer_needed(void); + +void tcp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* LWIP_HDR_TCP_PRIV_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/priv/tcpip_priv.h b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/tcpip_priv.h new file mode 100644 index 0000000..8c8e0cc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/priv/tcpip_priv.h @@ -0,0 +1,170 @@ +/** + * @file + * TCPIP API internal implementations (do not use in application code) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_TCPIP_PRIV_H +#define LWIP_HDR_TCPIP_PRIV_H + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/timeouts.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct pbuf; +struct netif; + +#if LWIP_MPU_COMPATIBLE +#define API_VAR_REF(name) (*(name)) +#define API_VAR_DECLARE(type, name) type * name +#define API_VAR_ALLOC(type, pool, name, errorval) do { \ + name = (type *)memp_malloc(pool); \ + if (name == NULL) { \ + return errorval; \ + } \ + } while(0) +#define API_VAR_ALLOC_POOL(type, pool, name, errorval) do { \ + name = (type *)LWIP_MEMPOOL_ALLOC(pool); \ + if (name == NULL) { \ + return errorval; \ + } \ + } while(0) +#define API_VAR_FREE(pool, name) memp_free(pool, name) +#define API_VAR_FREE_POOL(pool, name) LWIP_MEMPOOL_FREE(pool, name) +#define API_EXPR_REF(expr) (&(expr)) +#if LWIP_NETCONN_SEM_PER_THREAD +#define API_EXPR_REF_SEM(expr) (expr) +#else +#define API_EXPR_REF_SEM(expr) API_EXPR_REF(expr) +#endif +#define API_EXPR_DEREF(expr) expr +#define API_MSG_M_DEF(m) m +#define API_MSG_M_DEF_C(t, m) t m +#else /* LWIP_MPU_COMPATIBLE */ +#define API_VAR_REF(name) name +#define API_VAR_DECLARE(type, name) type name +#define API_VAR_ALLOC(type, pool, name, errorval) +#define API_VAR_ALLOC_POOL(type, pool, name, errorval) +#define API_VAR_FREE(pool, name) +#define API_VAR_FREE_POOL(pool, name) +#define API_EXPR_REF(expr) expr +#define API_EXPR_REF_SEM(expr) API_EXPR_REF(expr) +#define API_EXPR_DEREF(expr) (*(expr)) +#define API_MSG_M_DEF(m) *m +#define API_MSG_M_DEF_C(t, m) const t * m +#endif /* LWIP_MPU_COMPATIBLE */ + +err_t tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem); + +struct tcpip_api_call_data +{ +#if !LWIP_TCPIP_CORE_LOCKING + err_t err; +#if !LWIP_NETCONN_SEM_PER_THREAD + sys_sem_t sem; +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ +#else /* !LWIP_TCPIP_CORE_LOCKING */ + u8_t dummy; /* avoid empty struct :-( */ +#endif /* !LWIP_TCPIP_CORE_LOCKING */ +}; +typedef err_t (*tcpip_api_call_fn)(struct tcpip_api_call_data* call); +err_t tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call); + +enum tcpip_msg_type { + TCPIP_MSG_API, + TCPIP_MSG_API_CALL, + TCPIP_MSG_INPKT, +#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS + TCPIP_MSG_TIMEOUT, + TCPIP_MSG_UNTIMEOUT, +#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */ + TCPIP_MSG_CALLBACK, + TCPIP_MSG_CALLBACK_STATIC, +#if TLS_CONFIG_AP_OPT_FWD + TCPIP_MSG_OUTPKT, +#endif +}; + +struct tcpip_msg { + enum tcpip_msg_type type; + union { + struct { + tcpip_callback_fn function; + void* msg; + } api_msg; + struct { + tcpip_api_call_fn function; + struct tcpip_api_call_data *arg; + sys_sem_t *sem; + } api_call; + struct { + struct pbuf *p; + struct netif *netif; + netif_input_fn input_fn; + } inp; + struct { + tcpip_callback_fn function; + void *ctx; + } cb; +#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; +#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */ +#if TLS_CONFIG_AP_OPT_FWD + struct { + struct pbuf *p; + struct netif *netif; + ip_addr_t ipaddr; + } outp; +#endif + } msg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ + +#endif /* LWIP_HDR_TCPIP_PRIV_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/autoip.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/autoip.h new file mode 100644 index 0000000..fd3af8a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/autoip.h @@ -0,0 +1,78 @@ +/** + * @file + * AutoIP protocol definitions + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + */ + +#ifndef LWIP_HDR_PROT_AUTOIP_H +#define LWIP_HDR_PROT_AUTOIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* 169.254.0.0 */ +#define AUTOIP_NET 0xA9FE0000 +/* 169.254.1.0 */ +#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100) +/* 169.254.254.255 */ +#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF) + +/* RFC 3927 Constants */ +#define PROBE_WAIT 1 /* second (initial random delay) */ +#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */ +#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */ +#define PROBE_NUM 3 /* (number of probe packets) */ +#define ANNOUNCE_NUM 2 /* (number of announcement packets) */ +#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */ +#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */ +#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */ +#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */ +#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */ + +/* AutoIP client states */ +typedef enum { + AUTOIP_STATE_OFF = 0, + AUTOIP_STATE_PROBING = 1, + AUTOIP_STATE_ANNOUNCING = 2, + AUTOIP_STATE_BOUND = 3 +} autoip_state_enum_t; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_AUTOIP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/dhcp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/dhcp.h new file mode 100644 index 0000000..112953c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/dhcp.h @@ -0,0 +1,183 @@ +/** + * @file + * DHCP protocol definitions + */ + +/* + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Leon Woestenberg + * + */ +#ifndef LWIP_HDR_PROT_DHCP_H +#define LWIP_HDR_PROT_DHCP_H + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + + + /* DHCP message item offsets and length */ +#define DHCP_CHADDR_LEN 16U +#define DHCP_SNAME_OFS 44U +#define DHCP_SNAME_LEN 64U +#define DHCP_FILE_OFS 108U +#define DHCP_FILE_LEN 128U +#define DHCP_MSG_LEN 236U +#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4U) /* 4 byte: cookie */ + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** minimum set of fields of any DHCP message */ +struct dhcp_msg +{ + PACK_STRUCT_FLD_8(u8_t op); + PACK_STRUCT_FLD_8(u8_t htype); + PACK_STRUCT_FLD_8(u8_t hlen); + PACK_STRUCT_FLD_8(u8_t hops); + PACK_STRUCT_FIELD(u32_t xid); + PACK_STRUCT_FIELD(u16_t secs); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FLD_S(ip4_addr_p_t ciaddr); + PACK_STRUCT_FLD_S(ip4_addr_p_t yiaddr); + PACK_STRUCT_FLD_S(ip4_addr_p_t siaddr); + PACK_STRUCT_FLD_S(ip4_addr_p_t giaddr); + PACK_STRUCT_FLD_8(u8_t chaddr[DHCP_CHADDR_LEN]); + PACK_STRUCT_FLD_8(u8_t sname[DHCP_SNAME_LEN]); + PACK_STRUCT_FLD_8(u8_t file[DHCP_FILE_LEN]); + PACK_STRUCT_FIELD(u32_t cookie); +#define DHCP_MIN_OPTIONS_LEN 68U +/** make sure user does not configure this too small */ +#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN)) +# undef DHCP_OPTIONS_LEN +#endif +/** allow this to be configured in lwipopts.h, but not too small */ +#if (!defined(DHCP_OPTIONS_LEN)) +/** set this to be sufficient for your options in outgoing DHCP msgs */ +# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN +#endif + PACK_STRUCT_FLD_8(u8_t options[DHCP_OPTIONS_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +/* DHCP client states */ +typedef enum { + DHCP_STATE_OFF = 0, + DHCP_STATE_REQUESTING = 1, + DHCP_STATE_INIT = 2, + DHCP_STATE_REBOOTING = 3, + DHCP_STATE_REBINDING = 4, + DHCP_STATE_RENEWING = 5, + DHCP_STATE_SELECTING = 6, + DHCP_STATE_INFORMING = 7, + DHCP_STATE_CHECKING = 8, + DHCP_STATE_PERMANENT = 9, /* not yet implemented */ + DHCP_STATE_BOUND = 10, + DHCP_STATE_RELEASING = 11, /* not yet implemented */ + DHCP_STATE_BACKING_OFF = 12 +} dhcp_state_enum_t; + +/* DHCP op codes */ +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/* DHCP message types */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +/** DHCP hardware type, currently only ethernet is supported */ +#define DHCP_HTYPE_ETH 1 + +#define DHCP_MAGIC_COOKIE 0x63825363UL + +/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */ + +/* BootP options */ +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */ +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_HOSTNAME 12 +#define DHCP_OPTION_IP_TTL 23 +#define DHCP_OPTION_MTU 26 +#define DHCP_OPTION_BROADCAST 28 +#define DHCP_OPTION_TCP_TTL 37 +#define DHCP_OPTION_NTP 42 +#define DHCP_OPTION_END 255 + +/* DHCP options */ +#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */ +#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */ +#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */ + +#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */ +#define DHCP_OPTION_MESSAGE_TYPE_LEN 1 + +#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */ +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */ + +#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */ +#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2 + +#define DHCP_OPTION_T1 58 /* T1 renewal time */ +#define DHCP_OPTION_T2 59 /* T2 rebinding time */ +#define DHCP_OPTION_US 60 +#define DHCP_OPTION_CLIENT_ID 61 +#define DHCP_OPTION_TFTP_SERVERNAME 66 +#define DHCP_OPTION_BOOTFILE 67 + +/* possible combinations of overloading the file and sname fields with options */ +#define DHCP_OVERLOAD_NONE 0 +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_SNAME_FILE 3 + + +#ifdef __cplusplus +} +#endif + +#endif /*LWIP_HDR_PROT_DHCP_H*/ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/dns.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/dns.h new file mode 100644 index 0000000..94782d6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/dns.h @@ -0,0 +1,140 @@ +/** + * @file + * DNS - host name to IP address resolver. + */ + +/* + * Port to lwIP from uIP + * by Jim Pettinato April 2007 + * + * security fixes and more by Simon Goldschmidt + * + * uIP version Copyright (c) 2002-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_HDR_PROT_DNS_H +#define LWIP_HDR_PROT_DNS_H + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** DNS server port address */ +#ifndef DNS_SERVER_PORT +#define DNS_SERVER_PORT 53 +#endif + +/* DNS field TYPE used for "Resource Records" */ +#define DNS_RRTYPE_A 1 /* a host address */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define DNS_RRTYPE_WKS 11 /* a well known service description */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer */ +#define DNS_RRTYPE_HINFO 13 /* host information */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ +#define DNS_RRTYPE_MX 15 /* mail exchange */ +#define DNS_RRTYPE_TXT 16 /* text strings */ +#define DNS_RRTYPE_AAAA 28 /* IPv6 address */ +#define DNS_RRTYPE_SRV 33 /* service location */ +#define DNS_RRTYPE_ANY 255 /* any type */ + +/* DNS field CLASS used for "Resource Records" */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ +#define DNS_RRCLASS_CH 3 /* the CHAOS class */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ +#define DNS_RRCLASS_ANY 255 /* any class */ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x80 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +#define DNS_HDR_GET_OPCODE(hdr) ((((hdr)->flags1) >> 3) & 0xF) + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS message header */ +struct dns_hdr { + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FLD_8(u8_t flags1); + PACK_STRUCT_FLD_8(u8_t flags2); + PACK_STRUCT_FIELD(u16_t numquestions); + PACK_STRUCT_FIELD(u16_t numanswers); + PACK_STRUCT_FIELD(u16_t numauthrr); + PACK_STRUCT_FIELD(u16_t numextrarr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_HDR 12 + + +/* Multicast DNS definitions */ + +/** UDP port for multicast DNS queries */ +#ifndef DNS_MQUERY_PORT +#define DNS_MQUERY_PORT 5353 +#endif + +/* IPv4 group for multicast DNS queries: 224.0.0.251 */ +#ifndef DNS_MQUERY_IPV4_GROUP_INIT +#define DNS_MQUERY_IPV4_GROUP_INIT IPADDR4_INIT_BYTES(224,0,0,251) +#endif + +/* IPv6 group for multicast DNS queries: FF02::FB */ +#ifndef DNS_MQUERY_IPV6_GROUP_INIT +#define DNS_MQUERY_IPV6_GROUP_INIT IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_DNS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/etharp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/etharp.h new file mode 100644 index 0000000..ec78305 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/etharp.h @@ -0,0 +1,91 @@ +/** + * @file + * ARP protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_ETHARP_H +#define LWIP_HDR_PROT_ETHARP_H + +#include "lwip/arch.h" +#include "lwip/prot/ethernet.h" +#include "lwip/ip4_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN ETH_HWADDR_LEN +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** the ARP message, see RFC 826 ("Packet format") */ +struct etharp_hdr { + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t proto); + PACK_STRUCT_FLD_8(u8_t hwlen); + PACK_STRUCT_FLD_8(u8_t protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FLD_S(struct eth_addr shwaddr); + PACK_STRUCT_FLD_S(struct ip4_addr2 sipaddr); + PACK_STRUCT_FLD_S(struct eth_addr dhwaddr); + PACK_STRUCT_FLD_S(struct ip4_addr2 dipaddr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETHARP_HDR 28 + +/* ARP hwtype values */ +enum etharp_hwtype { + HWTYPE_ETHERNET = 1 + /* others not used */ +}; + +/* ARP message types (opcodes) */ +enum etharp_opcode { + ARP_REQUEST = 1, + ARP_REPLY = 2 +}; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_ETHARP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ethernet.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ethernet.h new file mode 100644 index 0000000..e4baa29 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ethernet.h @@ -0,0 +1,170 @@ +/** + * @file + * Ethernet protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_ETHERNET_H +#define LWIP_HDR_PROT_ETHERNET_H + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETH_HWADDR_LEN +#ifdef ETHARP_HWADDR_LEN +#define ETH_HWADDR_LEN ETHARP_HWADDR_LEN /* compatibility mode */ +#else +#define ETH_HWADDR_LEN 6 +#endif +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FLD_8(u8_t addr[ETH_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** Ethernet header */ +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FLD_8(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FLD_S(struct eth_addr dest); + PACK_STRUCT_FLD_S(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** VLAN header inserted between ethernet header and payload + * if 'type' in ethernet header is ETHTYPE_VLAN. + * See IEEE802.Q */ +struct eth_vlan_hdr { + PACK_STRUCT_FIELD(u16_t prio_vid); + PACK_STRUCT_FIELD(u16_t tpid); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_VLAN_HDR 4 +#define VLAN_ID(vlan_hdr) (lwip_htons((vlan_hdr)->prio_vid) & 0xFFF) + +/** + * @ingroup ethernet + * A list of often ethtypes (although lwIP does not use all of them): */ +enum eth_type { + /** Internet protocol v4 */ + ETHTYPE_IP = 0x0800U, + /** Address resolution protocol */ + ETHTYPE_ARP = 0x0806U, + /** Wake on lan */ + ETHTYPE_WOL = 0x0842U, + /** RARP */ + ETHTYPE_RARP = 0x8035U, + /** Virtual local area network */ + ETHTYPE_VLAN = 0x8100U, + /** Internet protocol v6 */ + ETHTYPE_IPV6 = 0x86DDU, + /** PPP Over Ethernet Discovery Stage */ + ETHTYPE_PPPOEDISC = 0x8863U, + /** PPP Over Ethernet Session Stage */ + ETHTYPE_PPPOE = 0x8864U, + /** Jumbo Frames */ + ETHTYPE_JUMBO = 0x8870U, + /** Process field network */ + ETHTYPE_PROFINET = 0x8892U, + /** Ethernet for control automation technology */ + ETHTYPE_ETHERCAT = 0x88A4U, + /** Link layer discovery protocol */ + ETHTYPE_LLDP = 0x88CCU, + /** Serial real-time communication system */ + ETHTYPE_SERCOS = 0x88CDU, + /** Media redundancy protocol */ + ETHTYPE_MRP = 0x88E3U, + /** Precision time protocol */ + ETHTYPE_PTP = 0x88F7U, + /** Q-in-Q, 802.1ad */ + ETHTYPE_QINQ = 0x9100U +}; + +/** The 24-bit IANA IPv4-multicast OUI is 01-00-5e: */ +#define LL_IP4_MULTICAST_ADDR_0 0x01 +#define LL_IP4_MULTICAST_ADDR_1 0x00 +#define LL_IP4_MULTICAST_ADDR_2 0x5e + +/** IPv6 multicast uses this prefix */ +#define LL_IP6_MULTICAST_ADDR_0 0x33 +#define LL_IP6_MULTICAST_ADDR_1 0x33 + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables + * or known to be 32-bit aligned within the protocol header. */ +#ifndef ETHADDR32_COPY +#define ETHADDR32_COPY(dst, src) SMEMCPY(dst, src, ETH_HWADDR_LEN) +#endif + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local + * variables and known to be 16-bit aligned within the protocol header. */ +#ifndef ETHADDR16_COPY +#define ETHADDR16_COPY(dst, src) SMEMCPY(dst, src, ETH_HWADDR_LEN) +#endif + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETH_HWADDR_LEN) == 0) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_ETHERNET_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/icmp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/icmp.h new file mode 100644 index 0000000..7d19385 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/icmp.h @@ -0,0 +1,91 @@ +/** + * @file + * ICMP protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_ICMP_H +#define LWIP_HDR_PROT_ICMP_H + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP_ER 0 /* echo reply */ +#define ICMP_DUR 3 /* destination unreachable */ +#define ICMP_SQ 4 /* source quench */ +#define ICMP_RD 5 /* redirect */ +#define ICMP_ECHO 8 /* echo */ +#define ICMP_TE 11 /* time exceeded */ +#define ICMP_PP 12 /* parameter problem */ +#define ICMP_TS 13 /* timestamp */ +#define ICMP_TSR 14 /* timestamp reply */ +#define ICMP_IRQ 15 /* information request */ +#define ICMP_IR 16 /* information reply */ +#define ICMP_AM 17 /* address mask request */ +#define ICMP_AMR 18 /* address mask reply */ + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +/** This is the standard ICMP header only that the u32_t data + * is split to two u16_t like ICMP echo needs it. + * This header is also used for other ICMP types that do not + * use the data part. + */ +PACK_STRUCT_BEGIN +struct icmp_echo_hdr { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Compatibility defines, old versions used to combine type and code to an u16_t */ +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) +#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t)) +#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c)) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_ICMP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/icmp6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/icmp6.h new file mode 100644 index 0000000..3461120 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/icmp6.h @@ -0,0 +1,170 @@ +/** + * @file + * ICMP6 protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_ICMP6_H +#define LWIP_HDR_PROT_ICMP6_H + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** ICMP type */ +enum icmp6_type { + /** Destination unreachable */ + ICMP6_TYPE_DUR = 1, + /** Packet too big */ + ICMP6_TYPE_PTB = 2, + /** Time exceeded */ + ICMP6_TYPE_TE = 3, + /** Parameter problem */ + ICMP6_TYPE_PP = 4, + /** Private experimentation */ + ICMP6_TYPE_PE1 = 100, + /** Private experimentation */ + ICMP6_TYPE_PE2 = 101, + /** Reserved for expansion of error messages */ + ICMP6_TYPE_RSV_ERR = 127, + + /** Echo request */ + ICMP6_TYPE_EREQ = 128, + /** Echo reply */ + ICMP6_TYPE_EREP = 129, + /** Multicast listener query */ + ICMP6_TYPE_MLQ = 130, + /** Multicast listener report */ + ICMP6_TYPE_MLR = 131, + /** Multicast listener done */ + ICMP6_TYPE_MLD = 132, + /** Router solicitation */ + ICMP6_TYPE_RS = 133, + /** Router advertisement */ + ICMP6_TYPE_RA = 134, + /** Neighbor solicitation */ + ICMP6_TYPE_NS = 135, + /** Neighbor advertisement */ + ICMP6_TYPE_NA = 136, + /** Redirect */ + ICMP6_TYPE_RD = 137, + /** Multicast router advertisement */ + ICMP6_TYPE_MRA = 151, + /** Multicast router solicitation */ + ICMP6_TYPE_MRS = 152, + /** Multicast router termination */ + ICMP6_TYPE_MRT = 153, + /** Private experimentation */ + ICMP6_TYPE_PE3 = 200, + /** Private experimentation */ + ICMP6_TYPE_PE4 = 201, + /** Reserved for expansion of informational messages */ + ICMP6_TYPE_RSV_INF = 255 +}; + +/** ICMP destination unreachable codes */ +enum icmp6_dur_code { + /** No route to destination */ + ICMP6_DUR_NO_ROUTE = 0, + /** Communication with destination administratively prohibited */ + ICMP6_DUR_PROHIBITED = 1, + /** Beyond scope of source address */ + ICMP6_DUR_SCOPE = 2, + /** Address unreachable */ + ICMP6_DUR_ADDRESS = 3, + /** Port unreachable */ + ICMP6_DUR_PORT = 4, + /** Source address failed ingress/egress policy */ + ICMP6_DUR_POLICY = 5, + /** Reject route to destination */ + ICMP6_DUR_REJECT_ROUTE = 6 +}; + +/** ICMP time exceeded codes */ +enum icmp6_te_code { + /** Hop limit exceeded in transit */ + ICMP6_TE_HL = 0, + /** Fragment reassembly time exceeded */ + ICMP6_TE_FRAG = 1 +}; + +/** ICMP parameter code */ +enum icmp6_pp_code { + /** Erroneous header field encountered */ + ICMP6_PP_FIELD = 0, + /** Unrecognized next header type encountered */ + ICMP6_PP_HEADER = 1, + /** Unrecognized IPv6 option encountered */ + ICMP6_PP_OPTION = 2 +}; + +/** This is the standard ICMP6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_hdr { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t data); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** This is the ICMP6 header adapted for echo req/resp. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_echo_hdr { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_ICMP6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/igmp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/igmp.h new file mode 100644 index 0000000..d60cb31 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/igmp.h @@ -0,0 +1,90 @@ +/** + * @file + * IGMP protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_IGMP_H +#define LWIP_HDR_PROT_IGMP_H + +#include "lwip/arch.h" +#include "lwip/ip4_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * IGMP constants + */ +#define IGMP_TTL 1 +#define IGMP_MINLEN 8 +#define ROUTER_ALERT 0x9404U +#define ROUTER_ALERTLEN 4 + +/* + * IGMP message types, including version number. + */ +#define IGMP_MEMB_QUERY 0x11 /* Membership query */ +#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ + +/* Group membership states */ +#define IGMP_GROUP_NON_MEMBER 0 +#define IGMP_GROUP_DELAYING_MEMBER 1 +#define IGMP_GROUP_IDLE_MEMBER 2 + +/** + * IGMP packet format. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct igmp_msg { + PACK_STRUCT_FLD_8(u8_t igmp_msgtype); + PACK_STRUCT_FLD_8(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FLD_S(ip4_addr_p_t igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_IGMP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip.h new file mode 100644 index 0000000..bbfae36 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip.h @@ -0,0 +1,51 @@ +/** + * @file + * IP protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_IP_H +#define LWIP_HDR_PROT_IP_H + +#include "lwip/arch.h" + +#define IP_PROTO_ICMP 1 +#define IP_PROTO_IGMP 2 +#define IP_PROTO_UDP 17 +#define IP_PROTO_UDPLITE 136 +#define IP_PROTO_TCP 6 + +/** This operates on a void* by loading the first byte */ +#define IP_HDR_GET_VERSION(ptr) ((*(u8_t*)(ptr)) >> 4) + +#endif /* LWIP_HDR_PROT_IP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip4.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip4.h new file mode 100644 index 0000000..bd442c6 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip4.h @@ -0,0 +1,127 @@ +/** + * @file + * IPv4 protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_IP4_H +#define LWIP_HDR_PROT_IP4_H + +#include "lwip/arch.h" +#include "lwip/ip4_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is the packed version of ip4_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip4_addr_packed { + PACK_STRUCT_FIELD(u32_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +typedef struct ip4_addr_packed ip4_addr_p_t; + +/* Size of the IPv4 header. Same as 'sizeof(struct ip_hdr)'. */ +#define IP_HLEN 20 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/* The IPv4 header */ +struct ip_hdr { + /* version / header length */ + PACK_STRUCT_FLD_8(u8_t _v_hl); + /* type of service */ + PACK_STRUCT_FLD_8(u8_t _tos); + /* total length */ + PACK_STRUCT_FIELD(u16_t _len); + /* identification */ + PACK_STRUCT_FIELD(u16_t _id); + /* fragment offset field */ + PACK_STRUCT_FIELD(u16_t _offset); +#define IP_RF 0x8000U /* reserved fragment flag */ +#define IP_DF 0x4000U /* don't fragment flag */ +#define IP_MF 0x2000U /* more fragments flag */ +#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */ + /* time to live */ + PACK_STRUCT_FLD_8(u8_t _ttl); + /* protocol*/ + PACK_STRUCT_FLD_8(u8_t _proto); + /* checksum */ + PACK_STRUCT_FIELD(u16_t _chksum); + /* source and destination IP addresses */ + PACK_STRUCT_FLD_S(ip4_addr_p_t src); + PACK_STRUCT_FLD_S(ip4_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Macros to get struct ip_hdr fields: */ +#define IPH_V(hdr) ((hdr)->_v_hl >> 4) +#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f) +#define IPH_TOS(hdr) ((hdr)->_tos) +#define IPH_LEN(hdr) ((hdr)->_len) +#define IPH_ID(hdr) ((hdr)->_id) +#define IPH_OFFSET(hdr) ((hdr)->_offset) +#define IPH_TTL(hdr) ((hdr)->_ttl) +#define IPH_PROTO(hdr) ((hdr)->_proto) +#define IPH_CHKSUM(hdr) ((hdr)->_chksum) + +/* Macros to set struct ip_hdr fields: */ +#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (u8_t)((((v) << 4) | (hl))) +#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos) +#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) +#define IPH_ID_SET(hdr, id) (hdr)->_id = (id) +#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) +#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl) +#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto) +#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_IP4_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip6.h new file mode 100644 index 0000000..6e1e263 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/ip6.h @@ -0,0 +1,169 @@ +/** + * @file + * IPv6 protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_IP6_H +#define LWIP_HDR_PROT_IP6_H + +#include "lwip/arch.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is the packed version of ip6_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_addr_packed { + PACK_STRUCT_FIELD(u32_t addr[4]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +typedef struct ip6_addr_packed ip6_addr_p_t; + +#define IP6_HLEN 40 + +#define IP6_NEXTH_HOPBYHOP 0 +#define IP6_NEXTH_TCP 6 +#define IP6_NEXTH_UDP 17 +#define IP6_NEXTH_ENCAPS 41 +#define IP6_NEXTH_ROUTING 43 +#define IP6_NEXTH_FRAGMENT 44 +#define IP6_NEXTH_ICMP6 58 +#define IP6_NEXTH_NONE 59 +#define IP6_NEXTH_DESTOPTS 60 +#define IP6_NEXTH_UDPLITE 136 + +/** The IPv6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hdr { + /** version / traffic class / flow label */ + PACK_STRUCT_FIELD(u32_t _v_tc_fl); + /** payload length */ + PACK_STRUCT_FIELD(u16_t _plen); + /** next header */ + PACK_STRUCT_FLD_8(u8_t _nexth); + /** hop limit */ + PACK_STRUCT_FLD_8(u8_t _hoplim); + /** source and destination IP addresses */ + PACK_STRUCT_FLD_S(ip6_addr_p_t src); + PACK_STRUCT_FLD_S(ip6_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Hop-by-hop router alert option. */ +#define IP6_HBH_HLEN 8 +#define IP6_PAD1_OPTION 0 +#define IP6_PADN_ALERT_OPTION 1 +#define IP6_ROUTER_ALERT_OPTION 5 +#define IP6_ROUTER_ALERT_VALUE_MLD 0 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hbh_hdr { + /* next header */ + PACK_STRUCT_FLD_8(u8_t _nexth); + /* header length */ + PACK_STRUCT_FLD_8(u8_t _hlen); + /* router alert option type */ + PACK_STRUCT_FLD_8(u8_t _ra_opt_type); + /* router alert option data len */ + PACK_STRUCT_FLD_8(u8_t _ra_opt_dlen); + /* router alert option data */ + PACK_STRUCT_FIELD(u16_t _ra_opt_data); + /* PadN option type */ + PACK_STRUCT_FLD_8(u8_t _padn_opt_type); + /* PadN option data len */ + PACK_STRUCT_FLD_8(u8_t _padn_opt_dlen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Fragment header. */ +#define IP6_FRAG_HLEN 8 +#define IP6_FRAG_OFFSET_MASK 0xfff8 +#define IP6_FRAG_MORE_FLAG 0x0001 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_frag_hdr { + /* next header */ + PACK_STRUCT_FLD_8(u8_t _nexth); + /* reserved */ + PACK_STRUCT_FLD_8(u8_t reserved); + /* fragment offset */ + PACK_STRUCT_FIELD(u16_t _fragment_offset); + /* fragmented packet identification */ + PACK_STRUCT_FIELD(u32_t _identification); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP6H_V(hdr) ((lwip_ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f) +#define IP6H_TC(hdr) ((lwip_ntohl((hdr)->_v_tc_fl) >> 20) & 0xff) +#define IP6H_FL(hdr) (lwip_ntohl((hdr)->_v_tc_fl) & 0x000fffff) +#define IP6H_PLEN(hdr) (lwip_ntohs((hdr)->_plen)) +#define IP6H_NEXTH(hdr) ((hdr)->_nexth) +#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6) +#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim) + +#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (lwip_htonl((((u32_t)(v)) << 28) | (((u32_t)(tc)) << 20) | (fl))) +#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = lwip_htons(plen) +#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth) +#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_IP6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/mld6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/mld6.h new file mode 100644 index 0000000..be3a006 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/mld6.h @@ -0,0 +1,70 @@ +/** + * @file + * MLD6 protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_MLD6_H +#define LWIP_HDR_PROT_MLD6_H + +#include "lwip/arch.h" +#include "lwip/prot/ip6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Multicast listener report/query/done message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mld_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t max_resp_delay); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FLD_S(ip6_addr_p_t multicast_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_MLD6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/nd6.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/nd6.h new file mode 100644 index 0000000..2d4903d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/nd6.h @@ -0,0 +1,277 @@ +/** + * @file + * ND6 protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_ND6_H +#define LWIP_HDR_PROT_ND6_H + +#include "lwip/arch.h" +#include "lwip/ip6_addr.h" +#include "lwip/prot/ip6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Neighbor solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ns_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FLD_S(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Neighbor advertisement message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct na_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FLD_8(u8_t flags); + PACK_STRUCT_FLD_8(u8_t reserved[3]); + PACK_STRUCT_FLD_S(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define ND6_FLAG_ROUTER (0x80) +#define ND6_FLAG_SOLICITED (0x40) +#define ND6_FLAG_OVERRIDE (0x20) + +/** Router solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct rs_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Router advertisement message header. */ +#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80) +#define ND6_RA_FLAG_OTHER_CONFIG (0x40) +#define ND6_RA_FLAG_HOME_AGENT (0x20) +#define ND6_RA_PREFERENCE_MASK (0x18) +#define ND6_RA_PREFERENCE_HIGH (0x08) +#define ND6_RA_PREFERENCE_MEDIUM (0x00) +#define ND6_RA_PREFERENCE_LOW (0x18) +#define ND6_RA_PREFERENCE_DISABLED (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ra_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FLD_8(u8_t current_hop_limit); + PACK_STRUCT_FLD_8(u8_t flags); + PACK_STRUCT_FIELD(u16_t router_lifetime); + PACK_STRUCT_FIELD(u32_t reachable_time); + PACK_STRUCT_FIELD(u32_t retrans_timer); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirect message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirect_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FLD_S(ip6_addr_p_t target_address); + PACK_STRUCT_FLD_S(ip6_addr_p_t destination_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Link-layer address option. */ +#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01) +#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct lladdr_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t addr[NETIF_MAX_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Prefix information option. */ +#define ND6_OPTION_TYPE_PREFIX_INFO (0x03) +#define ND6_PREFIX_FLAG_ON_LINK (0x80) +#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40) +#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20) +#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct prefix_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t prefix_length); + PACK_STRUCT_FLD_8(u8_t flags); + PACK_STRUCT_FIELD(u32_t valid_lifetime); + PACK_STRUCT_FIELD(u32_t preferred_lifetime); + PACK_STRUCT_FLD_8(u8_t reserved2[3]); + PACK_STRUCT_FLD_8(u8_t site_prefix_length); + PACK_STRUCT_FLD_S(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirected header option. */ +#define ND6_OPTION_TYPE_REDIR_HDR (0x04) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirected_header_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t reserved[6]); + /* Portion of redirected packet follows. */ + /* PACK_STRUCT_FLD_8(u8_t redirected[8]); */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** MTU option. */ +#define ND6_OPTION_TYPE_MTU (0x05) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mtu_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(u32_t mtu); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Route information option. */ +#define ND6_OPTION_TYPE_ROUTE_INFO (24) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct route_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t prefix_length); + PACK_STRUCT_FLD_8(u8_t preference); + PACK_STRUCT_FIELD(u32_t route_lifetime); + PACK_STRUCT_FLD_S(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Recursive DNS Server Option. */ +#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS +#define LWIP_RDNSS_OPTION_MAX_SERVERS LWIP_ND6_RDNSS_MAX_DNS_SERVERS +#else +#define LWIP_RDNSS_OPTION_MAX_SERVERS 1 +#endif +#define ND6_OPTION_TYPE_RDNSS (25) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct rdnss_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(u32_t lifetime); + PACK_STRUCT_FLD_S(ip6_addr_p_t rdnss_address[LWIP_RDNSS_OPTION_MAX_SERVERS]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_ND6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/tcp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/tcp.h new file mode 100644 index 0000000..67fe7b9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/tcp.h @@ -0,0 +1,97 @@ +/** + * @file + * TCP protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_TCP_H +#define LWIP_HDR_PROT_TCP_H + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Length of the TCP header, excluding options. */ +#define TCP_HLEN 20 + +/* Fields are (of course) in network byte order. + * Some fields are converted to host byte order in tcp_input(). + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct tcp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); + PACK_STRUCT_FIELD(u32_t seqno); + PACK_STRUCT_FIELD(u32_t ackno); + PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags); + PACK_STRUCT_FIELD(u16_t wnd); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t urgp); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* TCP header flags bits */ +#define TCP_FIN 0x01U +#define TCP_SYN 0x02U +#define TCP_RST 0x04U +#define TCP_PSH 0x08U +#define TCP_ACK 0x10U +#define TCP_URG 0x20U +#define TCP_ECE 0x40U +#define TCP_CWR 0x80U +/* Valid TCP header flags */ +#define TCP_FLAGS 0x3fU + +#define TCPH_HDRLEN(phdr) ((u16_t)(lwip_ntohs((phdr)->_hdrlen_rsvd_flags) >> 12)) +#define TCPH_FLAGS(phdr) ((u16_t)(lwip_ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)) + +#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = lwip_htons(((len) << 12) | TCPH_FLAGS(phdr)) +#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS(~TCP_FLAGS)) | lwip_htons(flags)) +#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = (u16_t)(lwip_htons((u16_t)((len) << 12) | (flags))) + +#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | lwip_htons(flags)) +#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags & ~lwip_htons(flags)) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_TCP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/prot/udp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/udp.h new file mode 100644 index 0000000..664f19a --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/prot/udp.h @@ -0,0 +1,68 @@ +/** + * @file + * UDP protocol definitions + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_PROT_UDP_H +#define LWIP_HDR_PROT_UDP_H + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define UDP_HLEN 8 + +/* Fields are (of course) in network byte order. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct udp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */ + PACK_STRUCT_FIELD(u16_t len); + PACK_STRUCT_FIELD(u16_t chksum); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PROT_UDP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/raw.h b/sdk/Src/Network/lwip2.0.3/include/lwip/raw.h new file mode 100644 index 0000000..9f37a0f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/raw.h @@ -0,0 +1,118 @@ +/** + * @file + * raw API (to be used from TCPIP thread)\n + * See also @ref raw_raw + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_RAW_H +#define LWIP_HDR_RAW_H + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct raw_pcb; + +/** Function prototype for raw pcb receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr); + +/** the RAW protocol control block */ +struct raw_pcb { + /* Common members of all PCB types */ + IP_PCB; + + struct raw_pcb *next; + + u8_t protocol; + + /** receive callback function */ + raw_recv_fn recv; + /* user-supplied argument for the recv callback */ + void *recv_arg; +#if LWIP_IPV6 + /* fields for handling checksum computations as per RFC3542. */ + u16_t chksum_offset; + u8_t chksum_reqd; +#endif +}; + +/* The following functions is the application layer interface to the + RAW code. */ +struct raw_pcb * raw_new (u8_t proto); +struct raw_pcb * raw_new_ip_type(u8_t type, u8_t proto); +void raw_remove (struct raw_pcb *pcb); +err_t raw_bind (struct raw_pcb *pcb, const ip_addr_t *ipaddr); +err_t raw_connect (struct raw_pcb *pcb, const ip_addr_t *ipaddr); + +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr); +err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); + +void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg); + +/* The following functions are the lower layer interface to RAW. */ +u8_t raw_input (struct pbuf *p, struct netif *inp); +#define raw_init() /* Compatibility define, no init needed. */ + +void raw_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr); + +/* for compatibility with older implementation */ +#define raw_new_ip6(proto) raw_new_ip_type(IPADDR_TYPE_V6, proto) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* LWIP_HDR_RAW_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/sio.h b/sdk/Src/Network/lwip2.0.3/include/lwip/sio.h new file mode 100644 index 0000000..efa8ade --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/sio.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + */ + +/* + * This is the interface to the platform specific serial IO module + * It needs to be implemented by those platforms which need SLIP or PPP + */ + +#ifndef SIO_H +#define SIO_H + +#include "lwip/arch.h" +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* If you want to define sio_fd_t elsewhere or differently, + define this in your cc.h file. */ +#ifndef __sio_fd_t_defined +typedef void * sio_fd_t; +#endif + +/* The following functions can be defined to something else in your cc.h file + or be implemented in your custom sio.c file. */ + +#ifndef sio_open +/** + * Opens a serial device for communication. + * + * @param devnum device number + * @return handle to serial device if successful, NULL otherwise + */ +sio_fd_t sio_open(u8_t devnum); +#endif + +#ifndef sio_send +/** + * Sends a single character to the serial device. + * + * @param c character to send + * @param fd serial device handle + * + * @note This function will block until the character can be sent. + */ +void sio_send(u8_t c, sio_fd_t fd); +#endif + +#ifndef sio_recv +/** + * Receives a single character from the serial device. + * + * @param fd serial device handle + * + * @note This function will block until a character is received. + */ +u8_t sio_recv(sio_fd_t fd); +#endif + +#ifndef sio_read +/** + * Reads from the serial device. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received - may be 0 if aborted by sio_read_abort + * + * @note This function will block until data can be received. The blocking + * can be cancelled by calling sio_read_abort(). + */ +u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_tryread +/** + * Tries to read from the serial device. Same as sio_read but returns + * immediately if no data is available and never blocks. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received + */ +u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_write +/** + * Writes to the serial device. + * + * @param fd serial device handle + * @param data pointer to data to send + * @param len length (in bytes) of data to send + * @return number of bytes actually sent + * + * @note This function will block until all data can be sent. + */ +u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_read_abort +/** + * Aborts a blocking sio_read() call. + * + * @param fd serial device handle + */ +void sio_read_abort(sio_fd_t fd); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* SIO_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/snmp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/snmp.h new file mode 100644 index 0000000..80e6e57 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/snmp.h @@ -0,0 +1,213 @@ +/** + * @file + * SNMP support API for implementing netifs and statitics for MIB2 + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Dirk Ziegelmeier + * + */ +#ifndef LWIP_HDR_SNMP_H +#define LWIP_HDR_SNMP_H + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct udp_pcb; +struct netif; + +/** + * @defgroup netif_mib2 MIB2 statistics + * @ingroup netif + */ + +/* MIB2 statistics functions */ +#if MIB2_STATS /* don't build if not configured for use in lwipopts.h */ +/** + * @ingroup netif_mib2 + * @see RFC1213, "MIB-II, 6. Definitions" + */ +enum snmp_ifType { + snmp_ifType_other=1, /* none of the following */ + snmp_ifType_regular1822, + snmp_ifType_hdh1822, + snmp_ifType_ddn_x25, + snmp_ifType_rfc877_x25, + snmp_ifType_ethernet_csmacd, + snmp_ifType_iso88023_csmacd, + snmp_ifType_iso88024_tokenBus, + snmp_ifType_iso88025_tokenRing, + snmp_ifType_iso88026_man, + snmp_ifType_starLan, + snmp_ifType_proteon_10Mbit, + snmp_ifType_proteon_80Mbit, + snmp_ifType_hyperchannel, + snmp_ifType_fddi, + snmp_ifType_lapb, + snmp_ifType_sdlc, + snmp_ifType_ds1, /* T-1 */ + snmp_ifType_e1, /* european equiv. of T-1 */ + snmp_ifType_basicISDN, + snmp_ifType_primaryISDN, /* proprietary serial */ + snmp_ifType_propPointToPointSerial, + snmp_ifType_ppp, + snmp_ifType_softwareLoopback, + snmp_ifType_eon, /* CLNP over IP [11] */ + snmp_ifType_ethernet_3Mbit, + snmp_ifType_nsip, /* XNS over IP */ + snmp_ifType_slip, /* generic SLIP */ + snmp_ifType_ultra, /* ULTRA technologies */ + snmp_ifType_ds3, /* T-3 */ + snmp_ifType_sip, /* SMDS */ + snmp_ifType_frame_relay +}; + +/** This macro has a precision of ~49 days because sys_now returns u32_t. \#define your own if you want ~490 days. */ +#ifndef MIB2_COPY_SYSUPTIME_TO +#define MIB2_COPY_SYSUPTIME_TO(ptrToVal) (*(ptrToVal) = (sys_now() / 10)) +#endif + +/** + * @ingroup netif_mib2 + * Increment stats member for SNMP MIB2 stats (struct stats_mib2_netif_ctrs) + */ +#define MIB2_STATS_NETIF_INC(n, x) do { ++(n)->mib2_counters.x; } while(0) +/** + * @ingroup netif_mib2 + * Add value to stats member for SNMP MIB2 stats (struct stats_mib2_netif_ctrs) + */ +#define MIB2_STATS_NETIF_ADD(n, x, val) do { (n)->mib2_counters.x += (val); } while(0) + +/** + * @ingroup netif_mib2 + * Init MIB2 statistic counters in netif + * @param netif Netif to init + * @param type one of enum @ref snmp_ifType + * @param speed your link speed here (units: bits per second) + */ +#define MIB2_INIT_NETIF(netif, type, speed) do { \ + (netif)->link_type = (type); \ + (netif)->link_speed = (speed);\ + (netif)->ts = 0; \ + (netif)->mib2_counters.ifinoctets = 0; \ + (netif)->mib2_counters.ifinucastpkts = 0; \ + (netif)->mib2_counters.ifinnucastpkts = 0; \ + (netif)->mib2_counters.ifindiscards = 0; \ + (netif)->mib2_counters.ifinerrors = 0; \ + (netif)->mib2_counters.ifinunknownprotos = 0; \ + (netif)->mib2_counters.ifoutoctets = 0; \ + (netif)->mib2_counters.ifoutucastpkts = 0; \ + (netif)->mib2_counters.ifoutnucastpkts = 0; \ + (netif)->mib2_counters.ifoutdiscards = 0; \ + (netif)->mib2_counters.ifouterrors = 0; } while(0) +#else /* MIB2_STATS */ +#ifndef MIB2_COPY_SYSUPTIME_TO +#define MIB2_COPY_SYSUPTIME_TO(ptrToVal) +#endif +#define MIB2_INIT_NETIF(netif, type, speed) +#define MIB2_STATS_NETIF_INC(n, x) +#define MIB2_STATS_NETIF_ADD(n, x, val) +#endif /* MIB2_STATS */ + +/* LWIP MIB2 callbacks */ +#if LWIP_MIB2_CALLBACKS /* don't build if not configured for use in lwipopts.h */ +/* network interface */ +void mib2_netif_added(struct netif *ni); +void mib2_netif_removed(struct netif *ni); + +#if LWIP_IPV4 && LWIP_ARP +/* ARP (for atTable and ipNetToMediaTable) */ +void mib2_add_arp_entry(struct netif *ni, ip4_addr_t *ip); +void mib2_remove_arp_entry(struct netif *ni, ip4_addr_t *ip); +#else /* LWIP_IPV4 && LWIP_ARP */ +#define mib2_add_arp_entry(ni,ip) +#define mib2_remove_arp_entry(ni,ip) +#endif /* LWIP_IPV4 && LWIP_ARP */ + +/* IP */ +#if LWIP_IPV4 +void mib2_add_ip4(struct netif *ni); +void mib2_remove_ip4(struct netif *ni); +void mib2_add_route_ip4(u8_t dflt, struct netif *ni); +void mib2_remove_route_ip4(u8_t dflt, struct netif *ni); +#endif /* LWIP_IPV4 */ + +/* UDP */ +#if LWIP_UDP +void mib2_udp_bind(struct udp_pcb *pcb); +void mib2_udp_unbind(struct udp_pcb *pcb); +#endif /* LWIP_UDP */ + +#else /* LWIP_MIB2_CALLBACKS */ +/* LWIP_MIB2_CALLBACKS support not available */ +/* define everything to be empty */ + +/* network interface */ +#define mib2_netif_added(ni) +#define mib2_netif_removed(ni) + +/* ARP */ +#define mib2_add_arp_entry(ni,ip) +#define mib2_remove_arp_entry(ni,ip) + +/* IP */ +#define mib2_add_ip4(ni) +#define mib2_remove_ip4(ni) +#define mib2_add_route_ip4(dflt, ni) +#define mib2_remove_route_ip4(dflt, ni) + +/* UDP */ +#define mib2_udp_bind(pcb) +#define mib2_udp_unbind(pcb) +#endif /* LWIP_MIB2_CALLBACKS */ + +/* for source-code compatibility reasons only, can be removed (not used internally) */ +#define NETIF_INIT_SNMP MIB2_INIT_NETIF +#define snmp_add_ifinoctets(ni,value) MIB2_STATS_NETIF_ADD(ni, ifinoctets, value) +#define snmp_inc_ifinucastpkts(ni) MIB2_STATS_NETIF_INC(ni, ifinucastpkts) +#define snmp_inc_ifinnucastpkts(ni) MIB2_STATS_NETIF_INC(ni, ifinnucastpkts) +#define snmp_inc_ifindiscards(ni) MIB2_STATS_NETIF_INC(ni, ifindiscards) +#define snmp_inc_ifinerrors(ni) MIB2_STATS_NETIF_INC(ni, ifinerrors) +#define snmp_inc_ifinunknownprotos(ni) MIB2_STATS_NETIF_INC(ni, ifinunknownprotos) +#define snmp_add_ifoutoctets(ni,value) MIB2_STATS_NETIF_ADD(ni, ifoutoctets, value) +#define snmp_inc_ifoutucastpkts(ni) MIB2_STATS_NETIF_INC(ni, ifoutucastpkts) +#define snmp_inc_ifoutnucastpkts(ni) MIB2_STATS_NETIF_INC(ni, ifoutnucastpkts) +#define snmp_inc_ifoutdiscards(ni) MIB2_STATS_NETIF_INC(ni, ifoutdiscards) +#define snmp_inc_ifouterrors(ni) MIB2_STATS_NETIF_INC(ni, ifouterrors) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_SNMP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/sockets.h b/sdk/Src/Network/lwip2.0.3/include/lwip/sockets.h new file mode 100644 index 0000000..86c87ac --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/sockets.h @@ -0,0 +1,602 @@ +/** + * @file + * Socket API (to be used from non-TCPIP threads) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +#ifndef LWIP_HDR_SOCKETS_H +#define LWIP_HDR_SOCKETS_H + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/inet.h" +#include "lwip/errno.h" +#include "wm_sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if 0 +/* If your port already typedef's sa_family_t, define SA_FAMILY_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(sa_family_t) && !defined(SA_FAMILY_T_DEFINED) +typedef u8_t sa_family_t; +#endif +/* If your port already typedef's in_port_t, define IN_PORT_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(in_port_t) && !defined(IN_PORT_T_DEFINED) +typedef u16_t in_port_t; +#endif + +#if LWIP_IPV4 +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; +#define SIN_ZERO_LEN 8 + char sin_zero[SIN_ZERO_LEN]; +}; +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +struct sockaddr_in6 { + u8_t sin6_len; /* length of this structure */ + sa_family_t sin6_family; /* AF_INET6 */ + in_port_t sin6_port; /* Transport layer port # */ + u32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + u32_t sin6_scope_id; /* Set of interfaces for scope */ +}; +#endif /* LWIP_IPV6 */ + +struct sockaddr { + u8_t sa_len; + sa_family_t sa_family; + char sa_data[14]; +}; + +struct sockaddr_storage { + u8_t s2_len; + sa_family_t ss_family; + char s2_data1[2]; + u32_t s2_data2[3]; +#if LWIP_IPV6 + u32_t s2_data3[3]; +#endif /* LWIP_IPV6 */ +}; + +/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef u32_t socklen_t; +#endif + +struct lwip_sock; +#endif + +#if !LWIP_TCPIP_CORE_LOCKING +/** Maximum optlen used by setsockopt/getsockopt */ +#define LWIP_SETGETSOCKOPT_MAXOPTLEN 16 + +/** This struct is used to pass data to the set/getsockopt_internal + * functions running in tcpip_thread context (only a void* is allowed) */ +struct lwip_setgetsockopt_data { + /** socket index for which to change options */ + int s; + /** level of the option to process */ + int level; + /** name of the option to process */ + int optname; + /** set: value to set the option to + * get: value of the option is stored here */ +#if LWIP_MPU_COMPATIBLE + u8_t optval[LWIP_SETGETSOCKOPT_MAXOPTLEN]; +#else + union { + void *p; + const void *pc; + } optval; +#endif + /** size of *optval */ + socklen_t optlen; + /** if an error occurs, it is temporarily stored here */ + err_t err; + /** semaphore to wake up the calling task */ + void* completed_sem; +}; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + +#if !defined(iovec) +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif + +struct msghdr { + void *msg_name; + socklen_t msg_namelen; + struct iovec *msg_iov; + int msg_iovlen; + void *msg_control; + socklen_t msg_controllen; + int msg_flags; +}; +#if 0 +/* Socket protocol types (TCP/UDP/RAW) */ +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +/* + * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c) + */ +#define SO_REUSEADDR 0x0004 /* Allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ + + +/* + * Additional options, not kept in so_options. + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_DONTLINGER ((int)(~SO_LINGER)) +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ +#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ +#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ +#define SO_NO_CHECK 0x100a /* don't create UDP checksum */ + + +/* + * Structure used for manipulating linger option. + */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time in seconds */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xfff /* options for socket level */ + + +#define AF_UNSPEC 0 +#define AF_INET 2 +#if LWIP_IPV6 +#define AF_INET6 10 +#else /* LWIP_IPV6 */ +#define AF_INET6 AF_UNSPEC +#endif /* LWIP_IPV6 */ +#define PF_INET AF_INET +#define PF_INET6 AF_INET6 +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_ICMP 1 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#if LWIP_IPV6 +#define IPPROTO_IPV6 41 +#define IPPROTO_ICMPV6 58 +#endif /* LWIP_IPV6 */ +#define IPPROTO_UDPLITE 136 +#define IPPROTO_RAW 255 + +/* Flags we can use with send and recv. */ +#define MSG_PEEK 0x01 /* Peeks at an incoming message */ +#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */ +#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */ +#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */ +#define MSG_MORE 0x10 /* Sender will send more */ + + +/* + * Options for level IPPROTO_IP + */ +#define IP_TOS 1 +#define IP_TTL 2 + +#if LWIP_TCP +/* + * Options for level IPPROTO_TCP + */ +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */ +#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ +#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ +#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* + * Options for level IPPROTO_IPV6 + */ +#define IPV6_CHECKSUM 7 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */ +#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE +/* + * Options for level IPPROTO_UDPLITE + */ +#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ +#endif /* LWIP_UDP && LWIP_UDPLITE*/ + + +#if LWIP_MULTICAST_TX_OPTIONS +/* + * Options and types for UDP multicast traffic handling + */ +#define IP_MULTICAST_TTL 5 +#define IP_MULTICAST_IF 6 +#define IP_MULTICAST_LOOP 7 +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + +#if LWIP_IGMP +/* + * Options and types related to multicast membership + */ +#define IP_ADD_MEMBERSHIP 3 +#define IP_DROP_MEMBERSHIP 4 + +typedef struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ip_mreq; +#endif /* LWIP_IGMP */ + +/* + * The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. + */ +#define IPTOS_PREC_MASK 0xe0 +#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* + * Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * lwip_ioctl only supports FIONREAD and FIONBIO, for now + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#if !defined(FIONREAD) || !defined(FIONBIO) +#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000UL /* no parameters */ +#define IOC_OUT 0x40000000UL /* copy out parameters */ +#define IOC_IN 0x80000000UL /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) +#endif /* !defined(FIONREAD) || !defined(FIONBIO) */ + +#ifndef FIONREAD +#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ +#endif +#ifndef FIONBIO +#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ +#endif + +/* Socket I/O Controls: unimplemented */ +#ifndef SIOCSHIWAT +#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ +#endif + +/* commands for fnctl */ +#ifndef F_GETFL +#define F_GETFL 3 +#endif +#ifndef F_SETFL +#define F_SETFL 4 +#endif + +/* File status flags and file access modes for fnctl, + these are bits in an int. */ +#ifndef O_NONBLOCK +#define O_NONBLOCK 1 /* nonblocking I/O */ +#endif +#ifndef O_NDELAY +#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */ +#endif + +#ifndef SHUT_RD + #define SHUT_RD 0 + #define SHUT_WR 1 + #define SHUT_RDWR 2 +#endif + +/* FD_SET used for lwip_select */ +#ifndef FD_SET +#undef FD_SETSIZE +/* Make FD_SETSIZE match NUM_SOCKETS in socket.c */ +#define FD_SETSIZE MEMP_NUM_NETCONN +#define FDSETSAFESET(n, code) do { \ + if (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0)) { \ + code; }} while(0) +#define FDSETSAFEGET(n, code) (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0) ?\ + (code) : 0) +#define FD_SET(n, p) FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] |= (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define FD_CLR(n, p) FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] &= ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define FD_ISSET(n,p) FDSETSAFEGET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] & (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p))) + +typedef struct fd_set +{ + unsigned char fd_bits [(FD_SETSIZE+7)/8]; +} fd_set; + +#elif LWIP_SOCKET_OFFSET +#error LWIP_SOCKET_OFFSET does not work with external FD_SET! +#elif FD_SETSIZE < MEMP_NUM_NETCONN +#error "external FD_SETSIZE too small for number of sockets" +#endif /* FD_SET */ + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#ifndef LWIP_TIMEVAL_PRIVATE +#define LWIP_TIMEVAL_PRIVATE 1 +#endif + +#if LWIP_TIMEVAL_PRIVATE +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* LWIP_TIMEVAL_PRIVATE */ +#endif +#define lwip_socket_init() /* Compatibility define, no init needed. */ +void lwip_socket_thread_init(void); /* LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */ +void lwip_socket_thread_cleanup(void); /* LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */ + +#if LWIP_COMPAT_SOCKETS == 2 +/* This helps code parsers/code completion by not having the COMPAT functions as defines */ +#define lwip_accept accept +#define lwip_bind bind +#define lwip_shutdown shutdown +#define lwip_getpeername getpeername +#define lwip_getsockname getsockname +#define lwip_setsockopt setsockopt +#define lwip_getsockopt getsockopt +#define lwip_close closesocket +#define lwip_connect connect +#define lwip_listen listen +#define lwip_recv recv +#define lwip_recvfrom recvfrom +#define lwip_send send +#define lwip_sendmsg sendmsg +#define lwip_sendto sendto +#define lwip_socket socket +#define lwip_select select +#define lwip_ioctlsocket ioctl + +#if LWIP_POSIX_SOCKETS_IO_NAMES +#define lwip_read read +#define lwip_write write +#define lwip_writev writev +#undef lwip_close +#define lwip_close close +#define closesocket(s) close(s) +#define lwip_fcntl fcntl +#define lwip_ioctl ioctl +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ +#endif /* LWIP_COMPAT_SOCKETS == 2 */ + +int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_shutdown(int s, int how); +int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen); +int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen); +int lwip_close(int s); +int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_listen(int s, int backlog); +int lwip_recv(int s, void *mem, size_t len, int flags); +int lwip_read(int s, void *mem, size_t len); +int lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen); +int lwip_send(int s, const void *dataptr, size_t size, int flags); +int lwip_sendmsg(int s, const struct msghdr *message, int flags); +int lwip_sendto(int s, const void *dataptr, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen); +int lwip_socket(int domain, int type, int protocol); +int lwip_write(int s, const void *dataptr, size_t size); +int lwip_writev(int s, const struct iovec *iov, int iovcnt); +int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout); +int lwip_ioctl(int s, long cmd, void *argp); +int lwip_fcntl(int s, int cmd, int val); + +#if LWIP_COMPAT_SOCKETS +#if LWIP_COMPAT_SOCKETS != 2 +#if 0 +/** @ingroup socket */ +#define accept(s,addr,addrlen) lwip_accept(s,addr,addrlen) +/** @ingroup socket */ +#define bind(s,name,namelen) lwip_bind(s,name,namelen) +/** @ingroup socket */ +#define shutdown(s,how) lwip_shutdown(s,how) +/** @ingroup socket */ +#define getpeername(s,name,namelen) lwip_getpeername(s,name,namelen) +/** @ingroup socket */ +#define getsockname(s,name,namelen) lwip_getsockname(s,name,namelen) +/** @ingroup socket */ +#define setsockopt(s,level,optname,opval,optlen) lwip_setsockopt(s,level,optname,opval,optlen) +/** @ingroup socket */ +#define getsockopt(s,level,optname,opval,optlen) lwip_getsockopt(s,level,optname,opval,optlen) +/** @ingroup socket */ +#define closesocket(s) lwip_close(s) +/** @ingroup socket */ +#define connect(s,name,namelen) lwip_connect(s,name,namelen) +/** @ingroup socket */ +#define listen(s,backlog) lwip_listen(s,backlog) +/** @ingroup socket */ +#define recv(s,mem,len,flags) lwip_recv(s,mem,len,flags) +/** @ingroup socket */ +#define recvfrom(s,mem,len,flags,from,fromlen) lwip_recvfrom(s,mem,len,flags,from,fromlen) +/** @ingroup socket */ +#define send(s,dataptr,size,flags) lwip_send(s,dataptr,size,flags) +/** @ingroup socket */ +#define sendmsg(s,message,flags) lwip_sendmsg(s,message,flags) +/** @ingroup socket */ +#define sendto(s,dataptr,size,flags,to,tolen) lwip_sendto(s,dataptr,size,flags,to,tolen) +/** @ingroup socket */ +#define socket(domain,type,protocol) lwip_socket(domain,type,protocol) +/** @ingroup socket */ +#define select(maxfdp1,readset,writeset,exceptset,timeout) lwip_select(maxfdp1,readset,writeset,exceptset,timeout) +/** @ingroup socket */ +#define ioctlsocket(s,cmd,argp) lwip_ioctl(s,cmd,argp) +#endif +#if LWIP_POSIX_SOCKETS_IO_NAMES +#if 0 +/** @ingroup socket */ +#define read(s,mem,len) lwip_read(s,mem,len) +/** @ingroup socket */ +#define write(s,dataptr,len) lwip_write(s,dataptr,len) +#endif +/** @ingroup socket */ +#define writev(s,iov,iovcnt) lwip_writev(s,iov,iovcnt) +#if 0 +/** @ingroup socket */ +#define close(s) lwip_close(s) +/** @ingroup socket */ +#define fcntl(s,cmd,val) lwip_fcntl(s,cmd,val) +/** @ingroup socket */ +#define ioctl(s,cmd,argp) lwip_ioctl(s,cmd,argp) +#endif +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ +#endif /* LWIP_COMPAT_SOCKETS != 2 */ + +#if 0 +#if LWIP_IPV4 && LWIP_IPV6 +/** @ingroup socket */ +#define inet_ntop(af,src,dst,size) \ + (((af) == AF_INET6) ? ip6addr_ntoa_r((const ip6_addr_t*)(src),(dst),(size)) \ + : (((af) == AF_INET) ? ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL)) +/** @ingroup socket */ +#define inet_pton(af,src,dst) \ + (((af) == AF_INET6) ? ip6addr_aton((src),(ip6_addr_t*)(dst)) \ + : (((af) == AF_INET) ? ip4addr_aton((src),(ip4_addr_t*)(dst)) : 0)) +#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */ +#define inet_ntop(af,src,dst,size) \ + (((af) == AF_INET) ? ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL) +#define inet_pton(af,src,dst) \ + (((af) == AF_INET) ? ip4addr_aton((src),(ip4_addr_t*)(dst)) : 0) +#else /* LWIP_IPV4 && LWIP_IPV6 */ +#define inet_ntop(af,src,dst,size) \ + (((af) == AF_INET6) ? ip6addr_ntoa_r((const ip6_addr_t*)(src),(dst),(size)) : NULL) +#define inet_pton(af,src,dst) \ + (((af) == AF_INET6) ? ip6addr_aton((src),(ip6_addr_t*)(dst)) : 0) +#endif /* LWIP_IPV4 && LWIP_IPV6 */ +#endif +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + +#endif /* LWIP_HDR_SOCKETS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/stats.h b/sdk/Src/Network/lwip2.0.3/include/lwip/stats.h new file mode 100644 index 0000000..a0c818b --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/stats.h @@ -0,0 +1,491 @@ +/** + * @file + * Statistics API (to be used from TCPIP thread) + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_STATS_H +#define LWIP_HDR_STATS_H + +#include "lwip/opt.h" + +#include "lwip/mem.h" +#include "lwip/memp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_STATS + +#ifndef LWIP_STATS_LARGE +#define LWIP_STATS_LARGE 0 +#endif + +#if LWIP_STATS_LARGE +#define STAT_COUNTER u32_t +#define STAT_COUNTER_F U32_F +#else +#define STAT_COUNTER u16_t +#define STAT_COUNTER_F U16_F +#endif + +/** Protocol related stats */ +struct stats_proto { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER fw; /* Forwarded packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER rterr; /* Routing error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER opterr; /* Error in options. */ + STAT_COUNTER err; /* Misc error. */ + STAT_COUNTER cachehit; +}; + +/** IGMP stats */ +struct stats_igmp { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER rx_v1; /* Received v1 frames. */ + STAT_COUNTER rx_group; /* Received group-specific queries. */ + STAT_COUNTER rx_general; /* Received general queries. */ + STAT_COUNTER rx_report; /* Received reports. */ + STAT_COUNTER tx_join; /* Sent joins. */ + STAT_COUNTER tx_leave; /* Sent leaves. */ + STAT_COUNTER tx_report; /* Sent reports. */ +}; + +/** Memory stats */ +struct stats_mem { +#if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY + const char *name; +#endif /* defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY */ + STAT_COUNTER err; + mem_size_t avail; + mem_size_t used; + mem_size_t max; + STAT_COUNTER illegal; +}; + +/** System element stats */ +struct stats_syselem { + STAT_COUNTER used; + STAT_COUNTER max; + STAT_COUNTER err; +}; + +/** System stats */ +struct stats_sys { + struct stats_syselem sem; + struct stats_syselem mutex; + struct stats_syselem mbox; +}; + +/** SNMP MIB2 stats */ +struct stats_mib2 { + /* IP */ + u32_t ipinhdrerrors; + u32_t ipinaddrerrors; + u32_t ipinunknownprotos; + u32_t ipindiscards; + u32_t ipindelivers; + u32_t ipoutrequests; + u32_t ipoutdiscards; + u32_t ipoutnoroutes; + u32_t ipreasmoks; + u32_t ipreasmfails; + u32_t ipfragoks; + u32_t ipfragfails; + u32_t ipfragcreates; + u32_t ipreasmreqds; + u32_t ipforwdatagrams; + u32_t ipinreceives; + + /* TCP */ + u32_t tcpactiveopens; + u32_t tcppassiveopens; + u32_t tcpattemptfails; + u32_t tcpestabresets; + u32_t tcpoutsegs; + u32_t tcpretranssegs; + u32_t tcpinsegs; + u32_t tcpinerrs; + u32_t tcpoutrsts; + + /* UDP */ + u32_t udpindatagrams; + u32_t udpnoports; + u32_t udpinerrors; + u32_t udpoutdatagrams; + + /* ICMP */ + u32_t icmpinmsgs; + u32_t icmpinerrors; + u32_t icmpindestunreachs; + u32_t icmpintimeexcds; + u32_t icmpinparmprobs; + u32_t icmpinsrcquenchs; + u32_t icmpinredirects; + u32_t icmpinechos; + u32_t icmpinechoreps; + u32_t icmpintimestamps; + u32_t icmpintimestampreps; + u32_t icmpinaddrmasks; + u32_t icmpinaddrmaskreps; + u32_t icmpoutmsgs; + u32_t icmpouterrors; + u32_t icmpoutdestunreachs; + u32_t icmpouttimeexcds; + u32_t icmpoutechos; /* can be incremented by user application ('ping') */ + u32_t icmpoutechoreps; +}; + +/** + * @ingroup netif_mib2 + * SNMP MIB2 interface stats + */ +struct stats_mib2_netif_ctrs { + /** The total number of octets received on the interface, including framing characters */ + u32_t ifinoctets; + /** The number of packets, delivered by this sub-layer to a higher (sub-)layer, which were + * not addressed to a multicast or broadcast address at this sub-layer */ + u32_t ifinucastpkts; + /** The number of packets, delivered by this sub-layer to a higher (sub-)layer, which were + * addressed to a multicast or broadcast address at this sub-layer */ + u32_t ifinnucastpkts; + /** The number of inbound packets which were chosen to be discarded even though no errors had + * been detected to prevent their being deliverable to a higher-layer protocol. One possible + * reason for discarding such a packet could be to free up buffer space */ + u32_t ifindiscards; + /** For packet-oriented interfaces, the number of inbound packets that contained errors + * preventing them from being deliverable to a higher-layer protocol. For character- + * oriented or fixed-length interfaces, the number of inbound transmission units that + * contained errors preventing them from being deliverable to a higher-layer protocol. */ + u32_t ifinerrors; + /** For packet-oriented interfaces, the number of packets received via the interface which + * were discarded because of an unknown or unsupported protocol. For character-oriented + * or fixed-length interfaces that support protocol multiplexing the number of transmission + * units received via the interface which were discarded because of an unknown or unsupported + * protocol. For any interface that does not support protocol multiplexing, this counter will + * always be 0 */ + u32_t ifinunknownprotos; + /** The total number of octets transmitted out of the interface, including framing characters. */ + u32_t ifoutoctets; + /** The total number of packets that higher-level protocols requested be transmitted, and + * which were not addressed to a multicast or broadcast address at this sub-layer, including + * those that were discarded or not sent. */ + u32_t ifoutucastpkts; + /** The total number of packets that higher-level protocols requested be transmitted, and which + * were addressed to a multicast or broadcast address at this sub-layer, including + * those that were discarded or not sent. */ + u32_t ifoutnucastpkts; + /** The number of outbound packets which were chosen to be discarded even though no errors had + * been detected to prevent their being transmitted. One possible reason for discarding + * such a packet could be to free up buffer space. */ + u32_t ifoutdiscards; + /** For packet-oriented interfaces, the number of outbound packets that could not be transmitted + * because of errors. For character-oriented or fixed-length interfaces, the number of outbound + * transmission units that could not be transmitted because of errors. */ + u32_t ifouterrors; +}; + +/** lwIP stats container */ +struct stats_ { +#if LINK_STATS + /** Link level */ + struct stats_proto link; +#endif +#if ETHARP_STATS + /** ARP */ + struct stats_proto etharp; +#endif +#if IPFRAG_STATS + /** Fragmentation */ + struct stats_proto ip_frag; +#endif +#if IP_STATS + /** IP */ + struct stats_proto ip; +#endif +#if ICMP_STATS + /** ICMP */ + struct stats_proto icmp; +#endif +#if IGMP_STATS + /** IGMP */ + struct stats_igmp igmp; +#endif +#if UDP_STATS + /** UDP */ + struct stats_proto udp; +#endif +#if TCP_STATS + /** TCP */ + struct stats_proto tcp; +#endif +#if MEM_STATS + /** Heap */ + struct stats_mem mem; +#endif +#if MEMP_STATS + /** Internal memory pools */ + struct stats_mem *memp[MEMP_MAX]; +#endif +#if SYS_STATS + /** System */ + struct stats_sys sys; +#endif +#if IP6_STATS + /** IPv6 */ + struct stats_proto ip6; +#endif +#if ICMP6_STATS + /** ICMP6 */ + struct stats_proto icmp6; +#endif +#if IP6_FRAG_STATS + /** IPv6 fragmentation */ + struct stats_proto ip6_frag; +#endif +#if MLD6_STATS + /** Multicast listener discovery */ + struct stats_igmp mld6; +#endif +#if ND6_STATS + /** Neighbor discovery */ + struct stats_proto nd6; +#endif +#if MIB2_STATS + /** SNMP MIB2 */ + struct stats_mib2 mib2; +#endif +}; + +/** Global variable containing lwIP internal statistics. Add this to your debugger's watchlist. */ +extern struct stats_ lwip_stats; + +/** Init statistics */ +void stats_init(void); + +#define STATS_INC(x) ++lwip_stats.x +#define STATS_DEC(x) --lwip_stats.x +#define STATS_INC_USED(x, y) do { lwip_stats.x.used += y; \ + if (lwip_stats.x.max < lwip_stats.x.used) { \ + lwip_stats.x.max = lwip_stats.x.used; \ + } \ + } while(0) +#define STATS_GET(x) lwip_stats.x +#else /* LWIP_STATS */ +#define stats_init() +#define STATS_INC(x) +#define STATS_DEC(x) +#define STATS_INC_USED(x) +#endif /* LWIP_STATS */ + +#if TCP_STATS +#define TCP_STATS_INC(x) STATS_INC(x) +#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP") +#else +#define TCP_STATS_INC(x) +#define TCP_STATS_DISPLAY() +#endif + +#if UDP_STATS +#define UDP_STATS_INC(x) STATS_INC(x) +#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP") +#else +#define UDP_STATS_INC(x) +#define UDP_STATS_DISPLAY() +#endif + +#if ICMP_STATS +#define ICMP_STATS_INC(x) STATS_INC(x) +#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP") +#else +#define ICMP_STATS_INC(x) +#define ICMP_STATS_DISPLAY() +#endif + +#if IGMP_STATS +#define IGMP_STATS_INC(x) STATS_INC(x) +#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp, "IGMP") +#else +#define IGMP_STATS_INC(x) +#define IGMP_STATS_DISPLAY() +#endif + +#if IP_STATS +#define IP_STATS_INC(x) STATS_INC(x) +#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP") +#else +#define IP_STATS_INC(x) +#define IP_STATS_DISPLAY() +#endif + +#if IPFRAG_STATS +#define IPFRAG_STATS_INC(x) STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG") +#else +#define IPFRAG_STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() +#endif + +#if ETHARP_STATS +#define ETHARP_STATS_INC(x) STATS_INC(x) +#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP") +#else +#define ETHARP_STATS_INC(x) +#define ETHARP_STATS_DISPLAY() +#endif + +#if LINK_STATS +#define LINK_STATS_INC(x) STATS_INC(x) +#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK") +#else +#define LINK_STATS_INC(x) +#define LINK_STATS_DISPLAY() +#endif + +#if MEM_STATS +#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y +#define MEM_STATS_INC(x) SYS_ARCH_INC(lwip_stats.mem.x, 1) +#define MEM_STATS_INC_USED(x, y) SYS_ARCH_INC(lwip_stats.mem.x, y) +#define MEM_STATS_DEC_USED(x, y) SYS_ARCH_DEC(lwip_stats.mem.x, y) +#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP") +#else +#define MEM_STATS_AVAIL(x, y) +#define MEM_STATS_INC(x) +#define MEM_STATS_INC_USED(x, y) +#define MEM_STATS_DEC_USED(x, y) +#define MEM_STATS_DISPLAY() +#endif + + #if MEMP_STATS +#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i]->x) +#define MEMP_STATS_DISPLAY(i) stats_display_memp(lwip_stats.memp[i], i) +#define MEMP_STATS_GET(x, i) STATS_GET(memp[i]->x) + #else +#define MEMP_STATS_DEC(x, i) +#define MEMP_STATS_DISPLAY(i) +#define MEMP_STATS_GET(x, i) 0 +#endif + +#if SYS_STATS +#define SYS_STATS_INC(x) STATS_INC(sys.x) +#define SYS_STATS_DEC(x) STATS_DEC(sys.x) +#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1) +#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys) +#else +#define SYS_STATS_INC(x) +#define SYS_STATS_DEC(x) +#define SYS_STATS_INC_USED(x) +#define SYS_STATS_DISPLAY() +#endif + +#if IP6_STATS +#define IP6_STATS_INC(x) STATS_INC(x) +#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6") +#else +#define IP6_STATS_INC(x) +#define IP6_STATS_DISPLAY() +#endif + +#if ICMP6_STATS +#define ICMP6_STATS_INC(x) STATS_INC(x) +#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6") +#else +#define ICMP6_STATS_INC(x) +#define ICMP6_STATS_DISPLAY() +#endif + +#if IP6_FRAG_STATS +#define IP6_FRAG_STATS_INC(x) STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG") +#else +#define IP6_FRAG_STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() +#endif + +#if MLD6_STATS +#define MLD6_STATS_INC(x) STATS_INC(x) +#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1") +#else +#define MLD6_STATS_INC(x) +#define MLD6_STATS_DISPLAY() +#endif + +#if ND6_STATS +#define ND6_STATS_INC(x) STATS_INC(x) +#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND") +#else +#define ND6_STATS_INC(x) +#define ND6_STATS_DISPLAY() +#endif + +#if MIB2_STATS +#define MIB2_STATS_INC(x) STATS_INC(x) +#else +#define MIB2_STATS_INC(x) +#endif + +/* Display of statistics */ +#if LWIP_STATS_DISPLAY +void stats_display(void); +void stats_display_proto(struct stats_proto *proto, const char *name); +void stats_display_igmp(struct stats_igmp *igmp, const char *name); +void stats_display_mem(struct stats_mem *mem, const char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); +#else /* LWIP_STATS_DISPLAY */ +#define stats_display() +#define stats_display_proto(proto, name) +#define stats_display_igmp(igmp, name) +#define stats_display_mem(mem, name) +#define stats_display_memp(mem, index) +#define stats_display_sys(sys) +#endif /* LWIP_STATS_DISPLAY */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_STATS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/sys.h b/sdk/Src/Network/lwip2.0.3/include/lwip/sys.h new file mode 100644 index 0000000..98c38d7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/sys.h @@ -0,0 +1,455 @@ +/** + * @file + * OS abstraction layer + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +#ifndef LWIP_HDR_SYS_H +#define LWIP_HDR_SYS_H + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if NO_SYS + +/* For a totally minimal and standalone system, we provide null + definitions of the sys_ functions. */ +typedef u8_t sys_sem_t; +typedef u8_t sys_mutex_t; +typedef u8_t sys_mbox_t; + +#define sys_sem_new(s, c) ERR_OK +#define sys_sem_signal(s) +#define sys_sem_wait(s) +#define sys_arch_sem_wait(s,t) +#define sys_sem_free(s) +#define sys_sem_valid(s) 0 +#define sys_sem_valid_val(s) 0 +#define sys_sem_set_invalid(s) +#define sys_sem_set_invalid_val(s) +#define sys_mutex_new(mu) ERR_OK +#define sys_mutex_lock(mu) +#define sys_mutex_unlock(mu) +#define sys_mutex_free(mu) +#define sys_mutex_valid(mu) 0 +#define sys_mutex_set_invalid(mu) +#define sys_mbox_new(m, s) ERR_OK +#define sys_mbox_fetch(m,d) +#define sys_mbox_tryfetch(m,d) +#define sys_mbox_post(m,d) +#define sys_mbox_trypost(m,d) +#define sys_mbox_free(m) +#define sys_mbox_valid(m) +#define sys_mbox_valid_val(m) +#define sys_mbox_set_invalid(m) +#define sys_mbox_set_invalid_val(m) + +#define sys_thread_new(n,t,a,s,p) + +#define sys_msleep(t) + +#else /* NO_SYS */ + +/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */ +#define SYS_ARCH_TIMEOUT 0xffffffffUL + +/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate. + * For now we use the same magic value, but we allow this to change in future. + */ +#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT + +#include "lwip/err.h" +#include "arch/sys_arch.h" + +/** Function prototype for thread functions */ +typedef void (*lwip_thread_fn)(void *arg); + +/* Function prototypes for functions to be implemented by platform ports + (in sys_arch.c) */ + +/* Mutex functions: */ + +/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores + should be used instead */ +#ifndef LWIP_COMPAT_MUTEX +#define LWIP_COMPAT_MUTEX 0 +#endif + +#if LWIP_COMPAT_MUTEX +/* for old ports that don't have mutexes: define them to binary semaphores */ +#define sys_mutex_t sys_sem_t +#define sys_mutex_new(mutex) sys_sem_new(mutex, 1) +#define sys_mutex_lock(mutex) sys_sem_wait(mutex) +#define sys_mutex_unlock(mutex) sys_sem_signal(mutex) +#define sys_mutex_free(mutex) sys_sem_free(mutex) +#define sys_mutex_valid(mutex) sys_sem_valid(mutex) +#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex) + +#else /* LWIP_COMPAT_MUTEX */ + +/** + * @ingroup sys_mutex + * Create a new mutex. + * Note that mutexes are expected to not be taken recursively by the lwIP code, + * so both implementation types (recursive or non-recursive) should work. + * @param mutex pointer to the mutex to create + * @return ERR_OK if successful, another err_t otherwise + */ +err_t sys_mutex_new(sys_mutex_t *mutex); +/** + * @ingroup sys_mutex + * Lock a mutex + * @param mutex the mutex to lock + */ +void sys_mutex_lock(sys_mutex_t *mutex); +/** + * @ingroup sys_mutex + * Unlock a mutex + * @param mutex the mutex to unlock + */ +void sys_mutex_unlock(sys_mutex_t *mutex); +/** + * @ingroup sys_mutex + * Delete a semaphore + * @param mutex the mutex to delete + */ +void sys_mutex_free(sys_mutex_t *mutex); +#ifndef sys_mutex_valid +/** + * @ingroup sys_mutex + * Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid + */ +int sys_mutex_valid(sys_mutex_t *mutex); +#endif +#ifndef sys_mutex_set_invalid +/** + * @ingroup sys_mutex + * Set a mutex invalid so that sys_mutex_valid returns 0 + */ +void sys_mutex_set_invalid(sys_mutex_t *mutex); +#endif +#endif /* LWIP_COMPAT_MUTEX */ + +/* Semaphore functions: */ + +/** + * @ingroup sys_sem + * Create a new semaphore + * @param sem pointer to the semaphore to create + * @param count initial count of the semaphore + * @return ERR_OK if successful, another err_t otherwise + */ +err_t sys_sem_new(sys_sem_t *sem, u8_t count); +/** + * @ingroup sys_sem + * Signals a semaphore + * @param sem the semaphore to signal + */ +void sys_sem_signal(sys_sem_t *sem); +/** + * @ingroup sys_sem + * Wait for a semaphore for the specified timeout + * @param sem the semaphore to wait for + * @param timeout timeout in milliseconds to wait (0 = wait forever) + * @return time (in milliseconds) waited for the semaphore + * or SYS_ARCH_TIMEOUT on timeout + */ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout); +/** + * @ingroup sys_sem + * Delete a semaphore + * @param sem semaphore to delete + */ +void sys_sem_free(sys_sem_t *sem); +/** Wait for a semaphore - forever/no timeout */ +#define sys_sem_wait(sem) sys_arch_sem_wait(sem, 0) +#ifndef sys_sem_valid +/** + * @ingroup sys_sem + * Check if a semaphore is valid/allocated: return 1 for valid, 0 for invalid + */ +int sys_sem_valid(sys_sem_t *sem); +#endif +#ifndef sys_sem_set_invalid +/** + * @ingroup sys_sem + * Set a semaphore invalid so that sys_sem_valid returns 0 + */ +void sys_sem_set_invalid(sys_sem_t *sem); +#endif +#ifndef sys_sem_valid_val +/** + * Same as sys_sem_valid() but taking a value, not a pointer + */ +#define sys_sem_valid_val(sem) sys_sem_valid(&(sem)) +#endif +#ifndef sys_sem_set_invalid_val +/** + * Same as sys_sem_set_invalid() but taking a value, not a pointer + */ +#define sys_sem_set_invalid_val(sem) sys_sem_set_invalid(&(sem)) +#endif + +#ifndef sys_msleep +/** + * @ingroup sys_misc + * Sleep for specified number of ms + */ +void sys_msleep(u32_t ms); /* only has a (close to) 1 ms resolution. */ +#endif + +/* Mailbox functions. */ + +/** + * @ingroup sys_mbox + * Create a new mbox of specified size + * @param mbox pointer to the mbox to create + * @param size (minimum) number of messages in this mbox + * @return ERR_OK if successful, another err_t otherwise + */ +err_t sys_mbox_new(sys_mbox_t *mbox, int size); +/** + * @ingroup sys_mbox + * Post a message to an mbox - may not fail + * -> blocks if full, only used from tasks not from ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) + */ +void sys_mbox_post(sys_mbox_t *mbox, void *msg); +/** + * @ingroup sys_mbox + * Try to post a message to an mbox - may fail if full or ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) + */ +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg); +/** + * @ingroup sys_mbox + * Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @param timeout maximum time (in milliseconds) to wait for a message (0 = wait forever) + * @return time (in milliseconds) waited for a message, may be 0 if not waited + or SYS_ARCH_TIMEOUT on timeout + * The returned time has to be accurate to prevent timer jitter! + */ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout); +/* Allow port to override with a macro, e.g. special timeout for sys_arch_mbox_fetch() */ +#ifndef sys_arch_mbox_tryfetch +/** + * @ingroup sys_mbox + * Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @return 0 (milliseconds) if a message has been received + * or SYS_MBOX_EMPTY if the mailbox is empty + */ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg); +#endif +/** + * For now, we map straight to sys_arch implementation. + */ +#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg) +/** + * @ingroup sys_mbox + * Delete an mbox + * @param mbox mbox to delete + */ +void sys_mbox_free(sys_mbox_t *mbox); +#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0) +#ifndef sys_mbox_valid +/** + * @ingroup sys_mbox + * Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid + */ +int sys_mbox_valid(sys_mbox_t *mbox); +#endif +#ifndef sys_mbox_set_invalid +/** + * @ingroup sys_mbox + * Set an mbox invalid so that sys_mbox_valid returns 0 + */ +void sys_mbox_set_invalid(sys_mbox_t *mbox); +#endif +#ifndef sys_mbox_valid_val +/** + * Same as sys_mbox_valid() but taking a value, not a pointer + */ +#define sys_mbox_valid_val(mbox) sys_mbox_valid(&(mbox)) +#endif +#ifndef sys_mbox_set_invalid_val +/** + * Same as sys_mbox_set_invalid() but taking a value, not a pointer + */ +#define sys_mbox_set_invalid_val(mbox) sys_mbox_set_invalid(&(mbox)) +#endif + + +/** + * @ingroup sys_misc + * The only thread function: + * Creates a new thread + * ATTENTION: although this function returns a value, it MUST NOT FAIL (ports have to assert this!) + * @param name human-readable name for the thread (used for debugging purposes) + * @param thread thread-function + * @param arg parameter passed to 'thread' + * @param stacksize stack size in bytes for the new thread (may be ignored by ports) + * @param prio priority of the new thread (may be ignored by ports) */ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio); + +#endif /* NO_SYS */ + +/* sys_init() must be called before anything else. */ +void sys_init(void); + +#ifndef sys_jiffies +/** + * Ticks/jiffies since power up. + */ +u32_t sys_jiffies(void); +#endif + +/** + * @ingroup sys_time + * Returns the current time in milliseconds, + * may be the same as sys_jiffies or at least based on it. + */ +u32_t sys_now(void); + +/* Critical Region Protection */ +/* These functions must be implemented in the sys_arch.c file. + In some implementations they can provide a more light-weight protection + mechanism than using semaphores. Otherwise semaphores can be used for + implementation */ +#ifndef SYS_ARCH_PROTECT +/** SYS_LIGHTWEIGHT_PROT + * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection + * for certain critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#if SYS_LIGHTWEIGHT_PROT + +/** + * @ingroup sys_prot + * SYS_ARCH_DECL_PROTECT + * declare a protection variable. This macro will default to defining a variable of + * type sys_prot_t. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h. + */ +#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev +/** + * @ingroup sys_prot + * SYS_ARCH_PROTECT + * Perform a "fast" protect. This could be implemented by + * disabling interrupts for an embedded system or by using a semaphore or + * mutex. The implementation should allow calling SYS_ARCH_PROTECT when + * already protected. The old protection level is returned in the variable + * "lev". This macro will default to calling the sys_arch_protect() function + * which should be implemented in sys_arch.c. If a particular port needs a + * different implementation, then this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect() +/** + * @ingroup sys_prot + * SYS_ARCH_UNPROTECT + * Perform a "fast" set of the protection level to "lev". This could be + * implemented by setting the interrupt level to "lev" within the MACRO or by + * using a semaphore or mutex. This macro will default to calling the + * sys_arch_unprotect() function which should be implemented in + * sys_arch.c. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev) +sys_prot_t sys_arch_protect(void); +void sys_arch_unprotect(sys_prot_t pval); + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#endif /* SYS_LIGHTWEIGHT_PROT */ + +#endif /* SYS_ARCH_PROTECT */ + +/* + * Macros to set/get and increase/decrease variables in a thread-safe way. + * Use these for accessing variable that are used from more than one thread. + */ + +#ifndef SYS_ARCH_INC +#define SYS_ARCH_INC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var += val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_INC */ + +#ifndef SYS_ARCH_DEC +#define SYS_ARCH_DEC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var -= val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_DEC */ + +#ifndef SYS_ARCH_GET +#define SYS_ARCH_GET(var, ret) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + ret = var; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_GET */ + +#ifndef SYS_ARCH_SET +#define SYS_ARCH_SET(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var = val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_SET */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_SYS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/tcp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/tcp.h new file mode 100644 index 0000000..30d2146 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/tcp.h @@ -0,0 +1,440 @@ +/** + * @file + * TCP API (to be used from TCPIP thread)\n + * See also @ref tcp_raw + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_TCP_H +#define LWIP_HDR_TCP_H + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcp_pcb; + +/** Function prototype for tcp accept callback functions. Called when a new + * connection can be accepted on a listening pcb. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param newpcb The new connection pcb + * @param err An error code if there has been an error accepting. + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err); + +/** Function prototype for tcp receive callback functions. Called when data has + * been received. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which received data + * @param p The received data (or NULL when the connection has been closed!) + * @param err An error code if there has been an error receiving + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err); + +/** Function prototype for tcp sent callback functions. Called when sent data has + * been acknowledged by the remote side. Use it to free corresponding resources. + * This also means that the pcb has now space available to send new data. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb for which data has been acknowledged + * @param len The amount of bytes acknowledged + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb, + u16_t len); + +/** Function prototype for tcp poll callback functions. Called periodically as + * specified by @see tcp_poll. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb tcp pcb + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb); + +/** Function prototype for tcp error callback functions. Called when the pcb + * receives a RST or is unexpectedly closed for any other reason. + * + * @note The corresponding pcb is already freed when this callback is called! + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param err Error code to indicate why the pcb has been closed + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ +typedef void (*tcp_err_fn)(void *arg, err_t err); + +/** Function prototype for tcp connected callback functions. Called when a pcb + * is connected to the remote side after initiating a connection attempt by + * calling tcp_connect(). + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which is connected + * @param err An unused error code, always ERR_OK currently ;-) @todo! + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + * + * @note When a connection attempt fails, the error callback is currently called! + */ +typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err); + +#if LWIP_WND_SCALE +#define RCV_WND_SCALE(pcb, wnd) (((wnd) >> (pcb)->rcv_scale)) +#define SND_WND_SCALE(pcb, wnd) (((wnd) << (pcb)->snd_scale)) +#define TCPWND16(x) ((u16_t)LWIP_MIN((x), 0xFFFF)) +#define TCP_WND_MAX(pcb) ((tcpwnd_size_t)(((pcb)->flags & TF_WND_SCALE) ? TCP_WND : TCPWND16(TCP_WND))) +typedef u32_t tcpwnd_size_t; +#else +#define RCV_WND_SCALE(pcb, wnd) (wnd) +#define SND_WND_SCALE(pcb, wnd) (wnd) +#define TCPWND16(x) (x) +#define TCP_WND_MAX(pcb) TCP_WND +typedef u16_t tcpwnd_size_t; +#endif + +#if LWIP_WND_SCALE || TCP_LISTEN_BACKLOG || LWIP_TCP_TIMESTAMPS +typedef u16_t tcpflags_t; +#else +typedef u8_t tcpflags_t; +#endif + +enum tcp_state { + CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 +}; + +/** + * members common to struct tcp_pcb and struct tcp_listen_pcb + */ +#define TCP_PCB_COMMON(type) \ + type *next; /* for the linked list */ \ + void *callback_arg; \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + /* ports are in host byte order */ \ + u16_t local_port + + +/** the TCP protocol control block for listening pcbs */ +struct tcp_pcb_listen { +/** Common members of all PCB types */ + IP_PCB; +/** Protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb_listen); + +#if LWIP_CALLBACK_API + /* Function to call when a listener has been connected. */ + tcp_accept_fn accept; +#endif /* LWIP_CALLBACK_API */ + +#if TCP_LISTEN_BACKLOG + u8_t backlog; + u8_t accepts_pending; +#endif /* TCP_LISTEN_BACKLOG */ +}; + + +/** the TCP protocol control block */ +struct tcp_pcb { +/** common PCB members */ + IP_PCB; +/** protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb); + + /* ports are in host byte order */ + u16_t remote_port; + + tcpflags_t flags; +#define TF_ACK_DELAY 0x01U /* Delayed ACK. */ +#define TF_ACK_NOW 0x02U /* Immediate ACK. */ +#define TF_INFR 0x04U /* In fast recovery. */ +#define TF_CLOSEPEND 0x08U /* If this is set, tcp_close failed to enqueue the FIN (retried in tcp_tmr) */ +#define TF_RXCLOSED 0x10U /* rx closed by tcp_shutdown */ +#define TF_FIN 0x20U /* Connection was closed locally (FIN segment enqueued). */ +#define TF_NODELAY 0x40U /* Disable Nagle algorithm */ +#define TF_NAGLEMEMERR 0x80U /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ +#if LWIP_WND_SCALE +#define TF_WND_SCALE 0x0100U /* Window Scale option enabled */ +#endif +#if TCP_LISTEN_BACKLOG +#define TF_BACKLOGPEND 0x0200U /* If this is set, a connection pcb has increased the backlog on its listener */ +#endif +#if LWIP_TCP_TIMESTAMPS +#define TF_TIMESTAMP 0x0400U /* Timestamp option enabled */ +#endif + + /* the rest of the fields are in host byte order + as we have to do some math with them */ + + /* Timers */ + u8_t polltmr, pollinterval; + u8_t last_timer; + u32_t tmr; + + /* receiver variables */ + u32_t rcv_nxt; /* next seqno expected */ + tcpwnd_size_t rcv_wnd; /* receiver window available */ + tcpwnd_size_t rcv_ann_wnd; /* receiver window to announce */ + u32_t rcv_ann_right_edge; /* announced right edge of window */ + + /* Retransmission timer. */ + s16_t rtime; + + u16_t mss; /* maximum segment size */ + + /* RTT (round trip time) estimation variables */ + u32_t rttest; /* RTT estimate in 500ms ticks */ + u32_t rtseq; /* sequence number being timed */ + s16_t sa, sv; /* @todo document this */ + + s16_t rto; /* retransmission time-out */ + u8_t nrtx; /* number of retransmissions */ + + /* fast retransmit/recovery */ + u8_t dupacks; + u32_t lastack; /* Highest acknowledged seqno. */ + + /* congestion avoidance/control variables */ + tcpwnd_size_t cwnd; + tcpwnd_size_t ssthresh; + + /* sender variables */ + u32_t snd_nxt; /* next new seqno to be sent */ + u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last + window update. */ + u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ + tcpwnd_size_t snd_wnd; /* sender window */ + tcpwnd_size_t snd_wnd_max; /* the maximum sender window announced by the remote host */ + + tcpwnd_size_t snd_buf; /* Available buffer space for sending (in bytes). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3) + u16_t snd_queuelen; /* Number of pbufs currently in the send buffer. */ + +#if TCP_OVERSIZE + /* Extra bytes available at the end of the last pbuf in unsent. */ + u16_t unsent_oversize; +#endif /* TCP_OVERSIZE */ + + /* These are ordered by sequence number: */ + struct tcp_seg *unsent; /* Unsent (queued) segments. */ + struct tcp_seg *unacked; /* Sent but unacknowledged segments. */ +#if TCP_QUEUE_OOSEQ + struct tcp_seg *ooseq; /* Received out of sequence segments. */ +#endif /* TCP_QUEUE_OOSEQ */ + + struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */ + +#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG + struct tcp_pcb_listen* listener; +#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */ + +#if LWIP_CALLBACK_API + /* Function to be called when more send buffer space is available. */ + tcp_sent_fn sent; + /* Function to be called when (in-sequence) data has arrived. */ + tcp_recv_fn recv; + /* Function to be called when a connection has been set up. */ + tcp_connected_fn connected; + /* Function which is called periodically. */ + tcp_poll_fn poll; + /* Function to be called whenever a fatal error occurs. */ + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + +#if LWIP_TCP_TIMESTAMPS + u32_t ts_lastacksent; + u32_t ts_recent; +#endif /* LWIP_TCP_TIMESTAMPS */ + + /* idle time before KEEPALIVE is sent */ + u32_t keep_idle; +#if LWIP_TCP_KEEPALIVE + u32_t keep_intvl; + u32_t keep_cnt; +#endif /* LWIP_TCP_KEEPALIVE */ + + /* Persist timer counter */ + u8_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + + /* KEEPALIVE counter */ + u8_t keep_cnt_sent; + +#if LWIP_WND_SCALE + u8_t snd_scale; + u8_t rcv_scale; +#endif +}; + +#if LWIP_EVENT_API + +enum lwip_event { + LWIP_EVENT_ACCEPT, + LWIP_EVENT_SENT, + LWIP_EVENT_RECV, + LWIP_EVENT_CONNECTED, + LWIP_EVENT_POLL, + LWIP_EVENT_ERR +}; + +err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb, + enum lwip_event, + struct pbuf *p, + u16_t size, + err_t err); + +#endif /* LWIP_EVENT_API */ + +/* Application program's interface: */ +struct tcp_pcb * tcp_new (void); +struct tcp_pcb * tcp_new_ip_type (u8_t type); + +void tcp_arg (struct tcp_pcb *pcb, void *arg); +#if LWIP_CALLBACK_API +void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv); +void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent); +void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err); +void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept); +#endif /* LWIP_CALLBACK_API */ +void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval); + +#define SNDBUF_SHARE 1 //share tx sndbuf + +#if LWIP_TCP_TIMESTAMPS +#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss) +#else /* LWIP_TCP_TIMESTAMPS */ +#define tcp_mss(pcb) ((pcb)->mss) +#endif /* LWIP_TCP_TIMESTAMPS */ +#if SNDBUF_SHARE +extern s32 sndbuf_len; +#define tcp_sndbuf(pcb) (TCPWND16(sndbuf_len)) +#else +#define tcp_sndbuf(pcb) (TCPWND16((pcb)->snd_buf)) +#endif +#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen) +/** @ingroup tcp_raw */ +#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY) +/** @ingroup tcp_raw */ +#define tcp_nagle_enable(pcb) ((pcb)->flags = (tcpflags_t)((pcb)->flags & ~TF_NODELAY)) +/** @ingroup tcp_raw */ +#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0) + +#if TCP_LISTEN_BACKLOG +#define tcp_backlog_set(pcb, new_backlog) do { \ + LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", (pcb)->state == LISTEN); \ + ((struct tcp_pcb_listen *)(pcb))->backlog = ((new_backlog) ? (new_backlog) : 1); } while(0) +void tcp_backlog_delayed(struct tcp_pcb* pcb); +void tcp_backlog_accepted(struct tcp_pcb* pcb); +#else /* TCP_LISTEN_BACKLOG */ +#define tcp_backlog_set(pcb, new_backlog) +#define tcp_backlog_delayed(pcb) +#define tcp_backlog_accepted(pcb) +#endif /* TCP_LISTEN_BACKLOG */ +#define tcp_accepted(pcb) /* compatibility define, not needed any more */ + +void tcp_recved (struct tcp_pcb *pcb, u16_t len); +err_t tcp_bind (struct tcp_pcb *pcb, const ip_addr_t *ipaddr, + u16_t port); +err_t tcp_connect (struct tcp_pcb *pcb, const ip_addr_t *ipaddr, + u16_t port, tcp_connected_fn connected); + +struct tcp_pcb * tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err); +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +/** @ingroup tcp_raw */ +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abort (struct tcp_pcb *pcb); +err_t tcp_close (struct tcp_pcb *pcb); +err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx); + +/* Flags for "apiflags" parameter in tcp_write */ +#define TCP_WRITE_FLAG_COPY 0x01 +#define TCP_WRITE_FLAG_MORE 0x02 + +err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags); + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +err_t tcp_output (struct tcp_pcb *pcb); + + +const char* tcp_debug_state_str(enum tcp_state s); + +/* for compatibility with older implementation */ +#define tcp_new_ip6() tcp_new_ip_type(IPADDR_TYPE_V6) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* LWIP_HDR_TCP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/tcpip.h b/sdk/Src/Network/lwip2.0.3/include/lwip/tcpip.h new file mode 100644 index 0000000..6bb9143 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/tcpip.h @@ -0,0 +1,108 @@ +/** + * @file + * Functions to sync with TCPIP thread + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_TCPIP_H +#define LWIP_HDR_TCPIP_H + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/err.h" +#include "lwip/timeouts.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +extern sys_mutex_t lock_tcpip_core; +/** Lock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */ +#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core) +/** Unlock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */ +#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core) +#else /* LWIP_TCPIP_CORE_LOCKING */ +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +struct pbuf; +struct netif; + +/** Function prototype for the init_done function passed to tcpip_init */ +typedef void (*tcpip_init_done_fn)(void *arg); +/** Function prototype for functions passed to tcpip_callback() */ +typedef void (*tcpip_callback_fn)(void *ctx); + +/* Forward declarations */ +struct tcpip_callback_msg; + +void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg); + +err_t tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn); +err_t tcpip_input(struct pbuf *p, struct netif *inp); +#if TLS_CONFIG_AP_OPT_FWD +err_t tcpip_output(struct pbuf *p, struct netif *inp, ip_addr_t ipaddr); +#endif +err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block); +/** + * @ingroup lwip_os + * @see tcpip_callback_with_block + */ +#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) + +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx); +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg); +err_t tcpip_trycallback(struct tcpip_callback_msg* msg); + +/* free pbufs or heap memory from another context without blocking */ +err_t pbuf_free_callback(struct pbuf *p); +err_t mem_free_callback(void *m); + +#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +err_t tcpip_untimeout(sys_timeout_handler h, void *arg); +#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */ + +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ + +#endif /* LWIP_HDR_TCPIP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/timeouts.h b/sdk/Src/Network/lwip2.0.3/include/lwip/timeouts.h new file mode 100644 index 0000000..afa4b07 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/timeouts.h @@ -0,0 +1,125 @@ +/** + * @file + * Timer implementations + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ +#ifndef LWIP_HDR_TIMEOUTS_H +#define LWIP_HDR_TIMEOUTS_H + +#include "lwip/opt.h" +#include "lwip/err.h" +#if !NO_SYS +#include "lwip/sys.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LWIP_DEBUG_TIMERNAMES +#ifdef LWIP_DEBUG +#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG +#else /* LWIP_DEBUG */ +#define LWIP_DEBUG_TIMERNAMES 0 +#endif /* LWIP_DEBUG*/ +#endif + +/** Function prototype for a stack-internal timer function that has to be + * called at a defined interval */ +typedef void (* lwip_cyclic_timer_handler)(void); + +/** This struct contains information about a stack-internal timer function + that has to be called at a defined interval */ +struct lwip_cyclic_timer { + u32_t interval_ms; + lwip_cyclic_timer_handler handler; +#if LWIP_DEBUG_TIMERNAMES + const char* handler_name; +#endif /* LWIP_DEBUG_TIMERNAMES */ +}; + +/** This array contains all stack-internal cyclic timers. To get the number of + * timers, use LWIP_ARRAYSIZE() */ +extern const struct lwip_cyclic_timer lwip_cyclic_timers[]; + +#if LWIP_TIMERS + +/** Function prototype for a timeout callback function. Register such a function + * using sys_timeout(). + * + * @param arg Additional argument to pass to the function - set up by sys_timeout() + */ +typedef void (* sys_timeout_handler)(void *arg); + +struct sys_timeo { + struct sys_timeo *next; + u32_t time; + sys_timeout_handler h; + void *arg; +#if LWIP_DEBUG_TIMERNAMES + const char* handler_name; +#endif /* LWIP_DEBUG_TIMERNAMES */ +}; + +void sys_timeouts_init(void); + +#if LWIP_DEBUG_TIMERNAMES +void sys_timeout_debug_p(u8 timeo_assigned, u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name); +#define sys_timeout_p(timeo_assigned, msecs, handler, arg) sys_timeout_debug_p(timeo_assigned, msecs, handler, arg, #handler) +#define sys_timeout(msecs, handler, arg) sys_timeout_p(0, msecs, handler, arg) +#else /* LWIP_DEBUG_TIMERNAMES */ +void sys_timeout_p(u8 timeo_assigned, u32_t msecs, sys_timeout_handler handler, void *arg); +#define sys_timeout(msecs, handler, arg) sys_timeout_p(0, msecs, handler, arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ + +void sys_untimeout_p(u8 timeo_assigned, sys_timeout_handler handler, void *arg); +#define sys_untimeout(handler, arg) sys_untimeout_p(0, handler, arg) +#if NO_SYS +void sys_check_timeouts(void); +u32_t sys_timeouts_sleeptime_p(u8 timeo_assigned); +#define sys_timeouts_sleeptime() sys_timeouts_sleeptime_p(0) +#else /* NO_SYS */ +void sys_timeouts_mbox_fetch_p(u8 timeo_assigned, sys_mbox_t *mbox, void **msg); +#define sys_timeouts_mbox_fetch(mbox, msg) sys_timeouts_mbox_fetch_p(0, mbox, msg) +#endif /* NO_SYS */ + + +#endif /* LWIP_TIMERS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_TIMEOUTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/timers.h b/sdk/Src/Network/lwip2.0.3/include/lwip/timers.h new file mode 100644 index 0000000..28a99b7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/timers.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ +#ifndef LWIP_HDR_TIMERS_H +#define LWIP_HDR_TIMERS_H + +#include "lwip/opt.h" + +/* Timers are not supported when NO_SYS==1 and NO_SYS_NO_TIMERS==1 */ +#undef LWIP_TIMERS +#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) + +#if LWIP_TIMERS + +#include "lwip/err.h" +#if !NO_SYS +#include "lwip/sys.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LWIP_DEBUG_TIMERNAMES +#ifdef LWIP_DEBUG +#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG +#else /* LWIP_DEBUG */ +#define LWIP_DEBUG_TIMERNAMES 0 +#endif /* LWIP_DEBUG*/ +#endif + +/** Function prototype for a timeout callback function. Register such a function + * using sys_timeout(). + * + * @param arg Additional argument to pass to the function - set up by sys_timeout() + */ +typedef void (* sys_timeout_handler)(void *arg); + +struct sys_timeo { + struct sys_timeo *next; + u32_t time; + sys_timeout_handler h; + void *arg; +#if LWIP_DEBUG_TIMERNAMES + const char* handler_name; +#endif /* LWIP_DEBUG_TIMERNAMES */ +}; + +void sys_timeouts_init(void); + +#if LWIP_DEBUG_TIMERNAMES +void sys_timeout_debug_p(u8 timeo_assigned, u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name); +#define sys_timeout_p(timeo_assigned, msecs, handler, arg) sys_timeout_debug_p(timeo_assigned, msecs, handler, arg, #handler) +#define sys_timeout(msecs, handler, arg) sys_timeout_p(0, msecs, handler, arg) +#else /* LWIP_DEBUG_TIMERNAMES */ +void sys_timeout_p(u8 timeo_assigned, u32_t msecs, sys_timeout_handler handler, void *arg); +#define sys_timeout(msecs, handler, arg) sys_timeout_p(0, msecs, handler, arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ + +void sys_untimeout_p(u8 timeo_assigned, sys_timeout_handler handler, void *arg); +#define sys_untimeout(handler, arg) sys_untimeout_p(0, handler, arg) +#if NO_SYS +void sys_check_timeouts(void); +void sys_restart_timeouts(void); +u32_t sys_timeouts_sleeptime(void); +#else /* NO_SYS */ +void sys_timeouts_mbox_fetch_p(u8 timeo_assigned, sys_mbox_t mbox, void **msg); +#define sys_timeouts_mbox_fetch(mbox, msg) sys_timeouts_mbox_fetch_p(0, mbox, msg) +#endif /* NO_SYS */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TIMERS */ +#endif /* LWIP_HDR_TIMERS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/lwip/udp.h b/sdk/Src/Network/lwip2.0.3/include/lwip/udp.h new file mode 100644 index 0000000..19ffcb4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/lwip/udp.h @@ -0,0 +1,184 @@ +/** + * @file + * UDP API (to be used from TCPIP thread)\n + * See also @ref udp_raw + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_UDP_H +#define LWIP_HDR_UDP_H + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" +#include "lwip/ip6_addr.h" +#include "lwip/prot/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define UDP_FLAGS_NOCHKSUM 0x01U +#define UDP_FLAGS_UDPLITE 0x02U +#define UDP_FLAGS_CONNECTED 0x04U +#define UDP_FLAGS_MULTICAST_LOOP 0x08U + +struct udp_pcb; + +/** Function prototype for udp pcb receive callback functions + * addr and port are in same byte order as in the pcb + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * ATTENTION: Be aware that 'addr' might point into the pbuf 'p' so freeing this pbuf + * can make 'addr' invalid, too. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port); + +/** the UDP protocol control block */ +struct udp_pcb { +/** Common members of all PCB types */ + IP_PCB; + +/* Protocol specific PCB members */ + + struct udp_pcb *next; + + u8_t flags; + /** ports are in host byte order */ + u16_t local_port, remote_port; + +#if LWIP_MULTICAST_TX_OPTIONS + /** outgoing network interface for multicast packets */ + ip_addr_t multicast_ip; + /** TTL for outgoing multicast packets */ + u8_t mcast_ttl; +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + +#if LWIP_UDPLITE + /** used for UDP_LITE only */ + u16_t chksum_len_rx, chksum_len_tx; +#endif /* LWIP_UDPLITE */ + + /** receive callback function */ + udp_recv_fn recv; + /** user-supplied argument for the recv callback */ + void *recv_arg; +}; +/* udp_pcbs export for external reference (e.g. SNMP agent) */ +extern struct udp_pcb *udp_pcbs; + +/* The following functions is the application layer interface to the + UDP code. */ +struct udp_pcb * udp_new (void); +struct udp_pcb * udp_new_ip_type(u8_t type); +void udp_remove (struct udp_pcb *pcb); +err_t udp_bind (struct udp_pcb *pcb, const ip_addr_t *ipaddr, + u16_t port); +err_t udp_connect (struct udp_pcb *pcb, const ip_addr_t *ipaddr, + u16_t port); +void udp_disconnect (struct udp_pcb *pcb); +void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, + void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif); +err_t udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif, const ip_addr_t *src_ip); +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port); +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); +err_t udp_bind_multicast_netif(struct udp_pcb *pcb, ip_addr_t *ipaddr); + + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +err_t udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif, u8_t have_chksum, + u16_t chksum); +err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port, + u8_t have_chksum, u16_t chksum); +err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum); +err_t udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, + u8_t have_chksum, u16_t chksum, const ip_addr_t *src_ip); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +#define udp_flags(pcb) ((pcb)->flags) +#define udp_setflags(pcb, f) ((pcb)->flags = (f)) + +/* The following functions are the lower layer interface to UDP. */ +void udp_input (struct pbuf *p, struct netif *inp); + +void udp_init (void); + +/* for compatibility with older implementation */ +#define udp_new_ip6() udp_new_ip_type(IPADDR_TYPE_V6) + +#if LWIP_MULTICAST_TX_OPTIONS +#define udp_set_multicast_netif_addr(pcb, ip4addr) ip_addr_copy_from_ip4((pcb)->multicast_ip, *(ip4addr)) +#define udp_get_multicast_netif_addr(pcb) ip_2_ip4(&(pcb)->multicast_ip) +#define udp_set_multicast_ttl(pcb, value) do { (pcb)->mcast_ttl = value; } while(0) +#define udp_get_multicast_ttl(pcb) ((pcb)->mcast_ttl) +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr); +#else +#define udp_debug_print(udphdr) +#endif + +void udp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* LWIP_HDR_UDP_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/etharp.h b/sdk/Src/Network/lwip2.0.3/include/netif/etharp.h new file mode 100644 index 0000000..c00de04 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/etharp.h @@ -0,0 +1,3 @@ +/* ARP has been moved to core/ipv4, provide this #include for compatibility only */ +#include "lwip/etharp.h" +#include "netif/ethernet.h" diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ethernet.h b/sdk/Src/Network/lwip2.0.3/include/netif/ethernet.h new file mode 100644 index 0000000..7b23369 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ethernet.h @@ -0,0 +1,85 @@ +/** + * @file + * Ethernet input function - handles INCOMING ethernet level traffic + * To be used in most low-level netif implementations + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef LWIP_HDR_NETIF_ETHERNET_H +#define LWIP_HDR_NETIF_ETHERNET_H + +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/prot/ethernet.h" +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_ARP || LWIP_ETHERNET + +/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type) + * to a filter function that returns the correct netif when using multiple + * netifs on one hardware interface where the netif's low-level receive + * routine cannot decide for the correct netif (e.g. when mapping multiple + * IP addresses to one hardware interface). + */ +#ifndef LWIP_ARP_FILTER_NETIF +#define LWIP_ARP_FILTER_NETIF 0 +#endif + +err_t ethernet_input(struct pbuf *p, struct netif *netif); +err_t ethernet_output(struct netif* netif, struct pbuf* p, const struct eth_addr* src, const struct eth_addr* dst, u16_t eth_type); +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETH_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +struct tls_netif_status_event +{ + struct dl_list list; + tls_netif_status_event_fn status_callback; +}; + +#endif /* LWIP_ARP || LWIP_ETHERNET */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_NETIF_ETHERNET_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ethernetif.h b/sdk/Src/Network/lwip2.0.3/include/netif/ethernetif.h new file mode 100644 index 0000000..c188076 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ethernetif.h @@ -0,0 +1,58 @@ +/** + * + * \file + * + * \brief Ethernet Interface Skeleton. + * + * Copyright (c) 2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ + +#ifndef ETHERNETIF_H_INCLUDED +#define ETHERNETIF_H_INCLUDED + +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "netif/etharp.h" +//#include "tls_wireless.h" + +err_t ethernetif_init(struct netif *netif); + +int ethernetif_input(const u8 *bssid, u8 *buf, u32 buf_len); + +#endif /* ETHERNETIF_H_INCLUDED */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/lowpan6.h b/sdk/Src/Network/lwip2.0.3/include/netif/lowpan6.h new file mode 100644 index 0000000..db49e30 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/lowpan6.h @@ -0,0 +1,86 @@ +/** + * @file + * + * 6LowPAN output for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units. + */ + +/* + * Copyright (c) 2015 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_LOWPAN6_H +#define LWIP_HDR_LOWPAN6_H + +#include "netif/lowpan6_opts.h" + +#if LWIP_IPV6 && LWIP_6LOWPAN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** 1 second period */ +#define LOWPAN6_TMR_INTERVAL 1000 + +void lowpan6_tmr(void); + +err_t lowpan6_set_context(u8_t index, const ip6_addr_t * context); +err_t lowpan6_set_short_addr(u8_t addr_high, u8_t addr_low); + +#if LWIP_IPV4 +err_t lowpan4_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr); +#endif /* LWIP_IPV4 */ +err_t lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr); +err_t lowpan6_input(struct pbuf * p, struct netif *netif); +err_t lowpan6_if_init(struct netif *netif); + +/* pan_id in network byte order. */ +err_t lowpan6_set_pan_id(u16_t pan_id); + +#if !NO_SYS +err_t tcpip_6lowpan_input(struct pbuf *p, struct netif *inp); +#endif /* !NO_SYS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 && LWIP_6LOWPAN */ + +#endif /* LWIP_HDR_LOWPAN6_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/lowpan6_opts.h b/sdk/Src/Network/lwip2.0.3/include/netif/lowpan6_opts.h new file mode 100644 index 0000000..96d5780 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/lowpan6_opts.h @@ -0,0 +1,70 @@ +/** + * @file + * 6LowPAN options list + */ + +/* + * Copyright (c) 2015 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_LOWPAN6_OPTS_H +#define LWIP_HDR_LOWPAN6_OPTS_H + +#include "lwip/opt.h" + +#ifndef LWIP_6LOWPAN +#define LWIP_6LOWPAN 0 +#endif + +#ifndef LWIP_6LOWPAN_NUM_CONTEXTS +#define LWIP_6LOWPAN_NUM_CONTEXTS 10 +#endif + +#ifndef LWIP_6LOWPAN_INFER_SHORT_ADDRESS +#define LWIP_6LOWPAN_INFER_SHORT_ADDRESS 1 +#endif + +#ifndef LWIP_6LOWPAN_IPHC +#define LWIP_6LOWPAN_IPHC 1 +#endif + +#ifndef LWIP_6LOWPAN_HW_CRC +#define LWIP_6LOWPAN_HW_CRC 1 +#endif + +#ifndef LOWPAN6_DEBUG +#define LOWPAN6_DEBUG LWIP_DBG_OFF +#endif + +#endif /* LWIP_HDR_LOWPAN6_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ccp.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ccp.h new file mode 100644 index 0000000..14dd659 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ccp.h @@ -0,0 +1,156 @@ +/* + * ccp.h - Definitions for PPP Compression Control Protocol. + * + * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ccp.h,v 1.12 2004/11/04 10:02:26 paulus Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && CCP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef CCP_H +#define CCP_H + +/* + * CCP codes. + */ + +#define CCP_CONFREQ 1 +#define CCP_CONFACK 2 +#define CCP_TERMREQ 5 +#define CCP_TERMACK 6 +#define CCP_RESETREQ 14 +#define CCP_RESETACK 15 + +/* + * Max # bytes for a CCP option + */ + +#define CCP_MAX_OPTION_LENGTH 32 + +/* + * Parts of a CCP packet. + */ + +#define CCP_CODE(dp) ((dp)[0]) +#define CCP_ID(dp) ((dp)[1]) +#define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3]) +#define CCP_HDRLEN 4 + +#define CCP_OPT_CODE(dp) ((dp)[0]) +#define CCP_OPT_LENGTH(dp) ((dp)[1]) +#define CCP_OPT_MINLEN 2 + +#if BSDCOMPRESS_SUPPORT +/* + * Definitions for BSD-Compress. + */ + +#define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */ +#define CILEN_BSD_COMPRESS 3 /* length of config. option */ + +/* Macros for handling the 3rd byte of the BSD-Compress config option. */ +#define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */ +#define BSD_VERSION(x) ((x) >> 5) /* version of option format */ +#define BSD_CURRENT_VERSION 1 /* current version number */ +#define BSD_MAKE_OPT(v, n) (((v) << 5) | (n)) + +#define BSD_MIN_BITS 9 /* smallest code size supported */ +#define BSD_MAX_BITS 15 /* largest code size supported */ +#endif /* BSDCOMPRESS_SUPPORT */ + +#if DEFLATE_SUPPORT +/* + * Definitions for Deflate. + */ + +#define CI_DEFLATE 26 /* config option for Deflate */ +#define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */ +#define CILEN_DEFLATE 4 /* length of its config option */ + +#define DEFLATE_MIN_SIZE 9 +#define DEFLATE_MAX_SIZE 15 +#define DEFLATE_METHOD_VAL 8 +#define DEFLATE_SIZE(x) (((x) >> 4) + 8) +#define DEFLATE_METHOD(x) ((x) & 0x0F) +#define DEFLATE_MAKE_OPT(w) ((((w) - 8) << 4) + DEFLATE_METHOD_VAL) +#define DEFLATE_CHK_SEQUENCE 0 +#endif /* DEFLATE_SUPPORT */ + +#if MPPE_SUPPORT +/* + * Definitions for MPPE. + */ + +#define CI_MPPE 18 /* config option for MPPE */ +#define CILEN_MPPE 6 /* length of config option */ +#endif /* MPPE_SUPPORT */ + +#if PREDICTOR_SUPPORT +/* + * Definitions for other, as yet unsupported, compression methods. + */ + +#define CI_PREDICTOR_1 1 /* config option for Predictor-1 */ +#define CILEN_PREDICTOR_1 2 /* length of its config option */ +#define CI_PREDICTOR_2 2 /* config option for Predictor-2 */ +#define CILEN_PREDICTOR_2 2 /* length of its config option */ +#endif /* PREDICTOR_SUPPORT */ + +typedef struct ccp_options { +#if DEFLATE_SUPPORT + unsigned int deflate :1; /* do Deflate? */ + unsigned int deflate_correct :1; /* use correct code for deflate? */ + unsigned int deflate_draft :1; /* use draft RFC code for deflate? */ +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + unsigned int bsd_compress :1; /* do BSD Compress? */ +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + unsigned int predictor_1 :1; /* do Predictor-1? */ + unsigned int predictor_2 :1; /* do Predictor-2? */ +#endif /* PREDICTOR_SUPPORT */ + +#if MPPE_SUPPORT + u8_t mppe; /* MPPE bitfield */ +#endif /* MPPE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + u_short bsd_bits; /* # bits/code for BSD Compress */ +#endif /* BSDCOMPRESS_SUPPORT */ +#if DEFLATE_SUPPORT + u_short deflate_size; /* lg(window size) for Deflate */ +#endif /* DEFLATE_SUPPORT */ + u8_t method; /* code for chosen compression method */ +} ccp_options; + +extern const struct protent ccp_protent; + +void ccp_resetrequest(ppp_pcb *pcb); /* Issue a reset-request. */ + +#endif /* CCP_H */ +#endif /* PPP_SUPPORT && CCP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap-md5.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap-md5.h new file mode 100644 index 0000000..eb0269f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap-md5.h @@ -0,0 +1,36 @@ +/* + * chap-md5.h - New CHAP/MD5 implementation. + * + * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +extern const struct chap_digest_type md5_digest; + +#endif /* PPP_SUPPORT && CHAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap-new.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap-new.h new file mode 100644 index 0000000..64eae32 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap-new.h @@ -0,0 +1,192 @@ +/* + * chap-new.c - New CHAP implementation. + * + * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef CHAP_H +#define CHAP_H + +#include "ppp.h" + +/* + * CHAP packets begin with a standard header with code, id, len (2 bytes). + */ +#define CHAP_HDRLEN 4 + +/* + * Values for the code field. + */ +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * CHAP digest codes. + */ +#define CHAP_MD5 5 +#if MSCHAP_SUPPORT +#define CHAP_MICROSOFT 0x80 +#define CHAP_MICROSOFT_V2 0x81 +#endif /* MSCHAP_SUPPORT */ + +/* + * Semi-arbitrary limits on challenge and response fields. + */ +#define MAX_CHALLENGE_LEN 64 +#define MAX_RESPONSE_LEN 64 + +/* + * These limits apply to challenge and response packets we send. + * The +4 is the +1 that we actually need rounded up. + */ +#define CHAL_MAX_PKTLEN (PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_CHALLENGE_LEN + MAXNAMELEN) +#define RESP_MAX_PKTLEN (PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_RESPONSE_LEN + MAXNAMELEN) + +/* bitmask of supported algorithms */ +#if MSCHAP_SUPPORT +#define MDTYPE_MICROSOFT_V2 0x1 +#define MDTYPE_MICROSOFT 0x2 +#endif /* MSCHAP_SUPPORT */ +#define MDTYPE_MD5 0x4 +#define MDTYPE_NONE 0 + +#if MSCHAP_SUPPORT +/* Return the digest alg. ID for the most preferred digest type. */ +#define CHAP_DIGEST(mdtype) \ + ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \ + ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \ + ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \ + 0 +#else /* !MSCHAP_SUPPORT */ +#define CHAP_DIGEST(mdtype) \ + ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \ + 0 +#endif /* MSCHAP_SUPPORT */ + +/* Return the bit flag (lsb set) for our most preferred digest type. */ +#define CHAP_MDTYPE(mdtype) ((mdtype) ^ ((mdtype) - 1)) & (mdtype) + +/* Return the bit flag for a given digest algorithm ID. */ +#if MSCHAP_SUPPORT +#define CHAP_MDTYPE_D(digest) \ + ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \ + ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \ + ((digest) == CHAP_MD5)? MDTYPE_MD5: \ + 0 +#else /* !MSCHAP_SUPPORT */ +#define CHAP_MDTYPE_D(digest) \ + ((digest) == CHAP_MD5)? MDTYPE_MD5: \ + 0 +#endif /* MSCHAP_SUPPORT */ + +/* Can we do the requested digest? */ +#if MSCHAP_SUPPORT +#define CHAP_CANDIGEST(mdtype, digest) \ + ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \ + ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \ + ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \ + 0 +#else /* !MSCHAP_SUPPORT */ +#define CHAP_CANDIGEST(mdtype, digest) \ + ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \ + 0 +#endif /* MSCHAP_SUPPORT */ + +/* + * The code for each digest type has to supply one of these. + */ +struct chap_digest_type { + int code; + +#if PPP_SERVER + /* + * Note: challenge and response arguments below are formatted as + * a length byte followed by the actual challenge/response data. + */ + void (*generate_challenge)(ppp_pcb *pcb, unsigned char *challenge); + int (*verify_response)(ppp_pcb *pcb, int id, const char *name, + const unsigned char *secret, int secret_len, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space); +#endif /* PPP_SERVER */ + void (*make_response)(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name, + const unsigned char *challenge, const char *secret, int secret_len, + unsigned char *priv); + int (*check_success)(ppp_pcb *pcb, unsigned char *pkt, int len, unsigned char *priv); + void (*handle_failure)(ppp_pcb *pcb, unsigned char *pkt, int len); +}; + +/* + * Each interface is described by chap structure. + */ +#if CHAP_SUPPORT +typedef struct chap_client_state { + u8_t flags; + const char *name; + const struct chap_digest_type *digest; + unsigned char priv[64]; /* private area for digest's use */ +} chap_client_state; + +#if PPP_SERVER +typedef struct chap_server_state { + u8_t flags; + u8_t id; + const char *name; + const struct chap_digest_type *digest; + int challenge_xmits; + int challenge_pktlen; + unsigned char challenge[CHAL_MAX_PKTLEN]; +} chap_server_state; +#endif /* PPP_SERVER */ +#endif /* CHAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* Hook for a plugin to validate CHAP challenge */ +extern int (*chap_verify_hook)(char *name, char *ourname, int id, + const struct chap_digest_type *digest, + unsigned char *challenge, unsigned char *response, + char *message, int message_space); +#endif /* UNUSED */ + +#if PPP_SERVER +/* Called by authentication code to start authenticating the peer. */ +extern void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code); +#endif /* PPP_SERVER */ + +/* Called by auth. code to start authenticating us to the peer. */ +extern void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code); + +/* Represents the CHAP protocol to the main pppd code */ +extern const struct protent chap_protent; + +#endif /* CHAP_H */ +#endif /* PPP_SUPPORT && CHAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap_ms.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap_ms.h new file mode 100644 index 0000000..0795291 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/chap_ms.h @@ -0,0 +1,44 @@ +/* + * chap_ms.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: chap_ms.h,v 1.13 2004/11/15 22:13:26 paulus Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef CHAPMS_INCLUDE +#define CHAPMS_INCLUDE + +extern const struct chap_digest_type chapms_digest; +extern const struct chap_digest_type chapms2_digest; + +#endif /* CHAPMS_INCLUDE */ + +#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/eap.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/eap.h new file mode 100644 index 0000000..3ee9aaf --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/eap.h @@ -0,0 +1,169 @@ +/* + * eap.h - Extensible Authentication Protocol for PPP (RFC 2284) + * + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + * + * Non-exclusive rights to redistribute, modify, translate, and use + * this software in source and binary forms, in whole or in part, is + * hereby granted, provided that the above copyright notice is + * duplicated in any source form, and that neither the name of the + * copyright holder nor the author is used to endorse or promote + * products derived from this software. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Original version by James Carlson + * + * $Id: eap.h,v 1.2 2003/06/11 23:56:26 paulus Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && EAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPP_EAP_H +#define PPP_EAP_H + +#include "ppp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Packet header = Code, id, length. + */ +#define EAP_HEADERLEN 4 + + +/* EAP message codes. */ +#define EAP_REQUEST 1 +#define EAP_RESPONSE 2 +#define EAP_SUCCESS 3 +#define EAP_FAILURE 4 + +/* EAP types */ +#define EAPT_IDENTITY 1 +#define EAPT_NOTIFICATION 2 +#define EAPT_NAK 3 /* (response only) */ +#define EAPT_MD5CHAP 4 +#define EAPT_OTP 5 /* One-Time Password; RFC 1938 */ +#define EAPT_TOKEN 6 /* Generic Token Card */ +/* 7 and 8 are unassigned. */ +#define EAPT_RSA 9 /* RSA Public Key Authentication */ +#define EAPT_DSS 10 /* DSS Unilateral */ +#define EAPT_KEA 11 /* KEA */ +#define EAPT_KEA_VALIDATE 12 /* KEA-VALIDATE */ +#define EAPT_TLS 13 /* EAP-TLS */ +#define EAPT_DEFENDER 14 /* Defender Token (AXENT) */ +#define EAPT_W2K 15 /* Windows 2000 EAP */ +#define EAPT_ARCOT 16 /* Arcot Systems */ +#define EAPT_CISCOWIRELESS 17 /* Cisco Wireless */ +#define EAPT_NOKIACARD 18 /* Nokia IP smart card */ +#define EAPT_SRP 19 /* Secure Remote Password */ +/* 20 is deprecated */ + +/* EAP SRP-SHA1 Subtypes */ +#define EAPSRP_CHALLENGE 1 /* Request 1 - Challenge */ +#define EAPSRP_CKEY 1 /* Response 1 - Client Key */ +#define EAPSRP_SKEY 2 /* Request 2 - Server Key */ +#define EAPSRP_CVALIDATOR 2 /* Response 2 - Client Validator */ +#define EAPSRP_SVALIDATOR 3 /* Request 3 - Server Validator */ +#define EAPSRP_ACK 3 /* Response 3 - final ack */ +#define EAPSRP_LWRECHALLENGE 4 /* Req/resp 4 - Lightweight rechal */ + +#define SRPVAL_EBIT 0x00000001 /* Use shared key for ECP */ + +#define SRP_PSEUDO_ID "pseudo_" +#define SRP_PSEUDO_LEN 7 + +#define MD5_SIGNATURE_SIZE 16 +#define EAP_MIN_CHALLENGE_LENGTH 17 +#define EAP_MAX_CHALLENGE_LENGTH 24 +#define EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH 3 /* 2^3-1 = 7, 17+7 = 24 */ + +#define EAP_STATES \ + "Initial", "Pending", "Closed", "Listen", "Identify", \ + "SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth" + +#define eap_client_active(pcb) ((pcb)->eap.es_client.ea_state == eapListen) +#if PPP_SERVER +#define eap_server_active(pcb) \ + ((pcb)->eap.es_server.ea_state >= eapIdentify && \ + (pcb)->eap.es_server.ea_state <= eapMD5Chall) +#endif /* PPP_SERVER */ + +/* + * Complete EAP state for one PPP session. + */ +enum eap_state_code { + eapInitial = 0, /* No EAP authentication yet requested */ + eapPending, /* Waiting for LCP (no timer) */ + eapClosed, /* Authentication not in use */ + eapListen, /* Client ready (and timer running) */ + eapIdentify, /* EAP Identify sent */ + eapSRP1, /* Sent EAP SRP-SHA1 Subtype 1 */ + eapSRP2, /* Sent EAP SRP-SHA1 Subtype 2 */ + eapSRP3, /* Sent EAP SRP-SHA1 Subtype 3 */ + eapMD5Chall, /* Sent MD5-Challenge */ + eapOpen, /* Completed authentication */ + eapSRP4, /* Sent EAP SRP-SHA1 Subtype 4 */ + eapBadAuth /* Failed authentication */ +}; + +struct eap_auth { + const char *ea_name; /* Our name */ + char ea_peer[MAXNAMELEN +1]; /* Peer's name */ + void *ea_session; /* Authentication library linkage */ + u_char *ea_skey; /* Shared encryption key */ + u_short ea_namelen; /* Length of our name */ + u_short ea_peerlen; /* Length of peer's name */ + enum eap_state_code ea_state; + u_char ea_id; /* Current id */ + u_char ea_requests; /* Number of Requests sent/received */ + u_char ea_responses; /* Number of Responses */ + u_char ea_type; /* One of EAPT_* */ + u32_t ea_keyflags; /* SRP shared key usage flags */ +}; + +#ifndef EAP_MAX_CHALLENGE_LENGTH +#define EAP_MAX_CHALLENGE_LENGTH 24 +#endif +typedef struct eap_state { + struct eap_auth es_client; /* Client (authenticatee) data */ +#if PPP_SERVER + struct eap_auth es_server; /* Server (authenticator) data */ +#endif /* PPP_SERVER */ + int es_savedtime; /* Saved timeout */ + int es_rechallenge; /* EAP rechallenge interval */ + int es_lwrechallenge; /* SRP lightweight rechallenge inter */ + u8_t es_usepseudo; /* Use SRP Pseudonym if offered one */ + int es_usedpseudo; /* Set if we already sent PN */ + int es_challen; /* Length of challenge string */ + u_char es_challenge[EAP_MAX_CHALLENGE_LENGTH]; +} eap_state; + +/* + * Timeouts. + */ +#if 0 /* moved to ppp_opts.h */ +#define EAP_DEFTIMEOUT 3 /* Timeout (seconds) for rexmit */ +#define EAP_DEFTRANSMITS 10 /* max # times to transmit */ +#define EAP_DEFREQTIME 20 /* Time to wait for peer request */ +#define EAP_DEFALLOWREQ 20 /* max # times to accept requests */ +#endif /* moved to ppp_opts.h */ + +void eap_authwithpeer(ppp_pcb *pcb, const char *localname); +void eap_authpeer(ppp_pcb *pcb, const char *localname); + +extern const struct protent eap_protent; + +#ifdef __cplusplus +} +#endif + +#endif /* PPP_EAP_H */ + +#endif /* PPP_SUPPORT && EAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ecp.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ecp.h new file mode 100644 index 0000000..5cdce29 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ecp.h @@ -0,0 +1,50 @@ +/* + * ecp.h - Definitions for PPP Encryption Control Protocol. + * + * Copyright (c) 2002 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ecp.h,v 1.2 2003/01/10 07:12:36 fcusack Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && ECP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +typedef struct ecp_options { + bool required; /* Is ECP required? */ + unsigned enctype; /* Encryption type */ +} ecp_options; + +extern fsm ecp_fsm[]; +extern ecp_options ecp_wantoptions[]; +extern ecp_options ecp_gotoptions[]; +extern ecp_options ecp_allowoptions[]; +extern ecp_options ecp_hisoptions[]; + +extern const struct protent ecp_protent; + +#endif /* PPP_SUPPORT && ECP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/eui64.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/eui64.h new file mode 100644 index 0000000..20ac22e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/eui64.h @@ -0,0 +1,94 @@ +/* + * eui64.h - EUI64 routines for IPv6CP. + * + * Copyright (c) 1999 Tommi Komulainen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: eui64.h,v 1.6 2002/12/04 23:03:32 paulus Exp $ +*/ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef EUI64_H +#define EUI64_H + +/* + * @todo: + * + * Maybe this should be done by processing struct in6_addr directly... + */ +typedef union +{ + u8_t e8[8]; + u16_t e16[4]; + u32_t e32[2]; +} eui64_t; + +#define eui64_iszero(e) (((e).e32[0] | (e).e32[1]) == 0) +#define eui64_equals(e, o) (((e).e32[0] == (o).e32[0]) && \ + ((e).e32[1] == (o).e32[1])) +#define eui64_zero(e) (e).e32[0] = (e).e32[1] = 0; + +#define eui64_copy(s, d) memcpy(&(d), &(s), sizeof(eui64_t)) + +#define eui64_magic(e) do { \ + (e).e32[0] = magic(); \ + (e).e32[1] = magic(); \ + (e).e8[0] &= ~2; \ + } while (0) +#define eui64_magic_nz(x) do { \ + eui64_magic(x); \ + } while (eui64_iszero(x)) +#define eui64_magic_ne(x, y) do { \ + eui64_magic(x); \ + } while (eui64_equals(x, y)) + +#define eui64_get(ll, cp) do { \ + eui64_copy((*cp), (ll)); \ + (cp) += sizeof(eui64_t); \ + } while (0) + +#define eui64_put(ll, cp) do { \ + eui64_copy((ll), (*cp)); \ + (cp) += sizeof(eui64_t); \ + } while (0) + +#define eui64_set32(e, l) do { \ + (e).e32[0] = 0; \ + (e).e32[1] = lwip_htonl(l); \ + } while (0) +#define eui64_setlo32(e, l) eui64_set32(e, l) + +char *eui64_ntoa(eui64_t); /* Returns ascii representation of id */ + +#endif /* EUI64_H */ +#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/fsm.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/fsm.h new file mode 100644 index 0000000..b6915d3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/fsm.h @@ -0,0 +1,175 @@ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: fsm.h,v 1.10 2004/11/13 02:28:15 paulus Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef FSM_H +#define FSM_H + +#include "ppp.h" + +/* + * Packet header = Code, id, length. + */ +#define HEADERLEN 4 + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + + +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + ppp_pcb *pcb; /* PPP Interface */ + const struct fsm_callbacks *callbacks; /* Callback routines */ + const char *term_reason; /* Reason for closing protocol */ + u8_t seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + /* -- This is our only flag, we might use u_int :1 if we have more flags */ + u16_t protocol; /* Data Link Layer Protocol field value */ + u8_t state; /* State */ + u8_t flags; /* Contains option bits */ + u8_t id; /* Current id */ + u8_t reqid; /* Current request id */ + u8_t retransmits; /* Number of retransmissions left */ + u8_t nakloops; /* Number of nak loops since last ack */ + u8_t rnakloops; /* Number of naks received */ + u8_t maxnakloops; /* Maximum number of nak loops tolerated + (necessary because IPCP require a custom large max nak loops value) */ + u8_t term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci) /* Reset our Configuration Information */ + (fsm *); + int (*cilen) /* Length of our Configuration Information */ + (fsm *); + void (*addci) /* Add our Configuration Information */ + (fsm *, u_char *, int *); + int (*ackci) /* ACK our Configuration Information */ + (fsm *, u_char *, int); + int (*nakci) /* NAK our Configuration Information */ + (fsm *, u_char *, int, int); + int (*rejci) /* Reject our Configuration Information */ + (fsm *, u_char *, int); + int (*reqci) /* Request peer's Configuration Information */ + (fsm *, u_char *, int *, int); + void (*up) /* Called when fsm reaches PPP_FSM_OPENED state */ + (fsm *); + void (*down) /* Called when fsm leaves PPP_FSM_OPENED state */ + (fsm *); + void (*starting) /* Called when we want the lower layer */ + (fsm *); + void (*finished) /* Called when we don't want the lower layer */ + (fsm *); + void (*protreject) /* Called when Protocol-Reject received */ + (int); + void (*retransmit) /* Retransmission is necessary */ + (fsm *); + int (*extcode) /* Called when unknown code received */ + (fsm *, int, int, u_char *, int); + const char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/* + * Link states. + */ +#define PPP_FSM_INITIAL 0 /* Down, hasn't been opened */ +#define PPP_FSM_STARTING 1 /* Down, been opened */ +#define PPP_FSM_CLOSED 2 /* Up, hasn't been opened */ +#define PPP_FSM_STOPPED 3 /* Open, waiting for down event */ +#define PPP_FSM_CLOSING 4 /* Terminating the connection, not open */ +#define PPP_FSM_STOPPING 5 /* Terminating, but open */ +#define PPP_FSM_REQSENT 6 /* We've sent a Config Request */ +#define PPP_FSM_ACKRCVD 7 /* We've received a Config Ack */ +#define PPP_FSM_ACKSENT 8 /* We've sent a Config Ack */ +#define PPP_FSM_OPENED 9 /* Connection available */ + + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Timeouts. + */ +#if 0 /* moved to ppp_opts.h */ +#define DEFTIMEOUT 3 /* Timeout time in seconds */ +#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif /* moved to ppp_opts.h */ + + +/* + * Prototypes + */ +void fsm_init(fsm *f); +void fsm_lowerup(fsm *f); +void fsm_lowerdown(fsm *f); +void fsm_open(fsm *f); +void fsm_close(fsm *f, const char *reason); +void fsm_input(fsm *f, u_char *inpacket, int l); +void fsm_protreject(fsm *f); +void fsm_sdata(fsm *f, u_char code, u_char id, const u_char *data, int datalen); + + +#endif /* FSM_H */ +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ipcp.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ipcp.h new file mode 100644 index 0000000..45f46b3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ipcp.h @@ -0,0 +1,126 @@ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ipcp.h,v 1.14 2002/12/04 23:03:32 paulus Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPP_IPV4_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef IPCP_H +#define IPCP_H + +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#if VJ_SUPPORT +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#endif /* VJ_SUPPORT */ +#define CI_ADDR 3 + +#if LWIP_DNS +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ +#endif /* LWIP_DNS */ +#if 0 /* UNUSED - WINS */ +#define CI_MS_WINS1 130 /* Primary WINS value */ +#define CI_MS_WINS2 132 /* Secondary WINS value */ +#endif /* UNUSED - WINS */ + +#if VJ_SUPPORT +#define MAX_STATES 16 /* from slcompress.h */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option*/ +#endif /* VJ_SUPPORT */ + +typedef struct ipcp_options { + unsigned int neg_addr :1; /* Negotiate IP Address? */ + unsigned int old_addrs :1; /* Use old (IP-Addresses) option? */ + unsigned int req_addr :1; /* Ask peer to send IP address? */ +#if 0 /* UNUSED */ + unsigned int default_route :1; /* Assign default route through interface? */ + unsigned int replace_default_route :1; /* Replace default route through interface? */ +#endif /* UNUSED */ +#if 0 /* UNUSED - PROXY ARP */ + unsigned int proxy_arp :1; /* Make proxy ARP entry for peer? */ +#endif /* UNUSED - PROXY ARP */ +#if VJ_SUPPORT + unsigned int neg_vj :1; /* Van Jacobson Compression? */ + unsigned int old_vj :1; /* use old (short) form of VJ option? */ + unsigned int cflag :1; +#endif /* VJ_SUPPORT */ + unsigned int accept_local :1; /* accept peer's value for ouraddr */ + unsigned int accept_remote :1; /* accept peer's value for hisaddr */ +#if LWIP_DNS + unsigned int req_dns1 :1; /* Ask peer to send primary DNS address? */ + unsigned int req_dns2 :1; /* Ask peer to send secondary DNS address? */ +#endif /* LWIP_DNS */ + + u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ +#if LWIP_DNS + u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ +#endif /* LWIP_DNS */ +#if 0 /* UNUSED - WINS */ + u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ +#endif /* UNUSED - WINS */ + +#if VJ_SUPPORT + u16_t vj_protocol; /* protocol value to use in VJ option */ + u8_t maxslotindex; /* values for RFC1332 VJ compression neg. */ +#endif /* VJ_SUPPORT */ +} ipcp_options; + +#if 0 /* UNUSED, already defined by lwIP */ +char *ip_ntoa (u32_t); +#endif /* UNUSED, already defined by lwIP */ + +extern const struct protent ipcp_protent; + +#endif /* IPCP_H */ +#endif /* PPP_SUPPORT && PPP_IPV4_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ipv6cp.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ipv6cp.h new file mode 100644 index 0000000..07d1ae3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ipv6cp.h @@ -0,0 +1,183 @@ +/* + * ipv6cp.h - PPP IPV6 Control Protocol. + * + * Copyright (c) 1999 Tommi Komulainen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* Original version, based on RFC2023 : + + Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt, + Alain.Durand@imag.fr, IMAG, + Jean-Luc.Richier@imag.fr, IMAG-LSR. + + Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE, + Alain.Durand@imag.fr, IMAG, + Jean-Luc.Richier@imag.fr, IMAG-LSR. + + Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt + Économique ayant pour membres BULL S.A. et l'INRIA). + + Ce logiciel informatique est disponible aux conditions + usuelles dans la recherche, c'est-à-dire qu'il peut + être utilisé, copié, modifié, distribué à l'unique + condition que ce texte soit conservé afin que + l'origine de ce logiciel soit reconnue. + + Le nom de l'Institut National de Recherche en Informatique + et en Automatique (INRIA), de l'IMAG, ou d'une personne morale + ou physique ayant participé à l'élaboration de ce logiciel ne peut + être utilisé sans son accord préalable explicite. + + Ce logiciel est fourni tel quel sans aucune garantie, + support ou responsabilité d'aucune sorte. + Ce logiciel est dérivé de sources d'origine + "University of California at Berkeley" et + "Digital Equipment Corporation" couvertes par des copyrights. + + L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG) + est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National + Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant + sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR). + + This work has been done in the context of GIE DYADE (joint R & D venture + between BULL S.A. and INRIA). + + This software is available with usual "research" terms + with the aim of retain credits of the software. + Permission to use, copy, modify and distribute this software for any + purpose and without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies, + and the name of INRIA, IMAG, or any contributor not be used in advertising + or publicity pertaining to this material without the prior explicit + permission. The software is provided "as is" without any + warranties, support or liabilities of any kind. + This software is derived from source code from + "University of California at Berkeley" and + "Digital Equipment Corporation" protected by copyrights. + + Grenoble's Institute of Computer Science and Applied Mathematics (IMAG) + is a federation of seven research units funded by the CNRS, National + Polytechnic Institute of Grenoble and University Joseph Fourier. + The research unit in Software, Systems, Networks (LSR) is member of IMAG. +*/ + +/* + * Derived from : + * + * + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ipv6cp.h,v 1.7 2002/12/04 23:03:32 paulus Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef IPV6CP_H +#define IPV6CP_H + +#include "eui64.h" + +/* + * Options. + */ +#define CI_IFACEID 1 /* Interface Identifier */ +#ifdef IPV6CP_COMP +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#endif /* IPV6CP_COMP */ + +/* No compression types yet defined. + *#define IPV6CP_COMP 0x004f + */ +typedef struct ipv6cp_options { + unsigned int neg_ifaceid :1; /* Negotiate interface identifier? */ + unsigned int req_ifaceid :1; /* Ask peer to send interface identifier? */ + unsigned int accept_local :1; /* accept peer's value for iface id? */ + unsigned int opt_local :1; /* ourtoken set by option */ + unsigned int opt_remote :1; /* histoken set by option */ + unsigned int use_ip :1; /* use IP as interface identifier */ +#if 0 + unsigned int use_persistent :1; /* use uniquely persistent value for address */ +#endif +#ifdef IPV6CP_COMP + unsigned int neg_vj :1; /* Van Jacobson Compression? */ +#endif /* IPV6CP_COMP */ + +#ifdef IPV6CP_COMP + u_short vj_protocol; /* protocol value to use in VJ option */ +#endif /* IPV6CP_COMP */ + eui64_t ourid, hisid; /* Interface identifiers */ +} ipv6cp_options; + +extern const struct protent ipv6cp_protent; + +#endif /* IPV6CP_H */ +#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/lcp.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/lcp.h new file mode 100644 index 0000000..12e2a05 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/lcp.h @@ -0,0 +1,171 @@ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: lcp.h,v 1.20 2004/11/14 22:53:42 carlsonj Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef LCP_H +#define LCP_H + +#include "ppp.h" + +/* + * Options. + */ +#define CI_VENDOR 0 /* Vendor Specific */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_FCSALTERN 9 /* FCS-Alternatives */ +#define CI_SDP 10 /* Self-Describing-Pad */ +#define CI_NUMBERED 11 /* Numbered-Mode */ +#define CI_CALLBACK 13 /* callback */ +#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ +#define CI_SSNHF 18 /* short sequence numbers for multilink */ +#define CI_EPDISC 19 /* endpoint discriminator */ +#define CI_MPPLUS 22 /* Multi-Link-Plus-Procedure */ +#define CI_LDISC 23 /* Link-Discriminator */ +#define CI_LCPAUTH 24 /* LCP Authentication */ +#define CI_COBS 25 /* Consistent Overhead Byte Stuffing */ +#define CI_PREFELIS 26 /* Prefix Elision */ +#define CI_MPHDRFMT 27 /* MP Header Format */ +#define CI_I18N 28 /* Internationalization */ +#define CI_SDL 29 /* Simple Data Link */ + +/* + * LCP-specific packet types (code numbers). + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define IDENTIF 12 /* Identification */ +#define TIMEREM 13 /* Time Remaining */ + +/* Value used as data for CI_CALLBACK option */ +#define CBCP_OPT 6 /* Use callback control protocol */ + +#if 0 /* moved to ppp_opts.h */ +#define DEFMRU 1500 /* Try for this */ +#define MINMRU 128 /* No MRUs below this */ +#define MAXMRU 16384 /* Normally limit MRU to this */ +#endif /* moved to ppp_opts.h */ + +/* An endpoint discriminator, used with multilink. */ +#define MAX_ENDP_LEN 20 /* maximum length of discriminator value */ +struct epdisc { + unsigned char class_; /* -- The word "class" is reserved in C++. */ + unsigned char length; + unsigned char value[MAX_ENDP_LEN]; +}; + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + unsigned int passive :1; /* Don't die if we don't get a response */ + unsigned int silent :1; /* Wait for the other end to start first */ +#if 0 /* UNUSED */ + unsigned int restart :1; /* Restart vs. exit after close */ +#endif /* UNUSED */ + unsigned int neg_mru :1; /* Negotiate the MRU? */ + unsigned int neg_asyncmap :1; /* Negotiate the async map? */ +#if PAP_SUPPORT + unsigned int neg_upap :1; /* Ask for UPAP authentication? */ +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + unsigned int neg_chap :1; /* Ask for CHAP authentication? */ +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + unsigned int neg_eap :1; /* Ask for EAP authentication? */ +#endif /* EAP_SUPPORT */ + unsigned int neg_magicnumber :1; /* Ask for magic number? */ + unsigned int neg_pcompression :1; /* HDLC Protocol Field Compression? */ + unsigned int neg_accompression :1; /* HDLC Address/Control Field Compression? */ +#if LQR_SUPPORT + unsigned int neg_lqr :1; /* Negotiate use of Link Quality Reports */ +#endif /* LQR_SUPPORT */ + unsigned int neg_cbcp :1; /* Negotiate use of CBCP */ +#ifdef HAVE_MULTILINK + unsigned int neg_mrru :1; /* negotiate multilink MRRU */ +#endif /* HAVE_MULTILINK */ + unsigned int neg_ssnhf :1; /* negotiate short sequence numbers */ + unsigned int neg_endpoint :1; /* negotiate endpoint discriminator */ + + u16_t mru; /* Value of MRU */ +#ifdef HAVE_MULTILINK + u16_t mrru; /* Value of MRRU, and multilink enable */ +#endif /* MULTILINK */ +#if CHAP_SUPPORT + u8_t chap_mdtype; /* which MD types (hashing algorithm) */ +#endif /* CHAP_SUPPORT */ + u32_t asyncmap; /* Value of async map */ + u32_t magicnumber; + u8_t numloops; /* Number of loops during magic number neg. */ +#if LQR_SUPPORT + u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +#endif /* LQR_SUPPORT */ + struct epdisc endpoint; /* endpoint discriminator */ +} lcp_options; + +void lcp_open(ppp_pcb *pcb); +void lcp_close(ppp_pcb *pcb, const char *reason); +void lcp_lowerup(ppp_pcb *pcb); +void lcp_lowerdown(ppp_pcb *pcb); +void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len); /* send protocol reject */ + +extern const struct protent lcp_protent; + +#if 0 /* moved to ppp_opts.h */ +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#define DEFLOOPBACKFAIL 10 +#endif /* moved to ppp_opts.h */ + +#endif /* LCP_H */ +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/magic.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/magic.h new file mode 100644 index 0000000..a2a9b53 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/magic.h @@ -0,0 +1,122 @@ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: magic.h,v 1.5 2003/06/11 23:56:26 paulus Exp $ + */ +/***************************************************************************** +* randm.h - Random number generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-05-29 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef MAGIC_H +#define MAGIC_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* + * Initialize the random number generator. + */ +void magic_init(void); + +/* + * Randomize our random seed value. To be called for truely random events + * such as user operations and network traffic. + */ +void magic_randomize(void); + +/* + * Return a new random number. + */ +u32_t magic(void); /* Returns the next magic number */ + +/* + * Fill buffer with random bytes + * + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using magic_churnrand(). + * Thus it's important to make sure that the results of this are not + * published directly because one could predict the next result to at + * least some degree. Also, it's important to get a good seed before + * the first use. + */ +void magic_random_bytes(unsigned char *buf, u32_t buf_len); + +/* + * Return a new random number between 0 and (2^pow)-1 included. + */ +u32_t magic_pow(u8_t pow); + +#endif /* MAGIC_H */ + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/mppe.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/mppe.h new file mode 100644 index 0000000..1ae8a5d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/mppe.h @@ -0,0 +1,173 @@ +/* + * mppe.h - Definitions for MPPE + * + * Copyright (c) 2008 Paul Mackerras. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && MPPE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef MPPE_H +#define MPPE_H + +#include "netif/ppp/pppcrypt.h" + +#define MPPE_PAD 4 /* MPPE growth per frame */ +#define MPPE_MAX_KEY_LEN 16 /* largest key length (128-bit) */ + +/* option bits for ccp_options.mppe */ +#define MPPE_OPT_40 0x01 /* 40 bit */ +#define MPPE_OPT_128 0x02 /* 128 bit */ +#define MPPE_OPT_STATEFUL 0x04 /* stateful mode */ +/* unsupported opts */ +#define MPPE_OPT_56 0x08 /* 56 bit */ +#define MPPE_OPT_MPPC 0x10 /* MPPC compression */ +#define MPPE_OPT_D 0x20 /* Unknown */ +#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D) +#define MPPE_OPT_UNKNOWN 0x40 /* Bits !defined in RFC 3078 were set */ + +/* + * This is not nice ... the alternative is a bitfield struct though. + * And unfortunately, we cannot share the same bits for the option + * names above since C and H are the same bit. We could do a u_int32 + * but then we have to do a lwip_htonl() all the time and/or we still need + * to know which octet is which. + */ +#define MPPE_C_BIT 0x01 /* MPPC */ +#define MPPE_D_BIT 0x10 /* Obsolete, usage unknown */ +#define MPPE_L_BIT 0x20 /* 40-bit */ +#define MPPE_S_BIT 0x40 /* 128-bit */ +#define MPPE_M_BIT 0x80 /* 56-bit, not supported */ +#define MPPE_H_BIT 0x01 /* Stateless (in a different byte) */ + +/* Does not include H bit; used for least significant octet only. */ +#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT) + +/* Build a CI from mppe opts (see RFC 3078) */ +#define MPPE_OPTS_TO_CI(opts, ci) \ + do { \ + u_char *ptr = ci; /* u_char[4] */ \ + \ + /* H bit */ \ + if (opts & MPPE_OPT_STATEFUL) \ + *ptr++ = 0x0; \ + else \ + *ptr++ = MPPE_H_BIT; \ + *ptr++ = 0; \ + *ptr++ = 0; \ + \ + /* S,L bits */ \ + *ptr = 0; \ + if (opts & MPPE_OPT_128) \ + *ptr |= MPPE_S_BIT; \ + if (opts & MPPE_OPT_40) \ + *ptr |= MPPE_L_BIT; \ + /* M,D,C bits not supported */ \ + } while (/* CONSTCOND */ 0) + +/* The reverse of the above */ +#define MPPE_CI_TO_OPTS(ci, opts) \ + do { \ + const u_char *ptr = ci; /* u_char[4] */ \ + \ + opts = 0; \ + \ + /* H bit */ \ + if (!(ptr[0] & MPPE_H_BIT)) \ + opts |= MPPE_OPT_STATEFUL; \ + \ + /* S,L bits */ \ + if (ptr[3] & MPPE_S_BIT) \ + opts |= MPPE_OPT_128; \ + if (ptr[3] & MPPE_L_BIT) \ + opts |= MPPE_OPT_40; \ + \ + /* M,D,C bits */ \ + if (ptr[3] & MPPE_M_BIT) \ + opts |= MPPE_OPT_56; \ + if (ptr[3] & MPPE_D_BIT) \ + opts |= MPPE_OPT_D; \ + if (ptr[3] & MPPE_C_BIT) \ + opts |= MPPE_OPT_MPPC; \ + \ + /* Other bits */ \ + if (ptr[0] & ~MPPE_H_BIT) \ + opts |= MPPE_OPT_UNKNOWN; \ + if (ptr[1] || ptr[2]) \ + opts |= MPPE_OPT_UNKNOWN; \ + if (ptr[3] & ~MPPE_ALL_BITS) \ + opts |= MPPE_OPT_UNKNOWN; \ + } while (/* CONSTCOND */ 0) + +/* Shared MPPE padding between MSCHAP and MPPE */ +#define SHA1_PAD_SIZE 40 + +static const u8_t mppe_sha1_pad1[SHA1_PAD_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const u8_t mppe_sha1_pad2[SHA1_PAD_SIZE] = { + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 +}; + +/* + * State for an MPPE (de)compressor. + */ +typedef struct ppp_mppe_state { + lwip_arc4_context arc4; + u8_t master_key[MPPE_MAX_KEY_LEN]; + u8_t session_key[MPPE_MAX_KEY_LEN]; + u8_t keylen; /* key length in bytes */ + /* NB: 128-bit == 16, 40-bit == 8! + * If we want to support 56-bit, the unit has to change to bits + */ + u8_t bits; /* MPPE control bits */ + u16_t ccount; /* 12-bit coherency count (seqno) */ + u16_t sanity_errors; /* take down LCP if too many */ + unsigned int stateful :1; /* stateful mode flag */ + unsigned int discard :1; /* stateful mode packet loss flag */ +} ppp_mppe_state; + +void mppe_set_key(ppp_pcb *pcb, ppp_mppe_state *state, u8_t *key); +void mppe_init(ppp_pcb *pcb, ppp_mppe_state *state, u8_t options); +void mppe_comp_reset(ppp_pcb *pcb, ppp_mppe_state *state); +err_t mppe_compress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb, u16_t protocol); +void mppe_decomp_reset(ppp_pcb *pcb, ppp_mppe_state *state); +err_t mppe_decompress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb); + +#endif /* MPPE_H */ +#endif /* PPP_SUPPORT && MPPE_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/arc4.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/arc4.h new file mode 100644 index 0000000..4af724c --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/arc4.h @@ -0,0 +1,81 @@ +/** + * \file arc4.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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 "netif/ppp/ppp_opts.h" +#if LWIP_INCLUDED_POLARSSL_ARC4 + +#ifndef LWIP_INCLUDED_POLARSSL_ARC4_H +#define LWIP_INCLUDED_POLARSSL_ARC4_H + +/** + * \brief ARC4 context structure + */ +typedef struct +{ + int x; /*!< permutation index */ + int y; /*!< permutation index */ + unsigned char m[256]; /*!< permutation table */ +} +arc4_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief ARC4 key schedule + * + * \param ctx ARC4 context to be initialized + * \param key the secret key + * \param keylen length of the key + */ +void arc4_setup( arc4_context *ctx, unsigned char *key, int keylen ); + +/** + * \brief ARC4 cipher function + * + * \param ctx ARC4 context + * \param buf buffer to be processed + * \param buflen amount of data in buf + */ +void arc4_crypt( arc4_context *ctx, unsigned char *buf, int buflen ); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_ARC4_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_ARC4 */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/des.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/des.h new file mode 100644 index 0000000..e893890 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/des.h @@ -0,0 +1,92 @@ +/** + * \file des.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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 "netif/ppp/ppp_opts.h" +#if LWIP_INCLUDED_POLARSSL_DES + +#ifndef LWIP_INCLUDED_POLARSSL_DES_H +#define LWIP_INCLUDED_POLARSSL_DES_H + +#define DES_ENCRYPT 1 +#define DES_DECRYPT 0 + +/** + * \brief DES context structure + */ +typedef struct +{ + int mode; /*!< encrypt/decrypt */ + unsigned long sk[32]; /*!< DES subkeys */ +} +des_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief DES key schedule (56-bit, encryption) + * + * \param ctx DES context to be initialized + * \param key 8-byte secret key + */ +void des_setkey_enc( des_context *ctx, unsigned char key[8] ); + +/** + * \brief DES key schedule (56-bit, decryption) + * + * \param ctx DES context to be initialized + * \param key 8-byte secret key + */ +void des_setkey_dec( des_context *ctx, unsigned char key[8] ); + +/** + * \brief DES-ECB block encryption/decryption + * + * \param ctx DES context + * \param input 64-bit input block + * \param output 64-bit output block + */ +void des_crypt_ecb( des_context *ctx, + const unsigned char input[8], + unsigned char output[8] ); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_DES_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_DES */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/md4.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/md4.h new file mode 100644 index 0000000..5704456 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/md4.h @@ -0,0 +1,97 @@ +/** + * \file md4.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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 "netif/ppp/ppp_opts.h" +#if LWIP_INCLUDED_POLARSSL_MD4 + +#ifndef LWIP_INCLUDED_POLARSSL_MD4_H +#define LWIP_INCLUDED_POLARSSL_MD4_H + +/** + * \brief MD4 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} +md4_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD4 context setup + * + * \param ctx context to be initialized + */ +void md4_starts( md4_context *ctx ); + +/** + * \brief MD4 process buffer + * + * \param ctx MD4 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md4_update( md4_context *ctx, const unsigned char *input, int ilen ); + +/** + * \brief MD4 final digest + * + * \param ctx MD4 context + * \param output MD4 checksum result + */ +void md4_finish( md4_context *ctx, unsigned char output[16] ); + +/** + * \brief Output = MD4( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD4 checksum result + */ +void md4( unsigned char *input, int ilen, unsigned char output[16] ); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_MD4_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_MD4 */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/md5.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/md5.h new file mode 100644 index 0000000..1244011 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/md5.h @@ -0,0 +1,96 @@ +/** + * \file md5.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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 "netif/ppp/ppp_opts.h" +#if LWIP_INCLUDED_POLARSSL_MD5 + +#ifndef LWIP_INCLUDED_POLARSSL_MD5_H +#define LWIP_INCLUDED_POLARSSL_MD5_H + +/** + * \brief MD5 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} +md5_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD5 context setup + * + * \param ctx context to be initialized + */ +void md5_starts( md5_context *ctx ); + +/** + * \brief MD5 process buffer + * + * \param ctx MD5 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md5_update( md5_context *ctx, const unsigned char *input, int ilen ); + +/** + * \brief MD5 final digest + * + * \param ctx MD5 context + * \param output MD5 checksum result + */ +void md5_finish( md5_context *ctx, unsigned char output[16] ); + +/** + * \brief Output = MD5( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD5 checksum result + */ +void md5( unsigned char *input, int ilen, unsigned char output[16] ); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_MD5_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_MD5 */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/sha1.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/sha1.h new file mode 100644 index 0000000..a4c53e0 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/polarssl/sha1.h @@ -0,0 +1,96 @@ +/** + * \file sha1.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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 "netif/ppp/ppp_opts.h" +#if LWIP_INCLUDED_POLARSSL_SHA1 + +#ifndef LWIP_INCLUDED_POLARSSL_SHA1_H +#define LWIP_INCLUDED_POLARSSL_SHA1_H + +/** + * \brief SHA-1 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} +sha1_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-1 context setup + * + * \param ctx context to be initialized + */ +void sha1_starts( sha1_context *ctx ); + +/** + * \brief SHA-1 process buffer + * + * \param ctx SHA-1 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen ); + +/** + * \brief SHA-1 final digest + * + * \param ctx SHA-1 context + * \param output SHA-1 checksum result + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ); + +/** + * \brief Output = SHA-1( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-1 checksum result + */ +void sha1( unsigned char *input, int ilen, unsigned char output[20] ); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_SHA1_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_SHA1 */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp.h new file mode 100644 index 0000000..d9ea097 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp.h @@ -0,0 +1,690 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPP_H +#define PPP_H + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timeouts.h" +#if PPP_IPV6_SUPPORT +#include "lwip/ip6_addr.h" +#endif /* PPP_IPV6_SUPPORT */ + +/* Disable non-working or rarely used PPP feature, so rarely that we don't want to bloat ppp_opts.h with them */ +#ifndef PPP_OPTIONS +#define PPP_OPTIONS 0 +#endif + +#ifndef PPP_NOTIFY +#define PPP_NOTIFY 0 +#endif + +#ifndef PPP_REMOTENAME +#define PPP_REMOTENAME 0 +#endif + +#ifndef PPP_IDLETIMELIMIT +#define PPP_IDLETIMELIMIT 0 +#endif + +#ifndef PPP_LCP_ADAPTIVE +#define PPP_LCP_ADAPTIVE 0 +#endif + +#ifndef PPP_MAXCONNECT +#define PPP_MAXCONNECT 0 +#endif + +#ifndef PPP_ALLOWED_ADDRS +#define PPP_ALLOWED_ADDRS 0 +#endif + +#ifndef PPP_PROTOCOLNAME +#define PPP_PROTOCOLNAME 0 +#endif + +#ifndef PPP_STATS_SUPPORT +#define PPP_STATS_SUPPORT 0 +#endif + +#ifndef DEFLATE_SUPPORT +#define DEFLATE_SUPPORT 0 +#endif + +#ifndef BSDCOMPRESS_SUPPORT +#define BSDCOMPRESS_SUPPORT 0 +#endif + +#ifndef PREDICTOR_SUPPORT +#define PREDICTOR_SUPPORT 0 +#endif + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + +/* + * Values for phase. + */ +#define PPP_PHASE_DEAD 0 +#define PPP_PHASE_MASTER 1 +#define PPP_PHASE_HOLDOFF 2 +#define PPP_PHASE_INITIALIZE 3 +#define PPP_PHASE_SERIALCONN 4 +#define PPP_PHASE_DORMANT 5 +#define PPP_PHASE_ESTABLISH 6 +#define PPP_PHASE_AUTHENTICATE 7 +#define PPP_PHASE_CALLBACK 8 +#define PPP_PHASE_NETWORK 9 +#define PPP_PHASE_RUNNING 10 +#define PPP_PHASE_TERMINATE 11 +#define PPP_PHASE_DISCONNECT 12 + +/* Error codes. */ +#define PPPERR_NONE 0 /* No error. */ +#define PPPERR_PARAM 1 /* Invalid parameter. */ +#define PPPERR_OPEN 2 /* Unable to open PPP session. */ +#define PPPERR_DEVICE 3 /* Invalid I/O device for PPP. */ +#define PPPERR_ALLOC 4 /* Unable to allocate resources. */ +#define PPPERR_USER 5 /* User interrupt. */ +#define PPPERR_CONNECT 6 /* Connection lost. */ +#define PPPERR_AUTHFAIL 7 /* Failed authentication challenge. */ +#define PPPERR_PROTOCOL 8 /* Failed to meet protocol. */ +#define PPPERR_PEERDEAD 9 /* Connection timeout */ +#define PPPERR_IDLETIMEOUT 10 /* Idle Timeout */ +#define PPPERR_CONNECTTIME 11 /* Max connect time reached */ +#define PPPERR_LOOPBACK 12 /* Loopback detected */ + +/* Whether auth support is enabled at all */ +#define PPP_AUTH_SUPPORT (PAP_SUPPORT || CHAP_SUPPORT || EAP_SUPPORT) + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * Other headers require ppp_pcb definition for prototypes, but ppp_pcb + * require some structure definition from other headers as well, we are + * fixing the dependency loop here by declaring the ppp_pcb type then + * by including headers containing necessary struct definition for ppp_pcb + */ +typedef struct ppp_pcb_s ppp_pcb; + +/* Type definitions for BSD code. */ +#ifndef __u_char_defined +typedef unsigned long u_long; +typedef unsigned int u_int; +typedef unsigned short u_short; +typedef unsigned char u_char; +#endif + +#include "fsm.h" +#include "lcp.h" +#if CCP_SUPPORT +#include "ccp.h" +#endif /* CCP_SUPPORT */ +#if MPPE_SUPPORT +#include "mppe.h" +#endif /* MPPE_SUPPORT */ +#if PPP_IPV4_SUPPORT +#include "ipcp.h" +#endif /* PPP_IPV4_SUPPORT */ +#if PPP_IPV6_SUPPORT +#include "ipv6cp.h" +#endif /* PPP_IPV6_SUPPORT */ +#if PAP_SUPPORT +#include "upap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "chap-new.h" +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT +#include "eap.h" +#endif /* EAP_SUPPORT */ +#if VJ_SUPPORT +#include "vj.h" +#endif /* VJ_SUPPORT */ + +/* Link status callback function prototype */ +typedef void (*ppp_link_status_cb_fn)(ppp_pcb *pcb, int err_code, void *ctx); + +/* + * PPP configuration. + */ +typedef struct ppp_settings_s { + +#if PPP_SERVER && PPP_AUTH_SUPPORT + unsigned int auth_required :1; /* Peer is required to authenticate */ + unsigned int null_login :1; /* Username of "" and a password of "" are acceptable */ +#endif /* PPP_SERVER && PPP_AUTH_SUPPORT */ +#if PPP_REMOTENAME + unsigned int explicit_remote :1; /* remote_name specified with remotename opt */ +#endif /* PPP_REMOTENAME */ +#if PAP_SUPPORT + unsigned int refuse_pap :1; /* Don't proceed auth. with PAP */ +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + unsigned int refuse_chap :1; /* Don't proceed auth. with CHAP */ +#endif /* CHAP_SUPPORT */ +#if MSCHAP_SUPPORT + unsigned int refuse_mschap :1; /* Don't proceed auth. with MS-CHAP */ + unsigned int refuse_mschap_v2 :1; /* Don't proceed auth. with MS-CHAPv2 */ +#endif /* MSCHAP_SUPPORT */ +#if EAP_SUPPORT + unsigned int refuse_eap :1; /* Don't proceed auth. with EAP */ +#endif /* EAP_SUPPORT */ +#if LWIP_DNS + unsigned int usepeerdns :1; /* Ask peer for DNS adds */ +#endif /* LWIP_DNS */ + unsigned int persist :1; /* Persist mode, always try to open the connection */ +#if PRINTPKT_SUPPORT + unsigned int hide_password :1; /* Hide password in dumped packets */ +#endif /* PRINTPKT_SUPPORT */ + unsigned int noremoteip :1; /* Let him have no IP address */ + unsigned int lax_recv :1; /* accept control chars in asyncmap */ + unsigned int noendpoint :1; /* don't send/accept endpoint discriminator */ +#if PPP_LCP_ADAPTIVE + unsigned int lcp_echo_adaptive :1; /* request echo only if the link was idle */ +#endif /* PPP_LCP_ADAPTIVE */ +#if MPPE_SUPPORT + unsigned int require_mppe :1; /* Require MPPE (Microsoft Point to Point Encryption) */ + unsigned int refuse_mppe_40 :1; /* Allow MPPE 40-bit mode? */ + unsigned int refuse_mppe_128 :1; /* Allow MPPE 128-bit mode? */ + unsigned int refuse_mppe_stateful :1; /* Allow MPPE stateful mode? */ +#endif /* MPPE_SUPPORT */ + + u16_t listen_time; /* time to listen first (ms), waiting for peer to send LCP packet */ + +#if PPP_IDLETIMELIMIT + u16_t idle_time_limit; /* Disconnect if idle for this many seconds */ +#endif /* PPP_IDLETIMELIMIT */ +#if PPP_MAXCONNECT + u32_t maxconnect; /* Maximum connect time (seconds) */ +#endif /* PPP_MAXCONNECT */ + +#if PPP_AUTH_SUPPORT + /* auth data */ + const char *user; /* Username for PAP */ + const char *passwd; /* Password for PAP, secret for CHAP */ +#if PPP_REMOTENAME + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +#endif /* PPP_REMOTENAME */ + +#if PAP_SUPPORT + u8_t pap_timeout_time; /* Timeout (seconds) for auth-req retrans. */ + u8_t pap_max_transmits; /* Number of auth-reqs sent */ +#if PPP_SERVER + u8_t pap_req_timeout; /* Time to wait for auth-req from peer */ +#endif /* PPP_SERVER */ +#endif /* PAP_SUPPPORT */ + +#if CHAP_SUPPORT + u8_t chap_timeout_time; /* Timeout (seconds) for retransmitting req */ + u8_t chap_max_transmits; /* max # times to send challenge */ +#if PPP_SERVER + u8_t chap_rechallenge_time; /* Time to wait for auth-req from peer */ +#endif /* PPP_SERVER */ +#endif /* CHAP_SUPPPORT */ + +#if EAP_SUPPORT + u8_t eap_req_time; /* Time to wait (for retransmit/fail) */ + u8_t eap_allow_req; /* Max Requests allowed */ +#if PPP_SERVER + u8_t eap_timeout_time; /* Time to wait (for retransmit/fail) */ + u8_t eap_max_transmits; /* Max Requests allowed */ +#endif /* PPP_SERVER */ +#endif /* EAP_SUPPORT */ + +#endif /* PPP_AUTH_SUPPORT */ + + u8_t fsm_timeout_time; /* Timeout time in seconds */ + u8_t fsm_max_conf_req_transmits; /* Maximum Configure-Request transmissions */ + u8_t fsm_max_term_transmits; /* Maximum Terminate-Request transmissions */ + u8_t fsm_max_nak_loops; /* Maximum number of nak loops tolerated */ + + u8_t lcp_loopbackfail; /* Number of times we receive our magic number from the peer + before deciding the link is looped-back. */ + u8_t lcp_echo_interval; /* Interval between LCP echo-requests */ + u8_t lcp_echo_fails; /* Tolerance to unanswered echo-requests */ + +} ppp_settings; + +#if PPP_SERVER +struct ppp_addrs { +#if PPP_IPV4_SUPPORT + ip4_addr_t our_ipaddr, his_ipaddr, netmask; +#if LWIP_DNS + ip4_addr_t dns1, dns2; +#endif /* LWIP_DNS */ +#endif /* PPP_IPV4_SUPPORT */ +#if PPP_IPV6_SUPPORT + ip6_addr_t our6_ipaddr, his6_ipaddr; +#endif /* PPP_IPV6_SUPPORT */ +}; +#endif /* PPP_SERVER */ + +/* + * PPP interface control block. + */ +struct ppp_pcb_s { + ppp_settings settings; + const struct link_callbacks *link_cb; + void *link_ctx_cb; + void (*link_status_cb)(ppp_pcb *pcb, int err_code, void *ctx); /* Status change callback */ +#if PPP_NOTIFY_PHASE + void (*notify_phase_cb)(ppp_pcb *pcb, u8_t phase, void *ctx); /* Notify phase callback */ +#endif /* PPP_NOTIFY_PHASE */ + void *ctx_cb; /* Callbacks optional pointer */ + struct netif *netif; /* PPP interface */ + u8_t phase; /* where the link is at */ + u8_t err_code; /* Code indicating why interface is down. */ + + /* flags */ +#if PPP_IPV4_SUPPORT + unsigned int ask_for_local :1; /* request our address from peer */ + unsigned int ipcp_is_open :1; /* haven't called np_finished() */ + unsigned int ipcp_is_up :1; /* have called ipcp_up() */ + unsigned int if4_up :1; /* True when the IPv4 interface is up. */ +#if 0 /* UNUSED - PROXY ARP */ + unsigned int proxy_arp_set :1; /* Have created proxy arp entry */ +#endif /* UNUSED - PROXY ARP */ +#endif /* PPP_IPV4_SUPPORT */ +#if PPP_IPV6_SUPPORT + unsigned int ipv6cp_is_up :1; /* have called ip6cp_up() */ + unsigned int if6_up :1; /* True when the IPv6 interface is up. */ +#endif /* PPP_IPV6_SUPPORT */ + unsigned int lcp_echo_timer_running :1; /* set if a timer is running */ +#if VJ_SUPPORT + unsigned int vj_enabled :1; /* Flag indicating VJ compression enabled. */ +#endif /* VJ_SUPPORT */ +#if CCP_SUPPORT + unsigned int ccp_all_rejected :1; /* we rejected all peer's options */ +#endif /* CCP_SUPPORT */ +#if MPPE_SUPPORT + unsigned int mppe_keys_set :1; /* Have the MPPE keys been set? */ +#endif /* MPPE_SUPPORT */ + +#if PPP_AUTH_SUPPORT + /* auth data */ +#if PPP_SERVER && defined(HAVE_MULTILINK) + char peer_authname[MAXNAMELEN + 1]; /* The name by which the peer authenticated itself to us. */ +#endif /* PPP_SERVER && defined(HAVE_MULTILINK) */ + u16_t auth_pending; /* Records which authentication operations haven't completed yet. */ + u16_t auth_done; /* Records which authentication operations have been completed. */ + +#if PAP_SUPPORT + upap_state upap; /* PAP data */ +#endif /* PAP_SUPPORT */ + +#if CHAP_SUPPORT + chap_client_state chap_client; /* CHAP client data */ +#if PPP_SERVER + chap_server_state chap_server; /* CHAP server data */ +#endif /* PPP_SERVER */ +#endif /* CHAP_SUPPORT */ + +#if EAP_SUPPORT + eap_state eap; /* EAP data */ +#endif /* EAP_SUPPORT */ +#endif /* PPP_AUTH_SUPPORT */ + + fsm lcp_fsm; /* LCP fsm structure */ + lcp_options lcp_wantoptions; /* Options that we want to request */ + lcp_options lcp_gotoptions; /* Options that peer ack'd */ + lcp_options lcp_allowoptions; /* Options we allow peer to request */ + lcp_options lcp_hisoptions; /* Options that we ack'd */ + u16_t peer_mru; /* currently negotiated peer MRU */ + u8_t lcp_echos_pending; /* Number of outstanding echo msgs */ + u8_t lcp_echo_number; /* ID number of next echo frame */ + + u8_t num_np_open; /* Number of network protocols which we have opened. */ + u8_t num_np_up; /* Number of network protocols which have come up. */ + +#if VJ_SUPPORT + struct vjcompress vj_comp; /* Van Jacobson compression header. */ +#endif /* VJ_SUPPORT */ + +#if CCP_SUPPORT + fsm ccp_fsm; /* CCP fsm structure */ + ccp_options ccp_wantoptions; /* what to request the peer to use */ + ccp_options ccp_gotoptions; /* what the peer agreed to do */ + ccp_options ccp_allowoptions; /* what we'll agree to do */ + ccp_options ccp_hisoptions; /* what we agreed to do */ + u8_t ccp_localstate; /* Local state (mainly for handling reset-reqs and reset-acks). */ + u8_t ccp_receive_method; /* Method chosen on receive path */ + u8_t ccp_transmit_method; /* Method chosen on transmit path */ +#if MPPE_SUPPORT + ppp_mppe_state mppe_comp; /* MPPE "compressor" structure */ + ppp_mppe_state mppe_decomp; /* MPPE "decompressor" structure */ +#endif /* MPPE_SUPPORT */ +#endif /* CCP_SUPPORT */ + +#if PPP_IPV4_SUPPORT + fsm ipcp_fsm; /* IPCP fsm structure */ + ipcp_options ipcp_wantoptions; /* Options that we want to request */ + ipcp_options ipcp_gotoptions; /* Options that peer ack'd */ + ipcp_options ipcp_allowoptions; /* Options we allow peer to request */ + ipcp_options ipcp_hisoptions; /* Options that we ack'd */ +#endif /* PPP_IPV4_SUPPORT */ + +#if PPP_IPV6_SUPPORT + fsm ipv6cp_fsm; /* IPV6CP fsm structure */ + ipv6cp_options ipv6cp_wantoptions; /* Options that we want to request */ + ipv6cp_options ipv6cp_gotoptions; /* Options that peer ack'd */ + ipv6cp_options ipv6cp_allowoptions; /* Options we allow peer to request */ + ipv6cp_options ipv6cp_hisoptions; /* Options that we ack'd */ +#endif /* PPP_IPV6_SUPPORT */ +}; + +/************************ + *** PUBLIC FUNCTIONS *** + ************************/ + +/* + * WARNING: For multi-threads environment, all ppp_set_* functions most + * only be called while the PPP is in the dead phase (i.e. disconnected). + */ + +#if PPP_AUTH_SUPPORT +/* + * Set PPP authentication. + * + * Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + * Default is none auth type, unset (NULL) user and passwd. + */ +#define PPPAUTHTYPE_NONE 0x00 +#define PPPAUTHTYPE_PAP 0x01 +#define PPPAUTHTYPE_CHAP 0x02 +#define PPPAUTHTYPE_MSCHAP 0x04 +#define PPPAUTHTYPE_MSCHAP_V2 0x08 +#define PPPAUTHTYPE_EAP 0x10 +#define PPPAUTHTYPE_ANY 0xff +void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd); + +/* + * If set, peer is required to authenticate. This is mostly necessary for PPP server support. + * + * Default is false. + */ +#define ppp_set_auth_required(ppp, boolval) (ppp->settings.auth_required = boolval) +#endif /* PPP_AUTH_SUPPORT */ + +#if PPP_IPV4_SUPPORT +/* + * Set PPP interface "our" and "his" IPv4 addresses. This is mostly necessary for PPP server + * support but it can also be used on a PPP link where each side choose its own IP address. + * + * Default is unset (0.0.0.0). + */ +#define ppp_set_ipcp_ouraddr(ppp, addr) do { ppp->ipcp_wantoptions.ouraddr = ip4_addr_get_u32(addr); \ + ppp->ask_for_local = ppp->ipcp_wantoptions.ouraddr != 0; } while(0) +#define ppp_set_ipcp_hisaddr(ppp, addr) (ppp->ipcp_wantoptions.hisaddr = ip4_addr_get_u32(addr)) +#if LWIP_DNS +/* + * Set DNS server addresses that are sent if the peer asks for them. This is mostly necessary + * for PPP server support. + * + * Default is unset (0.0.0.0). + */ +#define ppp_set_ipcp_dnsaddr(ppp, index, addr) (ppp->ipcp_allowoptions.dnsaddr[index] = ip4_addr_get_u32(addr)) + +/* + * If set, we ask the peer for up to 2 DNS server addresses. Received DNS server addresses are + * registered using the dns_setserver() function. + * + * Default is false. + */ +#define ppp_set_usepeerdns(ppp, boolval) (ppp->settings.usepeerdns = boolval) +#endif /* LWIP_DNS */ +#endif /* PPP_IPV4_SUPPORT */ + +#if MPPE_SUPPORT +/* Disable MPPE (Microsoft Point to Point Encryption). This parameter is exclusive. */ +#define PPP_MPPE_DISABLE 0x00 +/* Require the use of MPPE (Microsoft Point to Point Encryption). */ +#define PPP_MPPE_ENABLE 0x01 +/* Allow MPPE to use stateful mode. Stateless mode is still attempted first. */ +#define PPP_MPPE_ALLOW_STATEFUL 0x02 +/* Refuse the use of MPPE with 40-bit encryption. Conflict with PPP_MPPE_REFUSE_128. */ +#define PPP_MPPE_REFUSE_40 0x04 +/* Refuse the use of MPPE with 128-bit encryption. Conflict with PPP_MPPE_REFUSE_40. */ +#define PPP_MPPE_REFUSE_128 0x08 +/* + * Set MPPE configuration + * + * Default is disabled. + */ +void ppp_set_mppe(ppp_pcb *pcb, u8_t flags); +#endif /* MPPE_SUPPORT */ + +/* + * Wait for up to intval milliseconds for a valid PPP packet from the peer. + * At the end of this time, or when a valid PPP packet is received from the + * peer, we commence negotiation by sending our first LCP packet. + * + * Default is 0. + */ +#define ppp_set_listen_time(ppp, intval) (ppp->settings.listen_time = intval) + +/* + * If set, we will attempt to initiate a connection but if no reply is received from + * the peer, we will then just wait passively for a valid LCP packet from the peer. + * + * Default is false. + */ +#define ppp_set_passive(ppp, boolval) (ppp->lcp_wantoptions.passive = boolval) + +/* + * If set, we will not transmit LCP packets to initiate a connection until a valid + * LCP packet is received from the peer. This is what we usually call the server mode. + * + * Default is false. + */ +#define ppp_set_silent(ppp, boolval) (ppp->lcp_wantoptions.silent = boolval) + +/* + * If set, enable protocol field compression negotiation in both the receive and + * the transmit direction. + * + * Default is true. + */ +#define ppp_set_neg_pcomp(ppp, boolval) (ppp->lcp_wantoptions.neg_pcompression = \ + ppp->lcp_allowoptions.neg_pcompression = boolval) + +/* + * If set, enable Address/Control compression in both the receive and the transmit + * direction. + * + * Default is true. + */ +#define ppp_set_neg_accomp(ppp, boolval) (ppp->lcp_wantoptions.neg_accompression = \ + ppp->lcp_allowoptions.neg_accompression = boolval) + +/* + * If set, enable asyncmap negotiation. Otherwise forcing all control characters to + * be escaped for both the transmit and the receive direction. + * + * Default is true. + */ +#define ppp_set_neg_asyncmap(ppp, boolval) (ppp->lcp_wantoptions.neg_asyncmap = \ + ppp->lcp_allowoptions.neg_asyncmap = boolval) + +/* + * This option sets the Async-Control-Character-Map (ACCM) for this end of the link. + * The ACCM is a set of 32 bits, one for each of the ASCII control characters with + * values from 0 to 31, where a 1 bit indicates that the corresponding control + * character should not be used in PPP packets sent to this system. The map is + * an unsigned 32 bits integer where the least significant bit (00000001) represents + * character 0 and the most significant bit (80000000) represents character 31. + * We will then ask the peer to send these characters as a 2-byte escape sequence. + * + * Default is 0. + */ +#define ppp_set_asyncmap(ppp, intval) (ppp->lcp_wantoptions.asyncmap = intval) + +/* + * Set a PPP interface as the default network interface + * (used to output all packets for which no specific route is found). + */ +#define ppp_set_default(ppp) netif_set_default(ppp->netif) + +#if PPP_NOTIFY_PHASE +/* + * Set a PPP notify phase callback. + * + * This can be used for example to set a LED pattern depending on the + * current phase of the PPP session. + */ +typedef void (*ppp_notify_phase_cb_fn)(ppp_pcb *pcb, u8_t phase, void *ctx); +void ppp_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb); +#endif /* PPP_NOTIFY_PHASE */ + +/* + * Initiate a PPP connection. + * + * This can only be called if PPP is in the dead phase. + * + * Holdoff is the time to wait (in seconds) before initiating + * the connection. + * + * If this port connects to a modem, the modem connection must be + * established before calling this. + */ +err_t ppp_connect(ppp_pcb *pcb, u16_t holdoff); + +#if PPP_SERVER +/* + * Listen for an incoming PPP connection. + * + * This can only be called if PPP is in the dead phase. + * + * If this port connects to a modem, the modem connection must be + * established before calling this. + */ +err_t ppp_listen(ppp_pcb *pcb); +#endif /* PPP_SERVER */ + +/* + * Initiate the end of a PPP connection. + * Any outstanding packets in the queues are dropped. + * + * Setting nocarrier to 1 close the PPP connection without initiating the + * shutdown procedure. Always using nocarrier = 0 is still recommended, + * this is going to take a little longer time if your link is down, but + * is a safer choice for the PPP state machine. + * + * Return 0 on success, an error code on failure. + */ +err_t ppp_close(ppp_pcb *pcb, u8_t nocarrier); + +/* + * Release the control block. + * + * This can only be called if PPP is in the dead phase. + * + * You must use ppp_close() before if you wish to terminate + * an established PPP session. + * + * Return 0 on success, an error code on failure. + */ +err_t ppp_free(ppp_pcb *pcb); + +/* + * PPP IOCTL commands. + * + * Get the up status - 0 for down, non-zero for up. The argument must + * point to an int. + */ +#define PPPCTLG_UPSTATUS 0 + +/* + * Get the PPP error code. The argument must point to an int. + * Returns a PPPERR_* value. + */ +#define PPPCTLG_ERRCODE 1 + +/* + * Get the fd associated with a PPP over serial + */ +#define PPPCTLG_FD 2 + +/* + * Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. + */ +err_t ppp_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg); + +/* Get the PPP netif interface */ +#define ppp_netif(ppp) (ppp->netif) + +/* Set an lwIP-style status-callback for the selected PPP device */ +#define ppp_set_netif_statuscallback(ppp, status_cb) \ + netif_set_status_callback(ppp->netif, status_cb); + +/* Set an lwIP-style link-callback for the selected PPP device */ +#define ppp_set_netif_linkcallback(ppp, link_cb) \ + netif_set_link_callback(ppp->netif, link_cb); + +#endif /* PPP_H */ + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp_impl.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp_impl.h new file mode 100644 index 0000000..1d4c774 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp_impl.h @@ -0,0 +1,629 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +#ifndef LWIP_HDR_PPP_IMPL_H +#define LWIP_HDR_PPP_IMPL_H + +#include "netif/ppp/ppp_opts.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifdef PPP_INCLUDE_SETTINGS_HEADER +#include "ppp_settings.h" +#endif + +#include /* formats */ +#include +#include +#include /* strtol() */ + +#include "lwip/netif.h" +#include "lwip/def.h" +#include "lwip/timeouts.h" + +#include "ppp.h" +#include "pppdebug.h" + +/* + * Memory used for control packets. + * + * PPP_CTRL_PBUF_MAX_SIZE is the amount of memory we allocate when we + * cannot figure out how much we are going to use before filling the buffer. + */ +#if PPP_USE_PBUF_RAM +#define PPP_CTRL_PBUF_TYPE PBUF_RAM +#define PPP_CTRL_PBUF_MAX_SIZE 512 +#else /* PPP_USE_PBUF_RAM */ +#define PPP_CTRL_PBUF_TYPE PBUF_POOL +#define PPP_CTRL_PBUF_MAX_SIZE PBUF_POOL_BUFSIZE +#endif /* PPP_USE_PBUF_RAM */ + +/* + * The basic PPP frame. + */ +#define PPP_ADDRESS(p) (((u_char *)(p))[0]) +#define PPP_CONTROL(p) (((u_char *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#if 0 /* UNUSED */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_IPX 0x2b /* IPX protocol */ +#endif /* UNUSED */ +#if VJ_SUPPORT +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#endif /* VJ_SUPPORT */ +#if PPP_IPV6_SUPPORT +#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */ +#endif /* PPP_IPV6_SUPPORT */ +#if CCP_SUPPORT +#define PPP_COMP 0xfd /* compressed packet */ +#endif /* CCP_SUPPORT */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#if 0 /* UNUSED */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_IPXCP 0x802b /* IPX Control Protocol */ +#endif /* UNUSED */ +#if PPP_IPV6_SUPPORT +#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */ +#endif /* PPP_IPV6_SUPPORT */ +#if CCP_SUPPORT +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#endif /* CCP_SUPPORT */ +#if ECP_SUPPORT +#define PPP_ECP 0x8053 /* Encryption Control Protocol */ +#endif /* ECP_SUPPORT */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#if PAP_SUPPORT +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#endif /* PAP_SUPPORT */ +#if LQR_SUPPORT +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#endif /* LQR_SUPPORT */ +#if CHAP_SUPPORT +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ +#endif /* CBCP_SUPPORT */ +#if EAP_SUPPORT +#define PPP_EAP 0xc227 /* Extensible Authentication Protocol */ +#endif /* EAP_SUPPORT */ + +/* + * The following struct gives the addresses of procedures to call + * for a particular lower link level protocol. + */ +struct link_callbacks { + /* Start a connection (e.g. Initiate discovery phase) */ + void (*connect) (ppp_pcb *pcb, void *ctx); +#if PPP_SERVER + /* Listen for an incoming connection (Passive mode) */ + void (*listen) (ppp_pcb *pcb, void *ctx); +#endif /* PPP_SERVER */ + /* End a connection (i.e. initiate disconnect phase) */ + void (*disconnect) (ppp_pcb *pcb, void *ctx); + /* Free lower protocol control block */ + err_t (*free) (ppp_pcb *pcb, void *ctx); + /* Write a pbuf to a ppp link, only used from PPP functions to send PPP packets. */ + err_t (*write)(ppp_pcb *pcb, void *ctx, struct pbuf *p); + /* Send a packet from lwIP core (IPv4 or IPv6) */ + err_t (*netif_output)(ppp_pcb *pcb, void *ctx, struct pbuf *p, u_short protocol); + /* configure the transmit-side characteristics of the PPP interface */ + void (*send_config)(ppp_pcb *pcb, void *ctx, u32_t accm, int pcomp, int accomp); + /* confire the receive-side characteristics of the PPP interface */ + void (*recv_config)(ppp_pcb *pcb, void *ctx, u32_t accm, int pcomp, int accomp); +}; + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Statistics. + */ +#if PPP_STATS_SUPPORT +struct pppstat { + unsigned int ppp_ibytes; /* bytes received */ + unsigned int ppp_ipackets; /* packets received */ + unsigned int ppp_ierrors; /* receive errors */ + unsigned int ppp_obytes; /* bytes sent */ + unsigned int ppp_opackets; /* packets sent */ + unsigned int ppp_oerrors; /* transmit errors */ +}; + +#if VJ_SUPPORT +struct vjstat { + unsigned int vjs_packets; /* outbound packets */ + unsigned int vjs_compressed; /* outbound compressed packets */ + unsigned int vjs_searches; /* searches for connection state */ + unsigned int vjs_misses; /* times couldn't find conn. state */ + unsigned int vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned int vjs_compressedin; /* inbound compressed packets */ + unsigned int vjs_errorin; /* inbound unknown type packets */ + unsigned int vjs_tossed; /* inbound packets tossed because of error */ +}; +#endif /* VJ_SUPPORT */ + +struct ppp_stats { + struct pppstat p; /* basic PPP statistics */ +#if VJ_SUPPORT + struct vjstat vj; /* VJ header compression statistics */ +#endif /* VJ_SUPPORT */ +}; + +#if CCP_SUPPORT +struct compstat { + unsigned int unc_bytes; /* total uncompressed bytes */ + unsigned int unc_packets; /* total uncompressed packets */ + unsigned int comp_bytes; /* compressed bytes */ + unsigned int comp_packets; /* compressed packets */ + unsigned int inc_bytes; /* incompressible bytes */ + unsigned int inc_packets; /* incompressible packets */ + unsigned int ratio; /* recent compression ratio << 8 */ +}; + +struct ppp_comp_stats { + struct compstat c; /* packet compression statistics */ + struct compstat d; /* packet decompression statistics */ +}; +#endif /* CCP_SUPPORT */ + +#endif /* PPP_STATS_SUPPORT */ + +#if PPP_IDLETIMELIMIT +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + time_t xmit_idle; /* time since last NP packet sent */ + time_t recv_idle; /* time since last NP packet received */ +}; +#endif /* PPP_IDLETIMELIMIT */ + +/* values for epdisc.class */ +#define EPD_NULL 0 /* null discriminator, no data */ +#define EPD_LOCAL 1 +#define EPD_IP 2 +#define EPD_MAC 3 +#define EPD_MAGIC 4 +#define EPD_PHONENUM 5 + +/* + * Global variables. + */ +#ifdef HAVE_MULTILINK +extern u8_t multilink; /* enable multilink operation */ +extern u8_t doing_multilink; +extern u8_t multilink_master; +extern u8_t bundle_eof; +extern u8_t bundle_terminating; +#endif + +#ifdef MAXOCTETS +extern unsigned int maxoctets; /* Maximum octetes per session (in bytes) */ +extern int maxoctets_dir; /* Direction : + 0 - in+out (default) + 1 - in + 2 - out + 3 - max(in,out) */ +extern int maxoctets_timeout; /* Timeout for check of octets limit */ +#define PPP_OCTETS_DIRECTION_SUM 0 +#define PPP_OCTETS_DIRECTION_IN 1 +#define PPP_OCTETS_DIRECTION_OUT 2 +#define PPP_OCTETS_DIRECTION_MAXOVERAL 3 +/* same as previos, but little different on RADIUS side */ +#define PPP_OCTETS_DIRECTION_MAXSESSION 4 +#endif + +/* Data input may be used by CCP and ECP, remove this entry + * from struct protent to save some flash + */ +#define PPP_DATAINPUT 0 + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (ppp_pcb *pcb); + /* Process a received packet */ + void (*input) (ppp_pcb *pcb, u_char *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (ppp_pcb *pcb); + /* Lower layer has come up */ + void (*lowerup) (ppp_pcb *pcb); + /* Lower layer has gone down */ + void (*lowerdown) (ppp_pcb *pcb); + /* Open the protocol */ + void (*open) (ppp_pcb *pcb); + /* Close the protocol */ + void (*close) (ppp_pcb *pcb, const char *reason); +#if PRINTPKT_SUPPORT + /* Print a packet in readable form */ + int (*printpkt) (const u_char *pkt, int len, + void (*printer) (void *, const char *, ...), + void *arg); +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + /* Process a received data packet */ + void (*datainput) (ppp_pcb *pcb, u_char *pkt, int len); +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + const char *name; /* Text name of protocol */ + const char *data_name; /* Text name of corresponding data protocol */ +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + option_t *options; /* List of command-line options */ + /* Check requested options, assign defaults */ + void (*check_options) (void); +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u_char *pkt, int len); +#endif /* DEMAND_SUPPORT */ +}; + +/* Table of pointers to supported protocols */ +extern const struct protent* const protocols[]; + + +/* Values for auth_pending, auth_done */ +#if PAP_SUPPORT +#define PAP_WITHPEER 0x1 +#define PAP_PEER 0x2 +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#define CHAP_WITHPEER 0x4 +#define CHAP_PEER 0x8 +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT +#define EAP_WITHPEER 0x10 +#define EAP_PEER 0x20 +#endif /* EAP_SUPPORT */ + +/* Values for auth_done only */ +#if CHAP_SUPPORT +#define CHAP_MD5_WITHPEER 0x40 +#define CHAP_MD5_PEER 0x80 +#if MSCHAP_SUPPORT +#define CHAP_MS_SHIFT 8 /* LSB position for MS auths */ +#define CHAP_MS_WITHPEER 0x100 +#define CHAP_MS_PEER 0x200 +#define CHAP_MS2_WITHPEER 0x400 +#define CHAP_MS2_PEER 0x800 +#endif /* MSCHAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ + +/* Supported CHAP protocols */ +#if CHAP_SUPPORT + +#if MSCHAP_SUPPORT +#define CHAP_MDTYPE_SUPPORTED (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5) +#else /* MSCHAP_SUPPORT */ +#define CHAP_MDTYPE_SUPPORTED (MDTYPE_MD5) +#endif /* MSCHAP_SUPPORT */ + +#else /* CHAP_SUPPORT */ +#define CHAP_MDTYPE_SUPPORTED (MDTYPE_NONE) +#endif /* CHAP_SUPPORT */ + +#if PPP_STATS_SUPPORT +/* + * PPP statistics structure + */ +struct pppd_stats { + unsigned int bytes_in; + unsigned int bytes_out; + unsigned int pkts_in; + unsigned int pkts_out; +}; +#endif /* PPP_STATS_SUPPORT */ + + +/* + * PPP private functions + */ + + +/* + * Functions called from lwIP core. + */ + +/* initialize the PPP subsystem */ +int ppp_init(void); + +/* + * Functions called from PPP link protocols. + */ + +/* Create a new PPP control block */ +ppp_pcb *ppp_new(struct netif *pppif, const struct link_callbacks *callbacks, void *link_ctx_cb, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); + +/* Initiate LCP open request */ +void ppp_start(ppp_pcb *pcb); + +/* Called when link failed to setup */ +void ppp_link_failed(ppp_pcb *pcb); + +/* Called when link is normally down (i.e. it was asked to end) */ +void ppp_link_end(ppp_pcb *pcb); + +/* function called to process input packet */ +void ppp_input(ppp_pcb *pcb, struct pbuf *pb); + +/* helper function, merge a pbuf chain into one pbuf */ +struct pbuf *ppp_singlebuf(struct pbuf *p); + + +/* + * Functions called by PPP protocols. + */ + +/* function called by all PPP subsystems to send packets */ +err_t ppp_write(ppp_pcb *pcb, struct pbuf *p); + +/* functions called by auth.c link_terminated() */ +void ppp_link_terminated(ppp_pcb *pcb); + +void new_phase(ppp_pcb *pcb, int p); + +int ppp_send_config(ppp_pcb *pcb, int mtu, u32_t accm, int pcomp, int accomp); +int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp); + +#if PPP_IPV4_SUPPORT +int sifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr, u32_t netmask); +int cifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr); +#if 0 /* UNUSED - PROXY ARP */ +int sifproxyarp(ppp_pcb *pcb, u32_t his_adr); +int cifproxyarp(ppp_pcb *pcb, u32_t his_adr); +#endif /* UNUSED - PROXY ARP */ +#if LWIP_DNS +int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2); +int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2); +#endif /* LWIP_DNS */ +#if VJ_SUPPORT +int sifvjcomp(ppp_pcb *pcb, int vjcomp, int cidcomp, int maxcid); +#endif /* VJ_SUPPORT */ +int sifup(ppp_pcb *pcb); +int sifdown (ppp_pcb *pcb); +u32_t get_mask(u32_t addr); +#endif /* PPP_IPV4_SUPPORT */ + +#if PPP_IPV6_SUPPORT +int sif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64); +int cif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64); +int sif6up(ppp_pcb *pcb); +int sif6down (ppp_pcb *pcb); +#endif /* PPP_IPV6_SUPPORT */ + +#if DEMAND_SUPPORT +int sifnpmode(ppp_pcb *pcb, int proto, enum NPmode mode); +#endif /* DEMAND_SUPPORt */ + +void netif_set_mtu(ppp_pcb *pcb, int mtu); +int netif_get_mtu(ppp_pcb *pcb); + +#if CCP_SUPPORT +#if 0 /* unused */ +int ccp_test(ppp_pcb *pcb, u_char *opt_ptr, int opt_len, int for_transmit); +#endif /* unused */ +void ccp_set(ppp_pcb *pcb, u8_t isopen, u8_t isup, u8_t receive_method, u8_t transmit_method); +void ccp_reset_comp(ppp_pcb *pcb); +void ccp_reset_decomp(ppp_pcb *pcb); +#if 0 /* unused */ +int ccp_fatal_error(ppp_pcb *pcb); +#endif /* unused */ +#endif /* CCP_SUPPORT */ + +#if PPP_IDLETIMELIMIT +int get_idle_time(ppp_pcb *pcb, struct ppp_idle *ip); +#endif /* PPP_IDLETIMELIMIT */ + +#if DEMAND_SUPPORT +int get_loop_output(void); +#endif /* DEMAND_SUPPORT */ + +/* Optional protocol names list, to make our messages a little more informative. */ +#if PPP_PROTOCOLNAME +const char * protocol_name(int proto); +#endif /* PPP_PROTOCOLNAME */ + +/* Optional stats support, to get some statistics on the PPP interface */ +#if PPP_STATS_SUPPORT +void print_link_stats(void); /* Print stats, if available */ +void reset_link_stats(int u); /* Reset (init) stats when link goes up */ +void update_link_stats(int u); /* Get stats at link termination */ +#endif /* PPP_STATS_SUPPORT */ + + + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s); \ +} +#define GETLONG(l, cp) { \ + (l) = *(cp)++ << 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +/* + * System dependent definitions for user-level 4.3BSD UNIX implementation. + */ +#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0) +#define TIMEOUTMS(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t), (f), (a)); } while(0) +#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) + +#define BZERO(s, n) memset(s, 0, n) +#define BCMP(s1, s2, l) memcmp(s1, s2, l) + +#define PRINTMSG(m, l) { ppp_info("Remote message: %0.*v", l, m); } + +/* + * MAKEHEADER - Add Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/* Procedures exported from auth.c */ +void link_required(ppp_pcb *pcb); /* we are starting to use the link */ +void link_terminated(ppp_pcb *pcb); /* we are finished with the link */ +void link_down(ppp_pcb *pcb); /* the LCP layer has left the Opened state */ +void upper_layers_down(ppp_pcb *pcb); /* take all NCPs down */ +void link_established(ppp_pcb *pcb); /* the link is up; authenticate now */ +void start_networks(ppp_pcb *pcb); /* start all the network control protos */ +void continue_networks(ppp_pcb *pcb); /* start network [ip, etc] control protos */ +#if PPP_AUTH_SUPPORT +#if PPP_SERVER +int auth_check_passwd(ppp_pcb *pcb, char *auser, int userlen, char *apasswd, int passwdlen, const char **msg, int *msglen); + /* check the user name and passwd against configuration */ +void auth_peer_fail(ppp_pcb *pcb, int protocol); + /* peer failed to authenticate itself */ +void auth_peer_success(ppp_pcb *pcb, int protocol, int prot_flavor, const char *name, int namelen); + /* peer successfully authenticated itself */ +#endif /* PPP_SERVER */ +void auth_withpeer_fail(ppp_pcb *pcb, int protocol); + /* we failed to authenticate ourselves */ +void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor); + /* we successfully authenticated ourselves */ +#endif /* PPP_AUTH_SUPPORT */ +void np_up(ppp_pcb *pcb, int proto); /* a network protocol has come up */ +void np_down(ppp_pcb *pcb, int proto); /* a network protocol has gone down */ +void np_finished(ppp_pcb *pcb, int proto); /* a network protocol no longer needs link */ +#if PPP_AUTH_SUPPORT +int get_secret(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server); + /* get "secret" for chap */ +#endif /* PPP_AUTH_SUPPORT */ + +/* Procedures exported from ipcp.c */ +/* int parse_dotted_ip (char *, u32_t *); */ + +/* Procedures exported from demand.c */ +#if DEMAND_SUPPORT +void demand_conf (void); /* config interface(s) for demand-dial */ +void demand_block (void); /* set all NPs to queue up packets */ +void demand_unblock (void); /* set all NPs to pass packets */ +void demand_discard (void); /* set all NPs to discard packets */ +void demand_rexmit (int, u32_t); /* retransmit saved frames for an NP*/ +int loop_chars (unsigned char *, int); /* process chars from loopback */ +int loop_frame (unsigned char *, int); /* should we bring link up? */ +#endif /* DEMAND_SUPPORT */ + +/* Procedures exported from multilink.c */ +#ifdef HAVE_MULTILINK +void mp_check_options (void); /* Check multilink-related options */ +int mp_join_bundle (void); /* join our link to an appropriate bundle */ +void mp_exit_bundle (void); /* have disconnected our link from bundle */ +void mp_bundle_terminated (void); +char *epdisc_to_str (struct epdisc *); /* string from endpoint discrim. */ +int str_to_epdisc (struct epdisc *, char *); /* endpt disc. from str */ +#else +#define mp_bundle_terminated() /* nothing */ +#define mp_exit_bundle() /* nothing */ +#define doing_multilink 0 +#define multilink_master 0 +#endif + +/* Procedures exported from utils.c. */ +void ppp_print_string(const u_char *p, int len, void (*printer) (void *, const char *, ...), void *arg); /* Format a string for output */ +int ppp_slprintf(char *buf, int buflen, const char *fmt, ...); /* sprintf++ */ +int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args); /* vsprintf++ */ +size_t ppp_strlcpy(char *dest, const char *src, size_t len); /* safe strcpy */ +size_t ppp_strlcat(char *dest, const char *src, size_t len); /* safe strncpy */ +void ppp_dbglog(const char *fmt, ...); /* log a debug message */ +void ppp_info(const char *fmt, ...); /* log an informational message */ +void ppp_notice(const char *fmt, ...); /* log a notice-level message */ +void ppp_warn(const char *fmt, ...); /* log a warning message */ +void ppp_error(const char *fmt, ...); /* log an error message */ +void ppp_fatal(const char *fmt, ...); /* log an error message and die(1) */ +#if PRINTPKT_SUPPORT +void ppp_dump_packet(ppp_pcb *pcb, const char *tag, unsigned char *p, int len); + /* dump packet to debug log if interesting */ +#endif /* PRINTPKT_SUPPORT */ + + +#endif /* PPP_SUPPORT */ +#endif /* LWIP_HDR_PPP_IMPL_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp_opts.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp_opts.h new file mode 100644 index 0000000..fa79c09 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/ppp_opts.h @@ -0,0 +1,593 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef LWIP_PPP_OPTS_H +#define LWIP_PPP_OPTS_H + +#include "lwip/opt.h" + +/** + * PPP_SUPPORT==1: Enable PPP. + */ +#ifndef PPP_SUPPORT +#define PPP_SUPPORT 0 +#endif + +/** + * PPPOE_SUPPORT==1: Enable PPP Over Ethernet + */ +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 +#endif + +/** + * PPPOL2TP_SUPPORT==1: Enable PPP Over L2TP + */ +#ifndef PPPOL2TP_SUPPORT +#define PPPOL2TP_SUPPORT 0 +#endif + +/** + * PPPOL2TP_AUTH_SUPPORT==1: Enable PPP Over L2TP Auth (enable MD5 support) + */ +#ifndef PPPOL2TP_AUTH_SUPPORT +#define PPPOL2TP_AUTH_SUPPORT PPPOL2TP_SUPPORT +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT +#endif + +/** + * LWIP_PPP_API==1: Enable PPP API (in pppapi.c) + */ +#ifndef LWIP_PPP_API +#define LWIP_PPP_API (PPP_SUPPORT && (NO_SYS == 0)) +#endif + +/** + * MEMP_NUM_PPP_PCB: the number of simultaneously active PPP + * connections (requires the PPP_SUPPORT option) + */ +#ifndef MEMP_NUM_PPP_PCB +#define MEMP_NUM_PPP_PCB 1 +#endif + +#if PPP_SUPPORT + +/** + * MEMP_NUM_PPPOS_INTERFACES: the number of concurrently active PPPoS + * interfaces (only used with PPPOS_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOS_INTERFACES +#define MEMP_NUM_PPPOS_INTERFACES MEMP_NUM_PPP_PCB +#endif + +/** + * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE + * interfaces (only used with PPPOE_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOE_INTERFACES +#define MEMP_NUM_PPPOE_INTERFACES 1 +#endif + +/** + * MEMP_NUM_PPPOL2TP_INTERFACES: the number of concurrently active PPPoL2TP + * interfaces (only used with PPPOL2TP_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOL2TP_INTERFACES +#define MEMP_NUM_PPPOL2TP_INTERFACES 1 +#endif + +/** + * MEMP_NUM_PPP_API_MSG: Number of concurrent PPP API messages (in pppapi.c) + */ +#ifndef MEMP_NUM_PPP_API_MSG +#define MEMP_NUM_PPP_API_MSG 5 +#endif + +/** + * PPP_DEBUG: Enable debugging for PPP. + */ +#ifndef PPP_DEBUG +#define PPP_DEBUG LWIP_DBG_OFF +#endif + +/** + * PPP_INPROC_IRQ_SAFE==1 call pppos_input() using tcpip_callback(). + * + * Please read the "PPPoS input path" chapter in the PPP documentation about this option. + */ +#ifndef PPP_INPROC_IRQ_SAFE +#define PPP_INPROC_IRQ_SAFE 0 +#endif + +/** + * PRINTPKT_SUPPORT==1: Enable PPP print packet support + * + * Mandatory for debugging, it displays exchanged packet content in debug trace. + */ +#ifndef PRINTPKT_SUPPORT +#define PRINTPKT_SUPPORT 0 +#endif + +/** + * PPP_IPV4_SUPPORT==1: Enable PPP IPv4 support + */ +#ifndef PPP_IPV4_SUPPORT +#define PPP_IPV4_SUPPORT (LWIP_IPV4) +#endif + +/** + * PPP_IPV6_SUPPORT==1: Enable PPP IPv6 support + */ +#ifndef PPP_IPV6_SUPPORT +#define PPP_IPV6_SUPPORT (LWIP_IPV6) +#endif + +/** + * PPP_NOTIFY_PHASE==1: Support PPP notify phase support + * + * PPP notify phase support allows you to set a callback which is + * called on change of the internal PPP state machine. + * + * This can be used for example to set a LED pattern depending on the + * current phase of the PPP session. + */ +#ifndef PPP_NOTIFY_PHASE +#define PPP_NOTIFY_PHASE 0 +#endif + +/** + * pbuf_type PPP is using for LCP, PAP, CHAP, EAP, CCP, IPCP and IP6CP packets. + * + * Memory allocated must be single buffered for PPP to works, it requires pbuf + * that are not going to be chained when allocated. This requires setting + * PBUF_POOL_BUFSIZE to at least 512 bytes, which is quite huge for small systems. + * + * Setting PPP_USE_PBUF_RAM to 1 makes PPP use memory from heap where continuous + * buffers are required, allowing you to use a smaller PBUF_POOL_BUFSIZE. + */ +#ifndef PPP_USE_PBUF_RAM +#define PPP_USE_PBUF_RAM 0 +#endif + +/** + * PPP_FCS_TABLE: Keep a 256*2 byte table to speed up FCS calculation for PPPoS + */ +#ifndef PPP_FCS_TABLE +#define PPP_FCS_TABLE 1 +#endif + +/** + * PAP_SUPPORT==1: Support PAP. + */ +#ifndef PAP_SUPPORT +#define PAP_SUPPORT 0 +#endif + +/** + * CHAP_SUPPORT==1: Support CHAP. + */ +#ifndef CHAP_SUPPORT +#define CHAP_SUPPORT 0 +#endif + +/** + * MSCHAP_SUPPORT==1: Support MSCHAP. + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 +#endif +#if MSCHAP_SUPPORT +/* MSCHAP requires CHAP support */ +#undef CHAP_SUPPORT +#define CHAP_SUPPORT 1 +#endif /* MSCHAP_SUPPORT */ + +/** + * EAP_SUPPORT==1: Support EAP. + */ +#ifndef EAP_SUPPORT +#define EAP_SUPPORT 0 +#endif + +/** + * CCP_SUPPORT==1: Support CCP. + */ +#ifndef CCP_SUPPORT +#define CCP_SUPPORT 0 +#endif + +/** + * MPPE_SUPPORT==1: Support MPPE. + */ +#ifndef MPPE_SUPPORT +#define MPPE_SUPPORT 0 +#endif +#if MPPE_SUPPORT +/* MPPE requires CCP support */ +#undef CCP_SUPPORT +#define CCP_SUPPORT 1 +/* MPPE requires MSCHAP support */ +#undef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 1 +/* MSCHAP requires CHAP support */ +#undef CHAP_SUPPORT +#define CHAP_SUPPORT 1 +#endif /* MPPE_SUPPORT */ + +/** + * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CBCP_SUPPORT +#define CBCP_SUPPORT 0 +#endif + +/** + * ECP_SUPPORT==1: Support ECP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef ECP_SUPPORT +#define ECP_SUPPORT 0 +#endif + +/** + * DEMAND_SUPPORT==1: Support dial on demand. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef DEMAND_SUPPORT +#define DEMAND_SUPPORT 0 +#endif + +/** + * LQR_SUPPORT==1: Support Link Quality Report. Do nothing except exchanging some LCP packets. + */ +#ifndef LQR_SUPPORT +#define LQR_SUPPORT 0 +#endif + +/** + * PPP_SERVER==1: Enable PPP server support (waiting for incoming PPP session). + * + * Currently only supported for PPPoS. + */ +#ifndef PPP_SERVER +#define PPP_SERVER 0 +#endif + +#if PPP_SERVER +/* + * PPP_OUR_NAME: Our name for authentication purposes + */ +#ifndef PPP_OUR_NAME +#define PPP_OUR_NAME "lwIP" +#endif +#endif /* PPP_SERVER */ + +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 1 +#endif +/* VJ compression is only supported for TCP over IPv4 over PPPoS. */ +#if !PPPOS_SUPPORT || !PPP_IPV4_SUPPORT || !LWIP_TCP +#undef VJ_SUPPORT +#define VJ_SUPPORT 0 +#endif /* !PPPOS_SUPPORT */ + +/** + * PPP_MD5_RANDM==1: Use MD5 for better randomness. + * Enabled by default if CHAP, EAP, or L2TP AUTH support is enabled. + */ +#ifndef PPP_MD5_RANDM +#define PPP_MD5_RANDM (CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT) +#endif + +/** + * PolarSSL embedded library + * + * + * lwIP contains some files fetched from the latest BSD release of + * the PolarSSL project (PolarSSL 0.10.1-bsd) for ciphers and encryption + * methods we need for lwIP PPP support. + * + * The PolarSSL files were cleaned to contain only the necessary struct + * fields and functions needed for lwIP. + * + * The PolarSSL API was not changed at all, so if you are already using + * PolarSSL you can choose to skip the compilation of the included PolarSSL + * library into lwIP. + * + * If you are not using the embedded copy you must include external + * libraries into your arch/cc.h port file. + * + * Beware of the stack requirements which can be a lot larger if you are not + * using our cleaned PolarSSL library. + */ + +/** + * LWIP_USE_EXTERNAL_POLARSSL: Use external PolarSSL library + */ +#ifndef LWIP_USE_EXTERNAL_POLARSSL +#define LWIP_USE_EXTERNAL_POLARSSL 0 +#endif + +/** + * LWIP_USE_EXTERNAL_MBEDTLS: Use external mbed TLS library + */ +#ifndef LWIP_USE_EXTERNAL_MBEDTLS +#define LWIP_USE_EXTERNAL_MBEDTLS 0 +#endif + +/* + * PPP Timeouts + */ + +/** + * FSM_DEFTIMEOUT: Timeout time in seconds + */ +#ifndef FSM_DEFTIMEOUT +#define FSM_DEFTIMEOUT 6 +#endif + +/** + * FSM_DEFMAXTERMREQS: Maximum Terminate-Request transmissions + */ +#ifndef FSM_DEFMAXTERMREQS +#define FSM_DEFMAXTERMREQS 2 +#endif + +/** + * FSM_DEFMAXCONFREQS: Maximum Configure-Request transmissions + */ +#ifndef FSM_DEFMAXCONFREQS +#define FSM_DEFMAXCONFREQS 10 +#endif + +/** + * FSM_DEFMAXNAKLOOPS: Maximum number of nak loops + */ +#ifndef FSM_DEFMAXNAKLOOPS +#define FSM_DEFMAXNAKLOOPS 5 +#endif + +/** + * UPAP_DEFTIMEOUT: Timeout (seconds) for retransmitting req + */ +#ifndef UPAP_DEFTIMEOUT +#define UPAP_DEFTIMEOUT 6 +#endif + +/** + * UPAP_DEFTRANSMITS: Maximum number of auth-reqs to send + */ +#ifndef UPAP_DEFTRANSMITS +#define UPAP_DEFTRANSMITS 10 +#endif + +#if PPP_SERVER +/** + * UPAP_DEFREQTIME: Time to wait for auth-req from peer + */ +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 +#endif +#endif /* PPP_SERVER */ + +/** + * CHAP_DEFTIMEOUT: Timeout (seconds) for retransmitting req + */ +#ifndef CHAP_DEFTIMEOUT +#define CHAP_DEFTIMEOUT 6 +#endif + +/** + * CHAP_DEFTRANSMITS: max # times to send challenge + */ +#ifndef CHAP_DEFTRANSMITS +#define CHAP_DEFTRANSMITS 10 +#endif + +#if PPP_SERVER +/** + * CHAP_DEFRECHALLENGETIME: If this option is > 0, rechallenge the peer every n seconds + */ +#ifndef CHAP_DEFRECHALLENGETIME +#define CHAP_DEFRECHALLENGETIME 0 +#endif +#endif /* PPP_SERVER */ + +/** + * EAP_DEFREQTIME: Time to wait for peer request + */ +#ifndef EAP_DEFREQTIME +#define EAP_DEFREQTIME 6 +#endif + +/** + * EAP_DEFALLOWREQ: max # times to accept requests + */ +#ifndef EAP_DEFALLOWREQ +#define EAP_DEFALLOWREQ 10 +#endif + +#if PPP_SERVER +/** + * EAP_DEFTIMEOUT: Timeout (seconds) for rexmit + */ +#ifndef EAP_DEFTIMEOUT +#define EAP_DEFTIMEOUT 6 +#endif + +/** + * EAP_DEFTRANSMITS: max # times to transmit + */ +#ifndef EAP_DEFTRANSMITS +#define EAP_DEFTRANSMITS 10 +#endif +#endif /* PPP_SERVER */ + +/** + * LCP_DEFLOOPBACKFAIL: Default number of times we receive our magic number from the peer + * before deciding the link is looped-back. + */ +#ifndef LCP_DEFLOOPBACKFAIL +#define LCP_DEFLOOPBACKFAIL 10 +#endif + +/** + * LCP_ECHOINTERVAL: Interval in seconds between keepalive echo requests, 0 to disable. + */ +#ifndef LCP_ECHOINTERVAL +#define LCP_ECHOINTERVAL 0 +#endif + +/** + * LCP_MAXECHOFAILS: Number of unanswered echo requests before failure. + */ +#ifndef LCP_MAXECHOFAILS +#define LCP_MAXECHOFAILS 3 +#endif + +/** + * PPP_MAXIDLEFLAG: Max Xmit idle time (in ms) before resend flag char. + */ +#ifndef PPP_MAXIDLEFLAG +#define PPP_MAXIDLEFLAG 100 +#endif + +/** + * PPP Packet sizes + */ + +/** + * PPP_MRU: Default MRU + */ +#ifndef PPP_MRU +#define PPP_MRU 1500 +#endif + +/** + * PPP_DEFMRU: Default MRU to try + */ +#ifndef PPP_DEFMRU +#define PPP_DEFMRU 1500 +#endif + +/** + * PPP_MAXMRU: Normally limit MRU to this (pppd default = 16384) + */ +#ifndef PPP_MAXMRU +#define PPP_MAXMRU 1500 +#endif + +/** + * PPP_MINMRU: No MRUs below this + */ +#ifndef PPP_MINMRU +#define PPP_MINMRU 128 +#endif + +/** + * PPPOL2TP_DEFMRU: Default MTU and MRU for L2TP + * Default = 1500 - PPPoE(6) - PPP Protocol(2) - IPv4 header(20) - UDP Header(8) + * - L2TP Header(6) - HDLC Header(2) - PPP Protocol(2) - MPPE Header(2) - PPP Protocol(2) + */ +#if PPPOL2TP_SUPPORT +#ifndef PPPOL2TP_DEFMRU +#define PPPOL2TP_DEFMRU 1450 +#endif +#endif /* PPPOL2TP_SUPPORT */ + +/** + * MAXNAMELEN: max length of hostname or name for auth + */ +#ifndef MAXNAMELEN +#define MAXNAMELEN 256 +#endif + +/** + * MAXSECRETLEN: max length of password or secret + */ +#ifndef MAXSECRETLEN +#define MAXSECRETLEN 256 +#endif + +/* ------------------------------------------------------------------------- */ + +/* + * Build triggers for embedded PolarSSL + */ +#if !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS + +/* CHAP, EAP, L2TP AUTH and MD5 Random require MD5 support */ +#if CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT || PPP_MD5_RANDM +#define LWIP_INCLUDED_POLARSSL_MD5 1 +#endif /* CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT || PPP_MD5_RANDM */ + +#if MSCHAP_SUPPORT + +/* MSCHAP require MD4 support */ +#define LWIP_INCLUDED_POLARSSL_MD4 1 +/* MSCHAP require SHA1 support */ +#define LWIP_INCLUDED_POLARSSL_SHA1 1 +/* MSCHAP require DES support */ +#define LWIP_INCLUDED_POLARSSL_DES 1 + +/* MS-CHAP support is required for MPPE */ +#if MPPE_SUPPORT +/* MPPE require ARC4 support */ +#define LWIP_INCLUDED_POLARSSL_ARC4 1 +#endif /* MPPE_SUPPORT */ + +#endif /* MSCHAP_SUPPORT */ + +#endif /* !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS */ + +/* Default value if unset */ +#ifndef LWIP_INCLUDED_POLARSSL_MD4 +#define LWIP_INCLUDED_POLARSSL_MD4 0 +#endif /* LWIP_INCLUDED_POLARSSL_MD4 */ +#ifndef LWIP_INCLUDED_POLARSSL_MD5 +#define LWIP_INCLUDED_POLARSSL_MD5 0 +#endif /* LWIP_INCLUDED_POLARSSL_MD5 */ +#ifndef LWIP_INCLUDED_POLARSSL_SHA1 +#define LWIP_INCLUDED_POLARSSL_SHA1 0 +#endif /* LWIP_INCLUDED_POLARSSL_SHA1 */ +#ifndef LWIP_INCLUDED_POLARSSL_DES +#define LWIP_INCLUDED_POLARSSL_DES 0 +#endif /* LWIP_INCLUDED_POLARSSL_DES */ +#ifndef LWIP_INCLUDED_POLARSSL_ARC4 +#define LWIP_INCLUDED_POLARSSL_ARC4 0 +#endif /* LWIP_INCLUDED_POLARSSL_ARC4 */ + +#endif /* PPP_SUPPORT */ + +#endif /* LWIP_PPP_OPTS_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppapi.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppapi.h new file mode 100644 index 0000000..913d93f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppapi.h @@ -0,0 +1,137 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef LWIP_PPPAPI_H +#define LWIP_PPPAPI_H + +#include "netif/ppp/ppp_opts.h" + +#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "lwip/priv/tcpip_priv.h" +#include "netif/ppp/ppp.h" +#if PPPOS_SUPPORT +#include "netif/ppp/pppos.h" +#endif /* PPPOS_SUPPORT */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct pppapi_msg_msg { + ppp_pcb *ppp; + union { +#if PPP_NOTIFY_PHASE + struct { + ppp_notify_phase_cb_fn notify_phase_cb; + } setnotifyphasecb; +#endif /* PPP_NOTIFY_PHASE */ +#if PPPOS_SUPPORT + struct { + struct netif *pppif; + pppos_output_cb_fn output_cb; + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } serialcreate; +#endif /* PPPOS_SUPPORT */ +#if PPPOE_SUPPORT + struct { + struct netif *pppif; + struct netif *ethif; + const char *service_name; + const char *concentrator_name; + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } ethernetcreate; +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT + struct { + struct netif *pppif; + struct netif *netif; + API_MSG_M_DEF_C(ip_addr_t, ipaddr); + u16_t port; +#if PPPOL2TP_AUTH_SUPPORT + const u8_t *secret; + u8_t secret_len; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } l2tpcreate; +#endif /* PPPOL2TP_SUPPORT */ + struct { + u16_t holdoff; + } connect; + struct { + u8_t nocarrier; + } close; + struct { + u8_t cmd; + void *arg; + } ioctl; + } msg; +}; + +struct pppapi_msg { + struct tcpip_api_call_data call; + struct pppapi_msg_msg msg; +}; + +/* API for application */ +err_t pppapi_set_default(ppp_pcb *pcb); +#if PPP_NOTIFY_PHASE +err_t pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb); +#endif /* PPP_NOTIFY_PHASE */ +#if PPPOS_SUPPORT +ppp_pcb *pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb, ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOS_SUPPORT */ +#if PPPOE_SUPPORT +ppp_pcb *pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name, + const char *concentrator_name, ppp_link_status_cb_fn link_status_cb, + void *ctx_cb); +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT +ppp_pcb *pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port, + const u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOL2TP_SUPPORT */ +err_t pppapi_connect(ppp_pcb *pcb, u16_t holdoff); +#if PPP_SERVER +err_t pppapi_listen(ppp_pcb *pcb); +#endif /* PPP_SERVER */ +err_t pppapi_close(ppp_pcb *pcb, u8_t nocarrier); +err_t pppapi_free(ppp_pcb *pcb); +err_t pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_PPP_API */ + +#endif /* LWIP_PPPAPI_H */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppcrypt.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppcrypt.h new file mode 100644 index 0000000..a7b2099 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppcrypt.h @@ -0,0 +1,136 @@ +/* + * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1 + * + * Extracted from chap_ms.c by James Carlson. + * + * Copyright (c) 1995 Eric Rosenquist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* This header file is included in all PPP modules needing hashes and/or ciphers */ + +#ifndef PPPCRYPT_H +#define PPPCRYPT_H + +/* + * If included PolarSSL copy is not used, user is expected to include + * external libraries in arch/cc.h (which is included by lwip/arch.h). + */ +#include "lwip/arch.h" + +/* + * Map hashes and ciphers functions to PolarSSL + */ +#if !LWIP_USE_EXTERNAL_MBEDTLS + +#include "netif/ppp/polarssl/md4.h" +#define lwip_md4_context md4_context +#define lwip_md4_init(context) +#define lwip_md4_starts md4_starts +#define lwip_md4_update md4_update +#define lwip_md4_finish md4_finish +#define lwip_md4_free(context) + +#include "netif/ppp/polarssl/md5.h" +#define lwip_md5_context md5_context +#define lwip_md5_init(context) +#define lwip_md5_starts md5_starts +#define lwip_md5_update md5_update +#define lwip_md5_finish md5_finish +#define lwip_md5_free(context) + +#include "netif/ppp/polarssl/sha1.h" +#define lwip_sha1_context sha1_context +#define lwip_sha1_init(context) +#define lwip_sha1_starts sha1_starts +#define lwip_sha1_update sha1_update +#define lwip_sha1_finish sha1_finish +#define lwip_sha1_free(context) + +#include "netif/ppp/polarssl/des.h" +#define lwip_des_context des_context +#define lwip_des_init(context) +#define lwip_des_setkey_enc des_setkey_enc +#define lwip_des_crypt_ecb des_crypt_ecb +#define lwip_des_free(context) + +#include "netif/ppp/polarssl/arc4.h" +#define lwip_arc4_context arc4_context +#define lwip_arc4_init(context) +#define lwip_arc4_setup arc4_setup +#define lwip_arc4_crypt arc4_crypt +#define lwip_arc4_free(context) + +#endif /* !LWIP_USE_EXTERNAL_MBEDTLS */ + +/* + * Map hashes and ciphers functions to mbed TLS + */ +#if LWIP_USE_EXTERNAL_MBEDTLS + +#define lwip_md4_context mbedtls_md4_context +#define lwip_md4_init mbedtls_md4_init +#define lwip_md4_starts mbedtls_md4_starts +#define lwip_md4_update mbedtls_md4_update +#define lwip_md4_finish mbedtls_md4_finish +#define lwip_md4_free mbedtls_md4_free + +#define lwip_md5_context mbedtls_md5_context +#define lwip_md5_init mbedtls_md5_init +#define lwip_md5_starts mbedtls_md5_starts +#define lwip_md5_update mbedtls_md5_update +#define lwip_md5_finish mbedtls_md5_finish +#define lwip_md5_free mbedtls_md5_free + +#define lwip_sha1_context mbedtls_sha1_context +#define lwip_sha1_init mbedtls_sha1_init +#define lwip_sha1_starts mbedtls_sha1_starts +#define lwip_sha1_update mbedtls_sha1_update +#define lwip_sha1_finish mbedtls_sha1_finish +#define lwip_sha1_free mbedtls_sha1_free + +#define lwip_des_context mbedtls_des_context +#define lwip_des_init mbedtls_des_init +#define lwip_des_setkey_enc mbedtls_des_setkey_enc +#define lwip_des_crypt_ecb mbedtls_des_crypt_ecb +#define lwip_des_free mbedtls_des_free + +#define lwip_arc4_context mbedtls_arc4_context +#define lwip_arc4_init mbedtls_arc4_init +#define lwip_arc4_setup mbedtls_arc4_setup +#define lwip_arc4_crypt(context, buffer, length) mbedtls_arc4_crypt(context, length, buffer, buffer) +#define lwip_arc4_free mbedtls_arc4_free + +#endif /* LWIP_USE_EXTERNAL_MBEDTLS */ + +void pppcrypt_56_to_64_bit_key(u_char *key, u_char *des_key); + +#endif /* PPPCRYPT_H */ + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppdebug.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppdebug.h new file mode 100644 index 0000000..7ead045 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppdebug.h @@ -0,0 +1,80 @@ +/***************************************************************************** +* pppdebug.h - System debugging utilities. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* portions Copyright (c) 2001 by Cognizant Pty Ltd. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY (please don't use tabs!) +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-07-29 Guy Lancaster , Global Election Systems Inc. +* Original. +* +***************************************************************************** +*/ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPPDEBUG_H +#define PPPDEBUG_H + +/* Trace levels. */ +#define LOG_CRITICAL (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_INFO (PPP_DEBUG) +#define LOG_DETAIL (PPP_DEBUG) +#define LOG_DEBUG (PPP_DEBUG) + +#if PPP_DEBUG + +#define MAINDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define SYSDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define FSMDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define LCPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define IPCPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define IPV6CPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define UPAPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define CHAPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b) + +#else /* PPP_DEBUG */ + +#define MAINDEBUG(a) +#define SYSDEBUG(a) +#define FSMDEBUG(a) +#define LCPDEBUG(a) +#define IPCPDEBUG(a) +#define IPV6CPDEBUG(a) +#define UPAPDEBUG(a) +#define CHAPDEBUG(a) +#define PPPDEBUG(a, b) + +#endif /* PPP_DEBUG */ + +#endif /* PPPDEBUG_H */ + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppoe.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppoe.h new file mode 100644 index 0000000..9f8f289 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppoe.h @@ -0,0 +1,179 @@ +/***************************************************************************** +* pppoe.h - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "ppp.h" +#include "lwip/etharp.h" + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FLD_8(u8_t vertype); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef PPPOE_MAX_AC_COOKIE_LEN +#define PPPOE_MAX_AC_COOKIE_LEN 64 +#endif + +struct pppoe_softc { + struct pppoe_softc *next; + struct netif *sc_ethif; /* ethernet interface we are using */ + ppp_pcb *pcb; /* PPP PCB */ + + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + u8_t sc_state; /* discovery phase or session connected */ + +#ifdef PPPOE_TODO + u8_t *sc_service_name; /* if != NULL: requested name of service */ + u8_t *sc_concentrator_name; /* if != NULL: requested concentrator id */ +#endif /* PPPOE_TODO */ + u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */ + u8_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + u8_t sc_hunique_len; /* length of host unique */ +#endif + u8_t sc_padi_retried; /* number of PADI retries already done */ + u8_t sc_padr_retried; /* number of PADR retries already done */ +}; + + +#define pppoe_init() /* compatibility define, no initialization needed */ + +ppp_pcb *pppoe_create(struct netif *pppif, + struct netif *ethif, + const char *service_name, const char *concentrator_name, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); + +/* + * Functions called from lwIP + * DO NOT CALL FROM lwIP USER APPLICATION. + */ +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +#endif /* PPP_OE_H */ + +#endif /* PPP_SUPPORT && PPPOE_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppol2tp.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppol2tp.h new file mode 100644 index 0000000..f03950e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppol2tp.h @@ -0,0 +1,201 @@ +/** + * @file + * Network Point to Point Protocol over Layer 2 Tunneling Protocol header file. + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPPOL2TP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPPOL2TP_H +#define PPPOL2TP_H + +#include "ppp.h" + +/* Timeout */ +#define PPPOL2TP_CONTROL_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOL2TP_SLOW_RETRY (60*1000) /* persistent retry interval */ + +#define PPPOL2TP_MAXSCCRQ 4 /* retry SCCRQ four times (quickly) */ +#define PPPOL2TP_MAXICRQ 4 /* retry IRCQ four times */ +#define PPPOL2TP_MAXICCN 4 /* retry ICCN four times */ + +/* L2TP header flags */ +#define PPPOL2TP_HEADERFLAG_CONTROL 0x8000 +#define PPPOL2TP_HEADERFLAG_LENGTH 0x4000 +#define PPPOL2TP_HEADERFLAG_SEQUENCE 0x0800 +#define PPPOL2TP_HEADERFLAG_OFFSET 0x0200 +#define PPPOL2TP_HEADERFLAG_PRIORITY 0x0100 +#define PPPOL2TP_HEADERFLAG_VERSION 0x0002 + +/* Mandatory bits for control: Control, Length, Sequence, Version 2 */ +#define PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY (PPPOL2TP_HEADERFLAG_CONTROL|PPPOL2TP_HEADERFLAG_LENGTH|PPPOL2TP_HEADERFLAG_SEQUENCE|PPPOL2TP_HEADERFLAG_VERSION) +/* Forbidden bits for control: Offset, Priority */ +#define PPPOL2TP_HEADERFLAG_CONTROL_FORBIDDEN (PPPOL2TP_HEADERFLAG_OFFSET|PPPOL2TP_HEADERFLAG_PRIORITY) + +/* Mandatory bits for data: Version 2 */ +#define PPPOL2TP_HEADERFLAG_DATA_MANDATORY (PPPOL2TP_HEADERFLAG_VERSION) + +/* AVP (Attribute Value Pair) header */ +#define PPPOL2TP_AVPHEADERFLAG_MANDATORY 0x8000 +#define PPPOL2TP_AVPHEADERFLAG_HIDDEN 0x4000 +#define PPPOL2TP_AVPHEADERFLAG_LENGTHMASK 0x03ff + +/* -- AVP - Message type */ +#define PPPOL2TP_AVPTYPE_MESSAGE 0 /* Message type */ + +/* Control Connection Management */ +#define PPPOL2TP_MESSAGETYPE_SCCRQ 1 /* Start Control Connection Request */ +#define PPPOL2TP_MESSAGETYPE_SCCRP 2 /* Start Control Connection Reply */ +#define PPPOL2TP_MESSAGETYPE_SCCCN 3 /* Start Control Connection Connected */ +#define PPPOL2TP_MESSAGETYPE_STOPCCN 4 /* Stop Control Connection Notification */ +#define PPPOL2TP_MESSAGETYPE_HELLO 6 /* Hello */ +/* Call Management */ +#define PPPOL2TP_MESSAGETYPE_OCRQ 7 /* Outgoing Call Request */ +#define PPPOL2TP_MESSAGETYPE_OCRP 8 /* Outgoing Call Reply */ +#define PPPOL2TP_MESSAGETYPE_OCCN 9 /* Outgoing Call Connected */ +#define PPPOL2TP_MESSAGETYPE_ICRQ 10 /* Incoming Call Request */ +#define PPPOL2TP_MESSAGETYPE_ICRP 11 /* Incoming Call Reply */ +#define PPPOL2TP_MESSAGETYPE_ICCN 12 /* Incoming Call Connected */ +#define PPPOL2TP_MESSAGETYPE_CDN 14 /* Call Disconnect Notify */ +/* Error reporting */ +#define PPPOL2TP_MESSAGETYPE_WEN 15 /* WAN Error Notify */ +/* PPP Session Control */ +#define PPPOL2TP_MESSAGETYPE_SLI 16 /* Set Link Info */ + +/* -- AVP - Result code */ +#define PPPOL2TP_AVPTYPE_RESULTCODE 1 /* Result code */ +#define PPPOL2TP_RESULTCODE 1 /* General request to clear control connection */ + +/* -- AVP - Protocol version (!= L2TP Header version) */ +#define PPPOL2TP_AVPTYPE_VERSION 2 +#define PPPOL2TP_VERSION 0x0100 /* L2TP Protocol version 1, revision 0 */ + +/* -- AVP - Framing capabilities */ +#define PPPOL2TP_AVPTYPE_FRAMINGCAPABILITIES 3 /* Bearer capabilities */ +#define PPPOL2TP_FRAMINGCAPABILITIES 0x00000003 /* Async + Sync framing */ + +/* -- AVP - Bearer capabilities */ +#define PPPOL2TP_AVPTYPE_BEARERCAPABILITIES 4 /* Bearer capabilities */ +#define PPPOL2TP_BEARERCAPABILITIES 0x00000003 /* Analog + Digital Access */ + +/* -- AVP - Tie breaker */ +#define PPPOL2TP_AVPTYPE_TIEBREAKER 5 + +/* -- AVP - Host name */ +#define PPPOL2TP_AVPTYPE_HOSTNAME 7 /* Host name */ +#define PPPOL2TP_HOSTNAME "lwIP" /* FIXME: make it configurable */ + +/* -- AVP - Vendor name */ +#define PPPOL2TP_AVPTYPE_VENDORNAME 8 /* Vendor name */ +#define PPPOL2TP_VENDORNAME "lwIP" /* FIXME: make it configurable */ + +/* -- AVP - Assign tunnel ID */ +#define PPPOL2TP_AVPTYPE_TUNNELID 9 /* Assign Tunnel ID */ + +/* -- AVP - Receive window size */ +#define PPPOL2TP_AVPTYPE_RECEIVEWINDOWSIZE 10 /* Receive window size */ +#define PPPOL2TP_RECEIVEWINDOWSIZE 8 /* FIXME: make it configurable */ + +/* -- AVP - Challenge */ +#define PPPOL2TP_AVPTYPE_CHALLENGE 11 /* Challenge */ + +/* -- AVP - Cause code */ +#define PPPOL2TP_AVPTYPE_CAUSECODE 12 /* Cause code*/ + +/* -- AVP - Challenge response */ +#define PPPOL2TP_AVPTYPE_CHALLENGERESPONSE 13 /* Challenge response */ +#define PPPOL2TP_AVPTYPE_CHALLENGERESPONSE_SIZE 16 + +/* -- AVP - Assign session ID */ +#define PPPOL2TP_AVPTYPE_SESSIONID 14 /* Assign Session ID */ + +/* -- AVP - Call serial number */ +#define PPPOL2TP_AVPTYPE_CALLSERIALNUMBER 15 /* Call Serial Number */ + +/* -- AVP - Framing type */ +#define PPPOL2TP_AVPTYPE_FRAMINGTYPE 19 /* Framing Type */ +#define PPPOL2TP_FRAMINGTYPE 0x00000001 /* Sync framing */ + +/* -- AVP - TX Connect Speed */ +#define PPPOL2TP_AVPTYPE_TXCONNECTSPEED 24 /* TX Connect Speed */ +#define PPPOL2TP_TXCONNECTSPEED 100000000 /* Connect speed: 100 Mbits/s */ + +/* L2TP Session state */ +#define PPPOL2TP_STATE_INITIAL 0 +#define PPPOL2TP_STATE_SCCRQ_SENT 1 +#define PPPOL2TP_STATE_ICRQ_SENT 2 +#define PPPOL2TP_STATE_ICCN_SENT 3 +#define PPPOL2TP_STATE_DATA 4 + +#define PPPOL2TP_OUTPUT_DATA_HEADER_LEN 6 /* Our data header len */ + +/* + * PPPoL2TP interface control block. + */ +typedef struct pppol2tp_pcb_s pppol2tp_pcb; +struct pppol2tp_pcb_s { + ppp_pcb *ppp; /* PPP PCB */ + u8_t phase; /* L2TP phase */ + struct udp_pcb *udp; /* UDP L2TP Socket */ + struct netif *netif; /* Output interface, used as a default route */ + ip_addr_t remote_ip; /* LNS IP Address */ + u16_t remote_port; /* LNS port */ +#if PPPOL2TP_AUTH_SUPPORT + const u8_t *secret; /* Secret string */ + u8_t secret_len; /* Secret string length */ + u8_t secret_rv[16]; /* Random vector */ + u8_t challenge_hash[16]; /* Challenge response */ + u8_t send_challenge; /* Boolean whether the next sent packet should contains a challenge response */ +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + u16_t tunnel_port; /* Tunnel port */ + u16_t our_ns; /* NS to peer */ + u16_t peer_nr; /* NR from peer */ + u16_t peer_ns; /* NS from peer */ + u16_t source_tunnel_id; /* Tunnel ID assigned by peer */ + u16_t remote_tunnel_id; /* Tunnel ID assigned to peer */ + u16_t source_session_id; /* Session ID assigned by peer */ + u16_t remote_session_id; /* Session ID assigned to peer */ + + u8_t sccrq_retried; /* number of SCCRQ retries already done */ + u8_t icrq_retried; /* number of ICRQ retries already done */ + u8_t iccn_retried; /* number of ICCN retries already done */ +}; + + +/* Create a new L2TP session. */ +ppp_pcb *pppol2tp_create(struct netif *pppif, + struct netif *netif, const ip_addr_t *ipaddr, u16_t port, + const u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); + +#endif /* PPPOL2TP_H */ +#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppos.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppos.h new file mode 100644 index 0000000..d924a9f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/pppos.h @@ -0,0 +1,118 @@ +/** + * @file + * Network Point to Point Protocol over Serial header file. + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPPOS_H +#define PPPOS_H + +#include "lwip/sys.h" + +#include "ppp.h" +#include "vj.h" + +/* PPP packet parser states. Current state indicates operation yet to be + * completed. */ +enum { + PDIDLE = 0, /* Idle state - waiting. */ + PDSTART, /* Process start flag. */ + PDADDRESS, /* Process address field. */ + PDCONTROL, /* Process control field. */ + PDPROTOCOL1, /* Process protocol field 1. */ + PDPROTOCOL2, /* Process protocol field 2. */ + PDDATA /* Process data byte. */ +}; + +/* PPPoS serial output callback function prototype */ +typedef u32_t (*pppos_output_cb_fn)(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx); + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u8_t ext_accm[32]; + +/* + * PPPoS interface control block. + */ +typedef struct pppos_pcb_s pppos_pcb; +struct pppos_pcb_s { + /* -- below are data that will NOT be cleared between two sessions */ + ppp_pcb *ppp; /* PPP PCB */ + pppos_output_cb_fn output_cb; /* PPP serial output callback */ + + /* -- below are data that will be cleared between two sessions + * + * last_xmit must be the first member of cleared members, because it is + * used to know which part must not be cleared. + */ + u32_t last_xmit; /* Time of last transmission. */ + ext_accm out_accm; /* Async-Ctl-Char-Map for output. */ + + /* flags */ + unsigned int open :1; /* Set if PPPoS is open */ + unsigned int pcomp :1; /* Does peer accept protocol compression? */ + unsigned int accomp :1; /* Does peer accept addr/ctl compression? */ + + /* PPPoS rx */ + ext_accm in_accm; /* Async-Ctl-Char-Map for input. */ + struct pbuf *in_head, *in_tail; /* The input packet. */ + u16_t in_protocol; /* The input protocol code. */ + u16_t in_fcs; /* Input Frame Check Sequence value. */ + u8_t in_state; /* The input process state. */ + u8_t in_escaped; /* Escape next character. */ +}; + +/* Create a new PPPoS session. */ +ppp_pcb *pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); + +#if !NO_SYS && !PPP_INPROC_IRQ_SAFE +/* Pass received raw characters to PPPoS to be decoded through lwIP TCPIP thread. */ +err_t pppos_input_tcpip(ppp_pcb *ppp, u8_t *s, int l); +#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */ + +/* PPP over Serial: this is the input function to be called for received data. */ +void pppos_input(ppp_pcb *ppp, u8_t* data, int len); + + +/* + * Functions called from lwIP + * DO NOT CALL FROM lwIP USER APPLICATION. + */ +#if !NO_SYS && !PPP_INPROC_IRQ_SAFE +err_t pppos_input_sys(struct pbuf *p, struct netif *inp); +#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */ + +#endif /* PPPOS_H */ +#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/upap.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/upap.h new file mode 100644 index 0000000..7da792e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/upap.h @@ -0,0 +1,123 @@ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: upap.h,v 1.8 2002/12/04 23:03:33 paulus Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef UPAP_H +#define UPAP_H + +#include "ppp.h" + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN 4 + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +/* + * Timeouts. + */ +#if 0 /* moved to ppp_opts.h */ +#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */ +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif /* moved to ppp_opts.h */ + +/* + * Each interface is described by upap structure. + */ +#if PAP_SUPPORT +typedef struct upap_state { + const char *us_user; /* User */ + u8_t us_userlen; /* User length */ + const char *us_passwd; /* Password */ + u8_t us_passwdlen; /* Password length */ + u8_t us_clientstate; /* Client state */ +#if PPP_SERVER + u8_t us_serverstate; /* Server state */ +#endif /* PPP_SERVER */ + u8_t us_id; /* Current id */ + u8_t us_transmits; /* Number of auth-reqs sent */ +} upap_state; +#endif /* PAP_SUPPORT */ + + +void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password); +#if PPP_SERVER +void upap_authpeer(ppp_pcb *pcb); +#endif /* PPP_SERVER */ + +extern const struct protent pap_protent; + +#endif /* UPAP_H */ +#endif /* PPP_SUPPORT && PAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/ppp/vj.h b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/vj.h new file mode 100644 index 0000000..7f389c8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/ppp/vj.h @@ -0,0 +1,161 @@ +/* + * Definitions for tcp compression routines. + * + * $Id: vj.h,v 1.7 2010/02/22 17:52:09 goldsimon Exp $ + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef VJ_H +#define VJ_H + +#include "lwip/ip.h" +#include "lwip/priv/tcp_priv.h" + +#define MAX_SLOTS 16 /* must be > 2 and < 256 */ +#define MAX_HDR 128 + +/* + * Compressed packet format: + * + * The first octet contains the packet type (top 3 bits), TCP + * 'push' bit, and flags that indicate which of the 4 TCP sequence + * numbers have changed (bottom 5 bits). The next octet is a + * conversation number that associates a saved IP/TCP header with + * the compressed packet. The next two octets are the TCP checksum + * from the original datagram. The next 0 to 15 octets are + * sequence number changes, one change per bit set in the header + * (there may be no changes and there are two special cases where + * the receiver implicitly knows what changed -- see below). + * + * There are 5 numbers which can change (they are always inserted + * in the following order): TCP urgent pointer, window, + * acknowlegement, sequence number and IP ID. (The urgent pointer + * is different from the others in that its value is sent, not the + * change in value.) Since typical use of SLIP links is biased + * toward small packets (see comments on MTU/MSS below), changes + * use a variable length coding with one octet for numbers in the + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the + * range 256 - 65535 or 0. (If the change in sequence number or + * ack is more than 65535, an uncompressed packet is sent.) + */ + +/* + * Packet types (must not conflict with IP protocol version) + * + * The top nibble of the first octet is the packet type. There are + * three possible types: IP (not proto TCP or tcp with one of the + * control flags set); uncompressed TCP (a normal IP/TCP packet but + * with the 8-bit protocol field replaced by an 8-bit connection id -- + * this type of packet syncs the sender & receiver); and compressed + * TCP (described above). + * + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and + * is logically part of the 4-bit "changes" field that follows. Top + * three bits are actual packet type. For backward compatibility + * and in the interest of conserving bits, numbers are chosen so the + * IP protocol version number (4) which normally appears in this nibble + * means "IP packet". + */ + +/* packet types */ +#define TYPE_IP 0x40 +#define TYPE_UNCOMPRESSED_TCP 0x70 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + + +/* + * "state" data for each active tcp conversation on the wire. This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct cstate { + struct cstate *cs_next; /* next most recently used state (xmit only) */ + u16_t cs_hlen; /* size of hdr (receive only) */ + u8_t cs_id; /* connection # associated with this state */ + u8_t cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip_hdr csu_ip; /* ip/tcp hdr from most recent packet */ + } vjcs_u; +}; +#define cs_ip vjcs_u.csu_ip +#define cs_hdr vjcs_u.csu_hdr + + +struct vjstat { + u32_t vjs_packets; /* outbound packets */ + u32_t vjs_compressed; /* outbound compressed packets */ + u32_t vjs_searches; /* searches for connection state */ + u32_t vjs_misses; /* times couldn't find conn. state */ + u32_t vjs_uncompressedin; /* inbound uncompressed packets */ + u32_t vjs_compressedin; /* inbound compressed packets */ + u32_t vjs_errorin; /* inbound unknown type packets */ + u32_t vjs_tossed; /* inbound packets tossed because of error */ +}; + +/* + * all the state data for one serial line (we need one of these per line). + */ +struct vjcompress { + struct cstate *last_cs; /* most recently used tstate */ + u8_t last_recv; /* last rcvd conn. id */ + u8_t last_xmit; /* last sent conn. id */ + u16_t flags; + u8_t maxSlotIndex; + u8_t compressSlot; /* Flag indicating OK to compress slot ID. */ +#if LINK_STATS + struct vjstat stats; +#endif + struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ + struct cstate rstate[MAX_SLOTS]; /* receive connection states */ +}; + +/* flag values */ +#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ + +extern void vj_compress_init (struct vjcompress *comp); +extern u8_t vj_compress_tcp (struct vjcompress *comp, struct pbuf **pb); +extern void vj_uncompress_err (struct vjcompress *comp); +extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); +extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp); + +#endif /* VJ_H */ + +#endif /* PPP_SUPPORT && VJ_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/include/netif/slipif.h b/sdk/Src/Network/lwip2.0.3/include/netif/slipif.h new file mode 100644 index 0000000..4e68c94 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/netif/slipif.h @@ -0,0 +1,87 @@ +/** + * @file + * + * SLIP netif API + */ + +/* + * Copyright (c) 2001, 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 lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_HDR_NETIF_SLIPIF_H +#define LWIP_HDR_NETIF_SLIPIF_H + +#include "lwip/opt.h" +#include "lwip/netif.h" + +/** Set this to 1 to start a thread that blocks reading on the serial line + * (using sio_read()). + */ +#ifndef SLIP_USE_RX_THREAD +#define SLIP_USE_RX_THREAD !NO_SYS +#endif + +/** Set this to 1 to enable functions to pass in RX bytes from ISR context. + * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled + * packets on a queue, which is fed into lwIP from slipif_poll(). + * If disabled, slipif_poll() polls the serial line (using sio_tryread()). + */ +#ifndef SLIP_RX_FROM_ISR +#define SLIP_RX_FROM_ISR 0 +#endif + +/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets + * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available. + * If disabled, packets will be dropped if more than one packet is received. + */ +#ifndef SLIP_RX_QUEUE +#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +err_t slipif_init(struct netif * netif); +void slipif_poll(struct netif *netif); +#if SLIP_RX_FROM_ISR +void slipif_process_rxqueue(struct netif *netif); +void slipif_received_byte(struct netif *netif, u8_t data); +void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len); +#endif /* SLIP_RX_FROM_ISR */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_NETIF_SLIPIF_H */ + diff --git a/sdk/Src/Network/lwip2.0.3/include/posix/errno.h b/sdk/Src/Network/lwip2.0.3/include/posix/errno.h new file mode 100644 index 0000000..5917c75 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/posix/errno.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/errno.h. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/errno.h" diff --git a/sdk/Src/Network/lwip2.0.3/include/posix/netdb.h b/sdk/Src/Network/lwip2.0.3/include/posix/netdb.h new file mode 100644 index 0000000..29abbaf --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/posix/netdb.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/netdb.h. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/netdb.h" diff --git a/sdk/Src/Network/lwip2.0.3/include/posix/sys/socket.h b/sdk/Src/Network/lwip2.0.3/include/posix/sys/socket.h new file mode 100644 index 0000000..68267e5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/include/posix/sys/socket.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/sockets.h. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/sockets.h" diff --git a/sdk/Src/Network/lwip2.0.3/netif/FILES b/sdk/Src/Network/lwip2.0.3/netif/FILES new file mode 100644 index 0000000..a3ff431 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/FILES @@ -0,0 +1,24 @@ +This directory contains generic network interface device drivers that +do not contain any hardware or architecture specific code. The files +are: + +ethernet.c + Shared code for Ethernet based interfaces. + +ethernetif.c + An example of how an Ethernet device driver could look. This + file can be used as a "skeleton" for developing new Ethernet + network device drivers. It uses the etharp.c ARP code. + +lowpan6.c + A 6LoWPAN implementation as a netif. + +slipif.c + A generic implementation of the SLIP (Serial Line IP) + protocol. It requires a sio (serial I/O) module to work. + +ppp/ Point-to-Point Protocol stack + The lwIP PPP support is based from pppd (http://ppp.samba.org) with + huge changes to match code size and memory requirements for embedded + devices. Please read /doc/ppp.txt and ppp/PPPD_FOLLOWUP for a detailed + explanation. diff --git a/sdk/Src/Network/lwip2.0.3/netif/ethernet.c b/sdk/Src/Network/lwip2.0.3/netif/ethernet.c new file mode 100644 index 0000000..97a15d3 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ethernet.c @@ -0,0 +1,314 @@ +/** + * @file + * Ethernet common functions + * + * @defgroup ethernet Ethernet + * @ingroup callbackstyle_api + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET + +#include "netif/ethernet.h" +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/etharp.h" +#include "lwip/ip.h" +#include "lwip/snmp.h" + +#include + +#include "netif/ppp/ppp_opts.h" +#if PPPOE_SUPPORT +#include "netif/ppp/pppoe.h" +#endif /* PPPOE_SUPPORT */ + +#ifdef LWIP_HOOK_FILENAME +#include LWIP_HOOK_FILENAME +#endif + +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; + +/** + * @ingroup lwip_nosys + * Process received ethernet frames. Using this function instead of directly + * calling ip_input and passing ARP frames through etharp in ethernetif_input, + * the ARP cache is protected from concurrent access.\n + * Don't call directly, pass to netif_add() and call netif->input(). + * + * @param p the received packet, p->payload pointing to the ethernet header + * @param netif the network interface on which the packet was received + * + * @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL + * @see ETHARP_SUPPORT_VLAN + * @see LWIP_HOOK_VLAN_CHECK + */ +err_t +ethernet_input(struct pbuf *p, struct netif *netif) +{ + struct eth_hdr* ethhdr; + u16_t type; +#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6 + s16_t ip_hdr_offset = SIZEOF_ETH_HDR; +#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ + + if (p->len <= SIZEOF_ETH_HDR) { + /* a packet with only an ethernet header (or less) is not valid for us */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + MIB2_STATS_NETIF_INC(netif, ifinerrors); + goto free_and_return; + } + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = (struct eth_hdr *)p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", + (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2], + (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5], + (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2], + (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5], + lwip_htons(ethhdr->type))); + + type = ethhdr->type; +#if ETHARP_SUPPORT_VLAN + if (type == PP_HTONS(ETHTYPE_VLAN)) { + struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR); + if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { + /* a packet with only an ethernet/vlan header (or less) is not valid for us */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + MIB2_STATS_NETIF_INC(netif, ifinerrors); + goto free_and_return; + } +#if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ +#ifdef LWIP_HOOK_VLAN_CHECK + if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) { +#elif defined(ETHARP_VLAN_CHECK_FN) + if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { +#elif defined(ETHARP_VLAN_CHECK) + if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { +#endif + /* silently ignore this packet: not for our VLAN */ + pbuf_free(p); + return ERR_OK; + } +#endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ + type = vlan->tpid; + ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; + } +#endif /* ETHARP_SUPPORT_VLAN */ + +#if LWIP_ARP_FILTER_NETIF + netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type)); +#endif /* LWIP_ARP_FILTER_NETIF*/ + + if (ethhdr->dest.addr[0] & 1) { + /* this might be a multicast or broadcast packet */ + if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) { +#if LWIP_IPV4 + if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) && + (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) { + /* mark the pbuf as link-layer multicast */ + p->flags |= PBUF_FLAG_LLMCAST; + } +#endif /* LWIP_IPV4 */ + } +#if LWIP_IPV6 + else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) && + (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) { + /* mark the pbuf as link-layer multicast */ + p->flags |= PBUF_FLAG_LLMCAST; + } +#endif /* LWIP_IPV6 */ + else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { + /* mark the pbuf as link-layer broadcast */ + p->flags |= PBUF_FLAG_LLBCAST; + } + } + + switch (type) { +#if LWIP_IPV4 && LWIP_ARP + /* IP packet? */ + case PP_HTONS(ETHTYPE_IP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } + /* skip Ethernet header */ + if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("ethernet_input: IPv4 packet dropped, too short (%"S16_F"/%"S16_F")\n", + p->tot_len, ip_hdr_offset)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet")); + goto free_and_return; + } else { + /* pass to IP layer */ + ip4_input(p, netif); + } + break; + + case PP_HTONS(ETHTYPE_ARP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } + /* skip Ethernet header */ + if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("ethernet_input: ARP response packet dropped, too short (%"S16_F"/%"S16_F")\n", + p->tot_len, ip_hdr_offset)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet")); + ETHARP_STATS_INC(etharp.lenerr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } else { + /* pass p to ARP module */ + etharp_input(p, netif); + } + break; +#endif /* LWIP_IPV4 && LWIP_ARP */ +#if PPPOE_SUPPORT + case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + + case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */ + pppoe_data_input(netif, p); + break; +#endif /* PPPOE_SUPPORT */ + +#if LWIP_IPV6 + case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ + /* skip Ethernet header */ + if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("ethernet_input: IPv6 packet dropped, too short (%"S16_F"/%"S16_F")\n", + p->tot_len, ip_hdr_offset)); + goto free_and_return; + } else { + /* pass to IPv6 layer */ + ip6_input(p, netif); + } + break; +#endif /* LWIP_IPV6 */ + + default: +#ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL + if(LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) { + break; + } +#endif + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + MIB2_STATS_NETIF_INC(netif, ifinunknownprotos); + goto free_and_return; + } + + /* This means the pbuf is freed or consumed, + so the caller doesn't have to free it again */ + return ERR_OK; + +free_and_return: + pbuf_free(p); + return ERR_OK; +} + +/** + * @ingroup ethernet + * Send an ethernet packet on the network using netif->linkoutput(). + * The ethernet header is filled in before sending. + * + * @see LWIP_HOOK_VLAN_SET + * + * @param netif the lwIP network interface on which to send the packet + * @param p the packet to send. pbuf layer must be @ref PBUF_LINK. + * @param src the source MAC address to be copied into the ethernet header + * @param dst the destination MAC address to be copied into the ethernet header + * @param eth_type ethernet type (@ref eth_type) + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +err_t +ethernet_output(struct netif* netif, struct pbuf* p, + const struct eth_addr* src, const struct eth_addr* dst, + u16_t eth_type) +{ + struct eth_hdr* ethhdr; + u16_t eth_type_be = lwip_htons(eth_type); + +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + s32_t vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type); + if (vlan_prio_vid >= 0) { + struct eth_vlan_hdr* vlanhdr; + + LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF); + + if (pbuf_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) { + goto pbuf_header_failed; + } + vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)p->payload) + SIZEOF_ETH_HDR); + vlanhdr->tpid = eth_type_be; + vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid); + + eth_type_be = PP_HTONS(ETHTYPE_VLAN); + } else +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + { + if (pbuf_header(p, SIZEOF_ETH_HDR) != 0) { + goto pbuf_header_failed; + } + } + + ethhdr = (struct eth_hdr*)p->payload; + ethhdr->type = eth_type_be; + ETHADDR32_COPY(ðhdr->dest, dst); + ETHADDR16_COPY(ðhdr->src, src); + + LWIP_ASSERT("netif->hwaddr_len must be 6 for ethernet_output!", + (netif->hwaddr_len == ETH_HWADDR_LEN)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_output: sending packet %p\n", (void *)p)); + + /* send the packet */ + return netif->linkoutput(netif, p); + +pbuf_header_failed: + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("ethernet_output: could not allocate room for header.\n")); + LINK_STATS_INC(link.lenerr); + return ERR_BUF; +} + +#endif /* LWIP_ARP || LWIP_ETHERNET */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ethernetif.c b/sdk/Src/Network/lwip2.0.3/netif/ethernetif.c new file mode 100644 index 0000000..d925f32 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ethernetif.c @@ -0,0 +1,428 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * Copyright (c) 2001-2004 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * This file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include +#include "lwip/opt.h" + +#if 1 /* don't build, this is only a skeleton, see previous comment */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/ppp/pppoe.h" +#include "wm_mem.h" +#include "lwip/igmp.h" +#include "lwip/mld6.h" +#include "tls_common.h" +#if TLS_CONFIG_AP_OPT_FWD +#include "lwip/tcpip.h" +#endif + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +/** Maximum transfer unit */ +#define NET_MTU 1500 + +/** Network link speed */ +#define NET_LINK_SPEED 100000000 + +/** + * Helper struct to hold private data used to operate your ethernet interface. + * Keeping the ethernet address of the MAC in this struct is not necessary + * as it is already kept in the struct netif. + * But this is only an example, anyway... + */ +struct ethernetif { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ +}; + +/* Forward declarations. */ +int ethernetif_input(const u8 *bssid, u8 *buf, u32 buf_len); +extern struct netif *tls_get_netif(void); +extern u8* tls_wifi_buffer_acquire(int total_len); +extern void tls_wifi_buffer_release(bool is_apsta, u8* buffer); +extern u8 *wpa_supplicant_get_mac(void); +extern int tls_hw_set_multicast_key(u8* addr); +extern int tls_hw_del_multicast_key(u8* addr); +#if TLS_CONFIG_AP +extern u8 *hostapd_get_mac(void); +#endif + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void +low_level_init(struct netif *netif) +{ + u8* mac_addr; + + /* Set MAC hardware address length */ + netif->hwaddr_len = ETH_ALEN; + +#if TLS_CONFIG_AP + if (netif != tls_get_netif()) + { + mac_addr = hostapd_get_mac(); + /* Set MAC hardware address */ + MEMCPY(&netif->hwaddr[0], mac_addr, ETH_ALEN); + } + else +#endif + { + mac_addr = wpa_supplicant_get_mac(); + /* Set MAC hardware address */ + MEMCPY(&netif->hwaddr[0], mac_addr, ETH_ALEN); + } + + /* Maximum transfer unit */ + netif->mtu = NET_MTU; + +#if LWIP_IPV6_AUTOCONFIG + netif_set_ip6_autoconfig_enabled(netif, 1); +#endif + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | + NETIF_FLAG_IGMP +#if defined(DHCP_USED) + | NETIF_FLAG_DHCP +#endif + ; + +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* + * For hardware/netifs that implement MAC filtering. + * All-nodes link-local is handled by default, so we must let the hardware know + * to allow multicast packets in. + * Should set mld_mac_filter previously. */ + if (netif->mld_mac_filter != NULL) { + ip6_addr_t ip6_allnodes_ll; + ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll); + netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER); + } +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + /* Do whatever else is needed to initialize interface. */ +} + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become available since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ + struct pbuf *q = NULL; + int datalen = 0; + u8* buf = tls_wifi_buffer_acquire(p->tot_len); + if(buf == NULL) + return ERR_MEM; + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* Drop the padding word */ +#endif + + /* Check the buffer boundary */ + //if (p->tot_len > NET_RW_BUFF_SIZE) { + // return ERR_BUF; + //} + + for (q = p; q != NULL; q = q->next) { + /* Send data from(q->payload, q->len); */ + MEMCPY(buf + datalen, q->payload, q->len); + datalen += q->len; + } + +#if TLS_CONFIG_AP + if (netif != tls_get_netif()) + tls_wifi_buffer_release(true, buf); + else +#endif + tls_wifi_buffer_release(false, buf); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* Reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf *low_level_input(struct netif *netif, u8 *buf, u32 buf_len) +{ + struct pbuf *p = NULL, *q = NULL; + u16_t s_len; + u8_t *bufptr; + + /* Obtain the size of the packet and put it into the "len" + * variable. */ + + s_len = buf_len; + bufptr = buf; + +#if ETH_PAD_SIZE + s_len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, s_len, PBUF_RAM); + + if (p != NULL) { +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* Iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for (q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. */ + /* read data into(q->payload, q->len); */ + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } + /* Acknowledge that packet has been read(); */ + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* Reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + } else { + /* Drop packet(); */ + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +int ethernetif_input(const u8 *bssid, u8 *buf, u32 buf_len) +{ + struct netif *netif = tls_get_netif(); + struct pbuf *p; + +#if TLS_CONFIG_AP + u8* mac_addr = hostapd_get_mac(); + if (0 == compare_ether_addr(bssid, mac_addr)) + { + netif = netif->next; + } +#endif + + /* move received packet into a new pbuf */ + p = low_level_input(netif, buf, buf_len); + if (p) { + if (ERR_OK != netif->input(p, netif)) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } + return 0; + } else { + return -1; + } +} + +#if LWIP_IGMP +/*** + action: + IGMP_DEL_MAC_FILTER 0 + IGMP_ADD_MAC_FILTER 1 +***/ + +static err_t ethernetif_igmp_mac_filter(struct netif *netif, + const ip4_addr_t *group, enum netif_mac_filter_action action) +{ + u8 m_addr[6] = {0x01, 0x00, 0x5E}; + int ret = ERR_OK; + if (netif != tls_get_netif()) + { + return ERR_OK; + } + LWIP_DEBUGF(IGMP_DEBUG, ("IPaddr: %d.%d.%d.%d\n", ip4_addr1(group), + ip4_addr2(group), + ip4_addr3(group), + ip4_addr4(group))); + + //create group mac address: + memcpy(m_addr+3, (u8*)&(group->addr)+1, 3); + m_addr[3] &= 0x7F; //clear bit24 + + LWIP_DEBUGF(IGMP_DEBUG, ("MACaddr: %x:%x:%x:%x:%x:%x\n",m_addr[0], + m_addr[1], + m_addr[2], + m_addr[3], + m_addr[4], + m_addr[5])); + + if(action == IGMP_ADD_MAC_FILTER) + { + ret = tls_hw_set_multicast_key(m_addr); + } + else if(action == IGMP_DEL_MAC_FILTER) + { + ret = tls_hw_del_multicast_key(m_addr); + } + if (ret < 0) + return ret; + else + return ERR_OK; +} +#endif + + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t +ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); +#if LWIP_NETIF_HOSTNAME +#if TLS_CONFIG_AP + u8* mac_addr = hostapd_get_mac(); +#else + u8* mac_addr = wpa_supplicant_get_mac(); +#endif /* TLS_CONFIG_AP */ + u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; + memcpy(&hwaddr[0], mac_addr, ETH_ALEN); + char *prefix = "klyqa-"; + char *buf = (char *)tls_mem_calloc(1, strlen(prefix)+7); + if (buf != NULL) { + sprintf(buf, "%s%02X%02X%02X", prefix, hwaddr[NETIF_MAX_HWADDR_LEN-3], hwaddr[NETIF_MAX_HWADDR_LEN-2], hwaddr[NETIF_MAX_HWADDR_LEN-1]); + /* Initialize interface hostname */ + memcpy(&netif->hostname, buf, sizeof(buf)); + tls_mem_free(buf); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("ethernetif_init: hostname memory allocation failed\n")); + netif->hostname = "klyqa-XXXXXX"; + } + LWIP_DEBUGF(IGMP_DEBUG, ("ethernetif_init: hostname set to: %s\n", netif->hostname)); +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, NET_LINK_SPEED); + + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif /* LWIP_IPV6 */ + netif->linkoutput = low_level_output; + +#if TLS_CONFIG_AP_OPT_FWD + netif->ipfwd_output = tcpip_output; +#endif +#if LWIP_IGMP + netif->igmp_mac_filter = ethernetif_igmp_mac_filter; +#endif + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +#endif /* 0 */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/lowpan6.c b/sdk/Src/Network/lwip2.0.3/netif/lowpan6.c new file mode 100644 index 0000000..b59ac46 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/lowpan6.c @@ -0,0 +1,1193 @@ +/** + * @file + * + * 6LowPAN output for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units. + */ + +/* + * Copyright (c) 2015 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +/** + * @defgroup sixlowpan 6LowPAN netif + * @ingroup addons + * 6LowPAN netif implementation + */ + +#include "netif/lowpan6.h" + +#if LWIP_IPV6 && LWIP_6LOWPAN + +#include "lwip/ip.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/nd6.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" +#include "lwip/snmp.h" + +#include + +struct ieee_802154_addr { + u8_t addr_len; + u8_t addr[8]; +}; + +/** This is a helper struct. + */ +struct lowpan6_reass_helper { + struct pbuf *pbuf; + struct lowpan6_reass_helper *next_packet; + u8_t timer; + struct ieee_802154_addr sender_addr; + u16_t datagram_size; + u16_t datagram_tag; +}; + +static struct lowpan6_reass_helper * reass_list; + +#if LWIP_6LOWPAN_NUM_CONTEXTS > 0 +static ip6_addr_t lowpan6_context[LWIP_6LOWPAN_NUM_CONTEXTS]; +#endif + +static u16_t ieee_802154_pan_id; + +static const struct ieee_802154_addr ieee_802154_broadcast = {2, {0xff, 0xff}}; + +#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS +static struct ieee_802154_addr short_mac_addr = {2, {0,0}}; +#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */ + +static err_t dequeue_datagram(struct lowpan6_reass_helper *lrh); + +/** + * Periodic timer for 6LowPAN functions: + * + * - Remove incomplete/old packets + */ +void +lowpan6_tmr(void) +{ + struct lowpan6_reass_helper *lrh, *lrh_temp; + + lrh = reass_list; + while (lrh != NULL) { + lrh_temp = lrh->next_packet; + if ((--lrh->timer) == 0) { + dequeue_datagram(lrh); + pbuf_free(lrh->pbuf); + mem_free(lrh); + } + lrh = lrh_temp; + } +} + +/** + * Removes a datagram from the reassembly queue. + **/ +static err_t +dequeue_datagram(struct lowpan6_reass_helper *lrh) +{ + struct lowpan6_reass_helper *lrh_temp; + + if (reass_list == lrh) { + reass_list = reass_list->next_packet; + } else { + lrh_temp = reass_list; + while (lrh_temp != NULL) { + if (lrh_temp->next_packet == lrh) { + lrh_temp->next_packet = lrh->next_packet; + break; + } + lrh_temp = lrh_temp->next_packet; + } + } + + return ERR_OK; +} + +static s8_t +lowpan6_context_lookup(const ip6_addr_t *ip6addr) +{ + s8_t i; + + for (i = 0; i < LWIP_6LOWPAN_NUM_CONTEXTS; i++) { + if (ip6_addr_netcmp(&lowpan6_context[i], ip6addr)) { + return i; + } + } + + return -1; +} + +/* Determine compression mode for unicast address. */ +static s8_t +lowpan6_get_address_mode(const ip6_addr_t *ip6addr, const struct ieee_802154_addr *mac_addr) +{ + if (mac_addr->addr_len == 2) { + if ((ip6addr->addr[2] == (u32_t)PP_HTONL(0x000000ff)) && + ((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000))) { + if ((ip6addr->addr[3] & PP_HTONL(0x0000ffff)) == lwip_ntohl((mac_addr->addr[0] << 8) | mac_addr->addr[1])) { + return 3; + } + } + } else if (mac_addr->addr_len == 8) { + if ((ip6addr->addr[2] == lwip_ntohl(((mac_addr->addr[0] ^ 2) << 24) | (mac_addr->addr[1] << 16) | mac_addr->addr[2] << 8 | mac_addr->addr[3])) && + (ip6addr->addr[3] == lwip_ntohl((mac_addr->addr[4] << 24) | (mac_addr->addr[5] << 16) | mac_addr->addr[6] << 8 | mac_addr->addr[7]))) { + return 3; + } + } + + if ((ip6addr->addr[2] == PP_HTONL(0x000000ffUL)) && + ((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000UL))) { + return 2; + } + + return 1; +} + +/* Determine compression mode for multicast address. */ +static s8_t +lowpan6_get_address_mode_mc(const ip6_addr_t *ip6addr) +{ + if ((ip6addr->addr[0] == PP_HTONL(0xff020000)) && + (ip6addr->addr[1] == 0) && + (ip6addr->addr[2] == 0) && + ((ip6addr->addr[3] & PP_HTONL(0xffffff00)) == 0)) { + return 3; + } else if (((ip6addr->addr[0] & PP_HTONL(0xff00ffff)) == PP_HTONL(0xff000000)) && + (ip6addr->addr[1] == 0)) { + if ((ip6addr->addr[2] == 0) && + ((ip6addr->addr[3] & PP_HTONL(0xff000000)) == 0)) { + return 2; + } else if ((ip6addr->addr[2] & PP_HTONL(0xffffff00)) == 0) { + return 1; + } + } + + return 0; +} + +/* + * Encapsulates data into IEEE 802.15.4 frames. + * Fragments an IPv6 datagram into 6LowPAN units, which fit into IEEE 802.15.4 frames. + * If configured, will compress IPv6 and or UDP headers. + * */ +static err_t +lowpan6_frag(struct netif *netif, struct pbuf *p, const struct ieee_802154_addr *src, const struct ieee_802154_addr *dst) +{ + struct pbuf * p_frag; + u16_t frag_len, remaining_len; + u8_t * buffer; + u8_t ieee_header_len; + u8_t lowpan6_header_len; + s8_t i; + static u8_t frame_seq_num; + static u16_t datagram_tag; + u16_t datagram_offset; + err_t err = ERR_IF; + + /* We'll use a dedicated pbuf for building 6LowPAN fragments. */ + p_frag = pbuf_alloc(PBUF_RAW, 127, PBUF_RAM); + if (p_frag == NULL) { + MIB2_STATS_NETIF_INC(netif, ifoutdiscards); + return ERR_MEM; + } + + /* Write IEEE 802.15.4 header. */ + buffer = (u8_t*)p_frag->payload; + ieee_header_len = 0; + if (dst == &ieee_802154_broadcast) { + buffer[ieee_header_len++] = 0x01; /* data packet, no ack required. */ + } else { + buffer[ieee_header_len++] = 0x21; /* data packet, ack required. */ + } + buffer[ieee_header_len] = (0x00 << 4); /* 2003 frame version */ + buffer[ieee_header_len] |= (dst->addr_len == 2) ? (0x02 << 2) : (0x03 << 2); /* destination addressing mode */ + buffer[ieee_header_len] |= (src->addr_len == 2) ? (0x02 << 6) : (0x03 << 6); /* source addressing mode */ + ieee_header_len++; + buffer[ieee_header_len++] = frame_seq_num++; + + buffer[ieee_header_len++] = ieee_802154_pan_id & 0xff; /* pan id */ + buffer[ieee_header_len++] = (ieee_802154_pan_id >> 8) & 0xff; /* pan id */ + i = dst->addr_len; + while (i-- > 0) { + buffer[ieee_header_len++] = dst->addr[i]; + } + + buffer[ieee_header_len++] = ieee_802154_pan_id & 0xff; /* pan id */ + buffer[ieee_header_len++] = (ieee_802154_pan_id >> 8) & 0xff; /* pan id */ + i = src->addr_len; + while (i-- > 0) { + buffer[ieee_header_len++] = src->addr[i]; + } + +#if LWIP_6LOWPAN_IPHC + /* Perform 6LowPAN IPv6 header compression according to RFC 6282 */ + { + struct ip6_hdr *ip6hdr; + + /* Point to ip6 header and align copies of src/dest addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest); + ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src); + + /* Basic length of 6LowPAN header, set dispatch and clear fields. */ + lowpan6_header_len = 2; + buffer[ieee_header_len] = 0x60; + buffer[ieee_header_len + 1] = 0; + + /* Determine whether there will be a Context Identifier Extension byte or not. + * If so, set it already. */ +#if LWIP_6LOWPAN_NUM_CONTEXTS > 0 + buffer[ieee_header_len + 2] = 0; + + i = lowpan6_context_lookup(ip_2_ip6(&ip_data.current_iphdr_src)); + if (i >= 0) { + /* Stateful source address compression. */ + buffer[ieee_header_len + 1] |= 0x40; + buffer[ieee_header_len + 2] |= (i & 0x0f) << 4; + } + + i = lowpan6_context_lookup(ip_2_ip6(&ip_data.current_iphdr_dest)); + if (i >= 0) { + /* Stateful destination address compression. */ + buffer[ieee_header_len + 1] |= 0x04; + buffer[ieee_header_len + 2] |= i & 0x0f; + } + + if (buffer[ieee_header_len + 2] != 0x00) { + /* Context identifier extension byte is appended. */ + buffer[ieee_header_len + 1] |= 0x80; + lowpan6_header_len++; + } +#endif /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */ + + /* Determine TF field: Traffic Class, Flow Label */ + if (IP6H_FL(ip6hdr) == 0) { + /* Flow label is elided. */ + buffer[ieee_header_len] |= 0x10; + if (IP6H_TC(ip6hdr) == 0) { + /* Traffic class (ECN+DSCP) elided too. */ + buffer[ieee_header_len] |= 0x08; + } else { + /* Traffic class (ECN+DSCP) appended. */ + buffer[ieee_header_len + lowpan6_header_len++] = IP6H_TC(ip6hdr); + } + } else { + if (((IP6H_TC(ip6hdr) & 0x3f) == 0)) { + /* DSCP portion of Traffic Class is elided, ECN and FL are appended (3 bytes) */ + buffer[ieee_header_len] |= 0x08; + + buffer[ieee_header_len + lowpan6_header_len] = IP6H_TC(ip6hdr) & 0xc0; + buffer[ieee_header_len + lowpan6_header_len++] |= (IP6H_FL(ip6hdr) >> 16) & 0x0f; + buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff; + buffer[ieee_header_len + lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff; + } else { + /* Traffic class and flow label are appended (4 bytes) */ + buffer[ieee_header_len + lowpan6_header_len++] = IP6H_TC(ip6hdr); + buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 16) & 0x0f; + buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff; + buffer[ieee_header_len + lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff; + } + } + + /* Compress NH? + * Only if UDP for now. @todo support other NH compression. */ + if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) { + buffer[ieee_header_len] |= 0x04; + } else { + /* append nexth. */ + buffer[ieee_header_len + lowpan6_header_len++] = IP6H_NEXTH(ip6hdr); + } + + /* Compress hop limit? */ + if (IP6H_HOPLIM(ip6hdr) == 255) { + buffer[ieee_header_len] |= 0x03; + } else if (IP6H_HOPLIM(ip6hdr) == 64) { + buffer[ieee_header_len] |= 0x02; + } else if (IP6H_HOPLIM(ip6hdr) == 1) { + buffer[ieee_header_len] |= 0x01; + } else { + /* append hop limit */ + buffer[ieee_header_len + lowpan6_header_len++] = IP6H_HOPLIM(ip6hdr); + } + + /* Compress source address */ + if (((buffer[ieee_header_len + 1] & 0x40) != 0) || + (ip6_addr_islinklocal(ip_2_ip6(&ip_data.current_iphdr_src)))) { + /* Context-based or link-local source address compression. */ + i = lowpan6_get_address_mode(ip_2_ip6(&ip_data.current_iphdr_src), src); + buffer[ieee_header_len + 1] |= (i & 0x03) << 4; + if (i == 1) { + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 16, 8); + lowpan6_header_len += 8; + } else if (i == 2) { + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 22, 2); + lowpan6_header_len += 2; + } + } else if (ip6_addr_isany(ip_2_ip6(&ip_data.current_iphdr_src))) { + /* Special case: mark SAC and leave SAM=0 */ + buffer[ieee_header_len + 1] |= 0x40; + } else { + /* Append full address. */ + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 8, 16); + lowpan6_header_len += 16; + } + + /* Compress destination address */ + if (ip6_addr_ismulticast(ip_2_ip6(&ip_data.current_iphdr_dest))) { + /* @todo support stateful multicast address compression */ + + buffer[ieee_header_len + 1] |= 0x08; + + i = lowpan6_get_address_mode_mc(ip_2_ip6(&ip_data.current_iphdr_dest)); + buffer[ieee_header_len + 1] |= i & 0x03; + if (i == 0) { + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 24, 16); + lowpan6_header_len += 16; + } else if (i == 1) { + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[25]; + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 35, 5); + lowpan6_header_len += 5; + } else if (i == 2) { + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[25]; + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 37, 3); + lowpan6_header_len += 3; + } else if (i == 3) { + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[39]; + } + } else if (((buffer[ieee_header_len + 1] & 0x04) != 0) || + (ip6_addr_islinklocal(ip_2_ip6(&ip_data.current_iphdr_dest)))) { + /* Context-based or link-local destination address compression. */ + i = lowpan6_get_address_mode(ip_2_ip6(&ip_data.current_iphdr_dest), dst); + buffer[ieee_header_len + 1] |= i & 0x03; + if (i == 1) { + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 32, 8); + lowpan6_header_len += 8; + } else if (i == 2) { + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 38, 2); + lowpan6_header_len += 2; + } + } else { + /* Append full address. */ + MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 24, 16); + lowpan6_header_len += 16; + } + + /* Move to payload. */ + pbuf_header(p, -IP6_HLEN); + + /* Compress UDP header? */ + if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) { + /* @todo support optional checksum compression */ + + buffer[ieee_header_len + lowpan6_header_len] = 0xf0; + + /* determine port compression mode. */ + if ((((u8_t *)p->payload)[0] == 0xf0) && ((((u8_t *)p->payload)[1] & 0xf0) == 0xb0) && + (((u8_t *)p->payload)[2] == 0xf0) && ((((u8_t *)p->payload)[3] & 0xf0) == 0xb0)) { + /* Compress source and dest ports. */ + buffer[ieee_header_len + lowpan6_header_len++] |= 0x03; + buffer[ieee_header_len + lowpan6_header_len++] = ((((u8_t *)p->payload)[1] & 0x0f) << 4) | (((u8_t *)p->payload)[3] & 0x0f); + } else if (((u8_t *)p->payload)[0] == 0xf0) { + /* Compress source port. */ + buffer[ieee_header_len + lowpan6_header_len++] |= 0x02; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1]; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[2]; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3]; + } else if (((u8_t *)p->payload)[2] == 0xf0) { + /* Compress dest port. */ + buffer[ieee_header_len + lowpan6_header_len++] |= 0x01; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[0]; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1]; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3]; + } else { + /* append full ports. */ + lowpan6_header_len++; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[0]; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1]; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[2]; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3]; + } + + /* elide length and copy checksum */ + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[6]; + buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[7]; + + pbuf_header(p, -UDP_HLEN); + } + } + +#else /* LWIP_6LOWPAN_HC */ + /* Send uncompressed IPv6 header with appropriate dispatch byte. */ + lowpan6_header_len = 1; + buffer[ieee_header_len] = 0x41; /* IPv6 dispatch */ +#endif /* LWIP_6LOWPAN_HC */ + + /* Calculate remaining packet length */ + remaining_len = p->tot_len; + + if (remaining_len > 0x7FF) { + MIB2_STATS_NETIF_INC(netif, ifoutdiscards); + /* datagram_size must fit into 11 bit */ + pbuf_free(p_frag); + return ERR_VAL; + } + + /* Fragment, or 1 packet? */ + if (remaining_len > (127 - ieee_header_len - lowpan6_header_len - 3)) { /* 127 - header - 1 byte dispatch - 2 bytes CRC */ + /* We must move the 6LowPAN header to make room for the FRAG header. */ + i = lowpan6_header_len; + while (i-- != 0) { + buffer[ieee_header_len + i + 4] = buffer[ieee_header_len + i]; + } + + /* Now we need to fragment the packet. FRAG1 header first */ + buffer[ieee_header_len] = 0xc0 | (((p->tot_len + lowpan6_header_len) >> 8) & 0x7); + buffer[ieee_header_len + 1] = (p->tot_len + lowpan6_header_len) & 0xff; + + datagram_tag++; + buffer[ieee_header_len + 2] = datagram_tag & 0xff; + buffer[ieee_header_len + 3] = (datagram_tag >> 8) & 0xff; + + /* Fragment follows. */ + frag_len = (127 - ieee_header_len - 4 - 2) & 0xf8; + + pbuf_copy_partial(p, buffer + ieee_header_len + lowpan6_header_len + 4, frag_len - lowpan6_header_len, 0); + remaining_len -= frag_len - lowpan6_header_len; + datagram_offset = frag_len; + + /* 2 bytes CRC */ +#if LWIP_6LOWPAN_HW_CRC + /* Leave blank, will be filled by HW. */ +#else /* LWIP_6LOWPAN_HW_CRC */ + /* @todo calculate CRC */ +#endif /* LWIP_6LOWPAN_HW_CRC */ + + /* Calculate frame length */ + p_frag->len = p_frag->tot_len = ieee_header_len + 4 + frag_len + 2; /* add 2 dummy bytes for crc*/ + + /* send the packet */ + MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len); + LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p)); + err = netif->linkoutput(netif, p_frag); + + while ((remaining_len > 0) && (err == ERR_OK)) { + /* new frame, new seq num for ACK */ + buffer[2] = frame_seq_num++; + + buffer[ieee_header_len] |= 0x20; /* Change FRAG1 to FRAGN */ + + buffer[ieee_header_len + 4] = (u8_t)(datagram_offset >> 3); /* datagram offset in FRAGN header (datagram_offset is max. 11 bit) */ + + frag_len = (127 - ieee_header_len - 5 - 2) & 0xf8; + if (frag_len > remaining_len) { + frag_len = remaining_len; + } + + pbuf_copy_partial(p, buffer + ieee_header_len + 5, frag_len, p->tot_len - remaining_len); + remaining_len -= frag_len; + datagram_offset += frag_len; + + /* 2 bytes CRC */ +#if LWIP_6LOWPAN_HW_CRC + /* Leave blank, will be filled by HW. */ +#else /* LWIP_6LOWPAN_HW_CRC */ + /* @todo calculate CRC */ +#endif /* LWIP_6LOWPAN_HW_CRC */ + + /* Calculate frame length */ + p_frag->len = p_frag->tot_len = frag_len + 5 + ieee_header_len + 2; + + /* send the packet */ + MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len); + LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p)); + err = netif->linkoutput(netif, p_frag); + } + } else { + /* It fits in one frame. */ + frag_len = remaining_len; + + /* Copy IPv6 packet */ + pbuf_copy_partial(p, buffer + ieee_header_len + lowpan6_header_len, frag_len, 0); + remaining_len = 0; + + /* 2 bytes CRC */ +#if LWIP_6LOWPAN_HW_CRC + /* Leave blank, will be filled by HW. */ +#else /* LWIP_6LOWPAN_HW_CRC */ + /* @todo calculate CRC */ +#endif /* LWIP_6LOWPAN_HW_CRC */ + + /* Calculate frame length */ + p_frag->len = p_frag->tot_len = frag_len + lowpan6_header_len + ieee_header_len + 2; + + /* send the packet */ + MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len); + LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p)); + err = netif->linkoutput(netif, p_frag); + } + + pbuf_free(p_frag); + + return err; +} + +err_t +lowpan6_set_context(u8_t idx, const ip6_addr_t * context) +{ + if (idx >= LWIP_6LOWPAN_NUM_CONTEXTS) { + return ERR_ARG; + } + + ip6_addr_set(&lowpan6_context[idx], context); + + return ERR_OK; +} + +#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS +err_t +lowpan6_set_short_addr(u8_t addr_high, u8_t addr_low) +{ + short_mac_addr.addr[0] = addr_high; + short_mac_addr.addr[1] = addr_low; + + return ERR_OK; +} +#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */ + +#if LWIP_IPV4 +err_t +lowpan4_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) +{ + (void)netif; + (void)q; + (void)ipaddr; + + return ERR_IF; +} +#endif /* LWIP_IPV4 */ + +/** + * Resolve and fill-in IEEE 802.15.4 address header for outgoing IPv6 packet. + * + * Perform Header Compression and fragment if necessary. + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ip6addr The IP address of the packet destination. + * + * @return err_t + */ +err_t +lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) +{ + err_t result; + const u8_t *hwaddr; + struct ieee_802154_addr src, dest; +#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS + ip6_addr_t ip6_src; + struct ip6_hdr * ip6_hdr; +#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */ + +#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS + /* Check if we can compress source address (use aligned copy) */ + ip6_hdr = (struct ip6_hdr *)q->payload; + ip6_addr_set(&ip6_src, &ip6_hdr->src); + if (lowpan6_get_address_mode(&ip6_src, &short_mac_addr) == 3) { + src.addr_len = 2; + src.addr[0] = short_mac_addr.addr[0]; + src.addr[1] = short_mac_addr.addr[1]; + } else +#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */ + { + src.addr_len = netif->hwaddr_len; + SMEMCPY(src.addr, netif->hwaddr, netif->hwaddr_len); + } + + /* multicast destination IP address? */ + if (ip6_addr_ismulticast(ip6addr)) { + MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts); + /* We need to send to the broadcast address.*/ + return lowpan6_frag(netif, q, &src, &ieee_802154_broadcast); + } + + /* We have a unicast destination IP address */ + /* @todo anycast? */ + +#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS + if (src.addr_len == 2) { + /* If source address was compressable to short_mac_addr, and dest has same subnet and + * is also compressable to 2-bytes, assume we can infer dest as a short address too. */ + dest.addr_len = 2; + dest.addr[0] = ((u8_t *)q->payload)[38]; + dest.addr[1] = ((u8_t *)q->payload)[39]; + if ((src.addr_len == 2) && (ip6_addr_netcmp(&ip6_hdr->src, &ip6_hdr->dest)) && + (lowpan6_get_address_mode(ip6addr, &dest) == 3)) { + MIB2_STATS_NETIF_INC(netif, ifoutucastpkts); + return lowpan6_frag(netif, q, &src, &dest); + } + } +#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */ + + /* Ask ND6 what to do with the packet. */ + result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr); + if (result != ERR_OK) { + MIB2_STATS_NETIF_INC(netif, ifoutdiscards); + return result; + } + + /* If no hardware address is returned, nd6 has queued the packet for later. */ + if (hwaddr == NULL) { + return ERR_OK; + } + + /* Send out the packet using the returned hardware address. */ + dest.addr_len = netif->hwaddr_len; + SMEMCPY(dest.addr, hwaddr, netif->hwaddr_len); + MIB2_STATS_NETIF_INC(netif, ifoutucastpkts); + return lowpan6_frag(netif, q, &src, &dest); +} + +static struct pbuf * +lowpan6_decompress(struct pbuf * p, struct ieee_802154_addr * src, struct ieee_802154_addr * dest) +{ + struct pbuf * q; + u8_t * lowpan6_buffer; + s8_t lowpan6_offset; + struct ip6_hdr *ip6hdr; + s8_t i; + s8_t ip6_offset = IP6_HLEN; + + + q = pbuf_alloc(PBUF_IP, p->len + IP6_HLEN + UDP_HLEN, PBUF_POOL); + if (q == NULL) { + pbuf_free(p); + return NULL; + } + + lowpan6_buffer = (u8_t *)p->payload; + ip6hdr = (struct ip6_hdr *)q->payload; + + lowpan6_offset = 2; + if (lowpan6_buffer[1] & 0x80) { + lowpan6_offset++; + } + + /* Set IPv6 version, traffic class and flow label. */ + if ((lowpan6_buffer[0] & 0x18) == 0x00) { + IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset], ((lowpan6_buffer[lowpan6_offset+1] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset+3]); + lowpan6_offset += 4; + } else if ((lowpan6_buffer[0] & 0x18) == 0x08) { + IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset] & 0xc0, ((lowpan6_buffer[lowpan6_offset] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset+2]); + lowpan6_offset += 3; + } else if ((lowpan6_buffer[0] & 0x18) == 0x10) { + IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset],0); + lowpan6_offset += 1; + } else if ((lowpan6_buffer[0] & 0x18) == 0x18) { + IP6H_VTCFL_SET(ip6hdr, 6, 0, 0); + } + + /* Set Next Header */ + if ((lowpan6_buffer[0] & 0x04) == 0x00) { + IP6H_NEXTH_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]); + } else { + /* We should fill this later with NHC decoding */ + IP6H_NEXTH_SET(ip6hdr, 0); + } + + /* Set Hop Limit */ + if ((lowpan6_buffer[0] & 0x03) == 0x00) { + IP6H_HOPLIM_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]); + } else if ((lowpan6_buffer[0] & 0x03) == 0x01) { + IP6H_HOPLIM_SET(ip6hdr, 1); + } else if ((lowpan6_buffer[0] & 0x03) == 0x02) { + IP6H_HOPLIM_SET(ip6hdr, 64); + } else if ((lowpan6_buffer[0] & 0x03) == 0x03) { + IP6H_HOPLIM_SET(ip6hdr, 255); + } + + /* Source address decoding. */ + if ((lowpan6_buffer[1] & 0x40) == 0x00) { + /* Stateless compression */ + if ((lowpan6_buffer[1] & 0x30) == 0x00) { + /* copy full address */ + MEMCPY(&ip6hdr->src.addr[0], lowpan6_buffer + lowpan6_offset, 16); + lowpan6_offset += 16; + } else if ((lowpan6_buffer[1] & 0x30) == 0x10) { + ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL); + ip6hdr->src.addr[1] = 0; + MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8); + lowpan6_offset += 8; + } else if ((lowpan6_buffer[1] & 0x30) == 0x20) { + ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL); + ip6hdr->src.addr[1] = 0; + ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL); + ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | + lowpan6_buffer[lowpan6_offset+1]); + lowpan6_offset += 2; + } else if ((lowpan6_buffer[1] & 0x30) == 0x30) { + ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL); + ip6hdr->src.addr[1] = 0; + if (src->addr_len == 2) { + ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL); + ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]); + } else { + ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) | + (src->addr[2] << 8) | src->addr[3]); + ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) | + (src->addr[6] << 8) | src->addr[7]); + } + } + } else { + /* Stateful compression */ + if ((lowpan6_buffer[1] & 0x30) == 0x00) { + /* ANY address */ + ip6hdr->src.addr[0] = 0; + ip6hdr->src.addr[1] = 0; + ip6hdr->src.addr[2] = 0; + ip6hdr->src.addr[3] = 0; + } else { + /* Set prefix from context info */ + if (lowpan6_buffer[1] & 0x80) { + i = (lowpan6_buffer[2] >> 4) & 0x0f; + } else { + i = 0; + } + if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) { + /* Error */ + pbuf_free(p); + pbuf_free(q); + return NULL; + } + + ip6hdr->src.addr[0] = lowpan6_context[i].addr[0]; + ip6hdr->src.addr[1] = lowpan6_context[i].addr[1]; + } + + if ((lowpan6_buffer[1] & 0x30) == 0x10) { + MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8); + lowpan6_offset += 8; + } else if ((lowpan6_buffer[1] & 0x30) == 0x20) { + ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL); + ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset+1]); + lowpan6_offset += 2; + } else if ((lowpan6_buffer[1] & 0x30) == 0x30) { + if (src->addr_len == 2) { + ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL); + ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]); + } else { + ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) | (src->addr[2] << 8) | src->addr[3]); + ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) | (src->addr[6] << 8) | src->addr[7]); + } + } + } + + /* Destination address decoding. */ + if (lowpan6_buffer[1] & 0x08) { + /* Multicast destination */ + if (lowpan6_buffer[1] & 0x04) { + /* @todo support stateful multicast addressing */ + pbuf_free(p); + pbuf_free(q); + return NULL; + } + + if ((lowpan6_buffer[1] & 0x03) == 0x00) { + /* copy full address */ + MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16); + lowpan6_offset += 16; + } else if ((lowpan6_buffer[1] & 0x03) == 0x01) { + ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | (lowpan6_buffer[lowpan6_offset++] << 16)); + ip6hdr->dest.addr[1] = 0; + ip6hdr->dest.addr[2] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]); + ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 24) | (lowpan6_buffer[lowpan6_offset + 1] << 16) | (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset + 3]); + lowpan6_offset += 4; + } else if ((lowpan6_buffer[1] & 0x03) == 0x02) { + ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | lowpan6_buffer[lowpan6_offset++]); + ip6hdr->dest.addr[1] = 0; + ip6hdr->dest.addr[2] = 0; + ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset + 2]); + lowpan6_offset += 3; + } else if ((lowpan6_buffer[1] & 0x03) == 0x03) { + ip6hdr->dest.addr[0] = PP_HTONL(0xff020000UL); + ip6hdr->dest.addr[1] = 0; + ip6hdr->dest.addr[2] = 0; + ip6hdr->dest.addr[3] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]); + } + + } else { + if (lowpan6_buffer[1] & 0x04) { + /* Stateful destination compression */ + /* Set prefix from context info */ + if (lowpan6_buffer[1] & 0x80) { + i = lowpan6_buffer[2] & 0x0f; + } else { + i = 0; + } + if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) { + /* Error */ + pbuf_free(p); + pbuf_free(q); + return NULL; + } + + ip6hdr->dest.addr[0] = lowpan6_context[i].addr[0]; + ip6hdr->dest.addr[1] = lowpan6_context[i].addr[1]; + } else { + /* Link local address compression */ + ip6hdr->dest.addr[0] = PP_HTONL(0xfe800000UL); + ip6hdr->dest.addr[1] = 0; + } + + if ((lowpan6_buffer[1] & 0x03) == 0x00) { + /* copy full address */ + MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16); + lowpan6_offset += 16; + } else if ((lowpan6_buffer[1] & 0x03) == 0x01) { + MEMCPY(&ip6hdr->dest.addr[2], lowpan6_buffer + lowpan6_offset, 8); + lowpan6_offset += 8; + } else if ((lowpan6_buffer[1] & 0x03) == 0x02) { + ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL); + ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset + 1]); + lowpan6_offset += 2; + } else if ((lowpan6_buffer[1] & 0x03) == 0x03) { + if (dest->addr_len == 2) { + ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL); + ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (dest->addr[0] << 8) | dest->addr[1]); + } else { + ip6hdr->dest.addr[2] = lwip_htonl(((dest->addr[0] ^ 2) << 24) | (dest->addr[1] << 16) | dest->addr[2] << 8 | dest->addr[3]); + ip6hdr->dest.addr[3] = lwip_htonl((dest->addr[4] << 24) | (dest->addr[5] << 16) | dest->addr[6] << 8 | dest->addr[7]); + } + } + } + + + /* Next Header Compression (NHC) decoding? */ + if (lowpan6_buffer[0] & 0x04) { + if ((lowpan6_buffer[lowpan6_offset] & 0xf8) == 0xf0) { + struct udp_hdr *udphdr; + + /* UDP compression */ + IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_UDP); + udphdr = (struct udp_hdr *)((u8_t *)q->payload + ip6_offset); + + if (lowpan6_buffer[lowpan6_offset] & 0x04) { + /* @todo support checksum decompress */ + pbuf_free(p); + pbuf_free(q); + return NULL; + } + + /* Decompress ports */ + i = lowpan6_buffer[lowpan6_offset++] & 0x03; + if (i == 0) { + udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]); + udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 2] << 8 | lowpan6_buffer[lowpan6_offset + 3]); + lowpan6_offset += 4; + } else if (i == 0x01) { + udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]); + udphdr->dest = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset + 2]); + lowpan6_offset += 3; + } else if (i == 0x02) { + udphdr->src = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset]); + udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 1] << 8 | lowpan6_buffer[lowpan6_offset + 2]); + lowpan6_offset += 3; + } else if (i == 0x03) { + udphdr->src = lwip_htons(0xf0b0 | ((lowpan6_buffer[lowpan6_offset] >> 4) & 0x0f)); + udphdr->dest = lwip_htons(0xf0b0 | (lowpan6_buffer[lowpan6_offset] & 0x0f)); + lowpan6_offset += 1; + } + + udphdr->chksum = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]); + lowpan6_offset += 2; + udphdr->len = lwip_htons(p->tot_len - lowpan6_offset + UDP_HLEN); + + ip6_offset += UDP_HLEN; + } else { + /* @todo support NHC other than UDP */ + pbuf_free(p); + pbuf_free(q); + return NULL; + } + } + + /* Now we copy leftover contents from p to q, so we have all L2 and L3 headers (and L4?) in a single PBUF. + * Replace p with q, and free p */ + pbuf_header(p, -lowpan6_offset); + MEMCPY((u8_t*)q->payload + ip6_offset, p->payload, p->len); + q->len = q->tot_len = ip6_offset + p->len; + if (p->next != NULL) { + pbuf_cat(q, p->next); + } + p->next = NULL; + pbuf_free(p); + + /* Infer IPv6 payload length for header */ + IP6H_PLEN_SET(ip6hdr, q->tot_len - IP6_HLEN); + + /* all done */ + return q; +} + +err_t +lowpan6_input(struct pbuf * p, struct netif *netif) +{ + u8_t * puc; + s8_t i; + struct ieee_802154_addr src, dest; + u16_t datagram_size, datagram_offset, datagram_tag; + struct lowpan6_reass_helper *lrh, *lrh_temp; + + MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len); + + /* Analyze header. @todo validate. */ + puc = (u8_t*)p->payload; + datagram_offset = 5; + if ((puc[1] & 0x0c) == 0x0c) { + dest.addr_len = 8; + for (i = 0; i < 8; i++) { + dest.addr[i] = puc[datagram_offset + 7 - i]; + } + datagram_offset += 8; + } else { + dest.addr_len = 2; + dest.addr[0] = puc[datagram_offset + 1]; + dest.addr[1] = puc[datagram_offset]; + datagram_offset += 2; + } + + datagram_offset += 2; /* skip PAN ID. */ + + if ((puc[1] & 0xc0) == 0xc0) { + src.addr_len = 8; + for (i = 0; i < 8; i++) { + src.addr[i] = puc[datagram_offset + 7 - i]; + } + datagram_offset += 8; + } else { + src.addr_len = 2; + src.addr[0] = puc[datagram_offset + 1]; + src.addr[1] = puc[datagram_offset]; + datagram_offset += 2; + } + + pbuf_header(p, -datagram_offset); /* hide IEEE802.15.4 header. */ + + /* Check dispatch. */ + puc = (u8_t*)p->payload; + + if ((*puc & 0xf8) == 0xc0) { + /* FRAG1 dispatch. add this packet to reassembly list. */ + datagram_size = ((u16_t)(puc[0] & 0x07) << 8) | (u16_t)puc[1]; + datagram_tag = ((u16_t)puc[2] << 8) | (u16_t)puc[3]; + + /* check for duplicate */ + lrh = reass_list; + while (lrh != NULL) { + if ((lrh->sender_addr.addr_len == src.addr_len) && + (memcmp(lrh->sender_addr.addr, src.addr, src.addr_len) == 0)) { + /* address match with packet in reassembly. */ + if ((datagram_tag == lrh->datagram_tag) && (datagram_size == lrh->datagram_size)) { + MIB2_STATS_NETIF_INC(netif, ifindiscards); + /* duplicate fragment. */ + pbuf_free(p); + return ERR_OK; + } else { + /* We are receiving the start of a new datagram. Discard old one (incomplete). */ + lrh_temp = lrh->next_packet; + dequeue_datagram(lrh); + pbuf_free(lrh->pbuf); + mem_free(lrh); + + /* Check next datagram in queue. */ + lrh = lrh_temp; + } + } else { + /* Check next datagram in queue. */ + lrh = lrh->next_packet; + } + } + + pbuf_header(p, -4); /* hide frag1 dispatch */ + + lrh = (struct lowpan6_reass_helper *) mem_malloc(sizeof(struct lowpan6_reass_helper)); + if (lrh == NULL) { + MIB2_STATS_NETIF_INC(netif, ifindiscards); + pbuf_free(p); + return ERR_MEM; + } + + lrh->sender_addr.addr_len = src.addr_len; + for (i = 0; i < src.addr_len; i++) { + lrh->sender_addr.addr[i] = src.addr[i]; + } + lrh->datagram_size = datagram_size; + lrh->datagram_tag = datagram_tag; + lrh->pbuf = p; + lrh->next_packet = reass_list; + lrh->timer = 2; + reass_list = lrh; + + return ERR_OK; + } else if ((*puc & 0xf8) == 0xe0) { + /* FRAGN dispatch, find packet being reassembled. */ + datagram_size = ((u16_t)(puc[0] & 0x07) << 8) | (u16_t)puc[1]; + datagram_tag = ((u16_t)puc[2] << 8) | (u16_t)puc[3]; + datagram_offset = (u16_t)puc[4] << 3; + pbuf_header(p, -5); /* hide frag1 dispatch */ + + for (lrh = reass_list; lrh != NULL; lrh = lrh->next_packet) { + if ((lrh->sender_addr.addr_len == src.addr_len) && + (memcmp(lrh->sender_addr.addr, src.addr, src.addr_len) == 0) && + (datagram_tag == lrh->datagram_tag) && + (datagram_size == lrh->datagram_size)) { + break; + } + } + if (lrh == NULL) { + /* rogue fragment */ + MIB2_STATS_NETIF_INC(netif, ifindiscards); + pbuf_free(p); + return ERR_OK; + } + + if (lrh->pbuf->tot_len < datagram_offset) { + /* duplicate, ignore. */ + pbuf_free(p); + return ERR_OK; + } else if (lrh->pbuf->tot_len > datagram_offset) { + MIB2_STATS_NETIF_INC(netif, ifindiscards); + /* We have missed a fragment. Delete whole reassembly. */ + dequeue_datagram(lrh); + pbuf_free(lrh->pbuf); + mem_free(lrh); + pbuf_free(p); + return ERR_OK; + } + pbuf_cat(lrh->pbuf, p); + p = NULL; + + /* is packet now complete?*/ + if (lrh->pbuf->tot_len >= lrh->datagram_size) { + /* dequeue from reass list. */ + dequeue_datagram(lrh); + + /* get pbuf */ + p = lrh->pbuf; + + /* release helper */ + mem_free(lrh); + } else { + return ERR_OK; + } + } + + if (p == NULL) { + return ERR_OK; + } + + /* We have a complete packet, check dispatch for headers. */ + puc = (u8_t*)p->payload; + + if (*puc == 0x41) { + /* This is a complete IPv6 packet, just skip dispatch byte. */ + pbuf_header(p, -1); /* hide dispatch byte. */ + } else if ((*puc & 0xe0 )== 0x60) { + /* IPv6 headers are compressed using IPHC. */ + p = lowpan6_decompress(p, &src, &dest); + if (p == NULL) { + MIB2_STATS_NETIF_INC(netif, ifindiscards); + return ERR_OK; + } + } else { + MIB2_STATS_NETIF_INC(netif, ifindiscards); + pbuf_free(p); + return ERR_OK; + } + + /* @todo: distinguish unicast/multicast */ + MIB2_STATS_NETIF_INC(netif, ifinucastpkts); + + return ip6_input(p, netif); +} + +err_t +lowpan6_if_init(struct netif *netif) +{ + netif->name[0] = 'L'; + netif->name[1] = '6'; +#if LWIP_IPV4 + netif->output = lowpan4_output; +#endif /* LWIP_IPV4 */ + netif->output_ip6 = lowpan6_output; + + MIB2_INIT_NETIF(netif, snmp_ifType_other, 0); + + /* maximum transfer unit */ + netif->mtu = 1280; + + /* broadcast capability */ + netif->flags = NETIF_FLAG_BROADCAST /* | NETIF_FLAG_LOWPAN6 */; + + return ERR_OK; +} + +err_t +lowpan6_set_pan_id(u16_t pan_id) +{ + ieee_802154_pan_id = pan_id; + + return ERR_OK; +} + +#if !NO_SYS +/** + * Pass a received packet to tcpip_thread for input processing + * + * @param p the received packet, p->payload pointing to the + * IEEE 802.15.4 header. + * @param inp the network interface on which the packet was received + */ +err_t +tcpip_6lowpan_input(struct pbuf *p, struct netif *inp) +{ + return tcpip_inpkt(p, inp, lowpan6_input); +} +#endif /* !NO_SYS */ + +#endif /* LWIP_IPV6 && LWIP_6LOWPAN */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/PPPD_FOLLOWUP b/sdk/Src/Network/lwip2.0.3/netif/ppp/PPPD_FOLLOWUP new file mode 100644 index 0000000..c231982 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/PPPD_FOLLOWUP @@ -0,0 +1,473 @@ +The lwIP PPP support is based from pppd 2.4.5 (http://ppp.samba.org) with +huge changes to match code size and memory requirements for embedded devices. + +Anyway, pppd has a mature codebase for years and the average commit count +is getting low on their Git repository, meaning that we can follow what +is happening on their side and merge what is relevant for lwIP. + +So, here is the pppd follow up, so that we don't get away too far from pppd. + + +== Patch fetched from from pppd Debian packages == + +This has nothing to do with pppd, but we merged some good patch from +Debian and this is a good place to be. + +- LCP adaptive echo, so that we don't send LCP echo request if we + are receiving data from peer, can be enabled by setting PPP_LCP_ADAPTIVE + to true. + +- IPCP no/replace default route option, were added in the early stage of + the ppp port, but it wasn't really helpful and was disabled when adding + the new API ppp_set_default() call, which gives the lwIP user control over + which one is the default interface, it was actually a requirement if you + are doing PPP over PPP (i.e. PPPoL2TP, VPN link, over PPPoE, ADSL link). + +- using rp-pppoe pppd exits with EXIT_OK after receiving a timeout waiting + for PADO due to no modem attached, bug reported to pppd bug tracker, fixed + in Debian but not in the latest (at the time when the port were started) + pppd release. + + +== Commits on pppd == + +2010-03-06 - Document +ipv6 and ipv6cp-accept-local + e7537958aee79b3f653c601e903cb31d78fb7dcc + +Don't care. + + +2010-03-06 - Install pppol2tp plugins with sane permissions + 406215672cfadc03017341fe03802d1c7294b903 + +Don't care. + + +2010-03-07 - pppd: Terminate correctly if lcp_lowerup delayed calling + fsm_lowerup + 3eb9e810cfa515543655659b72dde30c54fea0a5 + +Merged 2012-05-17. + + +2010-03-07 - rp_pppoe: Copy acName and pppd_pppoe_service after option parsing + cab58617fd9d328029fffabc788020264b4fa91f + +Don't care, is a patch for pppd/plugins/rp-pppoe/plugin.c which is not part +of the port. + + +2010-08-23 - set and reset options to control environment variables + for scripts. + 2b6310fd24dba8e0fca8999916a162f0a1842a84 + +We can't fork processes in embedded, therefore all the pppd process run +feature is disabled in the port, so we don't care about the new +"environment variables" pppd feature. + + +2010-08-23 - Nit: use _exit when exec fails and restrict values to 0-255 + per POSIX. + 2b4ea140432eeba5a007c0d4e6236bd0e0c12ba4 + +Again, we are not running as a heavy process, so all exit() or _exit() calls +were removed. + + +2010-08-23 - Fix quote handling in configuration files to be more like shell + quoting. + 3089132cdf5b58dbdfc2daf08ec5c08eb47f8aca + +We are not parsing config file, all the filesystem I/O stuff were disabled +in our port. + + +2010-08-24 - rp-pppoe: allow MTU to be increased up to 1500 + fd1dcdf758418f040da3ed801ab001b5e46854e7 + +Only concern changes on RP-PPPoE plugin, which we don't use. + + +2010-09-11 - chat: Allow TIMEOUT value to come from environment variable + ae80bf833e48a6202f44a935a68083ae52ad3824 + +See 2b6310fd24dba8e0fca8999916a162f0a1842a84. + + +2011-03-05 - pppdump: Fix printfs with insufficient arguments + 7b8db569642c83ba3283745034f2e2c95e459423 + +pppdump is a ppp tool outside pppd source tree. + + +2012-05-06 - pppd: Don't unconditionally disable VJ compression under Linux + d8a66adf98a0e525cf38031b42098d539da6eeb6 + +Patch for sys-linux.c, which we don't use. + + +2012-05-20 - Remove old version of Linux if_pppol2tp.h + c41092dd4c49267f232f6cba3d31c6c68bfdf68d + +Not in the port. + + +2012-05-20 - pppd: Make MSCHAP-v2 cope better with packet loss + 08ef47ca532294eb428238c831616748940e24a2 + +This is an interesting patch. However it consumes much more memory for +MSCHAP and I am not sure if the benefit worth it. The PPP client can +always start the authentication again if it failed for whatever reason. + + +2012-05-20 - scripts: Make poff ignore extra arguments to pppd + 18f515f32c9f5723a9c2c912601e04335106534b + +Again, we are not running scripts. + + +2012-05-20 - rp-pppoe plugin: Print leading zeros in MAC address + f5dda0cfc220c4b52e26144096d729e27b30f0f7 + +Again, we are not using the RP-PPPoE plugin. + + +2012-05-20 - pppd: Notify IPv6 up/down as we do for IPv4 + 845cda8fa18939cf56e60b073f63a7efa65336fc + +This is just a patch that adds plugins hooks for IPv6, the plugin interface +was disabled because we don't have .so plugins in embedded. + + +2012-05-20 - pppd: Enable IPV6 by default and fix some warnings + 0b6118239615e98959f7e0b4e746bdd197533248 + +Change on Makefile for IPv6, warnings were already cleared during port. + + +2012-05-20 - contrib: Fix pppgetpass.gtk compilation + 80a8e2ce257ca12cce723519a0f20ea1d663b14a + +Change on Makefile, don't care. + + +2012-05-20 - pppd: Don't crash if crypt() returns NULL + 04c4348108d847e034dd91066cc6843f60d71731 + +We are using the PolarSSL DES implementation that does not return NULL. + + +2012-05-20 - pppd: Eliminate some warnings + c44ae5e6a7338c96eb463881fe709b2dfaffe568 + +Again, we are handling compilation warnings on our own. + + +2012-05-20 - rp-pppoe plugin: Import some fixes from rp-pppoe-3.10 + 1817d83e51a411044e730ba89ebdb0480e1c8cd4 + +Once more, we are not using the RP-PPPoE plugin. + + +2013-01-23 - pppd: Clarify circumstances where DNS1/DNS2 environment variables are set + cf2f5c9538b9400ade23446a194729b0a4113b3a + +Documentation only. + + +2013-02-03 - ppp: ignore unrecognised radiusclient configuration directives + 7f736dde0da3c19855997d9e67370e351e15e923 + +Radius plugin, not in the port. + + +2013-02-03 - pppd: Take out unused %r conversion completely + 356d8d558d844412119aa18c8e5a113bc6459c7b + +Merged 2014-04-15. + + +2013-02-03 - pppd: Arrange to use logwtmp from libutil on Linux + 9617a7eb137f4fee62799a677a9ecf8d834db3f5 + +Patch for sys-linux.c, which we don't use. + + +2013-02-03 - pppdump: Eliminate some compiler warnings + 3e3acf1ba2b3046c072a42c19164788a9e419bd1 + +pppdump is a ppp tool outside pppd source tree. + + +2013-02-03 - chat: Correct spelling errors in the man page + 8dea1b969d266ccbf6f3a8c5474eb6dcd8838e3b + +Documentation only. + + +2013-02-03 - pppd: Fix spelling errors in man page + 9e05a25d76b3f83096c661678010320df673df6b + +Documentation only. + + +2013-02-03 - plugins/passprompt: Fix potential out-of-bounds array reference + 8edb889b753056a691a3e4b217a110a35f9fdedb + +Plugin patch, we do not have plugins. + + +2013-02-03 - chat: Fix *roff errors in the man page + a7c3489eeaf44e83ce592143c7c8a5b5c29f4c48 + +Documentation only. + + +2013-03-02 - pppd: Fix man page description of case when remote IP address isn't known + 224841f4799f4f1e2e71bc490c54448d66740f4f + +Documentation only. + + +2013-03-02 - pppd: Add master_detach option + 398ed2585640d198c53e736ee5bbd67f7ce8168e + +Option for multilink support, we do not support multilink and this option +is about detaching from the terminal, which is out of the embedded scope. + + +2013-03-11 - pppd: Default exit status to EXIT_CONNECT_FAILED during connection phase + 225361d64ae737afdc8cb57579a2f33525461bc9 + +Commented out in our port, and already fixed by a previously applied Debian patch. + + +2013-03-11 - pppstats: Fix undefined macro in man page + d16a3985eade5280b8e171f5dd0670a91cba0d39 + +Documentation only. + + +2013-05-11 - plugins/radius: Handle bindaddr keyword in radiusclient.conf + d883b2dbafeed3ebd9d7a56ab1469373bd001a3b + +Radius plugin, not in the port. + + +2013-06-09 - pppoatm: Remove explicit loading of pppoatm kernel module + 52cd43a84bea524033b918b603698104f221bbb7 + +PPPoATM plugin, not in the port. + + +2013-06-09 - pppd: Fix segfault in update_db_entry() + 37476164f15a45015310b9d4b197c2d7db1f7f8f + +We do not use the samba db. + + +2013-06-09 - chat: Fix some text that was intended to be literal + cd9683676618adcee8add2c3cfa3382341b5a1f6 + +Documentation only. + + +2013-06-09 - README.pppoe: Minor semantic fix + b5b8898af6fd3d44e873cfc66810ace5f1f47e17 + +Documentation only. + + +2013-06-10 - radius: Handle additional attributes + 2f581cd986a56f2ec4a95abad4f8297a1b10d7e2 + +Radius plugin, not in the port. + + +2013-06-10 - chat, pppd: Use \e instead of \\ in man pages + 8d6942415d22f6ca4377340ca26e345c3f5fa5db + +Documentation only. + + +2014-01-02 - pppd: Don't crash if NULL pointer passed to vslprintf for %q or %v + 906814431bddeb2061825fa1ebad1a967b6d87a9 + +Merged 2014-04-15. + + +2014-01-02 - pppd: Accept IPCP ConfAck packets containing MS-WINS options + a243f217f1c6ac1aa7793806bc88590d077f490a + +Merged 2014-04-15. + + +2014-01-02 - config: Update Solaris compiler options and enable CHAPMS and IPV6 + 99c46caaed01b7edba87962aa52b77fad61bfd7b + +Solaris port, don't care. + + +2014-01-02 - Update README and patchlevel for 2.4.6 release + 4043750fca36e7e0eb90d702e048ad1da4929418 + +Just release stuff. + + +2014-02-18 - pppd: Add option "stop-bits" to set number of serial port stop bits. + ad993a20ee485f0d0e2ac4105221641b200da6e2 + +Low level serial port, not in the port. + + +2014-03-09 - pppd: Separate IPv6 handling for sifup/sifdown + b04d2dc6df5c6b5650fea44250d58757ee3dac4a + +Reimplemented. + + +2014-03-09 - pppol2tp: Connect up/down events to notifiers and add IPv6 ones + fafbe50251efc7d6b4a8be652d085316e112b34f + +Not in the port. + + +2014-03-09 - pppd: Add declarations to eliminate compile warnings + 50967962addebe15c7a7e63116ff46a0441dc464 + +We are handling compilation warnings on our own + + +2014-03-09 - pppd: Eliminate some unnecessary ifdefs + de8da14d845ee6db9236ccfddabf1d8ebf045ddb + +We mostly did that previously. Anyway, merged 2014-12-24. + + +2014-08-01 - radius: Fix realms-config-file option + 880a81be7c8e0fe8567227bc17a1bff3ea035943 + +Radius plugin, not in the port. + + +2014-08-01 - pppd: Eliminate potential integer overflow in option parsing + 7658e8257183f062dc01f87969c140707c7e52cb + +pppd config file parser, not in the port. + + +2014-08-01 - pppd: Eliminate memory leak with multiple instances of a string option + b94b7fbbaa0589aa6ec5fdc733aeb9ff294d2656 + +pppd config file parser, not in the port. + + +2014-08-01 - pppd: Fix a stack variable overflow in MSCHAP-v2 + 36733a891fb56594fcee580f667b33a64b990981 + +This fixes a bug introduced in 08ef47ca ("pppd: Make MSCHAP-v2 cope better with packet loss"). + +We didn't merge 08ef47ca ;-) + + +2014-08-01 - winbind plugin: Add -DMPPE=1 to eliminate compiler warnings + 2b05e22c62095e97dd0a97e4b5588402c2185071 + +Linux plugin, not in the port. + + +2014-08-09 - Update README and patchlevel for 2.4.7 release + 6e8eaa7a78b31cdab2edf140a9c8afdb02ffaca5 + +Just release stuff. + + +2014-08-10 - abort on errors in subdir builds + 5e90783d11a59268e05f4cfb29ce2343b13e8ab2 + +Linux Makefile, not in the port. + + +2014-06-03 - pppd: add support for defaultroute-metric option + 35e5a569c988b1ff865b02a24d9a727a00db4da9 + +Only necessary for Linux, lwIP does not support route metrics. + + +2014-12-13 - scripts: Avoid killing wrong pppd + 67811a647d399db5d188a242827760615a0f86b5 + +pppd helper script, not in the port. + + +2014-12-20 - pppd: Fix sign-extension when displaying bytes in octal + 5e8c3cb256a7e86e3572a82a75d51c6850efdbdc + +Merged 2016-07-02. + + +2015-03-01 - Suppress false error message on PPPoE disconnect + 219aac3b53d0827549377f1bfe22853ee52d4405 + +PPPoE plugin, not in the port. + + +2015-03-01 - Send PADT on PPPoE disconnect + cd2c14f998c57bbe6a01dc5854f2763c0d7f31fb + +PPPoE plugin, not in the port. And our PPPoE implementation already does +that: pppoe_disconnect() calls pppoe_send_padt(). + + +2015-08-14 - pppd: ipxcp: Prevent buffer overrun on remote router name + fe149de624f96629a7f46732055d8f718c74b856 + +We never ported IPX support. lwIP does not support IPX. + + +2015-03-25 - pppd: Fix ccp_options.mppe type + 234edab99a6bb250cc9ecd384cca27b0c8b475ce + +We found that while working on MPPE support in lwIP, that's our patch ;-) + + +2015-03-24 - pppd: Fix ccp_cilen calculated size if both deflate_correct and deflate_draft are enabled + 094cb8ae4c61db225e67fedadb4964f846dd0c27 + +We found that while working on MPPE support in lwIP, that's our patch ;-) + + +2015-08-14 - Merge branch 'master' of https://github.com/ncopa/ppp + 3a5c9a8fbc8970375cd881151d44e4b6fe249c6a + +Merge commit, we don't care. + + +2015-08-14 - Merge branch 'master' of git://github.com/vapier/ppp + 912e4fc6665aca188dced7ea7fdc663ce5a2dd24 + +Merge commit, we don't care. + + +2015-08-14 - Merge branch 'bug_fix' of git://github.com/radaiming/ppp + dfd33d7f526ecd7b39dd1bba8101260d02af5ebb + +Merge commit, we don't care. + + +2015-08-14 - Merge branch 'master' of git://github.com/pprindeville/ppp + aa4a985f6114d08cf4e47634fb6325da71016473 + +Merge commit, we don't care. + + +2015-08-14 - Merge branch 'no-error-on-already-closed' of git://github.com/farnz/ppp + 6edf252483b30dbcdcc5059f01831455365d5b6e + +Merge commit, we don't care. + + +2015-08-14 - Merge branch 'send-padt-on-disconnect' of git://github.com/farnz/ppp + 84684243d651f55f6df69d2a6707b52fbbe62bb9 + +Merge commit, we don't care. diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/auth.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/auth.c new file mode 100644 index 0000000..c8673ad --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/auth.c @@ -0,0 +1,2510 @@ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Derived from main.c, which is: + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_PATH_LASTLOG) && defined(__linux__) +#include +#endif + +#include +#include +#include + +#ifdef HAS_SHADOW +#include +#ifndef PW_PPP +#define PW_PPP PW_LOGIN +#endif +#endif + +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" +#if CCP_SUPPORT +#include "netif/ppp/ccp.h" +#endif /* CCP_SUPPORT */ +#if ECP_SUPPORT +#include "netif/ppp/ecp.h" +#endif /* ECP_SUPPORT */ +#include "netif/ppp/ipcp.h" +#if PAP_SUPPORT +#include "netif/ppp/upap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "netif/ppp/chap-new.h" +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT +#include "netif/ppp/eap.h" +#endif /* EAP_SUPPORT */ +#if CBCP_SUPPORT +#include "netif/ppp/cbcp.h" +#endif + +#if 0 /* UNUSED */ +#include "session.h" +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* Bits in scan_authfile return value */ +#define NONWILD_SERVER 1 +#define NONWILD_CLIENT 2 + +#define ISWILD(word) (word[0] == '*' && word[1] == 0) +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* List of addresses which the peer may use. */ +static struct permitted_ip *addresses[NUM_PPP]; + +/* Wordlist giving addresses which the peer may use + without authenticating itself. */ +static struct wordlist *noauth_addrs; + +/* Remote telephone number, if available */ +char remote_number[MAXNAMELEN]; + +/* Wordlist giving remote telephone numbers which may connect. */ +static struct wordlist *permitted_numbers; + +/* Extra options to apply, from the secrets file entry for the peer. */ +static struct wordlist *extra_options; +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* Set if we require authentication only because we have a default route. */ +static bool default_auth; + +/* Hook to enable a plugin to control the idle time limit */ +int (*idle_time_hook) (struct ppp_idle *) = NULL; + +/* Hook for a plugin to say whether we can possibly authenticate any peer */ +int (*pap_check_hook) (void) = NULL; + +/* Hook for a plugin to check the PAP user and password */ +int (*pap_auth_hook) (char *user, char *passwd, char **msgp, + struct wordlist **paddrs, + struct wordlist **popts) = NULL; + +/* Hook for a plugin to know about the PAP user logout */ +void (*pap_logout_hook) (void) = NULL; + +/* Hook for a plugin to get the PAP password for authenticating us */ +int (*pap_passwd_hook) (char *user, char *passwd) = NULL; + +/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */ +int (*chap_check_hook) (void) = NULL; + +/* Hook for a plugin to get the CHAP password for authenticating us */ +int (*chap_passwd_hook) (char *user, char *passwd) = NULL; + +/* Hook for a plugin to say whether it is OK if the peer + refuses to authenticate. */ +int (*null_auth_hook) (struct wordlist **paddrs, + struct wordlist **popts) = NULL; + +int (*allowed_address_hook) (u32_t addr) = NULL; +#endif /* UNUSED */ + +#ifdef HAVE_MULTILINK +/* Hook for plugin to hear when an interface joins a multilink bundle */ +void (*multilink_join_hook) (void) = NULL; +#endif + +#if PPP_NOTIFY +/* A notifier for when the peer has authenticated itself, + and we are proceeding to the network phase. */ +struct notifier *auth_up_notifier = NULL; + +/* A notifier for when the link goes down. */ +struct notifier *link_down_notifier = NULL; +#endif /* PPP_NOTIFY */ + +/* + * Option variables. + */ +#if 0 /* MOVED TO ppp_settings */ +bool uselogin = 0; /* Use /etc/passwd for checking PAP */ +bool session_mgmt = 0; /* Do session management (login records) */ +bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */ +bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ +bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ +bool refuse_eap = 0; /* Don't wanna auth. ourselves with EAP */ +#if MSCHAP_SUPPORT +bool refuse_mschap = 0; /* Don't wanna auth. ourselves with MS-CHAP */ +bool refuse_mschap_v2 = 0; /* Don't wanna auth. ourselves with MS-CHAPv2 */ +#else /* MSCHAP_SUPPORT */ +bool refuse_mschap = 1; /* Don't wanna auth. ourselves with MS-CHAP */ +bool refuse_mschap_v2 = 1; /* Don't wanna auth. ourselves with MS-CHAPv2 */ +#endif /* MSCHAP_SUPPORT */ +bool usehostname = 0; /* Use hostname for our_name */ +bool auth_required = 0; /* Always require authentication from peer */ +bool allow_any_ip = 0; /* Allow peer to use any IP address */ +bool explicit_remote = 0; /* User specified explicit remote name */ +bool explicit_user = 0; /* Set if "user" option supplied */ +bool explicit_passwd = 0; /* Set if "password" option supplied */ +char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ +static char *uafname; /* name of most recent +ua file */ + +extern char *crypt (const char *, const char *); +#endif /* UNUSED */ +/* Prototypes for procedures local to this file. */ + +static void network_phase(ppp_pcb *pcb); +#if PPP_IDLETIMELIMIT +static void check_idle(void *arg); +#endif /* PPP_IDLETIMELIMIT */ +#if PPP_MAXCONNECT +static void connect_time_expired(void *arg); +#endif /* PPP_MAXCONNECT */ +#if 0 /* UNUSED */ +static int null_login (int); +/* static int get_pap_passwd (char *); */ +static int have_pap_secret (int *); +static int have_chap_secret (char *, char *, int, int *); +static int have_srp_secret (char *client, char *server, int need_ip, + int *lacks_ipp); +static int ip_addr_check (u32_t, struct permitted_ip *); +static int scan_authfile (FILE *, char *, char *, char *, + struct wordlist **, struct wordlist **, + char *, int); +static void free_wordlist (struct wordlist *); +static void set_allowed_addrs (int, struct wordlist *, struct wordlist *); +static int some_ip_ok (struct wordlist *); +static int setupapfile (char **); +static int privgroup (char **); +static int set_noauth_addr (char **); +static int set_permitted_number (char **); +static void check_access (FILE *, char *); +static int wordlist_count (struct wordlist *); +#endif /* UNUSED */ + +#ifdef MAXOCTETS +static void check_maxoctets (void *); +#endif + +#if PPP_OPTIONS +/* + * Authentication-related options. + */ +option_t auth_options[] = { + { "auth", o_bool, &auth_required, + "Require authentication from peer", OPT_PRIO | 1 }, + { "noauth", o_bool, &auth_required, + "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV, + &allow_any_ip }, + { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", + OPT_PRIOSUB | 1, &auth_required }, + { "+pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required }, + { "require-chap", o_bool, &auth_required, + "Require CHAP authentication from peer", + OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5, + &lcp_wantoptions[0].chap_mdtype }, + { "+chap", o_bool, &auth_required, + "Require CHAP authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5, + &lcp_wantoptions[0].chap_mdtype }, +#if MSCHAP_SUPPORT + { "require-mschap", o_bool, &auth_required, + "Require MS-CHAP authentication from peer", + OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT, + &lcp_wantoptions[0].chap_mdtype }, + { "+mschap", o_bool, &auth_required, + "Require MS-CHAP authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT, + &lcp_wantoptions[0].chap_mdtype }, + { "require-mschap-v2", o_bool, &auth_required, + "Require MS-CHAPv2 authentication from peer", + OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2, + &lcp_wantoptions[0].chap_mdtype }, + { "+mschap-v2", o_bool, &auth_required, + "Require MS-CHAPv2 authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2, + &lcp_wantoptions[0].chap_mdtype }, +#endif /* MSCHAP_SUPPORT */ +#if 0 + { "refuse-pap", o_bool, &refuse_pap, + "Don't agree to auth to peer with PAP", 1 }, + { "-pap", o_bool, &refuse_pap, + "Don't allow PAP authentication with peer", OPT_ALIAS | 1 }, + { "refuse-chap", o_bool, &refuse_chap, + "Don't agree to auth to peer with CHAP", + OPT_A2CLRB | MDTYPE_MD5, + &lcp_allowoptions[0].chap_mdtype }, + { "-chap", o_bool, &refuse_chap, + "Don't allow CHAP authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5, + &lcp_allowoptions[0].chap_mdtype }, +#endif +#if MSCHAP_SUPPORT +#if 0 + { "refuse-mschap", o_bool, &refuse_mschap, + "Don't agree to auth to peer with MS-CHAP", + OPT_A2CLRB | MDTYPE_MICROSOFT, + &lcp_allowoptions[0].chap_mdtype }, + { "-mschap", o_bool, &refuse_mschap, + "Don't allow MS-CHAP authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT, + &lcp_allowoptions[0].chap_mdtype }, + { "refuse-mschap-v2", o_bool, &refuse_mschap_v2, + "Don't agree to auth to peer with MS-CHAPv2", + OPT_A2CLRB | MDTYPE_MICROSOFT_V2, + &lcp_allowoptions[0].chap_mdtype }, + { "-mschap-v2", o_bool, &refuse_mschap_v2, + "Don't allow MS-CHAPv2 authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2, + &lcp_allowoptions[0].chap_mdtype }, +#endif +#endif /* MSCHAP_SUPPORT*/ +#if EAP_SUPPORT + { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap, + "Require EAP authentication from peer", OPT_PRIOSUB | 1, + &auth_required }, +#if 0 + { "refuse-eap", o_bool, &refuse_eap, + "Don't agree to authenticate to peer with EAP", 1 }, +#endif +#endif /* EAP_SUPPORT */ + { "name", o_string, our_name, + "Set local name for authentication", + OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN }, + + { "+ua", o_special, (void *)setupapfile, + "Get PAP user and password from file", + OPT_PRIO | OPT_A2STRVAL, &uafname }, + +#if 0 + { "user", o_string, user, + "Set name for auth with peer", OPT_PRIO | OPT_STATIC, + &explicit_user, MAXNAMELEN }, + + { "password", o_string, passwd, + "Password for authenticating us to the peer", + OPT_PRIO | OPT_STATIC | OPT_HIDE, + &explicit_passwd, MAXSECRETLEN }, +#endif + + { "usehostname", o_bool, &usehostname, + "Must use hostname for authentication", 1 }, + + { "remotename", o_string, remote_name, + "Set remote name for authentication", OPT_PRIO | OPT_STATIC, + &explicit_remote, MAXNAMELEN }, + + { "login", o_bool, &uselogin, + "Use system password database for PAP", OPT_A2COPY | 1 , + &session_mgmt }, + { "enable-session", o_bool, &session_mgmt, + "Enable session accounting for remote peers", OPT_PRIV | 1 }, + + { "papcrypt", o_bool, &cryptpap, + "PAP passwords are encrypted", 1 }, + + { "privgroup", o_special, (void *)privgroup, + "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST }, + + { "allow-ip", o_special, (void *)set_noauth_addr, + "Set IP address(es) which can be used without authentication", + OPT_PRIV | OPT_A2LIST }, + + { "remotenumber", o_string, remote_number, + "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC, + NULL, MAXNAMELEN }, + + { "allow-number", o_special, (void *)set_permitted_number, + "Set telephone number(s) which are allowed to connect", + OPT_PRIV | OPT_A2LIST }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +#if 0 /* UNUSED */ +/* + * setupapfile - specifies UPAP info for authenticating with peer. + */ +static int +setupapfile(argv) + char **argv; +{ + FILE *ufile; + int l; + uid_t euid; + char u[MAXNAMELEN], p[MAXSECRETLEN]; + char *fname; + + lcp_allowoptions[0].neg_upap = 1; + + /* open user info file */ + fname = strdup(*argv); + if (fname == NULL) + novm("+ua file name"); + euid = geteuid(); + if (seteuid(getuid()) == -1) { + option_error("unable to reset uid before opening %s: %m", fname); + return 0; + } + ufile = fopen(fname, "r"); + if (seteuid(euid) == -1) + fatal("unable to regain privileges: %m"); + if (ufile == NULL) { + option_error("unable to open user login data file %s", fname); + return 0; + } + check_access(ufile, fname); + uafname = fname; + + /* get username */ + if (fgets(u, MAXNAMELEN - 1, ufile) == NULL + || fgets(p, MAXSECRETLEN - 1, ufile) == NULL) { + fclose(ufile); + option_error("unable to read user login data file %s", fname); + return 0; + } + fclose(ufile); + + /* get rid of newlines */ + l = strlen(u); + if (l > 0 && u[l-1] == '\n') + u[l-1] = 0; + l = strlen(p); + if (l > 0 && p[l-1] == '\n') + p[l-1] = 0; + + if (override_value("user", option_priority, fname)) { + strlcpy(ppp_settings.user, u, sizeof(ppp_settings.user)); + explicit_user = 1; + } + if (override_value("passwd", option_priority, fname)) { + strlcpy(ppp_settings.passwd, p, sizeof(ppp_settings.passwd)); + explicit_passwd = 1; + } + + return (1); +} + +/* + * privgroup - allow members of the group to have privileged access. + */ +static int +privgroup(argv) + char **argv; +{ + struct group *g; + int i; + + g = getgrnam(*argv); + if (g == 0) { + option_error("group %s is unknown", *argv); + return 0; + } + for (i = 0; i < ngroups; ++i) { + if (groups[i] == g->gr_gid) { + privileged = 1; + break; + } + } + return 1; +} + + +/* + * set_noauth_addr - set address(es) that can be used without authentication. + * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets. + */ +static int +set_noauth_addr(argv) + char **argv; +{ + char *addr = *argv; + int l = strlen(addr) + 1; + struct wordlist *wp; + + wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l); + if (wp == NULL) + novm("allow-ip argument"); + wp->word = (char *) (wp + 1); + wp->next = noauth_addrs; + MEMCPY(wp->word, addr, l); + noauth_addrs = wp; + return 1; +} + + +/* + * set_permitted_number - set remote telephone number(s) that may connect. + */ +static int +set_permitted_number(argv) + char **argv; +{ + char *number = *argv; + int l = strlen(number) + 1; + struct wordlist *wp; + + wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l); + if (wp == NULL) + novm("allow-number argument"); + wp->word = (char *) (wp + 1); + wp->next = permitted_numbers; + MEMCPY(wp->word, number, l); + permitted_numbers = wp; + return 1; +} +#endif + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + */ +void link_required(ppp_pcb *pcb) { + LWIP_UNUSED_ARG(pcb); +} + +#if 0 +/* + * Bring the link up to the point of being able to do ppp. + */ +void start_link(unit) + int unit; +{ + ppp_pcb *pcb = &ppp_pcb_list[unit]; + char *msg; + + status = EXIT_NEGOTIATION_FAILED; + new_phase(pcb, PPP_PHASE_SERIALCONN); + + hungup = 0; + devfd = the_channel->connect(); + msg = "Connect script failed"; + if (devfd < 0) + goto fail; + + /* set up the serial device as a ppp interface */ + /* + * N.B. we used to do tdb_writelock/tdb_writeunlock around this + * (from establish_ppp to set_ifunit). However, we won't be + * doing the set_ifunit in multilink mode, which is the only time + * we need the atomicity that the tdb_writelock/tdb_writeunlock + * gives us. Thus we don't need the tdb_writelock/tdb_writeunlock. + */ + fd_ppp = the_channel->establish_ppp(devfd); + msg = "ppp establishment failed"; + if (fd_ppp < 0) { + status = EXIT_FATAL_ERROR; + goto disconnect; + } + + if (!demand && ifunit >= 0) + set_ifunit(1); + + /* + * Start opening the connection and wait for + * incoming events (reply, timeout, etc.). + */ + if (ifunit >= 0) + ppp_notice("Connect: %s <--> %s", ifname, ppp_devnam); + else + ppp_notice("Starting negotiation on %s", ppp_devnam); + add_fd(fd_ppp); + + new_phase(pcb, PPP_PHASE_ESTABLISH); + + lcp_lowerup(pcb); + return; + + disconnect: + new_phase(pcb, PPP_PHASE_DISCONNECT); + if (the_channel->disconnect) + the_channel->disconnect(); + + fail: + new_phase(pcb, PPP_PHASE_DEAD); + if (the_channel->cleanup) + (*the_channel->cleanup)(); +} +#endif + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void link_terminated(ppp_pcb *pcb) { + if (pcb->phase == PPP_PHASE_DEAD +#ifdef HAVE_MULTILINK + || pcb->phase == PPP_PHASE_MASTER +#endif /* HAVE_MULTILINK */ + ) + return; + new_phase(pcb, PPP_PHASE_DISCONNECT); + +#if 0 /* UNUSED */ + if (pap_logout_hook) { + pap_logout_hook(); + } + session_end(devnam); +#endif /* UNUSED */ + + if (!doing_multilink) { + ppp_notice("Connection terminated."); +#if PPP_STATS_SUPPORT + print_link_stats(); +#endif /* PPP_STATS_SUPPORT */ + } else + ppp_notice("Link terminated."); + + lcp_lowerdown(pcb); + + ppp_link_terminated(pcb); +#if 0 + /* + * Delete pid files before disestablishing ppp. Otherwise it + * can happen that another pppd gets the same unit and then + * we delete its pid file. + */ + if (!doing_multilink && !demand) + remove_pidfiles(); + + /* + * If we may want to bring the link up again, transfer + * the ppp unit back to the loopback. Set the + * real serial device back to its normal mode of operation. + */ + if (fd_ppp >= 0) { + remove_fd(fd_ppp); + clean_check(); + the_channel->disestablish_ppp(devfd); + if (doing_multilink) + mp_exit_bundle(); + fd_ppp = -1; + } + if (!hungup) + lcp_lowerdown(pcb); + if (!doing_multilink && !demand) + script_unsetenv("IFNAME"); + + /* + * Run disconnector script, if requested. + * XXX we may not be able to do this if the line has hung up! + */ + if (devfd >= 0 && the_channel->disconnect) { + the_channel->disconnect(); + devfd = -1; + } + if (the_channel->cleanup) + (*the_channel->cleanup)(); + + if (doing_multilink && multilink_master) { + if (!bundle_terminating) + new_phase(pcb, PPP_PHASE_MASTER); + else + mp_bundle_terminated(); + } else + new_phase(pcb, PPP_PHASE_DEAD); +#endif +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void link_down(ppp_pcb *pcb) { +#if PPP_NOTIFY + notify(link_down_notifier, 0); +#endif /* PPP_NOTIFY */ + + if (!doing_multilink) { + upper_layers_down(pcb); + if (pcb->phase != PPP_PHASE_DEAD +#ifdef HAVE_MULTILINK + && pcb->phase != PPP_PHASE_MASTER +#endif /* HAVE_MULTILINK */ + ) + new_phase(pcb, PPP_PHASE_ESTABLISH); + } + /* XXX if doing_multilink, should do something to stop + network-layer traffic on the link */ +} + +void upper_layers_down(ppp_pcb *pcb) { + int i; + const struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) + (*protp->lowerdown)(pcb); + if (protp->protocol < 0xC000 && protp->close != NULL) + (*protp->close)(pcb, "LCP down"); + } + pcb->num_np_open = 0; + pcb->num_np_up = 0; +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void link_established(ppp_pcb *pcb) { +#if PPP_AUTH_SUPPORT + int auth; +#if PPP_SERVER +#if PAP_SUPPORT + lcp_options *wo = &pcb->lcp_wantoptions; +#endif /* PAP_SUPPORT */ + lcp_options *go = &pcb->lcp_gotoptions; +#endif /* PPP_SERVER */ + lcp_options *ho = &pcb->lcp_hisoptions; +#endif /* PPP_AUTH_SUPPORT */ + int i; + const struct protent *protp; + + /* + * Tell higher-level protocols that LCP is up. + */ + if (!doing_multilink) { + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol != PPP_LCP + && protp->lowerup != NULL) + (*protp->lowerup)(pcb); + } + +#if PPP_AUTH_SUPPORT +#if PPP_SERVER +#if PPP_ALLOWED_ADDRS + if (!auth_required && noauth_addrs != NULL) + set_allowed_addrs(unit, NULL, NULL); +#endif /* PPP_ALLOWED_ADDRS */ + + if (pcb->settings.auth_required && !(0 +#if PAP_SUPPORT + || go->neg_upap +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + || go->neg_chap +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || go->neg_eap +#endif /* EAP_SUPPORT */ + )) { + +#if PPP_ALLOWED_ADDRS + /* + * We wanted the peer to authenticate itself, and it refused: + * if we have some address(es) it can use without auth, fine, + * otherwise treat it as though it authenticated with PAP using + * a username of "" and a password of "". If that's not OK, + * boot it out. + */ + if (noauth_addrs != NULL) { + set_allowed_addrs(unit, NULL, NULL); + } else +#endif /* PPP_ALLOWED_ADDRS */ + if (!pcb->settings.null_login +#if PAP_SUPPORT + || !wo->neg_upap +#endif /* PAP_SUPPORT */ + ) { + ppp_warn("peer refused to authenticate: terminating link"); +#if 0 /* UNUSED */ + status = EXIT_PEER_AUTH_FAILED; +#endif /* UNUSED */ + pcb->err_code = PPPERR_AUTHFAIL; + lcp_close(pcb, "peer refused to authenticate"); + return; + } + } +#endif /* PPP_SERVER */ + + new_phase(pcb, PPP_PHASE_AUTHENTICATE); + auth = 0; +#if PPP_SERVER +#if EAP_SUPPORT + if (go->neg_eap) { + eap_authpeer(pcb, PPP_OUR_NAME); + auth |= EAP_PEER; + } else +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + if (go->neg_chap) { + chap_auth_peer(pcb, PPP_OUR_NAME, CHAP_DIGEST(go->chap_mdtype)); + auth |= CHAP_PEER; + } else +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + if (go->neg_upap) { + upap_authpeer(pcb); + auth |= PAP_PEER; + } else +#endif /* PAP_SUPPORT */ + {} +#endif /* PPP_SERVER */ + +#if EAP_SUPPORT + if (ho->neg_eap) { + eap_authwithpeer(pcb, pcb->settings.user); + auth |= EAP_WITHPEER; + } else +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + if (ho->neg_chap) { + chap_auth_with_peer(pcb, pcb->settings.user, CHAP_DIGEST(ho->chap_mdtype)); + auth |= CHAP_WITHPEER; + } else +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + if (ho->neg_upap) { + upap_authwithpeer(pcb, pcb->settings.user, pcb->settings.passwd); + auth |= PAP_WITHPEER; + } else +#endif /* PAP_SUPPORT */ + {} + + pcb->auth_pending = auth; + pcb->auth_done = 0; + + if (!auth) +#endif /* PPP_AUTH_SUPPORT */ + network_phase(pcb); +} + +/* + * Proceed to the network phase. + */ +static void network_phase(ppp_pcb *pcb) { +#if CBCP_SUPPORT + ppp_pcb *pcb = &ppp_pcb_list[unit]; +#endif +#if 0 /* UNUSED */ + lcp_options *go = &lcp_gotoptions[unit]; +#endif /* UNUSED */ + +#if 0 /* UNUSED */ + /* Log calling number. */ + if (*remote_number) + ppp_notice("peer from calling number %q authorized", remote_number); +#endif /* UNUSED */ + +#if PPP_NOTIFY + /* + * If the peer had to authenticate, notify it now. + */ + if (0 +#if CHAP_SUPPORT + || go->neg_chap +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + || go->neg_upap +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + || go->neg_eap +#endif /* EAP_SUPPORT */ + ) { + notify(auth_up_notifier, 0); + } +#endif /* PPP_NOTIFY */ + +#if CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + new_phase(pcb, PPP_PHASE_CALLBACK); + (*cbcp_protent.open)(pcb); + return; + } +#endif + +#if PPP_OPTIONS + /* + * Process extra options from the secrets file + */ + if (extra_options) { + options_from_list(extra_options, 1); + free_wordlist(extra_options); + extra_options = 0; + } +#endif /* PPP_OPTIONS */ + start_networks(pcb); +} + +void start_networks(ppp_pcb *pcb) { +#if CCP_SUPPORT || ECP_SUPPORT + int i; + const struct protent *protp; +#endif /* CCP_SUPPORT || ECP_SUPPORT */ + + new_phase(pcb, PPP_PHASE_NETWORK); + +#ifdef HAVE_MULTILINK + if (multilink) { + if (mp_join_bundle()) { + if (multilink_join_hook) + (*multilink_join_hook)(); + if (updetach && !nodetach) + detach(); + return; + } + } +#endif /* HAVE_MULTILINK */ + +#ifdef PPP_FILTER + if (!demand) + set_filters(&pass_filter, &active_filter); +#endif +#if CCP_SUPPORT || ECP_SUPPORT + /* Start CCP and ECP */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if ( + (0 +#if ECP_SUPPORT + || protp->protocol == PPP_ECP +#endif /* ECP_SUPPORT */ +#if CCP_SUPPORT + || protp->protocol == PPP_CCP +#endif /* CCP_SUPPORT */ + ) + && protp->open != NULL) + (*protp->open)(pcb); +#endif /* CCP_SUPPORT || ECP_SUPPORT */ + + /* + * Bring up other network protocols iff encryption is not required. + */ + if (1 +#if ECP_SUPPORT + && !ecp_gotoptions[unit].required +#endif /* ECP_SUPPORT */ +#if MPPE_SUPPORT + && !pcb->ccp_gotoptions.mppe +#endif /* MPPE_SUPPORT */ + ) + continue_networks(pcb); +} + +void continue_networks(ppp_pcb *pcb) { + int i; + const struct protent *protp; + + /* + * Start the "real" network protocols. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol < 0xC000 +#if CCP_SUPPORT + && protp->protocol != PPP_CCP +#endif /* CCP_SUPPORT */ +#if ECP_SUPPORT + && protp->protocol != PPP_ECP +#endif /* ECP_SUPPORT */ + && protp->open != NULL) { + (*protp->open)(pcb); + ++pcb->num_np_open; + } + + if (pcb->num_np_open == 0) + /* nothing to do */ + lcp_close(pcb, "No network protocols running"); +} + +#if PPP_AUTH_SUPPORT +#if PPP_SERVER +/* + * auth_check_passwd - Check the user name and passwd against configuration. + * + * returns: + * 0: Authentication failed. + * 1: Authentication succeeded. + * In either case, msg points to an appropriate message and msglen to the message len. + */ +int auth_check_passwd(ppp_pcb *pcb, char *auser, int userlen, char *apasswd, int passwdlen, const char **msg, int *msglen) { + int secretuserlen; + int secretpasswdlen; + + if (pcb->settings.user && pcb->settings.passwd) { + secretuserlen = (int)strlen(pcb->settings.user); + secretpasswdlen = (int)strlen(pcb->settings.passwd); + if (secretuserlen == userlen + && secretpasswdlen == passwdlen + && !memcmp(auser, pcb->settings.user, userlen) + && !memcmp(apasswd, pcb->settings.passwd, passwdlen) ) { + *msg = "Login ok"; + *msglen = sizeof("Login ok")-1; + return 1; + } + } + + *msg = "Login incorrect"; + *msglen = sizeof("Login incorrect")-1; + return 0; +} + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void auth_peer_fail(ppp_pcb *pcb, int protocol) { + LWIP_UNUSED_ARG(protocol); + /* + * Authentication failure: take the link down + */ +#if 0 /* UNUSED */ + status = EXIT_PEER_AUTH_FAILED; +#endif /* UNUSED */ + pcb->err_code = PPPERR_AUTHFAIL; + lcp_close(pcb, "Authentication failed"); +} + +/* + * The peer has been successfully authenticated using `protocol'. + */ +void auth_peer_success(ppp_pcb *pcb, int protocol, int prot_flavor, const char *name, int namelen) { + int bit; +#ifndef HAVE_MULTILINK + LWIP_UNUSED_ARG(name); + LWIP_UNUSED_ARG(namelen); +#endif /* HAVE_MULTILINK */ + + switch (protocol) { +#if CHAP_SUPPORT + case PPP_CHAP: + bit = CHAP_PEER; + switch (prot_flavor) { + case CHAP_MD5: + bit |= CHAP_MD5_PEER; + break; +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + bit |= CHAP_MS_PEER; + break; + case CHAP_MICROSOFT_V2: + bit |= CHAP_MS2_PEER; + break; +#endif /* MSCHAP_SUPPORT */ + default: + break; + } + break; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + case PPP_PAP: + bit = PAP_PEER; + break; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + case PPP_EAP: + bit = EAP_PEER; + break; +#endif /* EAP_SUPPORT */ + default: + ppp_warn("auth_peer_success: unknown protocol %x", protocol); + return; + } + +#ifdef HAVE_MULTILINK + /* + * Save the authenticated name of the peer for later. + */ + if (namelen > (int)sizeof(pcb->peer_authname) - 1) + namelen = (int)sizeof(pcb->peer_authname) - 1; + MEMCPY(pcb->peer_authname, name, namelen); + pcb->peer_authname[namelen] = 0; +#endif /* HAVE_MULTILINK */ +#if 0 /* UNUSED */ + script_setenv("PEERNAME", , 0); +#endif /* UNUSED */ + + /* Save the authentication method for later. */ + pcb->auth_done |= bit; + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((pcb->auth_pending &= ~bit) == 0) + network_phase(pcb); +} +#endif /* PPP_SERVER */ + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void auth_withpeer_fail(ppp_pcb *pcb, int protocol) { + LWIP_UNUSED_ARG(protocol); + /* + * We've failed to authenticate ourselves to our peer. + * + * Some servers keep sending CHAP challenges, but there + * is no point in persisting without any way to get updated + * authentication secrets. + * + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ + pcb->err_code = PPPERR_AUTHFAIL; + lcp_close(pcb, "Failed to authenticate ourselves to peer"); +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor) { + int bit; + const char *prot = ""; + + switch (protocol) { +#if CHAP_SUPPORT + case PPP_CHAP: + bit = CHAP_WITHPEER; + prot = "CHAP"; + switch (prot_flavor) { + case CHAP_MD5: + bit |= CHAP_MD5_WITHPEER; + break; +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + bit |= CHAP_MS_WITHPEER; + break; + case CHAP_MICROSOFT_V2: + bit |= CHAP_MS2_WITHPEER; + break; +#endif /* MSCHAP_SUPPORT */ + default: + break; + } + break; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + case PPP_PAP: + bit = PAP_WITHPEER; + prot = "PAP"; + break; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + case PPP_EAP: + bit = EAP_WITHPEER; + prot = "EAP"; + break; +#endif /* EAP_SUPPORT */ + default: + ppp_warn("auth_withpeer_success: unknown protocol %x", protocol); + bit = 0; + /* no break */ + } + + ppp_notice("%s authentication succeeded", prot); + + /* Save the authentication method for later. */ + pcb->auth_done |= bit; + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((pcb->auth_pending &= ~bit) == 0) + network_phase(pcb); +} +#endif /* PPP_AUTH_SUPPORT */ + + +/* + * np_up - a network protocol has come up. + */ +void np_up(ppp_pcb *pcb, int proto) { +#if PPP_IDLETIMELIMIT + int tlim; +#endif /* PPP_IDLETIMELIMIT */ + LWIP_UNUSED_ARG(proto); + + if (pcb->num_np_up == 0) { + /* + * At this point we consider that the link has come up successfully. + */ + new_phase(pcb, PPP_PHASE_RUNNING); + +#if PPP_IDLETIMELIMIT +#if 0 /* UNUSED */ + if (idle_time_hook != 0) + tlim = (*idle_time_hook)(NULL); + else +#endif /* UNUSED */ + tlim = pcb->settings.idle_time_limit; + if (tlim > 0) + TIMEOUT(check_idle, (void*)pcb, tlim); +#endif /* PPP_IDLETIMELIMIT */ + +#if PPP_MAXCONNECT + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (pcb->settings.maxconnect > 0) + TIMEOUT(connect_time_expired, (void*)pcb, pcb->settings.maxconnect); +#endif /* PPP_MAXCONNECT */ + +#ifdef MAXOCTETS + if (maxoctets > 0) + TIMEOUT(check_maxoctets, NULL, maxoctets_timeout); +#endif + +#if 0 /* Unused */ + /* + * Detach now, if the updetach option was given. + */ + if (updetach && !nodetach) + detach(); +#endif /* Unused */ + } + ++pcb->num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void np_down(ppp_pcb *pcb, int proto) { + LWIP_UNUSED_ARG(proto); + if (--pcb->num_np_up == 0) { +#if PPP_IDLETIMELIMIT + UNTIMEOUT(check_idle, (void*)pcb); +#endif /* PPP_IDLETIMELIMIT */ +#if PPP_MAXCONNECT + UNTIMEOUT(connect_time_expired, NULL); +#endif /* PPP_MAXCONNECT */ +#ifdef MAXOCTETS + UNTIMEOUT(check_maxoctets, NULL); +#endif + new_phase(pcb, PPP_PHASE_NETWORK); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void np_finished(ppp_pcb *pcb, int proto) { + LWIP_UNUSED_ARG(proto); + if (--pcb->num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(pcb, "No network protocols running"); + } +} + +#ifdef MAXOCTETS +static void +check_maxoctets(arg) + void *arg; +{ +#if PPP_STATS_SUPPORT + unsigned int used; + + update_link_stats(ifunit); + link_stats_valid=0; + + switch(maxoctets_dir) { + case PPP_OCTETS_DIRECTION_IN: + used = link_stats.bytes_in; + break; + case PPP_OCTETS_DIRECTION_OUT: + used = link_stats.bytes_out; + break; + case PPP_OCTETS_DIRECTION_MAXOVERAL: + case PPP_OCTETS_DIRECTION_MAXSESSION: + used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out; + break; + default: + used = link_stats.bytes_in+link_stats.bytes_out; + break; + } + if (used > maxoctets) { + ppp_notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used); + status = EXIT_TRAFFIC_LIMIT; + lcp_close(pcb, "Traffic limit"); +#if 0 /* UNUSED */ + need_holdoff = 0; +#endif /* UNUSED */ + } else { + TIMEOUT(check_maxoctets, NULL, maxoctets_timeout); + } +#endif /* PPP_STATS_SUPPORT */ +} +#endif /* MAXOCTETS */ + +#if PPP_IDLETIMELIMIT +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void check_idle(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + struct ppp_idle idle; + time_t itime; + int tlim; + + if (!get_idle_time(pcb, &idle)) + return; +#if 0 /* UNUSED */ + if (idle_time_hook != 0) { + tlim = idle_time_hook(&idle); + } else { +#endif /* UNUSED */ + itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); + tlim = pcb->settings.idle_time_limit - itime; +#if 0 /* UNUSED */ + } +#endif /* UNUSED */ + if (tlim <= 0) { + /* link is idle: shut it down. */ + ppp_notice("Terminating connection due to lack of activity."); + pcb->err_code = PPPERR_IDLETIMEOUT; + lcp_close(pcb, "Link inactive"); +#if 0 /* UNUSED */ + need_holdoff = 0; +#endif /* UNUSED */ + } else { + TIMEOUT(check_idle, (void*)pcb, tlim); + } +} +#endif /* PPP_IDLETIMELIMIT */ + +#if PPP_MAXCONNECT +/* + * connect_time_expired - log a message and close the connection. + */ +static void connect_time_expired(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + ppp_info("Connect time expired"); + pcb->err_code = PPPERR_CONNECTTIME; + lcp_close(pcb, "Connect time expired"); /* Close connection */ +} +#endif /* PPP_MAXCONNECT */ + +#if PPP_OPTIONS +/* + * auth_check_options - called to check authentication options. + */ +void +auth_check_options() +{ + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + int lacks_ip; + + /* Default our_name to hostname, and user to our_name */ + if (our_name[0] == 0 || usehostname) + strlcpy(our_name, hostname, sizeof(our_name)); + /* If a blank username was explicitly given as an option, trust + the user and don't use our_name */ + if (ppp_settings.user[0] == 0 && !explicit_user) + strlcpy(ppp_settings.user, our_name, sizeof(ppp_settings.user)); + + /* + * If we have a default route, require the peer to authenticate + * unless the noauth option was given or the real user is root. + */ + if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) { + auth_required = 1; + default_auth = 1; + } + +#if CHAP_SUPPORT + /* If we selected any CHAP flavors, we should probably negotiate it. :-) */ + if (wo->chap_mdtype) + wo->neg_chap = 1; +#endif /* CHAP_SUPPORT */ + + /* If authentication is required, ask peer for CHAP, PAP, or EAP. */ + if (auth_required) { + allow_any_ip = 0; + if (1 +#if CHAP_SUPPORT + && !wo->neg_chap +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + && !wo->neg_upap +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + && !wo->neg_eap +#endif /* EAP_SUPPORT */ + ) { +#if CHAP_SUPPORT + wo->neg_chap = CHAP_MDTYPE_SUPPORTED != MDTYPE_NONE; + wo->chap_mdtype = CHAP_MDTYPE_SUPPORTED; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + wo->neg_upap = 1; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + wo->neg_eap = 1; +#endif /* EAP_SUPPORT */ + } + } else { +#if CHAP_SUPPORT + wo->neg_chap = 0; + wo->chap_mdtype = MDTYPE_NONE; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + wo->neg_upap = 0; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + wo->neg_eap = 0; +#endif /* EAP_SUPPORT */ + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. Note that EAP can authenticate by way + * of a CHAP-like exchanges as well as SRP. + */ + lacks_ip = 0; +#if PAP_SUPPORT + can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip)); +#else + can_auth = 0; +#endif /* PAP_SUPPORT */ + if (!can_auth && (0 +#if CHAP_SUPPORT + || wo->neg_chap +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || wo->neg_eap +#endif /* EAP_SUPPORT */ + )) { +#if CHAP_SUPPORT + can_auth = have_chap_secret((explicit_remote? remote_name: NULL), + our_name, 1, &lacks_ip); +#else + can_auth = 0; +#endif + } + if (!can_auth +#if EAP_SUPPORT + && wo->neg_eap +#endif /* EAP_SUPPORT */ + ) { + can_auth = have_srp_secret((explicit_remote? remote_name: NULL), + our_name, 1, &lacks_ip); + } + + if (auth_required && !can_auth && noauth_addrs == NULL) { + if (default_auth) { + option_error( +"By default the remote system is required to authenticate itself"); + option_error( +"(because this system has a default route to the internet)"); + } else if (explicit_remote) + option_error( +"The remote system (%s) is required to authenticate itself", + remote_name); + else + option_error( +"The remote system is required to authenticate itself"); + option_error( +"but I couldn't find any suitable secret (password) for it to use to do so."); + if (lacks_ip) + option_error( +"(None of the available passwords would let it use an IP address.)"); + + exit(1); + } + + /* + * Early check for remote number authorization. + */ + if (!auth_number()) { + ppp_warn("calling number %q is not authorized", remote_number); + exit(EXIT_CNID_AUTH_FAILED); + } +} +#endif /* PPP_OPTIONS */ + +#if 0 /* UNUSED */ +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void +auth_reset(unit) + int unit; +{ + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + int hadchap; + + hadchap = -1; + ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL)); + ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2) + && (passwd[0] != 0 || + (hadchap = have_chap_secret(user, (explicit_remote? remote_name: + NULL), 0, NULL))); + ao->neg_eap = !refuse_eap && ( + passwd[0] != 0 || + (hadchap == 1 || (hadchap == -1 && have_chap_secret(user, + (explicit_remote? remote_name: NULL), 0, NULL))) || + have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL)); + + hadchap = -1; + if (go->neg_upap && !uselogin && !have_pap_secret(NULL)) + go->neg_upap = 0; + if (go->neg_chap) { + if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL), + our_name, 1, NULL))) + go->neg_chap = 0; + } + if (go->neg_eap && + (hadchap == 0 || (hadchap == -1 && + !have_chap_secret((explicit_remote? remote_name: NULL), our_name, + 1, NULL))) && + !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1, + NULL)) + go->neg_eap = 0; +} + +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +int +check_passwd(unit, auser, userlen, apasswd, passwdlen, msg) + int unit; + char *auser; + int userlen; + char *apasswd; + int passwdlen; + char **msg; +{ + return UPAP_AUTHNAK; + int ret; + char *filename; + FILE *f; + struct wordlist *addrs = NULL, *opts = NULL; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static int attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + * If there are unprintable characters in the password, make + * them visible. + */ + slprintf(ppp_settings.passwd, sizeof(ppp_settings.passwd), "%.*v", passwdlen, apasswd); + slprintf(ppp_settings.user, sizeof(ppp_settings.user), "%.*v", userlen, auser); + *msg = ""; + + /* + * Check if a plugin wants to handle this. + */ + if (pap_auth_hook) { + ret = (*pap_auth_hook)(ppp_settings.user, ppp_settings.passwd, msg, &addrs, &opts); + if (ret >= 0) { + /* note: set_allowed_addrs() saves opts (but not addrs): + don't free it! */ + if (ret) + set_allowed_addrs(unit, addrs, opts); + else if (opts != 0) + free_wordlist(opts); + if (addrs != 0) + free_wordlist(addrs); + BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd)); + return ret? UPAP_AUTHACK: UPAP_AUTHNAK; + } + } + + /* + * Open the file of pap secrets and scan for a suitable secret + * for authenticating this user. + */ + filename = _PATH_UPAPFILE; + addrs = opts = NULL; + ret = UPAP_AUTHNAK; + f = fopen(filename, "r"); + if (f == NULL) { + ppp_error("Can't open PAP password file %s: %m", filename); + + } else { + check_access(f, filename); + if (scan_authfile(f, ppp_settings.user, our_name, secret, &addrs, &opts, filename, 0) < 0) { + ppp_warn("no PAP secret found for %s", user); + } else { + /* + * If the secret is "@login", it means to check + * the password against the login database. + */ + int login_secret = strcmp(secret, "@login") == 0; + ret = UPAP_AUTHACK; + if (uselogin || login_secret) { + /* login option or secret is @login */ + if (session_full(ppp_settings.user, ppp_settings.passwd, devnam, msg) == 0) { + ret = UPAP_AUTHNAK; + } + } else if (session_mgmt) { + if (session_check(ppp_settings.user, NULL, devnam, NULL) == 0) { + ppp_warn("Peer %q failed PAP Session verification", user); + ret = UPAP_AUTHNAK; + } + } + if (secret[0] != 0 && !login_secret) { + /* password given in pap-secrets - must match */ + if ((cryptpap || strcmp(ppp_settings.passwd, secret) != 0) + && strcmp(crypt(ppp_settings.passwd, secret), secret) != 0) + ret = UPAP_AUTHNAK; + } + } + fclose(f); + } + + if (ret == UPAP_AUTHNAK) { + if (**msg == 0) + *msg = "Login incorrect"; + /* + * XXX can we ever get here more than once?? + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + ppp_warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user); + lcp_close(pcb, "login failed"); + } + if (attempts > 3) + sleep((u_int) (attempts - 3) * 5); + if (opts != NULL) + free_wordlist(opts); + + } else { + attempts = 0; /* Reset count */ + if (**msg == 0) + *msg = "Login ok"; + set_allowed_addrs(unit, addrs, opts); + } + + if (addrs != NULL) + free_wordlist(addrs); + BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd)); + BZERO(secret, sizeof(secret)); + + return ret; +} + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(unit) + int unit; +{ + char *filename; + FILE *f; + int i, ret; + struct wordlist *addrs, *opts; + char secret[MAXWORDLEN]; + + /* + * Check if a plugin wants to handle this. + */ + ret = -1; + if (null_auth_hook) + ret = (*null_auth_hook)(&addrs, &opts); + + /* + * Open the file of pap secrets and scan for a suitable secret. + */ + if (ret <= 0) { + filename = _PATH_UPAPFILE; + addrs = NULL; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + check_access(f, filename); + + i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename, 0); + ret = i >= 0 && secret[0] == 0; + BZERO(secret, sizeof(secret)); + fclose(f); + } + + if (ret) + set_allowed_addrs(unit, addrs, opts); + else if (opts != 0) + free_wordlist(opts); + if (addrs != 0) + free_wordlist(addrs); + + return ret; +} + +/* + * get_pap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null). + */ +static int +get_pap_passwd(passwd) + char *passwd; +{ + char *filename; + FILE *f; + int ret; + char secret[MAXWORDLEN]; + + /* + * Check whether a plugin wants to supply this. + */ + if (pap_passwd_hook) { + ret = (*pap_passwd_hook)(ppp_settings,user, ppp_settings.passwd); + if (ret >= 0) + return ret; + } + + filename = _PATH_UPAPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + check_access(f, filename); + ret = scan_authfile(f, user, + (remote_name[0]? remote_name: NULL), + secret, NULL, NULL, filename, 0); + fclose(f); + if (ret < 0) + return 0; + if (passwd != NULL) + strlcpy(passwd, secret, MAXSECRETLEN); + BZERO(secret, sizeof(secret)); + return 1; +} + +/* + * have_pap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_pap_secret(lacks_ipp) + int *lacks_ipp; +{ + FILE *f; + int ret; + char *filename; + struct wordlist *addrs; + + /* let the plugin decide, if there is one */ + if (pap_check_hook) { + ret = (*pap_check_hook)(); + if (ret >= 0) + return ret; + } + + filename = _PATH_UPAPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name, + NULL, &addrs, NULL, filename, 0); + fclose(f); + if (ret >= 0 && !some_ip_ok(addrs)) { + if (lacks_ipp != 0) + *lacks_ipp = 1; + ret = -1; + } + if (addrs != 0) + free_wordlist(addrs); + + return ret >= 0; +} + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_chap_secret(client, server, need_ip, lacks_ipp) + char *client; + char *server; + int need_ip; + int *lacks_ipp; +{ + FILE *f; + int ret; + char *filename; + struct wordlist *addrs; + + if (chap_check_hook) { + ret = (*chap_check_hook)(); + if (ret >= 0) { + return ret; + } + } + + filename = _PATH_CHAPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + if (client != NULL && client[0] == 0) + client = NULL; + else if (server != NULL && server[0] == 0) + server = NULL; + + ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); + fclose(f); + if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { + if (lacks_ipp != 0) + *lacks_ipp = 1; + ret = -1; + } + if (addrs != 0) + free_wordlist(addrs); + + return ret >= 0; +} + +/* + * have_srp_secret - check whether we have a SRP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_srp_secret(client, server, need_ip, lacks_ipp) + char *client; + char *server; + int need_ip; + int *lacks_ipp; +{ + FILE *f; + int ret; + char *filename; + struct wordlist *addrs; + + filename = _PATH_SRPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + if (client != NULL && client[0] == 0) + client = NULL; + else if (server != NULL && server[0] == 0) + server = NULL; + + ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); + fclose(f); + if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { + if (lacks_ipp != 0) + *lacks_ipp = 1; + ret = -1; + } + if (addrs != 0) + free_wordlist(addrs); + + return ret >= 0; +} +#endif /* UNUSED */ + +#if PPP_AUTH_SUPPORT +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int get_secret(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server) { + int len; + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(am_server); + + if (!client || !client[0] || !pcb->settings.user || !pcb->settings.passwd || strcmp(client, pcb->settings.user)) { + return 0; + } + + len = (int)strlen(pcb->settings.passwd); + if (len > MAXSECRETLEN) { + ppp_error("Secret for %s on %s is too long", client, server); + len = MAXSECRETLEN; + } + + MEMCPY(secret, pcb->settings.passwd, len); + *secret_len = len; + return 1; + +#if 0 /* UNUSED */ + FILE *f; + int ret, len; + char *filename; + struct wordlist *addrs, *opts; + char secbuf[MAXWORDLEN]; + struct wordlist *addrs; + addrs = NULL; + + if (!am_server && ppp_settings.passwd[0] != 0) { + strlcpy(secbuf, ppp_settings.passwd, sizeof(secbuf)); + } else if (!am_server && chap_passwd_hook) { + if ( (*chap_passwd_hook)(client, secbuf) < 0) { + ppp_error("Unable to obtain CHAP password for %s on %s from plugin", + client, server); + return 0; + } + } else { + filename = _PATH_CHAPFILE; + addrs = NULL; + secbuf[0] = 0; + + f = fopen(filename, "r"); + if (f == NULL) { + ppp_error("Can't open chap secret file %s: %m", filename); + return 0; + } + check_access(f, filename); + + ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename, 0); + fclose(f); + if (ret < 0) + return 0; + + if (am_server) + set_allowed_addrs(unit, addrs, opts); + else if (opts != 0) + free_wordlist(opts); + if (addrs != 0) + free_wordlist(addrs); + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + ppp_error("Secret for %s on %s is too long", client, server); + len = MAXSECRETLEN; + } + MEMCPY(secret, secbuf, len); + BZERO(secbuf, sizeof(secbuf)); + *secret_len = len; + + return 1; +#endif /* UNUSED */ +} +#endif /* PPP_AUTH_SUPPORT */ + + +#if 0 /* UNUSED */ +/* + * get_srp_secret - open the SRP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int +get_srp_secret(unit, client, server, secret, am_server) + int unit; + char *client; + char *server; + char *secret; + int am_server; +{ + FILE *fp; + int ret; + char *filename; + struct wordlist *addrs, *opts; + + if (!am_server && ppp_settings.passwd[0] != '\0') { + strlcpy(secret, ppp_settings.passwd, MAXWORDLEN); + } else { + filename = _PATH_SRPFILE; + addrs = NULL; + + fp = fopen(filename, "r"); + if (fp == NULL) { + ppp_error("Can't open srp secret file %s: %m", filename); + return 0; + } + check_access(fp, filename); + + secret[0] = '\0'; + ret = scan_authfile(fp, client, server, secret, &addrs, &opts, + filename, am_server); + fclose(fp); + if (ret < 0) + return 0; + + if (am_server) + set_allowed_addrs(unit, addrs, opts); + else if (opts != NULL) + free_wordlist(opts); + if (addrs != NULL) + free_wordlist(addrs); + } + + return 1; +} + +/* + * set_allowed_addrs() - set the list of allowed addresses. + * Also looks for `--' indicating options to apply for this peer + * and leaves the following words in extra_options. + */ +static void +set_allowed_addrs(unit, addrs, opts) + int unit; + struct wordlist *addrs; + struct wordlist *opts; +{ + int n; + struct wordlist *ap, **plink; + struct permitted_ip *ip; + char *ptr_word, *ptr_mask; + struct hostent *hp; + struct netent *np; + u32_t a, mask, ah, offset; + struct ipcp_options *wo = &ipcp_wantoptions[unit]; + u32_t suggested_ip = 0; + + if (addresses[unit] != NULL) + free(addresses[unit]); + addresses[unit] = NULL; + if (extra_options != NULL) + free_wordlist(extra_options); + extra_options = opts; + + /* + * Count the number of IP addresses given. + */ + n = wordlist_count(addrs) + wordlist_count(noauth_addrs); + if (n == 0) + return; + ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip)); + if (ip == 0) + return; + + /* temporarily append the noauth_addrs list to addrs */ + for (plink = &addrs; *plink != NULL; plink = &(*plink)->next) + ; + *plink = noauth_addrs; + + n = 0; + for (ap = addrs; ap != NULL; ap = ap->next) { + /* "-" means no addresses authorized, "*" means any address allowed */ + ptr_word = ap->word; + if (strcmp(ptr_word, "-") == 0) + break; + if (strcmp(ptr_word, "*") == 0) { + ip[n].permit = 1; + ip[n].base = ip[n].mask = 0; + ++n; + break; + } + + ip[n].permit = 1; + if (*ptr_word == '!') { + ip[n].permit = 0; + ++ptr_word; + } + + mask = ~ (u32_t) 0; + offset = 0; + ptr_mask = strchr (ptr_word, '/'); + if (ptr_mask != NULL) { + int bit_count; + char *endp; + + bit_count = (int) strtol (ptr_mask+1, &endp, 10); + if (bit_count <= 0 || bit_count > 32) { + ppp_warn("invalid address length %v in auth. address list", + ptr_mask+1); + continue; + } + bit_count = 32 - bit_count; /* # bits in host part */ + if (*endp == '+') { + offset = ifunit + 1; + ++endp; + } + if (*endp != 0) { + ppp_warn("invalid address length syntax: %v", ptr_mask+1); + continue; + } + *ptr_mask = '\0'; + mask <<= bit_count; + } + + hp = gethostbyname(ptr_word); + if (hp != NULL && hp->h_addrtype == AF_INET) { + a = *(u32_t *)hp->h_addr; + } else { + np = getnetbyname (ptr_word); + if (np != NULL && np->n_addrtype == AF_INET) { + a = lwip_htonl ((u32_t)np->n_net); + if (ptr_mask == NULL) { + /* calculate appropriate mask for net */ + ah = lwip_ntohl(a); + if (IN_CLASSA(ah)) + mask = IN_CLASSA_NET; + else if (IN_CLASSB(ah)) + mask = IN_CLASSB_NET; + else if (IN_CLASSC(ah)) + mask = IN_CLASSC_NET; + } + } else { + a = inet_addr (ptr_word); + } + } + + if (ptr_mask != NULL) + *ptr_mask = '/'; + + if (a == (u32_t)-1L) { + ppp_warn("unknown host %s in auth. address list", ap->word); + continue; + } + if (offset != 0) { + if (offset >= ~mask) { + ppp_warn("interface unit %d too large for subnet %v", + ifunit, ptr_word); + continue; + } + a = lwip_htonl((lwip_ntohl(a) & mask) + offset); + mask = ~(u32_t)0; + } + ip[n].mask = lwip_htonl(mask); + ip[n].base = a & ip[n].mask; + ++n; + if (~mask == 0 && suggested_ip == 0) + suggested_ip = a; + } + *plink = NULL; + + ip[n].permit = 0; /* make the last entry forbid all addresses */ + ip[n].base = 0; /* to terminate the list */ + ip[n].mask = 0; + + addresses[unit] = ip; + + /* + * If the address given for the peer isn't authorized, or if + * the user hasn't given one, AND there is an authorized address + * which is a single host, then use that if we find one. + */ + if (suggested_ip != 0 + && (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) { + wo->hisaddr = suggested_ip; + /* + * Do we insist on this address? No, if there are other + * addresses authorized than the suggested one. + */ + if (n > 1) + wo->accept_remote = 1; + } +} + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(unit, addr) + int unit; + u32_t addr; +{ + int ok; + + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) + return 0; + + if (allowed_address_hook) { + ok = allowed_address_hook(addr); + if (ok >= 0) return ok; + } + + if (addresses[unit] != NULL) { + ok = ip_addr_check(addr, addresses[unit]); + if (ok >= 0) + return ok; + } + + if (auth_required) + return 0; /* no addresses authorized */ + return allow_any_ip || privileged || !have_route_to(addr); +} + +static int +ip_addr_check(addr, addrs) + u32_t addr; + struct permitted_ip *addrs; +{ + for (; ; ++addrs) + if ((addr & addrs->mask) == addrs->base) + return addrs->permit; +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(addr) + u32_t addr; +{ + addr = lwip_ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + +/* + * some_ip_ok - check a wordlist to see if it authorizes any + * IP address(es). + */ +static int +some_ip_ok(addrs) + struct wordlist *addrs; +{ + for (; addrs != 0; addrs = addrs->next) { + if (addrs->word[0] == '-') + break; + if (addrs->word[0] != '!') + return 1; /* some IP address is allowed */ + } + return 0; +} + +/* + * auth_number - check whether the remote number is allowed to connect. + * Returns 1 if authorized, 0 otherwise. + */ +int +auth_number() +{ + struct wordlist *wp = permitted_numbers; + int l; + + /* Allow all if no authorization list. */ + if (!wp) + return 1; + + /* Allow if we have a match in the authorization list. */ + while (wp) { + /* trailing '*' wildcard */ + l = strlen(wp->word); + if ((wp->word)[l - 1] == '*') + l--; + if (!strncasecmp(wp->word, remote_number, l)) + return 1; + wp = wp->next; + } + + return 0; +} + +/* + * check_access - complain if a secret file has too-liberal permissions. + */ +static void +check_access(f, filename) + FILE *f; + char *filename; +{ + struct stat sbuf; + + if (fstat(fileno(f), &sbuf) < 0) { + ppp_warn("cannot stat secret file %s: %m", filename); + } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + ppp_warn("Warning - secret file %s has world and/or group access", + filename); + } +} + +/* + * scan_authfile - Scan an authorization file for a secret suitable + * for authenticating `client' on `server'. The return value is -1 + * if no secret is found, otherwise >= 0. The return value has + * NONWILD_CLIENT set if the secret didn't have "*" for the client, and + * NONWILD_SERVER set if the secret didn't have "*" for the server. + * Any following words on the line up to a "--" (i.e. address authorization + * info) are placed in a wordlist and returned in *addrs. Any + * following words (extra options) are placed in a wordlist and + * returned in *opts. + * We assume secret is NULL or points to MAXWORDLEN bytes of space. + * Flags are non-zero if we need two colons in the secret in order to + * match. + */ +static int +scan_authfile(f, client, server, secret, addrs, opts, filename, flags) + FILE *f; + char *client; + char *server; + char *secret; + struct wordlist **addrs; + struct wordlist **opts; + char *filename; + int flags; +{ + int newline, xxx; + int got_flag, best_flag; + FILE *sf; + struct wordlist *ap, *addr_list, *alist, **app; + char word[MAXWORDLEN]; + char atfile[MAXWORDLEN]; + char lsecret[MAXWORDLEN]; + char *cp; + + if (addrs != NULL) + *addrs = NULL; + if (opts != NULL) + *opts = NULL; + addr_list = NULL; + if (!getword(f, word, &newline, filename)) + return -1; /* file is empty??? */ + newline = 1; + best_flag = -1; + for (;;) { + /* + * Skip until we find a word at the start of a line. + */ + while (!newline && getword(f, word, &newline, filename)) + ; + if (!newline) + break; /* got to end of file */ + + /* + * Got a client - check if it's a match or a wildcard. + */ + got_flag = 0; + if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) { + newline = 0; + continue; + } + if (!ISWILD(word)) + got_flag = NONWILD_CLIENT; + + /* + * Now get a server and check if it matches. + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + if (!ISWILD(word)) { + if (server != NULL && strcmp(word, server) != 0) + continue; + got_flag |= NONWILD_SERVER; + } + + /* + * Got some sort of a match - see if it's better than what + * we have already. + */ + if (got_flag <= best_flag) + continue; + + /* + * Get the secret. + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + + /* + * SRP-SHA1 authenticator should never be reading secrets from + * a file. (Authenticatee may, though.) + */ + if (flags && ((cp = strchr(word, ':')) == NULL || + strchr(cp + 1, ':') == NULL)) + continue; + + if (secret != NULL) { + /* + * Special syntax: @/pathname means read secret from file. + */ + if (word[0] == '@' && word[1] == '/') { + strlcpy(atfile, word+1, sizeof(atfile)); + if ((sf = fopen(atfile, "r")) == NULL) { + ppp_warn("can't open indirect secret file %s", atfile); + continue; + } + check_access(sf, atfile); + if (!getword(sf, word, &xxx, atfile)) { + ppp_warn("no secret in indirect secret file %s", atfile); + fclose(sf); + continue; + } + fclose(sf); + } + strlcpy(lsecret, word, sizeof(lsecret)); + } + + /* + * Now read address authorization info and make a wordlist. + */ + app = &alist; + for (;;) { + if (!getword(f, word, &newline, filename) || newline) + break; + ap = (struct wordlist *) + malloc(sizeof(struct wordlist) + strlen(word) + 1); + if (ap == NULL) + novm("authorized addresses"); + ap->word = (char *) (ap + 1); + strcpy(ap->word, word); + *app = ap; + app = &ap->next; + } + *app = NULL; + + /* + * This is the best so far; remember it. + */ + best_flag = got_flag; + if (addr_list) + free_wordlist(addr_list); + addr_list = alist; + if (secret != NULL) + strlcpy(secret, lsecret, MAXWORDLEN); + + if (!newline) + break; + } + + /* scan for a -- word indicating the start of options */ + for (app = &addr_list; (ap = *app) != NULL; app = &ap->next) + if (strcmp(ap->word, "--") == 0) + break; + /* ap = start of options */ + if (ap != NULL) { + ap = ap->next; /* first option */ + free(*app); /* free the "--" word */ + *app = NULL; /* terminate addr list */ + } + if (opts != NULL) + *opts = ap; + else if (ap != NULL) + free_wordlist(ap); + if (addrs != NULL) + *addrs = addr_list; + else if (addr_list != NULL) + free_wordlist(addr_list); + + return best_flag; +} + +/* + * wordlist_count - return the number of items in a wordlist + */ +static int +wordlist_count(wp) + struct wordlist *wp; +{ + int n; + + for (n = 0; wp != NULL; wp = wp->next) + ++n; + return n; +} + +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(wp) + struct wordlist *wp; +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} +#endif /* UNUSED */ + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/ccp.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/ccp.c new file mode 100644 index 0000000..f8519eb --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/ccp.c @@ -0,0 +1,1740 @@ +/* + * ccp.c - PPP Compression Control Protocol. + * + * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && CCP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include +#include + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/ccp.h" + +#if MPPE_SUPPORT +#include "netif/ppp/lcp.h" /* lcp_close(), lcp_fsm */ +#include "netif/ppp/mppe.h" /* mppe_init() */ +#endif /* MPPE_SUPPORT */ + +/* + * Unfortunately there is a bug in zlib which means that using a + * size of 8 (window size = 256) for Deflate compression will cause + * buffer overruns and kernel crashes in the deflate module. + * Until this is fixed we only accept sizes in the range 9 .. 15. + * Thanks to James Carlson for pointing this out. + */ +#define DEFLATE_MIN_WORKS 9 + +/* + * Command-line options. + */ +#if PPP_OPTIONS +static int setbsdcomp (char **); +static int setdeflate (char **); +static char bsd_value[8]; +static char deflate_value[8]; + +/* + * Option variables. + */ +#if MPPE_SUPPORT +bool refuse_mppe_stateful = 1; /* Allow stateful mode? */ +#endif /* MPPE_SUPPORT */ + +static option_t ccp_option_list[] = { + { "noccp", o_bool, &ccp_protent.enabled_flag, + "Disable CCP negotiation" }, + { "-ccp", o_bool, &ccp_protent.enabled_flag, + "Disable CCP negotiation", OPT_ALIAS }, + + { "bsdcomp", o_special, (void *)setbsdcomp, + "Request BSD-Compress packet compression", + OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, bsd_value }, + { "nobsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress, + "don't allow BSD-Compress", OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].bsd_compress }, + { "-bsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress, + "don't allow BSD-Compress", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].bsd_compress }, + + { "deflate", o_special, (void *)setdeflate, + "request Deflate compression", + OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, deflate_value }, + { "nodeflate", o_bool, &ccp_wantoptions[0].deflate, + "don't allow Deflate compression", OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].deflate }, + { "-deflate", o_bool, &ccp_wantoptions[0].deflate, + "don't allow Deflate compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].deflate }, + + { "nodeflatedraft", o_bool, &ccp_wantoptions[0].deflate_draft, + "don't use draft deflate #", OPT_A2COPY, + &ccp_allowoptions[0].deflate_draft }, + + { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1, + "request Predictor-1", OPT_PRIO | 1 }, + { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1, + "don't allow Predictor-1", OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].predictor_1 }, + { "-predictor1", o_bool, &ccp_wantoptions[0].predictor_1, + "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].predictor_1 }, + +#if MPPE_SUPPORT + /* MPPE options are symmetrical ... we only set wantoptions here */ + { "require-mppe", o_bool, &ccp_wantoptions[0].mppe, + "require MPPE encryption", + OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 }, + { "+mppe", o_bool, &ccp_wantoptions[0].mppe, + "require MPPE encryption", + OPT_ALIAS | OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 }, + { "nomppe", o_bool, &ccp_wantoptions[0].mppe, + "don't allow MPPE encryption", OPT_PRIO }, + { "-mppe", o_bool, &ccp_wantoptions[0].mppe, + "don't allow MPPE encryption", OPT_ALIAS | OPT_PRIO }, + + /* We use ccp_allowoptions[0].mppe as a junk var ... it is reset later */ + { "require-mppe-40", o_bool, &ccp_allowoptions[0].mppe, + "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40, + &ccp_wantoptions[0].mppe }, + { "+mppe-40", o_bool, &ccp_allowoptions[0].mppe, + "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40, + &ccp_wantoptions[0].mppe }, + { "nomppe-40", o_bool, &ccp_allowoptions[0].mppe, + "don't allow MPPE 40-bit encryption", + OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe }, + { "-mppe-40", o_bool, &ccp_allowoptions[0].mppe, + "don't allow MPPE 40-bit encryption", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, + &ccp_wantoptions[0].mppe }, + + { "require-mppe-128", o_bool, &ccp_allowoptions[0].mppe, + "require MPPE 128-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_128, + &ccp_wantoptions[0].mppe }, + { "+mppe-128", o_bool, &ccp_allowoptions[0].mppe, + "require MPPE 128-bit encryption", + OPT_ALIAS | OPT_PRIO | OPT_A2OR | MPPE_OPT_128, + &ccp_wantoptions[0].mppe }, + { "nomppe-128", o_bool, &ccp_allowoptions[0].mppe, + "don't allow MPPE 128-bit encryption", + OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe }, + { "-mppe-128", o_bool, &ccp_allowoptions[0].mppe, + "don't allow MPPE 128-bit encryption", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, + &ccp_wantoptions[0].mppe }, + + /* strange one; we always request stateless, but will we allow stateful? */ + { "mppe-stateful", o_bool, &refuse_mppe_stateful, + "allow MPPE stateful mode", OPT_PRIO }, + { "nomppe-stateful", o_bool, &refuse_mppe_stateful, + "disallow MPPE stateful mode", OPT_PRIO | 1 }, +#endif /* MPPE_SUPPORT */ + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points from main code. + */ +static void ccp_init(ppp_pcb *pcb); +static void ccp_open(ppp_pcb *pcb); +static void ccp_close(ppp_pcb *pcb, const char *reason); +static void ccp_lowerup(ppp_pcb *pcb); +static void ccp_lowerdown(ppp_pcb *pcb); +static void ccp_input(ppp_pcb *pcb, u_char *pkt, int len); +static void ccp_protrej(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int ccp_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT +static void ccp_datainput(ppp_pcb *pcb, u_char *pkt, int len); +#endif /* PPP_DATAINPUT */ + +const struct protent ccp_protent = { + PPP_CCP, + ccp_init, + ccp_input, + ccp_protrej, + ccp_lowerup, + ccp_lowerdown, + ccp_open, + ccp_close, +#if PRINTPKT_SUPPORT + ccp_printpkt, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + ccp_datainput, +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + "CCP", + "Compressed", +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + ccp_option_list, + NULL, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +/* + * Callbacks for fsm code. + */ +static void ccp_resetci (fsm *); +static int ccp_cilen (fsm *); +static void ccp_addci (fsm *, u_char *, int *); +static int ccp_ackci (fsm *, u_char *, int); +static int ccp_nakci (fsm *, u_char *, int, int); +static int ccp_rejci (fsm *, u_char *, int); +static int ccp_reqci (fsm *, u_char *, int *, int); +static void ccp_up (fsm *); +static void ccp_down (fsm *); +static int ccp_extcode (fsm *, int, int, u_char *, int); +static void ccp_rack_timeout (void *); +static const char *method_name (ccp_options *, ccp_options *); + +static const fsm_callbacks ccp_callbacks = { + ccp_resetci, + ccp_cilen, + ccp_addci, + ccp_ackci, + ccp_nakci, + ccp_rejci, + ccp_reqci, + ccp_up, + ccp_down, + NULL, + NULL, + NULL, + NULL, + ccp_extcode, + "CCP" +}; + +/* + * Do we want / did we get any compression? + */ +static int ccp_anycompress(ccp_options *opt) { + return (0 +#if DEFLATE_SUPPORT + || (opt)->deflate +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + || (opt)->bsd_compress +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + || (opt)->predictor_1 || (opt)->predictor_2 +#endif /* PREDICTOR_SUPPORT */ +#if MPPE_SUPPORT + || (opt)->mppe +#endif /* MPPE_SUPPORT */ + ); +} + +/* + * Local state (mainly for handling reset-reqs and reset-acks). + */ +#define RACK_PENDING 1 /* waiting for reset-ack */ +#define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */ + +#define RACKTIMEOUT 1 /* second */ + +#if PPP_OPTIONS +/* + * Option parsing + */ +static int +setbsdcomp(argv) + char **argv; +{ + int rbits, abits; + char *str, *endp; + + str = *argv; + abits = rbits = strtol(str, &endp, 0); + if (endp != str && *endp == ',') { + str = endp + 1; + abits = strtol(str, &endp, 0); + } + if (*endp != 0 || endp == str) { + option_error("invalid parameter '%s' for bsdcomp option", *argv); + return 0; + } + if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS)) + || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) { + option_error("bsdcomp option values must be 0 or %d .. %d", + BSD_MIN_BITS, BSD_MAX_BITS); + return 0; + } + if (rbits > 0) { + ccp_wantoptions[0].bsd_compress = 1; + ccp_wantoptions[0].bsd_bits = rbits; + } else + ccp_wantoptions[0].bsd_compress = 0; + if (abits > 0) { + ccp_allowoptions[0].bsd_compress = 1; + ccp_allowoptions[0].bsd_bits = abits; + } else + ccp_allowoptions[0].bsd_compress = 0; + ppp_slprintf(bsd_value, sizeof(bsd_value), + rbits == abits? "%d": "%d,%d", rbits, abits); + + return 1; +} + +static int +setdeflate(argv) + char **argv; +{ + int rbits, abits; + char *str, *endp; + + str = *argv; + abits = rbits = strtol(str, &endp, 0); + if (endp != str && *endp == ',') { + str = endp + 1; + abits = strtol(str, &endp, 0); + } + if (*endp != 0 || endp == str) { + option_error("invalid parameter '%s' for deflate option", *argv); + return 0; + } + if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE)) + || (abits != 0 && (abits < DEFLATE_MIN_SIZE + || abits > DEFLATE_MAX_SIZE))) { + option_error("deflate option values must be 0 or %d .. %d", + DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE); + return 0; + } + if (rbits == DEFLATE_MIN_SIZE || abits == DEFLATE_MIN_SIZE) { + if (rbits == DEFLATE_MIN_SIZE) + rbits = DEFLATE_MIN_WORKS; + if (abits == DEFLATE_MIN_SIZE) + abits = DEFLATE_MIN_WORKS; + warn("deflate option value of %d changed to %d to avoid zlib bug", + DEFLATE_MIN_SIZE, DEFLATE_MIN_WORKS); + } + if (rbits > 0) { + ccp_wantoptions[0].deflate = 1; + ccp_wantoptions[0].deflate_size = rbits; + } else + ccp_wantoptions[0].deflate = 0; + if (abits > 0) { + ccp_allowoptions[0].deflate = 1; + ccp_allowoptions[0].deflate_size = abits; + } else + ccp_allowoptions[0].deflate = 0; + ppp_slprintf(deflate_value, sizeof(deflate_value), + rbits == abits? "%d": "%d,%d", rbits, abits); + + return 1; +} +#endif /* PPP_OPTIONS */ + +/* + * ccp_init - initialize CCP. + */ +static void ccp_init(ppp_pcb *pcb) { + fsm *f = &pcb->ccp_fsm; + + f->pcb = pcb; + f->protocol = PPP_CCP; + f->callbacks = &ccp_callbacks; + fsm_init(f); + +#if 0 /* Not necessary, everything is cleared in ppp_new() */ + memset(wo, 0, sizeof(*wo)); + memset(go, 0, sizeof(*go)); + memset(ao, 0, sizeof(*ao)); + memset(ho, 0, sizeof(*ho)); +#endif /* 0 */ + +#if DEFLATE_SUPPORT + wo->deflate = 1; + wo->deflate_size = DEFLATE_MAX_SIZE; + wo->deflate_correct = 1; + wo->deflate_draft = 1; + ao->deflate = 1; + ao->deflate_size = DEFLATE_MAX_SIZE; + ao->deflate_correct = 1; + ao->deflate_draft = 1; +#endif /* DEFLATE_SUPPORT */ + +#if BSDCOMPRESS_SUPPORT + wo->bsd_compress = 1; + wo->bsd_bits = BSD_MAX_BITS; + ao->bsd_compress = 1; + ao->bsd_bits = BSD_MAX_BITS; +#endif /* BSDCOMPRESS_SUPPORT */ + +#if PREDICTOR_SUPPORT + ao->predictor_1 = 1; +#endif /* PREDICTOR_SUPPORT */ +} + +/* + * ccp_open - CCP is allowed to come up. + */ +static void ccp_open(ppp_pcb *pcb) { + fsm *f = &pcb->ccp_fsm; + ccp_options *go = &pcb->ccp_gotoptions; + + if (f->state != PPP_FSM_OPENED) + ccp_set(pcb, 1, 0, 0, 0); + + /* + * Find out which compressors the kernel supports before + * deciding whether to open in silent mode. + */ + ccp_resetci(f); + if (!ccp_anycompress(go)) + f->flags |= OPT_SILENT; + + fsm_open(f); +} + +/* + * ccp_close - Terminate CCP. + */ +static void ccp_close(ppp_pcb *pcb, const char *reason) { + fsm *f = &pcb->ccp_fsm; + ccp_set(pcb, 0, 0, 0, 0); + fsm_close(f, reason); +} + +/* + * ccp_lowerup - we may now transmit CCP packets. + */ +static void ccp_lowerup(ppp_pcb *pcb) { + fsm *f = &pcb->ccp_fsm; + fsm_lowerup(f); +} + +/* + * ccp_lowerdown - we may not transmit CCP packets. + */ +static void ccp_lowerdown(ppp_pcb *pcb) { + fsm *f = &pcb->ccp_fsm; + fsm_lowerdown(f); +} + +/* + * ccp_input - process a received CCP packet. + */ +static void ccp_input(ppp_pcb *pcb, u_char *p, int len) { + fsm *f = &pcb->ccp_fsm; + ccp_options *go = &pcb->ccp_gotoptions; + int oldstate; + + /* + * Check for a terminate-request so we can print a message. + */ + oldstate = f->state; + fsm_input(f, p, len); + if (oldstate == PPP_FSM_OPENED && p[0] == TERMREQ && f->state != PPP_FSM_OPENED) { + ppp_notice("Compression disabled by peer."); +#if MPPE_SUPPORT + if (go->mppe) { + ppp_error("MPPE disabled, closing LCP"); + lcp_close(pcb, "MPPE disabled by peer"); + } +#endif /* MPPE_SUPPORT */ + } + + /* + * If we get a terminate-ack and we're not asking for compression, + * close CCP. + */ + if (oldstate == PPP_FSM_REQSENT && p[0] == TERMACK + && !ccp_anycompress(go)) + ccp_close(pcb, "No compression negotiated"); +} + +/* + * Handle a CCP-specific code. + */ +static int ccp_extcode(fsm *f, int code, int id, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(len); + + switch (code) { + case CCP_RESETREQ: + if (f->state != PPP_FSM_OPENED) + break; + ccp_reset_comp(pcb); + /* send a reset-ack, which the transmitter will see and + reset its compression state. */ + fsm_sdata(f, CCP_RESETACK, id, NULL, 0); + break; + + case CCP_RESETACK: + if ((pcb->ccp_localstate & RACK_PENDING) && id == f->reqid) { + pcb->ccp_localstate &= ~(RACK_PENDING | RREQ_REPEAT); + UNTIMEOUT(ccp_rack_timeout, f); + ccp_reset_decomp(pcb); + } + break; + + default: + return 0; + } + + return 1; +} + +/* + * ccp_protrej - peer doesn't talk CCP. + */ +static void ccp_protrej(ppp_pcb *pcb) { + fsm *f = &pcb->ccp_fsm; +#if MPPE_SUPPORT + ccp_options *go = &pcb->ccp_gotoptions; +#endif /* MPPE_SUPPORT */ + + ccp_set(pcb, 0, 0, 0, 0); + fsm_lowerdown(f); + +#if MPPE_SUPPORT + if (go->mppe) { + ppp_error("MPPE required but peer negotiation failed"); + lcp_close(pcb, "MPPE required but peer negotiation failed"); + } +#endif /* MPPE_SUPPORT */ + +} + +/* + * ccp_resetci - initialize at start of negotiation. + */ +static void ccp_resetci(fsm *f) { + ppp_pcb *pcb = f->pcb; + ccp_options *go = &pcb->ccp_gotoptions; + ccp_options *wo = &pcb->ccp_wantoptions; +#if MPPE_SUPPORT + ccp_options *ao = &pcb->ccp_allowoptions; +#endif /* MPPE_SUPPORT */ +#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT + u_char opt_buf[CCP_MAX_OPTION_LENGTH]; +#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT */ +#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT + int res; +#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT */ + +#if MPPE_SUPPORT + if (pcb->settings.require_mppe) { + wo->mppe = ao->mppe = + (pcb->settings.refuse_mppe_40 ? 0 : MPPE_OPT_40) + | (pcb->settings.refuse_mppe_128 ? 0 : MPPE_OPT_128); + } +#endif /* MPPE_SUPPORT */ + + *go = *wo; + pcb->ccp_all_rejected = 0; + +#if MPPE_SUPPORT + if (go->mppe) { + int auth_mschap_bits = pcb->auth_done; + int numbits; + + /* + * Start with a basic sanity check: mschap[v2] auth must be in + * exactly one direction. RFC 3079 says that the keys are + * 'derived from the credentials of the peer that initiated the call', + * however the PPP protocol doesn't have such a concept, and pppd + * cannot get this info externally. Instead we do the best we can. + * NB: If MPPE is required, all other compression opts are invalid. + * So, we return right away if we can't do it. + */ + + /* Leave only the mschap auth bits set */ + auth_mschap_bits &= (CHAP_MS_WITHPEER | CHAP_MS_PEER | + CHAP_MS2_WITHPEER | CHAP_MS2_PEER); + /* Count the mschap auths */ + auth_mschap_bits >>= CHAP_MS_SHIFT; + numbits = 0; + do { + numbits += auth_mschap_bits & 1; + auth_mschap_bits >>= 1; + } while (auth_mschap_bits); + if (numbits > 1) { + ppp_error("MPPE required, but auth done in both directions."); + lcp_close(pcb, "MPPE required but not available"); + return; + } + if (!numbits) { + ppp_error("MPPE required, but MS-CHAP[v2] auth not performed."); + lcp_close(pcb, "MPPE required but not available"); + return; + } + + /* A plugin (eg radius) may not have obtained key material. */ + if (!pcb->mppe_keys_set) { + ppp_error("MPPE required, but keys are not available. " + "Possible plugin problem?"); + lcp_close(pcb, "MPPE required but not available"); + return; + } + + /* LM auth not supported for MPPE */ + if (pcb->auth_done & (CHAP_MS_WITHPEER | CHAP_MS_PEER)) { + /* This might be noise */ + if (go->mppe & MPPE_OPT_40) { + ppp_notice("Disabling 40-bit MPPE; MS-CHAP LM not supported"); + go->mppe &= ~MPPE_OPT_40; + wo->mppe &= ~MPPE_OPT_40; + } + } + + /* Last check: can we actually negotiate something? */ + if (!(go->mppe & (MPPE_OPT_40 | MPPE_OPT_128))) { + /* Could be misconfig, could be 40-bit disabled above. */ + ppp_error("MPPE required, but both 40-bit and 128-bit disabled."); + lcp_close(pcb, "MPPE required but not available"); + return; + } + + /* sync options */ + ao->mppe = go->mppe; + /* MPPE is not compatible with other compression types */ +#if BSDCOMPRESS_SUPPORT + ao->bsd_compress = go->bsd_compress = 0; +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + ao->predictor_1 = go->predictor_1 = 0; + ao->predictor_2 = go->predictor_2 = 0; +#endif /* PREDICTOR_SUPPORT */ +#if DEFLATE_SUPPORT + ao->deflate = go->deflate = 0; +#endif /* DEFLATE_SUPPORT */ + } +#endif /* MPPE_SUPPORT */ + + /* + * Check whether the kernel knows about the various + * compression methods we might request. + */ +#if BSDCOMPRESS_SUPPORT + /* FIXME: we don't need to test if BSD compress is available + * if BSDCOMPRESS_SUPPORT is set, it is. + */ + if (go->bsd_compress) { + opt_buf[0] = CI_BSD_COMPRESS; + opt_buf[1] = CILEN_BSD_COMPRESS; + for (;;) { + if (go->bsd_bits < BSD_MIN_BITS) { + go->bsd_compress = 0; + break; + } + opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); + res = ccp_test(pcb, opt_buf, CILEN_BSD_COMPRESS, 0); + if (res > 0) { + break; + } else if (res < 0) { + go->bsd_compress = 0; + break; + } + go->bsd_bits--; + } + } +#endif /* BSDCOMPRESS_SUPPORT */ +#if DEFLATE_SUPPORT + /* FIXME: we don't need to test if deflate is available + * if DEFLATE_SUPPORT is set, it is. + */ + if (go->deflate) { + if (go->deflate_correct) { + opt_buf[0] = CI_DEFLATE; + opt_buf[1] = CILEN_DEFLATE; + opt_buf[3] = DEFLATE_CHK_SEQUENCE; + for (;;) { + if (go->deflate_size < DEFLATE_MIN_WORKS) { + go->deflate_correct = 0; + break; + } + opt_buf[2] = DEFLATE_MAKE_OPT(go->deflate_size); + res = ccp_test(pcb, opt_buf, CILEN_DEFLATE, 0); + if (res > 0) { + break; + } else if (res < 0) { + go->deflate_correct = 0; + break; + } + go->deflate_size--; + } + } + if (go->deflate_draft) { + opt_buf[0] = CI_DEFLATE_DRAFT; + opt_buf[1] = CILEN_DEFLATE; + opt_buf[3] = DEFLATE_CHK_SEQUENCE; + for (;;) { + if (go->deflate_size < DEFLATE_MIN_WORKS) { + go->deflate_draft = 0; + break; + } + opt_buf[2] = DEFLATE_MAKE_OPT(go->deflate_size); + res = ccp_test(pcb, opt_buf, CILEN_DEFLATE, 0); + if (res > 0) { + break; + } else if (res < 0) { + go->deflate_draft = 0; + break; + } + go->deflate_size--; + } + } + if (!go->deflate_correct && !go->deflate_draft) + go->deflate = 0; + } +#endif /* DEFLATE_SUPPORT */ +#if PREDICTOR_SUPPORT + /* FIXME: we don't need to test if predictor is available, + * if PREDICTOR_SUPPORT is set, it is. + */ + if (go->predictor_1) { + opt_buf[0] = CI_PREDICTOR_1; + opt_buf[1] = CILEN_PREDICTOR_1; + if (ccp_test(pcb, opt_buf, CILEN_PREDICTOR_1, 0) <= 0) + go->predictor_1 = 0; + } + if (go->predictor_2) { + opt_buf[0] = CI_PREDICTOR_2; + opt_buf[1] = CILEN_PREDICTOR_2; + if (ccp_test(pcb, opt_buf, CILEN_PREDICTOR_2, 0) <= 0) + go->predictor_2 = 0; + } +#endif /* PREDICTOR_SUPPORT */ +} + +/* + * ccp_cilen - Return total length of our configuration info. + */ +static int ccp_cilen(fsm *f) { + ppp_pcb *pcb = f->pcb; + ccp_options *go = &pcb->ccp_gotoptions; + + return 0 +#if BSDCOMPRESS_SUPPORT + + (go->bsd_compress? CILEN_BSD_COMPRESS: 0) +#endif /* BSDCOMPRESS_SUPPORT */ +#if DEFLATE_SUPPORT + + (go->deflate && go->deflate_correct? CILEN_DEFLATE: 0) + + (go->deflate && go->deflate_draft? CILEN_DEFLATE: 0) +#endif /* DEFLATE_SUPPORT */ +#if PREDICTOR_SUPPORT + + (go->predictor_1? CILEN_PREDICTOR_1: 0) + + (go->predictor_2? CILEN_PREDICTOR_2: 0) +#endif /* PREDICTOR_SUPPORT */ +#if MPPE_SUPPORT + + (go->mppe? CILEN_MPPE: 0) +#endif /* MPPE_SUPPORT */ + ; +} + +/* + * ccp_addci - put our requests in a packet. + */ +static void ccp_addci(fsm *f, u_char *p, int *lenp) { + ppp_pcb *pcb = f->pcb; + ccp_options *go = &pcb->ccp_gotoptions; + u_char *p0 = p; + + /* + * Add the compression types that we can receive, in decreasing + * preference order. + */ +#if MPPE_SUPPORT + if (go->mppe) { + p[0] = CI_MPPE; + p[1] = CILEN_MPPE; + MPPE_OPTS_TO_CI(go->mppe, &p[2]); + mppe_init(pcb, &pcb->mppe_decomp, go->mppe); + p += CILEN_MPPE; + } +#endif /* MPPE_SUPPORT */ +#if DEFLATE_SUPPORT + if (go->deflate) { + if (go->deflate_correct) { + p[0] = CI_DEFLATE; + p[1] = CILEN_DEFLATE; + p[2] = DEFLATE_MAKE_OPT(go->deflate_size); + p[3] = DEFLATE_CHK_SEQUENCE; + p += CILEN_DEFLATE; + } + if (go->deflate_draft) { + p[0] = CI_DEFLATE_DRAFT; + p[1] = CILEN_DEFLATE; + p[2] = p[2 - CILEN_DEFLATE]; + p[3] = DEFLATE_CHK_SEQUENCE; + p += CILEN_DEFLATE; + } + } +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + if (go->bsd_compress) { + p[0] = CI_BSD_COMPRESS; + p[1] = CILEN_BSD_COMPRESS; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); + p += CILEN_BSD_COMPRESS; + } +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + /* XXX Should Predictor 2 be preferable to Predictor 1? */ + if (go->predictor_1) { + p[0] = CI_PREDICTOR_1; + p[1] = CILEN_PREDICTOR_1; + p += CILEN_PREDICTOR_1; + } + if (go->predictor_2) { + p[0] = CI_PREDICTOR_2; + p[1] = CILEN_PREDICTOR_2; + p += CILEN_PREDICTOR_2; + } +#endif /* PREDICTOR_SUPPORT */ + + go->method = (p > p0)? p0[0]: 0; + + *lenp = p - p0; +} + +/* + * ccp_ackci - process a received configure-ack, and return + * 1 iff the packet was OK. + */ +static int ccp_ackci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ccp_options *go = &pcb->ccp_gotoptions; +#if BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT + u_char *p0 = p; +#endif /* BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT */ + +#if MPPE_SUPPORT + if (go->mppe) { + u_char opt_buf[CILEN_MPPE]; + + opt_buf[0] = CI_MPPE; + opt_buf[1] = CILEN_MPPE; + MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]); + if (len < CILEN_MPPE || memcmp(opt_buf, p, CILEN_MPPE)) + return 0; + p += CILEN_MPPE; + len -= CILEN_MPPE; + /* XXX Cope with first/fast ack */ + if (len == 0) + return 1; + } +#endif /* MPPE_SUPPORT */ +#if DEFLATE_SUPPORT + if (go->deflate) { + if (len < CILEN_DEFLATE + || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + || p[1] != CILEN_DEFLATE + || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + /* XXX Cope with first/fast ack */ + if (len == 0) + return 1; + if (go->deflate_correct && go->deflate_draft) { + if (len < CILEN_DEFLATE + || p[0] != CI_DEFLATE_DRAFT + || p[1] != CILEN_DEFLATE + || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + } +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + if (go->bsd_compress) { + if (len < CILEN_BSD_COMPRESS + || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS + || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) + return 0; + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + if (go->predictor_1) { + if (len < CILEN_PREDICTOR_1 + || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1) + return 0; + p += CILEN_PREDICTOR_1; + len -= CILEN_PREDICTOR_1; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + if (go->predictor_2) { + if (len < CILEN_PREDICTOR_2 + || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2) + return 0; + p += CILEN_PREDICTOR_2; + len -= CILEN_PREDICTOR_2; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } +#endif /* PREDICTOR_SUPPORT */ + + if (len != 0) + return 0; + return 1; +} + +/* + * ccp_nakci - process received configure-nak. + * Returns 1 iff the nak was OK. + */ +static int ccp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { + ppp_pcb *pcb = f->pcb; + ccp_options *go = &pcb->ccp_gotoptions; + ccp_options no; /* options we've seen already */ + ccp_options try_; /* options to ask for next time */ + LWIP_UNUSED_ARG(treat_as_reject); +#if !MPPE_SUPPORT && !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(len); +#endif /* !MPPE_SUPPORT && !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT */ + + memset(&no, 0, sizeof(no)); + try_ = *go; + +#if MPPE_SUPPORT + if (go->mppe && len >= CILEN_MPPE + && p[0] == CI_MPPE && p[1] == CILEN_MPPE) { + no.mppe = 1; + /* + * Peer wants us to use a different strength or other setting. + * Fail if we aren't willing to use his suggestion. + */ + MPPE_CI_TO_OPTS(&p[2], try_.mppe); + if ((try_.mppe & MPPE_OPT_STATEFUL) && pcb->settings.refuse_mppe_stateful) { + ppp_error("Refusing MPPE stateful mode offered by peer"); + try_.mppe = 0; + } else if (((go->mppe | MPPE_OPT_STATEFUL) & try_.mppe) != try_.mppe) { + /* Peer must have set options we didn't request (suggest) */ + try_.mppe = 0; + } + + if (!try_.mppe) { + ppp_error("MPPE required but peer negotiation failed"); + lcp_close(pcb, "MPPE required but peer negotiation failed"); + } + } +#endif /* MPPE_SUPPORT */ +#if DEFLATE_SUPPORT + if (go->deflate && len >= CILEN_DEFLATE + && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + && p[1] == CILEN_DEFLATE) { + no.deflate = 1; + /* + * Peer wants us to use a different code size or something. + * Stop asking for Deflate if we don't understand his suggestion. + */ + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL + || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_WORKS + || p[3] != DEFLATE_CHK_SEQUENCE) + try_.deflate = 0; + else if (DEFLATE_SIZE(p[2]) < go->deflate_size) + try_.deflate_size = DEFLATE_SIZE(p[2]); + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + if (go->deflate_correct && go->deflate_draft + && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT + && p[1] == CILEN_DEFLATE) { + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + } +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + if (go->bsd_compress && len >= CILEN_BSD_COMPRESS + && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { + no.bsd_compress = 1; + /* + * Peer wants us to use a different number of bits + * or a different version. + */ + if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION) + try_.bsd_compress = 0; + else if (BSD_NBITS(p[2]) < go->bsd_bits) + try_.bsd_bits = BSD_NBITS(p[2]); + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + } +#endif /* BSDCOMPRESS_SUPPORT */ + + /* + * Predictor-1 and 2 have no options, so they can't be Naked. + * + * There may be remaining options but we ignore them. + */ + + if (f->state != PPP_FSM_OPENED) + *go = try_; + return 1; +} + +/* + * ccp_rejci - reject some of our suggested compression methods. + */ +static int ccp_rejci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ccp_options *go = &pcb->ccp_gotoptions; + ccp_options try_; /* options to request next time */ + + try_ = *go; + + /* + * Cope with empty configure-rejects by ceasing to send + * configure-requests. + */ + if (len == 0 && pcb->ccp_all_rejected) + return -1; + +#if MPPE_SUPPORT + if (go->mppe && len >= CILEN_MPPE + && p[0] == CI_MPPE && p[1] == CILEN_MPPE) { + ppp_error("MPPE required but peer refused"); + lcp_close(pcb, "MPPE required but peer refused"); + p += CILEN_MPPE; + len -= CILEN_MPPE; + } +#endif /* MPPE_SUPPORT */ +#if DEFLATE_SUPPORT + if (go->deflate_correct && len >= CILEN_DEFLATE + && p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) { + if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; /* Rej is bad */ + try_.deflate_correct = 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + if (go->deflate_draft && len >= CILEN_DEFLATE + && p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) { + if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; /* Rej is bad */ + try_.deflate_draft = 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + if (!try_.deflate_correct && !try_.deflate_draft) + try_.deflate = 0; +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + if (go->bsd_compress && len >= CILEN_BSD_COMPRESS + && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { + if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) + return 0; + try_.bsd_compress = 0; + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + } +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + if (go->predictor_1 && len >= CILEN_PREDICTOR_1 + && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) { + try_.predictor_1 = 0; + p += CILEN_PREDICTOR_1; + len -= CILEN_PREDICTOR_1; + } + if (go->predictor_2 && len >= CILEN_PREDICTOR_2 + && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) { + try_.predictor_2 = 0; + p += CILEN_PREDICTOR_2; + len -= CILEN_PREDICTOR_2; + } +#endif /* PREDICTOR_SUPPORT */ + + if (len != 0) + return 0; + + if (f->state != PPP_FSM_OPENED) + *go = try_; + + return 1; +} + +/* + * ccp_reqci - processed a received configure-request. + * Returns CONFACK, CONFNAK or CONFREJ and the packet modified + * appropriately. + */ +static int ccp_reqci(fsm *f, u_char *p, int *lenp, int dont_nak) { + ppp_pcb *pcb = f->pcb; + ccp_options *ho = &pcb->ccp_hisoptions; + ccp_options *ao = &pcb->ccp_allowoptions; + int ret, newret; +#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT + int res; + int nb; +#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT */ + u_char *p0, *retp; + int len, clen, type; +#if MPPE_SUPPORT + u8_t rej_for_ci_mppe = 1; /* Are we rejecting based on a bad/missing */ + /* CI_MPPE, or due to other options? */ +#endif /* MPPE_SUPPORT */ + + ret = CONFACK; + retp = p0 = p; + len = *lenp; + + memset(ho, 0, sizeof(ccp_options)); + ho->method = (len > 0)? p[0]: 0; + + while (len > 0) { + newret = CONFACK; + if (len < 2 || p[1] < 2 || p[1] > len) { + /* length is bad */ + clen = len; + newret = CONFREJ; + + } else { + type = p[0]; + clen = p[1]; + + switch (type) { +#if MPPE_SUPPORT + case CI_MPPE: + if (!ao->mppe || clen != CILEN_MPPE) { + newret = CONFREJ; + break; + } + MPPE_CI_TO_OPTS(&p[2], ho->mppe); + + /* Nak if anything unsupported or unknown are set. */ + if (ho->mppe & MPPE_OPT_UNSUPPORTED) { + newret = CONFNAK; + ho->mppe &= ~MPPE_OPT_UNSUPPORTED; + } + if (ho->mppe & MPPE_OPT_UNKNOWN) { + newret = CONFNAK; + ho->mppe &= ~MPPE_OPT_UNKNOWN; + } + + /* Check state opt */ + if (ho->mppe & MPPE_OPT_STATEFUL) { + /* + * We can Nak and request stateless, but it's a + * lot easier to just assume the peer will request + * it if he can do it; stateful mode is bad over + * the Internet -- which is where we expect MPPE. + */ + if (pcb->settings.refuse_mppe_stateful) { + ppp_error("Refusing MPPE stateful mode offered by peer"); + newret = CONFREJ; + break; + } + } + + /* Find out which of {S,L} are set. */ + if ((ho->mppe & MPPE_OPT_128) + && (ho->mppe & MPPE_OPT_40)) { + /* Both are set, negotiate the strongest. */ + newret = CONFNAK; + if (ao->mppe & MPPE_OPT_128) + ho->mppe &= ~MPPE_OPT_40; + else if (ao->mppe & MPPE_OPT_40) + ho->mppe &= ~MPPE_OPT_128; + else { + newret = CONFREJ; + break; + } + } else if (ho->mppe & MPPE_OPT_128) { + if (!(ao->mppe & MPPE_OPT_128)) { + newret = CONFREJ; + break; + } + } else if (ho->mppe & MPPE_OPT_40) { + if (!(ao->mppe & MPPE_OPT_40)) { + newret = CONFREJ; + break; + } + } else { + /* Neither are set. */ + /* We cannot accept this. */ + newret = CONFNAK; + /* Give the peer our idea of what can be used, + so it can choose and confirm */ + ho->mppe = ao->mppe; + } + + /* rebuild the opts */ + MPPE_OPTS_TO_CI(ho->mppe, &p[2]); + if (newret == CONFACK) { + int mtu; + + mppe_init(pcb, &pcb->mppe_comp, ho->mppe); + /* + * We need to decrease the interface MTU by MPPE_PAD + * because MPPE frames **grow**. The kernel [must] + * allocate MPPE_PAD extra bytes in xmit buffers. + */ + mtu = netif_get_mtu(pcb); + if (mtu) + netif_set_mtu(pcb, mtu - MPPE_PAD); + else + newret = CONFREJ; + } + + /* + * We have accepted MPPE or are willing to negotiate + * MPPE parameters. A CONFREJ is due to subsequent + * (non-MPPE) processing. + */ + rej_for_ci_mppe = 0; + break; +#endif /* MPPE_SUPPORT */ +#if DEFLATE_SUPPORT + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (!ao->deflate || clen != CILEN_DEFLATE + || (!ao->deflate_correct && type == CI_DEFLATE) + || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) { + newret = CONFREJ; + break; + } + + ho->deflate = 1; + ho->deflate_size = nb = DEFLATE_SIZE(p[2]); + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL + || p[3] != DEFLATE_CHK_SEQUENCE + || nb > ao->deflate_size || nb < DEFLATE_MIN_WORKS) { + newret = CONFNAK; + if (!dont_nak) { + p[2] = DEFLATE_MAKE_OPT(ao->deflate_size); + p[3] = DEFLATE_CHK_SEQUENCE; + /* fall through to test this #bits below */ + } else + break; + } + + /* + * Check whether we can do Deflate with the window + * size they want. If the window is too big, reduce + * it until the kernel can cope and nak with that. + * We only check this for the first option. + */ + if (p == p0) { + for (;;) { + res = ccp_test(pcb, p, CILEN_DEFLATE, 1); + if (res > 0) + break; /* it's OK now */ + if (res < 0 || nb == DEFLATE_MIN_WORKS || dont_nak) { + newret = CONFREJ; + p[2] = DEFLATE_MAKE_OPT(ho->deflate_size); + break; + } + newret = CONFNAK; + --nb; + p[2] = DEFLATE_MAKE_OPT(nb); + } + } + break; +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + case CI_BSD_COMPRESS: + if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) { + newret = CONFREJ; + break; + } + + ho->bsd_compress = 1; + ho->bsd_bits = nb = BSD_NBITS(p[2]); + if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION + || nb > ao->bsd_bits || nb < BSD_MIN_BITS) { + newret = CONFNAK; + if (!dont_nak) { + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits); + /* fall through to test this #bits below */ + } else + break; + } + + /* + * Check whether we can do BSD-Compress with the code + * size they want. If the code size is too big, reduce + * it until the kernel can cope and nak with that. + * We only check this for the first option. + */ + if (p == p0) { + for (;;) { + res = ccp_test(pcb, p, CILEN_BSD_COMPRESS, 1); + if (res > 0) + break; + if (res < 0 || nb == BSD_MIN_BITS || dont_nak) { + newret = CONFREJ; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, + ho->bsd_bits); + break; + } + newret = CONFNAK; + --nb; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb); + } + } + break; +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + case CI_PREDICTOR_1: + if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) { + newret = CONFREJ; + break; + } + + ho->predictor_1 = 1; + if (p == p0 + && ccp_test(pcb, p, CILEN_PREDICTOR_1, 1) <= 0) { + newret = CONFREJ; + } + break; + + case CI_PREDICTOR_2: + if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) { + newret = CONFREJ; + break; + } + + ho->predictor_2 = 1; + if (p == p0 + && ccp_test(pcb, p, CILEN_PREDICTOR_2, 1) <= 0) { + newret = CONFREJ; + } + break; +#endif /* PREDICTOR_SUPPORT */ + + default: + newret = CONFREJ; + } + } + + if (newret == CONFNAK && dont_nak) + newret = CONFREJ; + if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) { + /* we're returning this option */ + if (newret == CONFREJ && ret == CONFNAK) + retp = p0; + ret = newret; + if (p != retp) + MEMCPY(retp, p, clen); + retp += clen; + } + + p += clen; + len -= clen; + } + + if (ret != CONFACK) { + if (ret == CONFREJ && *lenp == retp - p0) + pcb->ccp_all_rejected = 1; + else + *lenp = retp - p0; + } +#if MPPE_SUPPORT + if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) { + ppp_error("MPPE required but peer negotiation failed"); + lcp_close(pcb, "MPPE required but peer negotiation failed"); + } +#endif /* MPPE_SUPPORT */ + return ret; +} + +/* + * Make a string name for a compression method (or 2). + */ +static const char *method_name(ccp_options *opt, ccp_options *opt2) { + static char result[64]; +#if !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT + LWIP_UNUSED_ARG(opt2); +#endif /* !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT */ + + if (!ccp_anycompress(opt)) + return "(none)"; + switch (opt->method) { +#if MPPE_SUPPORT + case CI_MPPE: + { + char *p = result; + char *q = result + sizeof(result); /* 1 past result */ + + ppp_slprintf(p, q - p, "MPPE "); + p += 5; + if (opt->mppe & MPPE_OPT_128) { + ppp_slprintf(p, q - p, "128-bit "); + p += 8; + } + if (opt->mppe & MPPE_OPT_40) { + ppp_slprintf(p, q - p, "40-bit "); + p += 7; + } + if (opt->mppe & MPPE_OPT_STATEFUL) + ppp_slprintf(p, q - p, "stateful"); + else + ppp_slprintf(p, q - p, "stateless"); + + break; + } +#endif /* MPPE_SUPPORT */ +#if DEFLATE_SUPPORT + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (opt2 != NULL && opt2->deflate_size != opt->deflate_size) + ppp_slprintf(result, sizeof(result), "Deflate%s (%d/%d)", + (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), + opt->deflate_size, opt2->deflate_size); + else + ppp_slprintf(result, sizeof(result), "Deflate%s (%d)", + (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), + opt->deflate_size); + break; +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + case CI_BSD_COMPRESS: + if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits) + ppp_slprintf(result, sizeof(result), "BSD-Compress (%d/%d)", + opt->bsd_bits, opt2->bsd_bits); + else + ppp_slprintf(result, sizeof(result), "BSD-Compress (%d)", + opt->bsd_bits); + break; +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + case CI_PREDICTOR_1: + return "Predictor 1"; + case CI_PREDICTOR_2: + return "Predictor 2"; +#endif /* PREDICTOR_SUPPORT */ + default: + ppp_slprintf(result, sizeof(result), "Method %d", opt->method); + } + return result; +} + +/* + * CCP has come up - inform the kernel driver and log a message. + */ +static void ccp_up(fsm *f) { + ppp_pcb *pcb = f->pcb; + ccp_options *go = &pcb->ccp_gotoptions; + ccp_options *ho = &pcb->ccp_hisoptions; + char method1[64]; + + ccp_set(pcb, 1, 1, go->method, ho->method); + if (ccp_anycompress(go)) { + if (ccp_anycompress(ho)) { + if (go->method == ho->method) { + ppp_notice("%s compression enabled", method_name(go, ho)); + } else { + ppp_strlcpy(method1, method_name(go, NULL), sizeof(method1)); + ppp_notice("%s / %s compression enabled", + method1, method_name(ho, NULL)); + } + } else + ppp_notice("%s receive compression enabled", method_name(go, NULL)); + } else if (ccp_anycompress(ho)) + ppp_notice("%s transmit compression enabled", method_name(ho, NULL)); +#if MPPE_SUPPORT + if (go->mppe) { + continue_networks(pcb); /* Bring up IP et al */ + } +#endif /* MPPE_SUPPORT */ +} + +/* + * CCP has gone down - inform the kernel driver. + */ +static void ccp_down(fsm *f) { + ppp_pcb *pcb = f->pcb; +#if MPPE_SUPPORT + ccp_options *go = &pcb->ccp_gotoptions; +#endif /* MPPE_SUPPORT */ + + if (pcb->ccp_localstate & RACK_PENDING) + UNTIMEOUT(ccp_rack_timeout, f); + pcb->ccp_localstate = 0; + ccp_set(pcb, 1, 0, 0, 0); +#if MPPE_SUPPORT + if (go->mppe) { + go->mppe = 0; + if (pcb->lcp_fsm.state == PPP_FSM_OPENED) { + /* If LCP is not already going down, make sure it does. */ + ppp_error("MPPE disabled"); + lcp_close(pcb, "MPPE disabled"); + } + } +#endif /* MPPE_SUPPORT */ +} + +#if PRINTPKT_SUPPORT +/* + * Print the contents of a CCP packet. + */ +static const char* const ccp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", + NULL, NULL, NULL, NULL, NULL, NULL, + "ResetReq", "ResetAck", +}; + +static int ccp_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) { + const u_char *p0, *optend; + int code, id, len; + int optlen; + + p0 = p; + if (plen < HEADERLEN) + return 0; + code = p[0]; + id = p[1]; + len = (p[2] << 8) + p[3]; + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(ccp_codenames) && ccp_codenames[code-1] != NULL) + printer(arg, " %s", ccp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + p += HEADERLEN; + + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print list of possible compression methods */ + while (len >= 2) { + code = p[0]; + optlen = p[1]; + if (optlen < 2 || optlen > len) + break; + printer(arg, " <"); + len -= optlen; + optend = p + optlen; + switch (code) { +#if MPPE_SUPPORT + case CI_MPPE: + if (optlen >= CILEN_MPPE) { + u_char mppe_opts; + + MPPE_CI_TO_OPTS(&p[2], mppe_opts); + printer(arg, "mppe %s %s %s %s %s %s%s", + (p[2] & MPPE_H_BIT)? "+H": "-H", + (p[5] & MPPE_M_BIT)? "+M": "-M", + (p[5] & MPPE_S_BIT)? "+S": "-S", + (p[5] & MPPE_L_BIT)? "+L": "-L", + (p[5] & MPPE_D_BIT)? "+D": "-D", + (p[5] & MPPE_C_BIT)? "+C": "-C", + (mppe_opts & MPPE_OPT_UNKNOWN)? " +U": ""); + if (mppe_opts & MPPE_OPT_UNKNOWN) + printer(arg, " (%.2x %.2x %.2x %.2x)", + p[2], p[3], p[4], p[5]); + p += CILEN_MPPE; + } + break; +#endif /* MPPE_SUPPORT */ +#if DEFLATE_SUPPORT + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (optlen >= CILEN_DEFLATE) { + printer(arg, "deflate%s %d", + (code == CI_DEFLATE_DRAFT? "(old#)": ""), + DEFLATE_SIZE(p[2])); + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL) + printer(arg, " method %d", DEFLATE_METHOD(p[2])); + if (p[3] != DEFLATE_CHK_SEQUENCE) + printer(arg, " check %d", p[3]); + p += CILEN_DEFLATE; + } + break; +#endif /* DEFLATE_SUPPORT */ +#if BSDCOMPRESS_SUPPORT + case CI_BSD_COMPRESS: + if (optlen >= CILEN_BSD_COMPRESS) { + printer(arg, "bsd v%d %d", BSD_VERSION(p[2]), + BSD_NBITS(p[2])); + p += CILEN_BSD_COMPRESS; + } + break; +#endif /* BSDCOMPRESS_SUPPORT */ +#if PREDICTOR_SUPPORT + case CI_PREDICTOR_1: + if (optlen >= CILEN_PREDICTOR_1) { + printer(arg, "predictor 1"); + p += CILEN_PREDICTOR_1; + } + break; + case CI_PREDICTOR_2: + if (optlen >= CILEN_PREDICTOR_2) { + printer(arg, "predictor 2"); + p += CILEN_PREDICTOR_2; + } + break; +#endif /* PREDICTOR_SUPPORT */ + default: + break; + } + while (p < optend) + printer(arg, " %.2x", *p++); + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + ppp_print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + default: + break; + } + + /* dump out the rest of the packet in hex */ + while (--len >= 0) + printer(arg, " %.2x", *p++); + + return p - p0; +} +#endif /* PRINTPKT_SUPPORT */ + +#if PPP_DATAINPUT +/* + * We have received a packet that the decompressor failed to + * decompress. Here we would expect to issue a reset-request, but + * Motorola has a patent on resetting the compressor as a result of + * detecting an error in the decompressed data after decompression. + * (See US patent 5,130,993; international patent publication number + * WO 91/10289; Australian patent 73296/91.) + * + * So we ask the kernel whether the error was detected after + * decompression; if it was, we take CCP down, thus disabling + * compression :-(, otherwise we issue the reset-request. + */ +static void ccp_datainput(ppp_pcb *pcb, u_char *pkt, int len) { + fsm *f; +#if MPPE_SUPPORT + ccp_options *go = &pcb->ccp_gotoptions; +#endif /* MPPE_SUPPORT */ + LWIP_UNUSED_ARG(pkt); + LWIP_UNUSED_ARG(len); + + f = &pcb->ccp_fsm; + if (f->state == PPP_FSM_OPENED) { + if (ccp_fatal_error(pcb)) { + /* + * Disable compression by taking CCP down. + */ + ppp_error("Lost compression sync: disabling compression"); + ccp_close(pcb, "Lost compression sync"); +#if MPPE_SUPPORT + /* + * If we were doing MPPE, we must also take the link down. + */ + if (go->mppe) { + ppp_error("Too many MPPE errors, closing LCP"); + lcp_close(pcb, "Too many MPPE errors"); + } +#endif /* MPPE_SUPPORT */ + } else { + /* + * Send a reset-request to reset the peer's compressor. + * We don't do that if we are still waiting for an + * acknowledgement to a previous reset-request. + */ + if (!(pcb->ccp_localstate & RACK_PENDING)) { + fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + pcb->ccp_localstate |= RACK_PENDING; + } else + pcb->ccp_localstate |= RREQ_REPEAT; + } + } +} +#endif /* PPP_DATAINPUT */ + +/* + * We have received a packet that the decompressor failed to + * decompress. Issue a reset-request. + */ +void ccp_resetrequest(ppp_pcb *pcb) { + fsm *f = &pcb->ccp_fsm; + + if (f->state != PPP_FSM_OPENED) + return; + + /* + * Send a reset-request to reset the peer's compressor. + * We don't do that if we are still waiting for an + * acknowledgement to a previous reset-request. + */ + if (!(pcb->ccp_localstate & RACK_PENDING)) { + fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + pcb->ccp_localstate |= RACK_PENDING; + } else + pcb->ccp_localstate |= RREQ_REPEAT; +} + +/* + * Timeout waiting for reset-ack. + */ +static void ccp_rack_timeout(void *arg) { + fsm *f = (fsm*)arg; + ppp_pcb *pcb = f->pcb; + + if (f->state == PPP_FSM_OPENED && (pcb->ccp_localstate & RREQ_REPEAT)) { + fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + pcb->ccp_localstate &= ~RREQ_REPEAT; + } else + pcb->ccp_localstate &= ~RACK_PENDING; +} + +#endif /* PPP_SUPPORT && CCP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/chap-md5.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/chap-md5.c new file mode 100644 index 0000000..88f069f --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/chap-md5.c @@ -0,0 +1,126 @@ +/* + * chap-md5.c - New CHAP/MD5 implementation. + * + * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/chap-new.h" +#include "netif/ppp/chap-md5.h" +#include "netif/ppp/magic.h" +#include "netif/ppp/pppcrypt.h" + +#define MD5_HASH_SIZE 16 +#define MD5_MIN_CHALLENGE 17 +#define MD5_MAX_CHALLENGE 24 +#define MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE 3 /* 2^3-1 = 7, 17+7 = 24 */ + +#if PPP_SERVER +static void chap_md5_generate_challenge(ppp_pcb *pcb, unsigned char *cp) { + int clen; + LWIP_UNUSED_ARG(pcb); + + clen = MD5_MIN_CHALLENGE + magic_pow(MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE); + *cp++ = clen; + magic_random_bytes(cp, clen); +} + +static int chap_md5_verify_response(ppp_pcb *pcb, int id, const char *name, + const unsigned char *secret, int secret_len, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) { + lwip_md5_context ctx; + unsigned char idbyte = id; + unsigned char hash[MD5_HASH_SIZE]; + int challenge_len, response_len; + LWIP_UNUSED_ARG(name); + LWIP_UNUSED_ARG(pcb); + + challenge_len = *challenge++; + response_len = *response++; + if (response_len == MD5_HASH_SIZE) { + /* Generate hash of ID, secret, challenge */ + lwip_md5_init(&ctx); + lwip_md5_starts(&ctx); + lwip_md5_update(&ctx, &idbyte, 1); + lwip_md5_update(&ctx, secret, secret_len); + lwip_md5_update(&ctx, challenge, challenge_len); + lwip_md5_finish(&ctx, hash); + lwip_md5_free(&ctx); + + /* Test if our hash matches the peer's response */ + if (memcmp(hash, response, MD5_HASH_SIZE) == 0) { + ppp_slprintf(message, message_space, "Access granted"); + return 1; + } + } + ppp_slprintf(message, message_space, "Access denied"); + return 0; +} +#endif /* PPP_SERVER */ + +static void chap_md5_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name, + const unsigned char *challenge, const char *secret, int secret_len, + unsigned char *private_) { + lwip_md5_context ctx; + unsigned char idbyte = id; + int challenge_len = *challenge++; + LWIP_UNUSED_ARG(our_name); + LWIP_UNUSED_ARG(private_); + LWIP_UNUSED_ARG(pcb); + + lwip_md5_init(&ctx); + lwip_md5_starts(&ctx); + lwip_md5_update(&ctx, &idbyte, 1); + lwip_md5_update(&ctx, (const u_char *)secret, secret_len); + lwip_md5_update(&ctx, challenge, challenge_len); + lwip_md5_finish(&ctx, &response[1]); + lwip_md5_free(&ctx); + response[0] = MD5_HASH_SIZE; +} + +const struct chap_digest_type md5_digest = { + CHAP_MD5, /* code */ +#if PPP_SERVER + chap_md5_generate_challenge, + chap_md5_verify_response, +#endif /* PPP_SERVER */ + chap_md5_make_response, + NULL, /* check_success */ + NULL, /* handle_failure */ +}; + +#endif /* PPP_SUPPORT && CHAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/chap-new.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/chap-new.c new file mode 100644 index 0000000..485122d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/chap-new.c @@ -0,0 +1,677 @@ +/* + * chap-new.c - New CHAP implementation. + * + * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#if 0 /* UNUSED */ +#include "session.h" +#endif /* UNUSED */ + +#include "netif/ppp/chap-new.h" +#include "netif/ppp/chap-md5.h" +#if MSCHAP_SUPPORT +#include "netif/ppp/chap_ms.h" +#endif +#include "netif/ppp/magic.h" + +#if 0 /* UNUSED */ +/* Hook for a plugin to validate CHAP challenge */ +int (*chap_verify_hook)(const char *name, const char *ourname, int id, + const struct chap_digest_type *digest, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) = NULL; +#endif /* UNUSED */ + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static option_t chap_option_list[] = { + { "chap-restart", o_int, &chap_timeout_time, + "Set timeout for CHAP", OPT_PRIO }, + { "chap-max-challenge", o_int, &pcb->settings.chap_max_transmits, + "Set max #xmits for challenge", OPT_PRIO }, + { "chap-interval", o_int, &pcb->settings.chap_rechallenge_time, + "Set interval for rechallenge", OPT_PRIO }, + { NULL } +}; +#endif /* PPP_OPTIONS */ + + +/* Values for flags in chap_client_state and chap_server_state */ +#define LOWERUP 1 +#define AUTH_STARTED 2 +#define AUTH_DONE 4 +#define AUTH_FAILED 8 +#define TIMEOUT_PENDING 0x10 +#define CHALLENGE_VALID 0x20 + +/* + * Prototypes. + */ +static void chap_init(ppp_pcb *pcb); +static void chap_lowerup(ppp_pcb *pcb); +static void chap_lowerdown(ppp_pcb *pcb); +#if PPP_SERVER +static void chap_timeout(void *arg); +static void chap_generate_challenge(ppp_pcb *pcb); +static void chap_handle_response(ppp_pcb *pcb, int code, + unsigned char *pkt, int len); +static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id, + const struct chap_digest_type *digest, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space); +#endif /* PPP_SERVER */ +static void chap_respond(ppp_pcb *pcb, int id, + unsigned char *pkt, int len); +static void chap_handle_status(ppp_pcb *pcb, int code, int id, + unsigned char *pkt, int len); +static void chap_protrej(ppp_pcb *pcb); +static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen); +#if PRINTPKT_SUPPORT +static int chap_print_pkt(const unsigned char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ + +/* List of digest types that we know about */ +static const struct chap_digest_type* const chap_digests[] = { + &md5_digest, +#if MSCHAP_SUPPORT + &chapms_digest, + &chapms2_digest, +#endif /* MSCHAP_SUPPORT */ + NULL +}; + +/* + * chap_init - reset to initial state. + */ +static void chap_init(ppp_pcb *pcb) { + LWIP_UNUSED_ARG(pcb); + +#if 0 /* Not necessary, everything is cleared in ppp_new() */ + memset(&pcb->chap_client, 0, sizeof(chap_client_state)); +#if PPP_SERVER + memset(&pcb->chap_server, 0, sizeof(chap_server_state)); +#endif /* PPP_SERVER */ +#endif /* 0 */ +} + +/* + * chap_lowerup - we can start doing stuff now. + */ +static void chap_lowerup(ppp_pcb *pcb) { + + pcb->chap_client.flags |= LOWERUP; +#if PPP_SERVER + pcb->chap_server.flags |= LOWERUP; + if (pcb->chap_server.flags & AUTH_STARTED) + chap_timeout(pcb); +#endif /* PPP_SERVER */ +} + +static void chap_lowerdown(ppp_pcb *pcb) { + + pcb->chap_client.flags = 0; +#if PPP_SERVER + if (pcb->chap_server.flags & TIMEOUT_PENDING) + UNTIMEOUT(chap_timeout, pcb); + pcb->chap_server.flags = 0; +#endif /* PPP_SERVER */ +} + +#if PPP_SERVER +/* + * chap_auth_peer - Start authenticating the peer. + * If the lower layer is already up, we start sending challenges, + * otherwise we wait for the lower layer to come up. + */ +void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code) { + const struct chap_digest_type *dp; + int i; + + if (pcb->chap_server.flags & AUTH_STARTED) { + ppp_error("CHAP: peer authentication already started!"); + return; + } + for (i = 0; (dp = chap_digests[i]) != NULL; ++i) + if (dp->code == digest_code) + break; + if (dp == NULL) + ppp_fatal("CHAP digest 0x%x requested but not available", + digest_code); + + pcb->chap_server.digest = dp; + pcb->chap_server.name = our_name; + /* Start with a random ID value */ + pcb->chap_server.id = magic(); + pcb->chap_server.flags |= AUTH_STARTED; + if (pcb->chap_server.flags & LOWERUP) + chap_timeout(pcb); +} +#endif /* PPP_SERVER */ + +/* + * chap_auth_with_peer - Prepare to authenticate ourselves to the peer. + * There isn't much to do until we receive a challenge. + */ +void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code) { + const struct chap_digest_type *dp; + int i; + + if(NULL == our_name) + return; + + if (pcb->chap_client.flags & AUTH_STARTED) { + ppp_error("CHAP: authentication with peer already started!"); + return; + } + for (i = 0; (dp = chap_digests[i]) != NULL; ++i) + if (dp->code == digest_code) + break; + + if (dp == NULL) + ppp_fatal("CHAP digest 0x%x requested but not available", + digest_code); + + pcb->chap_client.digest = dp; + pcb->chap_client.name = our_name; + pcb->chap_client.flags |= AUTH_STARTED; +} + +#if PPP_SERVER +/* + * chap_timeout - It's time to send another challenge to the peer. + * This could be either a retransmission of a previous challenge, + * or a new challenge to start re-authentication. + */ +static void chap_timeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + struct pbuf *p; + + pcb->chap_server.flags &= ~TIMEOUT_PENDING; + if ((pcb->chap_server.flags & CHALLENGE_VALID) == 0) { + pcb->chap_server.challenge_xmits = 0; + chap_generate_challenge(pcb); + pcb->chap_server.flags |= CHALLENGE_VALID; + } else if (pcb->chap_server.challenge_xmits >= pcb->settings.chap_max_transmits) { + pcb->chap_server.flags &= ~CHALLENGE_VALID; + pcb->chap_server.flags |= AUTH_DONE | AUTH_FAILED; + auth_peer_fail(pcb, PPP_CHAP); + return; + } + + p = pbuf_alloc(PBUF_RAW, (u16_t)(pcb->chap_server.challenge_pktlen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + MEMCPY(p->payload, pcb->chap_server.challenge, pcb->chap_server.challenge_pktlen); + ppp_write(pcb, p); + ++pcb->chap_server.challenge_xmits; + pcb->chap_server.flags |= TIMEOUT_PENDING; + TIMEOUT(chap_timeout, arg, pcb->settings.chap_timeout_time); +} + +/* + * chap_generate_challenge - generate a challenge string and format + * the challenge packet in pcb->chap_server.challenge_pkt. + */ +static void chap_generate_challenge(ppp_pcb *pcb) { + int clen = 1, nlen, len; + unsigned char *p; + + p = pcb->chap_server.challenge; + MAKEHEADER(p, PPP_CHAP); + p += CHAP_HDRLEN; + pcb->chap_server.digest->generate_challenge(pcb, p); + clen = *p; + nlen = strlen(pcb->chap_server.name); + memcpy(p + 1 + clen, pcb->chap_server.name, nlen); + + len = CHAP_HDRLEN + 1 + clen + nlen; + pcb->chap_server.challenge_pktlen = PPP_HDRLEN + len; + + p = pcb->chap_server.challenge + PPP_HDRLEN; + p[0] = CHAP_CHALLENGE; + p[1] = ++pcb->chap_server.id; + p[2] = len >> 8; + p[3] = len; +} + +/* + * chap_handle_response - check the response to our challenge. + */ +static void chap_handle_response(ppp_pcb *pcb, int id, + unsigned char *pkt, int len) { + int response_len, ok, mlen; + const unsigned char *response; + unsigned char *outp; + struct pbuf *p; + const char *name = NULL; /* initialized to shut gcc up */ +#if 0 /* UNUSED */ + int (*verifier)(const char *, const char *, int, const struct chap_digest_type *, + const unsigned char *, const unsigned char *, char *, int); +#endif /* UNUSED */ + char rname[MAXNAMELEN+1]; + char message[256]; + + if ((pcb->chap_server.flags & LOWERUP) == 0) + return; + if (id != pcb->chap_server.challenge[PPP_HDRLEN+1] || len < 2) + return; + if (pcb->chap_server.flags & CHALLENGE_VALID) { + response = pkt; + GETCHAR(response_len, pkt); + len -= response_len + 1; /* length of name */ + name = (char *)pkt + response_len; + if (len < 0) + return; + + if (pcb->chap_server.flags & TIMEOUT_PENDING) { + pcb->chap_server.flags &= ~TIMEOUT_PENDING; + UNTIMEOUT(chap_timeout, pcb); + } +#if PPP_REMOTENAME + if (pcb->settings.explicit_remote) { + name = pcb->remote_name; + } else +#endif /* PPP_REMOTENAME */ + { + /* Null terminate and clean remote name. */ + ppp_slprintf(rname, sizeof(rname), "%.*v", len, name); + name = rname; + } + +#if 0 /* UNUSED */ + if (chap_verify_hook) + verifier = chap_verify_hook; + else + verifier = chap_verify_response; + ok = (*verifier)(name, pcb->chap_server.name, id, pcb->chap_server.digest, + pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN, + response, pcb->chap_server.message, sizeof(pcb->chap_server.message)); +#endif /* UNUSED */ + ok = chap_verify_response(pcb, name, pcb->chap_server.name, id, pcb->chap_server.digest, + pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN, + response, message, sizeof(message)); +#if 0 /* UNUSED */ + if (!ok || !auth_number()) { +#endif /* UNUSED */ + if (!ok) { + pcb->chap_server.flags |= AUTH_FAILED; + ppp_warn("Peer %q failed CHAP authentication", name); + } + } else if ((pcb->chap_server.flags & AUTH_DONE) == 0) + return; + + /* send the response */ + mlen = strlen(message); + len = CHAP_HDRLEN + mlen; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +len), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (unsigned char *)p->payload; + MAKEHEADER(outp, PPP_CHAP); + + outp[0] = (pcb->chap_server.flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS; + outp[1] = id; + outp[2] = len >> 8; + outp[3] = len; + if (mlen > 0) + memcpy(outp + CHAP_HDRLEN, message, mlen); + ppp_write(pcb, p); + + if (pcb->chap_server.flags & CHALLENGE_VALID) { + pcb->chap_server.flags &= ~CHALLENGE_VALID; + if (!(pcb->chap_server.flags & AUTH_DONE) && !(pcb->chap_server.flags & AUTH_FAILED)) { + +#if 0 /* UNUSED */ + /* + * Auth is OK, so now we need to check session restrictions + * to ensure everything is OK, but only if we used a + * plugin, and only if we're configured to check. This + * allows us to do PAM checks on PPP servers that + * authenticate against ActiveDirectory, and use AD for + * account info (like when using Winbind integrated with + * PAM). + */ + if (session_mgmt && + session_check(name, NULL, devnam, NULL) == 0) { + pcb->chap_server.flags |= AUTH_FAILED; + ppp_warn("Peer %q failed CHAP Session verification", name); + } +#endif /* UNUSED */ + + } + if (pcb->chap_server.flags & AUTH_FAILED) { + auth_peer_fail(pcb, PPP_CHAP); + } else { + if ((pcb->chap_server.flags & AUTH_DONE) == 0) + auth_peer_success(pcb, PPP_CHAP, + pcb->chap_server.digest->code, + name, strlen(name)); + if (pcb->settings.chap_rechallenge_time) { + pcb->chap_server.flags |= TIMEOUT_PENDING; + TIMEOUT(chap_timeout, pcb, + pcb->settings.chap_rechallenge_time); + } + } + pcb->chap_server.flags |= AUTH_DONE; + } +} + +/* + * chap_verify_response - check whether the peer's response matches + * what we think it should be. Returns 1 if it does (authentication + * succeeded), or 0 if it doesn't. + */ +static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id, + const struct chap_digest_type *digest, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) { + int ok; + unsigned char secret[MAXSECRETLEN]; + int secret_len; + + /* Get the secret that the peer is supposed to know */ + if (!get_secret(pcb, name, ourname, (char *)secret, &secret_len, 1)) { + ppp_error("No CHAP secret found for authenticating %q", name); + return 0; + } + ok = digest->verify_response(pcb, id, name, secret, secret_len, challenge, + response, message, message_space); + memset(secret, 0, sizeof(secret)); + + return ok; +} +#endif /* PPP_SERVER */ + +/* + * chap_respond - Generate and send a response to a challenge. + */ +static void chap_respond(ppp_pcb *pcb, int id, + unsigned char *pkt, int len) { + int clen, nlen; + int secret_len; + struct pbuf *p; + u_char *outp; + char rname[MAXNAMELEN+1]; + char secret[MAXSECRETLEN+1]; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(RESP_MAX_PKTLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + if ((pcb->chap_client.flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED)) + return; /* not ready */ + if (len < 2 || len < pkt[0] + 1) + return; /* too short */ + clen = pkt[0]; + nlen = len - (clen + 1); + + /* Null terminate and clean remote name. */ + ppp_slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1); + +#if PPP_REMOTENAME + /* Microsoft doesn't send their name back in the PPP packet */ + if (pcb->settings.explicit_remote || (pcb->settings.remote_name[0] != 0 && rname[0] == 0)) + strlcpy(rname, pcb->settings.remote_name, sizeof(rname)); +#endif /* PPP_REMOTENAME */ + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(pcb, pcb->chap_client.name, rname, secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + ppp_warn("No CHAP secret found for authenticating us to %q", rname); + } + + outp = (u_char*)p->payload; + MAKEHEADER(outp, PPP_CHAP); + outp += CHAP_HDRLEN; + + pcb->chap_client.digest->make_response(pcb, outp, id, pcb->chap_client.name, pkt, + secret, secret_len, pcb->chap_client.priv); + memset(secret, 0, secret_len); + + clen = *outp; + nlen = strlen(pcb->chap_client.name); + memcpy(outp + clen + 1, pcb->chap_client.name, nlen); + + outp = (u_char*)p->payload + PPP_HDRLEN; + len = CHAP_HDRLEN + clen + 1 + nlen; + outp[0] = CHAP_RESPONSE; + outp[1] = id; + outp[2] = len >> 8; + outp[3] = len; + + pbuf_realloc(p, PPP_HDRLEN + len); + ppp_write(pcb, p); +} + +static void chap_handle_status(ppp_pcb *pcb, int code, int id, + unsigned char *pkt, int len) { + const char *msg = NULL; + LWIP_UNUSED_ARG(id); + + if ((pcb->chap_client.flags & (AUTH_DONE|AUTH_STARTED|LOWERUP)) + != (AUTH_STARTED|LOWERUP)) + return; + pcb->chap_client.flags |= AUTH_DONE; + + if (code == CHAP_SUCCESS) { + /* used for MS-CHAP v2 mutual auth, yuck */ + if (pcb->chap_client.digest->check_success != NULL) { + if (!(*pcb->chap_client.digest->check_success)(pcb, pkt, len, pcb->chap_client.priv)) + code = CHAP_FAILURE; + } else + msg = "CHAP authentication succeeded"; + } else { + if (pcb->chap_client.digest->handle_failure != NULL) + (*pcb->chap_client.digest->handle_failure)(pcb, pkt, len); + else + msg = "CHAP authentication failed"; + } + if (msg) { + if (len > 0) + ppp_info("%s: %.*v", msg, len, pkt); + else + ppp_info("%s", msg); + } + if (code == CHAP_SUCCESS) + auth_withpeer_success(pcb, PPP_CHAP, pcb->chap_client.digest->code); + else { + pcb->chap_client.flags |= AUTH_FAILED; + ppp_error("CHAP authentication failed"); + auth_withpeer_fail(pcb, PPP_CHAP); + } +} + +static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen) { + unsigned char code, id; + int len; + + if (pktlen < CHAP_HDRLEN) + return; + GETCHAR(code, pkt); + GETCHAR(id, pkt); + GETSHORT(len, pkt); + if (len < CHAP_HDRLEN || len > pktlen) + return; + len -= CHAP_HDRLEN; + + switch (code) { + case CHAP_CHALLENGE: + chap_respond(pcb, id, pkt, len); + break; +#if PPP_SERVER + case CHAP_RESPONSE: + chap_handle_response(pcb, id, pkt, len); + break; +#endif /* PPP_SERVER */ + case CHAP_FAILURE: + case CHAP_SUCCESS: + chap_handle_status(pcb, code, id, pkt, len); + break; + default: + break; + } +} + +static void chap_protrej(ppp_pcb *pcb) { + +#if PPP_SERVER + if (pcb->chap_server.flags & TIMEOUT_PENDING) { + pcb->chap_server.flags &= ~TIMEOUT_PENDING; + UNTIMEOUT(chap_timeout, pcb); + } + if (pcb->chap_server.flags & AUTH_STARTED) { + pcb->chap_server.flags = 0; + auth_peer_fail(pcb, PPP_CHAP); + } +#endif /* PPP_SERVER */ + if ((pcb->chap_client.flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) { + pcb->chap_client.flags &= ~AUTH_STARTED; + ppp_error("CHAP authentication failed due to protocol-reject"); + auth_withpeer_fail(pcb, PPP_CHAP); + } +} + +#if PRINTPKT_SUPPORT +/* + * chap_print_pkt - print the contents of a CHAP packet. + */ +static const char* const chap_code_names[] = { + "Challenge", "Response", "Success", "Failure" +}; + +static int chap_print_pkt(const unsigned char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len; + int clen, nlen; + unsigned char x; + + if (plen < CHAP_HDRLEN) + return 0; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HDRLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(chap_code_names)) + printer(arg, " %s", chap_code_names[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= CHAP_HDRLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) + break; + clen = p[0]; + if (len < clen + 1) + break; + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = "); + ppp_print_string(p, nlen, printer, arg); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " "); + ppp_print_string(p, len, printer, arg); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + /* no break */ + } + + return len + CHAP_HDRLEN; +} +#endif /* PRINTPKT_SUPPORT */ + +const struct protent chap_protent = { + PPP_CHAP, + chap_init, + chap_input, + chap_protrej, + chap_lowerup, + chap_lowerdown, + NULL, /* open */ + NULL, /* close */ +#if PRINTPKT_SUPPORT + chap_print_pkt, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + NULL, /* datainput */ +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + "CHAP", /* name */ + NULL, /* data_name */ +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + chap_option_list, + NULL, /* check_options */ +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +#endif /* PPP_SUPPORT && CHAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/chap_ms.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/chap_ms.c new file mode 100644 index 0000000..5a989c9 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/chap_ms.c @@ -0,0 +1,962 @@ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +/* + * Modifications by Frank Cusack, frank@google.com, March 2002. + * + * Implemented MS-CHAPv2 functionality, heavily based on sample + * implementation in RFC 2759. Implemented MPPE functionality, + * heavily based on sample implementation in RFC 3079. + * + * Copyright (c) 2002 Google, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/chap-new.h" +#include "netif/ppp/chap_ms.h" +#include "netif/ppp/pppcrypt.h" +#include "netif/ppp/magic.h" +#if MPPE_SUPPORT +#include "netif/ppp/mppe.h" /* For mppe_sha1_pad*, mppe_set_key() */ +#endif /* MPPE_SUPPORT */ + +#define SHA1_SIGNATURE_SIZE 20 +#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */ +#define MAX_NT_PASSWORD 256 /* Max (Unicode) chars in an NT pass */ + +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ +#define MS_CHAP2_RESPONSE_LEN 49 /* Response length for MS-CHAPv2 */ +#define MS_AUTH_RESPONSE_LENGTH 40 /* MS-CHAPv2 authenticator response, */ + /* as ASCII */ + +/* Error codes for MS-CHAP failure messages. */ +#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS 646 +#define MS_CHAP_ERROR_ACCT_DISABLED 647 +#define MS_CHAP_ERROR_PASSWD_EXPIRED 648 +#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION 649 +#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE 691 +#define MS_CHAP_ERROR_CHANGING_PASSWORD 709 + +/* + * Offsets within the response field for MS-CHAP + */ +#define MS_CHAP_LANMANRESP 0 +#define MS_CHAP_LANMANRESP_LEN 24 +#define MS_CHAP_NTRESP 24 +#define MS_CHAP_NTRESP_LEN 24 +#define MS_CHAP_USENT 48 + +/* + * Offsets within the response field for MS-CHAP2 + */ +#define MS_CHAP2_PEER_CHALLENGE 0 +#define MS_CHAP2_PEER_CHAL_LEN 16 +#define MS_CHAP2_RESERVED_LEN 8 +#define MS_CHAP2_NTRESP 24 +#define MS_CHAP2_NTRESP_LEN 24 +#define MS_CHAP2_FLAGS 48 + +#if MPPE_SUPPORT +#if 0 /* UNUSED */ +/* These values are the RADIUS attribute values--see RFC 2548. */ +#define MPPE_ENC_POL_ENC_ALLOWED 1 +#define MPPE_ENC_POL_ENC_REQUIRED 2 +#define MPPE_ENC_TYPES_RC4_40 2 +#define MPPE_ENC_TYPES_RC4_128 4 + +/* used by plugins (using above values) */ +extern void set_mppe_enc_types(int, int); +#endif /* UNUSED */ +#endif /* MPPE_SUPPORT */ + +/* Are we the authenticator or authenticatee? For MS-CHAPv2 key derivation. */ +#define MS_CHAP2_AUTHENTICATEE 0 +#define MS_CHAP2_AUTHENTICATOR 1 + +static void ascii2unicode (const char[], int, u_char[]); +static void NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]); +static void ChallengeResponse (const u_char *, const u_char *, u_char[24]); +static void ChallengeHash (const u_char[16], const u_char *, const char *, u_char[8]); +static void ChapMS_NT (const u_char *, const char *, int, u_char[24]); +static void ChapMS2_NT (const u_char *, const u_char[16], const char *, const char *, int, + u_char[24]); +static void GenerateAuthenticatorResponsePlain + (const char*, int, u_char[24], const u_char[16], const u_char *, + const char *, u_char[41]); +#ifdef MSLANMAN +static void ChapMS_LANMan (u_char *, char *, int, u_char *); +#endif + +static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE], + u_char NTResponse[24], const u_char PeerChallenge[16], + const u_char *rchallenge, const char *username, + u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]); + +#if MPPE_SUPPORT +static void Set_Start_Key (ppp_pcb *pcb, const u_char *, const char *, int); +static void SetMasterKeys (ppp_pcb *pcb, const char *, int, u_char[24], int); +#endif /* MPPE_SUPPORT */ + +static void ChapMS (ppp_pcb *pcb, const u_char *, const char *, int, u_char *); +static void ChapMS2 (ppp_pcb *pcb, const u_char *, const u_char *, const char *, const char *, int, + u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int); + +#ifdef MSLANMAN +bool ms_lanman = 0; /* Use LanMan password instead of NT */ + /* Has meaning only with MS-CHAP challenges */ +#endif + +#if MPPE_SUPPORT +#ifdef DEBUGMPPEKEY +/* For MPPE debug */ +/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */ +static char *mschap_challenge = NULL; +/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */ +static char *mschap2_peer_challenge = NULL; +#endif + +#include "netif/ppp/fsm.h" /* Need to poke MPPE options */ +#include "netif/ppp/ccp.h" +#endif /* MPPE_SUPPORT */ + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static option_t chapms_option_list[] = { +#ifdef MSLANMAN + { "ms-lanman", o_bool, &ms_lanman, + "Use LanMan passwd when using MS-CHAP", 1 }, +#endif +#ifdef DEBUGMPPEKEY + { "mschap-challenge", o_string, &mschap_challenge, + "specify CHAP challenge" }, + { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge, + "specify CHAP peer challenge" }, +#endif + { NULL } +}; +#endif /* PPP_OPTIONS */ + +#if PPP_SERVER +/* + * chapms_generate_challenge - generate a challenge for MS-CHAP. + * For MS-CHAP the challenge length is fixed at 8 bytes. + * The length goes in challenge[0] and the actual challenge starts + * at challenge[1]. + */ +static void chapms_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) { + LWIP_UNUSED_ARG(pcb); + + *challenge++ = 8; +#ifdef DEBUGMPPEKEY + if (mschap_challenge && strlen(mschap_challenge) == 8) + memcpy(challenge, mschap_challenge, 8); + else +#endif + magic_random_bytes(challenge, 8); +} + +static void chapms2_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) { + LWIP_UNUSED_ARG(pcb); + + *challenge++ = 16; +#ifdef DEBUGMPPEKEY + if (mschap_challenge && strlen(mschap_challenge) == 16) + memcpy(challenge, mschap_challenge, 16); + else +#endif + magic_random_bytes(challenge, 16); +} + +static int chapms_verify_response(ppp_pcb *pcb, int id, const char *name, + const unsigned char *secret, int secret_len, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) { + unsigned char md[MS_CHAP_RESPONSE_LEN]; + int diff; + int challenge_len, response_len; + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(name); + + challenge_len = *challenge++; /* skip length, is 8 */ + response_len = *response++; + if (response_len != MS_CHAP_RESPONSE_LEN) + goto bad; + +#ifndef MSLANMAN + if (!response[MS_CHAP_USENT]) { + /* Should really propagate this into the error packet. */ + ppp_notice("Peer request for LANMAN auth not supported"); + goto bad; + } +#endif + + /* Generate the expected response. */ + ChapMS(pcb, (const u_char *)challenge, (const char *)secret, secret_len, md); + +#ifdef MSLANMAN + /* Determine which part of response to verify against */ + if (!response[MS_CHAP_USENT]) + diff = memcmp(&response[MS_CHAP_LANMANRESP], + &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN); + else +#endif + diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP], + MS_CHAP_NTRESP_LEN); + + if (diff == 0) { + ppp_slprintf(message, message_space, "Access granted"); + return 1; + } + + bad: + /* See comments below for MS-CHAP V2 */ + ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0", + challenge_len, challenge); + return 0; +} + +static int chapms2_verify_response(ppp_pcb *pcb, int id, const char *name, + const unsigned char *secret, int secret_len, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) { + unsigned char md[MS_CHAP2_RESPONSE_LEN]; + char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; + int challenge_len, response_len; + LWIP_UNUSED_ARG(id); + + challenge_len = *challenge++; /* skip length, is 16 */ + response_len = *response++; + if (response_len != MS_CHAP2_RESPONSE_LEN) + goto bad; /* not even the right length */ + + /* Generate the expected response and our mutual auth. */ + ChapMS2(pcb, (const u_char*)challenge, (const u_char*)&response[MS_CHAP2_PEER_CHALLENGE], name, + (const char *)secret, secret_len, md, + (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR); + + /* compare MDs and send the appropriate status */ + /* + * Per RFC 2759, success message must be formatted as + * "S= M=" + * where + * is the Authenticator Response (mutual auth) + * is a text message + * + * However, some versions of Windows (win98 tested) do not know + * about the M= part (required per RFC 2759) and flag + * it as an error (reported incorrectly as an encryption error + * to the user). Since the RFC requires it, and it can be + * useful information, we supply it if the peer is a conforming + * system. Luckily (?), win98 sets the Flags field to 0x04 + * (contrary to RFC requirements) so we can use that to + * distinguish between conforming and non-conforming systems. + * + * Special thanks to Alex Swiridov for + * help debugging this. + */ + if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP], + MS_CHAP2_NTRESP_LEN) == 0) { + if (response[MS_CHAP2_FLAGS]) + ppp_slprintf(message, message_space, "S=%s", saresponse); + else + ppp_slprintf(message, message_space, "S=%s M=%s", + saresponse, "Access granted"); + return 1; + } + + bad: + /* + * Failure message must be formatted as + * "E=e R=r C=c V=v M=m" + * where + * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE) + * r = retry (we use 1, ok to retry) + * c = challenge to use for next response, we reuse previous + * v = Change Password version supported, we use 0 + * m = text message + * + * The M=m part is only for MS-CHAPv2. Neither win2k nor + * win98 (others untested) display the message to the user anyway. + * They also both ignore the E=e code. + * + * Note that it's safe to reuse the same challenge as we don't + * actually accept another response based on the error message + * (and no clients try to resend a response anyway). + * + * Basically, this whole bit is useless code, even the small + * implementation here is only because of overspecification. + */ + ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", + challenge_len, challenge, "Access denied"); + return 0; +} +#endif /* PPP_SERVER */ + +static void chapms_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name, + const unsigned char *challenge, const char *secret, int secret_len, + unsigned char *private_) { + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(our_name); + LWIP_UNUSED_ARG(private_); + challenge++; /* skip length, should be 8 */ + *response++ = MS_CHAP_RESPONSE_LEN; + ChapMS(pcb, challenge, secret, secret_len, response); +} + +static void chapms2_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name, + const unsigned char *challenge, const char *secret, int secret_len, + unsigned char *private_) { + LWIP_UNUSED_ARG(id); + challenge++; /* skip length, should be 16 */ + *response++ = MS_CHAP2_RESPONSE_LEN; + ChapMS2(pcb, challenge, +#ifdef DEBUGMPPEKEY + mschap2_peer_challenge, +#else + NULL, +#endif + our_name, secret, secret_len, response, private_, + MS_CHAP2_AUTHENTICATEE); +} + +static int chapms2_check_success(ppp_pcb *pcb, unsigned char *msg, int len, unsigned char *private_) { + LWIP_UNUSED_ARG(pcb); + + if ((len < MS_AUTH_RESPONSE_LENGTH + 2) || + strncmp((char *)msg, "S=", 2) != 0) { + /* Packet does not start with "S=" */ + ppp_error("MS-CHAPv2 Success packet is badly formed."); + return 0; + } + msg += 2; + len -= 2; + if (len < MS_AUTH_RESPONSE_LENGTH + || memcmp(msg, private_, MS_AUTH_RESPONSE_LENGTH)) { + /* Authenticator Response did not match expected. */ + ppp_error("MS-CHAPv2 mutual authentication failed."); + return 0; + } + /* Authenticator Response matches. */ + msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */ + len -= MS_AUTH_RESPONSE_LENGTH; + if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) { + msg += 3; /* Eat the delimiter */ + } else if (len) { + /* Packet has extra text which does not begin " M=" */ + ppp_error("MS-CHAPv2 Success packet is badly formed."); + return 0; + } + return 1; +} + +static void chapms_handle_failure(ppp_pcb *pcb, unsigned char *inp, int len) { + int err; + const char *p; + char msg[64]; + LWIP_UNUSED_ARG(pcb); + + /* We want a null-terminated string for strxxx(). */ + len = LWIP_MIN(len, 63); + MEMCPY(msg, inp, len); + msg[len] = 0; + p = msg; + + /* + * Deal with MS-CHAP formatted failure messages; just print the + * M= part (if any). For MS-CHAP we're not really supposed + * to use M=, but it shouldn't hurt. See + * chapms[2]_verify_response. + */ + if (!strncmp(p, "E=", 2)) + err = strtol(p+2, NULL, 10); /* Remember the error code. */ + else + goto print_msg; /* Message is badly formatted. */ + + if (len && ((p = strstr(p, " M=")) != NULL)) { + /* M= field found. */ + p += 3; + } else { + /* No M=; use the error code. */ + switch (err) { + case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS: + p = "E=646 Restricted logon hours"; + break; + + case MS_CHAP_ERROR_ACCT_DISABLED: + p = "E=647 Account disabled"; + break; + + case MS_CHAP_ERROR_PASSWD_EXPIRED: + p = "E=648 Password expired"; + break; + + case MS_CHAP_ERROR_NO_DIALIN_PERMISSION: + p = "E=649 No dialin permission"; + break; + + case MS_CHAP_ERROR_AUTHENTICATION_FAILURE: + p = "E=691 Authentication failure"; + break; + + case MS_CHAP_ERROR_CHANGING_PASSWORD: + /* Should never see this, we don't support Change Password. */ + p = "E=709 Error changing password"; + break; + + default: + ppp_error("Unknown MS-CHAP authentication failure: %.*v", + len, inp); + return; + } + } +print_msg: + if (p != NULL) + ppp_error("MS-CHAP authentication failed: %v", p); +} + +static void ChallengeResponse(const u_char *challenge, + const u_char PasswordHash[MD4_SIGNATURE_SIZE], + u_char response[24]) { + u_char ZPasswordHash[21]; + lwip_des_context des; + u_char des_key[8]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + MEMCPY(ZPasswordHash, PasswordHash, MD4_SIGNATURE_SIZE); + +#if 0 + dbglog("ChallengeResponse - ZPasswordHash %.*B", + sizeof(ZPasswordHash), ZPasswordHash); +#endif + + pppcrypt_56_to_64_bit_key(ZPasswordHash + 0, des_key); + lwip_des_init(&des); + lwip_des_setkey_enc(&des, des_key); + lwip_des_crypt_ecb(&des, challenge, response +0); + lwip_des_free(&des); + + pppcrypt_56_to_64_bit_key(ZPasswordHash + 7, des_key); + lwip_des_init(&des); + lwip_des_setkey_enc(&des, des_key); + lwip_des_crypt_ecb(&des, challenge, response +8); + lwip_des_free(&des); + + pppcrypt_56_to_64_bit_key(ZPasswordHash + 14, des_key); + lwip_des_init(&des); + lwip_des_setkey_enc(&des, des_key); + lwip_des_crypt_ecb(&des, challenge, response +16); + lwip_des_free(&des); + +#if 0 + dbglog("ChallengeResponse - response %.24B", response); +#endif +} + +static void ChallengeHash(const u_char PeerChallenge[16], const u_char *rchallenge, + const char *username, u_char Challenge[8]) { + lwip_sha1_context sha1Context; + u_char sha1Hash[SHA1_SIGNATURE_SIZE]; + const char *user; + + /* remove domain from "domain\username" */ + if ((user = strrchr(username, '\\')) != NULL) + ++user; + else + user = username; + + lwip_sha1_init(&sha1Context); + lwip_sha1_starts(&sha1Context); + lwip_sha1_update(&sha1Context, PeerChallenge, 16); + lwip_sha1_update(&sha1Context, rchallenge, 16); + lwip_sha1_update(&sha1Context, (const unsigned char*)user, strlen(user)); + lwip_sha1_finish(&sha1Context, sha1Hash); + lwip_sha1_free(&sha1Context); + + MEMCPY(Challenge, sha1Hash, 8); +} + +/* + * Convert the ASCII version of the password to Unicode. + * This implicitly supports 8-bit ISO8859/1 characters. + * This gives us the little-endian representation, which + * is assumed by all M$ CHAP RFCs. (Unicode byte ordering + * is machine-dependent.) + */ +static void ascii2unicode(const char ascii[], int ascii_len, u_char unicode[]) { + int i; + + BZERO(unicode, ascii_len * 2); + for (i = 0; i < ascii_len; i++) + unicode[i * 2] = (u_char) ascii[i]; +} + +static void NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE]) { + lwip_md4_context md4Context; + + lwip_md4_init(&md4Context); + lwip_md4_starts(&md4Context); + lwip_md4_update(&md4Context, secret, secret_len); + lwip_md4_finish(&md4Context, hash); + lwip_md4_free(&md4Context); +} + +static void ChapMS_NT(const u_char *rchallenge, const char *secret, int secret_len, + u_char NTResponse[24]) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + + /* Hash the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + + ChallengeResponse(rchallenge, PasswordHash, NTResponse); +} + +static void ChapMS2_NT(const u_char *rchallenge, const u_char PeerChallenge[16], const char *username, + const char *secret, int secret_len, u_char NTResponse[24]) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char Challenge[8]; + + ChallengeHash(PeerChallenge, rchallenge, username, Challenge); + + /* Hash the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + + ChallengeResponse(Challenge, PasswordHash, NTResponse); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len, + unsigned char *response) { + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + lwip_des_context des; + u_char des_key[8]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) + UcasePassword[i] = (u_char)toupper(secret[i]); + + pppcrypt_56_to_64_bit_key(UcasePassword +0, des_key); + lwip_des_init(&des); + lwip_des_setkey_enc(&des, des_key); + lwip_des_crypt_ecb(&des, StdText, PasswordHash +0); + lwip_des_free(&des); + + pppcrypt_56_to_64_bit_key(UcasePassword +7, des_key); + lwip_des_init(&des); + lwip_des_setkey_enc(&des, des_key); + lwip_des_crypt_ecb(&des, StdText, PasswordHash +8); + lwip_des_free(&des); + + ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]); +} +#endif + + +static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE], + u_char NTResponse[24], const u_char PeerChallenge[16], + const u_char *rchallenge, const char *username, + u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) { + /* + * "Magic" constants used in response generation, from RFC 2759. + */ + static const u_char Magic1[39] = /* "Magic server to client signing constant" */ + { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, + 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; + static const u_char Magic2[41] = /* "Pad to make it do more than one iteration" */ + { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, + 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, + 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, + 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, + 0x6E }; + + int i; + lwip_sha1_context sha1Context; + u_char Digest[SHA1_SIGNATURE_SIZE]; + u_char Challenge[8]; + + lwip_sha1_init(&sha1Context); + lwip_sha1_starts(&sha1Context); + lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + lwip_sha1_update(&sha1Context, NTResponse, 24); + lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1)); + lwip_sha1_finish(&sha1Context, Digest); + lwip_sha1_free(&sha1Context); + + ChallengeHash(PeerChallenge, rchallenge, username, Challenge); + + lwip_sha1_init(&sha1Context); + lwip_sha1_starts(&sha1Context); + lwip_sha1_update(&sha1Context, Digest, sizeof(Digest)); + lwip_sha1_update(&sha1Context, Challenge, sizeof(Challenge)); + lwip_sha1_update(&sha1Context, Magic2, sizeof(Magic2)); + lwip_sha1_finish(&sha1Context, Digest); + lwip_sha1_free(&sha1Context); + + /* Convert to ASCII hex string. */ + for (i = 0; i < LWIP_MAX((MS_AUTH_RESPONSE_LENGTH / 2), (int)sizeof(Digest)); i++) + sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]); +} + + +static void GenerateAuthenticatorResponsePlain( + const char *secret, int secret_len, + u_char NTResponse[24], const u_char PeerChallenge[16], + const u_char *rchallenge, const char *username, + u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; + + /* Hash (x2) the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + NTPasswordHash(PasswordHash, sizeof(PasswordHash), + PasswordHashHash); + + GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge, + rchallenge, username, authResponse); +} + + +#if MPPE_SUPPORT +/* + * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079) + */ +static void Set_Start_Key(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; + lwip_sha1_context sha1Context; + u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ + + /* Hash (x2) the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); + + lwip_sha1_init(&sha1Context); + lwip_sha1_starts(&sha1Context); + lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + lwip_sha1_update(&sha1Context, rchallenge, 8); + lwip_sha1_finish(&sha1Context, Digest); + lwip_sha1_free(&sha1Context); + + /* Same key in both directions. */ + mppe_set_key(pcb, &pcb->mppe_comp, Digest); + mppe_set_key(pcb, &pcb->mppe_decomp, Digest); + + pcb->mppe_keys_set = 1; +} + +/* + * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079) + */ +static void SetMasterKeys(ppp_pcb *pcb, const char *secret, int secret_len, u_char NTResponse[24], int IsServer) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; + lwip_sha1_context sha1Context; + u_char MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ + u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ + const u_char *s; + + /* "This is the MPPE Master Key" */ + static const u_char Magic1[27] = + { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 }; + /* "On the client side, this is the send key; " + "on the server side, it is the receive key." */ + static const u_char Magic2[84] = + { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e }; + /* "On the client side, this is the receive key; " + "on the server side, it is the send key." */ + static const u_char Magic3[84] = + { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e }; + + /* Hash (x2) the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); + + lwip_sha1_init(&sha1Context); + lwip_sha1_starts(&sha1Context); + lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + lwip_sha1_update(&sha1Context, NTResponse, 24); + lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1)); + lwip_sha1_finish(&sha1Context, MasterKey); + lwip_sha1_free(&sha1Context); + + /* + * generate send key + */ + if (IsServer) + s = Magic3; + else + s = Magic2; + lwip_sha1_init(&sha1Context); + lwip_sha1_starts(&sha1Context); + lwip_sha1_update(&sha1Context, MasterKey, 16); + lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE); + lwip_sha1_update(&sha1Context, s, 84); + lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE); + lwip_sha1_finish(&sha1Context, Digest); + lwip_sha1_free(&sha1Context); + + mppe_set_key(pcb, &pcb->mppe_comp, Digest); + + /* + * generate recv key + */ + if (IsServer) + s = Magic2; + else + s = Magic3; + lwip_sha1_init(&sha1Context); + lwip_sha1_starts(&sha1Context); + lwip_sha1_update(&sha1Context, MasterKey, 16); + lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE); + lwip_sha1_update(&sha1Context, s, 84); + lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE); + lwip_sha1_finish(&sha1Context, Digest); + lwip_sha1_free(&sha1Context); + + mppe_set_key(pcb, &pcb->mppe_decomp, Digest); + + pcb->mppe_keys_set = 1; +} + +#endif /* MPPE_SUPPORT */ + + +static void ChapMS(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len, + unsigned char *response) { +#if !MPPE_SUPPORT + LWIP_UNUSED_ARG(pcb); +#endif /* !MPPE_SUPPORT */ + BZERO(response, MS_CHAP_RESPONSE_LEN); + + ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, secret, secret_len, + &response[MS_CHAP_LANMANRESP]); + + /* preferred method is set by option */ + response[MS_CHAP_USENT] = !ms_lanman; +#else + response[MS_CHAP_USENT] = 1; +#endif + +#if MPPE_SUPPORT + Set_Start_Key(pcb, rchallenge, secret, secret_len); +#endif /* MPPE_SUPPORT */ +} + + +/* + * If PeerChallenge is NULL, one is generated and the PeerChallenge + * field of response is filled in. Call this way when generating a response. + * If PeerChallenge is supplied, it is copied into the PeerChallenge field. + * Call this way when verifying a response (or debugging). + * Do not call with PeerChallenge = response. + * + * The PeerChallenge field of response is then used for calculation of the + * Authenticator Response. + */ +static void ChapMS2(ppp_pcb *pcb, const u_char *rchallenge, const u_char *PeerChallenge, + const char *user, const char *secret, int secret_len, unsigned char *response, + u_char authResponse[], int authenticator) { + /* ARGSUSED */ + LWIP_UNUSED_ARG(authenticator); +#if !MPPE_SUPPORT + LWIP_UNUSED_ARG(pcb); +#endif /* !MPPE_SUPPORT */ + + BZERO(response, MS_CHAP2_RESPONSE_LEN); + + /* Generate the Peer-Challenge if requested, or copy it if supplied. */ + if (!PeerChallenge) + magic_random_bytes(&response[MS_CHAP2_PEER_CHALLENGE], MS_CHAP2_PEER_CHAL_LEN); + else + MEMCPY(&response[MS_CHAP2_PEER_CHALLENGE], PeerChallenge, + MS_CHAP2_PEER_CHAL_LEN); + + /* Generate the NT-Response */ + ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user, + secret, secret_len, &response[MS_CHAP2_NTRESP]); + + /* Generate the Authenticator Response. */ + GenerateAuthenticatorResponsePlain(secret, secret_len, + &response[MS_CHAP2_NTRESP], + &response[MS_CHAP2_PEER_CHALLENGE], + rchallenge, user, authResponse); + +#if MPPE_SUPPORT + SetMasterKeys(pcb, secret, secret_len, + &response[MS_CHAP2_NTRESP], authenticator); +#endif /* MPPE_SUPPORT */ +} + +#if 0 /* UNUSED */ +#if MPPE_SUPPORT +/* + * Set MPPE options from plugins. + */ +void set_mppe_enc_types(int policy, int types) { + /* Early exit for unknown policies. */ + if (policy != MPPE_ENC_POL_ENC_ALLOWED || + policy != MPPE_ENC_POL_ENC_REQUIRED) + return; + + /* Don't modify MPPE if it's optional and wasn't already configured. */ + if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe) + return; + + /* + * Disable undesirable encryption types. Note that we don't ENABLE + * any encryption types, to avoid overriding manual configuration. + */ + switch(types) { + case MPPE_ENC_TYPES_RC4_40: + ccp_wantoptions[0].mppe &= ~MPPE_OPT_128; /* disable 128-bit */ + break; + case MPPE_ENC_TYPES_RC4_128: + ccp_wantoptions[0].mppe &= ~MPPE_OPT_40; /* disable 40-bit */ + break; + default: + break; + } +} +#endif /* MPPE_SUPPORT */ +#endif /* UNUSED */ + +const struct chap_digest_type chapms_digest = { + CHAP_MICROSOFT, /* code */ +#if PPP_SERVER + chapms_generate_challenge, + chapms_verify_response, +#endif /* PPP_SERVER */ + chapms_make_response, + NULL, /* check_success */ + chapms_handle_failure, +}; + +const struct chap_digest_type chapms2_digest = { + CHAP_MICROSOFT_V2, /* code */ +#if PPP_SERVER + chapms2_generate_challenge, + chapms2_verify_response, +#endif /* PPP_SERVER */ + chapms2_make_response, + chapms2_check_success, + chapms_handle_failure, +}; + +#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/demand.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/demand.c new file mode 100644 index 0000000..26c6c30 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/demand.c @@ -0,0 +1,465 @@ +/* + * demand.c - Support routines for demand-dialling. + * + * Copyright (c) 1996-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && DEMAND_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PPP_FILTER +#include +#endif + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/ipcp.h" +#include "netif/ppp/lcp.h" + +char *frame; +int framelen; +int framemax; +int escape_flag; +int flush_flag; +int fcs; + +struct packet { + int length; + struct packet *next; + unsigned char data[1]; +}; + +struct packet *pend_q; +struct packet *pend_qtail; + +static int active_packet (unsigned char *, int); + +/* + * demand_conf - configure the interface for doing dial-on-demand. + */ +void +demand_conf() +{ + int i; + const struct protent *protp; + +/* framemax = lcp_allowoptions[0].mru; + if (framemax < PPP_MRU) */ + framemax = PPP_MRU; + framemax += PPP_HDRLEN + PPP_FCSLEN; + frame = malloc(framemax); + if (frame == NULL) + novm("demand frame"); + framelen = 0; + pend_q = NULL; + escape_flag = 0; + flush_flag = 0; + fcs = PPP_INITFCS; + + netif_set_mtu(pcb, LWIP_MIN(lcp_allowoptions[0].mru, PPP_MRU)); + if (ppp_send_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0 + || ppp_recv_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0) + fatal("Couldn't set up demand-dialled PPP interface: %m"); + +#ifdef PPP_FILTER + set_filters(&pass_filter, &active_filter); +#endif + + /* + * Call the demand_conf procedure for each protocol that's got one. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->demand_conf != NULL) + ((*protp->demand_conf)(pcb)); +/* FIXME: find a way to die() here */ +#if 0 + if (!((*protp->demand_conf)(pcb))) + die(1); +#endif +} + + +/* + * demand_block - set each network protocol to block further packets. + */ +void +demand_block() +{ + int i; + const struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->demand_conf != NULL) + sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_QUEUE); + get_loop_output(); +} + +/* + * demand_discard - set each network protocol to discard packets + * with an error. + */ +void +demand_discard() +{ + struct packet *pkt, *nextpkt; + int i; + const struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->demand_conf != NULL) + sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_ERROR); + get_loop_output(); + + /* discard all saved packets */ + for (pkt = pend_q; pkt != NULL; pkt = nextpkt) { + nextpkt = pkt->next; + free(pkt); + } + pend_q = NULL; + framelen = 0; + flush_flag = 0; + escape_flag = 0; + fcs = PPP_INITFCS; +} + +/* + * demand_unblock - set each enabled network protocol to pass packets. + */ +void +demand_unblock() +{ + int i; + const struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->demand_conf != NULL) + sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_PASS); +} + +/* + * FCS lookup table as calculated by genfcstab. + */ +static u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* + * loop_chars - process characters received from the loopback. + * Calls loop_frame when a complete frame has been accumulated. + * Return value is 1 if we need to bring up the link, 0 otherwise. + */ +int +loop_chars(p, n) + unsigned char *p; + int n; +{ + int c, rv; + + rv = 0; + +/* check for synchronous connection... */ + + if ( (p[0] == 0xFF) && (p[1] == 0x03) ) { + rv = loop_frame(p,n); + return rv; + } + + for (; n > 0; --n) { + c = *p++; + if (c == PPP_FLAG) { + if (!escape_flag && !flush_flag + && framelen > 2 && fcs == PPP_GOODFCS) { + framelen -= 2; + if (loop_frame((unsigned char *)frame, framelen)) + rv = 1; + } + framelen = 0; + flush_flag = 0; + escape_flag = 0; + fcs = PPP_INITFCS; + continue; + } + if (flush_flag) + continue; + if (escape_flag) { + c ^= PPP_TRANS; + escape_flag = 0; + } else if (c == PPP_ESCAPE) { + escape_flag = 1; + continue; + } + if (framelen >= framemax) { + flush_flag = 1; + continue; + } + frame[framelen++] = c; + fcs = PPP_FCS(fcs, c); + } + return rv; +} + +/* + * loop_frame - given a frame obtained from the loopback, + * decide whether to bring up the link or not, and, if we want + * to transmit this frame later, put it on the pending queue. + * Return value is 1 if we need to bring up the link, 0 otherwise. + * We assume that the kernel driver has already applied the + * pass_filter, so we won't get packets it rejected. + * We apply the active_filter to see if we want this packet to + * bring up the link. + */ +int +loop_frame(frame, len) + unsigned char *frame; + int len; +{ + struct packet *pkt; + + /* dbglog("from loop: %P", frame, len); */ + if (len < PPP_HDRLEN) + return 0; + if ((PPP_PROTOCOL(frame) & 0x8000) != 0) + return 0; /* shouldn't get any of these anyway */ + if (!active_packet(frame, len)) + return 0; + + pkt = (struct packet *) malloc(sizeof(struct packet) + len); + if (pkt != NULL) { + pkt->length = len; + pkt->next = NULL; + memcpy(pkt->data, frame, len); + if (pend_q == NULL) + pend_q = pkt; + else + pend_qtail->next = pkt; + pend_qtail = pkt; + } + return 1; +} + +/* + * demand_rexmit - Resend all those frames which we got via the + * loopback, now that the real serial link is up. + */ +void +demand_rexmit(proto, newip) + int proto; + u32_t newip; +{ + struct packet *pkt, *prev, *nextpkt; + unsigned short checksum; + unsigned short pkt_checksum = 0; + unsigned iphdr; + struct timeval tv; + char cv = 0; + char ipstr[16]; + + prev = NULL; + pkt = pend_q; + pend_q = NULL; + tv.tv_sec = 1; + tv.tv_usec = 0; + select(0,NULL,NULL,NULL,&tv); /* Sleep for 1 Seconds */ + for (; pkt != NULL; pkt = nextpkt) { + nextpkt = pkt->next; + if (PPP_PROTOCOL(pkt->data) == proto) { + if ( (proto == PPP_IP) && newip ) { + /* Get old checksum */ + + iphdr = (pkt->data[4] & 15) << 2; + checksum = *((unsigned short *) (pkt->data+14)); + if (checksum == 0xFFFF) { + checksum = 0; + } + + + if (pkt->data[13] == 17) { + pkt_checksum = *((unsigned short *) (pkt->data+10+iphdr)); + if (pkt_checksum) { + cv = 1; + if (pkt_checksum == 0xFFFF) { + pkt_checksum = 0; + } + } + else { + cv = 0; + } + } + + if (pkt->data[13] == 6) { + pkt_checksum = *((unsigned short *) (pkt->data+20+iphdr)); + cv = 1; + if (pkt_checksum == 0xFFFF) { + pkt_checksum = 0; + } + } + + /* Delete old Source-IP-Address */ + checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF; + checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF; + + pkt_checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF; + pkt_checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF; + + /* Change Source-IP-Address */ + * ((u32_t *) (pkt->data + 16)) = newip; + + /* Add new Source-IP-Address */ + checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF; + checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF; + + pkt_checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF; + pkt_checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF; + + /* Write new checksum */ + if (!checksum) { + checksum = 0xFFFF; + } + *((unsigned short *) (pkt->data+14)) = checksum; + if (pkt->data[13] == 6) { + *((unsigned short *) (pkt->data+20+iphdr)) = pkt_checksum; + } + if (cv && (pkt->data[13] == 17) ) { + *((unsigned short *) (pkt->data+10+iphdr)) = pkt_checksum; + } + + /* Log Packet */ + strcpy(ipstr,inet_ntoa(*( (struct in_addr *) (pkt->data+16)))); + if (pkt->data[13] == 1) { + syslog(LOG_INFO,"Open ICMP %s -> %s\n", + ipstr, + inet_ntoa(*( (struct in_addr *) (pkt->data+20)))); + } else { + syslog(LOG_INFO,"Open %s %s:%d -> %s:%d\n", + pkt->data[13] == 6 ? "TCP" : "UDP", + ipstr, + ntohs(*( (short *) (pkt->data+iphdr+4))), + inet_ntoa(*( (struct in_addr *) (pkt->data+20))), + ntohs(*( (short *) (pkt->data+iphdr+6)))); + } + } + output(pcb, pkt->data, pkt->length); + free(pkt); + } else { + if (prev == NULL) + pend_q = pkt; + else + prev->next = pkt; + prev = pkt; + } + } + pend_qtail = prev; + if (prev != NULL) + prev->next = NULL; +} + +/* + * Scan a packet to decide whether it is an "active" packet, + * that is, whether it is worth bringing up the link for. + */ +static int +active_packet(p, len) + unsigned char *p; + int len; +{ + int proto, i; + const struct protent *protp; + + if (len < PPP_HDRLEN) + return 0; + proto = PPP_PROTOCOL(p); +#ifdef PPP_FILTER + p[0] = 1; /* outbound packet indicator */ + if ((pass_filter.bf_len != 0 + && bpf_filter(pass_filter.bf_insns, p, len, len) == 0) + || (active_filter.bf_len != 0 + && bpf_filter(active_filter.bf_insns, p, len, len) == 0)) { + p[0] = 0xff; + return 0; + } + p[0] = 0xff; +#endif + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) { + if (protp->active_pkt == NULL) + return 1; + return (*protp->active_pkt)(p, len); + } + } + return 0; /* not a supported protocol !!?? */ +} + +#endif /* PPP_SUPPORT && DEMAND_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/eap.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/eap.c new file mode 100644 index 0000000..8fb5636 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/eap.c @@ -0,0 +1,2423 @@ +/* + * eap.c - Extensible Authentication Protocol for PPP (RFC 2284) + * + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + * + * Non-exclusive rights to redistribute, modify, translate, and use + * this software in source and binary forms, in whole or in part, is + * hereby granted, provided that the above copyright notice is + * duplicated in any source form, and that neither the name of the + * copyright holder nor the author is used to endorse or promote + * products derived from this software. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Original version by James Carlson + * + * This implementation of EAP supports MD5-Challenge and SRP-SHA1 + * authentication styles. Note that support of MD5-Challenge is a + * requirement of RFC 2284, and that it's essentially just a + * reimplementation of regular RFC 1994 CHAP using EAP messages. + * + * As an authenticator ("server"), there are multiple phases for each + * style. In the first phase of each style, the unauthenticated peer + * name is queried using the EAP Identity request type. If the + * "remotename" option is used, then this phase is skipped, because + * the peer's name is presumed to be known. + * + * For MD5-Challenge, there are two phases, and the second phase + * consists of sending the challenge itself and handling the + * associated response. + * + * For SRP-SHA1, there are four phases. The second sends 's', 'N', + * and 'g'. The reply contains 'A'. The third sends 'B', and the + * reply contains 'M1'. The forth sends the 'M2' value. + * + * As an authenticatee ("client"), there's just a single phase -- + * responding to the queries generated by the peer. EAP is an + * authenticator-driven protocol. + * + * Based on draft-ietf-pppext-eap-srp-03.txt. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && EAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/eap.h" +#include "netif/ppp/magic.h" +#include "netif/ppp/pppcrypt.h" + +#ifdef USE_SRP +#include +#include +#include +#endif /* USE_SRP */ + +#ifndef SHA_DIGESTSIZE +#define SHA_DIGESTSIZE 20 +#endif + +#ifdef USE_SRP +static char *pn_secret = NULL; /* Pseudonym generating secret */ +#endif + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static option_t eap_option_list[] = { + { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout, + "Set retransmit timeout for EAP Requests (server)" }, + { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests, + "Set max number of EAP Requests sent (server)" }, + { "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout, + "Set time limit for peer EAP authentication" }, + { "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests, + "Set max number of EAP Requests allows (client)" }, + { "eap-interval", o_int, &eap_states[0].es_rechallenge, + "Set interval for EAP rechallenge" }, +#ifdef USE_SRP + { "srp-interval", o_int, &eap_states[0].es_lwrechallenge, + "Set interval for SRP lightweight rechallenge" }, + { "srp-pn-secret", o_string, &pn_secret, + "Long term pseudonym generation secret" }, + { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo, + "Use pseudonym if offered one by server", 1 }, +#endif + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points. + */ +static void eap_init(ppp_pcb *pcb); +static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen); +static void eap_protrej(ppp_pcb *pcb); +static void eap_lowerup(ppp_pcb *pcb); +static void eap_lowerdown(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int eap_printpkt(const u_char *inp, int inlen, + void (*)(void *arg, const char *fmt, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ + +const struct protent eap_protent = { + PPP_EAP, /* protocol number */ + eap_init, /* initialization procedure */ + eap_input, /* process a received packet */ + eap_protrej, /* process a received protocol-reject */ + eap_lowerup, /* lower layer has gone up */ + eap_lowerdown, /* lower layer has gone down */ + NULL, /* open the protocol */ + NULL, /* close the protocol */ +#if PRINTPKT_SUPPORT + eap_printpkt, /* print a packet in readable form */ +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + NULL, /* process a received data packet */ +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + "EAP", /* text name of protocol */ + NULL, /* text name of corresponding data protocol */ +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + eap_option_list, /* list of command-line options */ + NULL, /* check requested options; assign defaults */ +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, /* configure interface for demand-dial */ + NULL /* say whether to bring up link for this pkt */ +#endif /* DEMAND_SUPPORT */ +}; + +#ifdef USE_SRP +/* + * A well-known 2048 bit modulus. + */ +static const u_char wkmodulus[] = { + 0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B, + 0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F, + 0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07, + 0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50, + 0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED, + 0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D, + 0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D, + 0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50, + 0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0, + 0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3, + 0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8, + 0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8, + 0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA, + 0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74, + 0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7, + 0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B, + 0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16, + 0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81, + 0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A, + 0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48, + 0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D, + 0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA, + 0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78, + 0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6, + 0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29, + 0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8, + 0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82, + 0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6, + 0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4, + 0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75, + 0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2, + 0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73 +}; +#endif + +#if PPP_SERVER +/* Local forward declarations. */ +static void eap_server_timeout(void *arg); +#endif /* PPP_SERVER */ + +/* + * Convert EAP state code to printable string for debug. + */ +static const char * eap_state_name(enum eap_state_code esc) +{ + static const char *state_names[] = { EAP_STATES }; + + return (state_names[(int)esc]); +} + +/* + * eap_init - Initialize state for an EAP user. This is currently + * called once by main() during start-up. + */ +static void eap_init(ppp_pcb *pcb) { + + BZERO(&pcb->eap, sizeof(eap_state)); +#if PPP_SERVER + pcb->eap.es_server.ea_id = magic(); +#endif /* PPP_SERVER */ +} + +/* + * eap_client_timeout - Give up waiting for the peer to send any + * Request messages. + */ +static void eap_client_timeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (!eap_client_active(pcb)) + return; + + ppp_error("EAP: timeout waiting for Request from peer"); + auth_withpeer_fail(pcb, PPP_EAP); + pcb->eap.es_client.ea_state = eapBadAuth; +} + +/* + * eap_authwithpeer - Authenticate to our peer (behave as client). + * + * Start client state and wait for requests. This is called only + * after eap_lowerup. + */ +void eap_authwithpeer(ppp_pcb *pcb, const char *localname) { + + if(NULL == localname) + return; + + /* Save the peer name we're given */ + pcb->eap.es_client.ea_name = localname; + pcb->eap.es_client.ea_namelen = strlen(localname); + + pcb->eap.es_client.ea_state = eapListen; + + /* + * Start a timer so that if the other end just goes + * silent, we don't sit here waiting forever. + */ + if (pcb->settings.eap_req_time > 0) + TIMEOUT(eap_client_timeout, pcb, + pcb->settings.eap_req_time); +} + +#if PPP_SERVER +/* + * Format a standard EAP Failure message and send it to the peer. + * (Server operation) + */ +static void eap_send_failure(ppp_pcb *pcb) { + struct pbuf *p; + u_char *outp; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_FAILURE, outp); + pcb->eap.es_server.ea_id++; + PUTCHAR(pcb->eap.es_server.ea_id, outp); + PUTSHORT(EAP_HEADERLEN, outp); + + ppp_write(pcb, p); + + pcb->eap.es_server.ea_state = eapBadAuth; + auth_peer_fail(pcb, PPP_EAP); +} + +/* + * Format a standard EAP Success message and send it to the peer. + * (Server operation) + */ +static void eap_send_success(ppp_pcb *pcb) { + struct pbuf *p; + u_char *outp; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_SUCCESS, outp); + pcb->eap.es_server.ea_id++; + PUTCHAR(pcb->eap.es_server.ea_id, outp); + PUTSHORT(EAP_HEADERLEN, outp); + + ppp_write(pcb, p); + + auth_peer_success(pcb, PPP_EAP, 0, + pcb->eap.es_server.ea_peer, pcb->eap.es_server.ea_peerlen); +} +#endif /* PPP_SERVER */ + +#ifdef USE_SRP +/* + * Set DES key according to pseudonym-generating secret and current + * date. + */ +static bool +pncrypt_setkey(int timeoffs) +{ + struct tm *tp; + char tbuf[9]; + SHA1_CTX ctxt; + u_char dig[SHA_DIGESTSIZE]; + time_t reftime; + + if (pn_secret == NULL) + return (0); + reftime = time(NULL) + timeoffs; + tp = localtime(&reftime); + SHA1Init(&ctxt); + SHA1Update(&ctxt, pn_secret, strlen(pn_secret)); + strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp); + SHA1Update(&ctxt, tbuf, strlen(tbuf)); + SHA1Final(dig, &ctxt); + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + return (DesSetkey(dig)); +} + +static char base64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +struct b64state { + u32_t bs_bits; + int bs_offs; +}; + +static int +b64enc(bs, inp, inlen, outp) +struct b64state *bs; +u_char *inp; +int inlen; +u_char *outp; +{ + int outlen = 0; + + while (inlen > 0) { + bs->bs_bits = (bs->bs_bits << 8) | *inp++; + inlen--; + bs->bs_offs += 8; + if (bs->bs_offs >= 24) { + *outp++ = base64[(bs->bs_bits >> 18) & 0x3F]; + *outp++ = base64[(bs->bs_bits >> 12) & 0x3F]; + *outp++ = base64[(bs->bs_bits >> 6) & 0x3F]; + *outp++ = base64[bs->bs_bits & 0x3F]; + outlen += 4; + bs->bs_offs = 0; + bs->bs_bits = 0; + } + } + return (outlen); +} + +static int +b64flush(bs, outp) +struct b64state *bs; +u_char *outp; +{ + int outlen = 0; + + if (bs->bs_offs == 8) { + *outp++ = base64[(bs->bs_bits >> 2) & 0x3F]; + *outp++ = base64[(bs->bs_bits << 4) & 0x3F]; + outlen = 2; + } else if (bs->bs_offs == 16) { + *outp++ = base64[(bs->bs_bits >> 10) & 0x3F]; + *outp++ = base64[(bs->bs_bits >> 4) & 0x3F]; + *outp++ = base64[(bs->bs_bits << 2) & 0x3F]; + outlen = 3; + } + bs->bs_offs = 0; + bs->bs_bits = 0; + return (outlen); +} + +static int +b64dec(bs, inp, inlen, outp) +struct b64state *bs; +u_char *inp; +int inlen; +u_char *outp; +{ + int outlen = 0; + char *cp; + + while (inlen > 0) { + if ((cp = strchr(base64, *inp++)) == NULL) + break; + bs->bs_bits = (bs->bs_bits << 6) | (cp - base64); + inlen--; + bs->bs_offs += 6; + if (bs->bs_offs >= 8) { + *outp++ = bs->bs_bits >> (bs->bs_offs - 8); + outlen++; + bs->bs_offs -= 8; + } + } + return (outlen); +} +#endif /* USE_SRP */ + +#if PPP_SERVER +/* + * Assume that current waiting server state is complete and figure + * next state to use based on available authentication data. 'status' + * indicates if there was an error in handling the last query. It is + * 0 for success and non-zero for failure. + */ +static void eap_figure_next_state(ppp_pcb *pcb, int status) { +#ifdef USE_SRP + unsigned char secbuf[MAXSECRETLEN], clear[8], *sp, *dp; + struct t_pw tpw; + struct t_confent *tce, mytce; + char *cp, *cp2; + struct t_server *ts; + int id, i, plen, toffs; + u_char vals[2]; + struct b64state bs; +#endif /* USE_SRP */ + + pcb->settings.eap_timeout_time = pcb->eap.es_savedtime; + switch (pcb->eap.es_server.ea_state) { + case eapBadAuth: + return; + + case eapIdentify: +#ifdef USE_SRP + /* Discard any previous session. */ + ts = (struct t_server *)pcb->eap.es_server.ea_session; + if (ts != NULL) { + t_serverclose(ts); + pcb->eap.es_server.ea_session = NULL; + pcb->eap.es_server.ea_skey = NULL; + } +#endif /* USE_SRP */ + if (status != 0) { + pcb->eap.es_server.ea_state = eapBadAuth; + break; + } +#ifdef USE_SRP + /* If we've got a pseudonym, try to decode to real name. */ + if (pcb->eap.es_server.ea_peerlen > SRP_PSEUDO_LEN && + strncmp(pcb->eap.es_server.ea_peer, SRP_PSEUDO_ID, + SRP_PSEUDO_LEN) == 0 && + (pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 < + sizeof (secbuf)) { + BZERO(&bs, sizeof (bs)); + plen = b64dec(&bs, + pcb->eap.es_server.ea_peer + SRP_PSEUDO_LEN, + pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN, + secbuf); + toffs = 0; + for (i = 0; i < 5; i++) { + pncrypt_setkey(toffs); + toffs -= 86400; + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + if (!DesDecrypt(secbuf, clear)) { + ppp_dbglog("no DES here; cannot decode " + "pseudonym"); + return; + } + id = *(unsigned char *)clear; + if (id + 1 <= plen && id + 9 > plen) + break; + } + if (plen % 8 == 0 && i < 5) { + /* + * Note that this is always shorter than the + * original stored string, so there's no need + * to realloc. + */ + if ((i = plen = *(unsigned char *)clear) > 7) + i = 7; + pcb->eap.es_server.ea_peerlen = plen; + dp = (unsigned char *)pcb->eap.es_server.ea_peer; + MEMCPY(dp, clear + 1, i); + plen -= i; + dp += i; + sp = secbuf + 8; + while (plen > 0) { + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + (void) DesDecrypt(sp, dp); + sp += 8; + dp += 8; + plen -= 8; + } + pcb->eap.es_server.ea_peer[ + pcb->eap.es_server.ea_peerlen] = '\0'; + ppp_dbglog("decoded pseudonym to \"%.*q\"", + pcb->eap.es_server.ea_peerlen, + pcb->eap.es_server.ea_peer); + } else { + ppp_dbglog("failed to decode real name"); + /* Stay in eapIdentfy state; requery */ + break; + } + } + /* Look up user in secrets database. */ + if (get_srp_secret(pcb->eap.es_unit, pcb->eap.es_server.ea_peer, + pcb->eap.es_server.ea_name, (char *)secbuf, 1) != 0) { + /* Set up default in case SRP entry is bad */ + pcb->eap.es_server.ea_state = eapMD5Chall; + /* Get t_confent based on index in srp-secrets */ + id = strtol((char *)secbuf, &cp, 10); + if (*cp++ != ':' || id < 0) + break; + if (id == 0) { + mytce.index = 0; + mytce.modulus.data = (u_char *)wkmodulus; + mytce.modulus.len = sizeof (wkmodulus); + mytce.generator.data = (u_char *)"\002"; + mytce.generator.len = 1; + tce = &mytce; + } else if ((tce = gettcid(id)) != NULL) { + /* + * Client will have to verify this modulus/ + * generator combination, and that will take + * a while. Lengthen the timeout here. + */ + if (pcb->settings.eap_timeout_time > 0 && + pcb->settings.eap_timeout_time < 30) + pcb->settings.eap_timeout_time = 30; + } else { + break; + } + if ((cp2 = strchr(cp, ':')) == NULL) + break; + *cp2++ = '\0'; + tpw.pebuf.name = pcb->eap.es_server.ea_peer; + tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf, + cp); + tpw.pebuf.password.data = tpw.pwbuf; + tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf, + cp2); + tpw.pebuf.salt.data = tpw.saltbuf; + if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL) + break; + pcb->eap.es_server.ea_session = (void *)ts; + pcb->eap.es_server.ea_state = eapSRP1; + vals[0] = pcb->eap.es_server.ea_id + 1; + vals[1] = EAPT_SRP; + t_serveraddexdata(ts, vals, 2); + /* Generate B; must call before t_servergetkey() */ + t_servergenexp(ts); + break; + } +#endif /* USE_SRP */ + pcb->eap.es_server.ea_state = eapMD5Chall; + break; + + case eapSRP1: +#ifdef USE_SRP + ts = (struct t_server *)pcb->eap.es_server.ea_session; + if (ts != NULL && status != 0) { + t_serverclose(ts); + pcb->eap.es_server.ea_session = NULL; + pcb->eap.es_server.ea_skey = NULL; + } +#endif /* USE_SRP */ + if (status == 1) { + pcb->eap.es_server.ea_state = eapMD5Chall; + } else if (status != 0 || pcb->eap.es_server.ea_session == NULL) { + pcb->eap.es_server.ea_state = eapBadAuth; + } else { + pcb->eap.es_server.ea_state = eapSRP2; + } + break; + + case eapSRP2: +#ifdef USE_SRP + ts = (struct t_server *)pcb->eap.es_server.ea_session; + if (ts != NULL && status != 0) { + t_serverclose(ts); + pcb->eap.es_server.ea_session = NULL; + pcb->eap.es_server.ea_skey = NULL; + } +#endif /* USE_SRP */ + if (status != 0 || pcb->eap.es_server.ea_session == NULL) { + pcb->eap.es_server.ea_state = eapBadAuth; + } else { + pcb->eap.es_server.ea_state = eapSRP3; + } + break; + + case eapSRP3: + case eapSRP4: +#ifdef USE_SRP + ts = (struct t_server *)pcb->eap.es_server.ea_session; + if (ts != NULL && status != 0) { + t_serverclose(ts); + pcb->eap.es_server.ea_session = NULL; + pcb->eap.es_server.ea_skey = NULL; + } +#endif /* USE_SRP */ + if (status != 0 || pcb->eap.es_server.ea_session == NULL) { + pcb->eap.es_server.ea_state = eapBadAuth; + } else { + pcb->eap.es_server.ea_state = eapOpen; + } + break; + + case eapMD5Chall: + if (status != 0) { + pcb->eap.es_server.ea_state = eapBadAuth; + } else { + pcb->eap.es_server.ea_state = eapOpen; + } + break; + + default: + pcb->eap.es_server.ea_state = eapBadAuth; + break; + } + if (pcb->eap.es_server.ea_state == eapBadAuth) + eap_send_failure(pcb); +} + +/* + * Format an EAP Request message and send it to the peer. Message + * type depends on current state. (Server operation) + */ +static void eap_send_request(ppp_pcb *pcb) { + struct pbuf *p; + u_char *outp; + u_char *lenloc; + int outlen; + int len; + const char *str; +#ifdef USE_SRP + struct t_server *ts; + u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp; + int i, j; + struct b64state b64; + SHA1_CTX ctxt; +#endif /* USE_SRP */ + + /* Handle both initial auth and restart */ + if (pcb->eap.es_server.ea_state < eapIdentify && + pcb->eap.es_server.ea_state != eapInitial) { + pcb->eap.es_server.ea_state = eapIdentify; +#if PPP_REMOTENAME + if (pcb->settings.explicit_remote && pcb->remote_name) { + /* + * If we already know the peer's + * unauthenticated name, then there's no + * reason to ask. Go to next state instead. + */ + int len = (int)strlen(pcb->remote_name); + if (len > MAXNAMELEN) { + len = MAXNAMELEN; + } + MEMCPY(pcb->eap.es_server.ea_peer, pcb->remote_name, len); + pcb->eap.es_server.ea_peer[len] = '\0'; + pcb->eap.es_server.ea_peerlen = len; + eap_figure_next_state(pcb, 0); + } +#endif /* PPP_REMOTENAME */ + } + + if (pcb->settings.eap_max_transmits > 0 && + pcb->eap.es_server.ea_requests >= pcb->settings.eap_max_transmits) { + if (pcb->eap.es_server.ea_responses > 0) + ppp_error("EAP: too many Requests sent"); + else + ppp_error("EAP: no response to Requests"); + eap_send_failure(pcb); + return; + } + + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_REQUEST, outp); + PUTCHAR(pcb->eap.es_server.ea_id, outp); + lenloc = outp; + INCPTR(2, outp); + + switch (pcb->eap.es_server.ea_state) { + case eapIdentify: + PUTCHAR(EAPT_IDENTITY, outp); + str = "Name"; + len = strlen(str); + MEMCPY(outp, str, len); + INCPTR(len, outp); + break; + + case eapMD5Chall: + PUTCHAR(EAPT_MD5CHAP, outp); + /* + * pick a random challenge length between + * EAP_MIN_CHALLENGE_LENGTH and EAP_MAX_CHALLENGE_LENGTH + */ + pcb->eap.es_challen = EAP_MIN_CHALLENGE_LENGTH + + magic_pow(EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH); + PUTCHAR(pcb->eap.es_challen, outp); + magic_random_bytes(pcb->eap.es_challenge, pcb->eap.es_challen); + MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen); + INCPTR(pcb->eap.es_challen, outp); + MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen); + INCPTR(pcb->eap.es_server.ea_namelen, outp); + break; + +#ifdef USE_SRP + case eapSRP1: + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_CHALLENGE, outp); + + PUTCHAR(pcb->eap.es_server.ea_namelen, outp); + MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen); + INCPTR(pcb->eap.es_server.ea_namelen, outp); + + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + PUTCHAR(ts->s.len, outp); + MEMCPY(outp, ts->s.data, ts->s.len); + INCPTR(ts->s.len, outp); + + if (ts->g.len == 1 && ts->g.data[0] == 2) { + PUTCHAR(0, outp); + } else { + PUTCHAR(ts->g.len, outp); + MEMCPY(outp, ts->g.data, ts->g.len); + INCPTR(ts->g.len, outp); + } + + if (ts->n.len != sizeof (wkmodulus) || + BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) { + MEMCPY(outp, ts->n.data, ts->n.len); + INCPTR(ts->n.len, outp); + } + break; + + case eapSRP2: + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_SKEY, outp); + + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + MEMCPY(outp, ts->B.data, ts->B.len); + INCPTR(ts->B.len, outp); + break; + + case eapSRP3: + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_SVALIDATOR, outp); + PUTLONG(SRPVAL_EBIT, outp); + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + MEMCPY(outp, t_serverresponse(ts), SHA_DIGESTSIZE); + INCPTR(SHA_DIGESTSIZE, outp); + + if (pncrypt_setkey(0)) { + /* Generate pseudonym */ + optr = outp; + cp = (unsigned char *)pcb->eap.es_server.ea_peer; + if ((j = i = pcb->eap.es_server.ea_peerlen) > 7) + j = 7; + clear[0] = i; + MEMCPY(clear + 1, cp, j); + i -= j; + cp += j; + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + if (!DesEncrypt(clear, cipher)) { + ppp_dbglog("no DES here; not generating pseudonym"); + break; + } + BZERO(&b64, sizeof (b64)); + outp++; /* space for pseudonym length */ + outp += b64enc(&b64, cipher, 8, outp); + while (i >= 8) { + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + (void) DesEncrypt(cp, cipher); + outp += b64enc(&b64, cipher, 8, outp); + cp += 8; + i -= 8; + } + if (i > 0) { + MEMCPY(clear, cp, i); + cp += i; + magic_random_bytes(cp, 8-i); + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + (void) DesEncrypt(clear, cipher); + outp += b64enc(&b64, cipher, 8, outp); + } + outp += b64flush(&b64, outp); + + /* Set length and pad out to next 20 octet boundary */ + i = outp - optr - 1; + *optr = i; + i %= SHA_DIGESTSIZE; + if (i != 0) { + magic_random_bytes(outp, SHA_DIGESTSIZE-i); + INCPTR(SHA_DIGESTSIZE-i, outp); + } + + /* Obscure the pseudonym with SHA1 hash */ + SHA1Init(&ctxt); + SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1); + SHA1Update(&ctxt, pcb->eap.es_server.ea_skey, + SESSION_KEY_LEN); + SHA1Update(&ctxt, pcb->eap.es_server.ea_peer, + pcb->eap.es_server.ea_peerlen); + while (optr < outp) { + SHA1Final(dig, &ctxt); + cp = dig; + while (cp < dig + SHA_DIGESTSIZE) + *optr++ ^= *cp++; + SHA1Init(&ctxt); + SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1); + SHA1Update(&ctxt, pcb->eap.es_server.ea_skey, + SESSION_KEY_LEN); + SHA1Update(&ctxt, optr - SHA_DIGESTSIZE, + SHA_DIGESTSIZE); + } + } + break; + + case eapSRP4: + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_LWRECHALLENGE, outp); + pcb->eap.es_challen = EAP_MIN_CHALLENGE_LENGTH + + magic_pow(EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH); + magic_random_bytes(pcb->eap.es_challenge, pcb->eap.es_challen); + MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen); + INCPTR(pcb->eap.es_challen, outp); + break; +#endif /* USE_SRP */ + + default: + return; + } + + outlen = (outp - (unsigned char*)p->payload) - PPP_HDRLEN; + PUTSHORT(outlen, lenloc); + + pbuf_realloc(p, outlen + PPP_HDRLEN); + ppp_write(pcb, p); + + pcb->eap.es_server.ea_requests++; + + if (pcb->settings.eap_timeout_time > 0) + TIMEOUT(eap_server_timeout, pcb, pcb->settings.eap_timeout_time); +} + +/* + * eap_authpeer - Authenticate our peer (behave as server). + * + * Start server state and send first request. This is called only + * after eap_lowerup. + */ +void eap_authpeer(ppp_pcb *pcb, const char *localname) { + + /* Save the name we're given. */ + pcb->eap.es_server.ea_name = localname; + pcb->eap.es_server.ea_namelen = strlen(localname); + + pcb->eap.es_savedtime = pcb->settings.eap_timeout_time; + + /* Lower layer up yet? */ + if (pcb->eap.es_server.ea_state == eapInitial || + pcb->eap.es_server.ea_state == eapPending) { + pcb->eap.es_server.ea_state = eapPending; + return; + } + + pcb->eap.es_server.ea_state = eapPending; + + /* ID number not updated here intentionally; hashed into M1 */ + eap_send_request(pcb); +} + +/* + * eap_server_timeout - Retransmission timer for sending Requests + * expired. + */ +static void eap_server_timeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (!eap_server_active(pcb)) + return; + + /* EAP ID number must not change on timeout. */ + eap_send_request(pcb); +} + +/* + * When it's time to send rechallenge the peer, this timeout is + * called. Once the rechallenge is successful, the response handler + * will restart the timer. If it fails, then the link is dropped. + */ +static void eap_rechallenge(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (pcb->eap.es_server.ea_state != eapOpen && + pcb->eap.es_server.ea_state != eapSRP4) + return; + + pcb->eap.es_server.ea_requests = 0; + pcb->eap.es_server.ea_state = eapIdentify; + eap_figure_next_state(pcb, 0); + pcb->eap.es_server.ea_id++; + eap_send_request(pcb); +} + +static void srp_lwrechallenge(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (pcb->eap.es_server.ea_state != eapOpen || + pcb->eap.es_server.ea_type != EAPT_SRP) + return; + + pcb->eap.es_server.ea_requests = 0; + pcb->eap.es_server.ea_state = eapSRP4; + pcb->eap.es_server.ea_id++; + eap_send_request(pcb); +} +#endif /* PPP_SERVER */ + +/* + * eap_lowerup - The lower layer is now up. + * + * This is called before either eap_authpeer or eap_authwithpeer. See + * link_established() in auth.c. All that's necessary here is to + * return to closed state so that those two routines will do the right + * thing. + */ +static void eap_lowerup(ppp_pcb *pcb) { + pcb->eap.es_client.ea_state = eapClosed; +#if PPP_SERVER + pcb->eap.es_server.ea_state = eapClosed; +#endif /* PPP_SERVER */ +} + +/* + * eap_lowerdown - The lower layer is now down. + * + * Cancel all timeouts and return to initial state. + */ +static void eap_lowerdown(ppp_pcb *pcb) { + + if (eap_client_active(pcb) && pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + } +#if PPP_SERVER + if (eap_server_active(pcb)) { + if (pcb->settings.eap_timeout_time > 0) { + UNTIMEOUT(eap_server_timeout, pcb); + } + } else { + if ((pcb->eap.es_server.ea_state == eapOpen || + pcb->eap.es_server.ea_state == eapSRP4) && + pcb->eap.es_rechallenge > 0) { + UNTIMEOUT(eap_rechallenge, (void *)pcb); + } + if (pcb->eap.es_server.ea_state == eapOpen && + pcb->eap.es_lwrechallenge > 0) { + UNTIMEOUT(srp_lwrechallenge, (void *)pcb); + } + } + + pcb->eap.es_client.ea_state = pcb->eap.es_server.ea_state = eapInitial; + pcb->eap.es_client.ea_requests = pcb->eap.es_server.ea_requests = 0; +#endif /* PPP_SERVER */ +} + +/* + * eap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. If it does, it represents authentication + * failure. + */ +static void eap_protrej(ppp_pcb *pcb) { + + if (eap_client_active(pcb)) { + ppp_error("EAP authentication failed due to Protocol-Reject"); + auth_withpeer_fail(pcb, PPP_EAP); + } +#if PPP_SERVER + if (eap_server_active(pcb)) { + ppp_error("EAP authentication of peer failed on Protocol-Reject"); + auth_peer_fail(pcb, PPP_EAP); + } +#endif /* PPP_SERVER */ + eap_lowerdown(pcb); +} + +/* + * Format and send a regular EAP Response message. + */ +static void eap_send_response(ppp_pcb *pcb, u_char id, u_char typenum, const u_char *str, int lenstr) { + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(typenum, outp); + if (lenstr > 0) { + MEMCPY(outp, str, lenstr); + } + + ppp_write(pcb, p); +} + +/* + * Format and send an MD5-Challenge EAP Response message. + */ +static void eap_chap_response(ppp_pcb *pcb, u_char id, u_char *hash, const char *name, int namelen) { + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE + + namelen; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_MD5CHAP, outp); + PUTCHAR(MD5_SIGNATURE_SIZE, outp); + MEMCPY(outp, hash, MD5_SIGNATURE_SIZE); + INCPTR(MD5_SIGNATURE_SIZE, outp); + if (namelen > 0) { + MEMCPY(outp, name, namelen); + } + + ppp_write(pcb, p); +} + +#ifdef USE_SRP +/* + * Format and send a SRP EAP Response message. + */ +static void +eap_srp_response(esp, id, subtypenum, str, lenstr) +eap_state *esp; +u_char id; +u_char subtypenum; +u_char *str; +int lenstr; +{ + ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit]; + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(subtypenum, outp); + if (lenstr > 0) { + MEMCPY(outp, str, lenstr); + } + + ppp_write(pcb, p); +} + +/* + * Format and send a SRP EAP Client Validator Response message. + */ +static void +eap_srpval_response(esp, id, flags, str) +eap_state *esp; +u_char id; +u32_t flags; +u_char *str; +{ + ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit]; + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u32_t) + + SHA_DIGESTSIZE; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_CVALIDATOR, outp); + PUTLONG(flags, outp); + MEMCPY(outp, str, SHA_DIGESTSIZE); + + ppp_write(pcb, p); +} +#endif /* USE_SRP */ + +static void eap_send_nak(ppp_pcb *pcb, u_char id, u_char type) { + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char); + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_NAK, outp); + PUTCHAR(type, outp); + + ppp_write(pcb, p); +} + +#ifdef USE_SRP +static char * +name_of_pn_file() +{ + char *user, *path, *file; + struct passwd *pw; + size_t pl; + static bool pnlogged = 0; + + pw = getpwuid(getuid()); + if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) { + errno = EINVAL; + return (NULL); + } + file = _PATH_PSEUDONYM; + pl = strlen(user) + strlen(file) + 2; + path = malloc(pl); + if (path == NULL) + return (NULL); + (void) slprintf(path, pl, "%s/%s", user, file); + if (!pnlogged) { + ppp_dbglog("pseudonym file: %s", path); + pnlogged = 1; + } + return (path); +} + +static int +open_pn_file(modebits) +mode_t modebits; +{ + char *path; + int fd, err; + + if ((path = name_of_pn_file()) == NULL) + return (-1); + fd = open(path, modebits, S_IRUSR | S_IWUSR); + err = errno; + free(path); + errno = err; + return (fd); +} + +static void +remove_pn_file() +{ + char *path; + + if ((path = name_of_pn_file()) != NULL) { + (void) unlink(path); + (void) free(path); + } +} + +static void +write_pseudonym(esp, inp, len, id) +eap_state *esp; +u_char *inp; +int len, id; +{ + u_char val; + u_char *datp, *digp; + SHA1_CTX ctxt; + u_char dig[SHA_DIGESTSIZE]; + int dsize, fd, olen = len; + + /* + * Do the decoding by working backwards. This eliminates the need + * to save the decoded output in a separate buffer. + */ + val = id; + while (len > 0) { + if ((dsize = len % SHA_DIGESTSIZE) == 0) + dsize = SHA_DIGESTSIZE; + len -= dsize; + datp = inp + len; + SHA1Init(&ctxt); + SHA1Update(&ctxt, &val, 1); + SHA1Update(&ctxt, pcb->eap.es_client.ea_skey, SESSION_KEY_LEN); + if (len > 0) { + SHA1Update(&ctxt, datp, SHA_DIGESTSIZE); + } else { + SHA1Update(&ctxt, pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_namelen); + } + SHA1Final(dig, &ctxt); + for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++) + *datp++ ^= *digp; + } + + /* Now check that the result is sane */ + if (olen <= 0 || *inp + 1 > olen) { + ppp_dbglog("EAP: decoded pseudonym is unusable <%.*B>", olen, inp); + return; + } + + /* Save it away */ + fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + ppp_dbglog("EAP: error saving pseudonym: %m"); + return; + } + len = write(fd, inp + 1, *inp); + if (close(fd) != -1 && len == *inp) { + ppp_dbglog("EAP: saved pseudonym"); + pcb->eap.es_usedpseudo = 0; + } else { + ppp_dbglog("EAP: failed to save pseudonym"); + remove_pn_file(); + } +} +#endif /* USE_SRP */ + +/* + * eap_request - Receive EAP Request message (client mode). + */ +static void eap_request(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char typenum; + u_char vallen; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[MAXNAMELEN]; + lwip_md5_context mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; +#ifdef USE_SRP + struct t_client *tc; + struct t_num sval, gval, Nval, *Ap, Bval; + u_char vals[2]; + SHA1_CTX ctxt; + u_char dig[SHA_DIGESTSIZE]; + int fd; +#endif /* USE_SRP */ + + /* + * Note: we update es_client.ea_id *only if* a Response + * message is being generated. Otherwise, we leave it the + * same for duplicate detection purposes. + */ + + pcb->eap.es_client.ea_requests++; + if (pcb->settings.eap_allow_req != 0 && + pcb->eap.es_client.ea_requests > pcb->settings.eap_allow_req) { + ppp_info("EAP: received too many Request messages"); + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + } + auth_withpeer_fail(pcb, PPP_EAP); + return; + } + + if (len <= 0) { + ppp_error("EAP: empty Request message discarded"); + return; + } + + GETCHAR(typenum, inp); + len--; + + switch (typenum) { + case EAPT_IDENTITY: + if (len > 0) + ppp_info("EAP: Identity prompt \"%.*q\"", len, inp); +#ifdef USE_SRP + if (pcb->eap.es_usepseudo && + (pcb->eap.es_usedpseudo == 0 || + (pcb->eap.es_usedpseudo == 1 && + id == pcb->eap.es_client.ea_id))) { + pcb->eap.es_usedpseudo = 1; + /* Try to get a pseudonym */ + if ((fd = open_pn_file(O_RDONLY)) >= 0) { + strcpy(rhostname, SRP_PSEUDO_ID); + len = read(fd, rhostname + SRP_PSEUDO_LEN, + sizeof (rhostname) - SRP_PSEUDO_LEN); + /* XXX NAI unsupported */ + if (len > 0) { + eap_send_response(pcb, id, typenum, + rhostname, len + SRP_PSEUDO_LEN); + } + (void) close(fd); + if (len > 0) + break; + } + } + /* Stop using pseudonym now. */ + if (pcb->eap.es_usepseudo && pcb->eap.es_usedpseudo != 2) { + remove_pn_file(); + pcb->eap.es_usedpseudo = 2; + } +#endif /* USE_SRP */ + eap_send_response(pcb, id, typenum, (const u_char*)pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_namelen); + break; + + case EAPT_NOTIFICATION: + if (len > 0) + ppp_info("EAP: Notification \"%.*q\"", len, inp); + eap_send_response(pcb, id, typenum, NULL, 0); + break; + + case EAPT_NAK: + /* + * Avoid the temptation to send Response Nak in reply + * to Request Nak here. It can only lead to trouble. + */ + ppp_warn("EAP: unexpected Nak in Request; ignored"); + /* Return because we're waiting for something real. */ + return; + + case EAPT_MD5CHAP: + if (len < 1) { + ppp_error("EAP: received MD5-Challenge with no data"); + /* Bogus request; wait for something real. */ + return; + } + GETCHAR(vallen, inp); + len--; + if (vallen < 8 || vallen > len) { + ppp_error("EAP: MD5-Challenge with bad length %d (8..%d)", + vallen, len); + /* Try something better. */ + eap_send_nak(pcb, id, EAPT_SRP); + break; + } + + /* Not so likely to happen. */ + if (vallen >= len + sizeof (rhostname)) { + ppp_dbglog("EAP: trimming really long peer name down"); + MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1); + rhostname[sizeof (rhostname) - 1] = '\0'; + } else { + MEMCPY(rhostname, inp + vallen, len - vallen); + rhostname[len - vallen] = '\0'; + } + +#if PPP_REMOTENAME + /* In case the remote doesn't give us his name. */ + if (pcb->settings.explicit_remote || + (pcb->settings.remote_name[0] != '\0' && vallen == len)) + strlcpy(rhostname, pcb->settings.remote_name, sizeof (rhostname)); +#endif /* PPP_REMOTENAME */ + + /* + * Get the secret for authenticating ourselves with + * the specified host. + */ + if (!get_secret(pcb, pcb->eap.es_client.ea_name, + rhostname, secret, &secret_len, 0)) { + ppp_dbglog("EAP: no MD5 secret for auth to %q", rhostname); + eap_send_nak(pcb, id, EAPT_SRP); + break; + } + lwip_md5_init(&mdContext); + lwip_md5_starts(&mdContext); + typenum = id; + lwip_md5_update(&mdContext, &typenum, 1); + lwip_md5_update(&mdContext, (u_char *)secret, secret_len); + BZERO(secret, sizeof (secret)); + lwip_md5_update(&mdContext, inp, vallen); + lwip_md5_finish(&mdContext, hash); + lwip_md5_free(&mdContext); + eap_chap_response(pcb, id, hash, pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_namelen); + break; + +#ifdef USE_SRP + case EAPT_SRP: + if (len < 1) { + ppp_error("EAP: received empty SRP Request"); + /* Bogus request; wait for something real. */ + return; + } + + /* Get subtype */ + GETCHAR(vallen, inp); + len--; + switch (vallen) { + case EAPSRP_CHALLENGE: + tc = NULL; + if (pcb->eap.es_client.ea_session != NULL) { + tc = (struct t_client *)pcb->eap.es_client. + ea_session; + /* + * If this is a new challenge, then start + * over with a new client session context. + * Otherwise, just resend last response. + */ + if (id != pcb->eap.es_client.ea_id) { + t_clientclose(tc); + pcb->eap.es_client.ea_session = NULL; + tc = NULL; + } + } + /* No session key just yet */ + pcb->eap.es_client.ea_skey = NULL; + if (tc == NULL) { + int rhostnamelen; + + GETCHAR(vallen, inp); + len--; + if (vallen >= len) { + ppp_error("EAP: badly-formed SRP Challenge" + " (name)"); + /* Ignore badly-formed messages */ + return; + } + MEMCPY(rhostname, inp, vallen); + rhostname[vallen] = '\0'; + INCPTR(vallen, inp); + len -= vallen; + + /* + * In case the remote doesn't give us his name, + * use configured name. + */ + if (explicit_remote || + (remote_name[0] != '\0' && vallen == 0)) { + strlcpy(rhostname, remote_name, + sizeof (rhostname)); + } + + rhostnamelen = (int)strlen(rhostname); + if (rhostnamelen > MAXNAMELEN) { + rhostnamelen = MAXNAMELEN; + } + MEMCPY(pcb->eap.es_client.ea_peer, rhostname, rhostnamelen); + pcb->eap.es_client.ea_peer[rhostnamelen] = '\0'; + pcb->eap.es_client.ea_peerlen = rhostnamelen; + + GETCHAR(vallen, inp); + len--; + if (vallen >= len) { + ppp_error("EAP: badly-formed SRP Challenge" + " (s)"); + /* Ignore badly-formed messages */ + return; + } + sval.data = inp; + sval.len = vallen; + INCPTR(vallen, inp); + len -= vallen; + + GETCHAR(vallen, inp); + len--; + if (vallen > len) { + ppp_error("EAP: badly-formed SRP Challenge" + " (g)"); + /* Ignore badly-formed messages */ + return; + } + /* If no generator present, then use value 2 */ + if (vallen == 0) { + gval.data = (u_char *)"\002"; + gval.len = 1; + } else { + gval.data = inp; + gval.len = vallen; + } + INCPTR(vallen, inp); + len -= vallen; + + /* + * If no modulus present, then use well-known + * value. + */ + if (len == 0) { + Nval.data = (u_char *)wkmodulus; + Nval.len = sizeof (wkmodulus); + } else { + Nval.data = inp; + Nval.len = len; + } + tc = t_clientopen(pcb->eap.es_client.ea_name, + &Nval, &gval, &sval); + if (tc == NULL) { + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + pcb->eap.es_client.ea_session = (void *)tc; + + /* Add Challenge ID & type to verifier */ + vals[0] = id; + vals[1] = EAPT_SRP; + t_clientaddexdata(tc, vals, 2); + } + Ap = t_clientgenexp(tc); + eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data, + Ap->len); + break; + + case EAPSRP_SKEY: + tc = (struct t_client *)pcb->eap.es_client.ea_session; + if (tc == NULL) { + ppp_warn("EAP: peer sent Subtype 2 without 1"); + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + if (pcb->eap.es_client.ea_skey != NULL) { + /* + * ID number should not change here. Warn + * if it does (but otherwise ignore). + */ + if (id != pcb->eap.es_client.ea_id) { + ppp_warn("EAP: ID changed from %d to %d " + "in SRP Subtype 2 rexmit", + pcb->eap.es_client.ea_id, id); + } + } else { + if (get_srp_secret(pcb->eap.es_unit, + pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_peer, secret, 0) == 0) { + /* + * Can't work with this peer because + * the secret is missing. Just give + * up. + */ + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + Bval.data = inp; + Bval.len = len; + t_clientpasswd(tc, secret); + BZERO(secret, sizeof (secret)); + pcb->eap.es_client.ea_skey = + t_clientgetkey(tc, &Bval); + if (pcb->eap.es_client.ea_skey == NULL) { + /* Server is rogue; stop now */ + ppp_error("EAP: SRP server is rogue"); + goto client_failure; + } + } + eap_srpval_response(esp, id, SRPVAL_EBIT, + t_clientresponse(tc)); + break; + + case EAPSRP_SVALIDATOR: + tc = (struct t_client *)pcb->eap.es_client.ea_session; + if (tc == NULL || pcb->eap.es_client.ea_skey == NULL) { + ppp_warn("EAP: peer sent Subtype 3 without 1/2"); + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + /* + * If we're already open, then this ought to be a + * duplicate. Otherwise, check that the server is + * who we think it is. + */ + if (pcb->eap.es_client.ea_state == eapOpen) { + if (id != pcb->eap.es_client.ea_id) { + ppp_warn("EAP: ID changed from %d to %d " + "in SRP Subtype 3 rexmit", + pcb->eap.es_client.ea_id, id); + } + } else { + len -= sizeof (u32_t) + SHA_DIGESTSIZE; + if (len < 0 || t_clientverify(tc, inp + + sizeof (u32_t)) != 0) { + ppp_error("EAP: SRP server verification " + "failed"); + goto client_failure; + } + GETLONG(pcb->eap.es_client.ea_keyflags, inp); + /* Save pseudonym if user wants it. */ + if (len > 0 && pcb->eap.es_usepseudo) { + INCPTR(SHA_DIGESTSIZE, inp); + write_pseudonym(esp, inp, len, id); + } + } + /* + * We've verified our peer. We're now mostly done, + * except for waiting on the regular EAP Success + * message. + */ + eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0); + break; + + case EAPSRP_LWRECHALLENGE: + if (len < 4) { + ppp_warn("EAP: malformed Lightweight rechallenge"); + return; + } + SHA1Init(&ctxt); + vals[0] = id; + SHA1Update(&ctxt, vals, 1); + SHA1Update(&ctxt, pcb->eap.es_client.ea_skey, + SESSION_KEY_LEN); + SHA1Update(&ctxt, inp, len); + SHA1Update(&ctxt, pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_namelen); + SHA1Final(dig, &ctxt); + eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig, + SHA_DIGESTSIZE); + break; + + default: + ppp_error("EAP: unknown SRP Subtype %d", vallen); + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + break; +#endif /* USE_SRP */ + + default: + ppp_info("EAP: unknown authentication type %d; Naking", typenum); + eap_send_nak(pcb, id, EAPT_SRP); + break; + } + + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + TIMEOUT(eap_client_timeout, pcb, + pcb->settings.eap_req_time); + } + return; + +#ifdef USE_SRP +client_failure: + pcb->eap.es_client.ea_state = eapBadAuth; + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, (void *)esp); + } + pcb->eap.es_client.ea_session = NULL; + t_clientclose(tc); + auth_withpeer_fail(pcb, PPP_EAP); +#endif /* USE_SRP */ +} + +#if PPP_SERVER +/* + * eap_response - Receive EAP Response message (server mode). + */ +static void eap_response(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char typenum; + u_char vallen; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[MAXNAMELEN]; + lwip_md5_context mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; +#ifdef USE_SRP + struct t_server *ts; + struct t_num A; + SHA1_CTX ctxt; + u_char dig[SHA_DIGESTSIZE]; +#endif /* USE_SRP */ + + if (pcb->eap.es_server.ea_id != id) { + ppp_dbglog("EAP: discarding Response %d; expected ID %d", id, + pcb->eap.es_server.ea_id); + return; + } + + pcb->eap.es_server.ea_responses++; + + if (len <= 0) { + ppp_error("EAP: empty Response message discarded"); + return; + } + + GETCHAR(typenum, inp); + len--; + + switch (typenum) { + case EAPT_IDENTITY: + if (pcb->eap.es_server.ea_state != eapIdentify) { + ppp_dbglog("EAP discarding unwanted Identify \"%.q\"", len, + inp); + break; + } + ppp_info("EAP: unauthenticated peer name \"%.*q\"", len, inp); + if (len > MAXNAMELEN) { + len = MAXNAMELEN; + } + MEMCPY(pcb->eap.es_server.ea_peer, inp, len); + pcb->eap.es_server.ea_peer[len] = '\0'; + pcb->eap.es_server.ea_peerlen = len; + eap_figure_next_state(pcb, 0); + break; + + case EAPT_NOTIFICATION: + ppp_dbglog("EAP unexpected Notification; response discarded"); + break; + + case EAPT_NAK: + if (len < 1) { + ppp_info("EAP: Nak Response with no suggested protocol"); + eap_figure_next_state(pcb, 1); + break; + } + + GETCHAR(vallen, inp); + len--; + + if ( +#if PPP_REMOTENAME + !pcb->explicit_remote && +#endif /* PPP_REMOTENAME */ + pcb->eap.es_server.ea_state == eapIdentify){ + /* Peer cannot Nak Identify Request */ + eap_figure_next_state(pcb, 1); + break; + } + + switch (vallen) { + case EAPT_SRP: + /* Run through SRP validator selection again. */ + pcb->eap.es_server.ea_state = eapIdentify; + eap_figure_next_state(pcb, 0); + break; + + case EAPT_MD5CHAP: + pcb->eap.es_server.ea_state = eapMD5Chall; + break; + + default: + ppp_dbglog("EAP: peer requesting unknown Type %d", vallen); + switch (pcb->eap.es_server.ea_state) { + case eapSRP1: + case eapSRP2: + case eapSRP3: + pcb->eap.es_server.ea_state = eapMD5Chall; + break; + case eapMD5Chall: + case eapSRP4: + pcb->eap.es_server.ea_state = eapIdentify; + eap_figure_next_state(pcb, 0); + break; + default: + break; + } + break; + } + break; + + case EAPT_MD5CHAP: + if (pcb->eap.es_server.ea_state != eapMD5Chall) { + ppp_error("EAP: unexpected MD5-Response"); + eap_figure_next_state(pcb, 1); + break; + } + if (len < 1) { + ppp_error("EAP: received MD5-Response with no data"); + eap_figure_next_state(pcb, 1); + break; + } + GETCHAR(vallen, inp); + len--; + if (vallen != 16 || vallen > len) { + ppp_error("EAP: MD5-Response with bad length %d", vallen); + eap_figure_next_state(pcb, 1); + break; + } + + /* Not so likely to happen. */ + if (vallen >= len + sizeof (rhostname)) { + ppp_dbglog("EAP: trimming really long peer name down"); + MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1); + rhostname[sizeof (rhostname) - 1] = '\0'; + } else { + MEMCPY(rhostname, inp + vallen, len - vallen); + rhostname[len - vallen] = '\0'; + } + +#if PPP_REMOTENAME + /* In case the remote doesn't give us his name. */ + if (explicit_remote || + (remote_name[0] != '\0' && vallen == len)) + strlcpy(rhostname, remote_name, sizeof (rhostname)); +#endif /* PPP_REMOTENAME */ + + /* + * Get the secret for authenticating the specified + * host. + */ + if (!get_secret(pcb, rhostname, + pcb->eap.es_server.ea_name, secret, &secret_len, 1)) { + ppp_dbglog("EAP: no MD5 secret for auth of %q", rhostname); + eap_send_failure(pcb); + break; + } + lwip_md5_init(&mdContext); + lwip_md5_starts(&mdContext); + lwip_md5_update(&mdContext, &pcb->eap.es_server.ea_id, 1); + lwip_md5_update(&mdContext, (u_char *)secret, secret_len); + BZERO(secret, sizeof (secret)); + lwip_md5_update(&mdContext, pcb->eap.es_challenge, pcb->eap.es_challen); + lwip_md5_finish(&mdContext, hash); + lwip_md5_free(&mdContext); + if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) { + eap_send_failure(pcb); + break; + } + pcb->eap.es_server.ea_type = EAPT_MD5CHAP; + eap_send_success(pcb); + eap_figure_next_state(pcb, 0); + if (pcb->eap.es_rechallenge != 0) + TIMEOUT(eap_rechallenge, pcb, pcb->eap.es_rechallenge); + break; + +#ifdef USE_SRP + case EAPT_SRP: + if (len < 1) { + ppp_error("EAP: empty SRP Response"); + eap_figure_next_state(pcb, 1); + break; + } + GETCHAR(typenum, inp); + len--; + switch (typenum) { + case EAPSRP_CKEY: + if (pcb->eap.es_server.ea_state != eapSRP1) { + ppp_error("EAP: unexpected SRP Subtype 1 Response"); + eap_figure_next_state(pcb, 1); + break; + } + A.data = inp; + A.len = len; + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + pcb->eap.es_server.ea_skey = t_servergetkey(ts, &A); + if (pcb->eap.es_server.ea_skey == NULL) { + /* Client's A value is bogus; terminate now */ + ppp_error("EAP: bogus A value from client"); + eap_send_failure(pcb); + } else { + eap_figure_next_state(pcb, 0); + } + break; + + case EAPSRP_CVALIDATOR: + if (pcb->eap.es_server.ea_state != eapSRP2) { + ppp_error("EAP: unexpected SRP Subtype 2 Response"); + eap_figure_next_state(pcb, 1); + break; + } + if (len < sizeof (u32_t) + SHA_DIGESTSIZE) { + ppp_error("EAP: M1 length %d < %d", len, + sizeof (u32_t) + SHA_DIGESTSIZE); + eap_figure_next_state(pcb, 1); + break; + } + GETLONG(pcb->eap.es_server.ea_keyflags, inp); + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + if (t_serververify(ts, inp)) { + ppp_info("EAP: unable to validate client identity"); + eap_send_failure(pcb); + break; + } + eap_figure_next_state(pcb, 0); + break; + + case EAPSRP_ACK: + if (pcb->eap.es_server.ea_state != eapSRP3) { + ppp_error("EAP: unexpected SRP Subtype 3 Response"); + eap_send_failure(esp); + break; + } + pcb->eap.es_server.ea_type = EAPT_SRP; + eap_send_success(pcb, esp); + eap_figure_next_state(pcb, 0); + if (pcb->eap.es_rechallenge != 0) + TIMEOUT(eap_rechallenge, pcb, + pcb->eap.es_rechallenge); + if (pcb->eap.es_lwrechallenge != 0) + TIMEOUT(srp_lwrechallenge, pcb, + pcb->eap.es_lwrechallenge); + break; + + case EAPSRP_LWRECHALLENGE: + if (pcb->eap.es_server.ea_state != eapSRP4) { + ppp_info("EAP: unexpected SRP Subtype 4 Response"); + return; + } + if (len != SHA_DIGESTSIZE) { + ppp_error("EAP: bad Lightweight rechallenge " + "response"); + return; + } + SHA1Init(&ctxt); + vallen = id; + SHA1Update(&ctxt, &vallen, 1); + SHA1Update(&ctxt, pcb->eap.es_server.ea_skey, + SESSION_KEY_LEN); + SHA1Update(&ctxt, pcb->eap.es_challenge, pcb->eap.es_challen); + SHA1Update(&ctxt, pcb->eap.es_server.ea_peer, + pcb->eap.es_server.ea_peerlen); + SHA1Final(dig, &ctxt); + if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) { + ppp_error("EAP: failed Lightweight rechallenge"); + eap_send_failure(pcb); + break; + } + pcb->eap.es_server.ea_state = eapOpen; + if (pcb->eap.es_lwrechallenge != 0) + TIMEOUT(srp_lwrechallenge, esp, + pcb->eap.es_lwrechallenge); + break; + } + break; +#endif /* USE_SRP */ + + default: + /* This can't happen. */ + ppp_error("EAP: unknown Response type %d; ignored", typenum); + return; + } + + if (pcb->settings.eap_timeout_time > 0) { + UNTIMEOUT(eap_server_timeout, pcb); + } + + if (pcb->eap.es_server.ea_state != eapBadAuth && + pcb->eap.es_server.ea_state != eapOpen) { + pcb->eap.es_server.ea_id++; + eap_send_request(pcb); + } +} +#endif /* PPP_SERVER */ + +/* + * eap_success - Receive EAP Success message (client mode). + */ +static void eap_success(ppp_pcb *pcb, u_char *inp, int id, int len) { + LWIP_UNUSED_ARG(id); + + if (pcb->eap.es_client.ea_state != eapOpen && !eap_client_active(pcb)) { + ppp_dbglog("EAP unexpected success message in state %s (%d)", + eap_state_name(pcb->eap.es_client.ea_state), + pcb->eap.es_client.ea_state); + return; + } + + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + } + + if (len > 0) { + /* This is odd. The spec doesn't allow for this. */ + PRINTMSG(inp, len); + } + + pcb->eap.es_client.ea_state = eapOpen; + auth_withpeer_success(pcb, PPP_EAP, 0); +} + +/* + * eap_failure - Receive EAP Failure message (client mode). + */ +static void eap_failure(ppp_pcb *pcb, u_char *inp, int id, int len) { + LWIP_UNUSED_ARG(id); + + if (!eap_client_active(pcb)) { + ppp_dbglog("EAP unexpected failure message in state %s (%d)", + eap_state_name(pcb->eap.es_client.ea_state), + pcb->eap.es_client.ea_state); + } + + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + } + + if (len > 0) { + /* This is odd. The spec doesn't allow for this. */ + PRINTMSG(inp, len); + } + + pcb->eap.es_client.ea_state = eapBadAuth; + + ppp_error("EAP: peer reports authentication failure"); + auth_withpeer_fail(pcb, PPP_EAP); +} + +/* + * eap_input - Handle received EAP message. + */ +static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen) { + u_char code, id; + int len; + + /* + * Parse header (code, id and length). If packet too short, + * drop it. + */ + if (inlen < EAP_HEADERLEN) { + ppp_error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < EAP_HEADERLEN || len > inlen) { + ppp_error("EAP: packet has illegal length field %d (%d..%d)", len, + EAP_HEADERLEN, inlen); + return; + } + len -= EAP_HEADERLEN; + + /* Dispatch based on message code */ + switch (code) { + case EAP_REQUEST: + eap_request(pcb, inp, id, len); + break; + +#if PPP_SERVER + case EAP_RESPONSE: + eap_response(pcb, inp, id, len); + break; +#endif /* PPP_SERVER */ + + case EAP_SUCCESS: + eap_success(pcb, inp, id, len); + break; + + case EAP_FAILURE: + eap_failure(pcb, inp, id, len); + break; + + default: /* XXX Need code reject */ + /* Note: it's not legal to send EAP Nak here. */ + ppp_warn("EAP: unknown code %d received", code); + break; + } +} + +#if PRINTPKT_SUPPORT +/* + * eap_printpkt - print the contents of an EAP packet. + */ +static const char* const eap_codenames[] = { + "Request", "Response", "Success", "Failure" +}; + +static const char* const eap_typenames[] = { + "Identity", "Notification", "Nak", "MD5-Challenge", + "OTP", "Generic-Token", NULL, NULL, + "RSA", "DSS", "KEA", "KEA-Validate", + "TLS", "Defender", "Windows 2000", "Arcot", + "Cisco", "Nokia", "SRP" +}; + +static int eap_printpkt(const u_char *inp, int inlen, void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len, rtype, vallen; + const u_char *pstart; + u32_t uval; + + if (inlen < EAP_HEADERLEN) + return (0); + pstart = inp; + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < EAP_HEADERLEN || len > inlen) + return (0); + + if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(eap_codenames)) + printer(arg, " %s", eap_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= EAP_HEADERLEN; + switch (code) { + case EAP_REQUEST: + if (len < 1) { + printer(arg, " "); + break; + } + GETCHAR(rtype, inp); + len--; + if (rtype >= 1 && rtype <= (int)LWIP_ARRAYSIZE(eap_typenames)) + printer(arg, " %s", eap_typenames[rtype-1]); + else + printer(arg, " type=0x%x", rtype); + switch (rtype) { + case EAPT_IDENTITY: + case EAPT_NOTIFICATION: + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + break; + + case EAPT_MD5CHAP: + if (len <= 0) + break; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + break; + + case EAPT_SRP: + if (len < 3) + goto truncated; + GETCHAR(vallen, inp); + len--; + printer(arg, "-%d", vallen); + switch (vallen) { + case EAPSRP_CHALLENGE: + GETCHAR(vallen, inp); + len--; + if (vallen >= len) + goto truncated; + if (vallen > 0) { + printer(arg, " "); + } else { + printer(arg, " "); + } + INCPTR(vallen, inp); + len -= vallen; + GETCHAR(vallen, inp); + len--; + if (vallen >= len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + if (vallen == 0) { + printer(arg, " "); + } else { + printer(arg, " ", vallen, inp); + } + INCPTR(vallen, inp); + len -= vallen; + if (len == 0) { + printer(arg, " "); + } else { + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPSRP_SKEY: + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + break; + + case EAPSRP_SVALIDATOR: + if (len < (int)sizeof (u32_t)) + break; + GETLONG(uval, inp); + len -= sizeof (u32_t); + if (uval & SRPVAL_EBIT) { + printer(arg, " E"); + uval &= ~SRPVAL_EBIT; + } + if (uval != 0) { + printer(arg, " f<%X>", uval); + } + if ((vallen = len) > SHA_DIGESTSIZE) + vallen = SHA_DIGESTSIZE; + printer(arg, " ", len, inp, + len < SHA_DIGESTSIZE ? "?" : ""); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPSRP_LWRECHALLENGE: + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + break; + default: + break; + } + break; + default: + break; + } + break; + + case EAP_RESPONSE: + if (len < 1) + break; + GETCHAR(rtype, inp); + len--; + if (rtype >= 1 && rtype <= (int)LWIP_ARRAYSIZE(eap_typenames)) + printer(arg, " %s", eap_typenames[rtype-1]); + else + printer(arg, " type=0x%x", rtype); + switch (rtype) { + case EAPT_IDENTITY: + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPT_NAK: + if (len <= 0) { + printer(arg, " "); + break; + } + GETCHAR(rtype, inp); + len--; + printer(arg, " = 1 && rtype < (int)LWIP_ARRAYSIZE(eap_typenames)) + printer(arg, " (%s)", eap_typenames[rtype-1]); + printer(arg, ">"); + break; + + case EAPT_MD5CHAP: + if (len <= 0) { + printer(arg, " "); + break; + } + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + break; + + case EAPT_SRP: + if (len < 1) + goto truncated; + GETCHAR(vallen, inp); + len--; + printer(arg, "-%d", vallen); + switch (vallen) { + case EAPSRP_CKEY: + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + break; + + case EAPSRP_CVALIDATOR: + if (len < (int)sizeof (u32_t)) + break; + GETLONG(uval, inp); + len -= sizeof (u32_t); + if (uval & SRPVAL_EBIT) { + printer(arg, " E"); + uval &= ~SRPVAL_EBIT; + } + if (uval != 0) { + printer(arg, " f<%X>", uval); + } + printer(arg, " ", len, inp, + len == SHA_DIGESTSIZE ? "" : "?"); + INCPTR(len, inp); + len = 0; + break; + + case EAPSRP_ACK: + break; + + case EAPSRP_LWRECHALLENGE: + printer(arg, " ", len, inp, + len == SHA_DIGESTSIZE ? "" : "?"); + if ((vallen = len) > SHA_DIGESTSIZE) + vallen = SHA_DIGESTSIZE; + INCPTR(vallen, inp); + len -= vallen; + break; + default: + break; + } + break; + default: + break; + } + break; + + case EAP_SUCCESS: /* No payload expected for these! */ + case EAP_FAILURE: + default: + break; + + truncated: + printer(arg, " "); + break; + } + + if (len > 8) + printer(arg, "%8B...", inp); + else if (len > 0) + printer(arg, "%.*B", len, inp); + INCPTR(len, inp); + + return (inp - pstart); +} +#endif /* PRINTPKT_SUPPORT */ + +#endif /* PPP_SUPPORT && EAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/ecp.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/ecp.c new file mode 100644 index 0000000..4d84f60 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/ecp.c @@ -0,0 +1,191 @@ +/* + * ecp.c - PPP Encryption Control Protocol. + * + * Copyright (c) 2002 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Derived from ccp.c, which is: + * + * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && ECP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/ecp.h" + +#if PPP_OPTIONS +static option_t ecp_option_list[] = { + { "noecp", o_bool, &ecp_protent.enabled_flag, + "Disable ECP negotiation" }, + { "-ecp", o_bool, &ecp_protent.enabled_flag, + "Disable ECP negotiation", OPT_ALIAS }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points from main code. + */ +static void ecp_init (int unit); +/* +static void ecp_open (int unit); +static void ecp_close (int unit, char *); +static void ecp_lowerup (int unit); +static void ecp_lowerdown (int); +static void ecp_input (int unit, u_char *pkt, int len); +static void ecp_protrej (int unit); +*/ +#if PRINTPKT_SUPPORT +static int ecp_printpkt (const u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); +#endif /* PRINTPKT_SUPPORT */ +/* +static void ecp_datainput (int unit, u_char *pkt, int len); +*/ + +const struct protent ecp_protent = { + PPP_ECP, + ecp_init, + NULL, /* ecp_input, */ + NULL, /* ecp_protrej, */ + NULL, /* ecp_lowerup, */ + NULL, /* ecp_lowerdown, */ + NULL, /* ecp_open, */ + NULL, /* ecp_close, */ +#if PRINTPKT_SUPPORT + ecp_printpkt, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + NULL, /* ecp_datainput, */ +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + "ECP", + "Encrypted", +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + ecp_option_list, + NULL, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +fsm ecp_fsm[NUM_PPP]; +ecp_options ecp_wantoptions[NUM_PPP]; /* what to request the peer to use */ +ecp_options ecp_gotoptions[NUM_PPP]; /* what the peer agreed to do */ +ecp_options ecp_allowoptions[NUM_PPP]; /* what we'll agree to do */ +ecp_options ecp_hisoptions[NUM_PPP]; /* what we agreed to do */ + +static const fsm_callbacks ecp_callbacks = { + NULL, /* ecp_resetci, */ + NULL, /* ecp_cilen, */ + NULL, /* ecp_addci, */ + NULL, /* ecp_ackci, */ + NULL, /* ecp_nakci, */ + NULL, /* ecp_rejci, */ + NULL, /* ecp_reqci, */ + NULL, /* ecp_up, */ + NULL, /* ecp_down, */ + NULL, + NULL, + NULL, + NULL, + NULL, /* ecp_extcode, */ + "ECP" +}; + +/* + * ecp_init - initialize ECP. + */ +static void +ecp_init(unit) + int unit; +{ + fsm *f = &ecp_fsm[unit]; + + f->unit = unit; + f->protocol = PPP_ECP; + f->callbacks = &ecp_callbacks; + fsm_init(f); + +#if 0 /* Not necessary, everything is cleared in ppp_new() */ + memset(&ecp_wantoptions[unit], 0, sizeof(ecp_options)); + memset(&ecp_gotoptions[unit], 0, sizeof(ecp_options)); + memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options)); + memset(&ecp_hisoptions[unit], 0, sizeof(ecp_options)); +#endif /* 0 */ + +} + + +#if PRINTPKT_SUPPORT +static int +ecp_printpkt(p, plen, printer, arg) + const u_char *p; + int plen; + void (*printer) (void *, char *, ...); + void *arg; +{ + return 0; +} +#endif /* PRINTPKT_SUPPORT */ + +#endif /* PPP_SUPPORT && ECP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/eui64.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/eui64.c new file mode 100644 index 0000000..01493bc --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/eui64.c @@ -0,0 +1,56 @@ +/* + * eui64.c - EUI64 routines for IPv6CP. + * + * Copyright (c) 1999 Tommi Komulainen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $ + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/eui64.h" + +/* + * eui64_ntoa - Make an ascii representation of an interface identifier + */ +char *eui64_ntoa(eui64_t e) { + static char buf[20]; + + sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x", + e.e8[0], e.e8[1], e.e8[2], e.e8[3], + e.e8[4], e.e8[5], e.e8[6], e.e8[7]); + return buf; +} + +#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/fsm.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/fsm.c new file mode 100644 index 0000000..81eba11 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/fsm.c @@ -0,0 +1,799 @@ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * @todo: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#if 0 /* UNUSED */ +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" + +static void fsm_timeout (void *); +static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len); +static void fsm_rconfack(fsm *f, int id, u_char *inp, int len); +static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len); +static void fsm_rtermreq(fsm *f, int id, u_char *p, int len); +static void fsm_rtermack(fsm *f); +static void fsm_rcoderej(fsm *f, u_char *inp, int len); +static void fsm_sconfreq(fsm *f, int retransmit); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void fsm_init(fsm *f) { + ppp_pcb *pcb = f->pcb; + f->state = PPP_FSM_INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->maxnakloops = pcb->settings.fsm_max_nak_loops; + f->term_reason_len = 0; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void fsm_lowerup(fsm *f) { + switch( f->state ){ + case PPP_FSM_INITIAL: + f->state = PPP_FSM_CLOSED; + break; + + case PPP_FSM_STARTING: + if( f->flags & OPT_SILENT ) + f->state = PPP_FSM_STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + } + break; + + default: + FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state)); + /* no break */ + } +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void fsm_lowerdown(fsm *f) { + switch( f->state ){ + case PPP_FSM_CLOSED: + f->state = PPP_FSM_INITIAL; + break; + + case PPP_FSM_STOPPED: + f->state = PPP_FSM_STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case PPP_FSM_CLOSING: + f->state = PPP_FSM_INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case PPP_FSM_STOPPING: + case PPP_FSM_REQSENT: + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + f->state = PPP_FSM_STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case PPP_FSM_OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + f->state = PPP_FSM_STARTING; + break; + + default: + FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state)); + /* no break */ + } +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void fsm_open(fsm *f) { + switch( f->state ){ + case PPP_FSM_INITIAL: + f->state = PPP_FSM_STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case PPP_FSM_CLOSED: + if( f->flags & OPT_SILENT ) + f->state = PPP_FSM_STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + } + break; + + case PPP_FSM_CLOSING: + f->state = PPP_FSM_STOPPING; + /* fall through */ + /* no break */ + case PPP_FSM_STOPPED: + case PPP_FSM_OPENED: + if( f->flags & OPT_RESTART ){ + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + default: + break; + } +} + +/* + * terminate_layer - Start process of shutting down the FSM + * + * Cancel any timeout running, notify upper layers we're done, and + * send a terminate-request message as configured. + */ +static void terminate_layer(fsm *f, int nextstate) { + ppp_pcb *pcb = f->pcb; + + if( f->state != PPP_FSM_OPENED ) + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + else if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + + /* Init restart counter and send Terminate-Request */ + f->retransmits = pcb->settings.fsm_max_term_transmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (const u_char *) f->term_reason, f->term_reason_len); + + if (f->retransmits == 0) { + /* + * User asked for no terminate requests at all; just close it. + * We've already fired off one Terminate-Request just to be nice + * to the peer, but we're not going to wait for a reply. + */ + f->state = nextstate == PPP_FSM_CLOSING ? PPP_FSM_CLOSED : PPP_FSM_STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + return; + } + + TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); + --f->retransmits; + + f->state = nextstate; +} + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the PPP_FSM_CLOSED state. + */ +void fsm_close(fsm *f, const char *reason) { + f->term_reason = reason; + f->term_reason_len = (reason == NULL? 0: LWIP_MIN(strlen(reason), 0xFF) ); + switch( f->state ){ + case PPP_FSM_STARTING: + f->state = PPP_FSM_INITIAL; + break; + case PPP_FSM_STOPPED: + f->state = PPP_FSM_CLOSED; + break; + case PPP_FSM_STOPPING: + f->state = PPP_FSM_CLOSING; + break; + + case PPP_FSM_REQSENT: + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + case PPP_FSM_OPENED: + terminate_layer(f, PPP_FSM_CLOSING); + break; + default: + break; + } +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void fsm_timeout(void *arg) { + fsm *f = (fsm *) arg; + ppp_pcb *pcb = f->pcb; + + switch (f->state) { + case PPP_FSM_CLOSING: + case PPP_FSM_STOPPING: + if( f->retransmits <= 0 ){ + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == PPP_FSM_CLOSING)? PPP_FSM_CLOSED: PPP_FSM_STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + } else { + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (const u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); + --f->retransmits; + } + break; + + case PPP_FSM_REQSENT: + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + if (f->retransmits <= 0) { + ppp_warn("%s: timeout sending Config-Requests", PROTO_NAME(f)); + f->state = PPP_FSM_STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) + (*f->callbacks->finished)(f); + + } else { + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) + (*f->callbacks->retransmit)(f); + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == PPP_FSM_ACKRCVD ) + f->state = PPP_FSM_REQSENT; + } + break; + + default: + FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state)); + /* no break */ + } +} + + +/* + * fsm_input - Input packet. + */ +void fsm_input(fsm *f, u_char *inpacket, int l) { + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < HEADERLEN) { + FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol)); + return; + } + if (len > l) { + FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == PPP_FSM_INITIAL || f->state == PPP_FSM_STARTING ){ + FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.", + f->protocol, f->state)); + return; + } + + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + if( !f->callbacks->extcode + || !(*f->callbacks->extcode)(f, code, id, inp, len) ) + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + break; + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) { + int code, reject_if_disagree; + + switch( f->state ){ + case PPP_FSM_CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case PPP_FSM_CLOSING: + case PPP_FSM_STOPPING: + return; + + case PPP_FSM_OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = PPP_FSM_REQSENT; + break; + + case PPP_FSM_STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = PPP_FSM_REQSENT; + break; + default: + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci){ /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) + code = CONFREJ; /* Reject all CI */ + else + code = CONFACK; + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, code, id, inp, len); + + if (code == CONFACK) { + if (f->state == PPP_FSM_ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = PPP_FSM_OPENED; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + } else + f->state = PPP_FSM_ACKSENT; + f->nakloops = 0; + + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != PPP_FSM_ACKRCVD) + f->state = PPP_FSM_REQSENT; + if( code == CONFNAK ) + ++f->nakloops; + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void fsm_rconfack(fsm *f, int id, u_char *inp, int len) { + ppp_pcb *pcb = f->pcb; + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): + (len == 0)) ){ + /* Ack is bad - ignore it */ + ppp_error("Received bad configure-ack: %P", inp, len); + return; + } + f->seen_ack = 1; + f->rnakloops = 0; + + switch (f->state) { + case PPP_FSM_CLOSED: + case PPP_FSM_STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case PPP_FSM_REQSENT: + f->state = PPP_FSM_ACKRCVD; + f->retransmits = pcb->settings.fsm_max_conf_req_transmits; + break; + + case PPP_FSM_ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + break; + + case PPP_FSM_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = PPP_FSM_OPENED; + f->retransmits = pcb->settings.fsm_max_conf_req_transmits; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + break; + + case PPP_FSM_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = PPP_FSM_REQSENT; + break; + default: + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) { + int ret; + int treat_as_reject; + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + + if (code == CONFNAK) { + ++f->rnakloops; + treat_as_reject = (f->rnakloops >= f->maxnakloops); + if (f->callbacks->nakci == NULL + || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) { + ppp_error("Received bad configure-nak: %P", inp, len); + return; + } + } else { + f->rnakloops = 0; + if (f->callbacks->rejci == NULL + || !(ret = f->callbacks->rejci(f, inp, len))) { + ppp_error("Received bad configure-rej: %P", inp, len); + return; + } + } + + f->seen_ack = 1; + + switch (f->state) { + case PPP_FSM_CLOSED: + case PPP_FSM_STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case PPP_FSM_REQSENT: + case PPP_FSM_ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) + f->state = PPP_FSM_STOPPED; /* kludge for stopping CCP */ + else + fsm_sconfreq(f, 0); /* Send Configure-Request */ + break; + + case PPP_FSM_ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + break; + + case PPP_FSM_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = PPP_FSM_REQSENT; + break; + default: + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void fsm_rtermreq(fsm *f, int id, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + + switch (f->state) { + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + f->state = PPP_FSM_REQSENT; /* Start over but keep trying */ + break; + + case PPP_FSM_OPENED: + if (len > 0) { + ppp_info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p); + } else + ppp_info("%s terminated by peer", PROTO_NAME(f)); + f->retransmits = 0; + f->state = PPP_FSM_STOPPING; + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); + break; + default: + break; + } + + fsm_sdata(f, TERMACK, id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void fsm_rtermack(fsm *f) { + switch (f->state) { + case PPP_FSM_CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = PPP_FSM_CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + case PPP_FSM_STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = PPP_FSM_STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case PPP_FSM_ACKRCVD: + f->state = PPP_FSM_REQSENT; + break; + + case PPP_FSM_OPENED: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + break; + default: + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void fsm_rcoderej(fsm *f, u_char *inp, int len) { + u_char code, id; + + if (len < HEADERLEN) { + FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + ppp_warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id); + + if( f->state == PPP_FSM_ACKRCVD ) + f->state = PPP_FSM_REQSENT; +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void fsm_protreject(fsm *f) { + switch( f->state ){ + case PPP_FSM_CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + /* no break */ + case PPP_FSM_CLOSED: + f->state = PPP_FSM_CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case PPP_FSM_STOPPING: + case PPP_FSM_REQSENT: + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + /* no break */ + case PPP_FSM_STOPPED: + f->state = PPP_FSM_STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case PPP_FSM_OPENED: + terminate_layer(f, PPP_FSM_STOPPING); + break; + + default: + FSMDEBUG(("%s: Protocol-reject event in state %d!", + PROTO_NAME(f), f->state)); + /* no break */ + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void fsm_sconfreq(fsm *f, int retransmit) { + ppp_pcb *pcb = f->pcb; + struct pbuf *p; + u_char *outp; + int cilen; + + if( f->state != PPP_FSM_REQSENT && f->state != PPP_FSM_ACKRCVD && f->state != PPP_FSM_ACKSENT ){ + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) + (*f->callbacks->resetci)(f); + f->nakloops = 0; + f->rnakloops = 0; + } + + if( !retransmit ){ + /* New request - reset retransmission counter, use new ID */ + f->retransmits = pcb->settings.fsm_max_conf_req_transmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + if( f->callbacks->cilen && f->callbacks->addci ){ + cilen = (*f->callbacks->cilen)(f); + if( cilen > pcb->peer_mru - HEADERLEN ) + cilen = pcb->peer_mru - HEADERLEN; + } else + cilen = 0; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(cilen + HEADERLEN + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + /* send the request to our peer */ + outp = (u_char*)p->payload; + MAKEHEADER(outp, f->protocol); + PUTCHAR(CONFREQ, outp); + PUTCHAR(f->reqid, outp); + PUTSHORT(cilen + HEADERLEN, outp); + if (cilen != 0) { + (*f->callbacks->addci)(f, outp, &cilen); + LWIP_ASSERT("cilen == p->len - HEADERLEN - PPP_HDRLEN", cilen == p->len - HEADERLEN - PPP_HDRLEN); + } + + ppp_write(pcb, p); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void fsm_sdata(fsm *f, u_char code, u_char id, const u_char *data, int datalen) { + ppp_pcb *pcb = f->pcb; + struct pbuf *p; + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + if (datalen > pcb->peer_mru - HEADERLEN) + datalen = pcb->peer_mru - HEADERLEN; + outlen = datalen + HEADERLEN; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(outlen + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + if (datalen) /* && data != outp + PPP_HDRLEN + HEADERLEN) -- was only for fsm_sconfreq() */ + MEMCPY(outp + PPP_HDRLEN + HEADERLEN, data, datalen); + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + ppp_write(pcb, p); +} + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/ipcp.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/ipcp.c new file mode 100644 index 0000000..b7c766e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/ipcp.c @@ -0,0 +1,2418 @@ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPP_IPV4_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * @todo: + */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/ipcp.h" + +#if 0 /* UNUSED */ +/* global vars */ +u32_t netmask = 0; /* IP netmask to set on interface */ +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +bool disable_defaultip = 0; /* Don't use hostname for default IP adrs */ +#endif /* UNUSED */ + +#if 0 /* moved to ppp_settings */ +bool noremoteip = 0; /* Let him have no IP address */ +#endif /* moved to ppp_setting */ + +#if 0 /* UNUSED */ +/* Hook for a plugin to know when IP protocol has come up */ +void (*ip_up_hook) (void) = NULL; + +/* Hook for a plugin to know when IP protocol has come down */ +void (*ip_down_hook) (void) = NULL; + +/* Hook for a plugin to choose the remote IP address */ +void (*ip_choose_hook) (u32_t *) = NULL; +#endif /* UNUSED */ + +#if PPP_NOTIFY +/* Notifiers for when IPCP goes up and down */ +struct notifier *ip_up_notifier = NULL; +struct notifier *ip_down_notifier = NULL; +#endif /* PPP_NOTIFY */ + +/* local vars */ +#if 0 /* moved to ppp_pcb */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ +static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */ +static int ipcp_is_up; /* have called np_up() */ +static int ipcp_is_open; /* haven't called np_finished() */ +static bool ask_for_local; /* request our address from peer */ +#endif /* moved to ppp_pcb */ +#if 0 /* UNUSED */ +static char vj_value[8]; /* string form of vj option value */ +static char netmask_str[20]; /* string form of netmask value */ +#endif /* UNUSED */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci(fsm *f); /* Reset our CI */ +static int ipcp_cilen(fsm *f); /* Return length of our CI */ +static void ipcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI */ +static int ipcp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */ +static int ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject);/* Peer nak'd our CI */ +static int ipcp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */ +static int ipcp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree); /* Rcv CI */ +static void ipcp_up(fsm *f); /* We're UP */ +static void ipcp_down(fsm *f); /* We're DOWN */ +static void ipcp_finished(fsm *f); /* Don't need lower layer */ + +static const fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches OPENED state */ + ipcp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + +/* + * Command-line options. + */ +#if PPP_OPTIONS +static int setvjslots (char **); +static int setdnsaddr (char **); +static int setwinsaddr (char **); +static int setnetmask (char **); +int setipaddr (char *, char **, int); + +static void printipaddr (option_t *, void (*)(void *, char *,...),void *); + +static option_t ipcp_option_list[] = { + { "noip", o_bool, &ipcp_protent.enabled_flag, + "Disable IP and IPCP" }, + { "-ip", o_bool, &ipcp_protent.enabled_flag, + "Disable IP and IPCP", OPT_ALIAS }, + + { "novj", o_bool, &ipcp_wantoptions[0].neg_vj, + "Disable VJ compression", OPT_A2CLR, &ipcp_allowoptions[0].neg_vj }, + { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj, + "Disable VJ compression", OPT_ALIAS | OPT_A2CLR, + &ipcp_allowoptions[0].neg_vj }, + + { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag, + "Disable VJ connection-ID compression", OPT_A2CLR, + &ipcp_allowoptions[0].cflag }, + { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag, + "Disable VJ connection-ID compression", OPT_ALIAS | OPT_A2CLR, + &ipcp_allowoptions[0].cflag }, + + { "vj-max-slots", o_special, (void *)setvjslots, + "Set maximum VJ header slots", + OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, vj_value }, + + { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local, + "Accept peer's address for us", 1 }, + { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote, + "Accept peer's address for it", 1 }, + + { "ipparam", o_string, &ipparam, + "Set ip script parameter", OPT_PRIO }, + + { "noipdefault", o_bool, &disable_defaultip, + "Don't use name for default IP adrs", 1 }, + + { "ms-dns", 1, (void *)setdnsaddr, + "DNS address for the peer's use" }, + { "ms-wins", 1, (void *)setwinsaddr, + "Nameserver for SMB over TCP/IP for peer" }, + + { "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime, + "Set timeout for IPCP", OPT_PRIO }, + { "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits, + "Set max #xmits for term-reqs", OPT_PRIO }, + { "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits, + "Set max #xmits for conf-reqs", OPT_PRIO }, + { "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops, + "Set max #conf-naks for IPCP", OPT_PRIO }, + + { "defaultroute", o_bool, &ipcp_wantoptions[0].default_route, + "Add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].default_route }, + { "nodefaultroute", o_bool, &ipcp_allowoptions[0].default_route, + "disable defaultroute option", OPT_A2CLR, + &ipcp_wantoptions[0].default_route }, + { "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route, + "disable defaultroute option", OPT_ALIAS | OPT_A2CLR, + &ipcp_wantoptions[0].default_route }, + + { "replacedefaultroute", o_bool, + &ipcp_wantoptions[0].replace_default_route, + "Replace default route", 1 + }, + { "noreplacedefaultroute", o_bool, + &ipcp_allowoptions[0].replace_default_route, + "Never replace default route", OPT_A2COPY, + &ipcp_wantoptions[0].replace_default_route }, + { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp, + "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp }, + { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp, + "disable proxyarp option", OPT_A2CLR, + &ipcp_wantoptions[0].proxy_arp }, + { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp, + "disable proxyarp option", OPT_ALIAS | OPT_A2CLR, + &ipcp_wantoptions[0].proxy_arp }, + + { "usepeerdns", o_bool, &usepeerdns, + "Ask peer for DNS address(es)", 1 }, + + { "netmask", o_special, (void *)setnetmask, + "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str }, + + { "ipcp-no-addresses", o_bool, &ipcp_wantoptions[0].old_addrs, + "Disable old-style IP-Addresses usage", OPT_A2CLR, + &ipcp_allowoptions[0].old_addrs }, + { "ipcp-no-address", o_bool, &ipcp_wantoptions[0].neg_addr, + "Disable IP-Address usage", OPT_A2CLR, + &ipcp_allowoptions[0].neg_addr }, + + { "noremoteip", o_bool, &noremoteip, + "Allow peer to have no IP address", 1 }, + + { "nosendip", o_bool, &ipcp_wantoptions[0].neg_addr, + "Don't send our IP address to peer", OPT_A2CLR, + &ipcp_wantoptions[0].old_addrs}, + + { "IP addresses", o_wild, (void *) &setipaddr, + "set local and remote IP addresses", + OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points from main code. + */ +static void ipcp_init(ppp_pcb *pcb); +static void ipcp_open(ppp_pcb *pcb); +static void ipcp_close(ppp_pcb *pcb, const char *reason); +static void ipcp_lowerup(ppp_pcb *pcb); +static void ipcp_lowerdown(ppp_pcb *pcb); +static void ipcp_input(ppp_pcb *pcb, u_char *p, int len); +static void ipcp_protrej(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int ipcp_printpkt(const u_char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS +static void ip_check_options (void); +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT +static int ip_demand_conf (int); +static int ip_active_pkt (u_char *, int); +#endif /* DEMAND_SUPPORT */ +#if 0 /* UNUSED */ +static void create_resolv (u32_t, u32_t); +#endif /* UNUSED */ + +const struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, +#if PRINTPKT_SUPPORT + ipcp_printpkt, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + NULL, +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + "IPCP", + "IP", +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + ipcp_option_list, + ip_check_options, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + ip_demand_conf, + ip_active_pkt +#endif /* DEMAND_SUPPORT */ +}; + +static void ipcp_clear_addrs(ppp_pcb *pcb, u32_t ouraddr, u32_t hisaddr, u8_t replacedefaultroute); + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + +#if 0 /* UNUSED, already defined by lwIP */ +/* + * Make a string representation of a network IP address. + */ +char * +ip_ntoa(ipaddr) +u32_t ipaddr; +{ + static char b[64]; + + slprintf(b, sizeof(b), "%I", ipaddr); + return b; +} +#endif /* UNUSED, already defined by lwIP */ + +/* + * Option parsing. + */ +#if PPP_OPTIONS +/* + * setvjslots - set maximum number of connection slots for VJ compression + */ +static int +setvjslots(argv) + char **argv; +{ + int value; + + if (!int_option(*argv, &value)) + return 0; + + if (value < 2 || value > 16) { + option_error("vj-max-slots value must be between 2 and 16"); + return 0; + } + ipcp_wantoptions [0].maxslotindex = + ipcp_allowoptions[0].maxslotindex = value - 1; + slprintf(vj_value, sizeof(vj_value), "%d", value); + return 1; +} + +/* + * setdnsaddr - set the dns address(es) + */ +static int +setdnsaddr(argv) + char **argv; +{ + u32_t dns; + struct hostent *hp; + + dns = inet_addr(*argv); + if (dns == (u32_t) -1) { + if ((hp = gethostbyname(*argv)) == NULL) { + option_error("invalid address parameter '%s' for ms-dns option", + *argv); + return 0; + } + dns = *(u32_t *)hp->h_addr; + } + + /* We take the last 2 values given, the 2nd-last as the primary + and the last as the secondary. If only one is given it + becomes both primary and secondary. */ + if (ipcp_allowoptions[0].dnsaddr[1] == 0) + ipcp_allowoptions[0].dnsaddr[0] = dns; + else + ipcp_allowoptions[0].dnsaddr[0] = ipcp_allowoptions[0].dnsaddr[1]; + + /* always set the secondary address value. */ + ipcp_allowoptions[0].dnsaddr[1] = dns; + + return (1); +} + +/* + * setwinsaddr - set the wins address(es) + * This is primrarly used with the Samba package under UNIX or for pointing + * the caller to the existing WINS server on a Windows NT platform. + */ +static int +setwinsaddr(argv) + char **argv; +{ + u32_t wins; + struct hostent *hp; + + wins = inet_addr(*argv); + if (wins == (u32_t) -1) { + if ((hp = gethostbyname(*argv)) == NULL) { + option_error("invalid address parameter '%s' for ms-wins option", + *argv); + return 0; + } + wins = *(u32_t *)hp->h_addr; + } + + /* We take the last 2 values given, the 2nd-last as the primary + and the last as the secondary. If only one is given it + becomes both primary and secondary. */ + if (ipcp_allowoptions[0].winsaddr[1] == 0) + ipcp_allowoptions[0].winsaddr[0] = wins; + else + ipcp_allowoptions[0].winsaddr[0] = ipcp_allowoptions[0].winsaddr[1]; + + /* always set the secondary address value. */ + ipcp_allowoptions[0].winsaddr[1] = wins; + + return (1); +} + +/* + * setipaddr - Set the IP address + * If doit is 0, the call is to check whether this option is + * potentially an IP address specification. + * Not static so that plugins can call it to set the addresses + */ +int +setipaddr(arg, argv, doit) + char *arg; + char **argv; + int doit; +{ + struct hostent *hp; + char *colon; + u32_t local, remote; + ipcp_options *wo = &ipcp_wantoptions[0]; + static int prio_local = 0, prio_remote = 0; + + /* + * IP address pair separated by ":". + */ + if ((colon = strchr(arg, ':')) == NULL) + return 0; + if (!doit) + return 1; + + /* + * If colon first character, then no local addr. + */ + if (colon != arg && option_priority >= prio_local) { + *colon = '\0'; + if ((local = inet_addr(arg)) == (u32_t) -1) { + if ((hp = gethostbyname(arg)) == NULL) { + option_error("unknown host: %s", arg); + return 0; + } + local = *(u32_t *)hp->h_addr; + } + if (bad_ip_adrs(local)) { + option_error("bad local IP address %s", ip_ntoa(local)); + return 0; + } + if (local != 0) + wo->ouraddr = local; + *colon = ':'; + prio_local = option_priority; + } + + /* + * If colon last character, then no remote addr. + */ + if (*++colon != '\0' && option_priority >= prio_remote) { + if ((remote = inet_addr(colon)) == (u32_t) -1) { + if ((hp = gethostbyname(colon)) == NULL) { + option_error("unknown host: %s", colon); + return 0; + } + remote = *(u32_t *)hp->h_addr; + if (remote_name[0] == 0) + strlcpy(remote_name, colon, sizeof(remote_name)); + } + if (bad_ip_adrs(remote)) { + option_error("bad remote IP address %s", ip_ntoa(remote)); + return 0; + } + if (remote != 0) + wo->hisaddr = remote; + prio_remote = option_priority; + } + + return 1; +} + +static void +printipaddr(opt, printer, arg) + option_t *opt; + void (*printer) (void *, char *, ...); + void *arg; +{ + ipcp_options *wo = &ipcp_wantoptions[0]; + + if (wo->ouraddr != 0) + printer(arg, "%I", wo->ouraddr); + printer(arg, ":"); + if (wo->hisaddr != 0) + printer(arg, "%I", wo->hisaddr); +} + +/* + * setnetmask - set the netmask to be used on the interface. + */ +static int +setnetmask(argv) + char **argv; +{ + u32_t mask; + int n; + char *p; + + /* + * Unfortunately, if we use inet_addr, we can't tell whether + * a result of all 1s is an error or a valid 255.255.255.255. + */ + p = *argv; + n = parse_dotted_ip(p, &mask); + + mask = lwip_htonl(mask); + + if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) { + option_error("invalid netmask value '%s'", *argv); + return 0; + } + + netmask = mask; + slprintf(netmask_str, sizeof(netmask_str), "%I", mask); + + return (1); +} + +int +parse_dotted_ip(p, vp) + char *p; + u32_t *vp; +{ + int n; + u32_t v, b; + char *endp, *p0 = p; + + v = 0; + for (n = 3;; --n) { + b = strtoul(p, &endp, 0); + if (endp == p) + return 0; + if (b > 255) { + if (n < 3) + return 0; + /* accept e.g. 0xffffff00 */ + *vp = b; + return endp - p0; + } + v |= b << (n * 8); + p = endp; + if (n == 0) + break; + if (*p != '.') + return 0; + ++p; + } + *vp = v; + return p - p0; +} +#endif /* PPP_OPTIONS */ + +/* + * ipcp_init - Initialize IPCP. + */ +static void ipcp_init(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + + ipcp_options *wo = &pcb->ipcp_wantoptions; + ipcp_options *ao = &pcb->ipcp_allowoptions; + + f->pcb = pcb; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(f); + + /* + * Some 3G modems use repeated IPCP NAKs as a way of stalling + * until they can contact a server on the network, so we increase + * the default number of NAKs we accept before we start treating + * them as rejects. + */ + f->maxnakloops = 100; + +#if 0 /* Not necessary, everything is cleared in ppp_new() */ + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); +#endif /* 0 */ + + wo->neg_addr = wo->old_addrs = 1; +#if VJ_SUPPORT + wo->neg_vj = 1; + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_STATES - 1; /* really max index */ + wo->cflag = 1; +#endif /* VJ_SUPPORT */ + +#if 0 /* UNUSED */ + /* wanting default route by default */ + wo->default_route = 1; +#endif /* UNUSED */ + + ao->neg_addr = ao->old_addrs = 1; +#if VJ_SUPPORT + /* max slots and slot-id compression are currently hardwired in */ + /* ppp_if.c to 16 and 1, this needs to be changed (among other */ + /* things) gmc */ + + ao->neg_vj = 1; + ao->maxslotindex = MAX_STATES - 1; + ao->cflag = 1; +#endif /* #if VJ_SUPPORT */ + +#if 0 /* UNUSED */ + /* + * XXX These control whether the user may use the proxyarp + * and defaultroute options. + */ + ao->proxy_arp = 1; + ao->default_route = 1; +#endif /* UNUSED */ +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void ipcp_open(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + fsm_open(f); + pcb->ipcp_is_open = 1; +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void ipcp_close(ppp_pcb *pcb, const char *reason) { + fsm *f = &pcb->ipcp_fsm; + fsm_close(f, reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void ipcp_lowerup(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + fsm_lowerup(f); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void ipcp_lowerdown(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + fsm_lowerdown(f); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void ipcp_input(ppp_pcb *pcb, u_char *p, int len) { + fsm *f = &pcb->ipcp_fsm; + fsm_input(f, p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void ipcp_protrej(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + fsm_lowerdown(f); +} + + +/* + * ipcp_resetci - Reset our CI. + * Called by fsm_sconfreq, Send Configure Request. + */ +static void ipcp_resetci(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipcp_options *wo = &pcb->ipcp_wantoptions; + ipcp_options *go = &pcb->ipcp_gotoptions; + ipcp_options *ao = &pcb->ipcp_allowoptions; + + wo->req_addr = (wo->neg_addr || wo->old_addrs) && + (ao->neg_addr || ao->old_addrs); + if (wo->ouraddr == 0) + wo->accept_local = 1; + if (wo->hisaddr == 0) + wo->accept_remote = 1; +#if LWIP_DNS + wo->req_dns1 = wo->req_dns2 = pcb->settings.usepeerdns; /* Request DNS addresses from the peer */ +#endif /* LWIP_DNS */ + *go = *wo; + if (!pcb->ask_for_local) + go->ouraddr = 0; +#if 0 /* UNUSED */ + if (ip_choose_hook) { + ip_choose_hook(&wo->hisaddr); + if (wo->hisaddr) { + wo->accept_remote = 0; + } + } +#endif /* UNUSED */ + BZERO(&pcb->ipcp_hisoptions, sizeof(ipcp_options)); +} + + +/* + * ipcp_cilen - Return length of our CI. + * Called by fsm_sconfreq, Send Configure Request. + */ +static int ipcp_cilen(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; +#if VJ_SUPPORT + ipcp_options *wo = &pcb->ipcp_wantoptions; +#endif /* VJ_SUPPORT */ + ipcp_options *ho = &pcb->ipcp_hisoptions; + +#define LENCIADDRS(neg) (neg ? CILEN_ADDRS : 0) +#if VJ_SUPPORT +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#endif /* VJ_SUPPORT */ +#define LENCIADDR(neg) (neg ? CILEN_ADDR : 0) +#if LWIP_DNS +#define LENCIDNS(neg) LENCIADDR(neg) +#endif /* LWIP_DNS */ +#if 0 /* UNUSED - WINS */ +#define LENCIWINS(neg) LENCIADDR(neg) +#endif /* UNUSED - WINS */ + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (go->neg_addr && go->old_addrs && !ho->neg_addr && ho->old_addrs) + go->neg_addr = 0; + +#if VJ_SUPPORT + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } +#endif /* VJ_SUPPORT */ + + return (LENCIADDRS(!go->neg_addr && go->old_addrs) + +#if VJ_SUPPORT + LENCIVJ(go->neg_vj, go->old_vj) + +#endif /* VJ_SUPPORT */ + LENCIADDR(go->neg_addr) + +#if LWIP_DNS + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2) + +#endif /* LWIP_DNS */ +#if 0 /* UNUSED - WINS */ + LENCIWINS(go->winsaddr[0]) + + LENCIWINS(go->winsaddr[1]) + +#endif /* UNUSED - WINS */ + 0); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + * Called by fsm_sconfreq, Send Configure Request. + */ +static void ipcp_addci(fsm *f, u_char *ucp, int *lenp) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + int len = *lenp; + +#define ADDCIADDRS(opt, neg, val1, val2) \ + if (neg) { \ + if (len >= CILEN_ADDRS) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDRS, ucp); \ + l = lwip_ntohl(val1); \ + PUTLONG(l, ucp); \ + l = lwip_ntohl(val2); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDRS; \ + } else \ + go->old_addrs = 0; \ + } + +#if VJ_SUPPORT +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else \ + neg = 0; \ + } +#endif /* VJ_SUPPORT */ + +#define ADDCIADDR(opt, neg, val) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = lwip_ntohl(val); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else \ + neg = 0; \ + } + +#if LWIP_DNS +#define ADDCIDNS(opt, neg, addr) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = lwip_ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else \ + neg = 0; \ + } +#endif /* LWIP_DNS */ + +#if 0 /* UNUSED - WINS */ +#define ADDCIWINS(opt, addr) \ + if (addr) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = lwip_ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else \ + addr = 0; \ + } +#endif /* UNUSED - WINS */ + + ADDCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr, + go->hisaddr); + +#if VJ_SUPPORT + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); +#endif /* VJ_SUPPORT */ + + ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr); + +#if LWIP_DNS + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); +#endif /* LWIP_DNS */ + +#if 0 /* UNUSED - WINS */ + ADDCIWINS(CI_MS_WINS1, go->winsaddr[0]); + + ADDCIWINS(CI_MS_WINS2, go->winsaddr[1]); +#endif /* UNUSED - WINS */ + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * Called by fsm_rconfack, Receive Configure ACK. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int ipcp_ackci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + u_short cilen, citype; + u32_t cilong; +#if VJ_SUPPORT + u_short cishort; + u_char cimaxslotindex, cicflag; +#endif /* VJ_SUPPORT */ + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIADDRS(opt, neg, val1, val2) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDRS) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDRS || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + if (val1 != cilong) \ + goto bad; \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + if (val2 != cilong) \ + goto bad; \ + } + +#if VJ_SUPPORT +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) \ + goto bad; \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) \ + goto bad; \ + } \ + } +#endif /* VJ_SUPPORT */ + +#define ACKCIADDR(opt, neg, val) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + if (val != cilong) \ + goto bad; \ + } + +#if LWIP_DNS +#define ACKCIDNS(opt, neg, addr) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + if (addr != cilong) \ + goto bad; \ + } +#endif /* LWIP_DNS */ + +#if 0 /* UNUSED - WINS */ +#define ACKCIWINS(opt, addr) \ + if (addr) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + if (addr != cilong) \ + goto bad; \ + } +#endif /* UNUSED - WINS */ + + ACKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr, + go->hisaddr); + +#if VJ_SUPPORT + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); +#endif /* VJ_SUPPORT */ + + ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr); + +#if LWIP_DNS + ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); +#endif /* LWIP_DNS */ + +#if 0 /* UNUSED - WINS */ + ACKCIWINS(CI_MS_WINS1, go->winsaddr[0]); + + ACKCIWINS(CI_MS_WINS2, go->winsaddr[1]); +#endif /* UNUSED - WINS */ + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); + +bad: + IPCPDEBUG(("ipcp_ackci: received bad Ack!")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the OPENED state. + * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + u_char citype, cilen, *next; +#if VJ_SUPPORT + u_char cimaxslotindex, cicflag; + u_short cishort; +#endif /* VJ_SUPPORT */ + u32_t ciaddr1, ciaddr2, l; +#if LWIP_DNS + u32_t cidnsaddr; +#endif /* LWIP_DNS */ + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try_; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try_ = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDRS(opt, neg, code) \ + if ((neg) && \ + (cilen = p[1]) == CILEN_ADDRS && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = lwip_htonl(l); \ + GETLONG(l, p); \ + ciaddr2 = lwip_htonl(l); \ + no.old_addrs = 1; \ + code \ + } + +#if VJ_SUPPORT +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#endif /* VJ_SUPPORT */ + +#define NAKCIADDR(opt, neg, code) \ + if (go->neg && \ + (cilen = p[1]) == CILEN_ADDR && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = lwip_htonl(l); \ + no.neg = 1; \ + code \ + } + +#if LWIP_DNS +#define NAKCIDNS(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cidnsaddr = lwip_htonl(l); \ + no.neg = 1; \ + code \ + } +#endif /* LWIP_DNS */ + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, + if (treat_as_reject) { + try_.old_addrs = 0; + } else { + if (go->accept_local && ciaddr1) { + /* take his idea of our address */ + try_.ouraddr = ciaddr1; + } + if (go->accept_remote && ciaddr2) { + /* take his idea of his address */ + try_.hisaddr = ciaddr2; + } + } + ); + +#if VJ_SUPPORT + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (treat_as_reject) { + try_.neg_vj = 0; + } else if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try_.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) + try_.maxslotindex = cimaxslotindex; + if (!cicflag) + try_.cflag = 0; + } else { + try_.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try_.old_vj = 1; + try_.vj_protocol = cishort; + } else { + try_.neg_vj = 0; + } + } + ); +#endif /* VJ_SUPPORT */ + + NAKCIADDR(CI_ADDR, neg_addr, + if (treat_as_reject) { + try_.neg_addr = 0; + try_.old_addrs = 0; + } else if (go->accept_local && ciaddr1) { + /* take his idea of our address */ + try_.ouraddr = ciaddr1; + } + ); + +#if LWIP_DNS + NAKCIDNS(CI_MS_DNS1, req_dns1, + if (treat_as_reject) { + try_.req_dns1 = 0; + } else { + try_.dnsaddr[0] = cidnsaddr; + } + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + if (treat_as_reject) { + try_.req_dns2 = 0; + } else { + try_.dnsaddr[1] = cidnsaddr; + } + ); +#endif /* #if LWIP_DNS */ + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + * If they want us to ask for ms-dns, we do that, since some + * peers get huffy if we don't. + */ + while (len >= CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if ( cilen < CILEN_VOID || (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { +#if VJ_SUPPORT + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) + goto bad; + no.neg_vj = 1; + break; +#endif /* VJ_SUPPORT */ + case CI_ADDRS: + if ((!go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) + goto bad; + try_.neg_addr = 0; + GETLONG(l, p); + ciaddr1 = lwip_htonl(l); + if (ciaddr1 && go->accept_local) + try_.ouraddr = ciaddr1; + GETLONG(l, p); + ciaddr2 = lwip_htonl(l); + if (ciaddr2 && go->accept_remote) + try_.hisaddr = ciaddr2; + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) + goto bad; + try_.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = lwip_htonl(l); + if (ciaddr1 && go->accept_local) + try_.ouraddr = ciaddr1; + if (try_.ouraddr != 0) + try_.neg_addr = 1; + no.neg_addr = 1; + break; +#if LWIP_DNS + case CI_MS_DNS1: + if (go->req_dns1 || no.req_dns1 || cilen != CILEN_ADDR) + goto bad; + GETLONG(l, p); + try_.dnsaddr[0] = lwip_htonl(l); + try_.req_dns1 = 1; + no.req_dns1 = 1; + break; + case CI_MS_DNS2: + if (go->req_dns2 || no.req_dns2 || cilen != CILEN_ADDR) + goto bad; + GETLONG(l, p); + try_.dnsaddr[1] = lwip_htonl(l); + try_.req_dns2 = 1; + no.req_dns2 = 1; + break; +#endif /* LWIP_DNS */ +#if 0 /* UNUSED - WINS */ + case CI_MS_WINS1: + case CI_MS_WINS2: + if (cilen != CILEN_ADDR) + goto bad; + GETLONG(l, p); + ciaddr1 = lwip_htonl(l); + if (ciaddr1) + try_.winsaddr[citype == CI_MS_WINS2] = ciaddr1; + break; +#endif /* UNUSED - WINS */ + default: + break; + } + p = next; + } + + /* + * OK, the Nak is good. Now we can update state. + * If there are any remaining options, we ignore them. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + + return 1; + +bad: + IPCPDEBUG(("ipcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + * Callback from fsm_rconfnakrej. + */ +static int ipcp_rejci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + u_char cilen; +#if VJ_SUPPORT + u_char cimaxslotindex, ciflag; + u_short cishort; +#endif /* VJ_SUPPORT */ + u32_t cilong; + ipcp_options try_; /* options to request next time */ + + try_ = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDRS(opt, neg, val1, val2) \ + if ((neg) && \ + (cilen = p[1]) == CILEN_ADDRS && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) \ + goto bad; \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) \ + goto bad; \ + try_.old_addrs = 0; \ + } + +#if VJ_SUPPORT +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) \ + goto bad; \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) \ + goto bad; \ + } \ + try_.neg = 0; \ + } +#endif /* VJ_SUPPORT */ + +#define REJCIADDR(opt, neg, val) \ + if (go->neg && \ + (cilen = p[1]) == CILEN_ADDR && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + try_.neg = 0; \ + } + +#if LWIP_DNS +#define REJCIDNS(opt, neg, dnsaddr) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + /* Check rejected value. */ \ + if (cilong != dnsaddr) \ + goto bad; \ + try_.neg = 0; \ + } +#endif /* LWIP_DNS */ + +#if 0 /* UNUSED - WINS */ +#define REJCIWINS(opt, addr) \ + if (addr && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = lwip_htonl(l); \ + /* Check rejected value. */ \ + if (cilong != addr) \ + goto bad; \ + try_.winsaddr[opt == CI_MS_WINS2] = 0; \ + } +#endif /* UNUSED - WINS */ + + REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, + go->ouraddr, go->hisaddr); + +#if VJ_SUPPORT + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); +#endif /* VJ_SUPPORT */ + + REJCIADDR(CI_ADDR, neg_addr, go->ouraddr); + +#if LWIP_DNS + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); +#endif /* LWIP_DNS */ + +#if 0 /* UNUSED - WINS */ + REJCIWINS(CI_MS_WINS1, go->winsaddr[0]); + + REJCIWINS(CI_MS_WINS2, go->winsaddr[1]); +#endif /* UNUSED - WINS */ + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + return 1; + +bad: + IPCPDEBUG(("ipcp_rejci: received bad Reject!")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * Callback from fsm_rconfreq, Receive Configure Request + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + * + * inp = Requested CIs + * len = Length of requested CIs + */ +static int ipcp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) { + ppp_pcb *pcb = f->pcb; + ipcp_options *wo = &pcb->ipcp_wantoptions; + ipcp_options *ho = &pcb->ipcp_hisoptions; + ipcp_options *ao = &pcb->ipcp_allowoptions; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ +#if VJ_SUPPORT + u_short cishort; /* Parsed short value */ +#endif /* VJ_SUPPORT */ + u32_t tl, ciaddr1, ciaddr2;/* Parsed address values */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ +#if VJ_SUPPORT + u_char maxslotindex, cflag; +#endif /* VJ_SUPPORT */ +#if LWIP_DNS + int d; +#endif /* LWIP_DNS */ + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG(("ipcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_ADDRS: + if (!ao->old_addrs || ho->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = lwip_htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = lwip_ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = lwip_htonl(tl); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = lwip_ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + wo->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; + + case CI_ADDR: + if (!ao->neg_addr || ho->old_addrs || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = lwip_htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = lwip_ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + break; + +#if LWIP_DNS + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (lwip_htonl(tl) != ao->dnsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = lwip_ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; +#endif /* LWIP_DNS */ + +#if 0 /* UNUSED - WINS */ + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (lwip_htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = lwip_ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; +#endif /* UNUSED - WINS */ + +#if VJ_SUPPORT + case CI_COMPRESSTYPE: + if (!ao->neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_STATES - 1; + ho->cflag = 1; + } + break; +#endif /* VJ_SUPPORT */ + + default: + orc = CONFREJ; + break; + } +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + MEMCPY(ucp, cip, cilen); /* Move it */ + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && !ho->old_addrs && + wo->req_addr && !reject_if_disagree && !pcb->settings.noremoteip) { + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = lwip_ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPCPDEBUG(("ipcp: returning Configure-%s", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +#if 0 /* UNUSED */ +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options() +{ + struct hostent *hp; + u32_t local; + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Default our local IP address based on our hostname. + * If local IP address already given, don't bother. + */ + if (wo->ouraddr == 0 && !disable_defaultip) { + /* + * Look up our hostname (possibly with domain name appended) + * and take the first IP address as our local IP address. + * If there isn't an IP address for our hostname, too bad. + */ + wo->accept_local = 1; /* don't insist on this default value */ + if ((hp = gethostbyname(hostname)) != NULL) { + local = *(u32_t *)hp->h_addr; + if (local != 0 && !bad_ip_adrs(local)) + wo->ouraddr = local; + } + } + ask_for_local = wo->ouraddr != 0 || !disable_defaultip; +} +#endif /* UNUSED */ + +#if DEMAND_SUPPORT +/* + * ip_demand_conf - configure the interface as though + * IPCP were up, for use with dial-on-demand. + */ +static int +ip_demand_conf(u) + int u; +{ + ppp_pcb *pcb = &ppp_pcb_list[u]; + ipcp_options *wo = &ipcp_wantoptions[u]; + + if (wo->hisaddr == 0 && !pcb->settings.noremoteip) { + /* make up an arbitrary address for the peer */ + wo->hisaddr = lwip_htonl(0x0a707070 + ifunit); + wo->accept_remote = 1; + } + if (wo->ouraddr == 0) { + /* make up an arbitrary address for us */ + wo->ouraddr = lwip_htonl(0x0a404040 + ifunit); + wo->accept_local = 1; + ask_for_local = 0; /* don't tell the peer this address */ + } + if (!sifaddr(pcb, wo->ouraddr, wo->hisaddr, get_mask(wo->ouraddr))) + return 0; + if (!sifup(pcb)) + return 0; + if (!sifnpmode(pcb, PPP_IP, NPMODE_QUEUE)) + return 0; +#if 0 /* UNUSED */ + if (wo->default_route) + if (sifdefaultroute(pcb, wo->ouraddr, wo->hisaddr, + wo->replace_default_route)) + default_route_set[u] = 1; +#endif /* UNUSED */ +#if 0 /* UNUSED - PROXY ARP */ + if (wo->proxy_arp) + if (sifproxyarp(pcb, wo->hisaddr)) + proxy_arp_set[u] = 1; +#endif /* UNUSED - PROXY ARP */ + + ppp_notice("local IP address %I", wo->ouraddr); + if (wo->hisaddr) + ppp_notice("remote IP address %I", wo->hisaddr); + + return 1; +} +#endif /* DEMAND_SUPPORT */ + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +static void ipcp_up(fsm *f) { + ppp_pcb *pcb = f->pcb; + u32_t mask; + ipcp_options *ho = &pcb->ipcp_hisoptions; + ipcp_options *go = &pcb->ipcp_gotoptions; + ipcp_options *wo = &pcb->ipcp_wantoptions; + + IPCPDEBUG(("ipcp: up")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr && !ho->old_addrs) + ho->hisaddr = wo->hisaddr; + + if (!(go->neg_addr || go->old_addrs) && (wo->neg_addr || wo->old_addrs) + && wo->ouraddr != 0) { + ppp_error("Peer refused to agree to our IP address"); + ipcp_close(f->pcb, "Refused our IP address"); + return; + } + if (go->ouraddr == 0) { + ppp_error("Could not determine local IP address"); + ipcp_close(f->pcb, "Could not determine local IP address"); + return; + } + if (ho->hisaddr == 0 && !pcb->settings.noremoteip) { + ho->hisaddr = lwip_htonl(0x0a404040); + ppp_warn("Could not determine remote IP address: defaulting to %I", + ho->hisaddr); + } +#if 0 /* UNUSED */ + script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0); + if (ho->hisaddr != 0) + script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1); +#endif /* UNUSED */ + +#if LWIP_DNS + if (!go->req_dns1) + go->dnsaddr[0] = 0; + if (!go->req_dns2) + go->dnsaddr[1] = 0; +#if 0 /* UNUSED */ + if (go->dnsaddr[0]) + script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0); + if (go->dnsaddr[1]) + script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0); +#endif /* UNUSED */ + if (pcb->settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + sdns(pcb, go->dnsaddr[0], go->dnsaddr[1]); +#if 0 /* UNUSED */ + script_setenv("USEPEERDNS", "1", 0); + create_resolv(go->dnsaddr[0], go->dnsaddr[1]); +#endif /* UNUSED */ + } +#endif /* LWIP_DNS */ + + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (ho->hisaddr != 0) { + u32_t addr = lwip_ntohl(ho->hisaddr); + if ((addr >> IP_CLASSA_NSHIFT) == IP_LOOPBACKNET + || IP_MULTICAST(addr) || IP_BADCLASS(addr) + /* + * For now, consider that PPP in server mode with peer required + * to authenticate must provide the peer IP address, reject any + * IP address wanted by peer different than the one we wanted. + */ +#if PPP_SERVER && PPP_AUTH_SUPPORT + || (pcb->settings.auth_required && wo->hisaddr != ho->hisaddr) +#endif /* PPP_SERVER && PPP_AUTH_SUPPORT */ + ) { + ppp_error("Peer is not authorized to use remote address %I", ho->hisaddr); + ipcp_close(pcb, "Unauthorized remote IP address"); + return; + } + } +#if 0 /* Unused */ + /* Upstream checking code */ + if (ho->hisaddr != 0 && !auth_ip_addr(f->unit, ho->hisaddr)) { + ppp_error("Peer is not authorized to use remote address %I", ho->hisaddr); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } +#endif /* Unused */ + +#if VJ_SUPPORT + /* set tcp compression */ + sifvjcomp(pcb, ho->neg_vj, ho->cflag, ho->maxslotindex); +#endif /* VJ_SUPPORT */ + +#if DEMAND_SUPPORT + /* + * If we are doing dial-on-demand, the interface is already + * configured, so we put out any saved-up packets, then set the + * interface to pass IP packets. + */ + if (demand) { + if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) { + ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr, + wo->replace_default_route); + if (go->ouraddr != wo->ouraddr) { + ppp_warn("Local IP address changed to %I", go->ouraddr); + script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0); + wo->ouraddr = go->ouraddr; + } else + script_unsetenv("OLDIPLOCAL"); + if (ho->hisaddr != wo->hisaddr && wo->hisaddr != 0) { + ppp_warn("Remote IP address changed to %I", ho->hisaddr); + script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0); + wo->hisaddr = ho->hisaddr; + } else + script_unsetenv("OLDIPREMOTE"); + + /* Set the interface to the new addresses */ + mask = get_mask(go->ouraddr); + if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) { +#if PPP_DEBUG + ppp_warn("Interface configuration failed"); +#endif /* PPP_DEBUG */ + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) + if (sifdefaultroute(pcb, go->ouraddr, ho->hisaddr, + wo->replace_default_route)) + default_route_set[f->unit] = 1; + +#if 0 /* UNUSED - PROXY ARP */ + /* Make a proxy ARP entry if requested. */ + if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp) + if (sifproxyarp(pcb, ho->hisaddr)) + proxy_arp_set[f->unit] = 1; +#endif /* UNUSED - PROXY ARP */ + + } + demand_rexmit(PPP_IP,go->ouraddr); + sifnpmode(pcb, PPP_IP, NPMODE_PASS); + + } else +#endif /* DEMAND_SUPPORT */ + { + /* + * Set IP addresses and (if specified) netmask. + */ + mask = get_mask(go->ouraddr); + +#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) { +#if PPP_DEBUG + ppp_warn("Interface configuration failed"); +#endif /* PPP_DEBUG */ + ipcp_close(f->pcb, "Interface configuration failed"); + return; + } +#endif + + /* bring the interface up for IP */ + if (!sifup(pcb)) { +#if PPP_DEBUG + ppp_warn("Interface failed to come up"); +#endif /* PPP_DEBUG */ + ipcp_close(f->pcb, "Interface configuration failed"); + return; + } + +#if (defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) { +#if PPP_DEBUG + ppp_warn("Interface configuration failed"); +#endif /* PPP_DEBUG */ + ipcp_close(f->unit, "Interface configuration failed"); + return; + } +#endif +#if DEMAND_SUPPORT + sifnpmode(pcb, PPP_IP, NPMODE_PASS); +#endif /* DEMAND_SUPPORT */ + +#if 0 /* UNUSED */ + /* assign a default route through the interface if required */ + if (wo->default_route) + if (sifdefaultroute(pcb, go->ouraddr, ho->hisaddr, + wo->replace_default_route)) + pcb->default_route_set = 1; +#endif /* UNUSED */ + +#if 0 /* UNUSED - PROXY ARP */ + /* Make a proxy ARP entry if requested. */ + if (ho->hisaddr != 0 && wo->proxy_arp) + if (sifproxyarp(pcb, ho->hisaddr)) + pcb->proxy_arp_set = 1; +#endif /* UNUSED - PROXY ARP */ + + wo->ouraddr = go->ouraddr; + + ppp_notice("local IP address %I", go->ouraddr); + if (ho->hisaddr != 0) + ppp_notice("remote IP address %I", ho->hisaddr); +#if LWIP_DNS + if (go->dnsaddr[0]) + ppp_notice("primary DNS address %I", go->dnsaddr[0]); + if (go->dnsaddr[1]) + ppp_notice("secondary DNS address %I", go->dnsaddr[1]); +#endif /* LWIP_DNS */ + } + +#if PPP_STATS_SUPPORT + reset_link_stats(f->unit); +#endif /* PPP_STATS_SUPPORT */ + + np_up(pcb, PPP_IP); + pcb->ipcp_is_up = 1; + +#if PPP_NOTIFY + notify(ip_up_notifier, 0); +#endif /* PPP_NOTIFY */ +#if 0 /* UNUSED */ + if (ip_up_hook) + ip_up_hook(); +#endif /* UNUSED */ +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void ipcp_down(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipcp_options *ho = &pcb->ipcp_hisoptions; + ipcp_options *go = &pcb->ipcp_gotoptions; + + IPCPDEBUG(("ipcp: down")); +#if PPP_STATS_SUPPORT + /* XXX a bit IPv4-centric here, we only need to get the stats + * before the interface is marked down. */ + /* XXX more correct: we must get the stats before running the notifiers, + * at least for the radius plugin */ + update_link_stats(f->unit); +#endif /* PPP_STATS_SUPPORT */ +#if PPP_NOTIFY + notify(ip_down_notifier, 0); +#endif /* PPP_NOTIFY */ +#if 0 /* UNUSED */ + if (ip_down_hook) + ip_down_hook(); +#endif /* UNUSED */ + if (pcb->ipcp_is_up) { + pcb->ipcp_is_up = 0; + np_down(pcb, PPP_IP); + } +#if VJ_SUPPORT + sifvjcomp(pcb, 0, 0, 0); +#endif /* VJ_SUPPORT */ + +#if PPP_STATS_SUPPORT + print_link_stats(); /* _after_ running the notifiers and ip_down_hook(), + * because print_link_stats() sets link_stats_valid + * to 0 (zero) */ +#endif /* PPP_STATS_SUPPORT */ + +#if DEMAND_SUPPORT + /* + * If we are doing dial-on-demand, set the interface + * to queue up outgoing packets (for now). + */ + if (demand) { + sifnpmode(pcb, PPP_IP, NPMODE_QUEUE); + } else +#endif /* DEMAND_SUPPORT */ + { +#if DEMAND_SUPPORT + sifnpmode(pcb, PPP_IP, NPMODE_DROP); +#endif /* DEMAND_SUPPORT */ + sifdown(pcb); + ipcp_clear_addrs(pcb, go->ouraddr, + ho->hisaddr, 0); +#if LWIP_DNS + cdns(pcb, go->dnsaddr[0], go->dnsaddr[1]); +#endif /* LWIP_DNS */ + } +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, + * proxy arp entries, etc. + */ +static void ipcp_clear_addrs(ppp_pcb *pcb, u32_t ouraddr, u32_t hisaddr, u8_t replacedefaultroute) { + LWIP_UNUSED_ARG(replacedefaultroute); + +#if 0 /* UNUSED - PROXY ARP */ + if (pcb->proxy_arp_set) { + cifproxyarp(pcb, hisaddr); + pcb->proxy_arp_set = 0; + } +#endif /* UNUSED - PROXY ARP */ +#if 0 /* UNUSED */ + /* If replacedefaultroute, sifdefaultroute will be called soon + * with replacedefaultroute set and that will overwrite the current + * default route. This is the case only when doing demand, otherwise + * during demand, this cifdefaultroute would restore the old default + * route which is not what we want in this case. In the non-demand + * case, we'll delete the default route and restore the old if there + * is one saved by an sifdefaultroute with replacedefaultroute. + */ + if (!replacedefaultroute && pcb->default_route_set) { + cifdefaultroute(pcb, ouraddr, hisaddr); + pcb->default_route_set = 0; + } +#endif /* UNUSED */ + cifaddr(pcb, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void ipcp_finished(fsm *f) { + ppp_pcb *pcb = f->pcb; + if (pcb->ipcp_is_open) { + pcb->ipcp_is_open = 0; + np_finished(pcb, PPP_IP); + } +} + + +#if 0 /* UNUSED */ +/* + * create_resolv - create the replacement resolv.conf file + */ +static void +create_resolv(peerdns1, peerdns2) + u32_t peerdns1, peerdns2; +{ + +} +#endif /* UNUSED */ + +#if PRINTPKT_SUPPORT +/* + * ipcp_printpkt - print the contents of an IPCP packet. + */ +static const char* const ipcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej" +}; + +static int ipcp_printpkt(const u_char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len, olen; + const u_char *pstart, *optend; +#if VJ_SUPPORT + u_short cishort; +#endif /* VJ_SUPPORT */ + u32_t cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(ipcp_codenames)) + printer(arg, " %s", ipcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_ADDRS: + if (olen == CILEN_ADDRS) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addrs %I", lwip_htonl(cilong)); + GETLONG(cilong, p); + printer(arg, " %I", lwip_htonl(cilong)); + } + break; +#if VJ_SUPPORT + case CI_COMPRESSTYPE: + if (olen >= CILEN_COMPRESS) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "compress "); + switch (cishort) { + case IPCP_VJ_COMP: + printer(arg, "VJ"); + break; + case IPCP_VJ_COMP_OLD: + printer(arg, "old-VJ"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; +#endif /* VJ_SUPPORT */ + case CI_ADDR: + if (olen == CILEN_ADDR) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addr %I", lwip_htonl(cilong)); + } + break; +#if LWIP_DNS + case CI_MS_DNS1: + case CI_MS_DNS2: + p += 2; + GETLONG(cilong, p); + printer(arg, "ms-dns%d %I", (code == CI_MS_DNS1? 1: 2), + htonl(cilong)); + break; +#endif /* LWIP_DNS */ +#if 0 /* UNUSED - WINS */ + case CI_MS_WINS1: + case CI_MS_WINS2: + p += 2; + GETLONG(cilong, p); + printer(arg, "ms-wins %I", lwip_htonl(cilong)); + break; +#endif /* UNUSED - WINS */ + default: + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + ppp_print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + default: + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} +#endif /* PRINTPKT_SUPPORT */ + +#if DEMAND_SUPPORT +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#ifndef IPPROTO_TCP +#define IPPROTO_TCP 6 +#endif +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(pkt, len) + u_char *pkt; + int len; +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) + return 0; + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) + return 0; + if (get_ipproto(pkt) != IPPROTO_TCP) + return 1; + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) + return 0; + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) + return 0; + return 1; +} +#endif /* DEMAND_SUPPORT */ + +#endif /* PPP_SUPPORT && PPP_IPV4_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/ipv6cp.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/ipv6cp.c new file mode 100644 index 0000000..11c18df --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/ipv6cp.c @@ -0,0 +1,1533 @@ +/* + * ipv6cp.c - PPP IPV6 Control Protocol. + * + * Copyright (c) 1999 Tommi Komulainen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* Original version, based on RFC2023 : + + Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt, + Alain.Durand@imag.fr, IMAG, + Jean-Luc.Richier@imag.fr, IMAG-LSR. + + Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE, + Alain.Durand@imag.fr, IMAG, + Jean-Luc.Richier@imag.fr, IMAG-LSR. + + Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt + Économique ayant pour membres BULL S.A. et l'INRIA). + + Ce logiciel informatique est disponible aux conditions + usuelles dans la recherche, c'est-à-dire qu'il peut + être utilisé, copié, modifié, distribué à l'unique + condition que ce texte soit conservé afin que + l'origine de ce logiciel soit reconnue. + + Le nom de l'Institut National de Recherche en Informatique + et en Automatique (INRIA), de l'IMAG, ou d'une personne morale + ou physique ayant participé à l'élaboration de ce logiciel ne peut + être utilisé sans son accord préalable explicite. + + Ce logiciel est fourni tel quel sans aucune garantie, + support ou responsabilité d'aucune sorte. + Ce logiciel est dérivé de sources d'origine + "University of California at Berkeley" et + "Digital Equipment Corporation" couvertes par des copyrights. + + L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG) + est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National + Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant + sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR). + + This work has been done in the context of GIE DYADE (joint R & D venture + between BULL S.A. and INRIA). + + This software is available with usual "research" terms + with the aim of retain credits of the software. + Permission to use, copy, modify and distribute this software for any + purpose and without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies, + and the name of INRIA, IMAG, or any contributor not be used in advertising + or publicity pertaining to this material without the prior explicit + permission. The software is provided "as is" without any + warranties, support or liabilities of any kind. + This software is derived from source code from + "University of California at Berkeley" and + "Digital Equipment Corporation" protected by copyrights. + + Grenoble's Institute of Computer Science and Applied Mathematics (IMAG) + is a federation of seven research units funded by the CNRS, National + Polytechnic Institute of Grenoble and University Joseph Fourier. + The research unit in Software, Systems, Networks (LSR) is member of IMAG. +*/ + +/* + * Derived from : + * + * + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $ + */ + +/* + * @todo: + * + * Proxy Neighbour Discovery. + * + * Better defines for selecting the ordering of + * interface up / set address. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/fsm.h" +#include "netif/ppp/ipcp.h" +#include "netif/ppp/ipv6cp.h" +#include "netif/ppp/magic.h" + +/* global vars */ +#if 0 /* UNUSED */ +int no_ifaceid_neg = 0; +#endif /* UNUSED */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipv6cp_resetci(fsm *f); /* Reset our CI */ +static int ipv6cp_cilen(fsm *f); /* Return length of our CI */ +static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI */ +static int ipv6cp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */ +static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */ +static int ipv6cp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */ +static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree); /* Rcv CI */ +static void ipv6cp_up(fsm *f); /* We're UP */ +static void ipv6cp_down(fsm *f); /* We're DOWN */ +static void ipv6cp_finished(fsm *f); /* Don't need lower layer */ + +static const fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */ + ipv6cp_resetci, /* Reset our Configuration Information */ + ipv6cp_cilen, /* Length of our Configuration Information */ + ipv6cp_addci, /* Add our Configuration Information */ + ipv6cp_ackci, /* ACK our Configuration Information */ + ipv6cp_nakci, /* NAK our Configuration Information */ + ipv6cp_rejci, /* Reject our Configuration Information */ + ipv6cp_reqci, /* Request peer's Configuration Information */ + ipv6cp_up, /* Called when fsm reaches OPENED state */ + ipv6cp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipv6cp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPV6CP" /* String name of protocol */ +}; + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static int setifaceid(char **arg)); +static void printifaceid(option_t *, + void (*)(void *, char *, ...), void *)); + +static option_t ipv6cp_option_list[] = { + { "ipv6", o_special, (void *)setifaceid, + "Set interface identifiers for IPV6", + OPT_A2PRINTER, (void *)printifaceid }, + + { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag, + "Enable IPv6 and IPv6CP", OPT_PRIO | 1 }, + { "noipv6", o_bool, &ipv6cp_protent.enabled_flag, + "Disable IPv6 and IPv6CP", OPT_PRIOSUB }, + { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag, + "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS }, + + { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local, + "Accept peer's interface identifier for us", 1 }, + + { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip, + "Use (default) IPv4 address as interface identifier", 1 }, + + { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent, + "Use uniquely-available persistent value for link local address", 1 }, + + { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime, + "Set timeout for IPv6CP", OPT_PRIO }, + { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits, + "Set max #xmits for term-reqs", OPT_PRIO }, + { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits, + "Set max #xmits for conf-reqs", OPT_PRIO }, + { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops, + "Set max #conf-naks for IPv6CP", OPT_PRIO }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points from main code. + */ +static void ipv6cp_init(ppp_pcb *pcb); +static void ipv6cp_open(ppp_pcb *pcb); +static void ipv6cp_close(ppp_pcb *pcb, const char *reason); +static void ipv6cp_lowerup(ppp_pcb *pcb); +static void ipv6cp_lowerdown(ppp_pcb *pcb); +static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len); +static void ipv6cp_protrej(ppp_pcb *pcb); +#if PPP_OPTIONS +static void ipv6_check_options(void); +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT +static int ipv6_demand_conf(int u); +#endif /* DEMAND_SUPPORT */ +#if PRINTPKT_SUPPORT +static int ipv6cp_printpkt(const u_char *p, int plen, + void (*printer)(void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ +#if DEMAND_SUPPORT +static int ipv6_active_pkt(u_char *pkt, int len); +#endif /* DEMAND_SUPPORT */ + +const struct protent ipv6cp_protent = { + PPP_IPV6CP, + ipv6cp_init, + ipv6cp_input, + ipv6cp_protrej, + ipv6cp_lowerup, + ipv6cp_lowerdown, + ipv6cp_open, + ipv6cp_close, +#if PRINTPKT_SUPPORT + ipv6cp_printpkt, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + NULL, +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + "IPV6CP", + "IPV6", +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + ipv6cp_option_list, + ipv6_check_options, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + ipv6_demand_conf, + ipv6_active_pkt +#endif /* DEMAND_SUPPORT */ +}; + +static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid); +#if 0 /* UNUSED */ +static void ipv6cp_script(char *)); +static void ipv6cp_script_done(void *)); +#endif /* UNUSED */ + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* length for RFC2023 compress opt. */ +#define CILEN_IFACEID 10 /* RFC2472, interface identifier */ + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + +#if 0 /* UNUSED */ +/* + * This state variable is used to ensure that we don't + * run an ipcp-up/down script while one is already running. + */ +static enum script_state { + s_down, + s_up, +} ipv6cp_script_state; +static pid_t ipv6cp_script_pid; +#endif /* UNUSED */ + +static char *llv6_ntoa(eui64_t ifaceid); + +#if PPP_OPTIONS +/* + * setifaceid - set the interface identifiers manually + */ +static int +setifaceid(argv) + char **argv; +{ + char *comma, *arg, c; + ipv6cp_options *wo = &ipv6cp_wantoptions[0]; + struct in6_addr addr; + static int prio_local, prio_remote; + +#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \ + (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) ) + + arg = *argv; + if ((comma = strchr(arg, ',')) == NULL) + comma = arg + strlen(arg); + + /* + * If comma first character, then no local identifier + */ + if (comma != arg) { + c = *comma; + *comma = '\0'; + + if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) { + option_error("Illegal interface identifier (local): %s", arg); + return 0; + } + + if (option_priority >= prio_local) { + eui64_copy(addr.s6_addr32[2], wo->ourid); + wo->opt_local = 1; + prio_local = option_priority; + } + *comma = c; + } + + /* + * If comma last character, the no remote identifier + */ + if (*comma != 0 && *++comma != '\0') { + if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) { + option_error("Illegal interface identifier (remote): %s", comma); + return 0; + } + if (option_priority >= prio_remote) { + eui64_copy(addr.s6_addr32[2], wo->hisid); + wo->opt_remote = 1; + prio_remote = option_priority; + } + } + + if (override_value("+ipv6", option_priority, option_source)) + ipv6cp_protent.enabled_flag = 1; + return 1; +} + +static void +printifaceid(opt, printer, arg) + option_t *opt; + void (*printer)(void *, char *, ...)); + void *arg; +{ + ipv6cp_options *wo = &ipv6cp_wantoptions[0]; + + if (wo->opt_local) + printer(arg, "%s", llv6_ntoa(wo->ourid)); + printer(arg, ","); + if (wo->opt_remote) + printer(arg, "%s", llv6_ntoa(wo->hisid)); +} +#endif /* PPP_OPTIONS */ + +/* + * Make a string representation of a network address. + */ +static char * +llv6_ntoa(eui64_t ifaceid) +{ + static char b[26]; + + sprintf(b, "fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x", + ifaceid.e8[0], ifaceid.e8[1], ifaceid.e8[2], ifaceid.e8[3], + ifaceid.e8[4], ifaceid.e8[5], ifaceid.e8[6], ifaceid.e8[7]); + + return b; +} + + +/* + * ipv6cp_init - Initialize IPV6CP. + */ +static void ipv6cp_init(ppp_pcb *pcb) { + fsm *f = &pcb->ipv6cp_fsm; + ipv6cp_options *wo = &pcb->ipv6cp_wantoptions; + ipv6cp_options *ao = &pcb->ipv6cp_allowoptions; + + f->pcb = pcb; + f->protocol = PPP_IPV6CP; + f->callbacks = &ipv6cp_callbacks; + fsm_init(f); + +#if 0 /* Not necessary, everything is cleared in ppp_new() */ + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); +#endif /* 0 */ + + wo->accept_local = 1; + wo->neg_ifaceid = 1; + ao->neg_ifaceid = 1; + +#ifdef IPV6CP_COMP + wo->neg_vj = 1; + ao->neg_vj = 1; + wo->vj_protocol = IPV6CP_COMP; +#endif + +} + + +/* + * ipv6cp_open - IPV6CP is allowed to come up. + */ +static void ipv6cp_open(ppp_pcb *pcb) { + fsm_open(&pcb->ipv6cp_fsm); +} + + +/* + * ipv6cp_close - Take IPV6CP down. + */ +static void ipv6cp_close(ppp_pcb *pcb, const char *reason) { + fsm_close(&pcb->ipv6cp_fsm, reason); +} + + +/* + * ipv6cp_lowerup - The lower layer is up. + */ +static void ipv6cp_lowerup(ppp_pcb *pcb) { + fsm_lowerup(&pcb->ipv6cp_fsm); +} + + +/* + * ipv6cp_lowerdown - The lower layer is down. + */ +static void ipv6cp_lowerdown(ppp_pcb *pcb) { + fsm_lowerdown(&pcb->ipv6cp_fsm); +} + + +/* + * ipv6cp_input - Input IPV6CP packet. + */ +static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len) { + fsm_input(&pcb->ipv6cp_fsm, p, len); +} + + +/* + * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void ipv6cp_protrej(ppp_pcb *pcb) { + fsm_lowerdown(&pcb->ipv6cp_fsm); +} + + +/* + * ipv6cp_resetci - Reset our CI. + */ +static void ipv6cp_resetci(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *wo = &pcb->ipv6cp_wantoptions; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + ipv6cp_options *ao = &pcb->ipv6cp_allowoptions; + + wo->req_ifaceid = wo->neg_ifaceid && ao->neg_ifaceid; + + if (!wo->opt_local) { + eui64_magic_nz(wo->ourid); + } + + *go = *wo; + eui64_zero(go->hisid); /* last proposed interface identifier */ +} + + +/* + * ipv6cp_cilen - Return length of our CI. + */ +static int ipv6cp_cilen(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + +#ifdef IPV6CP_COMP +#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0) +#endif /* IPV6CP_COMP */ +#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0) + + return (LENCIIFACEID(go->neg_ifaceid) + +#ifdef IPV6CP_COMP + LENCIVJ(go->neg_vj) + +#endif /* IPV6CP_COMP */ + 0); +} + + +/* + * ipv6cp_addci - Add our desired CIs to a packet. + */ +static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + int len = *lenp; + +#ifdef IPV6CP_COMP +#define ADDCIVJ(opt, neg, val) \ + if (neg) { \ + int vjlen = CILEN_COMPRESS; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + len -= vjlen; \ + } else \ + neg = 0; \ + } +#endif /* IPV6CP_COMP */ + +#define ADDCIIFACEID(opt, neg, val1) \ + if (neg) { \ + int idlen = CILEN_IFACEID; \ + if (len >= idlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(idlen, ucp); \ + eui64_put(val1, ucp); \ + len -= idlen; \ + } else \ + neg = 0; \ + } + + ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid); + +#ifdef IPV6CP_COMP + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol); +#endif /* IPV6CP_COMP */ + + *lenp -= len; +} + + +/* + * ipv6cp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int ipv6cp_ackci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + u_short cilen, citype; +#ifdef IPV6CP_COMP + u_short cishort; +#endif /* IPV6CP_COMP */ + eui64_t ifaceid; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#ifdef IPV6CP_COMP +#define ACKCIVJ(opt, neg, val) \ + if (neg) { \ + int vjlen = CILEN_COMPRESS; \ + if ((len -= vjlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#endif /* IPV6CP_COMP */ + +#define ACKCIIFACEID(opt, neg, val1) \ + if (neg) { \ + int idlen = CILEN_IFACEID; \ + if ((len -= idlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != idlen || \ + citype != opt) \ + goto bad; \ + eui64_get(ifaceid, p); \ + if (! eui64_equals(val1, ifaceid)) \ + goto bad; \ + } + + ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid); + +#ifdef IPV6CP_COMP + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol); +#endif /* IPV6CP_COMP */ + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); + +bad: + IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!")); + return (0); +} + +/* + * ipv6cp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPV6CP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + u_char citype, cilen, *next; +#ifdef IPV6CP_COMP + u_short cishort; +#endif /* IPV6CP_COMP */ + eui64_t ifaceid; + ipv6cp_options no; /* options we've seen Naks for */ + ipv6cp_options try_; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try_ = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIIFACEID(opt, neg, code) \ + if (go->neg && \ + len >= (cilen = CILEN_IFACEID) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + eui64_get(ifaceid, p); \ + no.neg = 1; \ + code \ + } + +#ifdef IPV6CP_COMP +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#endif /* IPV6CP_COMP */ + + /* + * Accept the peer's idea of {our,his} interface identifier, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIIFACEID(CI_IFACEID, neg_ifaceid, + if (treat_as_reject) { + try_.neg_ifaceid = 0; + } else if (go->accept_local) { + while (eui64_iszero(ifaceid) || + eui64_equals(ifaceid, go->hisid)) /* bad luck */ + eui64_magic(ifaceid); + try_.ourid = ifaceid; + IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid))); + } + ); + +#ifdef IPV6CP_COMP + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + { + if (cishort == IPV6CP_COMP && !treat_as_reject) { + try_.vj_protocol = cishort; + } else { + try_.neg_vj = 0; + } + } + ); +#endif /* IPV6CP_COMP */ + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about interface identifier, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len >= CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if ( cilen < CILEN_VOID || (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { +#ifdef IPV6CP_COMP + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_COMPRESS)) + goto bad; + no.neg_vj = 1; + break; +#endif /* IPV6CP_COMP */ + case CI_IFACEID: + if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID) + goto bad; + try_.neg_ifaceid = 1; + eui64_get(ifaceid, p); + if (go->accept_local) { + while (eui64_iszero(ifaceid) || + eui64_equals(ifaceid, go->hisid)) /* bad luck */ + eui64_magic(ifaceid); + try_.ourid = ifaceid; + } + no.neg_ifaceid = 1; + break; + default: + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) + goto bad; + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + + return 1; + +bad: + IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!")); + return 0; +} + + +/* + * ipv6cp_rejci - Reject some of our CIs. + */ +static int ipv6cp_rejci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + u_char cilen; +#ifdef IPV6CP_COMP + u_short cishort; +#endif /* IPV6CP_COMP */ + eui64_t ifaceid; + ipv6cp_options try_; /* options to request next time */ + + try_ = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIIFACEID(opt, neg, val1) \ + if (go->neg && \ + len >= (cilen = CILEN_IFACEID) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + eui64_get(ifaceid, p); \ + /* Check rejected value. */ \ + if (! eui64_equals(ifaceid, val1)) \ + goto bad; \ + try_.neg = 0; \ + } + +#ifdef IPV6CP_COMP +#define REJCIVJ(opt, neg, val) \ + if (go->neg && \ + p[1] == CILEN_COMPRESS && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + try_.neg = 0; \ + } +#endif /* IPV6CP_COMP */ + + REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid); + +#ifdef IPV6CP_COMP + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol); +#endif /* IPV6CP_COMP */ + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + return 1; + +bad: + IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!")); + return 0; +} + + +/* + * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + * + * inp = Requested CIs + * len = Length of requested CIs + * + */ +static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *wo = &pcb->ipv6cp_wantoptions; + ipv6cp_options *ho = &pcb->ipv6cp_hisoptions; + ipv6cp_options *ao = &pcb->ipv6cp_allowoptions; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ +#ifdef IPV6CP_COMP + u_short cishort; /* Parsed short value */ +#endif /* IPV6CP_COMP */ + eui64_t ifaceid; /* Parsed interface identifier */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_IFACEID: + IPV6CPDEBUG(("ipv6cp: received interface identifier ")); + + if (!ao->neg_ifaceid || + cilen != CILEN_IFACEID) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no interface identifier, or if we both have same + * identifier then NAK it with new idea. + * In particular, if we don't know his identifier, but he does, + * then accept it. + */ + eui64_get(ifaceid, p); + IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid))); + if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) { + orc = CONFREJ; /* Reject CI */ + break; + } + if (!eui64_iszero(wo->hisid) && + !eui64_equals(ifaceid, wo->hisid) && + eui64_iszero(go->hisid)) { + + orc = CONFNAK; + ifaceid = wo->hisid; + go->hisid = ifaceid; + DECPTR(sizeof(ifaceid), p); + eui64_put(ifaceid, p); + } else + if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) { + orc = CONFNAK; + if (eui64_iszero(go->hisid)) /* first time, try option */ + ifaceid = wo->hisid; + while (eui64_iszero(ifaceid) || + eui64_equals(ifaceid, go->ourid)) /* bad luck */ + eui64_magic(ifaceid); + go->hisid = ifaceid; + DECPTR(sizeof(ifaceid), p); + eui64_put(ifaceid, p); + } + + ho->neg_ifaceid = 1; + ho->hisid = ifaceid; + break; + +#ifdef IPV6CP_COMP + case CI_COMPRESSTYPE: + IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE ")); + if (!ao->neg_vj || + (cilen != CILEN_COMPRESS)) { + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + IPV6CPDEBUG(("(%d)", cishort)); + + if (!(cishort == IPV6CP_COMP)) { + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + break; +#endif /* IPV6CP_COMP */ + + default: + orc = CONFREJ; + break; + } + +endswitch: + IPV6CPDEBUG((" (%s)\n", CODENAME(orc))); + + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + MEMCPY(ucp, cip, cilen); /* Move it */ + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their identifier and they didn't send their identifier, then we + * send a NAK with a CI_IFACEID option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_ifaceid && + wo->req_ifaceid && !reject_if_disagree) { + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_ifaceid = 0; /* don't ask again */ + } + PUTCHAR(CI_IFACEID, ucp); + PUTCHAR(CILEN_IFACEID, ucp); + eui64_put(wo->hisid, ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc))); + return (rc); /* Return final code */ +} + +#if PPP_OPTIONS +/* + * ipv6_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void ipv6_check_options() { + ipv6cp_options *wo = &ipv6cp_wantoptions[0]; + + if (!ipv6cp_protent.enabled_flag) + return; + + /* + * Persistent link-local id is only used when user has not explicitly + * configure/hard-code the id + */ + if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) { + + /* + * On systems where there are no Ethernet interfaces used, there + * may be other ways to obtain a persistent id. Right now, it + * will fall back to using magic [see eui64_magic] below when + * an EUI-48 from MAC address can't be obtained. Other possibilities + * include obtaining EEPROM serial numbers, or some other unique + * yet persistent number. On Sparc platforms, this is possible, + * but too bad there's no standards yet for x86 machines. + */ + if (ether_to_eui64(&wo->ourid)) { + wo->opt_local = 1; + } + } + + if (!wo->opt_local) { /* init interface identifier */ + if (wo->use_ip && eui64_iszero(wo->ourid)) { + eui64_setlo32(wo->ourid, lwip_ntohl(ipcp_wantoptions[0].ouraddr)); + if (!eui64_iszero(wo->ourid)) + wo->opt_local = 1; + } + + while (eui64_iszero(wo->ourid)) + eui64_magic(wo->ourid); + } + + if (!wo->opt_remote) { + if (wo->use_ip && eui64_iszero(wo->hisid)) { + eui64_setlo32(wo->hisid, lwip_ntohl(ipcp_wantoptions[0].hisaddr)); + if (!eui64_iszero(wo->hisid)) + wo->opt_remote = 1; + } + } + + if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) { + option_error("local/remote LL address required for demand-dialling\n"); + exit(1); + } +} +#endif /* PPP_OPTIONS */ + +#if DEMAND_SUPPORT +/* + * ipv6_demand_conf - configure the interface as though + * IPV6CP were up, for use with dial-on-demand. + */ +static int ipv6_demand_conf(int u) { + ipv6cp_options *wo = &ipv6cp_wantoptions[u]; + + if (!sif6up(u)) + return 0; + + if (!sif6addr(u, wo->ourid, wo->hisid)) + return 0; + + if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE)) + return 0; + + ppp_notice("ipv6_demand_conf"); + ppp_notice("local LL address %s", llv6_ntoa(wo->ourid)); + ppp_notice("remote LL address %s", llv6_ntoa(wo->hisid)); + + return 1; +} +#endif /* DEMAND_SUPPORT */ + + +/* + * ipv6cp_up - IPV6CP has come UP. + * + * Configure the IPv6 network interface appropriately and bring it up. + */ +static void ipv6cp_up(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *wo = &pcb->ipv6cp_wantoptions; + ipv6cp_options *ho = &pcb->ipv6cp_hisoptions; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + + IPV6CPDEBUG(("ipv6cp: up")); + + /* + * We must have a non-zero LL address for both ends of the link. + */ + if (!ho->neg_ifaceid) + ho->hisid = wo->hisid; + +#if 0 /* UNUSED */ + if(!no_ifaceid_neg) { +#endif /* UNUSED */ + if (eui64_iszero(ho->hisid)) { + ppp_error("Could not determine remote LL address"); + ipv6cp_close(f->pcb, "Could not determine remote LL address"); + return; + } + if (eui64_iszero(go->ourid)) { + ppp_error("Could not determine local LL address"); + ipv6cp_close(f->pcb, "Could not determine local LL address"); + return; + } + if (eui64_equals(go->ourid, ho->hisid)) { + ppp_error("local and remote LL addresses are equal"); + ipv6cp_close(f->pcb, "local and remote LL addresses are equal"); + return; + } +#if 0 /* UNUSED */ + } +#endif /* UNUSED */ +#if 0 /* UNUSED */ + script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0); + script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0); +#endif /* UNUSED */ + +#ifdef IPV6CP_COMP + /* set tcp compression */ + sif6comp(f->unit, ho->neg_vj); +#endif + +#if DEMAND_SUPPORT + /* + * If we are doing dial-on-demand, the interface is already + * configured, so we put out any saved-up packets, then set the + * interface to pass IPv6 packets. + */ + if (demand) { + if (! eui64_equals(go->ourid, wo->ourid) || + ! eui64_equals(ho->hisid, wo->hisid)) { + if (! eui64_equals(go->ourid, wo->ourid)) + warn("Local LL address changed to %s", + llv6_ntoa(go->ourid)); + if (! eui64_equals(ho->hisid, wo->hisid)) + warn("Remote LL address changed to %s", + llv6_ntoa(ho->hisid)); + ipv6cp_clear_addrs(f->pcb, go->ourid, ho->hisid); + + /* Set the interface to the new addresses */ + if (!sif6addr(f->pcb, go->ourid, ho->hisid)) { + if (debug) + warn("sif6addr failed"); + ipv6cp_close(f->unit, "Interface configuration failed"); + return; + } + + } + demand_rexmit(PPP_IPV6); + sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS); + + } else +#endif /* DEMAND_SUPPORT */ + { + /* + * Set LL addresses + */ + if (!sif6addr(f->pcb, go->ourid, ho->hisid)) { + PPPDEBUG(LOG_DEBUG, ("sif6addr failed")); + ipv6cp_close(f->pcb, "Interface configuration failed"); + return; + } + + /* bring the interface up for IPv6 */ + if (!sif6up(f->pcb)) { + PPPDEBUG(LOG_DEBUG, ("sif6up failed (IPV6)")); + ipv6cp_close(f->pcb, "Interface configuration failed"); + return; + } +#if DEMAND_SUPPORT + sifnpmode(f->pcb, PPP_IPV6, NPMODE_PASS); +#endif /* DEMAND_SUPPORT */ + + ppp_notice("local LL address %s", llv6_ntoa(go->ourid)); + ppp_notice("remote LL address %s", llv6_ntoa(ho->hisid)); + } + + np_up(f->pcb, PPP_IPV6); + pcb->ipv6cp_is_up = 1; + +#if 0 /* UNUSED */ + /* + * Execute the ipv6-up script, like this: + * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL + */ + if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) { + ipv6cp_script_state = s_up; + ipv6cp_script(_PATH_IPV6UP); + } +#endif /* UNUSED */ +} + + +/* + * ipv6cp_down - IPV6CP has gone DOWN. + * + * Take the IPv6 network interface down, clear its addresses + * and delete routes through it. + */ +static void ipv6cp_down(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + ipv6cp_options *ho = &pcb->ipv6cp_hisoptions; + + IPV6CPDEBUG(("ipv6cp: down")); +#if PPP_STATS_SUPPORT + update_link_stats(f->unit); +#endif /* PPP_STATS_SUPPORT */ + if (pcb->ipv6cp_is_up) { + pcb->ipv6cp_is_up = 0; + np_down(f->pcb, PPP_IPV6); + } +#ifdef IPV6CP_COMP + sif6comp(f->unit, 0); +#endif + +#if DEMAND_SUPPORT + /* + * If we are doing dial-on-demand, set the interface + * to queue up outgoing packets (for now). + */ + if (demand) { + sifnpmode(f->pcb, PPP_IPV6, NPMODE_QUEUE); + } else +#endif /* DEMAND_SUPPORT */ + { +#if DEMAND_SUPPORT + sifnpmode(f->pcb, PPP_IPV6, NPMODE_DROP); +#endif /* DEMAND_SUPPORT */ + ipv6cp_clear_addrs(f->pcb, + go->ourid, + ho->hisid); + sif6down(f->pcb); + } + +#if 0 /* UNUSED */ + /* Execute the ipv6-down script */ + if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) { + ipv6cp_script_state = s_down; + ipv6cp_script(_PATH_IPV6DOWN); + } +#endif /* UNUSED */ +} + + +/* + * ipv6cp_clear_addrs() - clear the interface addresses, routes, + * proxy neighbour discovery entries, etc. + */ +static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid) { + cif6addr(pcb, ourid, hisid); +} + + +/* + * ipv6cp_finished - possibly shut down the lower layers. + */ +static void ipv6cp_finished(fsm *f) { + np_finished(f->pcb, PPP_IPV6); +} + + +#if 0 /* UNUSED */ +/* + * ipv6cp_script_done - called when the ipv6-up or ipv6-down script + * has finished. + */ +static void +ipv6cp_script_done(arg) + void *arg; +{ + ipv6cp_script_pid = 0; + switch (ipv6cp_script_state) { + case s_up: + if (ipv6cp_fsm[0].state != PPP_FSM_OPENED) { + ipv6cp_script_state = s_down; + ipv6cp_script(_PATH_IPV6DOWN); + } + break; + case s_down: + if (ipv6cp_fsm[0].state == PPP_FSM_OPENED) { + ipv6cp_script_state = s_up; + ipv6cp_script(_PATH_IPV6UP); + } + break; + } +} + + +/* + * ipv6cp_script - Execute a script with arguments + * interface-name tty-name speed local-LL remote-LL. + */ +static void +ipv6cp_script(script) + char *script; +{ + char strspeed[32], strlocal[32], strremote[32]; + char *argv[8]; + + sprintf(strspeed, "%d", baud_rate); + strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid)); + strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid)); + + argv[0] = script; + argv[1] = ifname; + argv[2] = devnam; + argv[3] = strspeed; + argv[4] = strlocal; + argv[5] = strremote; + argv[6] = ipparam; + argv[7] = NULL; + + ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, + NULL, 0); +} +#endif /* UNUSED */ + +#if PRINTPKT_SUPPORT +/* + * ipv6cp_printpkt - print the contents of an IPV6CP packet. + */ +static const char* const ipv6cp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej" +}; + +static int ipv6cp_printpkt(const u_char *p, int plen, + void (*printer)(void *, const char *, ...), void *arg) { + int code, id, len, olen; + const u_char *pstart, *optend; +#ifdef IPV6CP_COMP + u_short cishort; +#endif /* IPV6CP_COMP */ + eui64_t ifaceid; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(ipv6cp_codenames)) + printer(arg, " %s", ipv6cp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { +#ifdef IPV6CP_COMP + case CI_COMPRESSTYPE: + if (olen >= CILEN_COMPRESS) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "compress "); + printer(arg, "0x%x", cishort); + } + break; +#endif /* IPV6CP_COMP */ + case CI_IFACEID: + if (olen == CILEN_IFACEID) { + p += 2; + eui64_get(ifaceid, p); + printer(arg, "addr %s", llv6_ntoa(ifaceid)); + } + break; + default: + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + ppp_print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + default: + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} +#endif /* PRINTPKT_SUPPORT */ + +#if DEMAND_SUPPORT +/* + * ipv6_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP6_HDRLEN 40 /* bytes */ +#define IP6_NHDR_FRAG 44 /* fragment IPv6 header */ +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define get_ip6nh(x) (((unsigned char *)(x))[6]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int ipv6_active_pkt(u_char *pkt, int len) { + u_char *tcp; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP6_HDRLEN) + return 0; + if (get_ip6nh(pkt) == IP6_NHDR_FRAG) + return 0; + if (get_ip6nh(pkt) != IPPROTO_TCP) + return 1; + if (len < IP6_HDRLEN + TCP_HDRLEN) + return 0; + tcp = pkt + IP6_HDRLEN; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4) + return 0; + return 1; +} +#endif /* DEMAND_SUPPORT */ + +#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/lcp.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/lcp.c new file mode 100644 index 0000000..90ed183 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/lcp.c @@ -0,0 +1,2790 @@ +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * @todo: + */ + +#if 0 /* UNUSED */ +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" +#if CHAP_SUPPORT +#include "netif/ppp/chap-new.h" +#endif /* CHAP_SUPPORT */ +#include "netif/ppp/magic.h" + +/* + * When the link comes up we want to be able to wait for a short while, + * or until seeing some input from the peer, before starting to send + * configure-requests. We do this by delaying the fsm_lowerup call. + */ +/* steal a bit in fsm flags word */ +#define DELAYED_UP 0x80 + +static void lcp_delayed_up(void *arg); + +/* + * LCP-related command-line options. + */ +#if 0 /* UNUSED */ +int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ +int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* options */ +static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ +static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +#if PPP_LCP_ADAPTIVE +bool lcp_echo_adaptive = 0; /* request echo only if the link was idle */ +#endif +bool lax_recv = 0; /* accept control chars in asyncmap */ +bool noendpoint = 0; /* don't send/accept endpoint discriminator */ +#endif /* UNUSED */ + +#if PPP_OPTIONS +static int noopt (char **); +#endif /* PPP_OPTIONS */ + +#ifdef HAVE_MULTILINK +static int setendpoint (char **); +static void printendpoint (option_t *, void (*)(void *, char *, ...), + void *); +#endif /* HAVE_MULTILINK */ + +#if PPP_OPTIONS +static option_t lcp_option_list[] = { + /* LCP options */ + { "-all", o_special_noarg, (void *)noopt, + "Don't request/allow any LCP options" }, + + { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression, + "Disable address/control compression", + OPT_A2CLR, &lcp_allowoptions[0].neg_accompression }, + { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression, + "Disable address/control compression", + OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_accompression }, + + { "asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap, + "Set asyncmap (for received packets)", + OPT_OR, &lcp_wantoptions[0].neg_asyncmap }, + { "-as", o_uint32, &lcp_wantoptions[0].asyncmap, + "Set asyncmap (for received packets)", + OPT_ALIAS | OPT_OR, &lcp_wantoptions[0].neg_asyncmap }, + { "default-asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap, + "Disable asyncmap negotiation", + OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR, + &lcp_allowoptions[0].neg_asyncmap }, + { "-am", o_uint32, &lcp_wantoptions[0].asyncmap, + "Disable asyncmap negotiation", + OPT_ALIAS | OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR, + &lcp_allowoptions[0].neg_asyncmap }, + + { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber, + "Disable magic number negotiation (looped-back line detection)", + OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber }, + { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber, + "Disable magic number negotiation (looped-back line detection)", + OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber }, + + { "mru", o_int, &lcp_wantoptions[0].mru, + "Set MRU (maximum received packet size) for negotiation", + OPT_PRIO, &lcp_wantoptions[0].neg_mru }, + { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru, + "Disable MRU negotiation (use default 1500)", + OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru }, + { "-mru", o_bool, &lcp_wantoptions[0].neg_mru, + "Disable MRU negotiation (use default 1500)", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru }, + + { "mtu", o_int, &lcp_allowoptions[0].mru, + "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU }, + + { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression, + "Disable protocol field compression", + OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression }, + { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression, + "Disable protocol field compression", + OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression }, + + { "passive", o_bool, &lcp_wantoptions[0].passive, + "Set passive mode", 1 }, + { "-p", o_bool, &lcp_wantoptions[0].passive, + "Set passive mode", OPT_ALIAS | 1 }, + + { "silent", o_bool, &lcp_wantoptions[0].silent, + "Set silent mode", 1 }, + + { "lcp-echo-failure", o_int, &lcp_echo_fails, + "Set number of consecutive echo failures to indicate link failure", + OPT_PRIO }, + { "lcp-echo-interval", o_int, &lcp_echo_interval, + "Set time in seconds between LCP echo requests", OPT_PRIO }, +#if PPP_LCP_ADAPTIVE + { "lcp-echo-adaptive", o_bool, &lcp_echo_adaptive, + "Suppress LCP echo requests if traffic was received", 1 }, +#endif + { "lcp-restart", o_int, &lcp_fsm[0].timeouttime, + "Set time in seconds between LCP retransmissions", OPT_PRIO }, + { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits, + "Set maximum number of LCP terminate-request transmissions", OPT_PRIO }, + { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits, + "Set maximum number of LCP configure-request transmissions", OPT_PRIO }, + { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops, + "Set limit on number of LCP configure-naks", OPT_PRIO }, + + { "receive-all", o_bool, &lax_recv, + "Accept all received control characters", 1 }, + +#ifdef HAVE_MULTILINK + { "mrru", o_int, &lcp_wantoptions[0].mrru, + "Maximum received packet size for multilink bundle", + OPT_PRIO, &lcp_wantoptions[0].neg_mrru }, + + { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf, + "Use short sequence numbers in multilink headers", + OPT_PRIO | 1, &lcp_allowoptions[0].neg_ssnhf }, + { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf, + "Don't use short sequence numbers in multilink headers", + OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_ssnhf }, + + { "endpoint", o_special, (void *) setendpoint, + "Endpoint discriminator for multilink", + OPT_PRIO | OPT_A2PRINTER, (void *) printendpoint }, +#endif /* HAVE_MULTILINK */ + + { "noendpoint", o_bool, &noendpoint, + "Don't send or accept multilink endpoint discriminator", 1 }, + + {NULL} +}; +#endif /* PPP_OPTIONS */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci(fsm *f); /* Reset our CI */ +static int lcp_cilen(fsm *f); /* Return length of our CI */ +static void lcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI to pkt */ +static int lcp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */ +static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */ +static int lcp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */ +static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree); /* Rcv peer CI */ +static void lcp_up(fsm *f); /* We're UP */ +static void lcp_down(fsm *f); /* We're DOWN */ +static void lcp_starting (fsm *); /* We need lower layer up */ +static void lcp_finished (fsm *); /* We need lower layer down */ +static int lcp_extcode(fsm *f, int code, int id, u_char *inp, int len); +static void lcp_rprotrej(fsm *f, u_char *inp, int len); + +/* + * routines to send LCP echos to peer + */ + +static void lcp_echo_lowerup(ppp_pcb *pcb); +static void lcp_echo_lowerdown(ppp_pcb *pcb); +static void LcpEchoTimeout(void *arg); +static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len); +static void LcpSendEchoRequest(fsm *f); +static void LcpLinkFailure(fsm *f); +static void LcpEchoCheck(fsm *f); + +static const fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches OPENED state */ + lcp_down, /* Called when fsm leaves OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +/* + * Protocol entry points. + * Some of these are called directly. + */ + +static void lcp_init(ppp_pcb *pcb); +static void lcp_input(ppp_pcb *pcb, u_char *p, int len); +static void lcp_protrej(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int lcp_printpkt(const u_char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ + +const struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, +#if PRINTPKT_SUPPORT + lcp_printpkt, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + NULL, +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + "LCP", + NULL, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + lcp_option_list, + NULL, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + 2 */ +#if CHAP_SUPPORT +#define CILEN_CHAP 5 /* CILEN_VOID + 2 + 1 */ +#endif /* CHAP_SUPPORT */ +#define CILEN_LONG 6 /* CILEN_VOID + 4 */ +#if LQR_SUPPORT +#define CILEN_LQR 8 /* CILEN_VOID + 2 + 4 */ +#endif /* LQR_SUPPORT */ +#define CILEN_CBCP 3 + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + +#if PPP_OPTIONS +/* + * noopt - Disable all options (why?). + */ +static int +noopt(argv) + char **argv; +{ + BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options)); + BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options)); + + return (1); +} +#endif /* PPP_OPTIONS */ + +#ifdef HAVE_MULTILINK +static int +setendpoint(argv) + char **argv; +{ + if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) { + lcp_wantoptions[0].neg_endpoint = 1; + return 1; + } + option_error("Can't parse '%s' as an endpoint discriminator", *argv); + return 0; +} + +static void +printendpoint(opt, printer, arg) + option_t *opt; + void (*printer) (void *, char *, ...); + void *arg; +{ + printer(arg, "%s", epdisc_to_str(&lcp_wantoptions[0].endpoint)); +} +#endif /* HAVE_MULTILINK */ + +/* + * lcp_init - Initialize LCP. + */ +static void lcp_init(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + + f->pcb = pcb; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + BZERO(wo, sizeof(*wo)); + wo->neg_mru = 1; + wo->mru = PPP_DEFMRU; + wo->neg_asyncmap = 1; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + + BZERO(ao, sizeof(*ao)); + ao->neg_mru = 1; + ao->mru = PPP_MAXMRU; + ao->neg_asyncmap = 1; +#if CHAP_SUPPORT + ao->neg_chap = 1; + ao->chap_mdtype = CHAP_MDTYPE_SUPPORTED; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + ao->neg_upap = 1; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + ao->neg_eap = 1; +#endif /* EAP_SUPPORT */ + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_endpoint = 1; +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void lcp_open(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + lcp_options *wo = &pcb->lcp_wantoptions; + + f->flags &= ~(OPT_PASSIVE | OPT_SILENT); + if (wo->passive) + f->flags |= OPT_PASSIVE; + if (wo->silent) + f->flags |= OPT_SILENT; + fsm_open(f); +} + + +/* + * lcp_close - Take LCP down. + */ +void lcp_close(ppp_pcb *pcb, const char *reason) { + fsm *f = &pcb->lcp_fsm; + int oldstate; + + if (pcb->phase != PPP_PHASE_DEAD +#ifdef HAVE_MULTILINK + && pcb->phase != PPP_PHASE_MASTER +#endif /* HAVE_MULTILINK */ + ) + new_phase(pcb, PPP_PHASE_TERMINATE); + + if (f->flags & DELAYED_UP) { + UNTIMEOUT(lcp_delayed_up, f); + f->state = PPP_FSM_STOPPED; + } + oldstate = f->state; + + fsm_close(f, reason); + if (oldstate == PPP_FSM_STOPPED && (f->flags & (OPT_PASSIVE|OPT_SILENT|DELAYED_UP))) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do a + * lcp_close() when a connection hasn't been established + * because we are in passive/silent mode or because we have + * delayed the fsm_lowerup() call and it hasn't happened yet. + */ + f->flags &= ~DELAYED_UP; + lcp_finished(f); + } +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void lcp_lowerup(ppp_pcb *pcb) { + lcp_options *wo = &pcb->lcp_wantoptions; + fsm *f = &pcb->lcp_fsm; + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + if (ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0) < 0 + || ppp_recv_config(pcb, PPP_MRU, (pcb->settings.lax_recv? 0: 0xffffffff), + wo->neg_pcompression, wo->neg_accompression) < 0) + return; + pcb->peer_mru = PPP_MRU; + + if (pcb->settings.listen_time != 0) { + f->flags |= DELAYED_UP; + TIMEOUTMS(lcp_delayed_up, f, pcb->settings.listen_time); + } else + fsm_lowerup(f); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void lcp_lowerdown(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + + if (f->flags & DELAYED_UP) { + f->flags &= ~DELAYED_UP; + UNTIMEOUT(lcp_delayed_up, f); + } else + fsm_lowerdown(f); +} + + +/* + * lcp_delayed_up - Bring the lower layer up now. + */ +static void lcp_delayed_up(void *arg) { + fsm *f = (fsm*)arg; + + if (f->flags & DELAYED_UP) { + f->flags &= ~DELAYED_UP; + fsm_lowerup(f); + } +} + + +/* + * lcp_input - Input LCP packet. + */ +static void lcp_input(ppp_pcb *pcb, u_char *p, int len) { + fsm *f = &pcb->lcp_fsm; + + if (f->flags & DELAYED_UP) { + f->flags &= ~DELAYED_UP; + UNTIMEOUT(lcp_delayed_up, f); + fsm_lowerup(f); + } + fsm_input(f, p, len); +} + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int lcp_extcode(fsm *f, int code, int id, u_char *inp, int len) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u_char *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != PPP_FSM_OPENED) + break; + magp = inp; + PUTLONG(go->magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + case IDENTIF: + case TIMEREM: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void lcp_rprotrej(fsm *f, u_char *inp, int len) { + int i; + const struct protent *protp; + u_short prot; +#if PPP_PROTOCOLNAME + const char *pname; +#endif /* PPP_PROTOCOLNAME */ + + if (len < 2) { + LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!")); + return; + } + + GETSHORT(prot, inp); + + /* + * Protocol-Reject packets received in any state other than the LCP + * OPENED state SHOULD be silently discarded. + */ + if( f->state != PPP_FSM_OPENED ){ + LCPDEBUG(("Protocol-Reject discarded: LCP in state %d", f->state)); + return; + } + +#if PPP_PROTOCOLNAME + pname = protocol_name(prot); +#endif /* PPP_PROTOCOLNAME */ + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol == prot) { +#if PPP_PROTOCOLNAME + if (pname != NULL) + ppp_dbglog("Protocol-Reject for '%s' (0x%x) received", pname, + prot); + else +#endif /* PPP_PROTOCOLNAME */ + ppp_dbglog("Protocol-Reject for 0x%x received", prot); + (*protp->protrej)(f->pcb); + return; + } + +#if PPP_PROTOCOLNAME + if (pname != NULL) + ppp_warn("Protocol-Reject for unsupported protocol '%s' (0x%x)", pname, + prot); + else +#endif /* #if PPP_PROTOCOLNAME */ + ppp_warn("Protocol-Reject for unsupported protocol 0x%x", prot); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +/*ARGSUSED*/ +static void lcp_protrej(ppp_pcb *pcb) { + /* + * Can't reject LCP! + */ + ppp_error("Received Protocol-Reject for LCP!"); + fsm_protreject(&pcb->lcp_fsm); +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len) { + fsm *f = &pcb->lcp_fsm; + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the OPENED state. + */ +#if 0 + p += 2; + len -= 2; +#endif + + fsm_sdata(f, PROTREJ, ++f->id, + p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void lcp_resetci(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + +#if PPP_AUTH_SUPPORT + + /* note: default value is true for allow options */ + if (pcb->settings.user && pcb->settings.passwd) { +#if PAP_SUPPORT + if (pcb->settings.refuse_pap) { + ao->neg_upap = 0; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (pcb->settings.refuse_chap) { + ao->chap_mdtype &= ~MDTYPE_MD5; + } +#if MSCHAP_SUPPORT + if (pcb->settings.refuse_mschap) { + ao->chap_mdtype &= ~MDTYPE_MICROSOFT; + } + if (pcb->settings.refuse_mschap_v2) { + ao->chap_mdtype &= ~MDTYPE_MICROSOFT_V2; + } +#endif /* MSCHAP_SUPPORT */ + ao->neg_chap = (ao->chap_mdtype != MDTYPE_NONE); +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + if (pcb->settings.refuse_eap) { + ao->neg_eap = 0; + } +#endif /* EAP_SUPPORT */ + +#if PPP_SERVER + /* note: default value is false for wanted options */ + if (pcb->settings.auth_required) { +#if PAP_SUPPORT + if (!pcb->settings.refuse_pap) { + wo->neg_upap = 1; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (!pcb->settings.refuse_chap) { + wo->chap_mdtype |= MDTYPE_MD5; + } +#if MSCHAP_SUPPORT + if (!pcb->settings.refuse_mschap) { + wo->chap_mdtype |= MDTYPE_MICROSOFT; + } + if (!pcb->settings.refuse_mschap_v2) { + wo->chap_mdtype |= MDTYPE_MICROSOFT_V2; + } +#endif /* MSCHAP_SUPPORT */ + wo->neg_chap = (wo->chap_mdtype != MDTYPE_NONE); +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + if (!pcb->settings.refuse_eap) { + wo->neg_eap = 1; + } +#endif /* EAP_SUPPORT */ + } +#endif /* PPP_SERVER */ + + } else { +#if PAP_SUPPORT + ao->neg_upap = 0; +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + ao->neg_chap = 0; + ao->chap_mdtype = MDTYPE_NONE; +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + ao->neg_eap = 0; +#endif /* EAP_SUPPORT */ + } + + PPPDEBUG(LOG_DEBUG, ("ppp: auth protocols:")); +#if PAP_SUPPORT + PPPDEBUG(LOG_DEBUG, (" PAP=%d", ao->neg_upap)); +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + PPPDEBUG(LOG_DEBUG, (" CHAP=%d CHAP_MD5=%d", ao->neg_chap, !!(ao->chap_mdtype&MDTYPE_MD5))); +#if MSCHAP_SUPPORT + PPPDEBUG(LOG_DEBUG, (" CHAP_MS=%d CHAP_MS2=%d", !!(ao->chap_mdtype&MDTYPE_MICROSOFT), !!(ao->chap_mdtype&MDTYPE_MICROSOFT_V2))); +#endif /* MSCHAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + PPPDEBUG(LOG_DEBUG, (" EAP=%d", ao->neg_eap)); +#endif /* EAP_SUPPORT */ + PPPDEBUG(LOG_DEBUG, ("\n")); + +#endif /* PPP_AUTH_SUPPORT */ + + wo->magicnumber = magic(); + wo->numloops = 0; + *go = *wo; +#ifdef HAVE_MULTILINK + if (!multilink) { + go->neg_mrru = 0; +#endif /* HAVE_MULTILINK */ + go->neg_ssnhf = 0; + go->neg_endpoint = 0; +#ifdef HAVE_MULTILINK + } +#endif /* HAVE_MULTILINK */ + if (pcb->settings.noendpoint) + ao->neg_endpoint = 0; + pcb->peer_mru = PPP_MRU; +#if 0 /* UNUSED */ + auth_reset(pcb); +#endif /* UNUSED */ +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int lcp_cilen(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#if CHAP_SUPPORT +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#endif /* CHAP_SUPPORT */ +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#if LQR_SUPPORT +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#endif /* LQR_SUPPORT */ +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will + * accept more than one. We prefer EAP first, then CHAP, then + * PAP. + */ + return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + +#if EAP_SUPPORT + LENCISHORT(go->neg_eap) + +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT + LENCICHAP(!go->neg_eap && go->neg_chap) + +#endif /* EAP_SUPPORT */ +#if !EAP_SUPPORT + LENCICHAP(go->neg_chap) + +#endif /* !EAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT && CHAP_SUPPORT + LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) + +#endif /* EAP_SUPPORT && CHAP_SUPPORT */ +#if EAP_SUPPORT && !CHAP_SUPPORT + LENCISHORT(!go->neg_eap && go->neg_upap) + +#endif /* EAP_SUPPORT && !CHAP_SUPPORT */ +#if !EAP_SUPPORT && CHAP_SUPPORT + LENCISHORT(!go->neg_chap && go->neg_upap) + +#endif /* !EAP_SUPPORT && CHAP_SUPPORT */ +#if !EAP_SUPPORT && !CHAP_SUPPORT + LENCISHORT(go->neg_upap) + +#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ +#endif /* PAP_SUPPORT */ +#if LQR_SUPPORT + LENCILQR(go->neg_lqr) + +#endif /* LQR_SUPPORT */ + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression) + +#ifdef HAVE_MULTILINK + LENCISHORT(go->neg_mrru) + +#endif /* HAVE_MULTILINK */ + LENCIVOID(go->neg_ssnhf) + + (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void lcp_addci(fsm *f, u_char *ucp, int *lenp) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#if CHAP_SUPPORT +#define ADDCICHAP(opt, neg, val) \ + if (neg) { \ + PUTCHAR((opt), ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(PPP_CHAP, ucp); \ + PUTCHAR((CHAP_DIGEST(val)), ucp); \ + } +#endif /* CHAP_SUPPORT */ +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#if LQR_SUPPORT +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#endif /* LQR_SUPPORT */ +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } +#define ADDCIENDP(opt, neg, class, val, len) \ + if (neg) { \ + int i; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR + len, ucp); \ + PUTCHAR(class, ucp); \ + for (i = 0; i < len; ++i) \ + PUTCHAR(val[i], ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, + go->asyncmap); +#if EAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP); +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT + ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype); +#endif /* EAP_SUPPORT */ +#if !EAP_SUPPORT + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype); +#endif /* !EAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT && CHAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP); +#endif /* EAP_SUPPORT && CHAP_SUPPORT */ +#if EAP_SUPPORT && !CHAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP); +#endif /* EAP_SUPPORT && !CHAP_SUPPORT */ +#if !EAP_SUPPORT && CHAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); +#endif /* !EAP_SUPPORT && CHAP_SUPPORT */ +#if !EAP_SUPPORT && !CHAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP); +#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ +#endif /* PAP_SUPPORT */ +#if LQR_SUPPORT + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); +#endif /* LQR_SUPPORT */ + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); +#ifdef HAVE_MULTILINK + ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru); +#endif + ADDCIVOID(CI_SSNHF, go->neg_ssnhf); + ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_, + go->endpoint.value, go->endpoint.length); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + ppp_error("Bug in lcp_addci: wrong length"); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int lcp_ackci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u_char cilen, citype, cichar; + u_short cishort; + u32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || \ + citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || \ + citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#if CHAP_SUPPORT +#define ACKCICHAP(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || \ + citype != (opt)) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_CHAP) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != (CHAP_DIGEST(val))) \ + goto bad; \ + } +#endif /* CHAP_SUPPORT */ +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || \ + citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#if LQR_SUPPORT +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#endif /* LQR_SUPPORT */ +#define ACKCIENDP(opt, neg, class, val, vlen) \ + if (neg) { \ + int i; \ + if ((len -= CILEN_CHAR + vlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR + vlen || \ + citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != class) \ + goto bad; \ + for (i = 0; i < vlen; ++i) { \ + GETCHAR(cichar, p); \ + if (cichar != val[i]) \ + goto bad; \ + } \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, + go->asyncmap); +#if EAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP); +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT + ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype); +#endif /* EAP_SUPPORT */ +#if !EAP_SUPPORT + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype); +#endif /* !EAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT && CHAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP); +#endif /* EAP_SUPPORT && CHAP_SUPPORT */ +#if EAP_SUPPORT && !CHAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP); +#endif /* EAP_SUPPORT && !CHAP_SUPPORT */ +#if !EAP_SUPPORT && CHAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); +#endif /* !EAP_SUPPORT && CHAP_SUPPORT */ +#if !EAP_SUPPORT && !CHAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP); +#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ +#endif /* PAP_SUPPORT */ +#if LQR_SUPPORT + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); +#endif /* LQR_SUPPORT */ + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); +#ifdef HAVE_MULTILINK + ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru); +#endif /* HAVE_MULTILINK */ + ACKCIVOID(CI_SSNHF, go->neg_ssnhf); + ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_, + go->endpoint.value, go->endpoint.length); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); +bad: + LCPDEBUG(("lcp_acki: received bad Ack!")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *wo = &pcb->lcp_wantoptions; + u_char citype, cichar, *next; + u_short cishort; + u32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try_; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try_ = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + try_.neg = 0; \ + } +#if CHAP_SUPPORT +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#endif /* CHAP_SUPPORT */ +#define NAKCICHAR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#if LQR_SUPPORT +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#endif /* LQR_SUPPORT */ +#define NAKCIENDP(opt, neg) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[0] == opt && \ + p[1] >= CILEN_CHAR && \ + p[1] <= len) { \ + len -= p[1]; \ + INCPTR(p[1], p); \ + no.neg = 1; \ + try_.neg = 0; \ + } + + /* + * NOTE! There must be no assignments to individual fields of *go in + * the code below. Any such assignment is a BUG! + */ + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != PPP_DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort <= PPP_DEFMRU) + try_.mru = cishort; + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try_.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((0 +#if CHAP_SUPPORT + || go->neg_chap +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + || go->neg_upap +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + || go->neg_eap +#endif /* EAP_SUPPORT */ + ) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; +#if CHAP_SUPPORT + no.neg_chap = go->neg_chap; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + no.neg_upap = go->neg_upap; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + no.neg_eap = go->neg_eap; +#endif /* EAP_SUPPORT */ + INCPTR(2, p); + GETSHORT(cishort, p); + +#if PAP_SUPPORT + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { +#if EAP_SUPPORT + /* If we were asking for EAP, then we need to stop that. */ + if (go->neg_eap) + try_.neg_eap = 0; + else +#endif /* EAP_SUPPORT */ + +#if CHAP_SUPPORT + /* If we were asking for CHAP, then we need to stop that. */ + if (go->neg_chap) + try_.neg_chap = 0; + else +#endif /* CHAP_SUPPORT */ + + /* + * If we weren't asking for CHAP or EAP, then we were asking for + * PAP, in which case this Nak is bad. + */ + goto bad; + } else +#endif /* PAP_SUPPORT */ + +#if CHAP_SUPPORT + if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); +#if EAP_SUPPORT + /* Stop asking for EAP, if we were. */ + if (go->neg_eap) { + try_.neg_eap = 0; + /* Try to set up to use their suggestion, if possible */ + if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) + try_.chap_mdtype = CHAP_MDTYPE_D(cichar); + } else +#endif /* EAP_SUPPORT */ + if (go->neg_chap) { + /* + * We were asking for our preferred algorithm, they must + * want something different. + */ + if (cichar != CHAP_DIGEST(go->chap_mdtype)) { + if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) { + /* Use their suggestion if we support it ... */ + try_.chap_mdtype = CHAP_MDTYPE_D(cichar); + } else { + /* ... otherwise, try our next-preferred algorithm. */ + try_.chap_mdtype &= ~(CHAP_MDTYPE(try_.chap_mdtype)); + if (try_.chap_mdtype == MDTYPE_NONE) /* out of algos */ + try_.neg_chap = 0; + } + } else { + /* + * Whoops, they Nak'd our algorithm of choice + * but then suggested it back to us. + */ + goto bad; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ +#if PAP_SUPPORT + try_.neg_upap = 0; +#endif /* PAP_SUPPORT */ + } + + } else +#endif /* CHAP_SUPPORT */ + { + +#if EAP_SUPPORT + /* + * If we were asking for EAP, and they're Conf-Naking EAP, + * well, that's just strange. Nobody should do that. + */ + if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap) + ppp_dbglog("Unexpected Conf-Nak for EAP"); + + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_eap) + try_.neg_eap = 0; + else +#endif /* EAP_SUPPORT */ + +#if CHAP_SUPPORT + if (go->neg_chap) + try_.neg_chap = 0; + else +#endif /* CHAP_SUPPORT */ + +#if PAP_SUPPORT + if(1) + try_.neg_upap = 0; + else +#endif /* PAP_SUPPORT */ + {} + + p += cilen - CILEN_SHORT; + } + } + +#if LQR_SUPPORT + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) + try_.neg_lqr = 0; + else + try_.lqr_period = cilong; + ); +#endif /* LQR_SUPPORT */ + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try_.neg_cbcp = 0; + (void)cichar; /* if CHAP support is not compiled, cichar is set but not used, which makes some compilers complaining */ + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try_.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression); + +#ifdef HAVE_MULTILINK + /* + * Nak for MRRU option - accept their value if it is smaller + * than the one we want. + */ + if (go->neg_mrru) { + NAKCISHORT(CI_MRRU, neg_mrru, + if (treat_as_reject) + try_.neg_mrru = 0; + else if (cishort <= wo->mrru) + try_.mrru = cishort; + ); + } +#else /* HAVE_MULTILINK */ + LWIP_UNUSED_ARG(treat_as_reject); +#endif /* HAVE_MULTILINK */ + + /* + * Nak for short sequence numbers shouldn't be sent, treat it + * like a reject. + */ + NAKCIVOID(CI_SSNHF, neg_ssnhf); + + /* + * Nak of the endpoint discriminator option is not permitted, + * treat it like a reject. + */ + NAKCIENDP(CI_EPDISC, neg_endpoint); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len >= CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != PPP_DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) + goto bad; + GETSHORT(cishort, p); + if (cishort < PPP_DEFMRU) { + try_.neg_mru = 1; + try_.mru = cishort; + } + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + || no.neg_asyncmap || cilen != CILEN_LONG) + goto bad; + break; + case CI_AUTHTYPE: + if (0 +#if CHAP_SUPPORT + || go->neg_chap || no.neg_chap +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + || go->neg_upap || no.neg_upap +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + || go->neg_eap || no.neg_eap +#endif /* EAP_SUPPORT */ + ) + goto bad; + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) + goto bad; + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) + goto bad; + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) + goto bad; + break; +#if LQR_SUPPORT + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) + goto bad; + break; +#endif /* LQR_SUPPORT */ +#ifdef HAVE_MULTILINK + case CI_MRRU: + if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT) + goto bad; + break; +#endif /* HAVE_MULTILINK */ + case CI_SSNHF: + if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID) + goto bad; + try_.neg_ssnhf = 1; + break; + case CI_EPDISC: + if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR) + goto bad; + break; + default: + break; + } + p = next; + } + + /* + * OK, the Nak is good. Now we can update state. + * If there are any options left we ignore them. + */ + if (f->state != PPP_FSM_OPENED) { + if (looped_back) { + if (++try_.numloops >= pcb->settings.lcp_loopbackfail) { + ppp_notice("Serial line is looped back."); + pcb->err_code = PPPERR_LOOPBACK; + lcp_close(f->pcb, "Loopback detected"); + } + } else + try_.numloops = 0; + *go = try_; + } + + return 1; + +bad: + LCPDEBUG(("lcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int lcp_rejci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u_char cichar; + u_short cishort; + u32_t cilong; + lcp_options try_; /* options to request next time */ + + try_ = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try_.neg = 0; \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + try_.neg = 0; \ + } + +#if CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT +#define REJCICHAP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ + goto bad; \ + try_.neg = 0; \ + try_.neg_eap = try_.neg_upap = 0; \ + } +#endif /* CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT */ + +#if CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT +#define REJCICHAP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ + goto bad; \ + try_.neg = 0; \ + try_.neg_upap = 0; \ + } +#endif /* CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT */ + +#if CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT +#define REJCICHAP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ + goto bad; \ + try_.neg = 0; \ + try_.neg_eap = 0; \ + } +#endif /* CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT */ + +#if CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT +#define REJCICHAP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ + goto bad; \ + try_.neg = 0; \ + } +#endif /* CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT */ + +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + try_.neg = 0; \ + } +#if LQR_SUPPORT +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) \ + goto bad; \ + try_.neg = 0; \ + } +#endif /* LQR_SUPPORT */ +#define REJCICBCP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) \ + goto bad; \ + try_.neg = 0; \ + } +#define REJCIENDP(opt, neg, class, val, vlen) \ + if (go->neg && \ + len >= CILEN_CHAR + vlen && \ + p[0] == opt && \ + p[1] == CILEN_CHAR + vlen) { \ + int i; \ + len -= CILEN_CHAR + vlen; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + if (cichar != class) \ + goto bad; \ + for (i = 0; i < vlen; ++i) { \ + GETCHAR(cichar, p); \ + if (cichar != val[i]) \ + goto bad; \ + } \ + try_.neg = 0; \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); +#if EAP_SUPPORT + REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP); + if (!go->neg_eap) { +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype); + if (!go->neg_chap) { +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + } +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + } +#endif /* EAP_SUPPORT */ +#if LQR_SUPPORT + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); +#endif /* LQR_SUPPORT */ + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); +#ifdef HAVE_MULTILINK + REJCISHORT(CI_MRRU, neg_mrru, go->mrru); +#endif /* HAVE_MULTILINK */ + REJCIVOID(CI_SSNHF, neg_ssnhf); + REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class_, + go->endpoint.value, go->endpoint.length); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + return 1; + +bad: + LCPDEBUG(("lcp_rejci: received bad Reject!")); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + * + * inp = Requested CIs + * lenp = Length of requested CIs + */ +static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *ho = &pcb->lcp_hisoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + u_char *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype, cichar; /* Parsed len, type, char value */ + u_short cishort; /* Parsed short value */ + u32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *rejp; /* Pointer to next char in reject frame */ + struct pbuf *nakp; /* Nak buffer */ + u_char *nakoutp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE); + if(NULL == nakp) + return 0; + if(nakp->tot_len != nakp->len) { + pbuf_free(nakp); + return 0; + } + + nakoutp = (u_char*)nakp->payload; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG(("lcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + if (!ao->neg_mru || /* Allow option? */ + cilen != CILEN_SHORT) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < PPP_MINMRU) { + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakoutp); + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_MINMRU, nakoutp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ + break; + + case CI_ASYNCMAP: + if (!ao->neg_asyncmap || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakoutp); + PUTCHAR(CILEN_LONG, nakoutp); + PUTLONG(ao->asyncmap | cilong, nakoutp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; + break; + + case CI_AUTHTYPE: + if (cilen < CILEN_SHORT || + !(0 +#if PAP_SUPPORT + || ao->neg_upap +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + || ao->neg_chap +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || ao->neg_eap +#endif /* EAP_SUPPORT */ + )) { + /* + * Reject the option if we're not willing to authenticate. + */ + ppp_dbglog("No auth is possible"); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be PAP, CHAP, or EAP. + * + * Note: if more than one of ao->neg_upap, ao->neg_chap, and + * ao->neg_eap are set, and the peer sends a Configure-Request + * with two or more authenticate-protocol requests, then we will + * reject the second request. + * Whether we end up doing CHAP, UPAP, or EAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + +#if PAP_SUPPORT + if (cishort == PPP_PAP) { + /* we've already accepted CHAP or EAP */ + if (0 +#if CHAP_SUPPORT + || ho->neg_chap +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || ho->neg_eap +#endif /* EAP_SUPPORT */ + || cilen != CILEN_SHORT) { + LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + orc = CONFNAK; /* NAK it and suggest CHAP or EAP */ + PUTCHAR(CI_AUTHTYPE, nakoutp); +#if EAP_SUPPORT + if (ao->neg_eap) { + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_EAP, nakoutp); + } else { +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + PUTCHAR(CILEN_CHAP, nakoutp); + PUTSHORT(PPP_CHAP, nakoutp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + } +#endif /* EAP_SUPPORT */ + break; + } + ho->neg_upap = 1; + break; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (cishort == PPP_CHAP) { + /* we've already accepted PAP or EAP */ + if ( +#if PAP_SUPPORT + ho->neg_upap || +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + ho->neg_eap || +#endif /* EAP_SUPPORT */ + cilen != CILEN_CHAP) { + LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + orc = CONFNAK; /* NAK it and suggest EAP or PAP */ + PUTCHAR(CI_AUTHTYPE, nakoutp); + PUTCHAR(CILEN_SHORT, nakoutp); +#if EAP_SUPPORT + if (ao->neg_eap) { + PUTSHORT(PPP_EAP, nakoutp); + } else +#endif /* EAP_SUPPORT */ +#if PAP_SUPPORT + if(1) { + PUTSHORT(PPP_PAP, nakoutp); + } + else +#endif /* PAP_SUPPORT */ + {} + break; + } + GETCHAR(cichar, p); /* get digest type */ + if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) { + /* + * We can't/won't do the requested type, + * suggest something else. + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakoutp); + PUTCHAR(CILEN_CHAP, nakoutp); + PUTSHORT(PPP_CHAP, nakoutp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); + break; + } + ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */ + ho->neg_chap = 1; + break; + } +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + if (cishort == PPP_EAP) { + /* we've already accepted CHAP or PAP */ + if ( +#if CHAP_SUPPORT + ho->neg_chap || +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + ho->neg_upap || +#endif /* PAP_SUPPORT */ + cilen != CILEN_SHORT) { + LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_eap) { /* we don't want to do EAP */ + orc = CONFNAK; /* NAK it and suggest CHAP or PAP */ + PUTCHAR(CI_AUTHTYPE, nakoutp); +#if CHAP_SUPPORT + if (ao->neg_chap) { + PUTCHAR(CILEN_CHAP, nakoutp); + PUTSHORT(PPP_CHAP, nakoutp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); + } else +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + if(1) { + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_PAP, nakoutp); + } else +#endif /* PAP_SUPPORT */ + {} + break; + } + ho->neg_eap = 1; + break; + } +#endif /* EAP_SUPPORT */ + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap || + * ao->neg_eap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakoutp); + +#if EAP_SUPPORT + if (ao->neg_eap) { + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_EAP, nakoutp); + } else +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + if (ao->neg_chap) { + PUTCHAR(CILEN_CHAP, nakoutp); + PUTSHORT(PPP_CHAP, nakoutp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); + } else +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + if(1) { + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_PAP, nakoutp); + } else +#endif /* PAP_SUPPORT */ + {} + break; + +#if LQR_SUPPORT + case CI_QUALITY: + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + GETSHORT(cishort, p); + GETLONG(cilong, p); + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakoutp); + PUTCHAR(CILEN_LQR, nakoutp); + PUTSHORT(PPP_LQR, nakoutp); + PUTLONG(ao->lqr_period, nakoutp); + break; + } + break; +#endif /* LQR_SUPPORT */ + + case CI_MAGICNUMBER: + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic(); /* Don't put magic() inside macro! */ + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakoutp); + PUTCHAR(CILEN_LONG, nakoutp); + PUTLONG(cilong, nakoutp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + +#ifdef HAVE_MULTILINK + case CI_MRRU: + if (!ao->neg_mrru + || !multilink + || cilen != CILEN_SHORT) { + orc = CONFREJ; + break; + } + + GETSHORT(cishort, p); + /* possibly should insist on a minimum/maximum MRRU here */ + ho->neg_mrru = 1; + ho->mrru = cishort; + break; +#endif /* HAVE_MULTILINK */ + + case CI_SSNHF: + if (!ao->neg_ssnhf +#ifdef HAVE_MULTILINK + || !multilink +#endif /* HAVE_MULTILINK */ + || cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_ssnhf = 1; + break; + + case CI_EPDISC: + if (!ao->neg_endpoint || + cilen < CILEN_CHAR || + cilen > CILEN_CHAR + MAX_ENDP_LEN) { + orc = CONFREJ; + break; + } + GETCHAR(cichar, p); + cilen -= CILEN_CHAR; + ho->neg_endpoint = 1; + ho->endpoint.class_ = cichar; + ho->endpoint.length = cilen; + MEMCPY(ho->endpoint.value, p, cilen); + INCPTR(cilen, p); + break; + + default: + LCPDEBUG(("lcp_reqci: rcvd unknown option %d", citype)); + orc = CONFREJ; + break; + } + +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) /* Need to move rejected CI? */ + MEMCPY(rejp, cip, cilen); /* Move it */ + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakoutp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = next - inp; + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak buffer to the caller's buffer. + */ + *lenp = nakoutp - (u_char*)nakp->payload; + MEMCPY(inp, nakp->payload, *lenp); + break; + case CONFREJ: + *lenp = rejp - inp; + break; + default: + break; + } + + pbuf_free(nakp); + LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + */ +static void lcp_up(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *ho = &pcb->lcp_hisoptions; + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + int mtu, mru; + + if (!go->neg_magicnumber) + go->magicnumber = 0; + if (!ho->neg_magicnumber) + ho->magicnumber = 0; + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + * Note on the MTU: the link MTU can be the MRU the peer wanted, + * the interface MTU is set to the lowest of that, the + * MTU we want to use, and our link MRU. + */ + mtu = ho->neg_mru? ho->mru: PPP_MRU; + mru = go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU; +#ifdef HAVE_MULTILINK + if (!(multilink && go->neg_mrru && ho->neg_mrru)) +#endif /* HAVE_MULTILINK */ + netif_set_mtu(pcb, LWIP_MIN(LWIP_MIN(mtu, mru), ao->mru)); + ppp_send_config(pcb, mtu, + (ho->neg_asyncmap? ho->asyncmap: 0xffffffff), + ho->neg_pcompression, ho->neg_accompression); + ppp_recv_config(pcb, mru, + (pcb->settings.lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) + pcb->peer_mru = ho->mru; + + lcp_echo_lowerup(f->pcb); /* Enable echo messages */ + + link_established(pcb); +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void lcp_down(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + + lcp_echo_lowerdown(f->pcb); + + link_down(pcb); + + ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0); + ppp_recv_config(pcb, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0xffffffff), + go->neg_pcompression, go->neg_accompression); + pcb->peer_mru = PPP_MRU; +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void lcp_starting(fsm *f) { + ppp_pcb *pcb = f->pcb; + link_required(pcb); +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void lcp_finished(fsm *f) { + ppp_pcb *pcb = f->pcb; + link_terminated(pcb); +} + + +#if PRINTPKT_SUPPORT +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static const char* const lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq", "Ident", + "TimeRem" +}; + +static int lcp_printpkt(const u_char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len, olen, i; + const u_char *pstart, *optend; + u_short cishort; + u32_t cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(lcp_codenames)) + printer(arg, " %s", lcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%x", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { +#if PAP_SUPPORT + case PPP_PAP: + printer(arg, "pap"); + break; +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + case PPP_CHAP: + printer(arg, "chap"); + if (p < optend) { + switch (*p) { + case CHAP_MD5: + printer(arg, " MD5"); + ++p; + break; +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + printer(arg, " MS"); + ++p; + break; + + case CHAP_MICROSOFT_V2: + printer(arg, " MS-v2"); + ++p; + break; +#endif /* MSCHAP_SUPPORT */ + default: + break; + } + } + break; +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + case PPP_EAP: + printer(arg, "eap"); + break; +#endif /* EAP_SUPPORT */ + default: + printer(arg, "0x%x", cishort); + } + } + break; +#if LQR_SUPPORT + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; +#endif /* LQR_SUPPORT */ + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETCHAR(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + case CI_MRRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mrru %d", cishort); + } + break; + case CI_SSNHF: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "ssnhf"); + } + break; + case CI_EPDISC: +#ifdef HAVE_MULTILINK + if (olen >= CILEN_CHAR) { + struct epdisc epd; + p += 2; + GETCHAR(epd.class, p); + epd.length = olen - CILEN_CHAR; + if (epd.length > MAX_ENDP_LEN) + epd.length = MAX_ENDP_LEN; + if (epd.length > 0) { + MEMCPY(epd.value, p, epd.length); + p += epd.length; + } + printer(arg, "endpoint [%s]", epdisc_to_str(&epd)); + } +#else + printer(arg, "endpoint"); +#endif + break; + default: + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + ppp_print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + len -= 4; + } + break; + + case IDENTIF: + case TIMEREM: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + len -= 4; + } + if (code == TIMEREM) { + if (len < 4) + break; + GETLONG(cilong, p); + printer(arg, " seconds=%u", cilong); + len -= 4; + } + if (len > 0) { + printer(arg, " "); + ppp_print_string(p, len, printer, arg); + p += len; + len = 0; + } + break; + default: + break; + } + + /* print the rest of the bytes in the packet */ + for (i = 0; i < len && i < 32; ++i) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + if (i < len) { + printer(arg, " ..."); + p += len - i; + } + + return p - pstart; +} +#endif /* PRINTPKT_SUPPORT */ + +/* + * Time to shut down the link because there is nothing out there. + */ + +static void LcpLinkFailure(fsm *f) { + ppp_pcb *pcb = f->pcb; + if (f->state == PPP_FSM_OPENED) { + ppp_info("No response to %d echo-requests", pcb->lcp_echos_pending); + ppp_notice("Serial link appears to be disconnected."); + pcb->err_code = PPPERR_PEERDEAD; + lcp_close(pcb, "Peer not responding"); + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ + +static void LcpEchoCheck(fsm *f) { + ppp_pcb *pcb = f->pcb; + + LcpSendEchoRequest (f); + if (f->state != PPP_FSM_OPENED) + return; + + /* + * Start the timer for the next interval. + */ + if (pcb->lcp_echo_timer_running) + ppp_warn("assertion lcp_echo_timer_running==0 failed"); + TIMEOUT (LcpEchoTimeout, f, pcb->settings.lcp_echo_interval); + pcb->lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ + +static void LcpEchoTimeout(void *arg) { + fsm *f = (fsm*)arg; + ppp_pcb *pcb = f->pcb; + if (pcb->lcp_echo_timer_running != 0) { + pcb->lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ + +static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u32_t magic_val; + LWIP_UNUSED_ARG(id); + + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + ppp_dbglog("lcp: received short Echo-Reply, length %d", len); + return; + } + GETLONG(magic_val, inp); + if (go->neg_magicnumber + && magic_val == go->magicnumber) { + ppp_warn("appear to have received our own echo-reply!"); + return; + } + + /* Reset the number of outstanding echo frames */ + pcb->lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ + +static void LcpSendEchoRequest(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u32_t lcp_magic; + u_char pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (pcb->settings.lcp_echo_fails != 0) { + if (pcb->lcp_echos_pending >= pcb->settings.lcp_echo_fails) { + LcpLinkFailure(f); + pcb->lcp_echos_pending = 0; + } + } + +#if PPP_LCP_ADAPTIVE + /* + * If adaptive echos have been enabled, only send the echo request if + * no traffic was received since the last one. + */ + if (pcb->settings.lcp_echo_adaptive) { + static unsigned int last_pkts_in = 0; + +#if PPP_STATS_SUPPORT + update_link_stats(f->unit); + link_stats_valid = 0; +#endif /* PPP_STATS_SUPPORT */ + + if (link_stats.pkts_in != last_pkts_in) { + last_pkts_in = link_stats.pkts_in; + return; + } + } +#endif + + /* + * Make and send the echo request frame. + */ + if (f->state == PPP_FSM_OPENED) { + lcp_magic = go->magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, pcb->lcp_echo_number++, pkt, pktp - pkt); + ++pcb->lcp_echos_pending; + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void lcp_echo_lowerup(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + + /* Clear the parameters for generating echo frames */ + pcb->lcp_echos_pending = 0; + pcb->lcp_echo_number = 0; + pcb->lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (pcb->settings.lcp_echo_interval != 0) + LcpEchoCheck (f); +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void lcp_echo_lowerdown(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + + if (pcb->lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, f); + pcb->lcp_echo_timer_running = 0; + } +} + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/magic.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/magic.c new file mode 100644 index 0000000..d0d87c5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/magic.c @@ -0,0 +1,294 @@ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/***************************************************************************** +* randm.c - Random number generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-06-03 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/magic.h" + +#if PPP_MD5_RANDM /* Using MD5 for better randomness if enabled */ + +#include "netif/ppp/pppcrypt.h" + +#define MD5_HASH_SIZE 16 +static char magic_randpool[MD5_HASH_SIZE]; /* Pool of randomness. */ +static long magic_randcount; /* Pseudo-random incrementer */ +static u32_t magic_randomseed; /* Seed used for random number generation. */ + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + */ +static void magic_churnrand(char *rand_data, u32_t rand_len) { + lwip_md5_context md5_ctx; + + /* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: %u@%P\n", rand_len, rand_data)); */ + lwip_md5_init(&md5_ctx); + lwip_md5_starts(&md5_ctx); + lwip_md5_update(&md5_ctx, (u_char *)magic_randpool, sizeof(magic_randpool)); + if (rand_data) { + lwip_md5_update(&md5_ctx, (u_char *)rand_data, rand_len); + } else { + struct { + /* INCLUDE fields for any system sources of randomness */ + u32_t jiffies; +#ifdef LWIP_RAND + u32_t rand; +#endif /* LWIP_RAND */ + } sys_data; + magic_randomseed += sys_jiffies(); + sys_data.jiffies = magic_randomseed; +#ifdef LWIP_RAND + sys_data.rand = LWIP_RAND(); +#endif /* LWIP_RAND */ + /* Load sys_data fields here. */ + lwip_md5_update(&md5_ctx, (u_char *)&sys_data, sizeof(sys_data)); + } + lwip_md5_finish(&md5_ctx, (u_char *)magic_randpool); + lwip_md5_free(&md5_ctx); +/* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: -> 0\n")); */ +} + +/* + * Initialize the random number generator. + */ +void magic_init(void) { + magic_churnrand(NULL, 0); +} + +/* + * Randomize our random seed value. + */ +void magic_randomize(void) { + magic_churnrand(NULL, 0); +} + +/* + * magic_random_bytes - Fill a buffer with random bytes. + * + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using magic_churnrand(). + * Note: It's important that there be sufficient randomness in magic_randpool + * before this is called for otherwise the range of the result may be + * narrow enough to make a search feasible. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + * + * XXX Why does he not just call magic_churnrand() for each block? Probably + * so that you don't ever publish the seed which could possibly help + * predict future values. + * XXX Why don't we preserve md5 between blocks and just update it with + * magic_randcount each time? Probably there is a weakness but I wish that + * it was documented. + */ +void magic_random_bytes(unsigned char *buf, u32_t buf_len) { + lwip_md5_context md5_ctx; + u_char tmp[MD5_HASH_SIZE]; + u32_t n; + + while (buf_len > 0) { + lwip_md5_init(&md5_ctx); + lwip_md5_starts(&md5_ctx); + lwip_md5_update(&md5_ctx, (u_char *)magic_randpool, sizeof(magic_randpool)); + lwip_md5_update(&md5_ctx, (u_char *)&magic_randcount, sizeof(magic_randcount)); + lwip_md5_finish(&md5_ctx, tmp); + lwip_md5_free(&md5_ctx); + magic_randcount++; + n = LWIP_MIN(buf_len, MD5_HASH_SIZE); + MEMCPY(buf, tmp, n); + buf += n; + buf_len -= n; + } +} + +/* + * Return a new random number. + */ +u32_t magic(void) { + u32_t new_rand; + + magic_random_bytes((unsigned char *)&new_rand, sizeof(new_rand)); + + return new_rand; +} + +#else /* PPP_MD5_RANDM */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +#ifndef LWIP_RAND +static int magic_randomized; /* Set when truely randomized. */ +#endif /* LWIP_RAND */ +static u32_t magic_randomseed; /* Seed used for random number generation. */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ + +/* + * Initialize the random number generator. + * + * Here we attempt to compute a random number seed but even if + * it isn't random, we'll randomize it later. + * + * The current method uses the fields from the real time clock, + * the idle process counter, the millisecond counter, and the + * hardware timer tick counter. When this is invoked + * in startup(), then the idle counter and timer values may + * repeat after each boot and the real time clock may not be + * operational. Thus we call it again on the first random + * event. + */ +void magic_init(void) { + magic_randomseed += sys_jiffies(); +#ifndef LWIP_RAND + /* Initialize the Borland random number generator. */ + srand((unsigned)magic_randomseed); +#endif /* LWIP_RAND */ +} + +/* + * magic_init - Initialize the magic number generator. + * + * Randomize our random seed value. Here we use the fact that + * this function is called at *truely random* times by the polling + * and network functions. Here we only get 16 bits of new random + * value but we use the previous value to randomize the other 16 + * bits. + */ +void magic_randomize(void) { +#ifndef LWIP_RAND + if (!magic_randomized) { + magic_randomized = !0; + magic_init(); + /* The initialization function also updates the seed. */ + } else { +#endif /* LWIP_RAND */ + magic_randomseed += sys_jiffies(); +#ifndef LWIP_RAND + } +#endif /* LWIP_RAND */ +} + +/* + * Return a new random number. + * + * Here we use the Borland rand() function to supply a pseudo random + * number which we make truely random by combining it with our own + * seed which is randomized by truely random events. + * Thus the numbers will be truely random unless there have been no + * operator or network events in which case it will be pseudo random + * seeded by the real time clock. + */ +u32_t magic(void) { +#ifdef LWIP_RAND + return LWIP_RAND() + magic_randomseed; +#else /* LWIP_RAND */ + return ((u32_t)rand() << 16) + (u32_t)rand() + magic_randomseed; +#endif /* LWIP_RAND */ +} + +/* + * magic_random_bytes - Fill a buffer with random bytes. + */ +void magic_random_bytes(unsigned char *buf, u32_t buf_len) { + u32_t new_rand, n; + + while (buf_len > 0) { + new_rand = magic(); + n = LWIP_MIN(buf_len, sizeof(new_rand)); + MEMCPY(buf, &new_rand, n); + buf += n; + buf_len -= n; + } +} +#endif /* PPP_MD5_RANDM */ + +/* + * Return a new random number between 0 and (2^pow)-1 included. + */ +u32_t magic_pow(u8_t pow) { + return magic() & ~(~0UL<. + * Copyright (c) 2002,2003,2004 Google, Inc. + * All rights reserved. + * + * License: + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. + * + * Changelog: + * 08/12/05 - Matt Domsch + * Only need extra skb padding on transmit, not receive. + * 06/18/04 - Matt Domsch , Oleg Makarenko + * Use Linux kernel 2.6 arc4 and sha1 routines rather than + * providing our own. + * 2/15/04 - TS: added #include and testing for Kernel + * version before using + * MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are + * deprecated in 2.6 + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && MPPE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include + +#include "lwip/err.h" + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/ccp.h" +#include "netif/ppp/mppe.h" +#include "netif/ppp/pppdebug.h" +#include "netif/ppp/pppcrypt.h" + +#define SHA1_SIGNATURE_SIZE 20 + +/* ppp_mppe_state.bits definitions */ +#define MPPE_BIT_A 0x80 /* Encryption table were (re)inititalized */ +#define MPPE_BIT_B 0x40 /* MPPC only (not implemented) */ +#define MPPE_BIT_C 0x20 /* MPPC only (not implemented) */ +#define MPPE_BIT_D 0x10 /* This is an encrypted frame */ + +#define MPPE_BIT_FLUSHED MPPE_BIT_A +#define MPPE_BIT_ENCRYPTED MPPE_BIT_D + +#define MPPE_BITS(p) ((p)[0] & 0xf0) +#define MPPE_CCOUNT(p) ((((p)[0] & 0x0f) << 8) + (p)[1]) +#define MPPE_CCOUNT_SPACE 0x1000 /* The size of the ccount space */ + +#define MPPE_OVHD 2 /* MPPE overhead/packet */ +#define SANITY_MAX 1600 /* Max bogon factor we will tolerate */ + +/* + * Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3. + * Well, not what's written there, but rather what they meant. + */ +static void mppe_rekey(ppp_mppe_state * state, int initial_key) +{ + lwip_sha1_context sha1_ctx; + u8_t sha1_digest[SHA1_SIGNATURE_SIZE]; + + /* + * Key Derivation, from RFC 3078, RFC 3079. + * Equivalent to Get_Key() for MS-CHAP as described in RFC 3079. + */ + lwip_sha1_init(&sha1_ctx); + lwip_sha1_starts(&sha1_ctx); + lwip_sha1_update(&sha1_ctx, state->master_key, state->keylen); + lwip_sha1_update(&sha1_ctx, mppe_sha1_pad1, SHA1_PAD_SIZE); + lwip_sha1_update(&sha1_ctx, state->session_key, state->keylen); + lwip_sha1_update(&sha1_ctx, mppe_sha1_pad2, SHA1_PAD_SIZE); + lwip_sha1_finish(&sha1_ctx, sha1_digest); + lwip_sha1_free(&sha1_ctx); + MEMCPY(state->session_key, sha1_digest, state->keylen); + + if (!initial_key) { + lwip_arc4_init(&state->arc4); + lwip_arc4_setup(&state->arc4, sha1_digest, state->keylen); + lwip_arc4_crypt(&state->arc4, state->session_key, state->keylen); + lwip_arc4_free(&state->arc4); + } + if (state->keylen == 8) { + /* See RFC 3078 */ + state->session_key[0] = 0xd1; + state->session_key[1] = 0x26; + state->session_key[2] = 0x9e; + } + lwip_arc4_init(&state->arc4); + lwip_arc4_setup(&state->arc4, state->session_key, state->keylen); +} + +/* + * Set key, used by MSCHAP before mppe_init() is actually called by CCP so we + * don't have to keep multiple copies of keys. + */ +void mppe_set_key(ppp_pcb *pcb, ppp_mppe_state *state, u8_t *key) { + LWIP_UNUSED_ARG(pcb); + MEMCPY(state->master_key, key, MPPE_MAX_KEY_LEN); +} + +/* + * Initialize (de)compressor state. + */ +void +mppe_init(ppp_pcb *pcb, ppp_mppe_state *state, u8_t options) +{ +#if PPP_DEBUG + const u8_t *debugstr = (const u8_t*)"mppe_comp_init"; + if (&pcb->mppe_decomp == state) { + debugstr = (const u8_t*)"mppe_decomp_init"; + } +#endif /* PPP_DEBUG */ + + /* Save keys. */ + MEMCPY(state->session_key, state->master_key, sizeof(state->master_key)); + + if (options & MPPE_OPT_128) + state->keylen = 16; + else if (options & MPPE_OPT_40) + state->keylen = 8; + else { + PPPDEBUG(LOG_DEBUG, ("%s[%d]: unknown key length\n", debugstr, + pcb->netif->num)); + lcp_close(pcb, "MPPE required but peer negotiation failed"); + return; + } + if (options & MPPE_OPT_STATEFUL) + state->stateful = 1; + + /* Generate the initial session key. */ + mppe_rekey(state, 1); + +#if PPP_DEBUG + { + int i; + char mkey[sizeof(state->master_key) * 2 + 1]; + char skey[sizeof(state->session_key) * 2 + 1]; + + PPPDEBUG(LOG_DEBUG, ("%s[%d]: initialized with %d-bit %s mode\n", + debugstr, pcb->netif->num, (state->keylen == 16) ? 128 : 40, + (state->stateful) ? "stateful" : "stateless")); + + for (i = 0; i < (int)sizeof(state->master_key); i++) + sprintf(mkey + i * 2, "%02x", state->master_key[i]); + for (i = 0; i < (int)sizeof(state->session_key); i++) + sprintf(skey + i * 2, "%02x", state->session_key[i]); + PPPDEBUG(LOG_DEBUG, + ("%s[%d]: keys: master: %s initial session: %s\n", + debugstr, pcb->netif->num, mkey, skey)); + } +#endif /* PPP_DEBUG */ + + /* + * Initialize the coherency count. The initial value is not specified + * in RFC 3078, but we can make a reasonable assumption that it will + * start at 0. Setting it to the max here makes the comp/decomp code + * do the right thing (determined through experiment). + */ + state->ccount = MPPE_CCOUNT_SPACE - 1; + + /* + * Note that even though we have initialized the key table, we don't + * set the FLUSHED bit. This is contrary to RFC 3078, sec. 3.1. + */ + state->bits = MPPE_BIT_ENCRYPTED; +} + +/* + * We received a CCP Reset-Request (actually, we are sending a Reset-Ack), + * tell the compressor to rekey. Note that we MUST NOT rekey for + * every CCP Reset-Request; we only rekey on the next xmit packet. + * We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost. + * So, rekeying for every CCP Reset-Request is broken as the peer will not + * know how many times we've rekeyed. (If we rekey and THEN get another + * CCP Reset-Request, we must rekey again.) + */ +void mppe_comp_reset(ppp_pcb *pcb, ppp_mppe_state *state) +{ + LWIP_UNUSED_ARG(pcb); + state->bits |= MPPE_BIT_FLUSHED; +} + +/* + * Compress (encrypt) a packet. + * It's strange to call this a compressor, since the output is always + * MPPE_OVHD + 2 bytes larger than the input. + */ +err_t +mppe_compress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb, u16_t protocol) +{ + struct pbuf *n, *np; + u8_t *pl; + err_t err; + + LWIP_UNUSED_ARG(pcb); + + /* TCP stack requires that we don't change the packet payload, therefore we copy + * the whole packet before encryption. + */ + np = pbuf_alloc(PBUF_RAW, MPPE_OVHD + sizeof(protocol) + (*pb)->tot_len, PBUF_POOL); + if (!np) { + return ERR_MEM; + } + + /* Hide MPPE header + protocol */ + pbuf_header(np, -(s16_t)(MPPE_OVHD + sizeof(protocol))); + + if ((err = pbuf_copy(np, *pb)) != ERR_OK) { + pbuf_free(np); + return err; + } + + /* Reveal MPPE header + protocol */ + pbuf_header(np, (s16_t)(MPPE_OVHD + sizeof(protocol))); + + *pb = np; + pl = (u8_t*)np->payload; + + state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE; + PPPDEBUG(LOG_DEBUG, ("mppe_compress[%d]: ccount %d\n", pcb->netif->num, state->ccount)); + /* FIXME: use PUT* macros */ + pl[0] = state->ccount>>8; + pl[1] = state->ccount; + + if (!state->stateful || /* stateless mode */ + ((state->ccount & 0xff) == 0xff) || /* "flag" packet */ + (state->bits & MPPE_BIT_FLUSHED)) { /* CCP Reset-Request */ + /* We must rekey */ + if (state->stateful) { + PPPDEBUG(LOG_DEBUG, ("mppe_compress[%d]: rekeying\n", pcb->netif->num)); + } + mppe_rekey(state, 0); + state->bits |= MPPE_BIT_FLUSHED; + } + pl[0] |= state->bits; + state->bits &= ~MPPE_BIT_FLUSHED; /* reset for next xmit */ + pl += MPPE_OVHD; + + /* Add protocol */ + /* FIXME: add PFC support */ + pl[0] = protocol >> 8; + pl[1] = protocol; + + /* Hide MPPE header */ + pbuf_header(np, -(s16_t)MPPE_OVHD); + + /* Encrypt packet */ + for (n = np; n != NULL; n = n->next) { + lwip_arc4_crypt(&state->arc4, (u8_t*)n->payload, n->len); + if (n->tot_len == n->len) { + break; + } + } + + /* Reveal MPPE header */ + pbuf_header(np, (s16_t)MPPE_OVHD); + + return ERR_OK; +} + +/* + * We received a CCP Reset-Ack. Just ignore it. + */ +void mppe_decomp_reset(ppp_pcb *pcb, ppp_mppe_state *state) +{ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(state); + return; +} + +/* + * Decompress (decrypt) an MPPE packet. + */ +err_t +mppe_decompress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb) +{ + struct pbuf *n0 = *pb, *n; + u8_t *pl; + u16_t ccount; + u8_t flushed; + + /* MPPE Header */ + if (n0->len < MPPE_OVHD) { + PPPDEBUG(LOG_DEBUG, + ("mppe_decompress[%d]: short pkt (%d)\n", + pcb->netif->num, n0->len)); + state->sanity_errors += 100; + goto sanity_error; + } + + pl = (u8_t*)n0->payload; + flushed = MPPE_BITS(pl) & MPPE_BIT_FLUSHED; + ccount = MPPE_CCOUNT(pl); + PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: ccount %d\n", + pcb->netif->num, ccount)); + + /* sanity checks -- terminate with extreme prejudice */ + if (!(MPPE_BITS(pl) & MPPE_BIT_ENCRYPTED)) { + PPPDEBUG(LOG_DEBUG, + ("mppe_decompress[%d]: ENCRYPTED bit not set!\n", + pcb->netif->num)); + state->sanity_errors += 100; + goto sanity_error; + } + if (!state->stateful && !flushed) { + PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: FLUSHED bit not set in " + "stateless mode!\n", pcb->netif->num)); + state->sanity_errors += 100; + goto sanity_error; + } + if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) { + PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: FLUSHED bit not set on " + "flag packet!\n", pcb->netif->num)); + state->sanity_errors += 100; + goto sanity_error; + } + + /* + * Check the coherency count. + */ + + if (!state->stateful) { + /* Discard late packet */ + if ((ccount - state->ccount) % MPPE_CCOUNT_SPACE > MPPE_CCOUNT_SPACE / 2) { + state->sanity_errors++; + goto sanity_error; + } + + /* RFC 3078, sec 8.1. Rekey for every packet. */ + while (state->ccount != ccount) { + mppe_rekey(state, 0); + state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE; + } + } else { + /* RFC 3078, sec 8.2. */ + if (!state->discard) { + /* normal state */ + state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE; + if (ccount != state->ccount) { + /* + * (ccount > state->ccount) + * Packet loss detected, enter the discard state. + * Signal the peer to rekey (by sending a CCP Reset-Request). + */ + state->discard = 1; + ccp_resetrequest(pcb); + return ERR_BUF; + } + } else { + /* discard state */ + if (!flushed) { + /* ccp.c will be silent (no additional CCP Reset-Requests). */ + return ERR_BUF; + } else { + /* Rekey for every missed "flag" packet. */ + while ((ccount & ~0xff) != + (state->ccount & ~0xff)) { + mppe_rekey(state, 0); + state->ccount = + (state->ccount + + 256) % MPPE_CCOUNT_SPACE; + } + + /* reset */ + state->discard = 0; + state->ccount = ccount; + /* + * Another problem with RFC 3078 here. It implies that the + * peer need not send a Reset-Ack packet. But RFC 1962 + * requires it. Hopefully, M$ does send a Reset-Ack; even + * though it isn't required for MPPE synchronization, it is + * required to reset CCP state. + */ + } + } + if (flushed) + mppe_rekey(state, 0); + } + + /* Hide MPPE header */ + pbuf_header(n0, -(s16_t)(MPPE_OVHD)); + + /* Decrypt the packet. */ + for (n = n0; n != NULL; n = n->next) { + lwip_arc4_crypt(&state->arc4, (u8_t*)n->payload, n->len); + if (n->tot_len == n->len) { + break; + } + } + + /* good packet credit */ + state->sanity_errors >>= 1; + + return ERR_OK; + +sanity_error: + if (state->sanity_errors >= SANITY_MAX) { + /* + * Take LCP down if the peer is sending too many bogons. + * We don't want to do this for a single or just a few + * instances since it could just be due to packet corruption. + */ + lcp_close(pcb, "Too many MPPE errors"); + } + return ERR_BUF; +} + +#endif /* PPP_SUPPORT && MPPE_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/multilink.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/multilink.c new file mode 100644 index 0000000..62014e8 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/multilink.c @@ -0,0 +1,609 @@ +/* + * multilink.c - support routines for multilink. + * + * Copyright (c) 2000-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && defined(HAVE_MULTILINK) /* don't build if not configured for use in lwipopts.h */ + +/* Multilink support + * + * Multilink uses Samba TDB (Trivial Database Library), which + * we cannot port, because it needs a filesystem. + * + * We have to choose between doing a memory-shared TDB-clone, + * or dropping multilink support at all. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" +#include "netif/ppp/tdb.h" + +bool endpoint_specified; /* user gave explicit endpoint discriminator */ +char *bundle_id; /* identifier for our bundle */ +char *blinks_id; /* key for the list of links */ +bool doing_multilink; /* multilink was enabled and agreed to */ +bool multilink_master; /* we own the multilink bundle */ + +extern TDB_CONTEXT *pppdb; +extern char db_key[]; + +static void make_bundle_links (int append); +static void remove_bundle_link (void); +static void iterate_bundle_links (void (*func) (char *)); + +static int get_default_epdisc (struct epdisc *); +static int parse_num (char *str, const char *key, int *valp); +static int owns_unit (TDB_DATA pid, int unit); + +#define set_ip_epdisc(ep, addr) do { \ + ep->length = 4; \ + ep->value[0] = addr >> 24; \ + ep->value[1] = addr >> 16; \ + ep->value[2] = addr >> 8; \ + ep->value[3] = addr; \ +} while (0) + +#define LOCAL_IP_ADDR(addr) \ + (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \ + || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \ + || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */ + +#define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH) + +void +mp_check_options() +{ + lcp_options *wo = &lcp_wantoptions[0]; + lcp_options *ao = &lcp_allowoptions[0]; + + doing_multilink = 0; + if (!multilink) + return; + /* if we're doing multilink, we have to negotiate MRRU */ + if (!wo->neg_mrru) { + /* mrru not specified, default to mru */ + wo->mrru = wo->mru; + wo->neg_mrru = 1; + } + ao->mrru = ao->mru; + ao->neg_mrru = 1; + + if (!wo->neg_endpoint && !noendpoint) { + /* get a default endpoint value */ + wo->neg_endpoint = get_default_epdisc(&wo->endpoint); + } +} + +/* + * Make a new bundle or join us to an existing bundle + * if we are doing multilink. + */ +int +mp_join_bundle() +{ + lcp_options *go = &lcp_gotoptions[0]; + lcp_options *ho = &lcp_hisoptions[0]; + lcp_options *ao = &lcp_allowoptions[0]; + int unit, pppd_pid; + int l, mtu; + char *p; + TDB_DATA key, pid, rec; + + if (doing_multilink) { + /* have previously joined a bundle */ + if (!go->neg_mrru || !ho->neg_mrru) { + notice("oops, didn't get multilink on renegotiation"); + lcp_close(pcb, "multilink required"); + return 0; + } + /* XXX should check the peer_authname and ho->endpoint + are the same as previously */ + return 0; + } + + if (!go->neg_mrru || !ho->neg_mrru) { + /* not doing multilink */ + if (go->neg_mrru) + notice("oops, multilink negotiated only for receive"); + mtu = ho->neg_mru? ho->mru: PPP_MRU; + if (mtu > ao->mru) + mtu = ao->mru; + if (demand) { + /* already have a bundle */ + cfg_bundle(0, 0, 0, 0); + netif_set_mtu(pcb, mtu); + return 0; + } + make_new_bundle(0, 0, 0, 0); + set_ifunit(1); + netif_set_mtu(pcb, mtu); + return 0; + } + + doing_multilink = 1; + + /* + * Find the appropriate bundle or join a new one. + * First we make up a name for the bundle. + * The length estimate is worst-case assuming every + * character has to be quoted. + */ + l = 4 * strlen(peer_authname) + 10; + if (ho->neg_endpoint) + l += 3 * ho->endpoint.length + 8; + if (bundle_name) + l += 3 * strlen(bundle_name) + 2; + bundle_id = malloc(l); + if (bundle_id == 0) + novm("bundle identifier"); + + p = bundle_id; + p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname); + if (ho->neg_endpoint || bundle_name) + *p++ = '/'; + if (ho->neg_endpoint) + p += slprintf(p, bundle_id+l-p, "%s", + epdisc_to_str(&ho->endpoint)); + if (bundle_name) + p += slprintf(p, bundle_id+l-p, "/%v", bundle_name); + + /* Make the key for the list of links belonging to the bundle */ + l = p - bundle_id; + blinks_id = malloc(l + 7); + if (blinks_id == NULL) + novm("bundle links key"); + slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7); + + /* + * For demand mode, we only need to configure the bundle + * and attach the link. + */ + mtu = LWIP_MIN(ho->mrru, ao->mru); + if (demand) { + cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); + netif_set_mtu(pcb, mtu); + script_setenv("BUNDLE", bundle_id + 7, 1); + return 0; + } + + /* + * Check if the bundle ID is already in the database. + */ + unit = -1; + lock_db(); + key.dptr = bundle_id; + key.dsize = p - bundle_id; + pid = tdb_fetch(pppdb, key); + if (pid.dptr != NULL) { + /* bundle ID exists, see if the pppd record exists */ + rec = tdb_fetch(pppdb, pid); + if (rec.dptr != NULL && rec.dsize > 0) { + /* make sure the string is null-terminated */ + rec.dptr[rec.dsize-1] = 0; + /* parse the interface number */ + parse_num(rec.dptr, "IFNAME=ppp", &unit); + /* check the pid value */ + if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid) + || !process_exists(pppd_pid) + || !owns_unit(pid, unit)) + unit = -1; + free(rec.dptr); + } + free(pid.dptr); + } + + if (unit >= 0) { + /* attach to existing unit */ + if (bundle_attach(unit)) { + set_ifunit(0); + script_setenv("BUNDLE", bundle_id + 7, 0); + make_bundle_links(1); + unlock_db(); + info("Link attached to %s", ifname); + return 1; + } + /* attach failed because bundle doesn't exist */ + } + + /* we have to make a new bundle */ + make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); + set_ifunit(1); + netif_set_mtu(pcb, mtu); + script_setenv("BUNDLE", bundle_id + 7, 1); + make_bundle_links(pcb); + unlock_db(); + info("New bundle %s created", ifname); + multilink_master = 1; + return 0; +} + +void mp_exit_bundle() +{ + lock_db(); + remove_bundle_link(); + unlock_db(); +} + +static void sendhup(char *str) +{ + int pid; + + if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) { + if (debug) + dbglog("sending SIGHUP to process %d", pid); + kill(pid, SIGHUP); + } +} + +void mp_bundle_terminated() +{ + TDB_DATA key; + + bundle_terminating = 1; + upper_layers_down(pcb); + notice("Connection terminated."); +#if PPP_STATS_SUPPORT + print_link_stats(); +#endif /* PPP_STATS_SUPPORT */ + if (!demand) { + remove_pidfiles(); + script_unsetenv("IFNAME"); + } + + lock_db(); + destroy_bundle(); + iterate_bundle_links(sendhup); + key.dptr = blinks_id; + key.dsize = strlen(blinks_id); + tdb_delete(pppdb, key); + unlock_db(); + + new_phase(PPP_PHASE_DEAD); + + doing_multilink = 0; + multilink_master = 0; +} + +static void make_bundle_links(int append) +{ + TDB_DATA key, rec; + char *p; + char entry[32]; + int l; + + key.dptr = blinks_id; + key.dsize = strlen(blinks_id); + slprintf(entry, sizeof(entry), "%s;", db_key); + p = entry; + if (append) { + rec = tdb_fetch(pppdb, key); + if (rec.dptr != NULL && rec.dsize > 0) { + rec.dptr[rec.dsize-1] = 0; + if (strstr(rec.dptr, db_key) != NULL) { + /* already in there? strange */ + warn("link entry already exists in tdb"); + return; + } + l = rec.dsize + strlen(entry); + p = malloc(l); + if (p == NULL) + novm("bundle link list"); + slprintf(p, l, "%s%s", rec.dptr, entry); + } else { + warn("bundle link list not found"); + } + if (rec.dptr != NULL) + free(rec.dptr); + } + rec.dptr = p; + rec.dsize = strlen(p) + 1; + if (tdb_store(pppdb, key, rec, TDB_REPLACE)) + error("couldn't %s bundle link list", + append? "update": "create"); + if (p != entry) + free(p); +} + +static void remove_bundle_link() +{ + TDB_DATA key, rec; + char entry[32]; + char *p, *q; + int l; + + key.dptr = blinks_id; + key.dsize = strlen(blinks_id); + slprintf(entry, sizeof(entry), "%s;", db_key); + + rec = tdb_fetch(pppdb, key); + if (rec.dptr == NULL || rec.dsize <= 0) { + if (rec.dptr != NULL) + free(rec.dptr); + return; + } + rec.dptr[rec.dsize-1] = 0; + p = strstr(rec.dptr, entry); + if (p != NULL) { + q = p + strlen(entry); + l = strlen(q) + 1; + memmove(p, q, l); + rec.dsize = p - rec.dptr + l; + if (tdb_store(pppdb, key, rec, TDB_REPLACE)) + error("couldn't update bundle link list (removal)"); + } + free(rec.dptr); +} + +static void iterate_bundle_links(void (*func)(char *)) +{ + TDB_DATA key, rec, pp; + char *p, *q; + + key.dptr = blinks_id; + key.dsize = strlen(blinks_id); + rec = tdb_fetch(pppdb, key); + if (rec.dptr == NULL || rec.dsize <= 0) { + error("bundle link list not found (iterating list)"); + if (rec.dptr != NULL) + free(rec.dptr); + return; + } + p = rec.dptr; + p[rec.dsize-1] = 0; + while ((q = strchr(p, ';')) != NULL) { + *q = 0; + key.dptr = p; + key.dsize = q - p; + pp = tdb_fetch(pppdb, key); + if (pp.dptr != NULL && pp.dsize > 0) { + pp.dptr[pp.dsize-1] = 0; + func(pp.dptr); + } + if (pp.dptr != NULL) + free(pp.dptr); + p = q + 1; + } + free(rec.dptr); +} + +static int +parse_num(str, key, valp) + char *str; + const char *key; + int *valp; +{ + char *p, *endp; + int i; + + p = strstr(str, key); + if (p != 0) { + p += strlen(key); + i = strtol(p, &endp, 10); + if (endp != p && (*endp == 0 || *endp == ';')) { + *valp = i; + return 1; + } + } + return 0; +} + +/* + * Check whether the pppd identified by `key' still owns ppp unit `unit'. + */ +static int +owns_unit(key, unit) + TDB_DATA key; + int unit; +{ + char ifkey[32]; + TDB_DATA kd, vd; + int ret = 0; + + slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit); + kd.dptr = ifkey; + kd.dsize = strlen(ifkey); + vd = tdb_fetch(pppdb, kd); + if (vd.dptr != NULL) { + ret = vd.dsize == key.dsize + && memcmp(vd.dptr, key.dptr, vd.dsize) == 0; + free(vd.dptr); + } + return ret; +} + +static int +get_default_epdisc(ep) + struct epdisc *ep; +{ + char *p; + struct hostent *hp; + u32_t addr; + + /* First try for an ethernet MAC address */ + p = get_first_ethernet(); + if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) { + ep->class = EPD_MAC; + ep->length = 6; + return 1; + } + + /* see if our hostname corresponds to a reasonable IP address */ + hp = gethostbyname(hostname); + if (hp != NULL) { + addr = *(u32_t *)hp->h_addr; + if (!bad_ip_adrs(addr)) { + addr = lwip_ntohl(addr); + if (!LOCAL_IP_ADDR(addr)) { + ep->class = EPD_IP; + set_ip_epdisc(ep, addr); + return 1; + } + } + } + + return 0; +} + +/* + * epdisc_to_str - make a printable string from an endpoint discriminator. + */ + +static char *endp_class_names[] = { + "null", "local", "IP", "MAC", "magic", "phone" +}; + +char * +epdisc_to_str(ep) + struct epdisc *ep; +{ + static char str[MAX_ENDP_LEN*3+8]; + u_char *p = ep->value; + int i, mask = 0; + char *q, c, c2; + + if (ep->class == EPD_NULL && ep->length == 0) + return "null"; + if (ep->class == EPD_IP && ep->length == 4) { + u32_t addr; + + GETLONG(addr, p); + slprintf(str, sizeof(str), "IP:%I", lwip_htonl(addr)); + return str; + } + + c = ':'; + c2 = '.'; + if (ep->class == EPD_MAC && ep->length == 6) + c2 = ':'; + else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0) + mask = 3; + q = str; + if (ep->class <= EPD_PHONENUM) + q += slprintf(q, sizeof(str)-1, "%s", + endp_class_names[ep->class]); + else + q += slprintf(q, sizeof(str)-1, "%d", ep->class); + c = ':'; + for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) { + if ((i & mask) == 0) { + *q++ = c; + c = c2; + } + q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]); + } + return str; +} + +static int hexc_val(int c) +{ + if (c >= 'a') + return c - 'a' + 10; + if (c >= 'A') + return c - 'A' + 10; + return c - '0'; +} + +int +str_to_epdisc(ep, str) + struct epdisc *ep; + char *str; +{ + int i, l; + char *p, *endp; + + for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) { + int sl = strlen(endp_class_names[i]); + if (strncasecmp(str, endp_class_names[i], sl) == 0) { + str += sl; + break; + } + } + if (i > EPD_PHONENUM) { + /* not a class name, try a decimal class number */ + i = strtol(str, &endp, 10); + if (endp == str) + return 0; /* can't parse class number */ + str = endp; + } + ep->class = i; + if (*str == 0) { + ep->length = 0; + return 1; + } + if (*str != ':' && *str != '.') + return 0; + ++str; + + if (i == EPD_IP) { + u32_t addr; + i = parse_dotted_ip(str, &addr); + if (i == 0 || str[i] != 0) + return 0; + set_ip_epdisc(ep, addr); + return 1; + } + if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) { + ep->length = 6; + return 1; + } + + p = str; + for (l = 0; l < MAX_ENDP_LEN; ++l) { + if (*str == 0) + break; + if (p <= str) + for (p = str; isxdigit(*p); ++p) + ; + i = p - str; + if (i == 0) + return 0; + ep->value[l] = hexc_val(*str++); + if ((i & 1) == 0) + ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++); + if (*str == ':' || *str == '.') + ++str; + } + if (*str != 0 || (ep->class == EPD_MAC && l != 6)) + return 0; + ep->length = l; + return 1; +} + +#endif /* PPP_SUPPORT && HAVE_MULTILINK */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/README b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/README new file mode 100644 index 0000000..3fdf159 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/README @@ -0,0 +1,22 @@ +About PolarSSL files into lwIP PPP support +------------------------------------------ + +This folder contains some files fetched from the latest BSD release of +the PolarSSL project (PolarSSL 0.10.1-bsd) for ciphers and encryption +methods we need for lwIP PPP support. + +The PolarSSL files were cleaned to contain only the necessary struct +fields and functions needed for lwIP. + +The PolarSSL API was not changed at all, so if you are already using +PolarSSL you can choose to skip the compilation of the included PolarSSL +library into lwIP. + +If you are not using the embedded copy you must include external +libraries into your arch/cc.h port file. + +Beware of the stack requirements which can be a lot larger if you are not +using our cleaned PolarSSL library. + + +PolarSSL project website: http://polarssl.org/ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/arc4.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/arc4.c new file mode 100644 index 0000000..6e17ec4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/arc4.c @@ -0,0 +1,101 @@ +/* + * An implementation of the ARCFOUR algorithm + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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. + */ +/* + * The ARCFOUR algorithm was publicly disclosed on 94/09. + * + * http://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0 + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_ARC4 + +#include "netif/ppp/polarssl/arc4.h" +/* + * ARC4 key schedule + */ +void arc4_setup( arc4_context *ctx, unsigned char *key, int keylen ) +{ + int i, j, k, a; + unsigned char *m; + + ctx->x = 0; + ctx->y = 0; + m = ctx->m; + + for( i = 0; i < 256; i++ ) + m[i] = (unsigned char) i; + + j = k = 0; + + for( i = 0; i < 256; i++, k++ ) + { + if( k >= keylen ) k = 0; + + a = m[i]; + j = ( j + a + key[k] ) & 0xFF; + m[i] = m[j]; + m[j] = (unsigned char) a; + } +} + +/* + * ARC4 cipher function + */ +void arc4_crypt( arc4_context *ctx, unsigned char *buf, int buflen ) +{ + int i, x, y, a, b; + unsigned char *m; + + x = ctx->x; + y = ctx->y; + m = ctx->m; + + for( i = 0; i < buflen; i++ ) + { + x = ( x + 1 ) & 0xFF; a = m[x]; + y = ( y + a ) & 0xFF; b = m[y]; + + m[x] = (unsigned char) b; + m[y] = (unsigned char) a; + + buf[i] = (unsigned char) + ( buf[i] ^ m[(unsigned char)( a + b )] ); + } + + ctx->x = x; + ctx->y = y; +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/des.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/des.c new file mode 100644 index 0000000..9a89d00 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/des.c @@ -0,0 +1,422 @@ +/* + * FIPS-46-3 compliant Triple-DES implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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. + */ +/* + * DES, on which TDES is based, was originally designed by Horst Feistel + * at IBM in 1974, and was adopted as a standard by NIST (formerly NBS). + * + * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES + +#include "netif/ppp/polarssl/des.h" + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * Expanded DES S-boxes + */ +static const unsigned long SB1[64] = +{ + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const unsigned long SB2[64] = +{ + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const unsigned long SB3[64] = +{ + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const unsigned long SB4[64] = +{ + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const unsigned long SB5[64] = +{ + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const unsigned long SB6[64] = +{ + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const unsigned long SB7[64] = +{ + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const unsigned long SB8[64] = +{ + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* + * PC1: left and right halves bit-swap + */ +static const unsigned long LHs[16] = +{ + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const unsigned long RHs[16] = +{ + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* + * Initial Permutation macro + */ +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* + * Final Permutation macro + */ +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* + * DES round macro + */ +#define DES_ROUND(X,Y) \ +{ \ + T = *SK++ ^ X; \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = *SK++ ^ ((X << 28) | (X >> 4)); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +#define SWAP(a,b) { unsigned long t = a; a = b; b = t; t = 0; } + +static void des_setkey( unsigned long SK[32], unsigned char key[8] ) +{ + int i; + unsigned long X, Y, T; + + GET_ULONG_BE( X, key, 0 ); + GET_ULONG_BE( Y, key, 4 ); + + /* + * Permuted Choice 1 + */ + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); + + X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) + | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) + | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) + | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); + + Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) + | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) + | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) + | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* + * calculate subkeys + */ + for( i = 0; i < 16; i++ ) + { + if( i < 2 || i == 8 || i == 15 ) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } +} + +/* + * DES key schedule (56-bit, encryption) + */ +void des_setkey_enc( des_context *ctx, unsigned char key[8] ) +{ + des_setkey( ctx->sk, key ); +} + +/* + * DES key schedule (56-bit, decryption) + */ +void des_setkey_dec( des_context *ctx, unsigned char key[8] ) +{ + int i; + + des_setkey( ctx->sk, key ); + + for( i = 0; i < 16; i += 2 ) + { + SWAP( ctx->sk[i ], ctx->sk[30 - i] ); + SWAP( ctx->sk[i + 1], ctx->sk[31 - i] ); + } +} + +/* + * DES-ECB block encryption/decryption + */ +void des_crypt_ecb( des_context *ctx, + const unsigned char input[8], + unsigned char output[8] ) +{ + int i; + unsigned long X, Y, T, *SK; + + SK = ctx->sk; + + GET_ULONG_BE( X, input, 0 ); + GET_ULONG_BE( Y, input, 4 ); + + DES_IP( X, Y ); + + for( i = 0; i < 8; i++ ) + { + DES_ROUND( Y, X ); + DES_ROUND( X, Y ); + } + + DES_FP( Y, X ); + + PUT_ULONG_BE( Y, output, 0 ); + PUT_ULONG_BE( X, output, 4 ); +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/md4.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/md4.c new file mode 100644 index 0000000..b1701a0 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/md4.c @@ -0,0 +1,281 @@ +/* + * RFC 1186/1320 compliant MD4 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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. + */ +/* + * The MD4 algorithm was designed by Ron Rivest in 1990. + * + * http://www.ietf.org/rfc/rfc1186.txt + * http://www.ietf.org/rfc/rfc1320.txt + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD4 + +#include "netif/ppp/polarssl/md4.h" + +#include + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +/* + * MD4 context setup + */ +void md4_starts( md4_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +static void md4_process( md4_context *ctx, const unsigned char data[64] ) +{ + unsigned long X[16], A, B, C, D; + + GET_ULONG_LE( X[ 0], data, 0 ); + GET_ULONG_LE( X[ 1], data, 4 ); + GET_ULONG_LE( X[ 2], data, 8 ); + GET_ULONG_LE( X[ 3], data, 12 ); + GET_ULONG_LE( X[ 4], data, 16 ); + GET_ULONG_LE( X[ 5], data, 20 ); + GET_ULONG_LE( X[ 6], data, 24 ); + GET_ULONG_LE( X[ 7], data, 28 ); + GET_ULONG_LE( X[ 8], data, 32 ); + GET_ULONG_LE( X[ 9], data, 36 ); + GET_ULONG_LE( X[10], data, 40 ); + GET_ULONG_LE( X[11], data, 44 ); + GET_ULONG_LE( X[12], data, 48 ); + GET_ULONG_LE( X[13], data, 52 ); + GET_ULONG_LE( X[14], data, 56 ); + GET_ULONG_LE( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x, y, z) ((x & y) | ((~x) & z)) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 1], 7 ); + P( C, D, A, B, X[ 2], 11 ); + P( B, C, D, A, X[ 3], 19 ); + P( A, B, C, D, X[ 4], 3 ); + P( D, A, B, C, X[ 5], 7 ); + P( C, D, A, B, X[ 6], 11 ); + P( B, C, D, A, X[ 7], 19 ); + P( A, B, C, D, X[ 8], 3 ); + P( D, A, B, C, X[ 9], 7 ); + P( C, D, A, B, X[10], 11 ); + P( B, C, D, A, X[11], 19 ); + P( A, B, C, D, X[12], 3 ); + P( D, A, B, C, X[13], 7 ); + P( C, D, A, B, X[14], 11 ); + P( B, C, D, A, X[15], 19 ); + +#undef P +#undef F + +#define F(x,y,z) ((x & y) | (x & z) | (y & z)) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x5A827999; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 4], 5 ); + P( C, D, A, B, X[ 8], 9 ); + P( B, C, D, A, X[12], 13 ); + P( A, B, C, D, X[ 1], 3 ); + P( D, A, B, C, X[ 5], 5 ); + P( C, D, A, B, X[ 9], 9 ); + P( B, C, D, A, X[13], 13 ); + P( A, B, C, D, X[ 2], 3 ); + P( D, A, B, C, X[ 6], 5 ); + P( C, D, A, B, X[10], 9 ); + P( B, C, D, A, X[14], 13 ); + P( A, B, C, D, X[ 3], 3 ); + P( D, A, B, C, X[ 7], 5 ); + P( C, D, A, B, X[11], 9 ); + P( B, C, D, A, X[15], 13 ); + +#undef P +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x6ED9EBA1; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 8], 9 ); + P( C, D, A, B, X[ 4], 11 ); + P( B, C, D, A, X[12], 15 ); + P( A, B, C, D, X[ 2], 3 ); + P( D, A, B, C, X[10], 9 ); + P( C, D, A, B, X[ 6], 11 ); + P( B, C, D, A, X[14], 15 ); + P( A, B, C, D, X[ 1], 3 ); + P( D, A, B, C, X[ 9], 9 ); + P( C, D, A, B, X[ 5], 11 ); + P( B, C, D, A, X[13], 15 ); + P( A, B, C, D, X[ 3], 3 ); + P( D, A, B, C, X[11], 9 ); + P( C, D, A, B, X[ 7], 11 ); + P( B, C, D, A, X[15], 15 ); + +#undef F +#undef P + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD4 process buffer + */ +void md4_update( md4_context *ctx, const unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + MEMCPY( (void *) (ctx->buffer + left), + input, fill ); + md4_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + md4_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + MEMCPY( (void *) (ctx->buffer + left), + input, ilen ); + } +} + +static const unsigned char md4_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * MD4 final digest + */ +void md4_finish( md4_context *ctx, unsigned char output[16] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_LE( low, msglen, 0 ); + PUT_ULONG_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md4_update( ctx, md4_padding, padn ); + md4_update( ctx, msglen, 8 ); + + PUT_ULONG_LE( ctx->state[0], output, 0 ); + PUT_ULONG_LE( ctx->state[1], output, 4 ); + PUT_ULONG_LE( ctx->state[2], output, 8 ); + PUT_ULONG_LE( ctx->state[3], output, 12 ); +} + +/* + * output = MD4( input buffer ) + */ +void md4( unsigned char *input, int ilen, unsigned char output[16] ) +{ + md4_context ctx; + + md4_starts( &ctx ); + md4_update( &ctx, input, ilen ); + md4_finish( &ctx, output ); +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD4 */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/md5.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/md5.c new file mode 100644 index 0000000..1ec4d81 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/md5.c @@ -0,0 +1,300 @@ +/* + * RFC 1321 compliant MD5 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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. + */ +/* + * The MD5 algorithm was designed by Ron Rivest in 1991. + * + * http://www.ietf.org/rfc/rfc1321.txt + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD5 + +#include "netif/ppp/polarssl/md5.h" + +#include + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +/* + * MD5 context setup + */ +void md5_starts( md5_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +static void md5_process( md5_context *ctx, const unsigned char data[64] ) +{ + unsigned long X[16], A, B, C, D; + + GET_ULONG_LE( X[ 0], data, 0 ); + GET_ULONG_LE( X[ 1], data, 4 ); + GET_ULONG_LE( X[ 2], data, 8 ); + GET_ULONG_LE( X[ 3], data, 12 ); + GET_ULONG_LE( X[ 4], data, 16 ); + GET_ULONG_LE( X[ 5], data, 20 ); + GET_ULONG_LE( X[ 6], data, 24 ); + GET_ULONG_LE( X[ 7], data, 28 ); + GET_ULONG_LE( X[ 8], data, 32 ); + GET_ULONG_LE( X[ 9], data, 36 ); + GET_ULONG_LE( X[10], data, 40 ); + GET_ULONG_LE( X[11], data, 44 ); + GET_ULONG_LE( X[12], data, 48 ); + GET_ULONG_LE( X[13], data, 52 ); + GET_ULONG_LE( X[14], data, 56 ); + GET_ULONG_LE( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ +{ \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P( A, B, C, D, 0, 7, 0xD76AA478 ); + P( D, A, B, C, 1, 12, 0xE8C7B756 ); + P( C, D, A, B, 2, 17, 0x242070DB ); + P( B, C, D, A, 3, 22, 0xC1BDCEEE ); + P( A, B, C, D, 4, 7, 0xF57C0FAF ); + P( D, A, B, C, 5, 12, 0x4787C62A ); + P( C, D, A, B, 6, 17, 0xA8304613 ); + P( B, C, D, A, 7, 22, 0xFD469501 ); + P( A, B, C, D, 8, 7, 0x698098D8 ); + P( D, A, B, C, 9, 12, 0x8B44F7AF ); + P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); + P( B, C, D, A, 11, 22, 0x895CD7BE ); + P( A, B, C, D, 12, 7, 0x6B901122 ); + P( D, A, B, C, 13, 12, 0xFD987193 ); + P( C, D, A, B, 14, 17, 0xA679438E ); + P( B, C, D, A, 15, 22, 0x49B40821 ); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P( A, B, C, D, 1, 5, 0xF61E2562 ); + P( D, A, B, C, 6, 9, 0xC040B340 ); + P( C, D, A, B, 11, 14, 0x265E5A51 ); + P( B, C, D, A, 0, 20, 0xE9B6C7AA ); + P( A, B, C, D, 5, 5, 0xD62F105D ); + P( D, A, B, C, 10, 9, 0x02441453 ); + P( C, D, A, B, 15, 14, 0xD8A1E681 ); + P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); + P( A, B, C, D, 9, 5, 0x21E1CDE6 ); + P( D, A, B, C, 14, 9, 0xC33707D6 ); + P( C, D, A, B, 3, 14, 0xF4D50D87 ); + P( B, C, D, A, 8, 20, 0x455A14ED ); + P( A, B, C, D, 13, 5, 0xA9E3E905 ); + P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); + P( C, D, A, B, 7, 14, 0x676F02D9 ); + P( B, C, D, A, 12, 20, 0x8D2A4C8A ); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P( A, B, C, D, 5, 4, 0xFFFA3942 ); + P( D, A, B, C, 8, 11, 0x8771F681 ); + P( C, D, A, B, 11, 16, 0x6D9D6122 ); + P( B, C, D, A, 14, 23, 0xFDE5380C ); + P( A, B, C, D, 1, 4, 0xA4BEEA44 ); + P( D, A, B, C, 4, 11, 0x4BDECFA9 ); + P( C, D, A, B, 7, 16, 0xF6BB4B60 ); + P( B, C, D, A, 10, 23, 0xBEBFBC70 ); + P( A, B, C, D, 13, 4, 0x289B7EC6 ); + P( D, A, B, C, 0, 11, 0xEAA127FA ); + P( C, D, A, B, 3, 16, 0xD4EF3085 ); + P( B, C, D, A, 6, 23, 0x04881D05 ); + P( A, B, C, D, 9, 4, 0xD9D4D039 ); + P( D, A, B, C, 12, 11, 0xE6DB99E5 ); + P( C, D, A, B, 15, 16, 0x1FA27CF8 ); + P( B, C, D, A, 2, 23, 0xC4AC5665 ); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P( A, B, C, D, 0, 6, 0xF4292244 ); + P( D, A, B, C, 7, 10, 0x432AFF97 ); + P( C, D, A, B, 14, 15, 0xAB9423A7 ); + P( B, C, D, A, 5, 21, 0xFC93A039 ); + P( A, B, C, D, 12, 6, 0x655B59C3 ); + P( D, A, B, C, 3, 10, 0x8F0CCC92 ); + P( C, D, A, B, 10, 15, 0xFFEFF47D ); + P( B, C, D, A, 1, 21, 0x85845DD1 ); + P( A, B, C, D, 8, 6, 0x6FA87E4F ); + P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); + P( C, D, A, B, 6, 15, 0xA3014314 ); + P( B, C, D, A, 13, 21, 0x4E0811A1 ); + P( A, B, C, D, 4, 6, 0xF7537E82 ); + P( D, A, B, C, 11, 10, 0xBD3AF235 ); + P( C, D, A, B, 2, 15, 0x2AD7D2BB ); + P( B, C, D, A, 9, 21, 0xEB86D391 ); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD5 process buffer + */ +void md5_update( md5_context *ctx, const unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + MEMCPY( (void *) (ctx->buffer + left), + input, fill ); + md5_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + md5_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + MEMCPY( (void *) (ctx->buffer + left), + input, ilen ); + } +} + +static const unsigned char md5_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * MD5 final digest + */ +void md5_finish( md5_context *ctx, unsigned char output[16] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_LE( low, msglen, 0 ); + PUT_ULONG_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md5_update( ctx, md5_padding, padn ); + md5_update( ctx, msglen, 8 ); + + PUT_ULONG_LE( ctx->state[0], output, 0 ); + PUT_ULONG_LE( ctx->state[1], output, 4 ); + PUT_ULONG_LE( ctx->state[2], output, 8 ); + PUT_ULONG_LE( ctx->state[3], output, 12 ); +} + +/* + * output = MD5( input buffer ) + */ +void md5( unsigned char *input, int ilen, unsigned char output[16] ) +{ + md5_context ctx; + + md5_starts( &ctx ); + md5_update( &ctx, input, ilen ); + md5_finish( &ctx, output ); +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD5 */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/sha1.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/sha1.c new file mode 100644 index 0000000..c2192ea --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/polarssl/sha1.c @@ -0,0 +1,335 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of PolarSSL or XySSL 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 + * OWNER 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. + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_SHA1 + +#include "netif/ppp/polarssl/sha1.h" + +#include + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * SHA-1 context setup + */ +void sha1_starts( sha1_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +static void sha1_process( sha1_context *ctx, const unsigned char data[64] ) +{ + unsigned long temp, W[16], A, B, C, D, E; + + GET_ULONG_BE( W[ 0], data, 0 ); + GET_ULONG_BE( W[ 1], data, 4 ); + GET_ULONG_BE( W[ 2], data, 8 ); + GET_ULONG_BE( W[ 3], data, 12 ); + GET_ULONG_BE( W[ 4], data, 16 ); + GET_ULONG_BE( W[ 5], data, 20 ); + GET_ULONG_BE( W[ 6], data, 24 ); + GET_ULONG_BE( W[ 7], data, 28 ); + GET_ULONG_BE( W[ 8], data, 32 ); + GET_ULONG_BE( W[ 9], data, 36 ); + GET_ULONG_BE( W[10], data, 40 ); + GET_ULONG_BE( W[11], data, 44 ); + GET_ULONG_BE( W[12], data, 48 ); + GET_ULONG_BE( W[13], data, 52 ); + GET_ULONG_BE( W[14], data, 56 ); + GET_ULONG_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* + * SHA-1 process buffer + */ +void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + MEMCPY( (void *) (ctx->buffer + left), + input, fill ); + sha1_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha1_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + MEMCPY( (void *) (ctx->buffer + left), + input, ilen ); + } +} + +static const unsigned char sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * SHA-1 final digest + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_BE( high, msglen, 0 ); + PUT_ULONG_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha1_update( ctx, sha1_padding, padn ); + sha1_update( ctx, msglen, 8 ); + + PUT_ULONG_BE( ctx->state[0], output, 0 ); + PUT_ULONG_BE( ctx->state[1], output, 4 ); + PUT_ULONG_BE( ctx->state[2], output, 8 ); + PUT_ULONG_BE( ctx->state[3], output, 12 ); + PUT_ULONG_BE( ctx->state[4], output, 16 ); +} + +/* + * output = SHA-1( input buffer ) + */ +void sha1( unsigned char *input, int ilen, unsigned char output[20] ) +{ + sha1_context ctx; + + sha1_starts( &ctx ); + sha1_update( &ctx, input, ilen ); + sha1_finish( &ctx, output ); +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_SHA1 */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/ppp.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/ppp.c new file mode 100644 index 0000000..8b77765 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/ppp.c @@ -0,0 +1,1647 @@ +/***************************************************************************** +* ppp.c - Network Point to Point Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * ppp_defs.h - PPP definitions. + * + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/** + * @defgroup ppp PPP netif + * @ingroup addons + * @verbinclude "ppp.txt" + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "lwip/api.h" +#include "lwip/snmp.h" +#include "lwip/ip4.h" /* for ip4_input() */ +#if PPP_IPV6_SUPPORT +#include "lwip/ip6.h" /* for ip6_input() */ +#endif /* PPP_IPV6_SUPPORT */ +#include "lwip/dns.h" + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/pppos.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" +#include "netif/ppp/magic.h" + +#if PAP_SUPPORT +#include "netif/ppp/upap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "netif/ppp/chap-new.h" +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT +#include "netif/ppp/eap.h" +#endif /* EAP_SUPPORT */ +#if CCP_SUPPORT +#include "netif/ppp/ccp.h" +#endif /* CCP_SUPPORT */ +#if MPPE_SUPPORT +#include "netif/ppp/mppe.h" +#endif /* MPPE_SUPPORT */ +#if ECP_SUPPORT +#include "netif/ppp/ecp.h" +#endif /* EAP_SUPPORT */ +#if VJ_SUPPORT +#include "netif/ppp/vj.h" +#endif /* VJ_SUPPORT */ +#if PPP_IPV4_SUPPORT +#include "netif/ppp/ipcp.h" +#endif /* PPP_IPV4_SUPPORT */ +#if PPP_IPV6_SUPPORT +#include "netif/ppp/ipv6cp.h" +#endif /* PPP_IPV6_SUPPORT */ + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/* Memory pools */ +#if PPPOS_SUPPORT +LWIP_MEMPOOL_PROTOTYPE(PPPOS_PCB); +#endif +#if PPPOE_SUPPORT +LWIP_MEMPOOL_PROTOTYPE(PPPOE_IF); +#endif +#if PPPOL2TP_SUPPORT +LWIP_MEMPOOL_PROTOTYPE(PPPOL2TP_PCB); +#endif +#if LWIP_PPP_API && LWIP_MPU_COMPATIBLE +LWIP_MEMPOOL_PROTOTYPE(PPPAPI_MSG); +#endif +LWIP_MEMPOOL_DECLARE(PPP_PCB, MEMP_NUM_PPP_PCB, sizeof(ppp_pcb), "PPP_PCB") + +/* FIXME: add stats per PPP session */ +#if PPP_STATS_SUPPORT +static struct timeval start_time; /* Time when link was started. */ +static struct pppd_stats old_link_stats; +struct pppd_stats link_stats; +unsigned link_connect_time; +int link_stats_valid; +#endif /* PPP_STATS_SUPPORT */ + +/* + * PPP Data Link Layer "protocol" table. + * One entry per supported protocol. + * The last entry must be NULL. + */ +const struct protent* const protocols[] = { + &lcp_protent, +#if PAP_SUPPORT + &pap_protent, +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + &chap_protent, +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT + &cbcp_protent, +#endif /* CBCP_SUPPORT */ +#if PPP_IPV4_SUPPORT + &ipcp_protent, +#endif /* PPP_IPV4_SUPPORT */ +#if PPP_IPV6_SUPPORT + &ipv6cp_protent, +#endif /* PPP_IPV6_SUPPORT */ +#if CCP_SUPPORT + &ccp_protent, +#endif /* CCP_SUPPORT */ +#if ECP_SUPPORT + &ecp_protent, +#endif /* ECP_SUPPORT */ +#ifdef AT_CHANGE + &atcp_protent, +#endif /* AT_CHANGE */ +#if EAP_SUPPORT + &eap_protent, +#endif /* EAP_SUPPORT */ + NULL +}; + +/* Prototypes for procedures local to this file. */ +static void ppp_do_connect(void *arg); +static err_t ppp_netif_init_cb(struct netif *netif); +#if LWIP_IPV4 +static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, const ip4_addr_t *ipaddr); +#endif /* LWIP_IPV4 */ +#if PPP_IPV6_SUPPORT +static err_t ppp_netif_output_ip6(struct netif *netif, struct pbuf *pb, const ip6_addr_t *ipaddr); +#endif /* PPP_IPV6_SUPPORT */ +static err_t ppp_netif_output(struct netif *netif, struct pbuf *pb, u16_t protocol); + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +#if PPP_AUTH_SUPPORT +void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd) { +#if PAP_SUPPORT + pcb->settings.refuse_pap = !(authtype & PPPAUTHTYPE_PAP); +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + pcb->settings.refuse_chap = !(authtype & PPPAUTHTYPE_CHAP); +#if MSCHAP_SUPPORT + pcb->settings.refuse_mschap = !(authtype & PPPAUTHTYPE_MSCHAP); + pcb->settings.refuse_mschap_v2 = !(authtype & PPPAUTHTYPE_MSCHAP_V2); +#endif /* MSCHAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + pcb->settings.refuse_eap = !(authtype & PPPAUTHTYPE_EAP); +#endif /* EAP_SUPPORT */ + pcb->settings.user = user; + pcb->settings.passwd = passwd; +} +#endif /* PPP_AUTH_SUPPORT */ + +#if MPPE_SUPPORT +/* Set MPPE configuration */ +void ppp_set_mppe(ppp_pcb *pcb, u8_t flags) { + if (flags == PPP_MPPE_DISABLE) { + pcb->settings.require_mppe = 0; + return; + } + + pcb->settings.require_mppe = 1; + pcb->settings.refuse_mppe_stateful = !(flags & PPP_MPPE_ALLOW_STATEFUL); + pcb->settings.refuse_mppe_40 = !!(flags & PPP_MPPE_REFUSE_40); + pcb->settings.refuse_mppe_128 = !!(flags & PPP_MPPE_REFUSE_128); +} +#endif /* MPPE_SUPPORT */ + +#if PPP_NOTIFY_PHASE +void ppp_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb) { + pcb->notify_phase_cb = notify_phase_cb; + notify_phase_cb(pcb, pcb->phase, pcb->ctx_cb); +} +#endif /* PPP_NOTIFY_PHASE */ + +/* + * Initiate a PPP connection. + * + * This can only be called if PPP is in the dead phase. + * + * Holdoff is the time to wait (in seconds) before initiating + * the connection. + * + * If this port connects to a modem, the modem connection must be + * established before calling this. + */ +err_t ppp_connect(ppp_pcb *pcb, u16_t holdoff) { + if (pcb->phase != PPP_PHASE_DEAD) { + return ERR_ALREADY; + } + + PPPDEBUG(LOG_DEBUG, ("ppp_connect[%d]: holdoff=%d\n", pcb->netif->num, holdoff)); + + if (holdoff == 0) { + ppp_do_connect(pcb); + return ERR_OK; + } + + new_phase(pcb, PPP_PHASE_HOLDOFF); + sys_timeout((u32_t)(holdoff*1000), ppp_do_connect, pcb); + return ERR_OK; +} + +#if PPP_SERVER +/* + * Listen for an incoming PPP connection. + * + * This can only be called if PPP is in the dead phase. + * + * If this port connects to a modem, the modem connection must be + * established before calling this. + */ +err_t ppp_listen(ppp_pcb *pcb) { + if (pcb->phase != PPP_PHASE_DEAD) { + return ERR_ALREADY; + } + + PPPDEBUG(LOG_DEBUG, ("ppp_listen[%d]\n", pcb->netif->num)); + + if (pcb->link_cb->listen) { + new_phase(pcb, PPP_PHASE_INITIALIZE); + pcb->link_cb->listen(pcb, pcb->link_ctx_cb); + return ERR_OK; + } + return ERR_IF; +} +#endif /* PPP_SERVER */ + +/* + * Initiate the end of a PPP connection. + * Any outstanding packets in the queues are dropped. + * + * Setting nocarrier to 1 close the PPP connection without initiating the + * shutdown procedure. Always using nocarrier = 0 is still recommended, + * this is going to take a little longer time if your link is down, but + * is a safer choice for the PPP state machine. + * + * Return 0 on success, an error code on failure. + */ +err_t +ppp_close(ppp_pcb *pcb, u8_t nocarrier) +{ + pcb->err_code = PPPERR_USER; + + /* holdoff phase, cancel the reconnection */ + if (pcb->phase == PPP_PHASE_HOLDOFF) { + sys_untimeout(ppp_do_connect, pcb); + new_phase(pcb, PPP_PHASE_DEAD); + } + + /* dead phase, nothing to do, call the status callback to be consistent */ + if (pcb->phase == PPP_PHASE_DEAD) { + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); + return ERR_OK; + } + + /* Already terminating, nothing to do */ + if (pcb->phase >= PPP_PHASE_TERMINATE) { + return ERR_INPROGRESS; + } + + /* LCP not open, close link protocol */ + if (pcb->phase < PPP_PHASE_ESTABLISH) { + new_phase(pcb, PPP_PHASE_DISCONNECT); + ppp_link_terminated(pcb); + return ERR_OK; + } + + /* + * Only accept carrier lost signal on the stable running phase in order + * to prevent changing the PPP phase FSM in transition phases. + * + * Always using nocarrier = 0 is still recommended, this is going to + * take a little longer time, but is a safer choice from FSM point of view. + */ + if (nocarrier && pcb->phase == PPP_PHASE_RUNNING) { + PPPDEBUG(LOG_DEBUG, ("ppp_close[%d]: carrier lost -> lcp_lowerdown\n", pcb->netif->num)); + lcp_lowerdown(pcb); + /* forced link termination, this will force link protocol to disconnect. */ + link_terminated(pcb); + return ERR_OK; + } + + /* Disconnect */ + PPPDEBUG(LOG_DEBUG, ("ppp_close[%d]: kill_link -> lcp_close\n", pcb->netif->num)); + /* LCP soft close request. */ + lcp_close(pcb, "User request"); + return ERR_OK; +} + +/* + * Release the control block. + * + * This can only be called if PPP is in the dead phase. + * + * You must use ppp_close() before if you wish to terminate + * an established PPP session. + * + * Return 0 on success, an error code on failure. + */ +err_t ppp_free(ppp_pcb *pcb) { + err_t err; + if (pcb->phase != PPP_PHASE_DEAD) { + return ERR_CONN; + } + + PPPDEBUG(LOG_DEBUG, ("ppp_free[%d]\n", pcb->netif->num)); + + netif_remove(pcb->netif); + + err = pcb->link_cb->free(pcb, pcb->link_ctx_cb); + + LWIP_MEMPOOL_FREE(PPP_PCB, pcb); + return err; +} + +/* Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. */ +err_t +ppp_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg) +{ + if (pcb == NULL) { + return ERR_VAL; + } + + switch(cmd) { + case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ + if (!arg) { + goto fail; + } + *(int *)arg = (int)(0 +#if PPP_IPV4_SUPPORT + || pcb->if4_up +#endif /* PPP_IPV4_SUPPORT */ +#if PPP_IPV6_SUPPORT + || pcb->if6_up +#endif /* PPP_IPV6_SUPPORT */ + ); + return ERR_OK; + + case PPPCTLG_ERRCODE: /* Get the PPP error code. */ + if (!arg) { + goto fail; + } + *(int *)arg = (int)(pcb->err_code); + return ERR_OK; + + default: + goto fail; + } + +fail: + return ERR_VAL; +} + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +static void ppp_do_connect(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF", pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF); + + new_phase(pcb, PPP_PHASE_INITIALIZE); + pcb->link_cb->connect(pcb, pcb->link_ctx_cb); +} + +/* + * ppp_netif_init_cb - netif init callback + */ +static err_t ppp_netif_init_cb(struct netif *netif) { + netif->name[0] = 'p'; + netif->name[1] = 'p'; +#if LWIP_IPV4 + /* FIXME: change that when netif_null_output_ip4() will materialize */ + netif->output = ppp_netif_output_ip4; +#endif /* LWIP_IPV4 */ +#if PPP_IPV6_SUPPORT + netif->output_ip6 = ppp_netif_output_ip6; +#endif /* PPP_IPV6_SUPPORT */ + netif->flags = NETIF_FLAG_UP; +#if LWIP_NETIF_HOSTNAME + /* @todo: Initialize interface hostname */ + /* netif_set_hostname(netif, "lwip"); */ +#endif /* LWIP_NETIF_HOSTNAME */ + return ERR_OK; +} + +#if LWIP_IPV4 +/* + * Send an IPv4 packet on the given connection. + */ +static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, const ip4_addr_t *ipaddr) { + LWIP_UNUSED_ARG(ipaddr); +#if PPP_IPV4_SUPPORT + return ppp_netif_output(netif, pb, PPP_IP); +#else /* PPP_IPV4_SUPPORT */ + LWIP_UNUSED_ARG(netif); + LWIP_UNUSED_ARG(pb); + return ERR_IF; +#endif /* PPP_IPV4_SUPPORT */ +} +#endif /* LWIP_IPV4 */ + +#if PPP_IPV6_SUPPORT +/* + * Send an IPv6 packet on the given connection. + */ +static err_t ppp_netif_output_ip6(struct netif *netif, struct pbuf *pb, const ip6_addr_t *ipaddr) { + LWIP_UNUSED_ARG(ipaddr); + return ppp_netif_output(netif, pb, PPP_IPV6); +} +#endif /* PPP_IPV6_SUPPORT */ + +static err_t ppp_netif_output(struct netif *netif, struct pbuf *pb, u16_t protocol) { + ppp_pcb *pcb = (ppp_pcb*)netif->state; + err_t err; + struct pbuf *fpb = NULL; + + /* Check that the link is up. */ + if (0 +#if PPP_IPV4_SUPPORT + || (protocol == PPP_IP && !pcb->if4_up) +#endif /* PPP_IPV4_SUPPORT */ +#if PPP_IPV6_SUPPORT + || (protocol == PPP_IPV6 && !pcb->if6_up) +#endif /* PPP_IPV6_SUPPORT */ + ) { + PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: link not up\n", pcb->netif->num)); + goto err_rte_drop; + } + +#if MPPE_SUPPORT + /* If MPPE is required, refuse any IP packet until we are able to crypt them. */ + if (pcb->settings.require_mppe && pcb->ccp_transmit_method != CI_MPPE) { + PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: MPPE required, not up\n", pcb->netif->num)); + goto err_rte_drop; + } +#endif /* MPPE_SUPPORT */ + +#if VJ_SUPPORT + /* + * Attempt Van Jacobson header compression if VJ is configured and + * this is an IP packet. + */ + if (protocol == PPP_IP && pcb->vj_enabled) { + switch (vj_compress_tcp(&pcb->vj_comp, &pb)) { + case TYPE_IP: + /* No change... + protocol = PPP_IP; */ + break; + case TYPE_COMPRESSED_TCP: + /* vj_compress_tcp() returns a new allocated pbuf, indicate we should free + * our duplicated pbuf later */ + fpb = pb; + protocol = PPP_VJC_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + /* vj_compress_tcp() returns a new allocated pbuf, indicate we should free + * our duplicated pbuf later */ + fpb = pb; + protocol = PPP_VJC_UNCOMP; + break; + default: + PPPDEBUG(LOG_WARNING, ("ppp_netif_output[%d]: bad IP packet\n", pcb->netif->num)); + LINK_STATS_INC(link.proterr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(pcb->netif, ifoutdiscards); + return ERR_VAL; + } + } +#endif /* VJ_SUPPORT */ + +#if CCP_SUPPORT + switch (pcb->ccp_transmit_method) { + case 0: + break; /* Don't compress */ +#if MPPE_SUPPORT + case CI_MPPE: + if ((err = mppe_compress(pcb, &pcb->mppe_comp, &pb, protocol)) != ERR_OK) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(netif, ifoutdiscards); + goto err; + } + /* if VJ compressor returned a new allocated pbuf, free it */ + if (fpb) { + pbuf_free(fpb); + } + /* mppe_compress() returns a new allocated pbuf, indicate we should free + * our duplicated pbuf later */ + fpb = pb; + protocol = PPP_COMP; + break; +#endif /* MPPE_SUPPORT */ + default: + PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: bad CCP transmit method\n", pcb->netif->num)); + goto err_rte_drop; /* Cannot really happen, we only negotiate what we are able to do */ + } +#endif /* CCP_SUPPORT */ + + err = pcb->link_cb->netif_output(pcb, pcb->link_ctx_cb, pb, protocol); + goto err; + +err_rte_drop: + err = ERR_RTE; + LINK_STATS_INC(link.rterr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(netif, ifoutdiscards); +err: + if (fpb) { + pbuf_free(fpb); + } + return err; +} + +/************************************/ +/*** PRIVATE FUNCTION DEFINITIONS ***/ +/************************************/ + +/* Initialize the PPP subsystem. */ +int ppp_init(void) +{ +#if PPPOS_SUPPORT + LWIP_MEMPOOL_INIT(PPPOS_PCB); +#endif +#if PPPOE_SUPPORT + LWIP_MEMPOOL_INIT(PPPOE_IF); +#endif +#if PPPOL2TP_SUPPORT + LWIP_MEMPOOL_INIT(PPPOL2TP_PCB); +#endif +#if LWIP_PPP_API && LWIP_MPU_COMPATIBLE + LWIP_MEMPOOL_INIT(PPPAPI_MSG); +#endif + + LWIP_MEMPOOL_INIT(PPP_PCB); + + /* + * Initialize magic number generator now so that protocols may + * use magic numbers in initialization. + */ + magic_init(); + + return 0; +} + +/* + * Create a new PPP control block. + * + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. + * + * Return a new PPP connection control block pointer + * on success or a null pointer on failure. + */ +ppp_pcb *ppp_new(struct netif *pppif, const struct link_callbacks *callbacks, void *link_ctx_cb, ppp_link_status_cb_fn link_status_cb, void *ctx_cb) { + ppp_pcb *pcb; + const struct protent *protp; + int i; + + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + if (link_status_cb == NULL) { + return NULL; + } + + pcb = (ppp_pcb*)LWIP_MEMPOOL_ALLOC(PPP_PCB); + if (pcb == NULL) { + return NULL; + } + + memset(pcb, 0, sizeof(ppp_pcb)); + + /* default configuration */ +#if PAP_SUPPORT + pcb->settings.pap_timeout_time = UPAP_DEFTIMEOUT; + pcb->settings.pap_max_transmits = UPAP_DEFTRANSMITS; +#if PPP_SERVER + pcb->settings.pap_req_timeout = UPAP_DEFREQTIME; +#endif /* PPP_SERVER */ +#endif /* PAP_SUPPORT */ + +#if CHAP_SUPPORT + pcb->settings.chap_timeout_time = CHAP_DEFTIMEOUT; + pcb->settings.chap_max_transmits = CHAP_DEFTRANSMITS; +#if PPP_SERVER + pcb->settings.chap_rechallenge_time = CHAP_DEFRECHALLENGETIME; +#endif /* PPP_SERVER */ +#endif /* CHAP_SUPPPORT */ + +#if EAP_SUPPORT + pcb->settings.eap_req_time = EAP_DEFREQTIME; + pcb->settings.eap_allow_req = EAP_DEFALLOWREQ; +#if PPP_SERVER + pcb->settings.eap_timeout_time = EAP_DEFTIMEOUT; + pcb->settings.eap_max_transmits = EAP_DEFTRANSMITS; +#endif /* PPP_SERVER */ +#endif /* EAP_SUPPORT */ + + pcb->settings.lcp_loopbackfail = LCP_DEFLOOPBACKFAIL; + pcb->settings.lcp_echo_interval = LCP_ECHOINTERVAL; + pcb->settings.lcp_echo_fails = LCP_MAXECHOFAILS; + + pcb->settings.fsm_timeout_time = FSM_DEFTIMEOUT; + pcb->settings.fsm_max_conf_req_transmits = FSM_DEFMAXCONFREQS; + pcb->settings.fsm_max_term_transmits = FSM_DEFMAXTERMREQS; + pcb->settings.fsm_max_nak_loops = FSM_DEFMAXNAKLOOPS; + + pcb->netif = pppif; + MIB2_INIT_NETIF(pppif, snmp_ifType_ppp, 0); + if (!netif_add(pcb->netif, +#if LWIP_IPV4 + IP4_ADDR_ANY4, IP4_ADDR_BROADCAST, IP4_ADDR_ANY4, +#endif /* LWIP_IPV4 */ + (void *)pcb, ppp_netif_init_cb, NULL)) { + LWIP_MEMPOOL_FREE(PPP_PCB, pcb); + PPPDEBUG(LOG_ERR, ("ppp_new: netif_add failed\n")); + return NULL; + } + + pcb->link_cb = callbacks; + pcb->link_ctx_cb = link_ctx_cb; + pcb->link_status_cb = link_status_cb; + pcb->ctx_cb = ctx_cb; + + /* + * Initialize each protocol. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + (*protp->init)(pcb); + } + + new_phase(pcb, PPP_PHASE_DEAD); + return pcb; +} + +/** Initiate LCP open request */ +void ppp_start(ppp_pcb *pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_start[%d]\n", pcb->netif->num)); + + /* Clean data not taken care by anything else, mostly shared data. */ +#if PPP_STATS_SUPPORT + link_stats_valid = 0; +#endif /* PPP_STATS_SUPPORT */ +#if MPPE_SUPPORT + pcb->mppe_keys_set = 0; + memset(&pcb->mppe_comp, 0, sizeof(pcb->mppe_comp)); + memset(&pcb->mppe_decomp, 0, sizeof(pcb->mppe_decomp)); +#endif /* MPPE_SUPPORT */ +#if VJ_SUPPORT + vj_compress_init(&pcb->vj_comp); +#endif /* VJ_SUPPORT */ + + /* Start protocol */ + new_phase(pcb, PPP_PHASE_ESTABLISH); + lcp_open(pcb); + lcp_lowerup(pcb); + PPPDEBUG(LOG_DEBUG, ("ppp_start[%d]: finished\n", pcb->netif->num)); +} + +/** Called when link failed to setup */ +void ppp_link_failed(ppp_pcb *pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_link_failed[%d]\n", pcb->netif->num)); + new_phase(pcb, PPP_PHASE_DEAD); + pcb->err_code = PPPERR_OPEN; + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); +} + +/** Called when link is normally down (i.e. it was asked to end) */ +void ppp_link_end(ppp_pcb *pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_link_end[%d]\n", pcb->netif->num)); + new_phase(pcb, PPP_PHASE_DEAD); + if (pcb->err_code == PPPERR_NONE) { + pcb->err_code = PPPERR_CONNECT; + } + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); +} + +/* + * Pass the processed input packet to the appropriate handler. + * This function and all handlers run in the context of the tcpip_thread + */ +void ppp_input(ppp_pcb *pcb, struct pbuf *pb) { + u16_t protocol; +#if PPP_DEBUG && PPP_PROTOCOLNAME + const char *pname; +#endif /* PPP_DEBUG && PPP_PROTOCOLNAME */ + + magic_randomize(); + + if (pb->len < 2) { + PPPDEBUG(LOG_ERR, ("ppp_input[%d]: packet too short\n", pcb->netif->num)); + goto drop; + } + protocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + +#if PRINTPKT_SUPPORT + ppp_dump_packet(pcb, "rcvd", (unsigned char *)pb->payload, pb->len); +#endif /* PRINTPKT_SUPPORT */ + + pbuf_header(pb, -(s16_t)sizeof(protocol)); + + LINK_STATS_INC(link.recv); + MIB2_STATS_NETIF_INC(pcb->netif, ifinucastpkts); + MIB2_STATS_NETIF_ADD(pcb->netif, ifinoctets, pb->tot_len); + + /* + * Toss all non-LCP packets unless LCP is OPEN. + */ + if (protocol != PPP_LCP && pcb->lcp_fsm.state != PPP_FSM_OPENED) { + ppp_dbglog("Discarded non-LCP packet when LCP not open"); + goto drop; + } + + /* + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if (pcb->phase <= PPP_PHASE_AUTHENTICATE + && !(protocol == PPP_LCP +#if LQR_SUPPORT + || protocol == PPP_LQR +#endif /* LQR_SUPPORT */ +#if PAP_SUPPORT + || protocol == PPP_PAP +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + || protocol == PPP_CHAP +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || protocol == PPP_EAP +#endif /* EAP_SUPPORT */ + )) { + ppp_dbglog("discarding proto 0x%x in phase %d", protocol, pcb->phase); + goto drop; + } + +#if CCP_SUPPORT +#if MPPE_SUPPORT + /* + * MPPE is required and unencrypted data has arrived (this + * should never happen!). We should probably drop the link if + * the protocol is in the range of what should be encrypted. + * At the least, we drop this packet. + */ + if (pcb->settings.require_mppe && protocol != PPP_COMP && protocol < 0x8000) { + PPPDEBUG(LOG_ERR, ("ppp_input[%d]: MPPE required, received unencrypted data!\n", pcb->netif->num)); + goto drop; + } +#endif /* MPPE_SUPPORT */ + + if (protocol == PPP_COMP) { + u8_t *pl; + + switch (pcb->ccp_receive_method) { +#if MPPE_SUPPORT + case CI_MPPE: + if (mppe_decompress(pcb, &pcb->mppe_decomp, &pb) != ERR_OK) { + goto drop; + } + break; +#endif /* MPPE_SUPPORT */ + default: + PPPDEBUG(LOG_ERR, ("ppp_input[%d]: bad CCP receive method\n", pcb->netif->num)); + goto drop; /* Cannot really happen, we only negotiate what we are able to do */ + } + + /* Assume no PFC */ + if (pb->len < 2) { + goto drop; + } + + /* Extract and hide protocol (do PFC decompression if necessary) */ + pl = (u8_t*)pb->payload; + if (pl[0] & 0x01) { + protocol = pl[0]; + pbuf_header(pb, -(s16_t)1); + } else { + protocol = (pl[0] << 8) | pl[1]; + pbuf_header(pb, -(s16_t)2); + } + } +#endif /* CCP_SUPPORT */ + + switch(protocol) { + +#if PPP_IPV4_SUPPORT + case PPP_IP: /* Internet Protocol */ + PPPDEBUG(LOG_INFO, ("ppp_input[%d]: ip in pbuf len=%d\n", pcb->netif->num, pb->tot_len)); + ip4_input(pb, pcb->netif); + return; +#endif /* PPP_IPV4_SUPPORT */ + +#if PPP_IPV6_SUPPORT + case PPP_IPV6: /* Internet Protocol Version 6 */ + PPPDEBUG(LOG_INFO, ("ppp_input[%d]: ip6 in pbuf len=%d\n", pcb->netif->num, pb->tot_len)); + ip6_input(pb, pcb->netif); + return; +#endif /* PPP_IPV6_SUPPORT */ + +#if VJ_SUPPORT + case PPP_VJC_COMP: /* VJ compressed TCP */ + /* + * Clip off the VJ header and prepend the rebuilt TCP/IP header and + * pass the result to IP. + */ + PPPDEBUG(LOG_INFO, ("ppp_input[%d]: vj_comp in pbuf len=%d\n", pcb->netif->num, pb->tot_len)); + if (pcb->vj_enabled && vj_uncompress_tcp(&pb, &pcb->vj_comp) >= 0) { + ip4_input(pb, pcb->netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping VJ compressed\n", pcb->netif->num)); + break; + + case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */ + /* + * Process the TCP/IP header for VJ header compression and then pass + * the packet to IP. + */ + PPPDEBUG(LOG_INFO, ("ppp_input[%d]: vj_un in pbuf len=%d\n", pcb->netif->num, pb->tot_len)); + if (pcb->vj_enabled && vj_uncompress_uncomp(pb, &pcb->vj_comp) >= 0) { + ip4_input(pb, pcb->netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping VJ uncompressed\n", pcb->netif->num)); + break; +#endif /* VJ_SUPPORT */ + + default: { + int i; + const struct protent *protp; + + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol) { + pb = ppp_singlebuf(pb); + (*protp->input)(pcb, (u8_t*)pb->payload, pb->len); + goto out; + } +#if 0 /* UNUSED + * + * This is actually a (hacked?) way for the Linux kernel to pass a data + * packet to pppd. pppd in normal condition only do signaling + * (LCP, PAP, CHAP, IPCP, ...) and does not handle any data packet at all. + * + * We don't even need this interface, which is only there because of PPP + * interface limitation between Linux kernel and pppd. For MPPE, which uses + * CCP to negotiate although it is not really a (de)compressor, we added + * ccp_resetrequest() in CCP and MPPE input data flow is calling either + * ccp_resetrequest() or lcp_close() if the issue is, respectively, non-fatal + * or fatal, this is what ccp_datainput() really do. + */ + if (protocol == (protp->protocol & ~0x8000) + && protp->datainput != NULL) { + (*protp->datainput)(pcb, pb->payload, pb->len); + goto out; + } +#endif /* UNUSED */ + } + +#if PPP_DEBUG +#if PPP_PROTOCOLNAME + pname = protocol_name(protocol); + if (pname != NULL) { + ppp_warn("Unsupported protocol '%s' (0x%x) received", pname, protocol); + } else +#endif /* PPP_PROTOCOLNAME */ + ppp_warn("Unsupported protocol 0x%x received", protocol); +#endif /* PPP_DEBUG */ + pbuf_header(pb, (s16_t)sizeof(protocol)); + lcp_sprotrej(pcb, (u8_t*)pb->payload, pb->len); + } + break; + } + +drop: + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(pcb->netif, ifindiscards); + +out: + pbuf_free(pb); +} + +/* merge a pbuf chain into one pbuf */ +struct pbuf *ppp_singlebuf(struct pbuf *p) { + struct pbuf *q, *b; + u8_t *pl; + + if(p->tot_len == p->len) { + return p; + } + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(!q) { + PPPDEBUG(LOG_ERR, + ("ppp_singlebuf: unable to alloc new buf (%d)\n", p->tot_len)); + return p; /* live dangerously */ + } + + for(b = p, pl = (u8_t*)q->payload; b != NULL; b = b->next) { + MEMCPY(pl, b->payload, b->len); + pl += b->len; + } + + pbuf_free(p); + + return q; +} + +/* + * Write a pbuf to a ppp link, only used from PPP functions + * to send PPP packets. + * + * IPv4 and IPv6 packets from lwIP are sent, respectively, + * with ppp_netif_output_ip4() and ppp_netif_output_ip6() + * functions (which are callbacks of the netif PPP interface). + */ +err_t ppp_write(ppp_pcb *pcb, struct pbuf *p) { +#if PRINTPKT_SUPPORT + ppp_dump_packet(pcb, "sent", (unsigned char *)p->payload+2, p->len-2); +#endif /* PRINTPKT_SUPPORT */ + return pcb->link_cb->write(pcb, pcb->link_ctx_cb, p); +} + +void ppp_link_terminated(ppp_pcb *pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated[%d]\n", pcb->netif->num)); + pcb->link_cb->disconnect(pcb, pcb->link_ctx_cb); + PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated[%d]: finished.\n", pcb->netif->num)); +} + + +/************************************************************************ + * Functions called by various PPP subsystems to configure + * the PPP interface or change the PPP phase. + */ + +/* + * new_phase - signal the start of a new phase of pppd's operation. + */ +void new_phase(ppp_pcb *pcb, int p) { + pcb->phase = p; + PPPDEBUG(LOG_DEBUG, ("ppp phase changed[%d]: phase=%d\n", pcb->netif->num, pcb->phase)); +#if PPP_NOTIFY_PHASE + if (pcb->notify_phase_cb != NULL) { + pcb->notify_phase_cb(pcb, p, pcb->ctx_cb); + } +#endif /* PPP_NOTIFY_PHASE */ +} + +/* + * ppp_send_config - configure the transmit-side characteristics of + * the ppp interface. + */ +int ppp_send_config(ppp_pcb *pcb, int mtu, u32_t accm, int pcomp, int accomp) { + LWIP_UNUSED_ARG(mtu); + /* pcb->mtu = mtu; -- set correctly with netif_set_mtu */ + + if (pcb->link_cb->send_config) { + pcb->link_cb->send_config(pcb, pcb->link_ctx_cb, accm, pcomp, accomp); + } + + PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]\n", pcb->netif->num) ); + return 0; +} + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp) { + LWIP_UNUSED_ARG(mru); + + if (pcb->link_cb->recv_config) { + pcb->link_cb->recv_config(pcb, pcb->link_ctx_cb, accm, pcomp, accomp); + } + + PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]\n", pcb->netif->num)); + return 0; +} + +#if PPP_IPV4_SUPPORT +/* + * sifaddr - Config the interface IP addresses and netmask. + */ +int sifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr, u32_t netmask) { + ip4_addr_t ip, nm, gw; + + ip4_addr_set_u32(&ip, our_adr); + ip4_addr_set_u32(&nm, netmask); + ip4_addr_set_u32(&gw, his_adr); + netif_set_addr(pcb->netif, &ip, &nm, &gw); + return 1; +} + +/******************************************************************** + * + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + */ +int cifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr) { + LWIP_UNUSED_ARG(our_adr); + LWIP_UNUSED_ARG(his_adr); + + netif_set_addr(pcb->netif, IP4_ADDR_ANY4, IP4_ADDR_BROADCAST, IP4_ADDR_ANY4); + return 1; +} + +#if 0 /* UNUSED - PROXY ARP */ +/******************************************************************** + * + * sifproxyarp - Make a proxy ARP entry for the peer. + */ + +int sifproxyarp(ppp_pcb *pcb, u32_t his_adr) { + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(his_adr); + return 0; +} + +/******************************************************************** + * + * cifproxyarp - Delete the proxy ARP entry for the peer. + */ + +int cifproxyarp(ppp_pcb *pcb, u32_t his_adr) { + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(his_adr); + return 0; +} +#endif /* UNUSED - PROXY ARP */ + +#if LWIP_DNS +/* + * sdns - Config the DNS servers + */ +int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) { + ip_addr_t ns; + LWIP_UNUSED_ARG(pcb); + + ip_addr_set_ip4_u32(&ns, ns1); + dns_setserver(0, &ns); + ip_addr_set_ip4_u32(&ns, ns2); + dns_setserver(1, &ns); + return 1; +} + +/******************************************************************** + * + * cdns - Clear the DNS servers + */ +int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) { + const ip_addr_t *nsa; + ip_addr_t nsb; + LWIP_UNUSED_ARG(pcb); + + nsa = dns_getserver(0); + ip_addr_set_ip4_u32(&nsb, ns1); + if (ip_addr_cmp(nsa, &nsb)) { + dns_setserver(0, IP_ADDR_ANY); + } + nsa = dns_getserver(1); + ip_addr_set_ip4_u32(&nsb, ns2); + if (ip_addr_cmp(nsa, &nsb)) { + dns_setserver(1, IP_ADDR_ANY); + } + return 1; +} +#endif /* LWIP_DNS */ + +#if VJ_SUPPORT +/******************************************************************** + * + * sifvjcomp - config tcp header compression + */ +int sifvjcomp(ppp_pcb *pcb, int vjcomp, int cidcomp, int maxcid) { + pcb->vj_enabled = vjcomp; + pcb->vj_comp.compressSlot = cidcomp; + pcb->vj_comp.maxSlotIndex = maxcid; + PPPDEBUG(LOG_INFO, ("sifvjcomp[%d]: VJ compress enable=%d slot=%d max slot=%d\n", + pcb->netif->num, vjcomp, cidcomp, maxcid)); + return 0; +} +#endif /* VJ_SUPPORT */ + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int sifup(ppp_pcb *pcb) { + pcb->if4_up = 1; + pcb->err_code = PPPERR_NONE; + netif_set_link_up(pcb->netif); + + PPPDEBUG(LOG_DEBUG, ("sifup[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code)); + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); + return 1; +} + +/******************************************************************** + * + * sifdown - Disable the indicated protocol and config the interface + * down if there are no remaining protocols. + */ +int sifdown(ppp_pcb *pcb) { + + pcb->if4_up = 0; + + if (1 +#if PPP_IPV6_SUPPORT + /* set the interface down if IPv6 is down as well */ + && !pcb->if6_up +#endif /* PPP_IPV6_SUPPORT */ + ) { + /* make sure the netif link callback is called */ + netif_set_link_down(pcb->netif); + } + PPPDEBUG(LOG_DEBUG, ("sifdown[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code)); + return 1; +} + +/******************************************************************** + * + * Return user specified netmask, modified by any mask we might determine + * for address `addr' (in network byte order). + * Here we scan through the system's list of interfaces, looking for + * any non-point-to-point interfaces which might appear to be on the same + * network as `addr'. If we find any, we OR in their netmask to the + * user-specified netmask. + */ +u32_t get_mask(u32_t addr) { +#if 0 + u32_t mask, nmask; + + addr = lwip_htonl(addr); + if (IP_CLASSA(addr)) { /* determine network mask for address class */ + nmask = IP_CLASSA_NET; + } else if (IP_CLASSB(addr)) { + nmask = IP_CLASSB_NET; + } else { + nmask = IP_CLASSC_NET; + } + + /* class D nets are disallowed by bad_ip_adrs */ + mask = PP_HTONL(0xffffff00UL) | lwip_htonl(nmask); + + /* XXX + * Scan through the system's network interfaces. + * Get each netmask and OR them into our mask. + */ + /* return mask; */ + return mask; +#endif /* 0 */ + LWIP_UNUSED_ARG(addr); + return IPADDR_BROADCAST; +} +#endif /* PPP_IPV4_SUPPORT */ + +#if PPP_IPV6_SUPPORT +#define IN6_LLADDR_FROM_EUI64(ip6, eui64) do { \ + ip6.addr[0] = PP_HTONL(0xfe800000); \ + ip6.addr[1] = 0; \ + eui64_copy(eui64, ip6.addr[2]); \ + } while (0) + +/******************************************************************** + * + * sif6addr - Config the interface with an IPv6 link-local address + */ +int sif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64) { + ip6_addr_t ip6; + LWIP_UNUSED_ARG(his_eui64); + + IN6_LLADDR_FROM_EUI64(ip6, our_eui64); + netif_ip6_addr_set(pcb->netif, 0, &ip6); + netif_ip6_addr_set_state(pcb->netif, 0, IP6_ADDR_PREFERRED); + /* FIXME: should we add an IPv6 static neighbor using his_eui64 ? */ + return 1; +} + +/******************************************************************** + * + * cif6addr - Remove IPv6 address from interface + */ +int cif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64) { + LWIP_UNUSED_ARG(our_eui64); + LWIP_UNUSED_ARG(his_eui64); + + netif_ip6_addr_set(pcb->netif, 0, IP6_ADDR_ANY6); + netif_ip6_addr_set_state(pcb->netif, 0, IP6_ADDR_INVALID); + return 1; +} + +/* + * sif6up - Config the interface up and enable IPv6 packets to pass. + */ +int sif6up(ppp_pcb *pcb) { + + pcb->if6_up = 1; + pcb->err_code = PPPERR_NONE; + netif_set_link_up(pcb->netif); + + PPPDEBUG(LOG_DEBUG, ("sif6up[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code)); + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); + return 1; +} + +/******************************************************************** + * + * sif6down - Disable the indicated protocol and config the interface + * down if there are no remaining protocols. + */ +int sif6down(ppp_pcb *pcb) { + + pcb->if6_up = 0; + + if (1 +#if PPP_IPV4_SUPPORT + /* set the interface down if IPv4 is down as well */ + && !pcb->if4_up +#endif /* PPP_IPV4_SUPPORT */ + ) { + /* make sure the netif link callback is called */ + netif_set_link_down(pcb->netif); + } + PPPDEBUG(LOG_DEBUG, ("sif6down[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code)); + return 1; +} +#endif /* PPP_IPV6_SUPPORT */ + +#if DEMAND_SUPPORT +/* + * sifnpmode - Set the mode for handling packets for a given NP. + */ +int sifnpmode(ppp_pcb *pcb, int proto, enum NPmode mode) { + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(proto); + LWIP_UNUSED_ARG(mode); + return 0; +} +#endif /* DEMAND_SUPPORT */ + +/* + * netif_set_mtu - set the MTU on the PPP network interface. + */ +void netif_set_mtu(ppp_pcb *pcb, int mtu) { + + pcb->netif->mtu = mtu; + PPPDEBUG(LOG_INFO, ("netif_set_mtu[%d]: mtu=%d\n", pcb->netif->num, mtu)); +} + +/* + * netif_get_mtu - get PPP interface MTU + */ +int netif_get_mtu(ppp_pcb *pcb) { + + return pcb->netif->mtu; +} + +#if CCP_SUPPORT +#if 0 /* unused */ +/* + * ccp_test - whether a given compression method is acceptable for use. + */ +int +ccp_test(ppp_pcb *pcb, u_char *opt_ptr, int opt_len, int for_transmit) +{ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(opt_ptr); + LWIP_UNUSED_ARG(opt_len); + LWIP_UNUSED_ARG(for_transmit); + return -1; +} +#endif /* unused */ + +/* + * ccp_set - inform about the current state of CCP. + */ +void +ccp_set(ppp_pcb *pcb, u8_t isopen, u8_t isup, u8_t receive_method, u8_t transmit_method) +{ + LWIP_UNUSED_ARG(isopen); + LWIP_UNUSED_ARG(isup); + pcb->ccp_receive_method = receive_method; + pcb->ccp_transmit_method = transmit_method; + PPPDEBUG(LOG_DEBUG, ("ccp_set[%d]: is_open=%d, is_up=%d, receive_method=%u, transmit_method=%u\n", + pcb->netif->num, isopen, isup, receive_method, transmit_method)); +} + +void +ccp_reset_comp(ppp_pcb *pcb) +{ + switch (pcb->ccp_transmit_method) { +#if MPPE_SUPPORT + case CI_MPPE: + mppe_comp_reset(pcb, &pcb->mppe_comp); + break; +#endif /* MPPE_SUPPORT */ + default: + break; + } +} + +void +ccp_reset_decomp(ppp_pcb *pcb) +{ + switch (pcb->ccp_receive_method) { +#if MPPE_SUPPORT + case CI_MPPE: + mppe_decomp_reset(pcb, &pcb->mppe_decomp); + break; +#endif /* MPPE_SUPPORT */ + default: + break; + } +} + +#if 0 /* unused */ +/* + * ccp_fatal_error - returns 1 if decompression was disabled as a + * result of an error detected after decompression of a packet, + * 0 otherwise. This is necessary because of patent nonsense. + */ +int +ccp_fatal_error(ppp_pcb *pcb) +{ + LWIP_UNUSED_ARG(pcb); + return 1; +} +#endif /* unused */ +#endif /* CCP_SUPPORT */ + +#if PPP_IDLETIMELIMIT +/******************************************************************** + * + * get_idle_time - return how long the link has been idle. + */ +int get_idle_time(ppp_pcb *pcb, struct ppp_idle *ip) { + /* FIXME: add idle time support and make it optional */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(ip); + return 1; +} +#endif /* PPP_IDLETIMELIMIT */ + +#if DEMAND_SUPPORT +/******************************************************************** + * + * get_loop_output - get outgoing packets from the ppp device, + * and detect when we want to bring the real link up. + * Return value is 1 if we need to bring up the link, 0 otherwise. + */ +int get_loop_output(void) { + return 0; +} +#endif /* DEMAND_SUPPORT */ + +#if PPP_PROTOCOLNAME +/* List of protocol names, to make our messages a little more informative. */ +struct protocol_list { + u_short proto; + const char *name; +} const protocol_list[] = { + { 0x21, "IP" }, + { 0x23, "OSI Network Layer" }, + { 0x25, "Xerox NS IDP" }, + { 0x27, "DECnet Phase IV" }, + { 0x29, "Appletalk" }, + { 0x2b, "Novell IPX" }, + { 0x2d, "VJ compressed TCP/IP" }, + { 0x2f, "VJ uncompressed TCP/IP" }, + { 0x31, "Bridging PDU" }, + { 0x33, "Stream Protocol ST-II" }, + { 0x35, "Banyan Vines" }, + { 0x39, "AppleTalk EDDP" }, + { 0x3b, "AppleTalk SmartBuffered" }, + { 0x3d, "Multi-Link" }, + { 0x3f, "NETBIOS Framing" }, + { 0x41, "Cisco Systems" }, + { 0x43, "Ascom Timeplex" }, + { 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" }, + { 0x47, "DCA Remote Lan" }, + { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" }, + { 0x4b, "SNA over 802.2" }, + { 0x4d, "SNA" }, + { 0x4f, "IP6 Header Compression" }, + { 0x51, "KNX Bridging Data" }, + { 0x53, "Encryption" }, + { 0x55, "Individual Link Encryption" }, + { 0x57, "IPv6" }, + { 0x59, "PPP Muxing" }, + { 0x5b, "Vendor-Specific Network Protocol" }, + { 0x61, "RTP IPHC Full Header" }, + { 0x63, "RTP IPHC Compressed TCP" }, + { 0x65, "RTP IPHC Compressed non-TCP" }, + { 0x67, "RTP IPHC Compressed UDP 8" }, + { 0x69, "RTP IPHC Compressed RTP 8" }, + { 0x6f, "Stampede Bridging" }, + { 0x73, "MP+" }, + { 0xc1, "NTCITS IPI" }, + { 0xfb, "single-link compression" }, + { 0xfd, "Compressed Datagram" }, + { 0x0201, "802.1d Hello Packets" }, + { 0x0203, "IBM Source Routing BPDU" }, + { 0x0205, "DEC LANBridge100 Spanning Tree" }, + { 0x0207, "Cisco Discovery Protocol" }, + { 0x0209, "Netcs Twin Routing" }, + { 0x020b, "STP - Scheduled Transfer Protocol" }, + { 0x020d, "EDP - Extreme Discovery Protocol" }, + { 0x0211, "Optical Supervisory Channel Protocol" }, + { 0x0213, "Optical Supervisory Channel Protocol" }, + { 0x0231, "Luxcom" }, + { 0x0233, "Sigma Network Systems" }, + { 0x0235, "Apple Client Server Protocol" }, + { 0x0281, "MPLS Unicast" }, + { 0x0283, "MPLS Multicast" }, + { 0x0285, "IEEE p1284.4 standard - data packets" }, + { 0x0287, "ETSI TETRA Network Protocol Type 1" }, + { 0x0289, "Multichannel Flow Treatment Protocol" }, + { 0x2063, "RTP IPHC Compressed TCP No Delta" }, + { 0x2065, "RTP IPHC Context State" }, + { 0x2067, "RTP IPHC Compressed UDP 16" }, + { 0x2069, "RTP IPHC Compressed RTP 16" }, + { 0x4001, "Cray Communications Control Protocol" }, + { 0x4003, "CDPD Mobile Network Registration Protocol" }, + { 0x4005, "Expand accelerator protocol" }, + { 0x4007, "ODSICP NCP" }, + { 0x4009, "DOCSIS DLL" }, + { 0x400B, "Cetacean Network Detection Protocol" }, + { 0x4021, "Stacker LZS" }, + { 0x4023, "RefTek Protocol" }, + { 0x4025, "Fibre Channel" }, + { 0x4027, "EMIT Protocols" }, + { 0x405b, "Vendor-Specific Protocol (VSP)" }, + { 0x8021, "Internet Protocol Control Protocol" }, + { 0x8023, "OSI Network Layer Control Protocol" }, + { 0x8025, "Xerox NS IDP Control Protocol" }, + { 0x8027, "DECnet Phase IV Control Protocol" }, + { 0x8029, "Appletalk Control Protocol" }, + { 0x802b, "Novell IPX Control Protocol" }, + { 0x8031, "Bridging NCP" }, + { 0x8033, "Stream Protocol Control Protocol" }, + { 0x8035, "Banyan Vines Control Protocol" }, + { 0x803d, "Multi-Link Control Protocol" }, + { 0x803f, "NETBIOS Framing Control Protocol" }, + { 0x8041, "Cisco Systems Control Protocol" }, + { 0x8043, "Ascom Timeplex" }, + { 0x8045, "Fujitsu LBLB Control Protocol" }, + { 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" }, + { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" }, + { 0x804b, "SNA over 802.2 Control Protocol" }, + { 0x804d, "SNA Control Protocol" }, + { 0x804f, "IP6 Header Compression Control Protocol" }, + { 0x8051, "KNX Bridging Control Protocol" }, + { 0x8053, "Encryption Control Protocol" }, + { 0x8055, "Individual Link Encryption Control Protocol" }, + { 0x8057, "IPv6 Control Protocol" }, + { 0x8059, "PPP Muxing Control Protocol" }, + { 0x805b, "Vendor-Specific Network Control Protocol (VSNCP)" }, + { 0x806f, "Stampede Bridging Control Protocol" }, + { 0x8073, "MP+ Control Protocol" }, + { 0x80c1, "NTCITS IPI Control Protocol" }, + { 0x80fb, "Single Link Compression Control Protocol" }, + { 0x80fd, "Compression Control Protocol" }, + { 0x8207, "Cisco Discovery Protocol Control" }, + { 0x8209, "Netcs Twin Routing" }, + { 0x820b, "STP - Control Protocol" }, + { 0x820d, "EDPCP - Extreme Discovery Protocol Ctrl Prtcl" }, + { 0x8235, "Apple Client Server Protocol Control" }, + { 0x8281, "MPLSCP" }, + { 0x8285, "IEEE p1284.4 standard - Protocol Control" }, + { 0x8287, "ETSI TETRA TNP1 Control Protocol" }, + { 0x8289, "Multichannel Flow Treatment Protocol" }, + { 0xc021, "Link Control Protocol" }, + { 0xc023, "Password Authentication Protocol" }, + { 0xc025, "Link Quality Report" }, + { 0xc027, "Shiva Password Authentication Protocol" }, + { 0xc029, "CallBack Control Protocol (CBCP)" }, + { 0xc02b, "BACP Bandwidth Allocation Control Protocol" }, + { 0xc02d, "BAP" }, + { 0xc05b, "Vendor-Specific Authentication Protocol (VSAP)" }, + { 0xc081, "Container Control Protocol" }, + { 0xc223, "Challenge Handshake Authentication Protocol" }, + { 0xc225, "RSA Authentication Protocol" }, + { 0xc227, "Extensible Authentication Protocol" }, + { 0xc229, "Mitsubishi Security Info Exch Ptcl (SIEP)" }, + { 0xc26f, "Stampede Bridging Authorization Protocol" }, + { 0xc281, "Proprietary Authentication Protocol" }, + { 0xc283, "Proprietary Authentication Protocol" }, + { 0xc481, "Proprietary Node ID Authentication Protocol" }, + { 0, NULL }, +}; + +/* + * protocol_name - find a name for a PPP protocol. + */ +const char * protocol_name(int proto) { + const struct protocol_list *lp; + + for (lp = protocol_list; lp->proto != 0; ++lp) { + if (proto == lp->proto) { + return lp->name; + } + } + return NULL; +} +#endif /* PPP_PROTOCOLNAME */ + +#if PPP_STATS_SUPPORT + +/* ---- Note on PPP Stats support ---- + * + * The one willing link stats support should add the get_ppp_stats() + * to fetch statistics from lwIP. + */ + +/* + * reset_link_stats - "reset" stats when link goes up. + */ +void reset_link_stats(int u) { + if (!get_ppp_stats(u, &old_link_stats)) { + return; + } + gettimeofday(&start_time, NULL); +} + +/* + * update_link_stats - get stats at link termination. + */ +void update_link_stats(int u) { + struct timeval now; + char numbuf[32]; + + if (!get_ppp_stats(u, &link_stats) || gettimeofday(&now, NULL) < 0) { + return; + } + link_connect_time = now.tv_sec - start_time.tv_sec; + link_stats_valid = 1; + + link_stats.bytes_in -= old_link_stats.bytes_in; + link_stats.bytes_out -= old_link_stats.bytes_out; + link_stats.pkts_in -= old_link_stats.pkts_in; + link_stats.pkts_out -= old_link_stats.pkts_out; +} + +void print_link_stats() { + /* + * Print connect time and statistics. + */ + if (link_stats_valid) { + int t = (link_connect_time + 5) / 6; /* 1/10ths of minutes */ + info("Connect time %d.%d minutes.", t/10, t%10); + info("Sent %u bytes, received %u bytes.", link_stats.bytes_out, link_stats.bytes_in); + link_stats_valid = 0; + } +} +#endif /* PPP_STATS_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/pppapi.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppapi.c new file mode 100644 index 0000000..947f7ba --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppapi.c @@ -0,0 +1,427 @@ +/** + * @file + * Point To Point Protocol Sequential API module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "netif/ppp/ppp_opts.h" + +#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/pppapi.h" +#include "lwip/priv/tcpip_priv.h" +#include "netif/ppp/pppoe.h" +#include "netif/ppp/pppol2tp.h" +#include "netif/ppp/pppos.h" + +#if LWIP_MPU_COMPATIBLE +LWIP_MEMPOOL_DECLARE(PPPAPI_MSG, MEMP_NUM_PPP_API_MSG, sizeof(struct pppapi_msg), "PPPAPI_MSG") +#endif + +#define PPPAPI_VAR_REF(name) API_VAR_REF(name) +#define PPPAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct pppapi_msg, name) +#define PPPAPI_VAR_ALLOC(name) API_VAR_ALLOC_POOL(struct pppapi_msg, PPPAPI_MSG, name, ERR_MEM) +#define PPPAPI_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC_POOL(struct pppapi_msg, PPPAPI_MSG, name, NULL) +#define PPPAPI_VAR_FREE(name) API_VAR_FREE_POOL(PPPAPI_MSG, name) + +/** + * Call ppp_set_default() inside the tcpip_thread context. + */ +static err_t +pppapi_do_ppp_set_default(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + ppp_set_default(msg->msg.ppp); + return ERR_OK; +} + +/** + * Call ppp_set_default() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_set_default(ppp_pcb *pcb) +{ + err_t err; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = pcb; + err = tcpip_api_call(pppapi_do_ppp_set_default, &PPPAPI_VAR_REF(msg).call); + PPPAPI_VAR_FREE(msg); + return err; +} + + +#if PPP_NOTIFY_PHASE +/** + * Call ppp_set_notify_phase_callback() inside the tcpip_thread context. + */ +static err_t +pppapi_do_ppp_set_notify_phase_callback(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + ppp_set_notify_phase_callback(msg->msg.ppp, msg->msg.msg.setnotifyphasecb.notify_phase_cb); + return ERR_OK; +} + +/** + * Call ppp_set_notify_phase_callback() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb) +{ + err_t err; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = pcb; + PPPAPI_VAR_REF(msg).msg.msg.setnotifyphasecb.notify_phase_cb = notify_phase_cb; + err = tcpip_api_call(pppapi_do_ppp_set_notify_phase_callback, &PPPAPI_VAR_REF(msg).call); + PPPAPI_VAR_FREE(msg); + return err; +} +#endif /* PPP_NOTIFY_PHASE */ + + +#if PPPOS_SUPPORT +/** + * Call pppos_create() inside the tcpip_thread context. + */ +static err_t +pppapi_do_pppos_create(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + msg->msg.ppp = pppos_create(msg->msg.msg.serialcreate.pppif, msg->msg.msg.serialcreate.output_cb, + msg->msg.msg.serialcreate.link_status_cb, msg->msg.msg.serialcreate.ctx_cb); + return ERR_OK; +} + +/** + * Call pppos_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +ppp_pcb* +pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) +{ + ppp_pcb* result; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC_RETURN_NULL(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = NULL; + PPPAPI_VAR_REF(msg).msg.msg.serialcreate.pppif = pppif; + PPPAPI_VAR_REF(msg).msg.msg.serialcreate.output_cb = output_cb; + PPPAPI_VAR_REF(msg).msg.msg.serialcreate.link_status_cb = link_status_cb; + PPPAPI_VAR_REF(msg).msg.msg.serialcreate.ctx_cb = ctx_cb; + tcpip_api_call(pppapi_do_pppos_create, &PPPAPI_VAR_REF(msg).call); + result = PPPAPI_VAR_REF(msg).msg.ppp; + PPPAPI_VAR_FREE(msg); + return result; +} +#endif /* PPPOS_SUPPORT */ + + +#if PPPOE_SUPPORT +/** + * Call pppoe_create() inside the tcpip_thread context. + */ +static err_t +pppapi_do_pppoe_create(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + msg->msg.ppp = pppoe_create(msg->msg.msg.ethernetcreate.pppif, msg->msg.msg.ethernetcreate.ethif, + msg->msg.msg.ethernetcreate.service_name, msg->msg.msg.ethernetcreate.concentrator_name, + msg->msg.msg.ethernetcreate.link_status_cb, msg->msg.msg.ethernetcreate.ctx_cb); + return ERR_OK; +} + +/** + * Call pppoe_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +ppp_pcb* +pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name, + const char *concentrator_name, ppp_link_status_cb_fn link_status_cb, + void *ctx_cb) +{ + ppp_pcb* result; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC_RETURN_NULL(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = NULL; + PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.pppif = pppif; + PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.ethif = ethif; + PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.service_name = service_name; + PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.concentrator_name = concentrator_name; + PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.link_status_cb = link_status_cb; + PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.ctx_cb = ctx_cb; + tcpip_api_call(pppapi_do_pppoe_create, &PPPAPI_VAR_REF(msg).call); + result = PPPAPI_VAR_REF(msg).msg.ppp; + PPPAPI_VAR_FREE(msg); + return result; +} +#endif /* PPPOE_SUPPORT */ + + +#if PPPOL2TP_SUPPORT +/** + * Call pppol2tp_create() inside the tcpip_thread context. + */ +static err_t +pppapi_do_pppol2tp_create(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + msg->msg.ppp = pppol2tp_create(msg->msg.msg.l2tpcreate.pppif, + msg->msg.msg.l2tpcreate.netif, API_EXPR_REF(msg->msg.msg.l2tpcreate.ipaddr), msg->msg.msg.l2tpcreate.port, +#if PPPOL2TP_AUTH_SUPPORT + msg->msg.msg.l2tpcreate.secret, + msg->msg.msg.l2tpcreate.secret_len, +#else /* PPPOL2TP_AUTH_SUPPORT */ + NULL, + 0, +#endif /* PPPOL2TP_AUTH_SUPPORT */ + msg->msg.msg.l2tpcreate.link_status_cb, msg->msg.msg.l2tpcreate.ctx_cb); + return ERR_OK; +} + +/** + * Call pppol2tp_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +ppp_pcb* +pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port, + const u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) +{ + ppp_pcb* result; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC_RETURN_NULL(msg); +#if !PPPOL2TP_AUTH_SUPPORT + LWIP_UNUSED_ARG(secret); + LWIP_UNUSED_ARG(secret_len); +#endif /* !PPPOL2TP_AUTH_SUPPORT */ + + PPPAPI_VAR_REF(msg).msg.ppp = NULL; + PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.pppif = pppif; + PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.netif = netif; + PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.ipaddr = PPPAPI_VAR_REF(ipaddr); + PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.port = port; +#if PPPOL2TP_AUTH_SUPPORT + PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.secret = secret; + PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.secret_len = secret_len; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.link_status_cb = link_status_cb; + PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.ctx_cb = ctx_cb; + tcpip_api_call(pppapi_do_pppol2tp_create, &PPPAPI_VAR_REF(msg).call); + result = PPPAPI_VAR_REF(msg).msg.ppp; + PPPAPI_VAR_FREE(msg); + return result; +} +#endif /* PPPOL2TP_SUPPORT */ + + +/** + * Call ppp_connect() inside the tcpip_thread context. + */ +static err_t +pppapi_do_ppp_connect(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + return ppp_connect(msg->msg.ppp, msg->msg.msg.connect.holdoff); +} + +/** + * Call ppp_connect() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_connect(ppp_pcb *pcb, u16_t holdoff) +{ + err_t err; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = pcb; + PPPAPI_VAR_REF(msg).msg.msg.connect.holdoff = holdoff; + err = tcpip_api_call(pppapi_do_ppp_connect, &PPPAPI_VAR_REF(msg).call); + PPPAPI_VAR_FREE(msg); + return err; +} + + +#if PPP_SERVER +/** + * Call ppp_listen() inside the tcpip_thread context. + */ +static err_t +pppapi_do_ppp_listen(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + return ppp_listen(msg->msg.ppp); +} + +/** + * Call ppp_listen() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_listen(ppp_pcb *pcb) +{ + err_t err; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = pcb; + err = tcpip_api_call(pppapi_do_ppp_listen, &PPPAPI_VAR_REF(msg).call); + PPPAPI_VAR_FREE(msg); + return err; +} +#endif /* PPP_SERVER */ + + +/** + * Call ppp_close() inside the tcpip_thread context. + */ +static err_t +pppapi_do_ppp_close(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + return ppp_close(msg->msg.ppp, msg->msg.msg.close.nocarrier); +} + +/** + * Call ppp_close() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_close(ppp_pcb *pcb, u8_t nocarrier) +{ + err_t err; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = pcb; + PPPAPI_VAR_REF(msg).msg.msg.close.nocarrier = nocarrier; + err = tcpip_api_call(pppapi_do_ppp_close, &PPPAPI_VAR_REF(msg).call); + PPPAPI_VAR_FREE(msg); + return err; +} + + +/** + * Call ppp_free() inside the tcpip_thread context. + */ +static err_t +pppapi_do_ppp_free(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + return ppp_free(msg->msg.ppp); +} + +/** + * Call ppp_free() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_free(ppp_pcb *pcb) +{ + err_t err; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = pcb; + err = tcpip_api_call(pppapi_do_ppp_free, &PPPAPI_VAR_REF(msg).call); + PPPAPI_VAR_FREE(msg); + return err; +} + + +/** + * Call ppp_ioctl() inside the tcpip_thread context. + */ +static err_t +pppapi_do_ppp_ioctl(struct tcpip_api_call_data *m) +{ + /* cast through void* to silence alignment warnings. + * We know it works because the structs have been instantiated as struct pppapi_msg */ + struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m; + + return ppp_ioctl(msg->msg.ppp, msg->msg.msg.ioctl.cmd, msg->msg.msg.ioctl.arg); +} + +/** + * Call ppp_ioctl() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +err_t +pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg) +{ + err_t err; + PPPAPI_VAR_DECLARE(msg); + PPPAPI_VAR_ALLOC(msg); + + PPPAPI_VAR_REF(msg).msg.ppp = pcb; + PPPAPI_VAR_REF(msg).msg.msg.ioctl.cmd = cmd; + PPPAPI_VAR_REF(msg).msg.msg.ioctl.arg = arg; + err = tcpip_api_call(pppapi_do_ppp_ioctl, &PPPAPI_VAR_REF(msg).call); + PPPAPI_VAR_FREE(msg); + return err; +} + +#endif /* LWIP_PPP_API */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/pppcrypt.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppcrypt.c new file mode 100644 index 0000000..82d78c1 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppcrypt.c @@ -0,0 +1,66 @@ +/* + * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1 + * + * Extracted from chap_ms.c by James Carlson. + * + * Copyright (c) 1995 Eric Rosenquist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not necessary */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/pppcrypt.h" + + +static u_char pppcrypt_get_7bits(u_char *input, int startBit) { + unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +/* IN 56 bit DES key missing parity bits + * OUT 64 bit DES key with parity bits added + */ +void pppcrypt_56_to_64_bit_key(u_char *key, u_char * des_key) { + des_key[0] = pppcrypt_get_7bits(key, 0); + des_key[1] = pppcrypt_get_7bits(key, 7); + des_key[2] = pppcrypt_get_7bits(key, 14); + des_key[3] = pppcrypt_get_7bits(key, 21); + des_key[4] = pppcrypt_get_7bits(key, 28); + des_key[5] = pppcrypt_get_7bits(key, 35); + des_key[6] = pppcrypt_get_7bits(key, 42); + des_key[7] = pppcrypt_get_7bits(key, 49); +} + +#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/pppoe.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppoe.c new file mode 100644 index 0000000..eabfa4d --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppoe.c @@ -0,0 +1,1191 @@ +/***************************************************************************** +* pppoe.c - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#endif /* UNUSED */ + +#include "lwip/timeouts.h" +#include "lwip/memp.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include "netif/ethernet.h" +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/lcp.h" +#include "netif/ppp/ipcp.h" +#include "netif/ppp/pppoe.h" + +/* Memory pool */ +LWIP_MEMPOOL_DECLARE(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF") + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (u8_t)((VAL) / 256); \ + *(PTR)++ = (u8_t)((VAL) % 256) + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +#error "PPPOE_SERVER is not yet supported under lwIP!" +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +#define PPPOE_ERRORSTRING_LEN 64 + + +/* callbacks called from PPP core */ +static err_t pppoe_write(ppp_pcb *ppp, void *ctx, struct pbuf *p); +static err_t pppoe_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol); +static void pppoe_connect(ppp_pcb *ppp, void *ctx); +static void pppoe_disconnect(ppp_pcb *ppp, void *ctx); +static err_t pppoe_destroy(ppp_pcb *ppp, void *ctx); + +/* management routines */ +static void pppoe_abort_connect(struct pppoe_softc *); +#if 0 /* UNUSED */ +static void pppoe_clear_softc(struct pppoe_softc *, const char *); +#endif /* UNUSED */ + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static err_t pppoe_send_padi(struct pppoe_softc *); +static err_t pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static err_t pppoe_send_pado(struct pppoe_softc *); +static err_t pppoe_send_pads(struct pppoe_softc *); +#endif +static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *); + +/* internal helper functions */ +static err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); +static struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif); +static struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif); + +/** linked list of created pppoe interfaces */ +static struct pppoe_softc *pppoe_softc_list; + +/* Callbacks structure for PPP core */ +static const struct link_callbacks pppoe_callbacks = { + pppoe_connect, +#if PPP_SERVER + NULL, +#endif /* PPP_SERVER */ + pppoe_disconnect, + pppoe_destroy, + pppoe_write, + pppoe_netif_output, + NULL, + NULL +}; + +/* + * Create a new PPP Over Ethernet (PPPoE) connection. + * + * Return 0 on success, an error code on failure. + */ +ppp_pcb *pppoe_create(struct netif *pppif, + struct netif *ethif, + const char *service_name, const char *concentrator_name, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) +{ + ppp_pcb *ppp; + struct pppoe_softc *sc; + LWIP_UNUSED_ARG(service_name); + LWIP_UNUSED_ARG(concentrator_name); + + sc = (struct pppoe_softc *)LWIP_MEMPOOL_ALLOC(PPPOE_IF); + if (sc == NULL) { + return NULL; + } + + ppp = ppp_new(pppif, &pppoe_callbacks, sc, link_status_cb, ctx_cb); + if (ppp == NULL) { + LWIP_MEMPOOL_FREE(PPPOE_IF, sc); + return NULL; + } + + memset(sc, 0, sizeof(struct pppoe_softc)); + sc->pcb = ppp; + sc->sc_ethif = ethif; + /* put the new interface at the head of the list */ + sc->next = pppoe_softc_list; + pppoe_softc_list = sc; + return ppp; +} + +/* Called by PPP core */ +static err_t pppoe_write(ppp_pcb *ppp, void *ctx, struct pbuf *p) { + struct pppoe_softc *sc = (struct pppoe_softc *)ctx; + struct pbuf *ph; /* Ethernet + PPPoE header */ + err_t ret; +#if MIB2_STATS + u16_t tot_len; +#else /* MIB2_STATS */ + LWIP_UNUSED_ARG(ppp); +#endif /* MIB2_STATS */ + + /* skip address & flags */ + pbuf_header(p, -(s16_t)2); + + ph = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM); + if(!ph) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + pbuf_free(p); + return ERR_MEM; + } + + pbuf_header(ph, -(s16_t)PPPOE_HEADERLEN); /* hide PPPoE header */ + pbuf_cat(ph, p); +#if MIB2_STATS + tot_len = ph->tot_len; +#endif /* MIB2_STATS */ + + ret = pppoe_xmit(sc, ph); + if (ret != ERR_OK) { + LINK_STATS_INC(link.err); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + return ret; + } + + MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, (u16_t)tot_len); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} + +/* Called by PPP core */ +static err_t pppoe_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol) { + struct pppoe_softc *sc = (struct pppoe_softc *)ctx; + struct pbuf *pb; + u8_t *pl; + err_t err; +#if MIB2_STATS + u16_t tot_len; +#else /* MIB2_STATS */ + LWIP_UNUSED_ARG(ppp); +#endif /* MIB2_STATS */ + + /* @todo: try to use pbuf_header() here! */ + pb = pbuf_alloc(PBUF_LINK, PPPOE_HEADERLEN + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + return ERR_MEM; + } + + pbuf_header(pb, -(s16_t)PPPOE_HEADERLEN); + + pl = (u8_t*)pb->payload; + PUTSHORT(protocol, pl); + + pbuf_chain(pb, p); +#if MIB2_STATS + tot_len = pb->tot_len; +#endif /* MIB2_STATS */ + + if( (err = pppoe_xmit(sc, pb)) != ERR_OK) { + LINK_STATS_INC(link.err); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + return err; + } + + MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, tot_len); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} + +static err_t +pppoe_destroy(ppp_pcb *ppp, void *ctx) +{ + struct pppoe_softc *sc = (struct pppoe_softc *)ctx; + struct pppoe_softc **copp, *freep; + LWIP_UNUSED_ARG(ppp); + + sys_untimeout(pppoe_timeout, sc); + + /* remove interface from list */ + for (copp = &pppoe_softc_list; (freep = *copp); copp = &freep->next) { + if (freep == sc) { + *copp = freep->next; + break; + } + } + +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name) { + mem_free(sc->sc_concentrator_name); + } + if (sc->sc_service_name) { + mem_free(sc->sc_service_name); + } +#endif /* PPPOE_TODO */ + LWIP_MEMPOOL_FREE(PPPOE_IF, sc); + + return ERR_OK; +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif) { + struct pppoe_softc *sc; + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session + && sc->sc_ethif == rcvif) { + return sc; + } + } + return NULL; +} + +/* Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. */ +static struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) { + struct pppoe_softc *sc, *t; + + if (len != sizeof sc) { + return NULL; + } + MEMCPY(&t, token, len); + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc == t) { + break; + } + } + + if (sc == NULL) { + PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n")); + return NULL; + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state)); + return NULL; + } + if (sc->sc_ethif != rcvif) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": wrong interface, not accepting host unique\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + return NULL; + } + return sc; +} + +/* analyze and handle a single received packet while not in session state */ +void +pppoe_disc_input(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; +#if PPP_DEBUG + const char *err_msg = NULL; +#endif /* PPP_DEBUG */ + u8_t *ac_cookie; + u16_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off, err; + struct eth_hdr *ethhdr; + + /* don't do anything if there is not a single PPPoE instance */ + if (pppoe_softc_list == NULL) { + pbuf_free(pb); + return; + } + + pb = ppp_singlebuf(pb); + + if (pb->len < sizeof(*ethhdr)) { + goto done; + } + ethhdr = (struct eth_hdr *)pb->payload; + off = sizeof(*ethhdr); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + session = 0; + if (pb->len - off < (u16_t)PPPOE_HEADERLEN) { + PPPDEBUG(LOG_DEBUG, ("pppoe: packet too short: %d\n", pb->len)); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + PPPDEBUG(LOG_DEBUG, ("pppoe: unknown version/type packet: 0x%x\n", ph->vertype)); + goto done; + } + session = lwip_ntohs(ph->session); + plen = lwip_ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + PPPDEBUG(LOG_DEBUG, ("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + pb->len - off, plen)); + goto done; + } + if(pb->tot_len == pb->len) { + pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */ + } + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(pt) <= pb->len) { + MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt)); + tag = lwip_ntohs(pt.tag); + len = lwip_ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + PPPDEBUG(LOG_DEBUG, ("pppoe: tag 0x%x len 0x%x is too long\n", tag, len)); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) { + break; + } +#ifdef PPPOE_SERVER + hunique = (u8_t*)pb->payload + off + sizeof(pt); + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif); + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + if (len > PPPOE_MAX_AC_COOKIE_LEN) { + PPPDEBUG(LOG_DEBUG, ("pppoe: AC cookie is too long: len = %d, max = %d\n", len, PPPOE_MAX_AC_COOKIE_LEN)); + goto done; + } + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; +#if PPP_DEBUG + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + break; +#endif /* PPP_DEBUG */ + default: + break; + } +#if PPP_DEBUG + if (err_msg != NULL) { + char error_tmp[PPPOE_ERRORSTRING_LEN]; + u16_t error_len = LWIP_MIN(len, sizeof(error_tmp)-1); + strncpy(error_tmp, (char*)pb->payload + off + sizeof(pt), error_len); + error_tmp[error_len] = '\0'; + if (sc) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": %s: %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err_msg, error_tmp)); + } else { + PPPDEBUG(LOG_DEBUG, ("pppoe: %s: %s\n", err_msg, error_tmp)); + } + } +#endif /* PPP_DEBUG */ + off += sizeof(pt) + len; + } + +breakbreak:; + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * got service name, concentrator name, and/or host unique. + * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) { + goto done; + } + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + continue; + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + continue; + } + if (sc->sc_state == PPPOE_STATE_INITIAL) { + break; + } + } + if (sc == NULL) { + /* PPPDEBUG(LOG_DEBUG, ("pppoe: free passive interface is not found\n")); */ + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + break; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: +#ifdef PPPOE_SERVER + /* + * get sc from ac_cookie if IFF_PASSIVE + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but not includes ac_cookie\n")); + goto done; + } + sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but could not find request for it\n")); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + ppp_start(sc->pcb); /* notify upper layers */ + break; +#else + /* ignore, we are no access concentrator */ + goto done; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (pppoe_softc_list != NULL) { + PPPDEBUG(LOG_DEBUG, ("pppoe: received PADO but could not find request for it\n")); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + goto done; + } + if (ac_cookie) { + sc->sc_ac_cookie_len = ac_cookie_len; + MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr)); + sys_untimeout(pppoe_timeout, sc); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_CODE_PADS: + if (sc == NULL) { + goto done; + } + sc->sc_session = session; + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session)); + sc->sc_state = PPPOE_STATE_SESSION; + ppp_start(sc->pcb); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + /* Don't disconnect here, we let the LCP Echo/Reply find the fact + * that PPP session is down. Asking the PPP stack to end the session + * require strict checking about the PPP phase to prevent endless + * disconnection loops. + */ +#if 0 /* UNUSED */ + if (sc == NULL) { /* PADT frames are rarely sent with a hunique tag, this is actually almost always true */ + goto done; + } + pppoe_clear_softc(sc, "received PADT"); +#endif /* UNUSED */ + break; + default: + if(sc) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + (u16_t)ph->code, session)); + } else { + PPPDEBUG(LOG_DEBUG, ("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session)); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_data_input(struct netif *netif, struct pbuf *pb) +{ + u16_t session, plen; + struct pppoe_softc *sc; + struct pppoehdr *ph; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u8_t shost[ETHER_ADDR_LEN]; +#endif + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost)); +#endif + if (pbuf_header(pb, -(s16_t)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: could not get PPPoE header\n")); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + PPPDEBUG(LOG_DEBUG, ("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype)); + goto drop; + } + if (ph->code != 0) { + goto drop; + } + + session = lwip_ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + PPPDEBUG(LOG_DEBUG, ("pppoe: input for unknown session 0x%x, sending PADT\n", session)); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = lwip_ntohs(ph->plen); + + if (pbuf_header(pb, -(s16_t)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + pb->len, plen)); + + if (pb->tot_len < plen) { + goto drop; + } + + /* Dispatch the packet thereby consuming it. */ + ppp_input(sc->pcb, pb); + return; + +drop: + pbuf_free(pb); +} + +static err_t +pppoe_output(struct pppoe_softc *sc, struct pbuf *pb) +{ + struct eth_hdr *ethhdr; + u16_t etype; + err_t res; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, (s16_t)(sizeof(struct eth_hdr))) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_output: could not allocate room for Ethernet header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = lwip_htons(etype); + MEMCPY(ðhdr->dest.addr, &sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + MEMCPY(ðhdr->src.addr, &sc->sc_ethif->hwaddr, sizeof(ethhdr->src.addr)); + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype, + sc->sc_state, sc->sc_session, + sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5], + pb->tot_len)); + + res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb); + + pbuf_free(pb); + + return res; +} + +static err_t +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + int len; +#ifdef PPPOE_TODO + int l1 = 0, l2 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + l1 = (int)strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = (int)strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } +#endif /* PPPOE_TODO */ + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + MEMCPY(p, sc->sc_concentrator_name, l2); + p += l2; + } +#endif /* PPPOE_TODO */ + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + /* send pkt */ + return pppoe_output(sc, pb); +} + +static void +pppoe_timeout(void *arg) +{ + u32_t retry_wait; + int err; + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + if (sc->sc_padi_retried < 0xff) { + sc->sc_padi_retried++; + } + if (!sc->pcb->settings.persist && sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { +#if 0 + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else +#endif + { + pppoe_abort_connect(sc); + return; + } + } + /* initialize for quick retry mode */ + retry_wait = LWIP_MIN(PPPOE_DISC_TIMEOUT * sc->sc_padi_retried, PPPOE_SLOW_RETRY); + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(retry_wait, pppoe_timeout, sc); + break; + + case PPPOE_STATE_PADR_SENT: + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +static void +pppoe_connect(ppp_pcb *ppp, void *ctx) +{ + err_t err; + struct pppoe_softc *sc = (struct pppoe_softc *)ctx; + lcp_options *lcp_wo; + lcp_options *lcp_ao; +#if PPP_IPV4_SUPPORT && VJ_SUPPORT + ipcp_options *ipcp_wo; + ipcp_options *ipcp_ao; +#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */ + + sc->sc_session = 0; + sc->sc_ac_cookie_len = 0; + sc->sc_padi_retried = 0; + sc->sc_padr_retried = 0; + /* changed to real address later */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + return 0; + } +#endif + + lcp_wo = &ppp->lcp_wantoptions; + lcp_wo->mru = sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */ + lcp_wo->neg_asyncmap = 0; + lcp_wo->neg_pcompression = 0; + lcp_wo->neg_accompression = 0; + lcp_wo->passive = 0; + lcp_wo->silent = 0; + + lcp_ao = &ppp->lcp_allowoptions; + lcp_ao->mru = sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */ + lcp_ao->neg_asyncmap = 0; + lcp_ao->neg_pcompression = 0; + lcp_ao->neg_accompression = 0; + +#if PPP_IPV4_SUPPORT && VJ_SUPPORT + ipcp_wo = &ppp->ipcp_wantoptions; + ipcp_wo->neg_vj = 0; + ipcp_wo->old_vj = 0; + + ipcp_ao = &ppp->ipcp_allowoptions; + ipcp_ao->neg_vj = 0; + ipcp_ao->old_vj = 0; +#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */ + + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); +} + +/* disconnect */ +static void +pppoe_disconnect(ppp_pcb *ppp, void *ctx) +{ + struct pppoe_softc *sc = (struct pppoe_softc *)ctx; + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + if (sc->sc_state == PPPOE_STATE_SESSION) { + pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + } + + /* stop any timer, disconnect can be called while initiating is in progress */ + sys_untimeout(pppoe_timeout, sc); + sc->sc_state = PPPOE_STATE_INITIAL; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway */ + } + sc->sc_hunique_len = 0; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway */ +#endif + ppp_link_end(ppp); /* notify upper layers */ + return; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + sc->sc_state = PPPOE_STATE_INITIAL; + ppp_link_failed(sc->pcb); /* notify upper layers */ +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; +#ifdef PPPOE_TODO + size_t l1 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } +#endif /* PPPOE_TODO */ + if (sc->sc_ac_cookie_len > 0) { + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + } + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload; + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + return pppoe_output(sc, pb); +} + +/* send a PADT packet */ +static err_t +pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest) +{ + struct pbuf *pb; + struct eth_hdr *ethhdr; + err_t res; + u8_t *p; + + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + pbuf_header(pb, (s16_t)sizeof(struct eth_hdr)); + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC); + MEMCPY(ðhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + MEMCPY(ðhdr->src.addr, &outgoing_if->hwaddr, sizeof(ethhdr->src.addr)); + + p = (u8_t*)(ethhdr + 1); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + res = outgoing_if->linkoutput(outgoing_if, pb); + + pbuf_free(pb); + + return res; +} + +#ifdef PPPOE_SERVER +static err_t +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload; + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} + +static err_t +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload; + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} +#endif + +static err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + len = pb->tot_len; + + /* make room for PPPoE header - should not fail */ + if (pbuf_header(pb, (s16_t)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for PPPoE header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload; + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + return pppoe_output(sc, pb); +} + +#if 0 /*def PFIL_HOOKS*/ +static int +pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir) +{ + struct pppoe_softc *sc; + int s; + + if (mp != (struct pbuf **)PFIL_IFNET_DETACH) { + return 0; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif != ifp) { + continue; + } + if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { + sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": ethernet interface detached, going down\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + } + sc->sc_ethif = NULL; + pppoe_clear_softc(sc, "ethernet interface detached"); + } + + return 0; +} +#endif + +#if 0 /* UNUSED */ +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + LWIP_UNUSED_ARG(message); + + /* stop timer */ + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message)); + sc->sc_state = PPPOE_STATE_INITIAL; + ppp_link_end(sc->pcb); /* notify upper layers - /!\ dangerous /!\ - see pppoe_disc_input() */ +} +#endif /* UNUSED */ +#endif /* PPP_SUPPORT && PPPOE_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/pppol2tp.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppol2tp.c new file mode 100644 index 0000000..d44471e --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppol2tp.c @@ -0,0 +1,1131 @@ +/** + * @file + * Network Point to Point Protocol over Layer 2 Tunneling Protocol program file. + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +/* + * L2TP Support status: + * + * Supported: + * - L2TPv2 (PPP over L2TP, a.k.a. UDP tunnels) + * - LAC + * + * Not supported: + * - LNS (require PPP server support) + * - L2TPv3 ethernet pseudowires + * - L2TPv3 VLAN pseudowire + * - L2TPv3 PPP pseudowires + * - L2TPv3 IP encapsulation + * - L2TPv3 IP pseudowire + * - L2TP tunnel switching - http://tools.ietf.org/html/draft-ietf-l2tpext-tunnel-switching-08 + * - Multiple tunnels per UDP socket, as well as multiple sessions per tunnel + * - Hidden AVPs + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPPOL2TP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/err.h" +#include "lwip/memp.h" +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "lwip/snmp.h" + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/lcp.h" +#include "netif/ppp/ipcp.h" +#include "netif/ppp/pppol2tp.h" +#include "netif/ppp/pppcrypt.h" +#include "netif/ppp/magic.h" + +/* Memory pool */ +LWIP_MEMPOOL_DECLARE(PPPOL2TP_PCB, MEMP_NUM_PPPOL2TP_INTERFACES, sizeof(pppol2tp_pcb), "PPPOL2TP_PCB") + +/* callbacks called from PPP core */ +static err_t pppol2tp_write(ppp_pcb *ppp, void *ctx, struct pbuf *p); +static err_t pppol2tp_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol); +static err_t pppol2tp_destroy(ppp_pcb *ppp, void *ctx); /* Destroy a L2TP control block */ +static void pppol2tp_connect(ppp_pcb *ppp, void *ctx); /* Be a LAC, connect to a LNS. */ +static void pppol2tp_disconnect(ppp_pcb *ppp, void *ctx); /* Disconnect */ + + /* Prototypes for procedures local to this file. */ +static void pppol2tp_input(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port); +static void pppol2tp_dispatch_control_packet(pppol2tp_pcb *l2tp, u16_t port, struct pbuf *p, u16_t ns, u16_t nr); +static void pppol2tp_timeout(void *arg); +static void pppol2tp_abort_connect(pppol2tp_pcb *l2tp); +static err_t pppol2tp_send_sccrq(pppol2tp_pcb *l2tp); +static err_t pppol2tp_send_scccn(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_send_icrq(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_send_iccn(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_send_zlb(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_send_stopccn(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_xmit(pppol2tp_pcb *l2tp, struct pbuf *pb); +static err_t pppol2tp_udp_send(pppol2tp_pcb *l2tp, struct pbuf *pb); + +/* Callbacks structure for PPP core */ +static const struct link_callbacks pppol2tp_callbacks = { + pppol2tp_connect, +#if PPP_SERVER + NULL, +#endif /* PPP_SERVER */ + pppol2tp_disconnect, + pppol2tp_destroy, + pppol2tp_write, + pppol2tp_netif_output, + NULL, + NULL +}; + + +/* Create a new L2TP session. */ +ppp_pcb *pppol2tp_create(struct netif *pppif, + struct netif *netif, const ip_addr_t *ipaddr, u16_t port, + const u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) { + ppp_pcb *ppp; + pppol2tp_pcb *l2tp; + struct udp_pcb *udp; +#if !PPPOL2TP_AUTH_SUPPORT + LWIP_UNUSED_ARG(secret); + LWIP_UNUSED_ARG(secret_len); +#endif /* !PPPOL2TP_AUTH_SUPPORT */ + + if (ipaddr == NULL) { + goto ipaddr_check_failed; + } + + l2tp = (pppol2tp_pcb *)LWIP_MEMPOOL_ALLOC(PPPOL2TP_PCB); + if (l2tp == NULL) { + goto memp_malloc_l2tp_failed; + } + + udp = udp_new_ip_type(IP_GET_TYPE(ipaddr)); + if (udp == NULL) { + goto udp_new_failed; + } + udp_recv(udp, pppol2tp_input, l2tp); + + ppp = ppp_new(pppif, &pppol2tp_callbacks, l2tp, link_status_cb, ctx_cb); + if (ppp == NULL) { + goto ppp_new_failed; + } + + memset(l2tp, 0, sizeof(pppol2tp_pcb)); + l2tp->phase = PPPOL2TP_STATE_INITIAL; + l2tp->ppp = ppp; + l2tp->udp = udp; + l2tp->netif = netif; + ip_addr_copy(l2tp->remote_ip, *ipaddr); + l2tp->remote_port = port; +#if PPPOL2TP_AUTH_SUPPORT + l2tp->secret = secret; + l2tp->secret_len = secret_len; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + return ppp; + +ppp_new_failed: + udp_remove(udp); +udp_new_failed: + LWIP_MEMPOOL_FREE(PPPOL2TP_PCB, l2tp); +memp_malloc_l2tp_failed: +ipaddr_check_failed: + return NULL; +} + +/* Called by PPP core */ +static err_t pppol2tp_write(ppp_pcb *ppp, void *ctx, struct pbuf *p) { + pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx; + struct pbuf *ph; /* UDP + L2TP header */ + err_t ret; +#if MIB2_STATS + u16_t tot_len; +#else /* MIB2_STATS */ + LWIP_UNUSED_ARG(ppp); +#endif /* MIB2_STATS */ + + ph = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(PPPOL2TP_OUTPUT_DATA_HEADER_LEN), PBUF_RAM); + if(!ph) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + pbuf_free(p); + return ERR_MEM; + } + + pbuf_header(ph, -(s16_t)PPPOL2TP_OUTPUT_DATA_HEADER_LEN); /* hide L2TP header */ + pbuf_cat(ph, p); +#if MIB2_STATS + tot_len = ph->tot_len; +#endif /* MIB2_STATS */ + + ret = pppol2tp_xmit(l2tp, ph); + if (ret != ERR_OK) { + LINK_STATS_INC(link.err); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + return ret; + } + + MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, (u16_t)tot_len); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} + +/* Called by PPP core */ +static err_t pppol2tp_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol) { + pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx; + struct pbuf *pb; + u8_t *pl; + err_t err; +#if MIB2_STATS + u16_t tot_len; +#else /* MIB2_STATS */ + LWIP_UNUSED_ARG(ppp); +#endif /* MIB2_STATS */ + + /* @todo: try to use pbuf_header() here! */ + pb = pbuf_alloc(PBUF_TRANSPORT, PPPOL2TP_OUTPUT_DATA_HEADER_LEN + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + return ERR_MEM; + } + + pbuf_header(pb, -(s16_t)PPPOL2TP_OUTPUT_DATA_HEADER_LEN); + + pl = (u8_t*)pb->payload; + PUTSHORT(protocol, pl); + + pbuf_chain(pb, p); +#if MIB2_STATS + tot_len = pb->tot_len; +#endif /* MIB2_STATS */ + + if( (err = pppol2tp_xmit(l2tp, pb)) != ERR_OK) { + LINK_STATS_INC(link.err); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + return err; + } + + MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, tot_len); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} + +/* Destroy a L2TP control block */ +static err_t pppol2tp_destroy(ppp_pcb *ppp, void *ctx) { + pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx; + LWIP_UNUSED_ARG(ppp); + + sys_untimeout(pppol2tp_timeout, l2tp); + udp_remove(l2tp->udp); + LWIP_MEMPOOL_FREE(PPPOL2TP_PCB, l2tp); + return ERR_OK; +} + +/* Be a LAC, connect to a LNS. */ +static void pppol2tp_connect(ppp_pcb *ppp, void *ctx) { + err_t err; + pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx; + lcp_options *lcp_wo; + lcp_options *lcp_ao; +#if PPP_IPV4_SUPPORT && VJ_SUPPORT + ipcp_options *ipcp_wo; + ipcp_options *ipcp_ao; +#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */ + + l2tp->tunnel_port = l2tp->remote_port; + l2tp->our_ns = 0; + l2tp->peer_nr = 0; + l2tp->peer_ns = 0; + l2tp->source_tunnel_id = 0; + l2tp->remote_tunnel_id = 0; + l2tp->source_session_id = 0; + l2tp->remote_session_id = 0; + /* l2tp->*_retried are cleared when used */ + + lcp_wo = &ppp->lcp_wantoptions; + lcp_wo->mru = PPPOL2TP_DEFMRU; + lcp_wo->neg_asyncmap = 0; + lcp_wo->neg_pcompression = 0; + lcp_wo->neg_accompression = 0; + lcp_wo->passive = 0; + lcp_wo->silent = 0; + + lcp_ao = &ppp->lcp_allowoptions; + lcp_ao->mru = PPPOL2TP_DEFMRU; + lcp_ao->neg_asyncmap = 0; + lcp_ao->neg_pcompression = 0; + lcp_ao->neg_accompression = 0; + +#if PPP_IPV4_SUPPORT && VJ_SUPPORT + ipcp_wo = &ppp->ipcp_wantoptions; + ipcp_wo->neg_vj = 0; + ipcp_wo->old_vj = 0; + + ipcp_ao = &ppp->ipcp_allowoptions; + ipcp_ao->neg_vj = 0; + ipcp_ao->old_vj = 0; +#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */ + + /* Listen to a random source port, we need to do that instead of using udp_connect() + * because the L2TP LNS might answer with its own random source port (!= 1701) + */ +#if LWIP_IPV6 + if (IP_IS_V6_VAL(l2tp->udp->local_ip)) { + udp_bind(l2tp->udp, IP6_ADDR_ANY, 0); + } else +#endif /* LWIP_IPV6 */ + udp_bind(l2tp->udp, IP_ADDR_ANY, 0); + +#if PPPOL2TP_AUTH_SUPPORT + /* Generate random vector */ + if (l2tp->secret != NULL) { + magic_random_bytes(l2tp->secret_rv, sizeof(l2tp->secret_rv)); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + do { + l2tp->remote_tunnel_id = magic(); + } while(l2tp->remote_tunnel_id == 0); + /* save state, in case we fail to send SCCRQ */ + l2tp->sccrq_retried = 0; + l2tp->phase = PPPOL2TP_STATE_SCCRQ_SENT; + if ((err = pppol2tp_send_sccrq(l2tp)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCRQ, error=%d\n", err)); + } + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); +} + +/* Disconnect */ +static void pppol2tp_disconnect(ppp_pcb *ppp, void *ctx) { + pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx; + + l2tp->our_ns++; + pppol2tp_send_stopccn(l2tp, l2tp->our_ns); + + /* stop any timer, disconnect can be called while initiating is in progress */ + sys_untimeout(pppol2tp_timeout, l2tp); + l2tp->phase = PPPOL2TP_STATE_INITIAL; + ppp_link_end(ppp); /* notify upper layers */ +} + +/* UDP Callback for incoming IPv4 L2TP frames */ +static void pppol2tp_input(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { + pppol2tp_pcb *l2tp = (pppol2tp_pcb*)arg; + u16_t hflags, hlen, len=0, tunnel_id=0, session_id=0, ns=0, nr=0, offset=0; + u8_t *inp; + LWIP_UNUSED_ARG(pcb); + + /* we can't unbound a UDP pcb, thus we can still receive UDP frames after the link is closed */ + if (l2tp->phase < PPPOL2TP_STATE_SCCRQ_SENT) { + goto free_and_return; + } + + if (!ip_addr_cmp(&l2tp->remote_ip, addr)) { + goto free_and_return; + } + + /* discard packet if port mismatch, but only if we received a SCCRP */ + if (l2tp->phase > PPPOL2TP_STATE_SCCRQ_SENT && l2tp->tunnel_port != port) { + goto free_and_return; + } + + /* printf("-----------\nL2TP INPUT, %d\n", p->len); */ + + /* L2TP header */ + if (p->len < sizeof(hflags) + sizeof(tunnel_id) + sizeof(session_id) ) { + goto packet_too_short; + } + + inp = (u8_t*)p->payload; + GETSHORT(hflags, inp); + + if (hflags & PPPOL2TP_HEADERFLAG_CONTROL) { + /* check mandatory flags for a control packet */ + if ( (hflags & PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY) != PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: mandatory header flags for control packet not set\n")); + goto free_and_return; + } + /* check forbidden flags for a control packet */ + if (hflags & PPPOL2TP_HEADERFLAG_CONTROL_FORBIDDEN) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: forbidden header flags for control packet found\n")); + goto free_and_return; + } + } else { + /* check mandatory flags for a data packet */ + if ( (hflags & PPPOL2TP_HEADERFLAG_DATA_MANDATORY) != PPPOL2TP_HEADERFLAG_DATA_MANDATORY) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: mandatory header flags for data packet not set\n")); + goto free_and_return; + } + } + + /* Expected header size */ + hlen = sizeof(hflags) + sizeof(tunnel_id) + sizeof(session_id); + if (hflags & PPPOL2TP_HEADERFLAG_LENGTH) { + hlen += sizeof(len); + } + if (hflags & PPPOL2TP_HEADERFLAG_SEQUENCE) { + hlen += sizeof(ns) + sizeof(nr); + } + if (hflags & PPPOL2TP_HEADERFLAG_OFFSET) { + hlen += sizeof(offset); + } + if (p->len < hlen) { + goto packet_too_short; + } + + if (hflags & PPPOL2TP_HEADERFLAG_LENGTH) { + GETSHORT(len, inp); + if (p->len < len || len < hlen) { + goto packet_too_short; + } + } + GETSHORT(tunnel_id, inp); + GETSHORT(session_id, inp); + if (hflags & PPPOL2TP_HEADERFLAG_SEQUENCE) { + GETSHORT(ns, inp); + GETSHORT(nr, inp); + } + if (hflags & PPPOL2TP_HEADERFLAG_OFFSET) { + GETSHORT(offset, inp) + if (offset > 4096) { /* don't be fooled with large offset which might overflow hlen */ + PPPDEBUG(LOG_DEBUG, ("pppol2tp: strange packet received, offset=%d\n", offset)); + goto free_and_return; + } + hlen += offset; + if (p->len < hlen) { + goto packet_too_short; + } + INCPTR(offset, inp); + } + + /* printf("HLEN = %d\n", hlen); */ + + /* skip L2TP header */ + if (pbuf_header(p, -(s16_t)hlen) != 0) { + goto free_and_return; + } + + /* printf("LEN=%d, TUNNEL_ID=%d, SESSION_ID=%d, NS=%d, NR=%d, OFFSET=%d\n", len, tunnel_id, session_id, ns, nr, offset); */ + PPPDEBUG(LOG_DEBUG, ("pppol2tp: input packet, len=%"U16_F", tunnel=%"U16_F", session=%"U16_F", ns=%"U16_F", nr=%"U16_F"\n", + len, tunnel_id, session_id, ns, nr)); + + /* Control packet */ + if (hflags & PPPOL2TP_HEADERFLAG_CONTROL) { + pppol2tp_dispatch_control_packet(l2tp, port, p, ns, nr); + goto free_and_return; + } + + /* Data packet */ + if(l2tp->phase != PPPOL2TP_STATE_DATA) { + goto free_and_return; + } + if(tunnel_id != l2tp->remote_tunnel_id) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: tunnel ID mismatch, assigned=%d, received=%d\n", l2tp->remote_tunnel_id, tunnel_id)); + goto free_and_return; + } + if(session_id != l2tp->remote_session_id) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: session ID mismatch, assigned=%d, received=%d\n", l2tp->remote_session_id, session_id)); + goto free_and_return; + } + /* + * skip address & flags if necessary + * + * RFC 2661 does not specify whether the PPP frame in the L2TP payload should + * have a HDLC header or not. We handle both cases for compatibility. + */ + if (p->len >= 2) { + GETSHORT(hflags, inp); + if (hflags == 0xff03) { + pbuf_header(p, -(s16_t)2); + } + } + /* Dispatch the packet thereby consuming it. */ + ppp_input(l2tp->ppp, p); + return; + +packet_too_short: + PPPDEBUG(LOG_DEBUG, ("pppol2tp: packet too short: %d\n", p->len)); +free_and_return: + pbuf_free(p); +} + +/* L2TP Control packet entry point */ +static void pppol2tp_dispatch_control_packet(pppol2tp_pcb *l2tp, u16_t port, struct pbuf *p, u16_t ns, u16_t nr) { + u8_t *inp; + u16_t avplen, avpflags, vendorid, attributetype, messagetype=0; + err_t err; +#if PPPOL2TP_AUTH_SUPPORT + lwip_md5_context md5_ctx; + u8_t md5_hash[16]; + u8_t challenge_id = 0; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + l2tp->peer_nr = nr; + l2tp->peer_ns = ns; + /* printf("L2TP CTRL INPUT, ns=%d, nr=%d, len=%d\n", ns, nr, p->len); */ + + /* Handle the special case of the ICCN acknowledge */ + if (l2tp->phase == PPPOL2TP_STATE_ICCN_SENT && l2tp->peer_nr > l2tp->our_ns) { + l2tp->phase = PPPOL2TP_STATE_DATA; + } + + /* ZLB packets */ + if (p->tot_len == 0) { + return; + } + + p = ppp_singlebuf(p); + inp = (u8_t*)p->payload; + /* Decode AVPs */ + while (p->len > 0) { + if (p->len < sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype) ) { + goto packet_too_short; + } + GETSHORT(avpflags, inp); + avplen = avpflags & PPPOL2TP_AVPHEADERFLAG_LENGTHMASK; + /* printf("AVPLEN = %d\n", avplen); */ + if (p->len < avplen || avplen < sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype)) { + goto packet_too_short; + } + GETSHORT(vendorid, inp); + GETSHORT(attributetype, inp); + avplen -= sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype); + + /* Message type must be the first AVP */ + if (messagetype == 0) { + if (attributetype != 0 || vendorid != 0 || avplen != sizeof(messagetype) ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: message type must be the first AVP\n")); + return; + } + GETSHORT(messagetype, inp); + /* printf("Message type = %d\n", messagetype); */ + switch(messagetype) { + /* Start Control Connection Reply */ + case PPPOL2TP_MESSAGETYPE_SCCRP: + /* Only accept SCCRP packet if we sent a SCCRQ */ + if (l2tp->phase != PPPOL2TP_STATE_SCCRQ_SENT) { + goto send_zlb; + } + break; + /* Incoming Call Reply */ + case PPPOL2TP_MESSAGETYPE_ICRP: + /* Only accept ICRP packet if we sent a IRCQ */ + if (l2tp->phase != PPPOL2TP_STATE_ICRQ_SENT) { + goto send_zlb; + } + break; + /* Stop Control Connection Notification */ + case PPPOL2TP_MESSAGETYPE_STOPCCN: + pppol2tp_send_zlb(l2tp, l2tp->our_ns); /* Ack the StopCCN before we switch to down state */ + if (l2tp->phase < PPPOL2TP_STATE_DATA) { + pppol2tp_abort_connect(l2tp); + } else if (l2tp->phase == PPPOL2TP_STATE_DATA) { + /* Don't disconnect here, we let the LCP Echo/Reply find the fact + * that PPP session is down. Asking the PPP stack to end the session + * require strict checking about the PPP phase to prevent endless + * disconnection loops. + */ + } + return; + default: + break; + } + goto nextavp; + } + + /* Skip proprietary L2TP extensions */ + if (vendorid != 0) { + goto skipavp; + } + + switch (messagetype) { + /* Start Control Connection Reply */ + case PPPOL2TP_MESSAGETYPE_SCCRP: + switch (attributetype) { + case PPPOL2TP_AVPTYPE_TUNNELID: + if (avplen != sizeof(l2tp->source_tunnel_id) ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Assign tunnel ID length check failed\n")); + return; + } + GETSHORT(l2tp->source_tunnel_id, inp); + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Assigned tunnel ID %"U16_F"\n", l2tp->source_tunnel_id)); + goto nextavp; +#if PPPOL2TP_AUTH_SUPPORT + case PPPOL2TP_AVPTYPE_CHALLENGE: + if (avplen == 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Challenge length check failed\n")); + return; + } + if (l2tp->secret == NULL) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Received challenge from peer and no secret key available\n")); + pppol2tp_abort_connect(l2tp); + return; + } + /* Generate hash of ID, secret, challenge */ + lwip_md5_init(&md5_ctx); + lwip_md5_starts(&md5_ctx); + challenge_id = PPPOL2TP_MESSAGETYPE_SCCCN; + lwip_md5_update(&md5_ctx, &challenge_id, 1); + lwip_md5_update(&md5_ctx, l2tp->secret, l2tp->secret_len); + lwip_md5_update(&md5_ctx, inp, avplen); + lwip_md5_finish(&md5_ctx, l2tp->challenge_hash); + lwip_md5_free(&md5_ctx); + l2tp->send_challenge = 1; + goto skipavp; + case PPPOL2TP_AVPTYPE_CHALLENGERESPONSE: + if (avplen != PPPOL2TP_AVPTYPE_CHALLENGERESPONSE_SIZE) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Challenge Response length check failed\n")); + return; + } + /* Generate hash of ID, secret, challenge */ + lwip_md5_init(&md5_ctx); + lwip_md5_starts(&md5_ctx); + challenge_id = PPPOL2TP_MESSAGETYPE_SCCRP; + lwip_md5_update(&md5_ctx, &challenge_id, 1); + lwip_md5_update(&md5_ctx, l2tp->secret, l2tp->secret_len); + lwip_md5_update(&md5_ctx, l2tp->secret_rv, sizeof(l2tp->secret_rv)); + lwip_md5_finish(&md5_ctx, md5_hash); + lwip_md5_free(&md5_ctx); + if ( memcmp(inp, md5_hash, sizeof(md5_hash)) ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Received challenge response from peer and secret key do not match\n")); + pppol2tp_abort_connect(l2tp); + return; + } + goto skipavp; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + default: + break; + } + break; + /* Incoming Call Reply */ + case PPPOL2TP_MESSAGETYPE_ICRP: + switch (attributetype) { + case PPPOL2TP_AVPTYPE_SESSIONID: + if (avplen != sizeof(l2tp->source_session_id) ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Assign session ID length check failed\n")); + return; + } + GETSHORT(l2tp->source_session_id, inp); + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Assigned session ID %"U16_F"\n", l2tp->source_session_id)); + goto nextavp; + default: + break; + } + break; + default: + break; + } + +skipavp: + INCPTR(avplen, inp); +nextavp: + /* printf("AVP Found, vendor=%d, attribute=%d, len=%d\n", vendorid, attributetype, avplen); */ + /* next AVP */ + if (pbuf_header(p, -(s16_t)(avplen + sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype)) ) != 0) { + return; + } + } + + switch(messagetype) { + /* Start Control Connection Reply */ + case PPPOL2TP_MESSAGETYPE_SCCRP: + do { + l2tp->remote_session_id = magic(); + } while(l2tp->remote_session_id == 0); + l2tp->tunnel_port = port; /* LNS server might have chosen its own local port */ + l2tp->icrq_retried = 0; + l2tp->phase = PPPOL2TP_STATE_ICRQ_SENT; + l2tp->our_ns++; + if ((err = pppol2tp_send_scccn(l2tp, l2tp->our_ns)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCCN, error=%d\n", err)); + } + l2tp->our_ns++; + if ((err = pppol2tp_send_icrq(l2tp, l2tp->our_ns)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICRQ, error=%d\n", err)); + } + sys_untimeout(pppol2tp_timeout, l2tp); + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + /* Incoming Call Reply */ + case PPPOL2TP_MESSAGETYPE_ICRP: + l2tp->iccn_retried = 0; + l2tp->phase = PPPOL2TP_STATE_ICCN_SENT; + l2tp->our_ns++; + ppp_start(l2tp->ppp); /* notify upper layers */ + if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err)); + } + sys_untimeout(pppol2tp_timeout, l2tp); + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + /* Unhandled packet, send ZLB ACK */ + default: + goto send_zlb; + } + return; + +send_zlb: + pppol2tp_send_zlb(l2tp, l2tp->our_ns); + return; +packet_too_short: + PPPDEBUG(LOG_DEBUG, ("pppol2tp: packet too short: %d\n", p->len)); +} + +/* L2TP Timeout handler */ +static void pppol2tp_timeout(void *arg) { + pppol2tp_pcb *l2tp = (pppol2tp_pcb*)arg; + err_t err; + u32_t retry_wait; + + PPPDEBUG(LOG_DEBUG, ("pppol2tp: timeout\n")); + + switch (l2tp->phase) { + case PPPOL2TP_STATE_SCCRQ_SENT: + /* backoff wait */ + if (l2tp->sccrq_retried < 0xff) { + l2tp->sccrq_retried++; + } + if (!l2tp->ppp->settings.persist && l2tp->sccrq_retried >= PPPOL2TP_MAXSCCRQ) { + pppol2tp_abort_connect(l2tp); + return; + } + retry_wait = LWIP_MIN(PPPOL2TP_CONTROL_TIMEOUT * l2tp->sccrq_retried, PPPOL2TP_SLOW_RETRY); + PPPDEBUG(LOG_DEBUG, ("pppol2tp: sccrq_retried=%d\n", l2tp->sccrq_retried)); + if ((err = pppol2tp_send_sccrq(l2tp)) != 0) { + l2tp->sccrq_retried--; + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCRQ, error=%d\n", err)); + } + sys_timeout(retry_wait, pppol2tp_timeout, l2tp); + break; + + case PPPOL2TP_STATE_ICRQ_SENT: + l2tp->icrq_retried++; + if (l2tp->icrq_retried >= PPPOL2TP_MAXICRQ) { + pppol2tp_abort_connect(l2tp); + return; + } + PPPDEBUG(LOG_DEBUG, ("pppol2tp: icrq_retried=%d\n", l2tp->icrq_retried)); + if (l2tp->peer_nr <= l2tp->our_ns -1) { /* the SCCCN was not acknowledged */ + if ((err = pppol2tp_send_scccn(l2tp, l2tp->our_ns -1)) != 0) { + l2tp->icrq_retried--; + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCCN, error=%d\n", err)); + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + } + } + if ((err = pppol2tp_send_icrq(l2tp, l2tp->our_ns)) != 0) { + l2tp->icrq_retried--; + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICRQ, error=%d\n", err)); + } + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + + case PPPOL2TP_STATE_ICCN_SENT: + l2tp->iccn_retried++; + if (l2tp->iccn_retried >= PPPOL2TP_MAXICCN) { + pppol2tp_abort_connect(l2tp); + return; + } + PPPDEBUG(LOG_DEBUG, ("pppol2tp: iccn_retried=%d\n", l2tp->iccn_retried)); + if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) { + l2tp->iccn_retried--; + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err)); + } + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + + default: + return; /* all done, work in peace */ + } +} + +/* Connection attempt aborted */ +static void pppol2tp_abort_connect(pppol2tp_pcb *l2tp) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: could not establish connection\n")); + l2tp->phase = PPPOL2TP_STATE_INITIAL; + ppp_link_failed(l2tp->ppp); /* notify upper layers */ +} + +/* Initiate a new tunnel */ +static err_t pppol2tp_send_sccrq(pppol2tp_pcb *l2tp) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12 +8 +8 +10 +10 +6+sizeof(PPPOL2TP_HOSTNAME)-1 +6+sizeof(PPPOL2TP_VENDORNAME)-1 +8 +8; +#if PPPOL2TP_AUTH_SUPPORT + if (l2tp->secret != NULL) { + len += 6 + sizeof(l2tp->secret_rv); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(0, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(0, p); /* NS Sequence number - to peer */ + PUTSHORT(0, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_SCCRQ, p); /* Attribute value: Message type: SCCRQ */ + + /* AVP - L2TP Version */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_VERSION, p); /* Attribute type: Version */ + PUTSHORT(PPPOL2TP_VERSION, p); /* Attribute value: L2TP Version */ + + /* AVP - Framing capabilities */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_FRAMINGCAPABILITIES, p); /* Attribute type: Framing capabilities */ + PUTLONG(PPPOL2TP_FRAMINGCAPABILITIES, p); /* Attribute value: Framing capabilities */ + + /* AVP - Bearer capabilities */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_BEARERCAPABILITIES, p); /* Attribute type: Bearer capabilities */ + PUTLONG(PPPOL2TP_BEARERCAPABILITIES, p); /* Attribute value: Bearer capabilities */ + + /* AVP - Host name */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6+sizeof(PPPOL2TP_HOSTNAME)-1, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_HOSTNAME, p); /* Attribute type: Hostname */ + MEMCPY(p, PPPOL2TP_HOSTNAME, sizeof(PPPOL2TP_HOSTNAME)-1); /* Attribute value: Hostname */ + INCPTR(sizeof(PPPOL2TP_HOSTNAME)-1, p); + + /* AVP - Vendor name */ + PUTSHORT(6+sizeof(PPPOL2TP_VENDORNAME)-1, p); /* len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_VENDORNAME, p); /* Attribute type: Vendor name */ + MEMCPY(p, PPPOL2TP_VENDORNAME, sizeof(PPPOL2TP_VENDORNAME)-1); /* Attribute value: Vendor name */ + INCPTR(sizeof(PPPOL2TP_VENDORNAME)-1, p); + + /* AVP - Assign tunnel ID */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_TUNNELID, p); /* Attribute type: Tunnel ID */ + PUTSHORT(l2tp->remote_tunnel_id, p); /* Attribute value: Tunnel ID */ + + /* AVP - Receive window size */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_RECEIVEWINDOWSIZE, p); /* Attribute type: Receive window size */ + PUTSHORT(PPPOL2TP_RECEIVEWINDOWSIZE, p); /* Attribute value: Receive window size */ + +#if PPPOL2TP_AUTH_SUPPORT + /* AVP - Challenge */ + if (l2tp->secret != NULL) { + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6 + sizeof(l2tp->secret_rv), p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_CHALLENGE, p); /* Attribute type: Challenge */ + MEMCPY(p, l2tp->secret_rv, sizeof(l2tp->secret_rv)); /* Attribute value: Random vector */ + INCPTR(sizeof(l2tp->secret_rv), p); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + return pppol2tp_udp_send(l2tp, pb); +} + +/* Complete tunnel establishment */ +static err_t pppol2tp_send_scccn(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12 +8; +#if PPPOL2TP_AUTH_SUPPORT + if (l2tp->send_challenge) { + len += 6 + sizeof(l2tp->challenge_hash); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_SCCCN, p); /* Attribute value: Message type: SCCCN */ + +#if PPPOL2TP_AUTH_SUPPORT + /* AVP - Challenge response */ + if (l2tp->send_challenge) { + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6 + sizeof(l2tp->challenge_hash), p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_CHALLENGERESPONSE, p); /* Attribute type: Challenge response */ + MEMCPY(p, l2tp->challenge_hash, sizeof(l2tp->challenge_hash)); /* Attribute value: Computed challenge */ + INCPTR(sizeof(l2tp->challenge_hash), p); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + return pppol2tp_udp_send(l2tp, pb); +} + +/* Initiate a new session */ +static err_t pppol2tp_send_icrq(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + u32_t serialnumber; + + /* calculate UDP packet length */ + len = 12 +8 +8 +10; + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_ICRQ, p); /* Attribute value: Message type: ICRQ */ + + /* AVP - Assign session ID */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_SESSIONID, p); /* Attribute type: Session ID */ + PUTSHORT(l2tp->remote_session_id, p); /* Attribute value: Session ID */ + + /* AVP - Call Serial Number */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_CALLSERIALNUMBER, p); /* Attribute type: Serial number */ + serialnumber = magic(); + PUTLONG(serialnumber, p); /* Attribute value: Serial number */ + + return pppol2tp_udp_send(l2tp, pb); +} + +/* Complete tunnel establishment */ +static err_t pppol2tp_send_iccn(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12 +8 +10 +10; + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(l2tp->source_session_id, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_ICCN, p); /* Attribute value: Message type: ICCN */ + + /* AVP - Framing type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_FRAMINGTYPE, p); /* Attribute type: Framing type */ + PUTLONG(PPPOL2TP_FRAMINGTYPE, p); /* Attribute value: Framing type */ + + /* AVP - TX Connect speed */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_TXCONNECTSPEED, p); /* Attribute type: TX Connect speed */ + PUTLONG(PPPOL2TP_TXCONNECTSPEED, p); /* Attribute value: TX Connect speed */ + + return pppol2tp_udp_send(l2tp, pb); +} + +/* Send a ZLB ACK packet */ +static err_t pppol2tp_send_zlb(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12; + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + return pppol2tp_udp_send(l2tp, pb); +} + +/* Send a StopCCN packet */ +static err_t pppol2tp_send_stopccn(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12 +8 +8 +8; + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_STOPCCN, p); /* Attribute value: Message type: StopCCN */ + + /* AVP - Assign tunnel ID */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_TUNNELID, p); /* Attribute type: Tunnel ID */ + PUTSHORT(l2tp->remote_tunnel_id, p); /* Attribute value: Tunnel ID */ + + /* AVP - Result code */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_RESULTCODE, p); /* Attribute type: Result code */ + PUTSHORT(PPPOL2TP_RESULTCODE, p); /* Attribute value: Result code */ + + return pppol2tp_udp_send(l2tp, pb); +} + +static err_t pppol2tp_xmit(pppol2tp_pcb *l2tp, struct pbuf *pb) { + u8_t *p; + + /* make room for L2TP header - should not fail */ + if (pbuf_header(pb, (s16_t)PPPOL2TP_OUTPUT_DATA_HEADER_LEN) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppol2tp: pppol2tp_pcb: could not allocate room for L2TP header\n")); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload; + PUTSHORT(PPPOL2TP_HEADERFLAG_DATA_MANDATORY, p); + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(l2tp->source_session_id, p); /* Session Id */ + + return pppol2tp_udp_send(l2tp, pb); +} + +static err_t pppol2tp_udp_send(pppol2tp_pcb *l2tp, struct pbuf *pb) { + err_t err; + if (l2tp->netif) { + err = udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->tunnel_port, l2tp->netif); + } else { + err = udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->tunnel_port); + } + pbuf_free(pb); + return err; +} + +#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/pppos.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppos.c new file mode 100644 index 0000000..fb48df4 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/pppos.c @@ -0,0 +1,875 @@ +/** + * @file + * Network Point to Point Protocol over Serial file. + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include + +#include "lwip/arch.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/priv/tcpip_priv.h" +#include "lwip/api.h" +#include "lwip/ip4.h" /* for ip4_input() */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/pppos.h" +#include "netif/ppp/vj.h" + +/* Memory pool */ +LWIP_MEMPOOL_DECLARE(PPPOS_PCB, MEMP_NUM_PPPOS_INTERFACES, sizeof(pppos_pcb), "PPPOS_PCB") + +/* callbacks called from PPP core */ +static err_t pppos_write(ppp_pcb *ppp, void *ctx, struct pbuf *p); +static err_t pppos_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *pb, u16_t protocol); +static void pppos_connect(ppp_pcb *ppp, void *ctx); +#if PPP_SERVER +static void pppos_listen(ppp_pcb *ppp, void *ctx); +#endif /* PPP_SERVER */ +static void pppos_disconnect(ppp_pcb *ppp, void *ctx); +static err_t pppos_destroy(ppp_pcb *ppp, void *ctx); +static void pppos_send_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp); +static void pppos_recv_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp); + +/* Prototypes for procedures local to this file. */ +#if PPP_INPROC_IRQ_SAFE +static void pppos_input_callback(void *arg); +#endif /* PPP_INPROC_IRQ_SAFE */ +static void pppos_input_free_current_packet(pppos_pcb *pppos); +static void pppos_input_drop(pppos_pcb *pppos); +static err_t pppos_output_append(pppos_pcb *pppos, err_t err, struct pbuf *nb, u8_t c, u8_t accm, u16_t *fcs); +static err_t pppos_output_last(pppos_pcb *pppos, err_t err, struct pbuf *nb, u16_t *fcs); + +/* Callbacks structure for PPP core */ +static const struct link_callbacks pppos_callbacks = { + pppos_connect, +#if PPP_SERVER + pppos_listen, +#endif /* PPP_SERVER */ + pppos_disconnect, + pppos_destroy, + pppos_write, + pppos_netif_output, + pppos_send_config, + pppos_recv_config +}; + +/* PPP's Asynchronous-Control-Character-Map. The mask array is used + * to select the specific bit for a character. */ +#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & 1 << (c & 0x07)) + +#if PPP_FCS_TABLE +/* + * FCS lookup table as calculated by genfcstab. + */ +static const u16_t fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) +#else /* PPP_FCS_TABLE */ +/* The HDLC polynomial: X**0 + X**5 + X**12 + X**16 (0x8408) */ +#define PPP_FCS_POLYNOMIAL 0x8408 +static u16_t +ppp_get_fcs(u8_t byte) +{ + unsigned int octet; + int bit; + octet = byte; + for (bit = 8; bit-- > 0; ) { + octet = (octet & 0x01) ? ((octet >> 1) ^ PPP_FCS_POLYNOMIAL) : (octet >> 1); + } + return octet & 0xffff; +} +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ ppp_get_fcs(((fcs) ^ (c)) & 0xff)) +#endif /* PPP_FCS_TABLE */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ + +#if PPP_INPROC_IRQ_SAFE +#define PPPOS_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev) +#define PPPOS_PROTECT(lev) SYS_ARCH_PROTECT(lev) +#define PPPOS_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev) +#else +#define PPPOS_DECL_PROTECT(lev) +#define PPPOS_PROTECT(lev) +#define PPPOS_UNPROTECT(lev) +#endif /* PPP_INPROC_IRQ_SAFE */ + + +/* + * Create a new PPP connection using the given serial I/O device. + * + * Return 0 on success, an error code on failure. + */ +ppp_pcb *pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) +{ + pppos_pcb *pppos; + ppp_pcb *ppp; + + pppos = (pppos_pcb *)LWIP_MEMPOOL_ALLOC(PPPOS_PCB); + if (pppos == NULL) { + return NULL; + } + + ppp = ppp_new(pppif, &pppos_callbacks, pppos, link_status_cb, ctx_cb); + if (ppp == NULL) { + LWIP_MEMPOOL_FREE(PPPOS_PCB, pppos); + return NULL; + } + + memset(pppos, 0, sizeof(pppos_pcb)); + pppos->ppp = ppp; + pppos->output_cb = output_cb; + return ppp; +} + +/* Called by PPP core */ +static err_t +pppos_write(ppp_pcb *ppp, void *ctx, struct pbuf *p) +{ + pppos_pcb *pppos = (pppos_pcb *)ctx; + u8_t *s; + struct pbuf *nb; + u16_t n; + u16_t fcs_out; + err_t err; + LWIP_UNUSED_ARG(ppp); + + /* Grab an output buffer. */ + nb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (nb == NULL) { + PPPDEBUG(LOG_WARNING, ("pppos_write[%d]: alloc fail\n", ppp->netif->num)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + pbuf_free(p); + return ERR_MEM; + } + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + err = ERR_OK; + if ((sys_now() - pppos->last_xmit) >= PPP_MAXIDLEFLAG) { + err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL); + } + + /* Load output buffer. */ + fcs_out = PPP_INITFCS; + s = (u8_t*)p->payload; + n = p->len; + while (n-- > 0) { + err = pppos_output_append(pppos, err, nb, *s++, 1, &fcs_out); + } + + err = pppos_output_last(pppos, err, nb, &fcs_out); + if (err == ERR_OK) { + PPPDEBUG(LOG_INFO, ("pppos_write[%d]: len=%d\n", ppp->netif->num, p->len)); + } else { + PPPDEBUG(LOG_WARNING, ("pppos_write[%d]: output failed len=%d\n", ppp->netif->num, p->len)); + } + pbuf_free(p); + return err; +} + +/* Called by PPP core */ +static err_t +pppos_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *pb, u16_t protocol) +{ + pppos_pcb *pppos = (pppos_pcb *)ctx; + struct pbuf *nb, *p; + u16_t fcs_out; + err_t err; + LWIP_UNUSED_ARG(ppp); + + /* Grab an output buffer. */ + nb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (nb == NULL) { + PPPDEBUG(LOG_WARNING, ("pppos_netif_output[%d]: alloc fail\n", ppp->netif->num)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + return ERR_MEM; + } + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + err = ERR_OK; + if ((sys_now() - pppos->last_xmit) >= PPP_MAXIDLEFLAG) { + err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL); + } + + fcs_out = PPP_INITFCS; + if (!pppos->accomp) { + err = pppos_output_append(pppos, err, nb, PPP_ALLSTATIONS, 1, &fcs_out); + err = pppos_output_append(pppos, err, nb, PPP_UI, 1, &fcs_out); + } + if (!pppos->pcomp || protocol > 0xFF) { + err = pppos_output_append(pppos, err, nb, (protocol >> 8) & 0xFF, 1, &fcs_out); + } + err = pppos_output_append(pppos, err, nb, protocol & 0xFF, 1, &fcs_out); + + /* Load packet. */ + for(p = pb; p; p = p->next) { + u16_t n = p->len; + u8_t *s = (u8_t*)p->payload; + + while (n-- > 0) { + err = pppos_output_append(pppos, err, nb, *s++, 1, &fcs_out); + } + } + + err = pppos_output_last(pppos, err, nb, &fcs_out); + if (err == ERR_OK) { + PPPDEBUG(LOG_INFO, ("pppos_netif_output[%d]: proto=0x%"X16_F", len = %d\n", ppp->netif->num, protocol, pb->tot_len)); + } else { + PPPDEBUG(LOG_WARNING, ("pppos_netif_output[%d]: output failed proto=0x%"X16_F", len = %d\n", ppp->netif->num, protocol, pb->tot_len)); + } + return err; +} + +static void +pppos_connect(ppp_pcb *ppp, void *ctx) +{ + pppos_pcb *pppos = (pppos_pcb *)ctx; + PPPOS_DECL_PROTECT(lev); + +#if PPP_INPROC_IRQ_SAFE + /* input pbuf left over from last session? */ + pppos_input_free_current_packet(pppos); +#endif /* PPP_INPROC_IRQ_SAFE */ + + /* reset PPPoS control block to its initial state */ + memset(&pppos->last_xmit, 0, sizeof(pppos_pcb) - offsetof(pppos_pcb, last_xmit)); + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + pppos->in_accm[15] = 0x60; /* no need to protect since RX is not running */ + pppos->out_accm[15] = 0x60; + PPPOS_PROTECT(lev); + pppos->open = 1; + PPPOS_UNPROTECT(lev); + + /* + * Start the connection and handle incoming events (packet or timeout). + */ + PPPDEBUG(LOG_INFO, ("pppos_connect: unit %d: connecting\n", ppp->netif->num)); + ppp_start(ppp); /* notify upper layers */ +} + +#if PPP_SERVER +static void +pppos_listen(ppp_pcb *ppp, void *ctx) +{ + pppos_pcb *pppos = (pppos_pcb *)ctx; + PPPOS_DECL_PROTECT(lev); + +#if PPP_INPROC_IRQ_SAFE + /* input pbuf left over from last session? */ + pppos_input_free_current_packet(pppos); +#endif /* PPP_INPROC_IRQ_SAFE */ + + /* reset PPPoS control block to its initial state */ + memset(&pppos->last_xmit, 0, sizeof(pppos_pcb) - offsetof(pppos_pcb, last_xmit)); + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + pppos->in_accm[15] = 0x60; /* no need to protect since RX is not running */ + pppos->out_accm[15] = 0x60; + PPPOS_PROTECT(lev); + pppos->open = 1; + PPPOS_UNPROTECT(lev); + + /* + * Wait for something to happen. + */ + PPPDEBUG(LOG_INFO, ("pppos_listen: unit %d: listening\n", ppp->netif->num)); + ppp_start(ppp); /* notify upper layers */ +} +#endif /* PPP_SERVER */ + +static void +pppos_disconnect(ppp_pcb *ppp, void *ctx) +{ + pppos_pcb *pppos = (pppos_pcb *)ctx; + PPPOS_DECL_PROTECT(lev); + + PPPOS_PROTECT(lev); + pppos->open = 0; + PPPOS_UNPROTECT(lev); + + /* If PPP_INPROC_IRQ_SAFE is used we cannot call + * pppos_input_free_current_packet() here because + * rx IRQ might still call pppos_input(). + */ +#if !PPP_INPROC_IRQ_SAFE + /* input pbuf left ? */ + pppos_input_free_current_packet(pppos); +#endif /* !PPP_INPROC_IRQ_SAFE */ + + ppp_link_end(ppp); /* notify upper layers */ +} + +static err_t +pppos_destroy(ppp_pcb *ppp, void *ctx) +{ + pppos_pcb *pppos = (pppos_pcb *)ctx; + LWIP_UNUSED_ARG(ppp); + +#if PPP_INPROC_IRQ_SAFE + /* input pbuf left ? */ + pppos_input_free_current_packet(pppos); +#endif /* PPP_INPROC_IRQ_SAFE */ + + LWIP_MEMPOOL_FREE(PPPOS_PCB, pppos); + return ERR_OK; +} + +#if !NO_SYS && !PPP_INPROC_IRQ_SAFE +/** Pass received raw characters to PPPoS to be decoded through lwIP TCPIP thread. + * + * @param ppp PPP descriptor index, returned by pppos_create() + * @param s received data + * @param l length of received data + */ +err_t +pppos_input_tcpip(ppp_pcb *ppp, u8_t *s, int l) +{ + struct pbuf *p; + err_t err; + + p = pbuf_alloc(PBUF_RAW, l, PBUF_POOL); + if (!p) { + return ERR_MEM; + } + pbuf_take(p, s, l); + + err = tcpip_inpkt(p, ppp_netif(ppp), pppos_input_sys); + if (err != ERR_OK) { + pbuf_free(p); + } + return err; +} + +/* called from TCPIP thread */ +err_t pppos_input_sys(struct pbuf *p, struct netif *inp) { + ppp_pcb *ppp = (ppp_pcb*)inp->state; + struct pbuf *n; + + for (n = p; n; n = n->next) { + pppos_input(ppp, (u8_t*)n->payload, n->len); + } + pbuf_free(p); + return ERR_OK; +} +#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */ + +/** PPPoS input helper struct, must be packed since it is stored + * to pbuf->payload, which might be unaligned. */ +#if PPP_INPROC_IRQ_SAFE +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppos_input_header { + PACK_STRUCT_FIELD(ppp_pcb *ppp); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#endif /* PPP_INPROC_IRQ_SAFE */ + +/** Pass received raw characters to PPPoS to be decoded. + * + * @param ppp PPP descriptor index, returned by pppos_create() + * @param s received data + * @param l length of received data + */ +void +pppos_input(ppp_pcb *ppp, u8_t *s, int l) +{ + pppos_pcb *pppos = (pppos_pcb *)ppp->link_ctx_cb; + struct pbuf *next_pbuf; + u8_t cur_char; + u8_t escaped; + PPPOS_DECL_PROTECT(lev); + + PPPDEBUG(LOG_DEBUG, ("pppos_input[%d]: got %d bytes\n", ppp->netif->num, l)); + while (l-- > 0) { + cur_char = *s++; + + PPPOS_PROTECT(lev); + /* ppp_input can disconnect the interface, we need to abort to prevent a memory + * leak if there are remaining bytes because pppos_connect and pppos_listen + * functions expect input buffer to be free. Furthermore there are no real + * reason to continue reading bytes if we are disconnected. + */ + if (!pppos->open) { + PPPOS_UNPROTECT(lev); + return; + } + escaped = ESCAPE_P(pppos->in_accm, cur_char); + PPPOS_UNPROTECT(lev); + /* Handle special characters. */ + if (escaped) { + /* Check for escape sequences. */ + /* XXX Note that this does not handle an escaped 0x5d character which + * would appear as an escape character. Since this is an ASCII ']' + * and there is no reason that I know of to escape it, I won't complicate + * the code to handle this case. GLL */ + if (cur_char == PPP_ESCAPE) { + pppos->in_escaped = 1; + /* Check for the flag character. */ + } else if (cur_char == PPP_FLAG) { + /* If this is just an extra flag character, ignore it. */ + if (pppos->in_state <= PDADDRESS) { + /* ignore it */; + /* If we haven't received the packet header, drop what has come in. */ + } else if (pppos->in_state < PDDATA) { + PPPDEBUG(LOG_WARNING, + ("pppos_input[%d]: Dropping incomplete packet %d\n", + ppp->netif->num, pppos->in_state)); + LINK_STATS_INC(link.lenerr); + pppos_input_drop(pppos); + /* If the fcs is invalid, drop the packet. */ + } else if (pppos->in_fcs != PPP_GOODFCS) { + PPPDEBUG(LOG_INFO, + ("pppos_input[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n", + ppp->netif->num, pppos->in_fcs, pppos->in_protocol)); + /* Note: If you get lots of these, check for UART frame errors or try different baud rate */ + LINK_STATS_INC(link.chkerr); + pppos_input_drop(pppos); + /* Otherwise it's a good packet so pass it on. */ + } else { + struct pbuf *inp; + /* Trim off the checksum. */ + if(pppos->in_tail->len > 2) { + pppos->in_tail->len -= 2; + + pppos->in_tail->tot_len = pppos->in_tail->len; + if (pppos->in_tail != pppos->in_head) { + pbuf_cat(pppos->in_head, pppos->in_tail); + } + } else { + pppos->in_tail->tot_len = pppos->in_tail->len; + if (pppos->in_tail != pppos->in_head) { + pbuf_cat(pppos->in_head, pppos->in_tail); + } + + pbuf_realloc(pppos->in_head, pppos->in_head->tot_len - 2); + } + + /* Dispatch the packet thereby consuming it. */ + inp = pppos->in_head; + /* Packet consumed, release our references. */ + pppos->in_head = NULL; + pppos->in_tail = NULL; +#if IP_FORWARD || LWIP_IPV6_FORWARD + /* hide the room for Ethernet forwarding header */ + pbuf_header(inp, -(s16_t)(PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN)); +#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */ +#if PPP_INPROC_IRQ_SAFE + if(tcpip_callback_with_block(pppos_input_callback, inp, 0) != ERR_OK) { + PPPDEBUG(LOG_ERR, ("pppos_input[%d]: tcpip_callback() failed, dropping packet\n", ppp->netif->num)); + pbuf_free(inp); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(ppp->netif, ifindiscards); + } +#else /* PPP_INPROC_IRQ_SAFE */ + ppp_input(ppp, inp); +#endif /* PPP_INPROC_IRQ_SAFE */ + } + + /* Prepare for a new packet. */ + pppos->in_fcs = PPP_INITFCS; + pppos->in_state = PDADDRESS; + pppos->in_escaped = 0; + /* Other characters are usually control characters that may have + * been inserted by the physical layer so here we just drop them. */ + } else { + PPPDEBUG(LOG_WARNING, + ("pppos_input[%d]: Dropping ACCM char <%d>\n", ppp->netif->num, cur_char)); + } + /* Process other characters. */ + } else { + /* Unencode escaped characters. */ + if (pppos->in_escaped) { + pppos->in_escaped = 0; + cur_char ^= PPP_TRANS; + } + + /* Process character relative to current state. */ + switch(pppos->in_state) { + case PDIDLE: /* Idle state - waiting. */ + /* Drop the character if it's not 0xff + * we would have processed a flag character above. */ + if (cur_char != PPP_ALLSTATIONS) { + break; + } + /* no break */ + /* Fall through */ + + case PDSTART: /* Process start flag. */ + /* Prepare for a new packet. */ + pppos->in_fcs = PPP_INITFCS; + /* no break */ + /* Fall through */ + + case PDADDRESS: /* Process address field. */ + if (cur_char == PPP_ALLSTATIONS) { + pppos->in_state = PDCONTROL; + break; + } + /* no break */ + + /* Else assume compressed address and control fields so + * fall through to get the protocol... */ + case PDCONTROL: /* Process control field. */ + /* If we don't get a valid control code, restart. */ + if (cur_char == PPP_UI) { + pppos->in_state = PDPROTOCOL1; + break; + } + /* no break */ + +#if 0 + else { + PPPDEBUG(LOG_WARNING, + ("pppos_input[%d]: Invalid control <%d>\n", ppp->netif->num, cur_char)); + pppos->in_state = PDSTART; + } +#endif + case PDPROTOCOL1: /* Process protocol field 1. */ + /* If the lower bit is set, this is the end of the protocol + * field. */ + if (cur_char & 1) { + pppos->in_protocol = cur_char; + pppos->in_state = PDDATA; + } else { + pppos->in_protocol = (u16_t)cur_char << 8; + pppos->in_state = PDPROTOCOL2; + } + break; + case PDPROTOCOL2: /* Process protocol field 2. */ + pppos->in_protocol |= cur_char; + pppos->in_state = PDDATA; + break; + case PDDATA: /* Process data byte. */ + /* Make space to receive processed data. */ + if (pppos->in_tail == NULL || pppos->in_tail->len == PBUF_POOL_BUFSIZE) { + u16_t pbuf_alloc_len; + if (pppos->in_tail != NULL) { + pppos->in_tail->tot_len = pppos->in_tail->len; + if (pppos->in_tail != pppos->in_head) { + pbuf_cat(pppos->in_head, pppos->in_tail); + /* give up the in_tail reference now */ + pppos->in_tail = NULL; + } + } + /* If we haven't started a packet, we need a packet header. */ + pbuf_alloc_len = 0; +#if IP_FORWARD || LWIP_IPV6_FORWARD + /* If IP forwarding is enabled we are reserving PBUF_LINK_ENCAPSULATION_HLEN + * + PBUF_LINK_HLEN bytes so the packet is being allocated with enough header + * space to be forwarded (to Ethernet for example). + */ + if (pppos->in_head == NULL) { + pbuf_alloc_len = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN; + } +#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */ + next_pbuf = pbuf_alloc(PBUF_RAW, pbuf_alloc_len, PBUF_POOL); + if (next_pbuf == NULL) { + /* No free buffers. Drop the input packet and let the + * higher layers deal with it. Continue processing + * the received pbuf chain in case a new packet starts. */ + PPPDEBUG(LOG_ERR, ("pppos_input[%d]: NO FREE PBUFS!\n", ppp->netif->num)); + LINK_STATS_INC(link.memerr); + pppos_input_drop(pppos); + pppos->in_state = PDSTART; /* Wait for flag sequence. */ + break; + } + if (pppos->in_head == NULL) { + u8_t *payload = ((u8_t*)next_pbuf->payload) + pbuf_alloc_len; +#if PPP_INPROC_IRQ_SAFE + ((struct pppos_input_header*)payload)->ppp = ppp; + payload += sizeof(struct pppos_input_header); + next_pbuf->len += sizeof(struct pppos_input_header); +#endif /* PPP_INPROC_IRQ_SAFE */ + next_pbuf->len += sizeof(pppos->in_protocol); + *(payload++) = pppos->in_protocol >> 8; + *(payload) = pppos->in_protocol & 0xFF; + pppos->in_head = next_pbuf; + } + pppos->in_tail = next_pbuf; + } + /* Load character into buffer. */ + ((u8_t*)pppos->in_tail->payload)[pppos->in_tail->len++] = cur_char; + break; + default: + break; + } + + /* update the frame check sequence number. */ + pppos->in_fcs = PPP_FCS(pppos->in_fcs, cur_char); + } + } /* while (l-- > 0), all bytes processed */ +} + +#if PPP_INPROC_IRQ_SAFE +/* PPPoS input callback using one input pointer + */ +static void pppos_input_callback(void *arg) { + struct pbuf *pb = (struct pbuf*)arg; + ppp_pcb *ppp; + + ppp = ((struct pppos_input_header*)pb->payload)->ppp; + if(pbuf_header(pb, -(s16_t)sizeof(struct pppos_input_header))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + + /* Dispatch the packet thereby consuming it. */ + ppp_input(ppp, pb); + return; + +drop: + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(ppp->netif, ifindiscards); + pbuf_free(pb); +} +#endif /* PPP_INPROC_IRQ_SAFE */ + +static void +pppos_send_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp) +{ + int i; + pppos_pcb *pppos = (pppos_pcb *)ctx; + LWIP_UNUSED_ARG(ppp); + + pppos->pcomp = pcomp; + pppos->accomp = accomp; + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32/8; i++) { + pppos->out_accm[i] = (u8_t)((accm >> (8 * i)) & 0xFF); + } + + PPPDEBUG(LOG_INFO, ("pppos_send_config[%d]: out_accm=%X %X %X %X\n", + pppos->ppp->netif->num, + pppos->out_accm[0], pppos->out_accm[1], pppos->out_accm[2], pppos->out_accm[3])); +} + +static void +pppos_recv_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp) +{ + int i; + pppos_pcb *pppos = (pppos_pcb *)ctx; + PPPOS_DECL_PROTECT(lev); + LWIP_UNUSED_ARG(ppp); + LWIP_UNUSED_ARG(pcomp); + LWIP_UNUSED_ARG(accomp); + + /* Load the ACCM bits for the 32 control codes. */ + PPPOS_PROTECT(lev); + for (i = 0; i < 32 / 8; i++) { + pppos->in_accm[i] = (u8_t)(accm >> (i * 8)); + } + PPPOS_UNPROTECT(lev); + + PPPDEBUG(LOG_INFO, ("pppos_recv_config[%d]: in_accm=%X %X %X %X\n", + pppos->ppp->netif->num, + pppos->in_accm[0], pppos->in_accm[1], pppos->in_accm[2], pppos->in_accm[3])); +} + +/* + * Drop the input packet. + */ +static void +pppos_input_free_current_packet(pppos_pcb *pppos) +{ + if (pppos->in_head != NULL) { + if (pppos->in_tail && (pppos->in_tail != pppos->in_head)) { + pbuf_free(pppos->in_tail); + } + pbuf_free(pppos->in_head); + pppos->in_head = NULL; + } + pppos->in_tail = NULL; +} + +/* + * Drop the input packet and increase error counters. + */ +static void +pppos_input_drop(pppos_pcb *pppos) +{ + if (pppos->in_head != NULL) { +#if 0 + PPPDEBUG(LOG_INFO, ("pppos_input_drop: %d:%.*H\n", pppos->in_head->len, min(60, pppos->in_head->len * 2), pppos->in_head->payload)); +#endif + PPPDEBUG(LOG_INFO, ("pppos_input_drop: pbuf len=%d, addr %p\n", pppos->in_head->len, (void*)pppos->in_head)); + } + pppos_input_free_current_packet(pppos); +#if VJ_SUPPORT + vj_uncompress_err(&pppos->ppp->vj_comp); +#endif /* VJ_SUPPORT */ + + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(pppos->ppp->netif, ifindiscards); +} + +/* + * pppos_output_append - append given character to end of given pbuf. + * If out_accm is not 0 and the character needs to be escaped, do so. + * If pbuf is full, send the pbuf and reuse it. + * Return the current pbuf. + */ +static err_t +pppos_output_append(pppos_pcb *pppos, err_t err, struct pbuf *nb, u8_t c, u8_t accm, u16_t *fcs) +{ + if (err != ERR_OK) { + return err; + } + + /* Make sure there is room for the character and an escape code. + * Sure we don't quite fill the buffer if the character doesn't + * get escaped but is one character worth complicating this? */ + if ((PBUF_POOL_BUFSIZE - nb->len) < 2) { + u32_t l = pppos->output_cb(pppos->ppp, (u8_t*)nb->payload, nb->len, pppos->ppp->ctx_cb); + if (l != nb->len) { + return ERR_IF; + } + nb->len = 0; + } + + /* Update FCS before checking for special characters. */ + if (fcs) { + *fcs = PPP_FCS(*fcs, c); + } + + /* Copy to output buffer escaping special characters. */ + if (accm && ESCAPE_P(pppos->out_accm, c)) { + *((u8_t*)nb->payload + nb->len++) = PPP_ESCAPE; + *((u8_t*)nb->payload + nb->len++) = c ^ PPP_TRANS; + } else { + *((u8_t*)nb->payload + nb->len++) = c; + } + + return ERR_OK; +} + +static err_t +pppos_output_last(pppos_pcb *pppos, err_t err, struct pbuf *nb, u16_t *fcs) +{ + ppp_pcb *ppp = pppos->ppp; + + /* Add FCS and trailing flag. */ + err = pppos_output_append(pppos, err, nb, ~(*fcs) & 0xFF, 1, NULL); + err = pppos_output_append(pppos, err, nb, (~(*fcs) >> 8) & 0xFF, 1, NULL); + err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL); + + if (err != ERR_OK) { + goto failed; + } + + /* Send remaining buffer if not empty */ + if (nb->len > 0) { + u32_t l = pppos->output_cb(ppp, (u8_t*)nb->payload, nb->len, ppp->ctx_cb); + if (l != nb->len) { + err = ERR_IF; + goto failed; + } + } + + pppos->last_xmit = sys_now(); + MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, nb->tot_len); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts); + LINK_STATS_INC(link.xmit); + pbuf_free(nb); + return ERR_OK; + +failed: + pppos->last_xmit = 0; /* prepend PPP_FLAG to next packet */ + LINK_STATS_INC(link.err); + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards); + pbuf_free(nb); + return err; +} + +#endif /* PPP_SUPPORT && PPPOS_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/upap.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/upap.c new file mode 100644 index 0000000..d00c2d7 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/upap.c @@ -0,0 +1,677 @@ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * @todo: + */ + +#if 0 /* UNUSED */ +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/upap.h" + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static option_t pap_option_list[] = { + { "hide-password", o_bool, &hide_password, + "Don't output passwords to log", OPT_PRIO | 1 }, + { "show-password", o_bool, &hide_password, + "Show password string in debug log messages", OPT_PRIOSUB | 0 }, + + { "pap-restart", o_int, &upap[0].us_timeouttime, + "Set retransmit timeout for PAP", OPT_PRIO }, + { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, + "Set max number of transmissions for auth-reqs", OPT_PRIO }, + { "pap-timeout", o_int, &upap[0].us_reqtimeout, + "Set time limit for peer PAP authentication", OPT_PRIO }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points. + */ +static void upap_init(ppp_pcb *pcb); +static void upap_lowerup(ppp_pcb *pcb); +static void upap_lowerdown(ppp_pcb *pcb); +static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l); +static void upap_protrej(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ + +const struct protent pap_protent = { + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, +#if PRINTPKT_SUPPORT + upap_printpkt, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_DATAINPUT + NULL, +#endif /* PPP_DATAINPUT */ +#if PRINTPKT_SUPPORT + "PAP", + NULL, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + pap_option_list, + NULL, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +static void upap_timeout(void *arg); +#if PPP_SERVER +static void upap_reqtimeout(void *arg); +static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len); +#endif /* PPP_SERVER */ +static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len); +static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len); +static void upap_sauthreq(ppp_pcb *pcb); +#if PPP_SERVER +static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen); +#endif /* PPP_SERVER */ + + +/* + * upap_init - Initialize a UPAP unit. + */ +static void upap_init(ppp_pcb *pcb) { + pcb->upap.us_user = NULL; + pcb->upap.us_userlen = 0; + pcb->upap.us_passwd = NULL; + pcb->upap.us_passwdlen = 0; + pcb->upap.us_clientstate = UPAPCS_INITIAL; +#if PPP_SERVER + pcb->upap.us_serverstate = UPAPSS_INITIAL; +#endif /* PPP_SERVER */ + pcb->upap.us_id = 0; +} + + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password) { + + if(!user || !password) + return; + + /* Save the username and password we're given */ + pcb->upap.us_user = user; + pcb->upap.us_userlen = LWIP_MIN(strlen(user), 0xff); + pcb->upap.us_passwd = password; + pcb->upap.us_passwdlen = LWIP_MIN(strlen(password), 0xff); + pcb->upap.us_transmits = 0; + + /* Lower layer up yet? */ + if (pcb->upap.us_clientstate == UPAPCS_INITIAL || + pcb->upap.us_clientstate == UPAPCS_PENDING) { + pcb->upap.us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(pcb); /* Start protocol */ +} + +#if PPP_SERVER +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void upap_authpeer(ppp_pcb *pcb) { + + /* Lower layer up yet? */ + if (pcb->upap.us_serverstate == UPAPSS_INITIAL || + pcb->upap.us_serverstate == UPAPSS_PENDING) { + pcb->upap.us_serverstate = UPAPSS_PENDING; + return; + } + + pcb->upap.us_serverstate = UPAPSS_LISTEN; + if (pcb->settings.pap_req_timeout > 0) + TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout); +} +#endif /* PPP_SERVER */ + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void upap_timeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) + return; + + if (pcb->upap.us_transmits >= pcb->settings.pap_max_transmits) { + /* give up in disgust */ + ppp_error("No response to PAP authenticate-requests"); + pcb->upap.us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(pcb, PPP_PAP); + return; + } + + upap_sauthreq(pcb); /* Send Authenticate-Request */ +} + + +#if PPP_SERVER +/* + * upap_reqtimeout - Give up waiting for the peer to send an auth-req. + */ +static void upap_reqtimeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (pcb->upap.us_serverstate != UPAPSS_LISTEN) + return; /* huh?? */ + + auth_peer_fail(pcb, PPP_PAP); + pcb->upap.us_serverstate = UPAPSS_BADAUTH; +} +#endif /* PPP_SERVER */ + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +static void upap_lowerup(ppp_pcb *pcb) { + + if (pcb->upap.us_clientstate == UPAPCS_INITIAL) + pcb->upap.us_clientstate = UPAPCS_CLOSED; + else if (pcb->upap.us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(pcb); /* send an auth-request */ + } + +#if PPP_SERVER + if (pcb->upap.us_serverstate == UPAPSS_INITIAL) + pcb->upap.us_serverstate = UPAPSS_CLOSED; + else if (pcb->upap.us_serverstate == UPAPSS_PENDING) { + pcb->upap.us_serverstate = UPAPSS_LISTEN; + if (pcb->settings.pap_req_timeout > 0) + TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout); + } +#endif /* PPP_SERVER */ +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void upap_lowerdown(ppp_pcb *pcb) { + + if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */ + UNTIMEOUT(upap_timeout, pcb); /* Cancel timeout */ +#if PPP_SERVER + if (pcb->upap.us_serverstate == UPAPSS_LISTEN && pcb->settings.pap_req_timeout > 0) + UNTIMEOUT(upap_reqtimeout, pcb); +#endif /* PPP_SERVER */ + + pcb->upap.us_clientstate = UPAPCS_INITIAL; +#if PPP_SERVER + pcb->upap.us_serverstate = UPAPSS_INITIAL; +#endif /* PPP_SERVER */ +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +static void upap_protrej(ppp_pcb *pcb) { + + if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) { + ppp_error("PAP authentication failed due to protocol-reject"); + auth_withpeer_fail(pcb, PPP_PAP); + } +#if PPP_SERVER + if (pcb->upap.us_serverstate == UPAPSS_LISTEN) { + ppp_error("PAP authentication of peer failed (protocol-reject)"); + auth_peer_fail(pcb, PPP_PAP); + } +#endif /* PPP_SERVER */ + upap_lowerdown(pcb); +} + + +/* + * upap_input - Input UPAP packet. + */ +static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l) { + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < UPAP_HEADERLEN) { + UPAPDEBUG(("pap_input: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG(("pap_input: rcvd illegal length.")); + return; + } + if (len > l) { + UPAPDEBUG(("pap_input: rcvd short packet.")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: +#if PPP_SERVER + upap_rauthreq(pcb, inp, id, len); +#endif /* PPP_SERVER */ + break; + + case UPAP_AUTHACK: + upap_rauthack(pcb, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(pcb, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } +} + +#if PPP_SERVER +/* + * upap_rauth - Receive Authenticate. + */ +static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char ruserlen, rpasswdlen; + char *ruser; + char *rpasswd; + char rhostname[256]; + int retcode; + const char *msg; + int msglen; + + if (pcb->upap.us_serverstate < UPAPSS_LISTEN) + return; + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (pcb->upap.us_serverstate == UPAPSS_OPEN) { + upap_sresp(pcb, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (pcb->upap.us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(pcb, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < 1) { + UPAPDEBUG(("pap_rauth: rcvd short packet.")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG(("pap_rauth: rcvd short packet.")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG(("pap_rauth: rcvd short packet.")); + return; + } + + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = UPAP_AUTHNAK; + if (auth_check_passwd(pcb, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen)) { + retcode = UPAP_AUTHACK; + } + BZERO(rpasswd, rpasswdlen); + +#if 0 /* UNUSED */ + /* + * Check remote number authorization. A plugin may have filled in + * the remote number or added an allowed number, and rather than + * return an authenticate failure, is leaving it for us to verify. + */ + if (retcode == UPAP_AUTHACK) { + if (!auth_number()) { + /* We do not want to leak info about the pap result. */ + retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */ + warn("calling number %q is not authorized", remote_number); + } + } + + msglen = strlen(msg); + if (msglen > 255) + msglen = 255; +#endif /* UNUSED */ + + upap_sresp(pcb, retcode, id, msg, msglen); + + /* Null terminate and clean remote name. */ + ppp_slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser); + + if (retcode == UPAP_AUTHACK) { + pcb->upap.us_serverstate = UPAPSS_OPEN; + ppp_notice("PAP peer authentication succeeded for %q", rhostname); + auth_peer_success(pcb, PPP_PAP, 0, ruser, ruserlen); + } else { + pcb->upap.us_serverstate = UPAPSS_BADAUTH; + ppp_warn("PAP peer authentication failed for %q", rhostname); + auth_peer_fail(pcb, PPP_PAP); + } + + if (pcb->settings.pap_req_timeout > 0) + UNTIMEOUT(upap_reqtimeout, pcb); +} +#endif /* PPP_SERVER */ + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char msglen; + char *msg; + LWIP_UNUSED_ARG(id); + + if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < 1) { + UPAPDEBUG(("pap_rauthack: ignoring missing msg-length.")); + } else { + GETCHAR(msglen, inp); + if (msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(("pap_rauthack: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + pcb->upap.us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(pcb, PPP_PAP, 0); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nak. + */ +static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char msglen; + char *msg; + LWIP_UNUSED_ARG(id); + + if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < 1) { + UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length.")); + } else { + GETCHAR(msglen, inp); + if (msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(("pap_rauthnak: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + pcb->upap.us_clientstate = UPAPCS_BADAUTH; + + ppp_error("PAP authentication failed"); + auth_withpeer_fail(pcb, PPP_PAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void upap_sauthreq(ppp_pcb *pcb) { + struct pbuf *p; + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + pcb->upap.us_userlen + pcb->upap.us_passwdlen; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++pcb->upap.us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(pcb->upap.us_userlen, outp); + MEMCPY(outp, pcb->upap.us_user, pcb->upap.us_userlen); + INCPTR(pcb->upap.us_userlen, outp); + PUTCHAR(pcb->upap.us_passwdlen, outp); + MEMCPY(outp, pcb->upap.us_passwd, pcb->upap.us_passwdlen); + + ppp_write(pcb, p); + + TIMEOUT(upap_timeout, pcb, pcb->settings.pap_timeout_time); + ++pcb->upap.us_transmits; + pcb->upap.us_clientstate = UPAPCS_AUTHREQ; +} + +#if PPP_SERVER +/* + * upap_sresp - Send a response (ack or nak). + */ +static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen) { + struct pbuf *p; + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + MEMCPY(outp, msg, msglen); + + ppp_write(pcb, p); +} +#endif /* PPP_SERVER */ + +#if PRINTPKT_SUPPORT +/* + * upap_printpkt - print the contents of a PAP packet. + */ +static const char* const upap_codenames[] = { + "AuthReq", "AuthAck", "AuthNak" +}; + +static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len; + int mlen, ulen, wlen; + const u_char *user, *pwd, *msg; + const u_char *pstart; + + if (plen < UPAP_HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < UPAP_HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(upap_codenames)) + printer(arg, " %s", upap_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= UPAP_HEADERLEN; + switch (code) { + case UPAP_AUTHREQ: + if (len < 1) + break; + ulen = p[0]; + if (len < ulen + 2) + break; + wlen = p[ulen + 1]; + if (len < ulen + wlen + 2) + break; + user = (const u_char *) (p + 1); + pwd = (const u_char *) (p + ulen + 2); + p += ulen + wlen + 2; + len -= ulen + wlen + 2; + printer(arg, " user="); + ppp_print_string(user, ulen, printer, arg); + printer(arg, " password="); +/* FIXME: require ppp_pcb struct as printpkt() argument */ +#if 0 + if (!pcb->settings.hide_password) +#endif + ppp_print_string(pwd, wlen, printer, arg); +#if 0 + else + printer(arg, ""); +#endif + break; + case UPAP_AUTHACK: + case UPAP_AUTHNAK: + if (len < 1) + break; + mlen = p[0]; + if (len < mlen + 1) + break; + msg = (const u_char *) (p + 1); + p += mlen + 1; + len -= mlen + 1; + printer(arg, " "); + ppp_print_string(msg, mlen, printer, arg); + break; + default: + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} +#endif /* PRINTPKT_SUPPORT */ + +#endif /* PPP_SUPPORT && PAP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/utils.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/utils.c new file mode 100644 index 0000000..008c633 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/utils.c @@ -0,0 +1,959 @@ +/* + * utils.c - various utility functions used in pppd. + * + * Copyright (c) 1999-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SVR4 +#include +#endif +#endif /* UNUSED */ + +#include /* isdigit() */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" + +#if defined(SUNOS4) +extern char *strerror(); +#endif + +static void ppp_logit(int level, const char *fmt, va_list args); +static void ppp_log_write(int level, char *buf); +#if PRINTPKT_SUPPORT +static void ppp_vslp_printer(void *arg, const char *fmt, ...); +static void ppp_format_packet(const u_char *p, int len, + void (*printer) (void *, const char *, ...), void *arg); + +struct buffer_info { + char *ptr; + int len; +}; +#endif /* PRINTPKT_SUPPORT */ + +/* + * ppp_strlcpy - like strcpy/strncpy, doesn't overflow destination buffer, + * always leaves destination null-terminated (for len > 0). + */ +size_t ppp_strlcpy(char *dest, const char *src, size_t len) { + size_t ret = strlen(src); + + if (len != 0) { + if (ret < len) + strcpy(dest, src); + else { + strncpy(dest, src, len - 1); + dest[len-1] = 0; + } + } + return ret; +} + +/* + * ppp_strlcat - like strcat/strncat, doesn't overflow destination buffer, + * always leaves destination null-terminated (for len > 0). + */ +size_t ppp_strlcat(char *dest, const char *src, size_t len) { + size_t dlen = strlen(dest); + + return dlen + ppp_strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0)); +} + + +/* + * ppp_slprintf - format a message into a buffer. Like sprintf except we + * also specify the length of the output buffer, and we handle + * %m (error message), %v (visible string), + * %q (quoted string), %t (current time) and %I (IP address) formats. + * Doesn't do floating-point formats. + * Returns the number of chars put into buf. + */ +int ppp_slprintf(char *buf, int buflen, const char *fmt, ...) { + va_list args; + int n; + + va_start(args, fmt); + n = ppp_vslprintf(buf, buflen, fmt, args); + va_end(args); + return n; +} + +/* + * ppp_vslprintf - like ppp_slprintf, takes a va_list instead of a list of args. + */ +#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) + +int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args) { + int c, i, n; + int width, prec, fillch; + int base, len, neg, quoted; + unsigned long val = 0; + const char *f; + char *str, *buf0; + const unsigned char *p; + char num[32]; +#if 0 /* need port */ + time_t t; +#endif /* need port */ + u32_t ip; + static char hexchars[] = "0123456789abcdef"; +#if PRINTPKT_SUPPORT + struct buffer_info bufinfo; +#endif /* PRINTPKT_SUPPORT */ + + buf0 = buf; + --buflen; + while (buflen > 0) { + for (f = fmt; *f != '%' && *f != 0; ++f) + ; + if (f > fmt) { + len = f - fmt; + if (len > buflen) + len = buflen; + memcpy(buf, fmt, len); + buf += len; + buflen -= len; + fmt = f; + } + if (*fmt == 0) + break; + c = *++fmt; + width = 0; + prec = -1; + fillch = ' '; + if (c == '0') { + fillch = '0'; + c = *++fmt; + } + if (c == '*') { + width = va_arg(args, int); + c = *++fmt; + } else { + while (isdigit(c)) { + width = width * 10 + c - '0'; + c = *++fmt; + } + } + if (c == '.') { + c = *++fmt; + if (c == '*') { + prec = va_arg(args, int); + c = *++fmt; + } else { + prec = 0; + while (isdigit(c)) { + prec = prec * 10 + c - '0'; + c = *++fmt; + } + } + } + str = 0; + base = 0; + neg = 0; + ++fmt; + switch (c) { + case 'l': + c = *fmt++; + switch (c) { + case 'd': + val = va_arg(args, long); + if ((long)val < 0) { + neg = 1; + val = (unsigned long)-(long)val; + } + base = 10; + break; + case 'u': + val = va_arg(args, unsigned long); + base = 10; + break; + default: + OUTCHAR('%'); + OUTCHAR('l'); + --fmt; /* so %lz outputs %lz etc. */ + continue; + } + break; + case 'd': + i = va_arg(args, int); + if (i < 0) { + neg = 1; + val = -i; + } else + val = i; + base = 10; + break; + case 'u': + val = va_arg(args, unsigned int); + base = 10; + break; + case 'o': + val = va_arg(args, unsigned int); + base = 8; + break; + case 'x': + case 'X': + val = va_arg(args, unsigned int); + base = 16; + break; +#if 0 /* unused (and wrong on LLP64 systems) */ + case 'p': + val = (unsigned long) va_arg(args, void *); + base = 16; + neg = 2; + break; +#endif /* unused (and wrong on LLP64 systems) */ + case 's': + str = va_arg(args, char *); + break; + case 'c': + num[0] = va_arg(args, int); + num[1] = 0; + str = num; + break; +#if 0 /* do we always have strerror() in embedded ? */ + case 'm': + str = strerror(errno); + break; +#endif /* do we always have strerror() in embedded ? */ + case 'I': + ip = va_arg(args, u32_t); + ip = lwip_ntohl(ip); + ppp_slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff, + (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); + str = num; + break; +#if 0 /* need port */ + case 't': + time(&t); + str = ctime(&t); + str += 4; /* chop off the day name */ + str[15] = 0; /* chop off year and newline */ + break; +#endif /* need port */ + case 'v': /* "visible" string */ + case 'q': /* quoted string */ + quoted = c == 'q'; + p = va_arg(args, unsigned char *); + if (p == NULL) + p = (const unsigned char *)""; + if (fillch == '0' && prec >= 0) { + n = prec; + } else { + n = strlen((const char *)p); + if (prec >= 0 && n > prec) + n = prec; + } + while (n > 0 && buflen > 0) { + c = *p++; + --n; + if (!quoted && c >= 0x80) { + OUTCHAR('M'); + OUTCHAR('-'); + c -= 0x80; + } + if (quoted && (c == '"' || c == '\\')) + OUTCHAR('\\'); + if (c < 0x20 || (0x7f <= c && c < 0xa0)) { + if (quoted) { + OUTCHAR('\\'); + switch (c) { + case '\t': OUTCHAR('t'); break; + case '\n': OUTCHAR('n'); break; + case '\b': OUTCHAR('b'); break; + case '\f': OUTCHAR('f'); break; + default: + OUTCHAR('x'); + OUTCHAR(hexchars[c >> 4]); + OUTCHAR(hexchars[c & 0xf]); + } + } else { + if (c == '\t') + OUTCHAR(c); + else { + OUTCHAR('^'); + OUTCHAR(c ^ 0x40); + } + } + } else + OUTCHAR(c); + } + continue; +#if PRINTPKT_SUPPORT + case 'P': /* print PPP packet */ + bufinfo.ptr = buf; + bufinfo.len = buflen + 1; + p = va_arg(args, unsigned char *); + n = va_arg(args, int); + ppp_format_packet(p, n, ppp_vslp_printer, &bufinfo); + buf = bufinfo.ptr; + buflen = bufinfo.len - 1; + continue; +#endif /* PRINTPKT_SUPPORT */ + case 'B': + p = va_arg(args, unsigned char *); + for (n = prec; n > 0; --n) { + c = *p++; + if (fillch == ' ') + OUTCHAR(' '); + OUTCHAR(hexchars[(c >> 4) & 0xf]); + OUTCHAR(hexchars[c & 0xf]); + } + continue; + default: + *buf++ = '%'; + if (c != '%') + --fmt; /* so %z outputs %z etc. */ + --buflen; + continue; + } + if (base != 0) { + str = num + sizeof(num); + *--str = 0; + while (str > num + neg) { + *--str = hexchars[val % base]; + val = val / base; + if (--prec <= 0 && val == 0) + break; + } + switch (neg) { + case 1: + *--str = '-'; + break; + case 2: + *--str = 'x'; + *--str = '0'; + break; + default: + break; + } + len = num + sizeof(num) - 1 - str; + } else { + len = strlen(str); + if (prec >= 0 && len > prec) + len = prec; + } + if (width > 0) { + if (width > buflen) + width = buflen; + if ((n = width - len) > 0) { + buflen -= n; + for (; n > 0; --n) + *buf++ = fillch; + } + } + if (len > buflen) + len = buflen; + memcpy(buf, str, len); + buf += len; + buflen -= len; + } + *buf = 0; + return buf - buf0; +} + +#if PRINTPKT_SUPPORT +/* + * vslp_printer - used in processing a %P format + */ +static void ppp_vslp_printer(void *arg, const char *fmt, ...) { + int n; + va_list pvar; + struct buffer_info *bi; + + va_start(pvar, fmt); + bi = (struct buffer_info *) arg; + n = ppp_vslprintf(bi->ptr, bi->len, fmt, pvar); + va_end(pvar); + + bi->ptr += n; + bi->len -= n; +} +#endif /* PRINTPKT_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * log_packet - format a packet and log it. + */ + +void +log_packet(p, len, prefix, level) + u_char *p; + int len; + char *prefix; + int level; +{ + init_pr_log(prefix, level); + ppp_format_packet(p, len, pr_log, &level); + end_pr_log(); +} +#endif /* UNUSED */ + +#if PRINTPKT_SUPPORT +/* + * ppp_format_packet - make a readable representation of a packet, + * calling `printer(arg, format, ...)' to output it. + */ +static void ppp_format_packet(const u_char *p, int len, + void (*printer) (void *, const char *, ...), void *arg) { + int i, n; + u_short proto; + const struct protent *protp; + + if (len >= 2) { + GETSHORT(proto, p); + len -= 2; + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (proto == protp->protocol) + break; + if (protp != NULL) { + printer(arg, "[%s", protp->name); + n = (*protp->printpkt)(p, len, printer, arg); + printer(arg, "]"); + p += n; + len -= n; + } else { + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (proto == (protp->protocol & ~0x8000)) + break; + if (protp != 0 && protp->data_name != 0) { + printer(arg, "[%s data]", protp->data_name); + if (len > 8) + printer(arg, "%.8B ...", p); + else + printer(arg, "%.*B", len, p); + len = 0; + } else + printer(arg, "[proto=0x%x]", proto); + } + } + + if (len > 32) + printer(arg, "%.32B ...", p); + else + printer(arg, "%.*B", len, p); +} +#endif /* PRINTPKT_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * init_pr_log, end_pr_log - initialize and finish use of pr_log. + */ + +static char line[256]; /* line to be logged accumulated here */ +static char *linep; /* current pointer within line */ +static int llevel; /* level for logging */ + +void +init_pr_log(prefix, level) + const char *prefix; + int level; +{ + linep = line; + if (prefix != NULL) { + ppp_strlcpy(line, prefix, sizeof(line)); + linep = line + strlen(line); + } + llevel = level; +} + +void +end_pr_log() +{ + if (linep != line) { + *linep = 0; + ppp_log_write(llevel, line); + } +} + +/* + * pr_log - printer routine for outputting to log + */ +void +pr_log (void *arg, const char *fmt, ...) +{ + int l, n; + va_list pvar; + char *p, *eol; + char buf[256]; + + va_start(pvar, fmt); + n = ppp_vslprintf(buf, sizeof(buf), fmt, pvar); + va_end(pvar); + + p = buf; + eol = strchr(buf, '\n'); + if (linep != line) { + l = (eol == NULL)? n: eol - buf; + if (linep + l < line + sizeof(line)) { + if (l > 0) { + memcpy(linep, buf, l); + linep += l; + } + if (eol == NULL) + return; + p = eol + 1; + eol = strchr(p, '\n'); + } + *linep = 0; + ppp_log_write(llevel, line); + linep = line; + } + + while (eol != NULL) { + *eol = 0; + ppp_log_write(llevel, p); + p = eol + 1; + eol = strchr(p, '\n'); + } + + /* assumes sizeof(buf) <= sizeof(line) */ + l = buf + n - p; + if (l > 0) { + memcpy(line, p, n); + linep = line + l; + } +} +#endif /* UNUSED */ + +/* + * ppp_print_string - print a readable representation of a string using + * printer. + */ +void ppp_print_string(const u_char *p, int len, void (*printer) (void *, const char *, ...), void *arg) { + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') + printer(arg, "\\"); + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", (u8_t)c); + /* no break */ + } + } + } + printer(arg, "\""); +} + +/* + * ppp_logit - does the hard work for fatal et al. + */ +static void ppp_logit(int level, const char *fmt, va_list args) { + char buf[1024]; + + ppp_vslprintf(buf, sizeof(buf), fmt, args); + ppp_log_write(level, buf); +} + +static void ppp_log_write(int level, char *buf) { + LWIP_UNUSED_ARG(level); /* necessary if PPPDEBUG is defined to an empty function */ + LWIP_UNUSED_ARG(buf); + PPPDEBUG(level, ("%s\n", buf) ); +#if 0 + if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) { + int n = strlen(buf); + + if (n > 0 && buf[n-1] == '\n') + --n; + if (write(log_to_fd, buf, n) != n + || write(log_to_fd, "\n", 1) != 1) + log_to_fd = -1; + } +#endif +} + +/* + * ppp_fatal - log an error message and die horribly. + */ +void ppp_fatal(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_ERR, fmt, pvar); + va_end(pvar); + + LWIP_ASSERT("ppp_fatal", 0); /* as promised */ +} + +/* + * ppp_error - log an error message. + */ +void ppp_error(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_ERR, fmt, pvar); + va_end(pvar); +#if 0 /* UNUSED */ + ++error_count; +#endif /* UNUSED */ +} + +/* + * ppp_warn - log a warning message. + */ +void ppp_warn(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_WARNING, fmt, pvar); + va_end(pvar); +} + +/* + * ppp_notice - log a notice-level message. + */ +void ppp_notice(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_NOTICE, fmt, pvar); + va_end(pvar); +} + +/* + * ppp_info - log an informational message. + */ +void ppp_info(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_INFO, fmt, pvar); + va_end(pvar); +} + +/* + * ppp_dbglog - log a debug message. + */ +void ppp_dbglog(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_DEBUG, fmt, pvar); + va_end(pvar); +} + +#if PRINTPKT_SUPPORT +/* + * ppp_dump_packet - print out a packet in readable form if it is interesting. + * Assumes len >= PPP_HDRLEN. + */ +void ppp_dump_packet(ppp_pcb *pcb, const char *tag, unsigned char *p, int len) { + int proto; + + /* + * don't print data packets, i.e. IPv4, IPv6, VJ, and compressed packets. + */ + proto = (p[0] << 8) + p[1]; + if (proto < 0xC000 && (proto & ~0x8000) == proto) + return; + + /* + * don't print valid LCP echo request/reply packets if the link is up. + */ + if (proto == PPP_LCP && pcb->phase == PPP_PHASE_RUNNING && len >= 2 + HEADERLEN) { + unsigned char *lcp = p + 2; + int l = (lcp[2] << 8) + lcp[3]; + + if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP) + && l >= HEADERLEN && l <= len - 2) + return; + } + + ppp_dbglog("%s %P", tag, p, len); +} +#endif /* PRINTPKT_SUPPORT */ + +#if 0 /* Unused */ + +/* + * complete_read - read a full `count' bytes from fd, + * unless end-of-file or an error other than EINTR is encountered. + */ +ssize_t +complete_read(int fd, void *buf, size_t count) +{ + size_t done; + ssize_t nb; + char *ptr = buf; + + for (done = 0; done < count; ) { + nb = read(fd, ptr, count - done); + if (nb < 0) { + if (errno == EINTR) + continue; + return -1; + } + if (nb == 0) + break; + done += nb; + ptr += nb; + } + return done; +} + +/* Procedures for locking the serial device using a lock file. */ +#ifndef LOCK_DIR +#ifdef __linux__ +#define LOCK_DIR "/var/lock" +#else +#ifdef SVR4 +#define LOCK_DIR "/var/spool/locks" +#else +#define LOCK_DIR "/var/spool/lock" +#endif +#endif +#endif /* LOCK_DIR */ + +static char lock_file[MAXPATHLEN]; + +/* + * lock - create a lock file for the named device + */ +int +lock(dev) + char *dev; +{ +#ifdef LOCKLIB + int result; + + result = mklock (dev, (void *) 0); + if (result == 0) { + ppp_strlcpy(lock_file, dev, sizeof(lock_file)); + return 0; + } + + if (result > 0) + ppp_notice("Device %s is locked by pid %d", dev, result); + else + ppp_error("Can't create lock file %s", lock_file); + return -1; + +#else /* LOCKLIB */ + + char lock_buffer[12]; + int fd, pid, n; + +#ifdef SVR4 + struct stat sbuf; + + if (stat(dev, &sbuf) < 0) { + ppp_error("Can't get device number for %s: %m", dev); + return -1; + } + if ((sbuf.st_mode & S_IFMT) != S_IFCHR) { + ppp_error("Can't lock %s: not a character device", dev); + return -1; + } + ppp_slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d", + LOCK_DIR, major(sbuf.st_dev), + major(sbuf.st_rdev), minor(sbuf.st_rdev)); +#else + char *p; + char lockdev[MAXPATHLEN]; + + if ((p = strstr(dev, "dev/")) != NULL) { + dev = p + 4; + strncpy(lockdev, dev, MAXPATHLEN-1); + lockdev[MAXPATHLEN-1] = 0; + while ((p = strrchr(lockdev, '/')) != NULL) { + *p = '_'; + } + dev = lockdev; + } else + if ((p = strrchr(dev, '/')) != NULL) + dev = p + 1; + + ppp_slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev); +#endif + + while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) { + if (errno != EEXIST) { + ppp_error("Can't create lock file %s: %m", lock_file); + break; + } + + /* Read the lock file to find out who has the device locked. */ + fd = open(lock_file, O_RDONLY, 0); + if (fd < 0) { + if (errno == ENOENT) /* This is just a timing problem. */ + continue; + ppp_error("Can't open existing lock file %s: %m", lock_file); + break; + } +#ifndef LOCK_BINARY + n = read(fd, lock_buffer, 11); +#else + n = read(fd, &pid, sizeof(pid)); +#endif /* LOCK_BINARY */ + close(fd); + fd = -1; + if (n <= 0) { + ppp_error("Can't read pid from lock file %s", lock_file); + break; + } + + /* See if the process still exists. */ +#ifndef LOCK_BINARY + lock_buffer[n] = 0; + pid = atoi(lock_buffer); +#endif /* LOCK_BINARY */ + if (pid == getpid()) + return 1; /* somebody else locked it for us */ + if (pid == 0 + || (kill(pid, 0) == -1 && errno == ESRCH)) { + if (unlink (lock_file) == 0) { + ppp_notice("Removed stale lock on %s (pid %d)", dev, pid); + continue; + } + ppp_warn("Couldn't remove stale lock on %s", dev); + } else + ppp_notice("Device %s is locked by pid %d", dev, pid); + break; + } + + if (fd < 0) { + lock_file[0] = 0; + return -1; + } + + pid = getpid(); +#ifndef LOCK_BINARY + ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid); + write (fd, lock_buffer, 11); +#else + write(fd, &pid, sizeof (pid)); +#endif + close(fd); + return 0; + +#endif +} + +/* + * relock - called to update our lockfile when we are about to detach, + * thus changing our pid (we fork, the child carries on, and the parent dies). + * Note that this is called by the parent, with pid equal to the pid + * of the child. This avoids a potential race which would exist if + * we had the child rewrite the lockfile (the parent might die first, + * and another process could think the lock was stale if it checked + * between when the parent died and the child rewrote the lockfile). + */ +int +relock(pid) + int pid; +{ +#ifdef LOCKLIB + /* XXX is there a way to do this? */ + return -1; +#else /* LOCKLIB */ + + int fd; + char lock_buffer[12]; + + if (lock_file[0] == 0) + return -1; + fd = open(lock_file, O_WRONLY, 0); + if (fd < 0) { + ppp_error("Couldn't reopen lock file %s: %m", lock_file); + lock_file[0] = 0; + return -1; + } + +#ifndef LOCK_BINARY + ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid); + write (fd, lock_buffer, 11); +#else + write(fd, &pid, sizeof(pid)); +#endif /* LOCK_BINARY */ + close(fd); + return 0; + +#endif /* LOCKLIB */ +} + +/* + * unlock - remove our lockfile + */ +void +unlock() +{ + if (lock_file[0]) { +#ifdef LOCKLIB + (void) rmlock(lock_file, (void *) 0); +#else + unlink(lock_file); +#endif + lock_file[0] = 0; + } +} + +#endif /* Unused */ + +#endif /* PPP_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/ppp/vj.c b/sdk/Src/Network/lwip2.0.3/netif/ppp/vj.c new file mode 100644 index 0000000..168c340 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/ppp/vj.c @@ -0,0 +1,695 @@ +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * Initial distribution. + * + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * Modified March 1998 by Guy Lancaster, glanca@gesn.com, + * for a 16 bit processor. + */ + +#include "netif/ppp/ppp_opts.h" +#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/pppdebug.h" + +#include "netif/ppp/vj.h" + +#include + +#if LINK_STATS +#define INCR(counter) ++comp->stats.counter +#else +#define INCR(counter) +#endif + +void +vj_compress_init(struct vjcompress *comp) +{ + u8_t i; + struct cstate *tstate = comp->tstate; + +#if MAX_SLOTS == 0 + memset((char *)comp, 0, sizeof(*comp)); +#endif + comp->maxSlotIndex = MAX_SLOTS - 1; + comp->compressSlot = 0; /* Disable slot ID compression by default. */ + for (i = MAX_SLOTS - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = VJF_TOSS; +} + + +/* ENCODE encodes a number that is known to be non-zero. ENCODEZ + * checks for zero (since zero has to be encoded in the long, 3 byte + * form). + */ +#define ENCODE(n) { \ + if ((u16_t)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (u8_t)(n); \ + cp[0] = (u8_t)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u8_t)(n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u16_t)(n) >= 256 || (u16_t)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (u8_t)(n); \ + cp[0] = (u8_t)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u8_t)(n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + u32_t tmp_ = lwip_ntohl(f) + ((cp[1] << 8) | cp[2]); \ + (f) = lwip_htonl(tmp_); \ + cp += 3; \ + } else { \ + u32_t tmp_ = lwip_ntohl(f) + (u32_t)*cp++; \ + (f) = lwip_htonl(tmp_); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + u16_t tmp_ = lwip_ntohs(f) + (((u16_t)cp[1] << 8) | cp[2]); \ + (f) = lwip_htons(tmp_); \ + cp += 3; \ + } else { \ + u16_t tmp_ = lwip_ntohs(f) + (u16_t)*cp++; \ + (f) = lwip_htons(tmp_); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = lwip_htons(((u16_t)cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = lwip_htons((u16_t)*cp++); \ + } \ +} + +/* Helper structures for unaligned *u32_t and *u16_t accesses */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct vj_u32_t { + PACK_STRUCT_FIELD(u32_t v); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct vj_u16_t { + PACK_STRUCT_FIELD(u16_t v); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * vj_compress_tcp - Attempt to do Van Jacobson header compression on a + * packet. This assumes that nb and comp are not null and that the first + * buffer of the chain contains a valid IP header. + * Return the VJ type code indicating whether or not the packet was + * compressed. + */ +u8_t +vj_compress_tcp(struct vjcompress *comp, struct pbuf **pb) +{ + struct pbuf *np = *pb; + struct ip_hdr *ip = (struct ip_hdr *)np->payload; + struct cstate *cs = comp->last_cs->cs_next; + u16_t ilen = IPH_HL(ip); + u16_t hlen; + struct tcp_hdr *oth; + struct tcp_hdr *th; + u16_t deltaS, deltaA = 0; + u32_t deltaL; + u32_t changes = 0; + u8_t new_seq[16]; + u8_t *cp = new_seq; + + /* + * Check that the packet is IP proto TCP. + */ + if (IPH_PROTO(ip) != IP_PROTO_TCP) { + return (TYPE_IP); + } + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). + */ + if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || np->tot_len < 40) { + return (TYPE_IP); + } + th = (struct tcp_hdr *)&((struct vj_u32_t*)ip)[ilen]; + if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { + return (TYPE_IP); + } + + /* Check that the TCP/IP headers are contained in the first buffer. */ + hlen = ilen + TCPH_HDRLEN(th); + hlen <<= 2; + if (np->len < hlen) { + PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen)); + return (TYPE_IP); + } + + /* TCP stack requires that we don't change the packet payload, therefore we copy + * the whole packet before compression. */ + np = pbuf_alloc(PBUF_RAW, np->tot_len, PBUF_POOL); + if (!np) { + return (TYPE_IP); + } + + if (pbuf_copy(np, *pb) != ERR_OK) { + pbuf_free(np); + return (TYPE_IP); + } + + *pb = np; + ip = (struct ip_hdr *)np->payload; + + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(vjs_packets); + if (!ip4_addr_cmp(&ip->src, &cs->cs_ip.src) + || !ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest) + || (*(struct vj_u32_t*)th).v != (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + struct cstate *lcs; + struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(vjs_searches); + if (ip4_addr_cmp(&ip->src, &cs->cs_ip.src) + && ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest) + && (*(struct vj_u32_t*)th).v == (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) { + goto found; + } + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(vjs_misses); + comp->last_cs = lcs; + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) { + comp->last_cs = lcs; + } else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + oth = (struct tcp_hdr *)&((struct vj_u32_t*)&cs->cs_ip)[ilen]; + deltaS = ilen; + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + if ((((struct vj_u16_t*)ip)[0]).v != (((struct vj_u16_t*)&cs->cs_ip)[0]).v + || (((struct vj_u16_t*)ip)[3]).v != (((struct vj_u16_t*)&cs->cs_ip)[3]).v + || (((struct vj_u16_t*)ip)[4]).v != (((struct vj_u16_t*)&cs->cs_ip)[4]).v + || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth) + || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) + || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) { + goto uncompressed; + } + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (TCPH_FLAGS(th) & TCP_URG) { + deltaS = lwip_ntohs(th->urgp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->urgp != oth->urgp) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + + if ((deltaS = (u16_t)(lwip_ntohs(th->wnd) - lwip_ntohs(oth->wnd))) != 0) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if ((deltaL = lwip_ntohl(th->ackno) - lwip_ntohl(oth->ackno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaA = (u16_t)deltaL; + ENCODE(deltaA); + changes |= NEW_A; + } + + if ((deltaL = lwip_ntohl(th->seqno) - lwip_ntohl(oth->seqno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaS = (u16_t)deltaL; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) && + lwip_ntohs(IPH_LEN(&cs->cs_ip)) == hlen) { + break; + } + /* no break */ + /* fall through */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + + case NEW_S|NEW_A: + if (deltaS == deltaA && deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + default: + break; + } + + deltaS = (u16_t)(lwip_ntohs(IPH_ID(ip)) - lwip_ntohs(IPH_ID(&cs->cs_ip))); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (TCPH_FLAGS(th) & TCP_PSH) { + changes |= TCP_PUSH_BIT; + } + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = lwip_ntohs(th->chksum); + MEMCPY(&cs->cs_ip, ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = (u16_t)(cp - new_seq); + if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + if (pbuf_header(np, -(s16_t)hlen)){ + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u8_t*)np->payload; + *cp++ = (u8_t)(changes | NEW_C); + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + if (pbuf_header(np, -(s16_t)hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u8_t*)np->payload; + *cp++ = (u8_t)changes; + } + *cp++ = (u8_t)(deltaA >> 8); + *cp++ = (u8_t)deltaA; + MEMCPY(cp, new_seq, deltaS); + INCR(vjs_compressed); + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet (that is, + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + MEMCPY(&cs->cs_ip, ip, hlen); + IPH_PROTO_SET(ip, cs->cs_id); + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); +} + +/* + * Called when we may have missed a packet. + */ +void +vj_uncompress_err(struct vjcompress *comp) +{ + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); +} + +/* + * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. + * Return 0 on success, -1 on failure. + */ +int +vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) +{ + u32_t hlen; + struct cstate *cs; + struct ip_hdr *ip; + + ip = (struct ip_hdr *)nb->payload; + hlen = IPH_HL(ip) << 2; + if (IPH_PROTO(ip) >= MAX_SLOTS + || hlen + sizeof(struct tcp_hdr) > nb->len + || (hlen += TCPH_HDRLEN(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2) + > nb->len + || hlen > MAX_HDR) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", + IPH_PROTO(ip), hlen, nb->len)); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return -1; + } + cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)]; + comp->flags &=~ VJF_TOSS; + IPH_PROTO_SET(ip, IP_PROTO_TCP); + MEMCPY(&cs->cs_ip, ip, hlen); + cs->cs_hlen = (u16_t)hlen; + INCR(vjs_uncompressedin); + return 0; +} + +/* + * Uncompress a packet of type TYPE_COMPRESSED_TCP. + * The packet is composed of a buffer chain and the first buffer + * must contain an accurate chain length. + * The first buffer must include the entire compressed TCP/IP header. + * This procedure replaces the compressed header with the uncompressed + * header and returns the length of the VJ header. + */ +int +vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) +{ + u8_t *cp; + struct tcp_hdr *th; + struct cstate *cs; + struct vj_u16_t *bp; + struct pbuf *n0 = *nb; + u32_t tmp; + u32_t vjlen, hlen, changes; + + INCR(vjs_compressedin); + cp = (u8_t*)n0->payload; + changes = *cp++; + if (changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + if (*cp >= MAX_SLOTS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp)); + goto bad; + } + + comp->flags &=~ VJF_TOSS; + comp->last_recv = *cp++; + } else { + /* + * this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. + */ + if (comp->flags & VJF_TOSS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n")); + INCR(vjs_tossed); + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = IPH_HL(&cs->cs_ip) << 2; + th = (struct tcp_hdr *)&((u8_t*)&cs->cs_ip)[hlen]; + th->chksum = lwip_htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) { + TCPH_SET_FLAG(th, TCP_PSH); + } else { + TCPH_UNSET_FLAG(th, TCP_PSH); + } + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + u32_t i = lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + /* some compilers can't nest inline assembler.. */ + tmp = lwip_ntohl(th->ackno) + i; + th->ackno = lwip_htonl(tmp); + tmp = lwip_ntohl(th->seqno) + i; + th->seqno = lwip_htonl(tmp); + } + break; + + case SPECIAL_D: + /* some compilers can't nest inline assembler.. */ + tmp = lwip_ntohl(th->seqno) + lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + th->seqno = lwip_htonl(tmp); + break; + + default: + if (changes & NEW_U) { + TCPH_SET_FLAG(th, TCP_URG); + DECODEU(th->urgp); + } else { + TCPH_UNSET_FLAG(th, TCP_URG); + } + if (changes & NEW_W) { + DECODES(th->wnd); + } + if (changes & NEW_A) { + DECODEL(th->ackno); + } + if (changes & NEW_S) { + DECODEL(th->seqno); + } + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip._id); + } else { + IPH_ID_SET(&cs->cs_ip, lwip_ntohs(IPH_ID(&cs->cs_ip)) + 1); + IPH_ID_SET(&cs->cs_ip, lwip_htons(IPH_ID(&cs->cs_ip))); + } + + /* + * At this point, cp points to the first byte of data in the + * packet. Fill in the IP total length and update the IP + * header checksum. + */ + vjlen = (u16_t)(cp - (u8_t*)n0->payload); + if (n0->len < vjlen) { + /* + * We must have dropped some characters (crc should detect + * this but the old slip framing won't) + */ + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n", + n0->len, vjlen)); + goto bad; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + tmp = n0->tot_len - vjlen + cs->cs_hlen; + IPH_LEN_SET(&cs->cs_ip, lwip_htons((u16_t)tmp)); +#else + IPH_LEN_SET(&cs->cs_ip, lwip_htons(n0->tot_len - vjlen + cs->cs_hlen)); +#endif + + /* recompute the ip header checksum */ + bp = (struct vj_u16_t*) &cs->cs_ip; + IPH_CHKSUM_SET(&cs->cs_ip, 0); + for (tmp = 0; hlen > 0; hlen -= 2) { + tmp += (*bp++).v; + } + tmp = (tmp & 0xffff) + (tmp >> 16); + tmp = (tmp & 0xffff) + (tmp >> 16); + IPH_CHKSUM_SET(&cs->cs_ip, (u16_t)(~tmp)); + + /* Remove the compressed header and prepend the uncompressed header. */ + if (pbuf_header(n0, -(s16_t)vjlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { + struct pbuf *np, *q; + u8_t *bufptr; + +#if IP_FORWARD + /* If IP forwarding is enabled we are using a PBUF_LINK packet type so + * the packet is being allocated with enough header space to be + * forwarded (to Ethernet for example). + */ + np = pbuf_alloc(PBUF_LINK, n0->len + cs->cs_hlen, PBUF_POOL); +#else /* IP_FORWARD */ + np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); +#endif /* IP_FORWARD */ + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n")); + goto bad; + } + + if (pbuf_header(np, -(s16_t)cs->cs_hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + bufptr = (u8_t*)n0->payload; + for(q = np; q != NULL; q = q->next) { + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } + + if(n0->next) { + pbuf_chain(np, n0->next); + pbuf_dechain(n0); + } + pbuf_free(n0); + n0 = np; + } + + if (pbuf_header(n0, (s16_t)cs->cs_hlen)) { + struct pbuf *np; + + LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); + np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n")); + goto bad; + } + pbuf_cat(np, n0); + n0 = np; + } + LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); + MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); + + *nb = n0; + + return vjlen; + +bad: + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return (-1); +} + +#endif /* PPP_SUPPORT && VJ_SUPPORT */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/slipif.c b/sdk/Src/Network/lwip2.0.3/netif/slipif.c new file mode 100644 index 0000000..6df8d21 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/slipif.c @@ -0,0 +1,555 @@ +/** + * @file + * SLIP Interface + * + */ + +/* + * Copyright (c) 2001-2004 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 built upon the file: src/arch/rtxc/netif/sioslip.c + * + * Author: Magnus Ivarsson + * Simon Goldschmidt + */ + + +/** + * @defgroup slipif SLIP netif + * @ingroup addons + * + * This is an arch independent SLIP netif. The specific serial hooks must be + * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send + * + * Usage: This netif can be used in three ways:\n + * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read() + * until data is received.\n + * 2) In your main loop, call slipif_poll() to check for new RX bytes, + * completed packets are fed into netif->input().\n + * 3) Call slipif_received_byte[s]() from your serial RX ISR and + * slipif_process_rxqueue() from your main loop. ISR level decodes + * packets and puts completed packets on a queue which is fed into + * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for + * pbuf_alloc to work on ISR level!). + * + */ + +#include "netif/slipif.h" +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sys.h" +#include "lwip/sio.h" + +#define SLIP_END 0xC0 /* 0300: start and end of every packet */ +#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */ +#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */ +#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */ + +/** Maximum packet size that is received by this netif */ +#ifndef SLIP_MAX_SIZE +#define SLIP_MAX_SIZE 1500 +#endif + +/** Define this to the interface speed for SNMP + * (sio_fd is the sio_fd_t returned by sio_open). + * The default value of zero means 'unknown'. + */ +#ifndef SLIP_SIO_SPEED +#define SLIP_SIO_SPEED(sio_fd) 0 +#endif + +enum slipif_recv_state { + SLIP_RECV_NORMAL, + SLIP_RECV_ESCAPE +}; + +struct slipif_priv { + sio_fd_t sd; + /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ + struct pbuf *p, *q; + u8_t state; + u16_t i, recved; +#if SLIP_RX_FROM_ISR + struct pbuf *rxpackets; +#endif +}; + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chain packet to send + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output(struct netif *netif, struct pbuf *p) +{ + struct slipif_priv *priv; + struct pbuf *q; + u16_t i; + u8_t c; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len)); + priv = (struct slipif_priv *)netif->state; + + /* Send pbuf out on the serial I/O device. */ + /* Start with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + + for (q = p; q != NULL; q = q->next) { + for (i = 0; i < q->len; i++) { + c = ((u8_t *)q->payload)[i]; + switch (c) { + case SLIP_END: + /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_END, priv->sd); + break; + case SLIP_ESC: + /* need to escape this byte (0xDB -> 0xDB, 0xDD) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_ESC, priv->sd); + break; + default: + /* normal byte - no need for escaping */ + sio_send(c, priv->sd); + break; + } + } + } + /* End with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + return ERR_OK; +} + +#if LWIP_IPV4 +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chain packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} +#endif /* LWIP_IPV4 */ + +#if LWIP_IPV6 +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chain packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} +#endif /* LWIP_IPV6 */ + +/** + * Handle the incoming SLIP stream character by character + * + * @param netif the lwip network interface structure for this slipif + * @param c received character (multiple calls to this function will + * return a complete packet, NULL is returned before - used for polling) + * @return The IP packet when SLIP_END is received + */ +static struct pbuf* +slipif_rxbyte(struct netif *netif, u8_t c) +{ + struct slipif_priv *priv; + struct pbuf *t; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + switch (priv->state) { + case SLIP_RECV_NORMAL: + switch (c) { + case SLIP_END: + if (priv->recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(priv->q, priv->recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved)); + t = priv->q; + priv->p = priv->q = NULL; + priv->i = priv->recved = 0; + return t; + } + return NULL; + case SLIP_ESC: + priv->state = SLIP_RECV_ESCAPE; + return NULL; + default: + break; + } /* end switch (c) */ + break; + case SLIP_RECV_ESCAPE: + /* un-escape END or ESC bytes, leave other bytes + (although that would be a protocol error) */ + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + default: + break; + } + priv->state = SLIP_RECV_NORMAL; + break; + default: + break; + } /* end switch (priv->state) */ + + /* byte received, packet not yet completely received */ + if (priv->p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - PBUF_LINK_ENCAPSULATION_HLEN), PBUF_POOL); + + if (priv->p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + return NULL; + } + + if (priv->q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(priv->q, priv->p); + } else { + /* p is the first pbuf in the chain */ + priv->q = priv->p; + } + } + + /* this automatically drops bytes if > SLIP_MAX_SIZE */ + if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { + ((u8_t *)priv->p->payload)[priv->i] = c; + priv->recved++; + priv->i++; + if (priv->i >= priv->p->len) { + /* on to the next pbuf */ + priv->i = 0; + if (priv->p->next != NULL && priv->p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + priv->p = priv->p->next; + } else { + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + priv->p = NULL; + } + } + } + return NULL; +} + +/** Like slipif_rxbyte, but passes completed packets to netif->input + * + * @param netif The lwip network interface structure for this slipif + * @param c received character + */ +static void +slipif_rxbyte_input(struct netif *netif, u8_t c) +{ + struct pbuf *p; + p = slipif_rxbyte(netif, c); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } +} + +#if SLIP_USE_RX_THREAD +/** + * The SLIP input thread. + * + * Feed the IP layer with incoming packets + * + * @param nf the lwip network interface structure for this slipif + */ +static void +slipif_loop_thread(void *nf) +{ + u8_t c; + struct netif *netif = (struct netif *)nf; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + + while (1) { + if (sio_read(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } + } +} +#endif /* SLIP_USE_RX_THREAD */ + +/** + * SLIP netif initialization + * + * Call the arch specific sio_open and remember + * the opened device in the state field of the netif. + * + * @param netif the lwip network interface structure for this slipif + * @return ERR_OK if serial line could be opened, + * ERR_MEM if no memory could be allocated, + * ERR_IF is serial line couldn't be opened + * + * @note netif->num must contain the number of the serial port to open + * (0 by default). If netif->state is != NULL, it is interpreted as an + * u8_t pointer pointing to the serial port number instead of netif->num. + * + */ +err_t +slipif_init(struct netif *netif) +{ + struct slipif_priv *priv; + u8_t sio_num; + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); + + /* Allocate private data */ + priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv)); + if (!priv) { + return ERR_MEM; + } + + netif->name[0] = 's'; + netif->name[1] = 'l'; +#if LWIP_IPV4 + netif->output = slipif_output_v4; +#endif /* LWIP_IPV4 */ +#if LWIP_IPV6 + netif->output_ip6 = slipif_output_v6; +#endif /* LWIP_IPV6 */ + netif->mtu = SLIP_MAX_SIZE; + + /* netif->state or netif->num contain the port number */ + if (netif->state != NULL) { + sio_num = *(u8_t*)netif->state; + } else { + sio_num = netif->num; + } + /* Try to open the serial port. */ + priv->sd = sio_open(sio_num); + if (!priv->sd) { + /* Opening the serial port failed. */ + mem_free(priv); + return ERR_IF; + } + + /* Initialize private data */ + priv->p = NULL; + priv->q = NULL; + priv->state = SLIP_RECV_NORMAL; + priv->i = 0; + priv->recved = 0; +#if SLIP_RX_FROM_ISR + priv->rxpackets = NULL; +#endif + + netif->state = priv; + + /* initialize the snmp variables and counters inside the struct netif */ + MIB2_INIT_NETIF(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd)); + +#if SLIP_USE_RX_THREAD + /* Create a thread to poll the serial line. */ + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, + SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); +#endif /* SLIP_USE_RX_THREAD */ + return ERR_OK; +} + +/** + * Polls the serial device and feeds the IP layer with incoming packets. + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_poll(struct netif *netif) +{ + u8_t c; + struct slipif_priv *priv; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + while (sio_tryread(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } +} + +#if SLIP_RX_FROM_ISR +/** + * Feeds the IP layer with incoming packets that were receive + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_process_rxqueue(struct netif *netif) +{ + struct slipif_priv *priv; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + SYS_ARCH_PROTECT(old_level); + while (priv->rxpackets != NULL) { + struct pbuf *p = priv->rxpackets; +#if SLIP_RX_QUEUE + /* dequeue packet */ + struct pbuf *q = p; + while ((q->len != q->tot_len) && (q->next != NULL)) { + q = q->next; + } + priv->rxpackets = q->next; + q->next = NULL; +#else /* SLIP_RX_QUEUE */ + priv->rxpackets = NULL; +#endif /* SLIP_RX_QUEUE */ + SYS_ARCH_UNPROTECT(old_level); + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + SYS_ARCH_PROTECT(old_level); + } +} + +/** Like slipif_rxbyte, but queues completed packets. + * + * @param netif The lwip network interface structure for this slipif + * @param data Received serial byte + */ +static void +slipif_rxbyte_enqueue(struct netif *netif, u8_t data) +{ + struct pbuf *p; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + SYS_ARCH_DECL_PROTECT(old_level); + + p = slipif_rxbyte(netif, data); + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + if (priv->rxpackets != NULL) { +#if SLIP_RX_QUEUE + /* queue multiple pbufs */ + struct pbuf *q = p; + while (q->next != NULL) { + q = q->next; + } + q->next = p; + } else { +#else /* SLIP_RX_QUEUE */ + pbuf_free(priv->rxpackets); + } + { +#endif /* SLIP_RX_QUEUE */ + priv->rxpackets = p; + } + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Process a received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +void +slipif_received_byte(struct netif *netif, u8_t data) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + slipif_rxbyte_enqueue(netif, data); +} + +/** + * Process multiple received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + * @param len Number of received characters + */ +void +slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len) +{ + u8_t i; + u8_t *rxdata = data; + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + for (i = 0; i < len; i++, rxdata++) { + slipif_rxbyte_enqueue(netif, *rxdata); + } +} +#endif /* SLIP_RX_FROM_ISR */ diff --git a/sdk/Src/Network/lwip2.0.3/netif/wm_ethernet.c b/sdk/Src/Network/lwip2.0.3/netif/wm_ethernet.c new file mode 100644 index 0000000..9f94fd5 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/netif/wm_ethernet.c @@ -0,0 +1,477 @@ +/** + * @file + * Ethernet common functions + * + * @defgroup ethernet Ethernet + * @ingroup callbackstyle_api + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" +#include "lwip/sys.h" +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/memp.h" +#include "lwip/stats.h" +#include "lwip/dhcp.h" +#include "lwip/dns.h" +#include "lwip/netifapi.h" +#include "netif/ethernetif.h" +#include "netif/ethernet.h" +#include "wm_params.h" +#include "wm_mem.h" +#include +#if TLS_CONFIG_AP +#include "dhcp_server.h" +#include "dns_server.h" +#include "lwip/alg.h" +#include "tls_wireless.h" +#endif +#if TLS_CONFIG_SOCKET_RAW +#include "tls_netconn.h" +#endif +#if TLS_CONFIG_RMMS +#include "tls_sys.h" +#include "wm_rmms.h" +#endif +#include "wm_wifi.h" +extern int tls_wifi_get_oneshot_flag(void); +extern int tls_dhcp_get_ip_timeout_flag(void); +static struct tls_ethif *ethif = NULL; +static struct netif *nif = NULL; +static struct tls_netif_status_event netif_status_event; +static void netif_status_changed(struct netif *netif, u8_t netif_state) +{ + struct tls_netif_status_event *status_event; + + if (netif_is_up(netif)) + { + dl_list_for_each(status_event, &netif_status_event.list, struct tls_netif_status_event, list) + { + if(status_event->status_callback != NULL) + { + if (0 == 0 /*tls_wifi_get_oneshot_flag()*/) + { + if (ip_addr_isany(netif_ip4_addr(netif))) + { + if (tls_dhcp_get_ip_timeout_flag()) + { + status_event->status_callback(NETIF_WIFI_JOIN_FAILED); + } + } + else + { + if(netif_state & NETIF_STATE_IPV4) + status_event->status_callback(NETIF_IP_NET_UP); + else if(netif_state & NETIF_STATE_IPV6) + status_event->status_callback(NETIF_IPV6_NET_UP); + } + } + } + } + } +} + +#if TLS_CONFIG_AP +static struct tls_ethif *ethif2 = NULL; +static void netif_status_changed2(struct netif *netif, u8_t netif_state) +{ + struct tls_netif_status_event *status_event; + + if (netif_is_up(netif)) + { + dl_list_for_each(status_event, &netif_status_event.list, struct tls_netif_status_event, list) + { + if(status_event->status_callback) + { + if(netif_state & NETIF_STATE_IPV4) + status_event->status_callback(NETIF_IP_NET2_UP); + else if(netif_state & NETIF_STATE_IPV6) + status_event->status_callback(NETIF_IPV6_NET_UP); + } + } + } +} +#endif + +static void wifi_status_changed(u8 status) +{ + struct tls_netif_status_event *status_event; + dl_list_for_each(status_event, &netif_status_event.list, struct tls_netif_status_event, list) + { + if(status_event->status_callback != NULL) + { + switch(status) + { + case WIFI_JOIN_SUCCESS: + status_event->status_callback(NETIF_WIFI_JOIN_SUCCESS); + break; + case WIFI_JOIN_FAILED: + status_event->status_callback(NETIF_WIFI_JOIN_FAILED); + break; + case WIFI_DISCONNECTED: + status_event->status_callback(NETIF_WIFI_DISCONNECTED); + break; +#if TLS_CONFIG_AP + case WIFI_SOFTAP_SUCCESS: + status_event->status_callback(NETIF_WIFI_SOFTAP_SUCCESS); + break; + case WIFI_SOFTAP_FAILED: + status_event->status_callback(NETIF_WIFI_SOFTAP_FAILED); + break; + case WIFI_SOFTAP_CLOSED: + status_event->status_callback(NETIF_WIFI_SOFTAP_CLOSED); + break; +#endif + default: + break; + } + } + } +} + +struct netif *wm_ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src) +{ + struct netif *netif; + + for (netif = netif_list; netif != NULL; netif = netif->next) { + /* is the netif up, does it have a link and a valid address? */ + if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) { + if (NULL != src) + { + if (ip_addr_isany(src)) + { + /* broadcast? */ + if (ip_addr_isbroadcast(dest, netif)) + return netif; + } + else + { + if (ip4_addr_netcmp(src, netif_ip4_addr(netif), netif_ip4_netmask(netif))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + else + { + /* broadcast? */ + if (ip_addr_isbroadcast(dest, netif)) + return netif; + } + } + } + + return NULL; +} + +/*************************************************************************** +* Function: Tcpip_stack_init +* +* Description: This function is init ip stack. +* +* Input: +* ipaddr: +* netmask: +* gateway: +* Output: +* +* Return: +* netif: Init IP Stack OK +* NULL : Init IP Statck Fail Because no memory +* Date : 2014-6-4 +****************************************************************************/ +struct netif *Tcpip_stack_init() +{ +// err_t err; +#if TLS_CONFIG_AP + struct netif *nif4apsta = NULL; +#endif + + /* Setup lwIP. */ + tcpip_init(NULL, NULL); + +#if TLS_CONFIG_AP + /* add net info for apsta's ap */ + nif4apsta = (struct netif *)tls_mem_alloc(sizeof(struct netif)); + if (nif4apsta == NULL) + return NULL; +#endif + + /*Add Net Info to Netif, default */ + nif = (struct netif *)tls_mem_alloc(sizeof(struct netif)); + if (nif == NULL) + { +#if TLS_CONFIG_AP + tls_mem_free(nif4apsta); +#endif + return NULL; + } + +#if TLS_CONFIG_AP + memset(nif4apsta, 0, sizeof(struct netif)); + //nif->next = nif4apsta; + netifapi_netif_add(nif4apsta, IPADDR_ANY, IPADDR_ANY, IPADDR_ANY, NULL, ethernetif_init, tcpip_input); + netif_set_status_callback(nif4apsta, netif_status_changed2); +#endif + + memset(nif, 0, sizeof(struct netif)); + netifapi_netif_add(nif, IPADDR_ANY,IPADDR_ANY,IPADDR_ANY,NULL,ethernetif_init,tcpip_input); + netifapi_netif_set_default(nif); + dl_list_init(&netif_status_event.list); + netif_set_status_callback(nif, netif_status_changed); + tls_wifi_status_change_cb_register(wifi_status_changed); + + /*Register Ethernet Rx Data callback From wifi*/ + tls_ethernet_data_rx_callback(ethernetif_input); +#if TLS_CONFIG_AP_OPT_FWD + alg_napt_init(); + tls_ethernet_ip_rx_callback(alg_input); +#endif + + return nif; +} + +#ifndef TCPIP_STACK_INIT +#define TCPIP_STACK_INIT Tcpip_stack_init +#endif +int tls_ethernet_init() +{ + if(ethif) + tls_mem_free(ethif); + ethif = tls_mem_alloc(sizeof(struct tls_ethif)); + memset(ethif, 0, sizeof(struct tls_ethif)); + +#if TLS_CONFIG_AP + if(ethif2) + tls_mem_free(ethif2); + ethif2 = tls_mem_alloc(sizeof(struct tls_ethif)); + memset(ethif2, 0, sizeof(struct tls_ethif)); +#endif + TCPIP_STACK_INIT(); +#if TLS_CONFIG_SOCKET_RAW + tls_net_init(); +#endif + return 0; +} + +void tls_netif_set_status(u8 status) +{ + ethif->status = status; + + return; +} + +struct tls_ethif * tls_netif_get_ethif(void) +{ +#if TLS_CONFIG_IPV6 + int i; +#endif + ip_addr_t dns1,dns2; +// MEMCPY((char *)ðif->ip_addr.addr, &nif->ip_addr.addr, 4); + ip_addr_copy(ethif->ip_addr, nif->ip_addr); + // MEMCPY((char *)ðif->netmask.addr, &nif->netmask.addr, 4); + ip_addr_copy(ethif->netmask, nif->netmask); +// MEMCPY((char *)ðif->gw.addr, &nif->gw.addr, 4); + ip_addr_copy(ethif->gw, nif->gw); +#if TLS_CONFIG_IPV6 + for(i = 0;i < IPV6_ADDR_MAX_NUM;i ++) + { + ip_addr_copy(ethif->ip6_addr[i], nif->ip6_addr[i]); + } +#endif + + dns1 = *dns_getserver(0); + //MEMCPY(ðif->dns1.addr, (char *)&dns1.addr, 4); + ip_addr_copy(ethif->dns1, dns1); + dns2 = *dns_getserver(1); + //MEMCPY(ðif->dns2.addr, (char *)&dns2.addr, 4); + ip_addr_copy(ethif->dns2, dns2); + //ethif->status = nif->flags&NETIF_FLAG_UP; +#if TLS_CONFIG_IPV6 + for(i = 0; i < IPV6_ADDR_MAX_NUM; i++) + { + ethif->ipv6_status[i] = (nif->ip6_addr_state[i] == IP6_ADDR_PREFERRED ? 1 : 0); + } +#endif + return ethif; +} + +err_t tls_dhcp_start(void) +{ +#if 0 + if (nif->flags & NETIF_FLAG_UP) + nif->flags &= ~NETIF_FLAG_UP; +#endif + + return netifapi_dhcp_start(nif); +} + +err_t tls_dhcp_stop(void) +{ + return netifapi_dhcp_stop(nif); +} + +err_t tls_netif_set_addr(ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw) +{ + return netifapi_netif_set_addr(nif, (ip4_addr_t *)ipaddr, (ip4_addr_t *)netmask, (ip4_addr_t *)gw); +} +void tls_netif_dns_setserver(u8 numdns, ip_addr_t *dnsserver) +{ + dns_setserver(numdns, dnsserver); +} +err_t tls_netif_set_up(void) +{ + netif_set_link_up(nif); + return netifapi_netif_set_up(nif); +} +err_t tls_netif_set_down(void) +{ + return netifapi_netif_set_down(nif); +} +err_t tls_netif_add_status_event(tls_netif_status_event_fn event_fn) +{ + u32 cpu_sr; + struct tls_netif_status_event *evt; + //if exist, remove from event list first. + tls_netif_remove_status_event(event_fn); + evt = tls_mem_alloc(sizeof(struct tls_netif_status_event)); + if(evt==NULL) + return -1; + memset(evt, 0, sizeof(struct tls_netif_status_event)); + evt->status_callback = event_fn; + cpu_sr = tls_os_set_critical(); + dl_list_add_tail(&netif_status_event.list, &evt->list); + tls_os_release_critical(cpu_sr); + + return 0; +} +err_t tls_netif_remove_status_event(tls_netif_status_event_fn event_fn) +{ + struct tls_netif_status_event *status_event; + bool is_exist = FALSE; + u32 cpu_sr; + if(dl_list_empty(&netif_status_event.list)) + return 0; + dl_list_for_each(status_event, &netif_status_event.list, struct tls_netif_status_event, list) + { + if(status_event->status_callback == event_fn) + { + is_exist = TRUE; + break; + } + } + if(is_exist) + { + cpu_sr = tls_os_set_critical(); + dl_list_del(&status_event->list); + tls_os_release_critical(cpu_sr); + tls_mem_free(status_event); + } + return 0; +} + +#if TLS_CONFIG_RMMS +INT8S tls_rmms_start(void) +{ + return RMMS_Init(nif); +} +void tls_rmms_stop(void) +{ + RMMS_Fini(); +} +#endif + +#if TLS_CONFIG_AP +INT8S tls_dhcps_start(void) +{ + return DHCPS_Start(nif->next); +} +void tls_dhcps_stop(void) +{ + DHCPS_Stop(); +} + +INT8S tls_dnss_start(INT8U * DnsName) +{ + return DNSS_Start(nif->next, DnsName); +} +void tls_dnss_stop(void) +{ + DNSS_Stop(); +} +ip_addr_t *tls_dhcps_getip(const u8_t *mac) +{ + return DHCPS_GetIpByMac(mac); +} +u8 *tls_dhcps_getmac(const ip_addr_t *ip) +{ + return DHCPS_GetMacByIp(ip); +} +#endif + +#if TLS_CONFIG_AP +err_t tls_netif2_set_up(void) +{ + netif_set_link_up(nif->next); + return netifapi_netif_set_up(nif->next); +} +err_t tls_netif2_set_down(void) +{ + return netifapi_netif_set_down(nif->next); +} +err_t tls_netif2_set_addr(ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw) +{ + return netifapi_netif_set_addr(nif->next, (ip4_addr_t *)ipaddr, (ip4_addr_t *)netmask, (ip4_addr_t *)gw); +} +/* numdns 0/1 --> dns 1/2 */ +void tls_dhcps_setdns(u8_t numdns) +{ + const ip_addr_t* dns; + dns = dns_getserver(numdns); + DHCPS_SetDns(numdns, ip_addr_get_ip4_u32(dns)); + + return; +} +#endif + +struct netif *tls_get_netif(void) +{ + return nif; +} + diff --git a/sdk/Src/Network/lwip2.0.3/sys_arch.c b/sdk/Src/Network/lwip2.0.3/sys_arch.c new file mode 100644 index 0000000..b13b905 --- /dev/null +++ b/sdk/Src/Network/lwip2.0.3/sys_arch.c @@ -0,0 +1,439 @@ +#include "lwip/debug.h" +#include "lwip/sys.h" +#include "lwip/opt.h" +#include "lwip/stats.h" +#include "wm_wl_task.h" +#include "arch/sys_arch.h" +#include "wm_osal.h" +#include "wm_mem.h" +#include "wm_debug.h" +#include "wm_config.h" +#include "wm_socket.h" + +const void * const null_pointer = (void *)0; +OS_STK lwip_task_stk[LWIP_TASK_MAX*LWIP_STK_SIZE]; +u8_t lwip_task_priopity_stack[LWIP_TASK_MAX]; +//OS_TCB lwip_task_tcb[LWIP_TASK_MAX]; + + + +/** + * \brief Initialize the sys_arch layer. + */ +void sys_init(void) +{ + +} + +u32_t sys_now(void) +{ + return tls_os_get_time()*1000/HZ; +} + +/** + * \brief Creates and returns a new semaphore. + * + * \param sem Pointer to the semaphore. + * \param count Initial state of the semaphore. + * + * \return ERR_OK for OK, other value indicates error. + */ +err_t sys_sem_new(sys_sem_t *sem, u8_t count) +{ + err_t err_sem = ERR_MEM; + tls_os_status_t status; + + /* Sanity check */ + if (sem != NULL) { + + status = tls_os_sem_create(sem, count); + if (status == TLS_OS_SUCCESS) { + #if SYS_STATS + lwip_stats.sys.sem.used++; + if (lwip_stats.sys.sem.used > lwip_stats.sys.sem.max) { + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; + } + + #endif /* SYS_STATS */ + err_sem = ERR_OK; + } + + } + + return err_sem; +} + +/** + * \brief Frees a semaphore created by sys_sem_new. + * + * \param sem Pointer to the semaphore. + */ +void sys_sem_free(sys_sem_t *sem) +{ + /* Sanity check */ + if (*sem != NULL) + tls_os_sem_delete(*sem); + + *sem = SYS_SEM_NULL; +} + +/** + * \brief Signals (or releases) a semaphore. + * + * \param sem Pointer to the semaphore. + */ +void sys_sem_signal(sys_sem_t *sem) +{ + /* Sanity check */ + if (*sem != NULL) { + tls_os_sem_release(*sem); + } +} + +/** + * \brief Blocks the thread while waiting for the semaphore to be signaled. + * Note that there is another function sys_sem_wait in sys.c, but it is a wrapper + * for the sys_arch_sem_wait function. Please note that it is important for the + * semaphores to return an accurate count of elapsed milliseconds, since they are + * used to schedule timers in lwIP. + * + * \param sem Pointer to the semaphore. + * \param timeout The timeout parameter specifies how many milliseconds the + * function should block before returning; if the function times out, it should + * return SYS_ARCH_TIMEOUT. If timeout=0, then the function should block + * indefinitely. If the function acquires the semaphore, it should return how + * many milliseconds expired while waiting for the semaphore. + * + * \return SYS_ARCH_TIMEOUT if times out, ERR_MEM for semaphore erro otherwise + * return the milliseconds expired while waiting for the semaphore. + */ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) +{ + tls_os_status_t status; + u32_t os_timeout = 0; + u32_t in_timeout = (timeout*HZ)/1000; + + if(timeout && in_timeout == 0) + in_timeout = 1; + status = tls_os_sem_acquire(*sem, in_timeout); +#if TLS_OS_FREERTOS + if(status == TLS_OS_ERROR) + os_timeout = SYS_ARCH_TIMEOUT; +#endif + return os_timeout; +} + +#ifndef sys_sem_valid +/** + * \brief Check if a sempahore is valid/allocated. + * + * \param sem Pointer to the semaphore. + * + * \return Semaphore number on valid, 0 for invalid. + */ +int sys_sem_valid(sys_sem_t *sem) +{ + if(*sem == SYS_SEM_NULL) + return 0; + return 1; +} + +#endif + +#ifndef sys_sem_set_invalid +/** + * \brief Set a semaphore invalid. + * + * \param sem Pointer to the semaphore. + */ +void sys_sem_set_invalid(sys_sem_t *sem) +{ +#if 1 + *sem = SYS_SEM_NULL; +#endif +} +#endif + +/** + * \brief Creates an empty mailbox for maximum "size" elements. Elements stored + * in mailboxes are pointers. + * + * \param mBoxNew Pointer to the new mailbox. + * \param size Maximum "size" elements. + * + * \return ERR_OK if successfull or ERR_MEM on error. + */ +err_t sys_mbox_new(sys_mbox_t *mbox, int size) +{ +// void *msg; + err_t err; + tls_os_status_t status; + + if (size == 0) + size = 10; + + status = tls_os_queue_create(mbox,size); + if (status == TLS_OS_SUCCESS) { + err = ERR_OK; + } + else + err = ERR_RTE; + +#if SYS_STATS + if (SYS_MBOX_NULL != *mbox) { + lwip_stats.sys.mbox.used++; + if (lwip_stats.sys.mbox.used > lwip_stats.sys.mbox.max) { + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + } + } +#endif /* SYS_STATS */ + return err; +} + +/** + * \brief Deallocates a mailbox. + * If there are messages still present in the mailbox when the mailbox is + * deallocated, it is an indication of a programming error in lwIP and the + * developer should be notified. + * + * \param mbox Pointer to the new mailbox. + */ +void sys_mbox_free(sys_mbox_t *mbox) +{ + // u32_t cpu_sr; + LWIP_ASSERT( "sys_mbox_free ", *mbox != SYS_MBOX_NULL ); + + tls_os_queue_flush(*mbox); + //cpu_sr = tls_os_set_critical(); + int __attribute__((unused))err = tls_os_queue_delete(*mbox); + //tls_os_release_critical(cpu_sr); + LWIP_ASSERT("OSQDel ", err == TLS_OS_SUCCESS); + /* Sanity check */ +} + +/** + * \brief Posts the "msg" to the mailbox. This function have to block until the + * "msg" is really posted. + * + * \param mbox Pointer to the mailbox. + * \param msg Pointer to the message to be post. + */ +void sys_mbox_post(sys_mbox_t *mbox, void *msg) +{ + u8_t err; + u8_t i=0; + + if( msg == NULL ) + msg = (void*)null_pointer; + /* try 10 times */ + while (i < 10){ + //err = OSQPost(mbox, msg); + err = tls_os_queue_send(*mbox, msg, 0); + if(err == TLS_OS_SUCCESS) + break; + i++; + //OSTimeDly(5); + tls_os_time_delay(1); + } + LWIP_ASSERT( "sys_mbox_post error!\n", i !=10 ); +} + +/** + * \brief Try to posts the "msg" to the mailbox. + * + * \param mbox Pointer to the mailbox. + * \param msg Pointer to the message to be post. + * + * \return ERR_MEM if the mailbox is full otherwise ERR_OK if the "msg" is posted. + */ +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) +{ + u8_t err; + u8_t i=0; + + if(msg == NULL ) + msg = (void*)null_pointer; + + /* try 10 times */ + while (i < 10){ + // err = OSQPost(mbox, msg); + err = tls_os_queue_send(*mbox, msg, 0); + if(err == TLS_OS_SUCCESS) + return ERR_OK; + i++; + //OSTimeDly(5); + tls_os_time_delay(1); + } + + return ERR_ABRT; +} + +/** + * \brief Blocks the thread until a message arrives in the mailbox, but does not + * block the thread longer than "timeout" milliseconds (similar to the + * sys_arch_sem_wait() function). + * + * \param mbox Pointer to the mailbox. + * \param msg A result parameter that is set by the function (i.e., by doing + * "*msg = ptr"). The "msg" parameter maybe NULL to indicate that the message + * should be dropped. + * \timeout 0 indicates the thread should be blocked until a message arrives. + * + * \return Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was + * a timeout. Or ERR_MEM if invalid pointer to message box. + */ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) +{ + u8_t err; + u32_t in_timeout = timeout * HZ / 1000; + u32_t tick_start, tick_stop, tick_elapsed; + + if(timeout && in_timeout == 0) + in_timeout = 1; + + tick_start = tls_os_get_time(); +// *msg = OSQPend(mbox, in_timeout, &err); + err = tls_os_queue_receive(*mbox,msg,0,in_timeout); +// if (err == OS_ERR_TIMEOUT ) { + if (err != TLS_OS_SUCCESS ) { + + return SYS_ARCH_TIMEOUT; + } + tick_stop = tls_os_get_time(); + + // Take care of wrap-around. + if( tick_stop >= tick_start ) + tick_elapsed = tick_stop - tick_start; + else + tick_elapsed = 0xFFFFFFFF - tick_start + tick_stop ; + + + return tick_elapsed * 1000/HZ; +} + +#ifndef sys_mbox_valid +/** + * \brief Check if an mbox is valid/allocated. + * + * \param mbox Pointer to the mailbox. + * + * \return Mailbox for valid, 0 for invalid. + */ +int sys_mbox_valid(sys_mbox_t *mbox) +{ + if (*mbox == NULL) + return 0; + else + return 1; +} +#endif + +#ifndef sys_mbox_set_invalid +/** + * \brief Set an mbox invalid. + * + * \param mbox Pointer to the mailbox. + */ +void sys_mbox_set_invalid(sys_mbox_t *mbox) +{ +#if 1 + *mbox = SYS_MBOX_NULL; +#endif +} + +#endif +/** + * \brief Instantiate a thread for lwIP. Both the id and the priority are + * system dependent. + * + * \param name Pointer to the thread name. + * \param thread Thread function. + * \param arg Argument will be passed into the thread(). + * \param stacksize Stack size of the thread. + * \param prio Thread priority. + * + * \return The id of the new thread. + */ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, + int stacksize, int prio) +{ + u8_t ubPrio = LWIP_TASK_START_PRIO; + int i; + + if (prio) { + ubPrio += (prio-1); + for (i=0; i= (LWIP_TASK_START_PRIO+LWIP_TASK_MAX) || i == LWIP_TASK_MAX) { + LWIP_ASSERT( "sys_thread_new: there is no free priority", 0 ); + return (1); + } + } + if (stacksize > LWIP_STK_SIZE || !stacksize) + stacksize = LWIP_STK_SIZE; + + int tsk_prio = ubPrio-LWIP_TASK_START_PRIO; + OS_STK * task_stk = &lwip_task_stk[tsk_prio*LWIP_STK_SIZE]; + + tls_os_task_create(NULL, "LWIP", + thread, + (void *)arg, + (void *)task_stk, + stacksize * sizeof(u32), + ubPrio, + 0); + + return ubPrio; +} + +/** + * \brief Protect the system. + * + * \return 1 on success. + */ +sys_prot_t sys_arch_protect(void) +{ + return tls_os_set_critical(); /* Not used */ +} + +/** + * \brief Unprotect the system. + * + * \param pval Protect value. + */ +void sys_arch_unprotect(sys_prot_t pval) +{ + tls_os_release_critical(pval); +} diff --git a/sdk/Src/OS/RTOS/CMakeLists.txt b/sdk/Src/OS/RTOS/CMakeLists.txt new file mode 100644 index 0000000..556be73 --- /dev/null +++ b/sdk/Src/OS/RTOS/CMakeLists.txt @@ -0,0 +1,51 @@ +# THE VERSION NUMBER +SET(CMAKE_SYSTEM_NAME Generic) +CMAKE_MINIMUM_REQUIRED (VERSION 3.10) +PROJECT(WMRTOS) + +SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) +SET(CMAKE_NINJA_FORCE_RESPONSE_FILE 1 CACHE INTERNAL "") + +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# ENABLE ASM +ENABLE_LANGUAGE(ASM) + +SET(CMAKE_STATIC_LIBRARY_PREFIX) +SET(CMAKE_STATIC_LIBRARY_SUFFIX) + +SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX) +SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX) + +# C FLAGS +SET(CMAKE_C_FLAGS " -mthumb --function-sections --data-sections -mcpu=cortex-m3 -std=gnu99 -mabi=aapcs -march=armv7-m -fno-builtin ") +STRING(APPEND CMAKE_C_FLAGS "-DGCC_COMPILE=1 -DWOLFSSL_USER_SETTINGS -D_IN_ADDR_T_DECLARED -D__MACHINE_ENDIAN_H__ -D_TIMEVAL_DEFINED -D__INSIDE_CYGWIN_NET__ ") + +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -gdwarf-2 -Wall -DDEBUG ") +SET(CMAKE_C_FLAGS_RELEASE "-Os -Wall") + + +# DEBUG ASM FLAGS +SET(CMAKE_ASM_FLAGS_DEBUG "${CMAKE_ASM_FLAGS_DEBUG} -O0 -g3 -gdwarf-2 -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin -DDEBUG ") +SET(CMAKE_ASM_FLAGS_RELEASE "${CMAKE_ASM_FLAGS_RELEASE} -Os -Wall -mthumb-interwork -mthumb -std=gnu99 -mcpu=cortex-m3 -march=armv7-m -mabi=aapcs -fno-builtin ") + +# Library sources +SET(LIBRARY_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/source/croutine.c" + "${CMAKE_CURRENT_SOURCE_DIR}/source/heap_2.c" + "${CMAKE_CURRENT_SOURCE_DIR}/source/heap_3.c" + "${CMAKE_CURRENT_SOURCE_DIR}/source/heap_4.c" + "${CMAKE_CURRENT_SOURCE_DIR}/source/list.c" + "${CMAKE_CURRENT_SOURCE_DIR}/source/queue.c" + "${CMAKE_CURRENT_SOURCE_DIR}/source/rtostimers.c" + "${CMAKE_CURRENT_SOURCE_DIR}/source/tasks.c" + "${CMAKE_CURRENT_SOURCE_DIR}/wm_osal_rtos.c" + "${CMAKE_CURRENT_SOURCE_DIR}/ports/port_m3_gcc.c" +) + +function(ADD_LIB_TARGET LIBTARGET) + add_library(${LIBTARGET} OBJECT ${LIBRARY_SOURCES}) + # ADD_CUSTOM_COMMAND(TARGET ${LIBTARGET} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${EXECUTABLE_OUTPUT_PATH}/${LIBTARGET} ${CMAKE_CURRENT_SOURCE_DIR}/../../../Lib/GNU/${LIBTARGET}) +endfunction() + +ADD_LIB_TARGET(wmrtos.a) \ No newline at end of file diff --git a/sdk/Src/OS/RTOS/include/FreeRTOS.h b/sdk/Src/OS/RTOS/include/FreeRTOS.h new file mode 100644 index 0000000..0307c98 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/FreeRTOS.h @@ -0,0 +1,481 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef INC_FREERTOS_H +#define INC_FREERTOS_H + + +/* + * Include the generic headers required for the FreeRTOS port being used. + */ +#include + +/* Basic FreeRTOS definitions. */ +#include "projdefs.h" + +/* Application specific configuration options. */ +#include "FreeRTOSConfig.h" + +/* Definitions specific to the port being used. */ +#include "portable.h" + + +/* Defines the prototype to which the application task hook function must +conform. */ +typedef portBASE_TYPE (*pdTASK_HOOK_CODE)( void * ); + + +extern int ulSetInterruptMaskFromISR( void ); +extern void vClearInterruptMaskFromISR( int ulMask ); + + +/* + * Check all the required application specific macros have been defined. + * These macros are application specific and (as downloaded) are defined + * within FreeRTOSConfig.h. + */ + +#ifndef configUSE_PREEMPTION + #error Missing definition: configUSE_PREEMPTION should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_IDLE_HOOK + #error Missing definition: configUSE_IDLE_HOOK should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_TICK_HOOK + #error Missing definition: configUSE_TICK_HOOK should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_CO_ROUTINES + #error Missing definition: configUSE_CO_ROUTINES should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskPrioritySet + #error Missing definition: INCLUDE_vTaskPrioritySet should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_uxTaskPriorityGet + #error Missing definition: INCLUDE_uxTaskPriorityGet should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelete + #error Missing definition: INCLUDE_vTaskDelete should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskSuspend + #error Missing definition: INCLUDE_vTaskSuspend should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelayUntil + #error Missing definition: INCLUDE_vTaskDelayUntil should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelay + #error Missing definition: INCLUDE_vTaskDelay should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_16_BIT_TICKS + #error Missing definition: configUSE_16_BIT_TICKS should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_xTaskGetIdleTaskHandle + #define INCLUDE_xTaskGetIdleTaskHandle 1 +#endif + +#ifndef INCLUDE_xTimerGetTimerDaemonTaskHandle + #define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 +#endif + +#ifndef INCLUDE_pcTaskGetTaskName + #define INCLUDE_pcTaskGetTaskName 0 +#endif + +#ifndef configUSE_APPLICATION_TASK_TAG + #define configUSE_APPLICATION_TASK_TAG 0 +#endif + +#ifndef INCLUDE_uxTaskGetStackHighWaterMark + #define INCLUDE_uxTaskGetStackHighWaterMark 1 +#endif + +#ifndef configUSE_RECURSIVE_MUTEXES + #define configUSE_RECURSIVE_MUTEXES 0 +#endif + +#ifndef configUSE_MUTEXES + #define configUSE_MUTEXES 0 +#endif + +#ifndef configUSE_TIMERS + #define configUSE_TIMERS 1 +#endif + +#ifndef configUSE_COUNTING_SEMAPHORES + #define configUSE_COUNTING_SEMAPHORES 1 +#endif + +#ifndef configUSE_MAILBOX + #define configUSE_MAILBOX 1 +#endif + +#ifndef configUSE_ALTERNATIVE_API + #define configUSE_ALTERNATIVE_API 0 +#endif + +#ifndef portCRITICAL_NESTING_IN_TCB + #define portCRITICAL_NESTING_IN_TCB 0 +#endif + +#ifndef configMAX_TASK_NAME_LEN + #define configMAX_TASK_NAME_LEN 16 +#endif + +#ifndef configIDLE_SHOULD_YIELD + #define configIDLE_SHOULD_YIELD 1 +#endif + +#if configMAX_TASK_NAME_LEN < 1 + #error configMAX_TASK_NAME_LEN must be set to a minimum of 1 in FreeRTOSConfig.h +#endif + +#ifndef INCLUDE_xTaskResumeFromISR + #define INCLUDE_xTaskResumeFromISR 1 +#endif + +#ifndef configASSERT + #define configASSERT( x ) +#endif + +/* The timers module relies on xTaskGetSchedulerState(). */ +#if configUSE_TIMERS == 1 + + #ifndef configTIMER_TASK_PRIORITY + #define configTIMER_TASK_PRIORITY 1 + // #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_PRIORITY must also be defined. + #endif /* configTIMER_TASK_PRIORITY */ + + #ifndef configTIMER_QUEUE_LENGTH + //#error If configUSE_TIMERS is set to 1 then configTIMER_QUEUE_LENGTH must also be defined. + #define configTIMER_QUEUE_LENGTH 100 + #endif /* configTIMER_QUEUE_LENGTH */ + + #ifndef configTIMER_TASK_STACK_DEPTH + #define configTIMER_TASK_STACK_DEPTH 200 + //#error If configUSE_TIMERS is set to 1 then configTIMER_TASK_STACK_DEPTH must also be defined. + #endif /* configTIMER_TASK_STACK_DEPTH */ + +#endif /* configUSE_TIMERS */ + +#ifndef INCLUDE_xTaskGetSchedulerState + #define INCLUDE_xTaskGetSchedulerState 0 +#endif + +#ifndef INCLUDE_xTaskGetCurrentTaskHandle + #define INCLUDE_xTaskGetCurrentTaskHandle 0 +#endif + + +#ifndef portSET_INTERRUPT_MASK_FROM_ISR + #define portSET_INTERRUPT_MASK_FROM_ISR() ulSetInterruptMaskFromISR() +#endif + +#ifndef portCLEAR_INTERRUPT_MASK_FROM_ISR + #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) vClearInterruptMaskFromISR( uxSavedStatusValue ) +#endif + + +#ifndef configQUEUE_REGISTRY_SIZE + #define configQUEUE_REGISTRY_SIZE 0U +#endif + +#if ( configQUEUE_REGISTRY_SIZE < 1 ) + #define vQueueAddToRegistry( xQueue, pcName ) + #define vQueueUnregisterQueue( xQueue ) +#endif + +#ifndef portPOINTER_SIZE_TYPE + #define portPOINTER_SIZE_TYPE unsigned long +#endif + +/* Remove any unused trace macros. */ +#ifndef traceSTART + /* Used to perform any necessary initialisation - for example, open a file + into which trace is to be written. */ + #define traceSTART() +#endif + +#ifndef traceEND + /* Use to close a trace, for example close a file into which trace has been + written. */ + #define traceEND() +#endif + +#ifndef traceTASK_SWITCHED_IN + /* Called after a task has been selected to run. pxCurrentTCB holds a pointer + to the task control block of the selected task. */ + #define traceTASK_SWITCHED_IN() +#endif + +#ifndef traceTASK_SWITCHED_OUT + /* Called before a task has been selected to run. pxCurrentTCB holds a pointer + to the task control block of the task being switched out. */ + #define traceTASK_SWITCHED_OUT() +#endif + +#ifndef traceBLOCKING_ON_QUEUE_RECEIVE + /* Task is about to block because it cannot read from a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the read was attempted. pxCurrentTCB points to the TCB of the + task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_SEND + /* Task is about to block because it cannot write to a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the write was attempted. pxCurrentTCB points to the TCB of the + task that attempted the write. */ + #define traceBLOCKING_ON_QUEUE_SEND( pxQueue ) +#endif + +#ifndef configCHECK_FOR_STACK_OVERFLOW + #define configCHECK_FOR_STACK_OVERFLOW 2 +#endif + +/* The following event macros are embedded in the kernel API calls. */ + +#ifndef traceQUEUE_CREATE + #define traceQUEUE_CREATE( pxNewQueue ) +#endif + +#ifndef traceQUEUE_CREATE_FAILED + #define traceQUEUE_CREATE_FAILED() +#endif + +#ifndef traceCREATE_MUTEX + #define traceCREATE_MUTEX( pxNewQueue ) +#endif + +#ifndef traceCREATE_MUTEX_FAILED + #define traceCREATE_MUTEX_FAILED() +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE + #define traceGIVE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE_FAILED + #define traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE + #define traceTAKE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE_FAILED + #define traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE + #define traceCREATE_COUNTING_SEMAPHORE() +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE_FAILED + #define traceCREATE_COUNTING_SEMAPHORE_FAILED() +#endif + +#ifndef traceQUEUE_SEND + #define traceQUEUE_SEND( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FAILED + #define traceQUEUE_SEND_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE + #define traceQUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK + #define traceQUEUE_PEEK( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FAILED + #define traceQUEUE_RECEIVE_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR + #define traceQUEUE_SEND_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR_FAILED + #define traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR + #define traceQUEUE_RECEIVE_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR_FAILED + #define traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_DELETE + #define traceQUEUE_DELETE( pxQueue ) +#endif + +#ifndef traceTASK_CREATE + #define traceTASK_CREATE( pxNewTCB ) +#endif + +#ifndef traceTASK_CREATE_FAILED + #define traceTASK_CREATE_FAILED() +#endif + +#ifndef traceTASK_DELETE + #define traceTASK_DELETE( pxTaskToDelete ) +#endif + +#ifndef traceTASK_DELAY_UNTIL + #define traceTASK_DELAY_UNTIL() +#endif + +#ifndef traceTASK_DELAY + #define traceTASK_DELAY() +#endif + +#ifndef traceTASK_PRIORITY_SET + #define traceTASK_PRIORITY_SET( pxTask, uxNewPriority ) +#endif + +#ifndef traceTASK_SUSPEND + #define traceTASK_SUSPEND( pxTaskToSuspend ) +#endif + +#ifndef traceTASK_RESUME + #define traceTASK_RESUME( pxTaskToResume ) +#endif + +#ifndef traceTASK_RESUME_FROM_ISR + #define traceTASK_RESUME_FROM_ISR( pxTaskToResume ) +#endif + +#ifndef traceTASK_INCREMENT_TICK + #define traceTASK_INCREMENT_TICK( xTickCount ) +#endif + +#ifndef traceTIMER_CREATE + #define traceTIMER_CREATE( pxNewTimer ) +#endif + +#ifndef traceTIMER_CREATE_FAILED + #define traceTIMER_CREATE_FAILED() +#endif + +#ifndef traceTIMER_COMMAND_SEND + #define traceTIMER_COMMAND_SEND( xTimer, xMessageID, xMessageValueValue, xReturn ) +#endif + +#ifndef traceTIMER_EXPIRED + #define traceTIMER_EXPIRED( pxTimer ) +#endif + +#ifndef traceTIMER_COMMAND_RECEIVED + #define traceTIMER_COMMAND_RECEIVED( pxTimer, xMessageID, xMessageValue ) +#endif + +#ifndef configGENERATE_RUN_TIME_STATS + #define configGENERATE_RUN_TIME_STATS 0 +#endif + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #error If configGENERATE_RUN_TIME_STATS is defined then portCONFIGURE_TIMER_FOR_RUN_TIME_STATS must also be defined. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS should call a port layer function to setup a peripheral timer/counter that can then be used as the run time counter time base. + #endif /* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS */ + + #ifndef portGET_RUN_TIME_COUNTER_VALUE + #ifndef portALT_GET_RUN_TIME_COUNTER_VALUE + #error If configGENERATE_RUN_TIME_STATS is defined then either portGET_RUN_TIME_COUNTER_VALUE or portALT_GET_RUN_TIME_COUNTER_VALUE must also be defined. See the examples provided and the FreeRTOS web site for more information. + #endif /* portALT_GET_RUN_TIME_COUNTER_VALUE */ + #endif /* portGET_RUN_TIME_COUNTER_VALUE */ + +#endif /* configGENERATE_RUN_TIME_STATS */ + +#ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#endif + +#ifndef configUSE_MALLOC_FAILED_HOOK + #define configUSE_MALLOC_FAILED_HOOK 0 +#endif + +#ifndef portPRIVILEGE_BIT + #define portPRIVILEGE_BIT ( ( unsigned portBASE_TYPE ) 0x00 ) +#endif + +#ifndef portYIELD_WITHIN_API + #define portYIELD_WITHIN_API portYIELD +#endif + +#ifndef pvPortMallocAligned + #define pvPortMallocAligned( x, puxStackBuffer ) ( ( ( puxStackBuffer ) == NULL ) ? ( pvPortMalloc( ( x ) ) ) : ( puxStackBuffer ) ) +#endif + +#ifndef vPortFreeAligned + #define vPortFreeAligned( pvBlockToFree ) if (xPortMemIsKernel(pvBlockToFree)) vPortFree( pvBlockToFree ) +#endif + +#endif /* INC_FREERTOS_H */ + diff --git a/sdk/Src/OS/RTOS/include/FreeRTOSConfig.h b/sdk/Src/OS/RTOS/include/FreeRTOSConfig.h new file mode 100644 index 0000000..fdc3d29 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/FreeRTOSConfig.h @@ -0,0 +1,118 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + + + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +#define configUSE_PREEMPTION 1 //使用抢先å¼�内核 +#define configUSE_IDLE_HOOK 1 //使用空闲钩å­� +#define configUSE_TICK_HOOK 0 //ä¸�使用时间片钩å­� + +//#define configCPU_CLOCK_HZ ( ( unsigned long ) 80000000 ) //内部处ç�†å™¨æ‰§è¡Œé¢‘率,没有使用 + +#define configTICK_RATE_HZ ( ( portTickType ) 500u ) //时间片中断的频率 +#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 63) //应用程åº�中å�¯ç”¨ä¼˜å…ˆçº§çš„æ•°ç›® +#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 90 ) //空闲任务使用的堆栈大å°� +#define configTOTAL_HEAP_SIZE ( ( size_t ) 20 * 1024 ) //内核中å�¯ç”¨çš„RAMæ•°é‡�,heap2使用 +#define configMAX_TASK_NAME_LEN ( 8 ) //创建任务å��称最大å…�许长度 +#define configUSE_TRACE_FACILITY 1 //是å�¦ä½¿ç”¨å�¯è§†åŒ–追踪 +#define configUSE_16_BIT_TICKS 0 //定义portTickType +#define configIDLE_SHOULD_YIELD 1 //讲阻止空闲任务让出时间直到他的时间片用完 +#define configUSE_HEAP2 0 +#define configUSE_HEAP3 0 +#define configUSE_HEAP4 1 +#define configUSE_MUTEXES 1 + +#define portBYTE_ALIGNMENT 8 +// #define portBYTE_ALIGNMENT_MASK ( portBYTE_ALIGNMENT - 1 ) + +#define configFREERTOS_HEAP 4 + +#define configQUEUE_REGISTRY_SIZE 0 +#define configSEMAPHORE_INIT_VALUE 5 //创建信å�·é‡�的最大计数值 + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 //ä¸�使用å�ˆä½œè½®è½¬å¼�程åº� +#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) //å�ˆä½œå¼�应用程åº�中å�¯ç”¨çš„优先级数目 + +#define configGENERATE_RUN_TIME_STATS 0 + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ + +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 0 +#define INCLUDE_vTaskDelay 1 + +#define configUSE_MUTEXES 1 //使用互斥é”� +#define INCLUDE_xTaskGetCurrentTaskHandle 1 //å�¯ä»¥èŽ·å�–当å‰�任务 + +#endif /* FREERTOS_CONFIG_H */ diff --git a/sdk/Src/OS/RTOS/include/StackMacros.h b/sdk/Src/OS/RTOS/include/StackMacros.h new file mode 100644 index 0000000..46d7f97 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/StackMacros.h @@ -0,0 +1,168 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef STACK_MACROS_H +#define STACK_MACROS_H + +/* + * Call the stack overflow hook function if the stack of the task being swapped + * out is currently overflowed, or looks like it might have overflowed in the + * past. + * + * Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check + * the current stack state only - comparing the current top of stack value to + * the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1 + * will also cause the last few stack bytes to be checked to ensure the value + * to which the bytes were set when the task was created have not been + * overwritten. Note this second test does not guarantee that an overflowed + * stack will always be recognised. + */ + +/*-----------------------------------------------------------*/ + +#if( configCHECK_FOR_STACK_OVERFLOW == 0 ) + + /* FreeRTOSConfig.h is not set to check for stack overflows. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 0 */ +/*-----------------------------------------------------------*/ + +#if( configCHECK_FOR_STACK_OVERFLOW == 1 ) + + /* FreeRTOSConfig.h is only set to use the first method of + overflow checking. */ + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() + +#endif +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 0 ) && ( portSTACK_GROWTH < 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() \ + { \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ + { \ + vApplicationStackOverflowHook( ( xTaskHandle ) pxCurrentTCB, pxCurrentTCB->pcTaskName, pxCurrentTCB->uxPriority); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW > 0 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 0 ) && ( portSTACK_GROWTH > 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() \ + { \ + \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ + { \ + vApplicationStackOverflowHook( ( xTaskHandle ) pxCurrentTCB, pxCurrentTCB->pcTaskName,pxCurrentTCB->uxPriority ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) + + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() \ + { \ + static const unsigned char ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pxCurrentTCB->pxStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( xTaskHandle ) pxCurrentTCB, pxCurrentTCB->pcTaskName,pxCurrentTCB->uxPriority ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) + + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() \ + { \ + char *pcEndOfStack = ( char * ) pxCurrentTCB->pxEndOfStack; \ + static const unsigned char ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( xTaskHandle ) pxCurrentTCB, pxCurrentTCB->pcTaskName,pxCurrentTCB->uxPriority ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#endif /* STACK_MACROS_H */ + diff --git a/sdk/Src/OS/RTOS/include/croutine.h b/sdk/Src/OS/RTOS/include/croutine.h new file mode 100644 index 0000000..6e9fcdb --- /dev/null +++ b/sdk/Src/OS/RTOS/include/croutine.h @@ -0,0 +1,746 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef CO_ROUTINE_H +#define CO_ROUTINE_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include croutine.h" +#endif + +#include "rtoslist.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Used to hide the implementation of the co-routine control block. The +control block structure however has to be included in the header due to +the macro implementation of the co-routine functionality. */ +typedef void * xCoRoutineHandle; + +/* Defines the prototype to which co-routine functions must conform. */ +typedef void (*crCOROUTINE_CODE)( xCoRoutineHandle, unsigned portBASE_TYPE ); + +typedef struct corCoRoutineControlBlock +{ + crCOROUTINE_CODE pxCoRoutineFunction; + xListItem xGenericListItem; /*< List item used to place the CRCB in ready and blocked queues. */ + xListItem xEventListItem; /*< List item used to place the CRCB in event lists. */ + unsigned portBASE_TYPE uxPriority; /*< The priority of the co-routine in relation to other co-routines. */ + unsigned portBASE_TYPE uxIndex; /*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */ + unsigned short uxState; /*< Used internally by the co-routine implementation. */ +} corCRCB; /* Co-routine control block. Note must be identical in size down to uxPriority with tskTCB. */ + +/** + * croutine. h + *
+ portBASE_TYPE xCoRoutineCreate(
+                                 crCOROUTINE_CODE pxCoRoutineCode,
+                                 unsigned portBASE_TYPE uxPriority,
+                                 unsigned portBASE_TYPE uxIndex
+                               );
+ * + * Create a new co-routine and add it to the list of co-routines that are + * ready to run. + * + * @param pxCoRoutineCode Pointer to the co-routine function. Co-routine + * functions require special syntax - see the co-routine section of the WEB + * documentation for more information. + * + * @param uxPriority The priority with respect to other co-routines at which + * the co-routine will run. + * + * @param uxIndex Used to distinguish between different co-routines that + * execute the same function. See the example below and the co-routine section + * of the WEB documentation for further information. + * + * @return pdPASS if the co-routine was successfully created and added to a ready + * list, otherwise an error code defined with ProjDefs.h. + * + * Example usage: +
+ // Co-routine to be created.
+ void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ // This may not be necessary for const variables.
+ static const char cLedToFlash[ 2 ] = { 5, 6 };
+ static const portTickType uxFlashRates[ 2 ] = { 200, 400 };
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // This co-routine just delays for a fixed period, then toggles
+         // an LED.  Two co-routines are created using this function, so
+         // the uxIndex parameter is used to tell the co-routine which
+         // LED to flash and how long to delay.  This assumes xQueue has
+         // already been created.
+         vParTestToggleLED( cLedToFlash[ uxIndex ] );
+         crDELAY( xHandle, uxFlashRates[ uxIndex ] );
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+
+ // Function that creates two co-routines.
+ void vOtherFunction( void )
+ {
+ unsigned char ucParameterToPass;
+ xTaskHandle xHandle;
+		
+     // Create two co-routines at priority 0.  The first is given index 0
+     // so (from the code above) toggles LED 5 every 200 ticks.  The second
+     // is given index 1 so toggles LED 6 every 400 ticks.
+     for( uxIndex = 0; uxIndex < 2; uxIndex++ )
+     {
+         xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
+     }
+ }
+   
+ * \defgroup xCoRoutineCreate xCoRoutineCreate + * \ingroup Tasks + */ +signed portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex ); + + +/** + * croutine. h + *
+ void vCoRoutineSchedule( void );
+ * + * Run a co-routine. + * + * vCoRoutineSchedule() executes the highest priority co-routine that is able + * to run. The co-routine will execute until it either blocks, yields or is + * preempted by a task. Co-routines execute cooperatively so one + * co-routine cannot be preempted by another, but can be preempted by a task. + * + * If an application comprises of both tasks and co-routines then + * vCoRoutineSchedule should be called from the idle task (in an idle task + * hook). + * + * Example usage: +
+ // This idle task hook will schedule a co-routine each time it is called.
+ // The rest of the idle task will execute between co-routine calls.
+ void vApplicationIdleHook( void )
+ {
+	vCoRoutineSchedule();
+ }
+
+ // Alternatively, if you do not require any other part of the idle task to
+ // execute, the idle task hook can call vCoRoutineScheduler() within an
+ // infinite loop.
+ void vApplicationIdleHook( void )
+ {
+    for( ;; )
+    {
+        vCoRoutineSchedule();
+    }
+ }
+ 
+ * \defgroup vCoRoutineSchedule vCoRoutineSchedule + * \ingroup Tasks + */ +void vCoRoutineSchedule( void ); + +/** + * croutine. h + *
+ crSTART( xCoRoutineHandle xHandle );
+ * + * This macro MUST always be called at the start of a co-routine function. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static long ulAVariable;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+          // Co-routine functionality goes here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crSTART crSTART + * \ingroup Tasks + */ +#define crSTART( pxCRCB ) switch( ( ( corCRCB * )( pxCRCB ) )->uxState ) { case 0: + +/** + * croutine. h + *
+ crEND();
+ * + * This macro MUST always be called at the end of a co-routine function. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static long ulAVariable;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+          // Co-routine functionality goes here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crSTART crSTART + * \ingroup Tasks + */ +#define crEND() } + +/* + * These macros are intended for internal use by the co-routine implementation + * only. The macros should not be used directly by application writers. + */ +#define crSET_STATE0( xHandle ) ( ( corCRCB * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2): +#define crSET_STATE1( xHandle ) ( ( corCRCB * )( xHandle ) )->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1): + +/** + * croutine. h + *
+ crDELAY( xCoRoutineHandle xHandle, portTickType xTicksToDelay );
+ * + * Delay a co-routine for a fixed period of time. + * + * crDELAY can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * @param xHandle The handle of the co-routine to delay. This is the xHandle + * parameter of the co-routine function. + * + * @param xTickToDelay The number of ticks that the co-routine should delay + * for. The actual amount of time this equates to is defined by + * configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant portTICK_RATE_MS + * can be used to convert ticks to milliseconds. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ // This may not be necessary for const variables.
+ // We are to delay for 200ms.
+ static const xTickType xDelayTime = 200 / portTICK_RATE_MS;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+        // Delay for 200ms.
+        crDELAY( xHandle, xDelayTime );
+
+        // Do something here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crDELAY crDELAY + * \ingroup Tasks + */ +#define crDELAY( xHandle, xTicksToDelay ) \ + if( ( xTicksToDelay ) > 0 ) \ + { \ + vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL ); \ + } \ + crSET_STATE0( ( xHandle ) ); + +/** + *
+ crQUEUE_SEND(
+                  xCoRoutineHandle xHandle,
+                  xQueueHandle pxQueue,
+                  void *pvItemToQueue,
+                  portTickType xTicksToWait,
+                  portBASE_TYPE *pxResult
+             )
+ * + * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine + * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. + * + * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas + * xQueueSend() and xQueueReceive() can only be used from tasks. + * + * crQUEUE_SEND can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xHandle The handle of the calling co-routine. This is the xHandle + * parameter of the co-routine function. + * + * @param pxQueue The handle of the queue on which the data will be posted. + * The handle is obtained as the return value when the queue is created using + * the xQueueCreate() API function. + * + * @param pvItemToQueue A pointer to the data being posted onto the queue. + * The number of bytes of each queued item is specified when the queue is + * created. This number of bytes is copied from pvItemToQueue into the queue + * itself. + * + * @param xTickToDelay The number of ticks that the co-routine should block + * to wait for space to become available on the queue, should space not be + * available immediately. The actual amount of time this equates to is defined + * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant + * portTICK_RATE_MS can be used to convert ticks to milliseconds (see example + * below). + * + * @param pxResult The variable pointed to by pxResult will be set to pdPASS if + * data was successfully posted onto the queue, otherwise it will be set to an + * error defined within ProjDefs.h. + * + * Example usage: +
+ // Co-routine function that blocks for a fixed period then posts a number onto
+ // a queue.
+ static void prvCoRoutineFlashTask( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static portBASE_TYPE xNumberToPost = 0;
+ static portBASE_TYPE xResult;
+
+    // Co-routines must begin with a call to crSTART().
+    crSTART( xHandle );
+
+    for( ;; )
+    {
+        // This assumes the queue has already been created.
+        crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
+
+        if( xResult != pdPASS )
+        {
+            // The message was not posted!
+        }
+
+        // Increment the number to be posted onto the queue.
+        xNumberToPost++;
+
+        // Delay for 100 ticks.
+        crDELAY( xHandle, 100 );
+    }
+
+    // Co-routines must end with a call to crEND().
+    crEND();
+ }
+ * \defgroup crQUEUE_SEND crQUEUE_SEND + * \ingroup Tasks + */ +#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \ +{ \ + *( pxResult ) = xQueueCRSend( ( pxQueue) , ( pvItemToQueue) , ( xTicksToWait ) ); \ + if( *( pxResult ) == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( ( xHandle ) ); \ + *pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 ); \ + } \ + if( *pxResult == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( ( xHandle ) ); \ + *pxResult = pdPASS; \ + } \ +} + +/** + * croutine. h + *
+  crQUEUE_RECEIVE(
+                     xCoRoutineHandle xHandle,
+                     xQueueHandle pxQueue,
+                     void *pvBuffer,
+                     portTickType xTicksToWait,
+                     portBASE_TYPE *pxResult
+                 )
+ * + * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine + * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. + * + * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas + * xQueueSend() and xQueueReceive() can only be used from tasks. + * + * crQUEUE_RECEIVE can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xHandle The handle of the calling co-routine. This is the xHandle + * parameter of the co-routine function. + * + * @param pxQueue The handle of the queue from which the data will be received. + * The handle is obtained as the return value when the queue is created using + * the xQueueCreate() API function. + * + * @param pvBuffer The buffer into which the received item is to be copied. + * The number of bytes of each queued item is specified when the queue is + * created. This number of bytes is copied into pvBuffer. + * + * @param xTickToDelay The number of ticks that the co-routine should block + * to wait for data to become available from the queue, should data not be + * available immediately. The actual amount of time this equates to is defined + * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant + * portTICK_RATE_MS can be used to convert ticks to milliseconds (see the + * crQUEUE_SEND example). + * + * @param pxResult The variable pointed to by pxResult will be set to pdPASS if + * data was successfully retrieved from the queue, otherwise it will be set to + * an error code as defined within ProjDefs.h. + * + * Example usage: +
+ // A co-routine receives the number of an LED to flash from a queue.  It
+ // blocks on the queue until the number is received.
+ static void prvCoRoutineFlashWorkTask( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static portBASE_TYPE xResult;
+ static unsigned portBASE_TYPE uxLEDToFlash;
+
+    // All co-routines must start with a call to crSTART().
+    crSTART( xHandle );
+
+    for( ;; )
+    {
+        // Wait for data to become available on the queue.
+        crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+
+        if( xResult == pdPASS )
+        {
+            // We received the LED to flash - flash it!
+            vParTestToggleLED( uxLEDToFlash );
+        }
+    }
+
+    crEND();
+ }
+ * \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE + * \ingroup Tasks + */ +#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \ +{ \ + *( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), ( xTicksToWait ) ); \ + if( *( pxResult ) == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( ( xHandle ) ); \ + *( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), 0 ); \ + } \ + if( *( pxResult ) == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( ( xHandle ) ); \ + *( pxResult ) = pdPASS; \ + } \ +} + +/** + * croutine. h + *
+  crQUEUE_SEND_FROM_ISR(
+                            xQueueHandle pxQueue,
+                            void *pvItemToQueue,
+                            portBASE_TYPE xCoRoutinePreviouslyWoken
+                       )
+ * + * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the + * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() + * functions used by tasks. + * + * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to + * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and + * xQueueReceiveFromISR() can only be used to pass data between a task and and + * ISR. + * + * crQUEUE_SEND_FROM_ISR can only be called from an ISR to send data to a queue + * that is being used from within a co-routine. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xCoRoutinePreviouslyWoken This is included so an ISR can post onto + * the same queue multiple times from a single interrupt. The first call + * should always pass in pdFALSE. Subsequent calls should pass in + * the value returned from the previous call. + * + * @return pdTRUE if a co-routine was woken by posting onto the queue. This is + * used by the ISR to determine if a context switch may be required following + * the ISR. + * + * Example usage: +
+ // A co-routine that blocks on a queue waiting for characters to be received.
+ static void vReceivingCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ char cRxedChar;
+ portBASE_TYPE xResult;
+
+     // All co-routines must start with a call to crSTART().
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // Wait for data to become available on the queue.  This assumes the
+         // queue xCommsRxQueue has already been created!
+         crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+
+         // Was a character received?
+         if( xResult == pdPASS )
+         {
+             // Process the character here.
+         }
+     }
+
+     // All co-routines must end with a call to crEND().
+     crEND();
+ }
+
+ // An ISR that uses a queue to send characters received on a serial port to
+ // a co-routine.
+ void vUART_ISR( void )
+ {
+ char cRxedChar;
+ portBASE_TYPE xCRWokenByPost = pdFALSE;
+
+     // We loop around reading characters until there are none left in the UART.
+     while( UART_RX_REG_NOT_EMPTY() )
+     {
+         // Obtain the character from the UART.
+         cRxedChar = UART_RX_REG;
+
+         // Post the character onto a queue.  xCRWokenByPost will be pdFALSE
+         // the first time around the loop.  If the post causes a co-routine
+         // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
+         // In this manner we can ensure that if more than one co-routine is
+         // blocked on the queue only one is woken by this ISR no matter how
+         // many characters are posted to the queue.
+         xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
+     }
+ }
+ * \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR + * \ingroup Tasks + */ +#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) xQueueCRSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( xCoRoutinePreviouslyWoken ) ) + + +/** + * croutine. h + *
+  crQUEUE_SEND_FROM_ISR(
+                            xQueueHandle pxQueue,
+                            void *pvBuffer,
+                            portBASE_TYPE * pxCoRoutineWoken
+                       )
+ * + * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the + * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() + * functions used by tasks. + * + * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to + * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and + * xQueueReceiveFromISR() can only be used to pass data between a task and and + * ISR. + * + * crQUEUE_RECEIVE_FROM_ISR can only be called from an ISR to receive data + * from a queue that is being used from within a co-routine (a co-routine + * posted to the queue). + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvBuffer A pointer to a buffer into which the received item will be + * placed. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from the queue into + * pvBuffer. + * + * @param pxCoRoutineWoken A co-routine may be blocked waiting for space to become + * available on the queue. If crQUEUE_RECEIVE_FROM_ISR causes such a + * co-routine to unblock *pxCoRoutineWoken will get set to pdTRUE, otherwise + * *pxCoRoutineWoken will remain unchanged. + * + * @return pdTRUE an item was successfully received from the queue, otherwise + * pdFALSE. + * + * Example usage: +
+ // A co-routine that posts a character to a queue then blocks for a fixed
+ // period.  The character is incremented each time.
+ static void vSendingCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // cChar holds its value while this co-routine is blocked and must therefore
+ // be declared static.
+ static char cCharToTx = 'a';
+ portBASE_TYPE xResult;
+
+     // All co-routines must start with a call to crSTART().
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // Send the next character to the queue.
+         crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
+
+         if( xResult == pdPASS )
+         {
+             // The character was successfully posted to the queue.
+         }
+		 else
+		 {
+			// Could not post the character to the queue.
+		 }
+
+         // Enable the UART Tx interrupt to cause an interrupt in this
+		 // hypothetical UART.  The interrupt will obtain the character
+		 // from the queue and send it.
+		 ENABLE_RX_INTERRUPT();
+
+		 // Increment to the next character then block for a fixed period.
+		 // cCharToTx will maintain its value across the delay as it is
+		 // declared static.
+		 cCharToTx++;
+		 if( cCharToTx > 'x' )
+		 {
+			cCharToTx = 'a';
+		 }
+		 crDELAY( 100 );
+     }
+
+     // All co-routines must end with a call to crEND().
+     crEND();
+ }
+
+ // An ISR that uses a queue to receive characters to send on a UART.
+ void vUART_ISR( void )
+ {
+ char cCharToTx;
+ portBASE_TYPE xCRWokenByPost = pdFALSE;
+
+     while( UART_TX_REG_EMPTY() )
+     {
+         // Are there any characters in the queue waiting to be sent?
+		 // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
+		 // is woken by the post - ensuring that only a single co-routine is
+		 // woken no matter how many times we go around this loop.
+         if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
+		 {
+			 SEND_CHARACTER( cCharToTx );
+		 }
+     }
+ }
+ * \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR + * \ingroup Tasks + */ +#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) xQueueCRReceiveFromISR( ( pxQueue ), ( pvBuffer ), ( pxCoRoutineWoken ) ) + +/* + * This function is intended for internal use by the co-routine macros only. + * The macro nature of the co-routine implementation requires that the + * prototype appears here. The function should not be used by application + * writers. + * + * Removes the current co-routine from its ready list and places it in the + * appropriate delayed list. + */ +void vCoRoutineAddToDelayedList( portTickType xTicksToDelay, xList *pxEventList ); + +/* + * This function is intended for internal use by the queue implementation only. + * The function should not be used by application writers. + * + * Removes the highest priority co-routine from the event list and places it in + * the pending ready list. + */ +signed portBASE_TYPE xCoRoutineRemoveFromEventList( const xList *pxEventList ); + +#ifdef __cplusplus +} +#endif + +#endif /* CO_ROUTINE_H */ diff --git a/sdk/Src/OS/RTOS/include/mpu_wrappers.h b/sdk/Src/OS/RTOS/include/mpu_wrappers.h new file mode 100644 index 0000000..c1372d6 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/mpu_wrappers.h @@ -0,0 +1,135 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef MPU_WRAPPERS_H +#define MPU_WRAPPERS_H + +/* This file redefines API functions to be called through a wrapper macro, but +only for ports that are using the MPU. */ +#ifdef portUSING_MPU_WRAPPERS + + /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE will be defined when this file is + included from queue.c or task.c to prevent it from having an effect within + those files. */ + #ifndef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + + #define xTaskGenericCreate MPU_xTaskGenericCreate + #define vTaskAllocateMPURegions MPU_vTaskAllocateMPURegions + #define vTaskDelete MPU_vTaskDelete + #define vTaskDelayUntil MPU_vTaskDelayUntil + #define vTaskDelay MPU_vTaskDelay + #define uxTaskPriorityGet MPU_uxTaskPriorityGet + #define vTaskPrioritySet MPU_vTaskPrioritySet + #define vTaskSuspend MPU_vTaskSuspend + #define xTaskIsTaskSuspended MPU_xTaskIsTaskSuspended + #define vTaskResume MPU_vTaskResume + #define vTaskSuspendAll MPU_vTaskSuspendAll + #define xTaskResumeAll MPU_xTaskResumeAll + #define xTaskGetTickCount MPU_xTaskGetTickCount + #define uxTaskGetNumberOfTasks MPU_uxTaskGetNumberOfTasks + #define vTaskList MPU_vTaskList + #define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats + #define vTaskStartTrace MPU_vTaskStartTrace + #define ulTaskEndTrace MPU_ulTaskEndTrace + #define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag + #define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag + #define xTaskCallApplicationTaskHook MPU_xTaskCallApplicationTaskHook + #define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark + #define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle + #define xTaskGetSchedulerState MPU_xTaskGetSchedulerState + + #define xQueueCreate MPU_xQueueCreate + #define xQueueCreateMutex MPU_xQueueCreateMutex + #define xQueueGiveMutexRecursive MPU_xQueueGiveMutexRecursive + #define xQueueTakeMutexRecursive MPU_xQueueTakeMutexRecursive + #define xQueueCreateCountingSemaphore MPU_xQueueCreateCountingSemaphore + #define xQueueGenericSend MPU_xQueueGenericSend + #define xQueueAltGenericSend MPU_xQueueAltGenericSend + #define xQueueAltGenericReceive MPU_xQueueAltGenericReceive + #define xQueueGenericReceive MPU_xQueueGenericReceive + #define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting + #define vQueueDelete MPU_vQueueDelete + + #define pvPortMalloc MPU_pvPortMalloc + #define vPortFree MPU_vPortFree + #define xPortGetFreeHeapSize MPU_xPortGetFreeHeapSize + #define vPortInitialiseBlocks MPU_vPortInitialiseBlocks + + #if configQUEUE_REGISTRY_SIZE > 0 + #define vQueueAddToRegistry MPU_vQueueAddToRegistry + #define vQueueUnregisterQueue MPU_vQueueUnregisterQueue + #endif + + /* Remove the privileged function macro. */ + #define PRIVILEGED_FUNCTION + + #else /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + + /* Ensure API functions go in the privileged execution section. */ + #define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + //#define PRIVILEGED_DATA + + #endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + +#else /* portUSING_MPU_WRAPPERS */ + + #define PRIVILEGED_FUNCTION + #define PRIVILEGED_DATA + #define portUSING_MPU_WRAPPERS 0 + +#endif /* portUSING_MPU_WRAPPERS */ + + +#endif /* MPU_WRAPPERS_H */ + diff --git a/sdk/Src/OS/RTOS/include/portable.h b/sdk/Src/OS/RTOS/include/portable.h new file mode 100644 index 0000000..b5c5197 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/portable.h @@ -0,0 +1,391 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +/*----------------------------------------------------------- + * Portable layer API. Each function must be defined for each port. + *----------------------------------------------------------*/ + +#ifndef PORTABLE_H +#define PORTABLE_H + +/* Include the macro file relevant to the port being used. */ + +#ifdef OPEN_WATCOM_INDUSTRIAL_PC_PORT + #include "..\..\Source\portable\owatcom\16bitdos\pc\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef OPEN_WATCOM_FLASH_LITE_186_PORT + #include "..\..\Source\portable\owatcom\16bitdos\flsh186\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef GCC_MEGA_AVR + #include "../portable/GCC/ATMega323/portmacro.h" +#endif + +#ifdef IAR_MEGA_AVR + #include "../portable/IAR/ATMega323/portmacro.h" +#endif + +#ifdef MPLAB_PIC24_PORT + #include "..\..\Source\portable\MPLAB\PIC24_dsPIC\portmacro.h" +#endif + +#ifdef MPLAB_DSPIC_PORT + #include "..\..\Source\portable\MPLAB\PIC24_dsPIC\portmacro.h" +#endif + +#ifdef MPLAB_PIC18F_PORT + #include "..\..\Source\portable\MPLAB\PIC18F\portmacro.h" +#endif + +#ifdef MPLAB_PIC32MX_PORT + #include "..\..\Source\portable\MPLAB\PIC32MX\portmacro.h" +#endif + +#ifdef _FEDPICC + #include "libFreeRTOS/Include/portmacro.h" +#endif + +#ifdef SDCC_CYGNAL + #include "../../Source/portable/SDCC/Cygnal/portmacro.h" +#endif + +#ifdef GCC_ARM7 + #include "../../Source/portable/GCC/ARM7_LPC2000/portmacro.h" +#endif + +#ifdef GCC_ARM7_ECLIPSE + #include "portmacro.h" +#endif + +#ifdef ROWLEY_LPC23xx + #include "../../Source/portable/GCC/ARM7_LPC23xx/portmacro.h" +#endif + +#ifdef IAR_MSP430 + #include "..\..\Source\portable\IAR\MSP430\portmacro.h" +#endif + +#ifdef GCC_MSP430 + #include "../../Source/portable/GCC/MSP430F449/portmacro.h" +#endif + +#ifdef ROWLEY_MSP430 + #include "../../Source/portable/Rowley/MSP430F449/portmacro.h" +#endif + +#ifdef ARM7_LPC21xx_KEIL_RVDS + #include "..\..\Source\portable\RVDS\ARM7_LPC21xx\portmacro.h" +#endif + +#ifdef SAM7_GCC + #include "../../Source/portable/GCC/ARM7_AT91SAM7S/portmacro.h" +#endif + +#ifdef SAM7_IAR + #include "..\..\Source\portable\IAR\AtmelSAM7S64\portmacro.h" +#endif + +#ifdef SAM9XE_IAR + #include "..\..\Source\portable\IAR\AtmelSAM9XE\portmacro.h" +#endif + +#ifdef LPC2000_IAR + #include "..\..\Source\portable\IAR\LPC2000\portmacro.h" +#endif + +#ifdef STR71X_IAR + #include "..\..\Source\portable\IAR\STR71x\portmacro.h" +#endif + +#ifdef STR75X_IAR + #include "..\..\Source\portable\IAR\STR75x\portmacro.h" +#endif + +#ifdef STR75X_GCC + #include "..\..\Source\portable\GCC\STR75x\portmacro.h" +#endif + +#ifdef STR91X_IAR + #include "..\..\Source\portable\IAR\STR91x\portmacro.h" +#endif + +#ifdef GCC_H8S + #include "../../Source/portable/GCC/H8S2329/portmacro.h" +#endif + +#ifdef GCC_AT91FR40008 + #include "../../Source/portable/GCC/ARM7_AT91FR40008/portmacro.h" +#endif + +#ifdef RVDS_ARMCM3_LM3S102 + #include "../../Source/portable/RVDS/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3_LM3S102 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARM_CM3 + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARMCM3_LM + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef HCS12_CODE_WARRIOR + #include "../../Source/portable/CodeWarrior/HCS12/portmacro.h" +#endif + +#ifdef MICROBLAZE_GCC + #include "../../Source/portable/GCC/MicroBlaze/portmacro.h" +#endif + +#ifdef TERN_EE + #include "..\..\Source\portable\Paradigm\Tern_EE\small\portmacro.h" +#endif + +#ifdef GCC_HCS12 + #include "../../Source/portable/GCC/HCS12/portmacro.h" +#endif + +#ifdef GCC_MCF5235 + #include "../../Source/portable/GCC/MCF5235/portmacro.h" +#endif + +#ifdef COLDFIRE_V2_GCC + #include "../../../Source/portable/GCC/ColdFire_V2/portmacro.h" +#endif + +#ifdef COLDFIRE_V2_CODEWARRIOR + #include "../../Source/portable/CodeWarrior/ColdFire_V2/portmacro.h" +#endif + +#ifdef GCC_PPC405 + #include "../../Source/portable/GCC/PPC405_Xilinx/portmacro.h" +#endif + +#ifdef GCC_PPC440 + #include "../../Source/portable/GCC/PPC440_Xilinx/portmacro.h" +#endif + +#ifdef _16FX_SOFTUNE + #include "..\..\Source\portable\Softune\MB96340\portmacro.h" +#endif + +#ifdef BCC_INDUSTRIAL_PC_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\PC\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef BCC_FLASH_LITE_186_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\flsh186\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef __GNUC__ + #ifdef __AVR32_AVR32A__ + #include "portmacro.h" + #endif +#endif + +#ifdef __ICCAVR32__ + #ifdef __CORE__ + #if __CORE__ == __AVR32A__ + #include "portmacro.h" + #endif + #endif +#endif + +#ifdef __91467D + #include "portmacro.h" +#endif + +#ifdef __96340 + #include "portmacro.h" +#endif + + +#ifdef __IAR_V850ES_Fx3__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx3__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx3_L__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx2__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Hx2__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_78K0R_Kx3__ + #include "../../Source/portable/IAR/78K0R/portmacro.h" +#endif + +#ifdef __IAR_78K0R_Kx3L__ + #include "../../Source/portable/IAR/78K0R/portmacro.h" +#endif + +/* Catch all to ensure portmacro.h is included in the build. Newer demos +have the path as part of the project options, rather than as relative from +the project location. If portENTER_CRITICAL() has not been defined then +portmacro.h has not yet been included - as every portmacro.h provides a +portENTER_CRITICAL() definition. Check the demo application for your demo +to find the path to the correct portmacro.h file. */ +#ifndef portENTER_CRITICAL + #include "portmacro.h" +#endif + +#if portBYTE_ALIGNMENT == 8 + #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) +#endif + +#if portBYTE_ALIGNMENT == 4 + #define portBYTE_ALIGNMENT_MASK ( 0x0003 ) +#endif + +#if portBYTE_ALIGNMENT == 2 + #define portBYTE_ALIGNMENT_MASK ( 0x0001 ) +#endif + +#if portBYTE_ALIGNMENT == 1 + #define portBYTE_ALIGNMENT_MASK ( 0x0000 ) +#endif + +#ifndef portBYTE_ALIGNMENT_MASK + #error "Invalid portBYTE_ALIGNMENT definition" +#endif + +#ifndef portNUM_CONFIGURABLE_REGIONS + #define portNUM_CONFIGURABLE_REGIONS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mpu_wrappers.h" + +/* + * Setup the stack of a new task so it is ready to be placed under the + * scheduler control. The registers have to be placed on the stack in + * the order that the port expects to find them. + * + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters, portBASE_TYPE xRunPrivileged ) PRIVILEGED_FUNCTION; +#else + portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ); +#endif + +/* + * Map to the memory management routines required for the port. + */ +void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION; +void vPortFree( void *pv ) PRIVILEGED_FUNCTION; +void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION; +size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION; +int xPortMemIsKernel(void *mem) PRIVILEGED_FUNCTION; + +/* + * Setup the hardware ready for the scheduler to take control. This generally + * sets up a tick interrupt and sets timers for the correct tick frequency. + */ +portBASE_TYPE xPortStartScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so + * the hardware is left in its original condition after the scheduler stops + * executing. + */ +void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * The structures and methods of manipulating the MPU are contained within the + * port layer. + * + * Fills the xMPUSettings structure with the memory region information + * contained in xRegions. + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + struct xMEMORY_REGION; + void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, portSTACK_TYPE *pxBottomOfStack, unsigned short usStackDepth ) PRIVILEGED_FUNCTION; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PORTABLE_H */ + diff --git a/sdk/Src/OS/RTOS/include/portmacro.h b/sdk/Src/OS/RTOS/include/portmacro.h new file mode 100644 index 0000000..323fa3b --- /dev/null +++ b/sdk/Src/OS/RTOS/include/portmacro.h @@ -0,0 +1,171 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE unsigned portLONG +#define portBASE_TYPE portLONG + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef unsigned portSHORT portTickType; + #define portMAX_DELAY ( portTickType ) 0xffff +#else + typedef unsigned portLONG portTickType; + #define portMAX_DELAY ( portTickType ) 0xffffffff +#endif +/*-----------------------------------------------------------*/ + +/* Hardware specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Task utilities. */ + +/*----------------------------------------------------------- + * ISR entry and exit macros. These are only required if a task switch + * is required from an ISR. + *----------------------------------------------------------*/ + +/* If a switch is required then we just need to call */ +/* vTaskSwitchContext() as the context has already been */ +/* saved. */ + +#define portEXIT_SWITCHING_ISR(SwitchRequired) \ +{ \ +extern void vTaskSwitchContext(void); \ + \ + if(SwitchRequired) \ + { \ + vTaskSwitchContext(); \ + } \ +} \ + +extern void vPortYield( void ); +#define portYIELD() vPortYield() +#define portYIELD_FROM_ISR() vPortYield() + +/* Critical section management. */ + +/* + ****************************************************************** + * We don't need to worry about whether we're in ARM or + * THUMB mode with the Keil Real View compiler when enabling + * or disabling interrupts as the compiler's intrinsic functions + * take care of that for us. + ******************************************************************* + */ +//#define portDISABLE_INTERRUPTS() __disable_irq() +//#define portENABLE_INTERRUPTS() __enable_irq() + + +/*----------------------------------------------------------- + * Critical section control + * + * The code generated by the Keil compiler does not maintain separate + * stack and frame pointers. The portENTER_CRITICAL macro cannot therefore + * use the stack as per other ports. Instead a variable is used to keep + * track of the critical section nesting. This necessitates the use of a + * function in place of the macro. + *----------------------------------------------------------*/ + +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); + +#define portENTER_CRITICAL() vPortEnterCritical(); +#define portEXIT_CRITICAL() vPortExitCritical(); +/*-----------------------------------------------------------*/ + +/* Compiler specifics. */ +#define inline +#define register +#define portNOP() __asm{ NOP } +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/sdk/Src/OS/RTOS/include/portmacro.inc b/sdk/Src/OS/RTOS/include/portmacro.inc new file mode 100644 index 0000000..f66eab8 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/portmacro.inc @@ -0,0 +1,115 @@ +;/* +; FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. +; +; +; *************************************************************************** +; * * +; * FreeRTOS tutorial books are available in pdf and paperback. * +; * Complete, revised, and edited pdf reference manuals are also * +; * available. * +; * * +; * Purchasing FreeRTOS documentation will not only help you, by * +; * ensuring you get running as quickly as possible and with an * +; * in-depth knowledge of how to use FreeRTOS, it will also help * +; * the FreeRTOS project to continue with its mission of providing * +; * professional grade, cross platform, de facto standard solutions * +; * for microcontrollers - completely free of charge! * +; * * +; * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * +; * * +; * Thank you for using FreeRTOS, and thank you for your support! * +; * * +; *************************************************************************** +; +; +; This file is part of the FreeRTOS distribution. +; +; FreeRTOS is free software; you can redistribute it and/or modify it under +; the terms of the GNU General Public License (version 2) as published by the +; Free Software Foundation AND MODIFIED BY the FreeRTOS exception. +; >>>NOTE<<< The modification to the GPL is included to allow you to +; distribute a combined work that includes FreeRTOS without being obliged to +; provide the source code for proprietary components outside of the FreeRTOS +; kernel. FreeRTOS is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +; more details. You should have received a copy of the GNU General Public +; License and the FreeRTOS license exception along with FreeRTOS; if not it +; can be viewed here: http://www.freertos.org/a00114.html and also obtained +; by writing to Richard Barry, contact details for whom are available on the +; FreeRTOS WEB site. +; +; 1 tab == 4 spaces! +; +; http://www.FreeRTOS.org - Documentation, latest information, license and +; contact details. +; +; http://www.SafeRTOS.com - A version that is certified for use in safety +; critical systems. +; +; http://www.OpenRTOS.com - Commercial support, development, porting, +; licensing and training services. +;*/ + + IMPORT ulCriticalNesting ; + IMPORT pxCurrentTCB ; + + + MACRO + portRESTORE_CONTEXT + + LDR R0, =pxCurrentTCB ; Set the LR to the task stack. The location was... + LDR R0, [R0] ; ... stored in pxCurrentTCB + LDR LR, [R0] + + LDR R0, =ulCriticalNesting ; The critical nesting depth is the first item on... + LDMFD LR!, {R1} ; ...the stack. Load it into the ulCriticalNesting var. + STR R1, [R0] ; + + LDMFD LR!, {R0} ; Get the SPSR from the stack. + MSR SPSR_cxsf, R0 ; + + LDMFD LR, {R0-R14}^ ; Restore all system mode registers for the task. + NOP ; + + LDR LR, [LR, #+60] ; Restore the return address + + ; And return - correcting the offset in the LR to obtain ... + SUBS PC, LR, #4 ; ...the correct address. + + MEND + +; /**********************************************************************/ + + MACRO + portSAVE_CONTEXT + + STMDB SP!, {R0} ; Store R0 first as we need to use it. + + STMDB SP,{SP}^ ; Set R0 to point to the task stack pointer. + NOP ; + SUB SP, SP, #4 ; + LDMIA SP!,{R0} ; + + STMDB R0!, {LR} ; Push the return address onto the stack. + MOV LR, R0 ; Now we have saved LR we can use it instead of R0. + LDMIA SP!, {R0} ; Pop R0 so we can save it onto the system mode stack. + + STMDB LR,{R0-LR}^ ; Push all the system mode registers onto the task stack. + NOP ; + SUB LR, LR, #60 ; + + MRS R0, SPSR ; Push the SPSR onto the task stack. + STMDB LR!, {R0} ; + + LDR R0, =ulCriticalNesting ; + LDR R0, [R0] ; + STMDB LR!, {R0} ; + + LDR R0, =pxCurrentTCB ; Store the new top of stack for the task. + LDR R1, [R0] ; + STR LR, [R1] ; + + MEND + + END diff --git a/sdk/Src/OS/RTOS/include/projdefs.h b/sdk/Src/OS/RTOS/include/projdefs.h new file mode 100644 index 0000000..54ba870 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/projdefs.h @@ -0,0 +1,77 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef PROJDEFS_H +#define PROJDEFS_H + +/* Defines the prototype to which task functions must conform. */ +typedef void (*pdTASK_CODE)( void * ); + +#define pdTRUE ( 1 ) +#define pdFALSE ( 0 ) + +#define pdPASS ( 1 ) +#define pdFAIL ( 0 ) +#define errQUEUE_EMPTY ( 0 ) +#define errQUEUE_FULL ( 0 ) + +/* Error definitions. */ +#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) +#define errNO_TASK_TO_RUN ( -2 ) +#define errQUEUE_BLOCKED ( -4 ) +#define errQUEUE_YIELD ( -5 ) + +#endif /* PROJDEFS_H */ + + + diff --git a/sdk/Src/OS/RTOS/include/rtoslist.h b/sdk/Src/OS/RTOS/include/rtoslist.h new file mode 100644 index 0000000..fbd0a61 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/rtoslist.h @@ -0,0 +1,308 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +/* + * This is the list implementation used by the scheduler. While it is tailored + * heavily for the schedulers needs, it is also available for use by + * application code. + * + * xLists can only store pointers to xListItems. Each xListItem contains a + * numeric value (xItemValue). Most of the time the lists are sorted in + * descending item value order. + * + * Lists are created already containing one list item. The value of this + * item is the maximum possible that can be stored, it is therefore always at + * the end of the list and acts as a marker. The list member pxHead always + * points to this marker - even though it is at the tail of the list. This + * is because the tail contains a wrap back pointer to the true head of + * the list. + * + * In addition to it's value, each list item contains a pointer to the next + * item in the list (pxNext), a pointer to the list it is in (pxContainer) + * and a pointer to back to the object that contains it. These later two + * pointers are included for efficiency of list manipulation. There is + * effectively a two way link between the object containing the list item and + * the list item itself. + * + * + * \page ListIntroduction List Implementation + * \ingroup FreeRTOSIntro + */ + + +#ifndef RTOS_LIST_H +#define RTOS_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif +/* + * Definition of the only type of object that a list can contain. + */ +struct xLIST_ITEM +{ + portTickType xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ + volatile struct xLIST_ITEM * pxNext; /*< Pointer to the next xListItem in the list. */ + volatile struct xLIST_ITEM * pxPrevious;/*< Pointer to the previous xListItem in the list. */ + void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ + void * pvContainer; /*< Pointer to the list in which this list item is placed (if any). */ +}; +typedef struct xLIST_ITEM xListItem; /* For some reason lint wants this as two separate definitions. */ + +struct xMINI_LIST_ITEM +{ + portTickType xItemValue; + volatile struct xLIST_ITEM *pxNext; + volatile struct xLIST_ITEM *pxPrevious; +}; +typedef struct xMINI_LIST_ITEM xMiniListItem; + +/* + * Definition of the type of queue used by the scheduler. + */ +typedef struct xLIST +{ + volatile unsigned portBASE_TYPE uxNumberOfItems; + volatile xListItem * pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to pvListGetOwnerOfNextEntry (). */ + volatile xMiniListItem xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ +} xList; + +/* + * Access macro to set the owner of a list item. The owner of a list item + * is the object (usually a TCB) that contains the list item. + * + * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) + +/* + * Access macro to set the value of the list item. In most cases the value is + * used to sort the list in descending order. + * + * \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( pxListItem )->xItemValue = ( xValue ) + +/* + * Access macro the retrieve the value of the list item. The value can + * represent anything - for example a the priority of a task, or the time at + * which a task should be unblocked. + * + * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue ) + +/* + * Access macro the retrieve the value of the list item at the head of a given + * list. + * + * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->xItemValue ) + +/* + * Access macro to determine if a list contains any items. The macro will + * only have the value true if the list is empty. + * + * \page listLIST_IS_EMPTY listLIST_IS_EMPTY + * \ingroup LinkedList + */ +#define listLIST_IS_EMPTY( pxList ) ( ( pxList )->uxNumberOfItems == ( unsigned portBASE_TYPE ) 0 ) + +/* + * Access macro to return the number of items in the list. + */ +#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems ) + +/* + * Access function to obtain the owner of the next entry in a list. + * + * The list member pxIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list + * and returns that entries pxOwner parameter. Using multiple calls to this + * function it is therefore possible to move through every item contained in + * a list. + * + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxList The list from which the next item owner is to be returned. + * + * \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ +{ \ +xList * const pxConstList = ( pxList ); \ + /* Increment the index to the next item and return the item, ensuring */ \ + /* we don't return the marker used at the end of the list. */ \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + if( ( pxConstList )->pxIndex == ( xListItem * ) &( ( pxConstList )->xListEnd ) ) \ + { \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + } \ + ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \ +} + + +/* + * Access function to obtain the owner of the first entry in a list. Lists + * are normally sorted in ascending item value order. + * + * This function returns the pxOwner member of the first item in the list. + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxList The list from which the owner of the head item is to be + * returned. + * + * \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner ) + +/* + * Check to see if a list item is within a list. The list item maintains a + * "container" pointer that points to the list it is in. All this macro does + * is check to see if the container and the list match. + * + * @param pxList The list we want to know if the list item is within. + * @param pxListItem The list item we want to know if is in the list. + * @return pdTRUE is the list item is in the list, otherwise pdFALSE. + * pointer against + */ +#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) + +/* + * Must be called before a list is used! This initialises all the members + * of the list structure and inserts the xListEnd item into the list as a + * marker to the back of the list. + * + * @param pxList Pointer to the list being initialised. + * + * \page vListInitialise vListInitialise + * \ingroup LinkedList + */ +void vListInitialise( xList *pxList ); + +/* + * Must be called before a list item is used. This sets the list container to + * null so the item does not think that it is already contained in a list. + * + * @param pxItem Pointer to the list item being initialised. + * + * \page vListInitialiseItem vListInitialiseItem + * \ingroup LinkedList + */ +void vListInitialiseItem( xListItem *pxItem ); + +/* + * Insert a list item into a list. The item will be inserted into the list in + * a position determined by its item value (descending item value order). + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The item to that is to be placed in the list. + * + * \page vListInsert vListInsert + * \ingroup LinkedList + */ +void vListInsert( xList *pxList, xListItem *pxNewListItem ); + +/* + * Insert a list item into a list. The item will be inserted in a position + * such that it will be the last item within the list returned by multiple + * calls to listGET_OWNER_OF_NEXT_ENTRY. + * + * The list member pvIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pvIndex to the next item in the list. + * Placing an item in a list using vListInsertEnd effectively places the item + * in the list position pointed to by pvIndex. This means that every other + * item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before + * the pvIndex parameter again points to the item being inserted. + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The list item to be inserted into the list. + * + * \page vListInsertEnd vListInsertEnd + * \ingroup LinkedList + */ +void vListInsertEnd( xList *pxList, xListItem *pxNewListItem ); + +/* + * Remove an item from a list. The list item has a pointer to the list that + * it is in, so only the list item need be passed into the function. + * + * @param vListRemove The item to be removed. The item will remove itself from + * the list pointed to by it's pxContainer parameter. + * + * \page vListRemove vListRemove + * \ingroup LinkedList + */ +void vListRemove( xListItem *pxItemToRemove ); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/sdk/Src/OS/RTOS/include/rtosqueue.h b/sdk/Src/OS/RTOS/include/rtosqueue.h new file mode 100644 index 0000000..a73411b --- /dev/null +++ b/sdk/Src/OS/RTOS/include/rtosqueue.h @@ -0,0 +1,1287 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#ifndef QUEUE_H +#define QUEUE_H + +#ifndef INC_FREERTOS_H + #error "#include FreeRTOS.h" must appear in source files before "#include queue.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "mpu_wrappers.h" + +typedef struct QueueDefinition +{ + signed char *pcHead; /*< Points to the beginning of the queue storage area. */ + signed char *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + + signed char *pcWriteTo; /*< Points to the free next place in the storage area. */ + signed char *pcReadFrom; /*< Points to the last place that a queued item was read from. */ + + xList xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ + xList xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ + + volatile unsigned portBASE_TYPE uxMessagesWaiting;/*< The number of items currently in the queue. */ + unsigned portBASE_TYPE uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ + unsigned portBASE_TYPE uxItemSize; /*< The size of each items that the queue will hold. */ + + signed portBASE_TYPE xRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + signed portBASE_TYPE xTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + +} xQUEUE; + + +/** + * Type by which queues are referenced. For example, a call to xQueueCreate + * returns (via a pointer parameter) an xQueueHandle variable that can then + * be used as a parameter to xQueueSend(), xQueueReceive(), etc. + */ +typedef xQUEUE * xQueueHandle; + + +/* For internal use only. */ +#define queueSEND_TO_BACK ( 0 ) +#define queueSEND_TO_FRONT ( 1 ) + + +/** + * queue. h + *
+ xQueueHandle xQueueCreate(
+							  unsigned portBASE_TYPE uxQueueLength,
+							  unsigned portBASE_TYPE uxItemSize
+						  );
+ * 
+ * + * Creates a new queue instance. This allocates the storage required by the + * new queue and returns a handle for the queue. + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @return If the queue is successfully create then a handle to the newly + * created queue is returned. If the queue cannot be created then 0 is + * returned. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ };
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+	if( xQueue1 == 0 )
+	{
+		// Queue was not created and must not be used.
+	}
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue2 == 0 )
+	{
+		// Queue was not created and must not be used.
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueCreate xQueueCreate + * \ingroup QueueManagement + */ + +xQueueHandle xQueueCreateExt( void *QueueStart, unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ); +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendToToFront(
+								   xQueueHandle	xQueue,
+								   const void	*	pvItemToQueue,
+								   portTickType	xTicksToWait
+							   );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the front of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ unsigned long ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an unsigned long.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendToBack(
+								   xQueueHandle	xQueue,
+								   const	void	*	pvItemToQueue,
+								   portTickType	xTicksToWait
+							   );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the back of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the queue + * is full. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ unsigned long ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an unsigned long.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueSend(
+							  xQueueHandle xQueue,
+							  const void * pvItemToQueue,
+							  portTickType xTicksToWait
+						 );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). It is included for + * backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToFront() and xQueueSendToBack() macros. It is + * equivalent to xQueueSendToBack(). + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ unsigned long ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an unsigned long.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + + +/** + * queue. h + *
+ portBASE_TYPE xQueueGenericSend(
+									xQueueHandle xQueue,
+									const void * pvItemToQueue,
+									portTickType xTicksToWait
+									portBASE_TYPE xCopyPosition
+								);
+ * 
+ * + * It is preferred that the macros xQueueSend(), xQueueSendToFront() and + * xQueueSendToBack() are used in place of calling this function directly. + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ unsigned long ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an unsigned long.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10, queueSEND_TO_BACK ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0, queueSEND_TO_BACK );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ); + +/** + * queue. h + *
+ portBASE_TYPE xQueuePeek(
+							 xQueueHandle xQueue,
+							 void *pvBuffer,
+							 portTickType xTicksToWait
+						 );
+ * + * This is a macro that calls the xQueueGenericReceive() function. + * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * This macro must not be used in an interrupt service routine. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * xQueuePeek() will return immediately if xTicksToWait is 0 and the queue + * is empty. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ xQueueHandle xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to peek the data from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Peek a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueuePeek( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask, but the item still remains on the queue.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueReceive(
+								 xQueueHandle xQueue,
+								 void *pvBuffer,
+								 portTickType xTicksToWait
+							);
+ * + * This is a macro that calls the xQueueGenericReceive() function. + * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * Successfully received items are removed from the queue. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. xQueueReceive() will return immediately if xTicksToWait + * is zero and the queue is empty. The time is defined in tick periods so the + * constant portTICK_RATE_MS should be used to convert to real time if this is + * required. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ xQueueHandle xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to receive from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Receive a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE ) + + +/** + * queue. h + *
+ portBASE_TYPE xQueueGenericReceive(
+									   xQueueHandle	xQueue,
+									   void	*pvBuffer,
+									   portTickType	xTicksToWait
+									   portBASE_TYPE	xJustPeek
+									);
+ * + * It is preferred that the macro xQueueReceive() be used rather than calling + * this function directly. + * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * xQueueGenericReceive() will return immediately if the queue is empty and + * xTicksToWait is 0. + * + * @param xJustPeek When set to true, the item received from the queue is not + * actually removed from the queue - meaning a subsequent call to + * xQueueReceive() will return the same item. When set to false, the item + * being received from the queue is also removed from the queue. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ xQueueHandle xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to receive from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Receive a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueGenericReceive( xQueueHandle xQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeek ); + +/** + * queue. h + *
unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle xQueue );
+ * + * Return the number of messages stored in a queue. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of messages available in the queue. + * + * \page uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle xQueue ); + +/** + * queue. h + *
void vQueueDelete( xQueueHandle xQueue );
+ * + * Delete a queue - freeing all the memory allocated for storing of items + * placed on the queue. + * + * @param xQueue A handle to the queue to be deleted. + * + * \page vQueueDelete vQueueDelete + * \ingroup QueueManagement + */ +void vQueueDelete( xQueueHandle pxQueue ); +void vQueueDeleteExt( xQueueHandle pxQueue ); +/** + * queue. h + *
+ portBASE_TYPE xQueueSendToFrontFromISR(
+										 xQueueHandle pxQueue,
+										 const void *pvItemToQueue,
+										 portBASE_TYPE *pxHigherPriorityTaskWoken
+									  );
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the front of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToFromFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ portBASE_TYPE xHigherPrioritTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToFrontFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) + + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendToBackFromISR(
+										 xQueueHandle pxQueue,
+										 const void *pvItemToQueue,
+										 portBASE_TYPE *pxHigherPriorityTaskWoken
+									  );
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the back of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ portBASE_TYPE xHigherPriorityTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToBackFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendFromISR(
+									 xQueueHandle pxQueue,
+									 const void *pvItemToQueue,
+									 portBASE_TYPE *pxHigherPriorityTaskWoken
+								);
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). It is included + * for backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() + * macros. + * + * Post an item to the back of a queue. It is safe to use this function from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ portBASE_TYPE xHigherPriorityTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		// Actual macro used here is port specific.
+		taskYIELD_FROM_ISR ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueGenericSendFromISR(
+										   xQueueHandle	pxQueue,
+										   const	void	*pvItemToQueue,
+										   portBASE_TYPE	*pxHigherPriorityTaskWoken,
+										   portBASE_TYPE	xCopyPosition
+									   );
+ 
+ * + * It is preferred that the macros xQueueSendFromISR(), + * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place + * of calling this function directly. + * + * Post an item on a queue. It is safe to use this function from within an + * interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ portBASE_TYPE xHigherPriorityTaskWokenByPost;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWokenByPost = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post each byte.
+		xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.  Note that the
+	// name of the yield function required is port specific.
+	if( xHigherPriorityTaskWokenByPost )
+	{
+		taskYIELD_YIELD_FROM_ISR();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueReceiveFromISR(
+									   xQueueHandle	pxQueue,
+									   void	*pvBuffer,
+									   portBASE_TYPE	*pxTaskWoken
+								   );
+ * 
+ * + * Receive an item from a queue. It is safe to use this function from within an + * interrupt service routine. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param pxTaskWoken A task may be blocked waiting for space to become + * available on the queue. If xQueueReceiveFromISR causes such a task to + * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will + * remain unchanged. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+
+ xQueueHandle xQueue;
+
+ // Function to create a queue and post some values.
+ void vAFunction( void *pvParameters )
+ {
+ char cValueToPost;
+ const portTickType xBlockTime = ( portTickType )0xff;
+
+	// Create a queue capable of containing 10 characters.
+	xQueue = xQueueCreate( 10, sizeof( char ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Post some characters that will be used within an ISR.  If the queue
+	// is full then this task will block for xBlockTime ticks.
+	cValueToPost = 'a';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+	cValueToPost = 'b';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+
+	// ... keep posting characters ... this task may block when the queue
+	// becomes full.
+
+	cValueToPost = 'c';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+ }
+
+ // ISR that outputs all the characters received on the queue.
+ void vISR_Routine( void )
+ {
+ portBASE_TYPE xTaskWokenByReceive = pdFALSE;
+ char cRxedChar;
+
+	while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
+	{
+		// A character was received.  Output the character now.
+		vOutputCharacter( cRxedChar );
+
+		// If removing the character from the queue woke the task that was
+		// posting onto the queue cTaskWokenByReceive will have been set to
+		// pdTRUE.  No matter how many times this loop iterates only one
+		// task will be woken.
+	}
+
+	if( cTaskWokenByPost != ( char ) pdFALSE;
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void * const pvBuffer, signed portBASE_TYPE *pxTaskWoken ); + +/* + * Utilities to query queue that are safe to use from an ISR. These utilities + * should be used only from witin an ISR, or within a critical section. + */ +signed portBASE_TYPE xQueueIsQueueEmptyFromISR( const xQueueHandle pxQueue ); +signed portBASE_TYPE xQueueIsQueueFullFromISR( const xQueueHandle pxQueue ); +unsigned portBASE_TYPE uxQueueMessagesWaitingFromISR( const xQueueHandle pxQueue ); + + +/* + * xQueueAltGenericSend() is an alternative version of xQueueGenericSend(). + * Likewise xQueueAltGenericReceive() is an alternative version of + * xQueueGenericReceive(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +signed portBASE_TYPE xQueueAltGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ); +signed portBASE_TYPE xQueueAltGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ); +#define xQueueAltSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueAltGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) +#define xQueueAltSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueAltGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) +#define xQueueAltReceive( xQueue, pvBuffer, xTicksToWait ) xQueueAltGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE ) +#define xQueueAltPeek( xQueue, pvBuffer, xTicksToWait ) xQueueAltGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) + +/* + * The functions defined above are for passing data to and from tasks. The + * functions below are the equivalents for passing data to and from + * co-routines. + * + * These functions are called from the co-routine macro implementation and + * should not be called directly from application code. Instead use the macro + * wrappers defined within croutine.h. + */ +signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ); +signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ); +signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ); +signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ); + +/* + * For internal use only. Use xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting() instead of calling these functions directly. + */ +xQueueHandle xQueueCreateMutex( void ); +xQueueHandle xQueueCreateCountingSemaphore( unsigned portBASE_TYPE uxCountValue, unsigned portBASE_TYPE uxInitialCount ); + +/* + * For internal use only. Use xSemaphoreTakeMutexRecursive() or + * xSemaphoreGiveMutexRecursive() instead of calling these functions directly. + */ +portBASE_TYPE xQueueTakeMutexRecursive( xQueueHandle pxMutex, portTickType xBlockTime ); +portBASE_TYPE xQueueGiveMutexRecursive( xQueueHandle pxMutex ); + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger. If you are not using a kernel + * aware debugger then this function can be ignored. + * + * configQUEUE_REGISTRY_SIZE defines the maximum number of handles the + * registry can hold. configQUEUE_REGISTRY_SIZE must be greater than 0 + * within FreeRTOSConfig.h for the registry to be available. Its value + * does not effect the number of queues, semaphores and mutexes that can be + * created - just the number that the registry can hold. + * + * @param xQueue The handle of the queue being added to the registry. This + * is the handle returned by a call to xQueueCreate(). Semaphore and mutex + * handles can also be passed in here. + * + * @param pcName The name to be associated with the handle. This is the + * name that the kernel aware debugger will display. + */ +#if configQUEUE_REGISTRY_SIZE > 0U + void vQueueAddToRegistry( xQueueHandle xQueue, signed char *pcName ); +#endif + +/* Not a public API function, hence the 'Restricted' in the name. */ +void vQueueWaitForMessageRestricted( xQueueHandle pxQueue, portTickType xTicksToWait ); + + +#ifdef __cplusplus +} +#endif + +#endif /* QUEUE_H */ + diff --git a/sdk/Src/OS/RTOS/include/rtostimers.h b/sdk/Src/OS/RTOS/include/rtostimers.h new file mode 100644 index 0000000..884d765 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/rtostimers.h @@ -0,0 +1,954 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#ifndef TIMERS_H +#define TIMERS_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include timers.h" +#endif + +#include "portable.h" +#include "rtoslist.h" +#include "task.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* IDs for commands that can be sent/received on the timer queue. These are to +be used solely through the macros that make up the public software timer API, +as defined below. */ +#define tmrCOMMAND_START 0 +#define tmrCOMMAND_STOP 1 +#define tmrCOMMAND_CHANGE_PERIOD 2 +#define tmrCOMMAND_DELETE 3 + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + + /** + * Type by which software timers are referenced. For example, a call to + * xTimerCreate() returns an xTimerHandle variable that can then be used to + * reference the subject timer in calls to other software timer API functions + * (for example, xTimerStart(), xTimerReset(), etc.). + */ +typedef void * xTimerHandle; + +/* Define the prototype to which timer callback functions must conform. */ +//typedef void (*tmrTIMER_CALLBACK)( xTimerHandle xTimer ); +typedef void (*tmrTIMER_CALLBACK)( xTimerHandle xTimer,void *callback_arg ); //modify by dave + +/* The definition of the timers themselves. */ +typedef struct tmrTimerControl +{ + const signed char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ + xListItem xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ + portTickType xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */ + unsigned portBASE_TYPE uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one shot timer. */ + void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ + tmrTIMER_CALLBACK pxCallbackFunction; /*<< The function that will be called when the timer expires. */ + void *callback_arg; /*added by dave */ +} xTIMER; + +/** + * xTimerHandle xTimerCreate( const signed char *pcTimerName, + * portTickType xTimerPeriod, + * unsigned portBASE_TYPE uxAutoReload, + * void * pvTimerID, + * tmrTIMER_CALLBACK pxCallbackFunction ); + * + * Creates a new software timer instance. This allocates the storage required + * by the new timer, initialises the new timers internal state, and returns a + * handle by which the new timer can be referenced. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a timer into the + * active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer by + * its handle, and never by its name. + * + * @param xTimerPeriod The timer period. The time is defined in tick periods so + * the constant portTICK_RATE_MS can be used to convert a time that has been + * specified in milliseconds. For example, if the timer must expire after 100 + * ticks, then xTimerPeriod should be set to 100. Alternatively, if the timer + * must expire after 500ms, then xPeriod can be set to ( 500 / portTICK_RATE_MS ) + * provided configTICK_RATE_HZ is less than or equal to 1000. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriod parameter. If + * uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by tmrTIMER_CALLBACK, + * which is "void vCallbackFunction( xTimerHandle xTimer );". + * + * @return If the timer is successfully create then a handle to the newly + * created timer is returned. If the timer cannot be created (because either + * there is insufficient FreeRTOS heap remaining to allocate the timer + * structures, or the timer period was set to 0) then 0 is returned. + * + * Example usage: + * + * + * #define NUM_TIMERS 5 + * + * // An array to hold handles to the created timers. + * xTimerHandle xTimers[ NUM_TIMERS ]; + * + * // An array to hold a count of the number of times each timer expires. + * long lExpireCounters[ NUM_TIMERS ] = { 0 }; + * + * // Define a callback function that will be used by multiple timer instances. + * // The callback function does nothing but count the number of times the + * // associated timer expires, and stop the timer once the timer has expired + * // 10 times. + * void vTimerCallback( xTimerHandle pxTimer ) + * { + * long lArrayIndex; + * const long xMaxExpiryCountBeforeStopping = 10; + * + * // Optionally do something if the pxTimer parameter is NULL. + * configASSERT( pxTimer ); + * + * // Which timer expired? + * lArrayIndex = ( long ) pvTimerGetTimerID( pxTimer ); + * + * // Increment the number of times that pxTimer has expired. + * lExpireCounters[ lArrayIndex ] += 1; + * + * // If the timer has expired 10 times then stop it from running. + * if( lExpireCounters[ lArrayIndex ] == xMaxExpiryCountBeforeStopping ) + * { + * // Do not use a block time if calling a timer API function from a + * // timer callback function, as doing so could cause a deadlock! + * xTimerStop( pxTimer, 0 ); + * } + * } + * + * void main( void ) + * { + * long x; + * + * // Create then start some timers. Starting the timers before the scheduler + * // has been started means the timers will start running immediately that + * // the scheduler starts. + * for( x = 0; x < NUM_TIMERS; x++ ) + * { + * xTimers[ x ] = xTimerCreate( "Timer", // Just a text name, not used by the kernel. + * ( 100 * x ), // The timer period in ticks. + * pdTRUE, // The timers will auto-reload themselves when they expire. + * ( void * ) x, // Assign each timer a unique id equal to its array index. + * vTimerCallback // Each timer calls the same callback when it expires. + * ); + * + * if( xTimers[ x ] == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xTimers[ x ], 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * xTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + */ +xTimerHandle xTimerCreate( const signed char *pcTimerName, portTickType xTimerPeriodInTicks, unsigned portBASE_TYPE uxAutoReload, void * pvTimerID, tmrTIMER_CALLBACK pxCallbackFunction ) PRIVILEGED_FUNCTION; +xTimerHandle xTimerCreateExt( const signed char *pcTimerName, portTickType xTimerPeriodInTicks, unsigned portBASE_TYPE uxAutoReload, void *pvTimerID, tmrTIMER_CALLBACK pxCallbackFunction, void *callback_arg ); + +/** + * void *pvTimerGetTimerID( xTimerHandle xTimer ); + * + * Returns the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used within the callback function to identify which timer actually + * expired. + * + * @param xTimer The timer being queried. + * + * @return The ID assigned to the timer being queried. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void *pvTimerGetTimerID( xTimerHandle xTimer ) PRIVILEGED_FUNCTION; + +/** + * portBASE_TYPE xTimerIsTimerActive( xTimerHandle xTimer ); + * + * Queries a timer to see if it is active or dormant. + * + * A timer will be dormant if: + * 1) It has been created but not started, or + * 2) It is an expired on-shot timer that has not been restarted. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a timer into the + * active state. + * + * @param xTimer The timer being queried. + * + * @return pdFALSE will be returned if the timer is dormant. A value other than + * pdFALSE will be returned if the timer is active. + * + * Example usage: + * + * // This function assumes xTimer has already been created. + * void vAFunction( xTimerHandle xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is active, do something. + * } + * else + * { + * // xTimer is not active, do something else. + * } + * } + */ +portBASE_TYPE xTimerIsTimerActive( xTimerHandle xTimer ) PRIVILEGED_FUNCTION; + +/** + * xTimerGetTimerDaemonTaskHandle() is only available if + * INCLUDE_xTimerGetTimerDaemonTaskHandle is set to 1 in FreeRTOSConfig.h. + * + * Simply returns the handle of the timer service/daemon task. It it not valid + * to call xTimerGetTimerDaemonTaskHandle() before the scheduler has been started. + */ +xTaskHandle xTimerGetTimerDaemonTaskHandle( void ); + +/** + * portBASE_TYPE xTimerStart( xTimerHandle xTimer, portTickType xBlockTime ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * though a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStart() starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerStart() has equivalent functionality + * to the xTimerReset() API function. + * + * Starting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerStart() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerStart() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerStart() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStart() + * to be available. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param xBlockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the start command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStart() was called. xBlockTime is ignored if xTimerStart() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue even after xBlockTime ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStart( xTimer, xBlockTime ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xBlockTime ) ) + +/** + * portBASE_TYPE xTimerStop( xTimerHandle xTimer, portTickType xBlockTime ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * though a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStop() stops a timer that was previously started using either of the + * The xTimerStart(), xTimerReset(), xTimerStartFromISR(), xTimerResetFromISR(), + * xTimerChangePeriod() or xTimerChangePeriodFromISR() API functions. + * + * Stopping a timer ensures the timer is not in the active state. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStop() + * to be available. + * + * @param xTimer The handle of the timer being stopped. + * + * @param xBlockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the stop command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStop() was called. xBlockTime is ignored if xTimerStop() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue even after xBlockTime ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStop( xTimer, xBlockTime ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xBlockTime ) ) + +/** + * portBASE_TYPE xTimerChangePeriod( xTimerHandle xTimer, + * portTickType xNewPeriod, + * portTickType xBlockTime ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * though a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerChangePeriod() changes the period of a timer that was previously + * created using the xTimerCreate() API function. + * + * xTimerChangePeriod() can be called to change the period of an active or + * dormant state timer. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerChangePeriod() to be available. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_RATE_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_RATE_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param xBlockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the change period command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerChangePeriod() was called. xBlockTime is ignored if + * xTimerChangePeriod() is called before the scheduler is started. + * + * @return pdFAIL will be returned if the change period command could not be + * sent to the timer command queue even after xBlockTime ticks had passed. + * pdPASS will be returned if the command was successfully sent to the timer + * command queue. When the command is actually processed will depend on the + * priority of the timer service/daemon task relative to other tasks in the + * system. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * + * // This function assumes xTimer has already been created. If the timer + * // referenced by xTimer is already active when it is called, then the timer + * // is deleted. If the timer referenced by xTimer is not active when it is + * // called, then the period of the timer is set to 500ms and the timer is + * // started. + * void vAFunction( xTimerHandle xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is already active - delete it. + * xTimerDelete( xTimer ); + * } + * else + * { + * // xTimer is not active, change its period to 500ms. This will also + * // cause the timer to start. Block for a maximum of 100 ticks if the + * // change period command cannot immediately be sent to the timer + * // command queue. + * if( xTimerChangePeriod( xTimer, 500 / portTICK_RATE_MS, 100 ) == pdPASS ) + * { + * // The command was successfully sent. + * } + * else + * { + * // The command could not be sent, even after waiting for 100 ticks + * // to pass. Take appropriate action here. + * } + * } + * } + */ + #define xTimerChangePeriod( xTimer, xNewPeriod, xBlockTime ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xBlockTime ) ) + +/** + * portBASE_TYPE xTimerDelete( xTimerHandle xTimer, portTickType xBlockTime ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * though a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerDelete() deletes a timer that was previously created using the + * xTimerCreate() API function. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerDelete() to be available. + * + * @param xTimer The handle of the timer being deleted. + * + * @param xBlockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the delete command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerDelete() was called. xBlockTime is ignored if xTimerDelete() + * is called before the scheduler is started. + * + * @return pdFAIL will be returned if the delete command could not be sent to + * the timer command queue even after xBlockTime ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerChangePeriod() API function example usage scenario. + */ +#define xTimerDelete( xTimer, xBlockTime ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xBlockTime ) ) + +/** + * portBASE_TYPE xTimerReset( xTimerHandle xTimer, portTickType xBlockTime ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * though a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerReset() re-starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerReset() will cause the timer to + * re-evaluate its expiry time so that it is relative to when xTimerReset() was + * called. If the timer was in the dormant state then xTimerReset() has + * equivalent functionality to the xTimerStart() API function. + * + * Resetting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerReset() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerReset() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerReset() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerReset() + * to be available. + * + * @param xTimer The handle of the timer being reset/started/restarted. + * + * @param xBlockTime Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the reset command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerReset() was called. xBlockTime is ignored if xTimerReset() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue even after xBlockTime ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer. + * + * xTimerHandle xBacklightTimer = NULL; + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( xTimerHandle pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press event handler. + * void vKeyPressEventHandler( char cKey ) + * { + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. Wait 10 ticks for the command to be successfully sent + * // if it cannot be sent immediately. + * vSetBacklightState( BACKLIGHT_ON ); + * if( xTimerReset( xBacklightTimer, 100 ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * } + * + * void main( void ) + * { + * long x; + * + * // Create then start the one-shot timer that is responsible for turning + * // the back-light off if no keys are pressed within a 5 second period. + * xBacklightTimer = xTimerCreate( "BacklightTimer", // Just a text name, not used by the kernel. + * ( 5000 / portTICK_RATE_MS), // The timer period in ticks. + * pdFALSE, // The timer is a one-shot timer. + * 0, // The id is not used by the callback so can take any value. + * vBacklightTimerCallback // The callback function that switches the LCD back-light off. + * ); + * + * if( xBacklightTimer == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xBacklightTimer, 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timer running as it has already + * // been set into the active state. + * xTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + */ +#define xTimerReset( xTimer, xBlockTime ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xBlockTime ) ) + +/** + * portBASE_TYPE xTimerStartFromISR( xTimerHandle xTimer, + * portBASE_TYPE *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStart() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStartFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStartFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStartFromISR() function. If + * xTimerStartFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerStartFromISR() is actually called. The timer service/daemon + * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( xTimerHandle pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then restart the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerStartFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The start command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used.{ + * } + * } + */ +#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * portBASE_TYPE xTimerStopFromISR( xTimerHandle xTimer, + * portBASE_TYPE *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStop() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being stopped. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStopFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStopFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStopFromISR() function. If + * xTimerStopFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the timer should be simply stopped. + * + * // The interrupt service routine that stops the timer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - simply stop the timer. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerStopFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The stop command was not executed successfully. Take appropriate + * // action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used. + * } + * } + */ +#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0, ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * portBASE_TYPE xTimerChangePeriodFromISR( xTimerHandle xTimer, + * portTickType xNewPeriod, + * portBASE_TYPE *pxHigherPriorityTaskWoken ); + * + * A version of xTimerChangePeriod() that can be called from an interrupt + * service routine. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_RATE_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_RATE_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerChangePeriodFromISR() writes a message to the + * timer command queue, so has the potential to transition the timer service/ + * daemon task out of the Blocked state. If calling xTimerChangePeriodFromISR() + * causes the timer service/daemon task to leave the Blocked state, and the + * timer service/daemon task has a priority equal to or greater than the + * currently executing task (the task that was interrupted), then + * *pxHigherPriorityTaskWoken will get set to pdTRUE internally within the + * xTimerChangePeriodFromISR() function. If xTimerChangePeriodFromISR() sets + * this value to pdTRUE then a context switch should be performed before the + * interrupt exits. + * + * @return pdFAIL will be returned if the command to change the timers period + * could not be sent to the timer command queue. pdPASS will be returned if the + * command was successfully sent to the timer command queue. When the command + * is actually processed will depend on the priority of the timer service/daemon + * task relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the period of xTimer should be changed to 500ms. + * + * // The interrupt service routine that changes the period of xTimer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - change the period of xTimer to 500ms. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerChangePeriodFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The command to change the timers period was not executed + * // successfully. Take appropriate action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used. + * } + * } + */ +#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * portBASE_TYPE xTimerResetFromISR( xTimerHandle xTimer, + * portBASE_TYPE *pxHigherPriorityTaskWoken ); + * + * A version of xTimerReset() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer that is to be started, reset, or + * restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerResetFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerResetFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerResetFromISR() function. If + * xTimerResetFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerResetFromISR() is actually called. The timer service/daemon + * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( xTimerHandle pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerResetFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used. + * } + * } + */ +#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + +/* + * Functions beyond this part are not part of the public API and are intended + * for use by the kernel only. + */ +portBASE_TYPE xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; +portBASE_TYPE xTimerGenericCommand( xTimerHandle xTimer, portBASE_TYPE xCommandID, portTickType xOptionalValue, portBASE_TYPE *pxHigherPriorityTaskWoken, portTickType xBlockTime ) PRIVILEGED_FUNCTION; + +#ifdef __cplusplus +} +#endif +#endif /* TIMERS_H */ + + + diff --git a/sdk/Src/OS/RTOS/include/semphr.h b/sdk/Src/OS/RTOS/include/semphr.h new file mode 100644 index 0000000..6afd3f2 --- /dev/null +++ b/sdk/Src/OS/RTOS/include/semphr.h @@ -0,0 +1,724 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#ifndef INC_FREERTOS_H + #error "#include FreeRTOS.h" must appear in source files before "#include semphr.h" +#endif + +#include "rtosqueue.h" + +typedef xQueueHandle xSemaphoreHandle; + +#define semBINARY_SEMAPHORE_QUEUE_LENGTH ( ( unsigned char ) 1U ) +#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( unsigned char ) 0U ) +#define semGIVE_BLOCK_TIME ( ( portTickType ) 0U ) + + +/** + * semphr. h + *
vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore )
+ * + * Macro that implements a semaphore by using the existing queue mechanism. + * The queue length is 1 as this is a binary semaphore. The data size is 0 + * as we don't want to actually store any data - we just want to know if the + * queue is empty or full. + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @param xSemaphore Handle to the created semaphore. Should be of type xSemaphoreHandle. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
+    // This is a macro so pass the variable in directly.
+    vSemaphoreCreateBinary( xSemaphore );
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * \ingroup Semaphores + */ +#define vSemaphoreCreateBinary( xSemaphore ) { \ + ( xSemaphore ) = xQueueCreate( ( unsigned portBASE_TYPE ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH ); \ + if( ( xSemaphore ) != NULL ) \ + { \ + xSemaphoreGive( ( xSemaphore ) ); \ + } \ + } + +/** + * semphr. h + *
xSemaphoreTake( 
+ *                   xSemaphoreHandle xSemaphore, 
+ *                   portTickType xBlockTime 
+ *               )
+ * + * Macro to obtain a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting(). + * + * @param xSemaphore A handle to the semaphore being taken - obtained when + * the semaphore was created. + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_RATE_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. A block + * time of portMAX_DELAY can be used to block indefinitely (provided + * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h). + * + * @return pdTRUE if the semaphore was obtained. pdFALSE + * if xBlockTime expired without the semaphore becoming available. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore = NULL;
+
+ // A task that creates a semaphore.
+ void vATask( void * pvParameters )
+ {
+    // Create the semaphore to guard a shared resource.
+    vSemaphoreCreateBinary( xSemaphore );
+ }
+
+ // A task that uses the semaphore.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xSemaphore != NULL )
+    {
+        // See if we can obtain the semaphore.  If the semaphore is not available
+        // wait 10 ticks to see if it becomes free.	
+        if( xSemaphoreTake( xSemaphore, ( portTickType ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the semaphore and can now access the
+            // shared resource.
+
+            // ...
+
+            // We have finished accessing the shared resource.  Release the 
+            // semaphore.
+            xSemaphoreGive( xSemaphore );
+        }
+        else
+        {
+            // We could not obtain the semaphore and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreTake xSemaphoreTake + * \ingroup Semaphores + */ +#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( xQueueHandle ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) + +/** + * semphr. h + * xSemaphoreTakeRecursive( + * xSemaphoreHandle xMutex, + * portTickType xBlockTime + * ) + * + * Macro to recursively obtain, or 'take', a mutex type semaphore. + * The mutex must have previously been created using a call to + * xSemaphoreCreateRecursiveMutex(); + * + * configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h for this + * macro to be available. + * + * This macro must not be used on mutexes created using xSemaphoreCreateMutex(). + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * @param xMutex A handle to the mutex being obtained. This is the + * handle returned by xSemaphoreCreateRecursiveMutex(); + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_RATE_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. If + * the task already owns the semaphore then xSemaphoreTakeRecursive() will + * return immediately no matter what the value of xBlockTime. + * + * @return pdTRUE if the semaphore was obtained. pdFALSE if xBlockTime + * expired without the semaphore becoming available. + * + * Example usage: +
+ xSemaphoreHandle xMutex = NULL;
+
+ // A task that creates a mutex.
+ void vATask( void * pvParameters )
+ {
+    // Create the mutex to guard a shared resource.
+    xMutex = xSemaphoreCreateRecursiveMutex();
+ }
+
+ // A task that uses the mutex.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xMutex != NULL )
+    {
+        // See if we can obtain the mutex.  If the mutex is not available
+        // wait 10 ticks to see if it becomes free.	
+        if( xSemaphoreTakeRecursive( xSemaphore, ( portTickType ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the mutex and can now access the
+            // shared resource.
+
+            // ...
+            // For some reason due to the nature of the code further calls to 
+			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
+			// code these would not be just sequential calls as this would make
+			// no sense.  Instead the calls are likely to be buried inside
+			// a more complex call structure.
+            xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
+            xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
+
+            // The mutex has now been 'taken' three times, so will not be 
+			// available to another task until it has also been given back
+			// three times.  Again it is unlikely that real code would have
+			// these calls sequentially, but instead buried in a more complex
+			// call structure.  This is just for illustrative purposes.
+            xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+
+			// Now the mutex can be taken by other tasks.
+        }
+        else
+        {
+            // We could not obtain the mutex and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreTakeRecursive xSemaphoreTakeRecursive + * \ingroup Semaphores + */ +#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) ) + + +/* + * xSemaphoreAltTake() is an alternative version of xSemaphoreTake(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +#define xSemaphoreAltTake( xSemaphore, xBlockTime ) xQueueAltGenericReceive( ( xQueueHandle ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) + +/** + * semphr. h + *
xSemaphoreGive( xSemaphoreHandle xSemaphore )
+ * + * Macro to release a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting(). and obtained using sSemaphoreTake(). + * + * This macro must not be used from an ISR. See xSemaphoreGiveFromISR () for + * an alternative which can be used from an ISR. + * + * This macro must also not be used on semaphores created using + * xSemaphoreCreateRecursiveMutex(). + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned when the semaphore was created. + * + * @return pdTRUE if the semaphore was released. pdFALSE if an error occurred. + * Semaphores are implemented using queues. An error can occur if there is + * no space on the queue to post a message - indicating that the + * semaphore was not first obtained correctly. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore = NULL;
+
+ void vATask( void * pvParameters )
+ {
+    // Create the semaphore to guard a shared resource.
+    vSemaphoreCreateBinary( xSemaphore );
+
+    if( xSemaphore != NULL )
+    {
+        if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+        {
+            // We would expect this call to fail because we cannot give
+            // a semaphore without first "taking" it!
+        }
+
+        // Obtain the semaphore - don't block if the semaphore is not
+        // immediately available.
+        if( xSemaphoreTake( xSemaphore, ( portTickType ) 0 ) )
+        {
+            // We now have the semaphore and can access the shared resource.
+
+            // ...
+
+            // We have finished accessing the shared resource so can free the
+            // semaphore.
+            if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+            {
+                // We would not expect this call to fail because we must have
+                // obtained the semaphore to get here.
+            }
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreGive xSemaphoreGive + * \ingroup Semaphores + */ +#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( xQueueHandle ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) + +/** + * semphr. h + *
xSemaphoreGiveRecursive( xSemaphoreHandle xMutex )
+ * + * Macro to recursively release, or 'give', a mutex type semaphore. + * The mutex must have previously been created using a call to + * xSemaphoreCreateRecursiveMutex(); + * + * configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h for this + * macro to be available. + * + * This macro must not be used on mutexes created using xSemaphoreCreateMutex(). + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * @param xMutex A handle to the mutex being released, or 'given'. This is the + * handle returned by xSemaphoreCreateMutex(); + * + * @return pdTRUE if the semaphore was given. + * + * Example usage: +
+ xSemaphoreHandle xMutex = NULL;
+
+ // A task that creates a mutex.
+ void vATask( void * pvParameters )
+ {
+    // Create the mutex to guard a shared resource.
+    xMutex = xSemaphoreCreateRecursiveMutex();
+ }
+
+ // A task that uses the mutex.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xMutex != NULL )
+    {
+        // See if we can obtain the mutex.  If the mutex is not available
+        // wait 10 ticks to see if it becomes free.	
+        if( xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the mutex and can now access the
+            // shared resource.
+
+            // ...
+            // For some reason due to the nature of the code further calls to 
+			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
+			// code these would not be just sequential calls as this would make
+			// no sense.  Instead the calls are likely to be buried inside
+			// a more complex call structure.
+            xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
+            xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
+
+            // The mutex has now been 'taken' three times, so will not be 
+			// available to another task until it has also been given back
+			// three times.  Again it is unlikely that real code would have
+			// these calls sequentially, it would be more likely that the calls
+			// to xSemaphoreGiveRecursive() would be called as a call stack
+			// unwound.  This is just for demonstrative purposes.
+            xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+
+			// Now the mutex can be taken by other tasks.
+        }
+        else
+        {
+            // We could not obtain the mutex and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreGiveRecursive xSemaphoreGiveRecursive + * \ingroup Semaphores + */ +#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) ) + +/* + * xSemaphoreAltGive() is an alternative version of xSemaphoreGive(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +#define xSemaphoreAltGive( xSemaphore ) xQueueAltGenericSend( ( xQueueHandle ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) + +/** + * semphr. h + *
+ xSemaphoreGiveFromISR( 
+                          xSemaphoreHandle xSemaphore, 
+                          signed portBASE_TYPE *pxHigherPriorityTaskWoken
+                      )
+ * + * Macro to release a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary() or xSemaphoreCreateCounting(). + * + * Mutex type semaphores (those created using a call to xSemaphoreCreateMutex()) + * must not be used with this macro. + * + * This macro can be used from an ISR. + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned when the semaphore was created. + * + * @param pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if giving the semaphore caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xSemaphoreGiveFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL. + * + * Example usage: +
+ \#define LONG_TIME 0xffff
+ \#define TICKS_TO_WAIT	10
+ xSemaphoreHandle xSemaphore = NULL;
+
+ // Repetitive task.
+ void vATask( void * pvParameters )
+ {
+    for( ;; )
+    {
+        // We want this task to run every 10 ticks of a timer.  The semaphore 
+        // was created before this task was started.
+
+        // Block waiting for the semaphore to become available.
+        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
+        {
+            // It is time to execute.
+
+            // ...
+
+            // We have finished our task.  Return to the top of the loop where
+            // we will block on the semaphore until it is time to execute 
+            // again.  Note when using the semaphore for synchronisation with an
+			// ISR in this manner there is no need to 'give' the semaphore back.
+        }
+    }
+ }
+
+ // Timer ISR
+ void vTimerISR( void * pvParameters )
+ {
+ static unsigned char ucLocalTickCount = 0;
+ static signed portBASE_TYPE xHigherPriorityTaskWoken;
+
+    // A timer tick has occurred.
+
+    // ... Do other time functions.
+
+    // Is it time for vATask () to run?
+	xHigherPriorityTaskWoken = pdFALSE;
+    ucLocalTickCount++;
+    if( ucLocalTickCount >= TICKS_TO_WAIT )
+    {
+        // Unblock the task by releasing the semaphore.
+        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
+
+        // Reset the count so we release the semaphore again in 10 ticks time.
+        ucLocalTickCount = 0;
+    }
+
+    if( xHigherPriorityTaskWoken != pdFALSE )
+    {
+        // We can force a context switch here.  Context switching from an
+        // ISR uses port specific syntax.  Check the demo task for your port
+        // to find the syntax required.
+    }
+ }
+ 
+ * \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR + * \ingroup Semaphores + */ +#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueueHandle ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * semphr. h + *
xSemaphoreHandle xSemaphoreCreateMutex( void )
+ * + * Macro that implements a mutex semaphore by using the existing queue + * mechanism. + * + * Mutexes created using this macro can be accessed using the xSemaphoreTake() + * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and + * xSemaphoreGiveRecursive() macros should not be used. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See vSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @return xSemaphore Handle to the created mutex semaphore. Should be of type + * xSemaphoreHandle. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
+    // This is a macro so pass the variable in directly.
+    xSemaphore = xSemaphoreCreateMutex();
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * \ingroup Semaphores + */ +#define xSemaphoreCreateMutex() xQueueCreateMutex() + + +/** + * semphr. h + *
xSemaphoreHandle xSemaphoreCreateRecursiveMutex( void )
+ * + * Macro that implements a recursive mutex by using the existing queue + * mechanism. + * + * Mutexes created using this macro can be accessed using the + * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The + * xSemaphoreTake() and xSemaphoreGive() macros should not be used. + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See vSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @return xSemaphore Handle to the created mutex semaphore. Should be of type + * xSemaphoreHandle. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
+    // This is a macro so pass the variable in directly.
+    xSemaphore = xSemaphoreCreateRecursiveMutex();
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * \ingroup Semaphores + */ +#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex() + +/** + * semphr. h + *
xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount, unsigned portBASE_TYPE uxInitialCount )
+ * + * Macro that creates a counting semaphore by using the existing + * queue mechanism. + * + * Counting semaphores are typically used for two things: + * + * 1) Counting events. + * + * In this usage scenario an event handler will 'give' a semaphore each time + * an event occurs (incrementing the semaphore count value), and a handler + * task will 'take' a semaphore each time it processes an event + * (decrementing the semaphore count value). The count value is therefore + * the difference between the number of events that have occurred and the + * number that have been processed. In this case it is desirable for the + * initial count value to be zero. + * + * 2) Resource management. + * + * In this usage scenario the count value indicates the number of resources + * available. To obtain control of a resource a task must first obtain a + * semaphore - decrementing the semaphore count value. When the count value + * reaches zero there are no free resources. When a task finishes with the + * resource it 'gives' the semaphore back - incrementing the semaphore count + * value. In this case it is desirable for the initial count value to be + * equal to the maximum count value, indicating that all resources are free. + * + * @param uxMaxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * + * @param uxInitialCount The count value assigned to the semaphore when it is + * created. + * + * @return Handle to the created semaphore. Null if the semaphore could not be + * created. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+ xSemaphoreHandle xSemaphore = NULL;
+
+    // Semaphore cannot be used before a call to xSemaphoreCreateCounting().
+    // The max value to which the semaphore can count should be 10, and the
+    // initial value assigned to the count should be 0.
+    xSemaphore = xSemaphoreCreateCounting( 10, 0 );
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting + * \ingroup Semaphores + */ +#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) + +/** + * semphr. h + *
void vSemaphoreDelete( xSemaphoreHandle xSemaphore );
+ * + * Delete a semaphore. This function must be used with care. For example, + * do not delete a mutex type semaphore if the mutex is held by a task. + * + * @param xSemaphore A handle to the semaphore to be deleted. + * + * \page vSemaphoreDelete vSemaphoreDelete + * \ingroup Semaphores + */ +#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( xQueueHandle ) xSemaphore ) + +#endif /* SEMAPHORE_H */ + + diff --git a/sdk/Src/OS/RTOS/include/task.h b/sdk/Src/OS/RTOS/include/task.h new file mode 100644 index 0000000..45136ef --- /dev/null +++ b/sdk/Src/OS/RTOS/include/task.h @@ -0,0 +1,1317 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#ifndef TASK_H +#define TASK_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include task.h" +#endif + +#include "portable.h" +#include "rtoslist.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + +#define tskKERNEL_VERSION_NUMBER "V7.0.1" + +/** + * task. h + * + * Type by which tasks are referenced. For example, a call to xTaskCreate + * returns (via a pointer parameter) an xTaskHandle variable that can then + * be used as a parameter to vTaskDelete to delete the task. + * + * \page xTaskHandle xTaskHandle + * \ingroup Tasks + */ +typedef void * xTaskHandle; + +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + portBASE_TYPE xOverflowCount; + portTickType xTimeOnEntering; +} xTimeOutType; + +/* + * Defines the memory ranges allocated to the task when an MPU is used. + */ +typedef struct xMEMORY_REGION +{ + void *pvBaseAddress; + unsigned long ulLengthInBytes; + unsigned long ulParameters; +} xMemoryRegion; + +/* + * Parameters required to create an MPU protected task. + */ +typedef struct xTASK_PARAMTERS +{ + pdTASK_CODE pvTaskCode; + const signed char * const pcName; + unsigned short usStackDepth; + void *pvParameters; + unsigned portBASE_TYPE uxPriority; + portSTACK_TYPE *puxStackBuffer; + xMemoryRegion xRegions[ portNUM_CONFIGURABLE_REGIONS ]; +} xTaskParameters; + +/* + * Defines the priority used by the idle task. This must not be modified. + * + * \ingroup TaskUtils + */ +#define tskIDLE_PRIORITY ( ( unsigned portBASE_TYPE ) 0U ) + +/** + * task. h + * + * Macro for forcing a context switch. + * + * \page taskYIELD taskYIELD + * \ingroup SchedulerControl + */ +#define taskYIELD() portYIELD() + +/** + * task. h + * + * Macro to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \page taskENTER_CRITICAL taskENTER_CRITICAL + * \ingroup SchedulerControl + */ +#define taskENTER_CRITICAL() portENTER_CRITICAL() + +/** + * task. h + * + * Macro to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \page taskEXIT_CRITICAL taskEXIT_CRITICAL + * \ingroup SchedulerControl + */ +#define taskEXIT_CRITICAL() portEXIT_CRITICAL() + +extern void portDISABLE_INTERRUPTS(void); +extern void portENABLE_INTERRUPTS(void); + +extern void freeRtosTickIrqHandler( void ); +/** + * task. h + * + * Macro to disable all maskable interrupts. + * + * \page taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + +/** + * task. h + * + * Macro to enable microcontroller interrupts. + * + * \page taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + +/* Definitions returned by xTaskGetSchedulerState(). */ +#define taskSCHEDULER_NOT_STARTED 0 +#define taskSCHEDULER_RUNNING 1 +#define taskSCHEDULER_SUSPENDED 2 + +/*----------------------------------------------------------- + * TASK CREATION API + *----------------------------------------------------------*/ + +/** + * task. h + *
+ portBASE_TYPE xTaskCreate(
+							  pdTASK_CODE pvTaskCode,
+							  const char * const pcName,
+							  unsigned short usStackDepth,
+							  void *pvParameters,
+							  unsigned portBASE_TYPE uxPriority,
+							  xTaskHandle *pvCreatedTask
+						  );
+ * + * Create a new task and add it to the list of tasks that are ready to run. + * + * xTaskCreate() can only be used to create a task that has unrestricted + * access to the entire microcontroller memory map. Systems that include MPU + * support can alternatively create an MPU constrained task using + * xTaskCreateRestricted(). + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by tskMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pvCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file errors. h + * + * Example usage: +
+ // Task to be created.
+ void vTaskCode( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+	 }
+ }
+
+ // Function that creates a task.
+ void vOtherFunction( void )
+ {
+ static unsigned char ucParameterToPass;
+ xTaskHandle xHandle;
+
+	 // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
+	 // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
+	 // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
+	 // the new task attempts to access it.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
+
+	 // Use the handle to delete the task.
+	 vTaskDelete( xHandle );
+ }
+   
+ * \defgroup xTaskCreate xTaskCreate + * \ingroup Tasks + */ +#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ) ) +#define xTaskCreateExt( pvTaskCode, pcName, puxStackBuffer,usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( puxStackBuffer ), ( NULL ) ) + +/** + * task. h + *
+ portBASE_TYPE xTaskCreateRestricted( xTaskParameters *pxTaskDefinition, xTaskHandle *pxCreatedTask );
+ * + * xTaskCreateRestricted() should only be used in systems that include an MPU + * implementation. + * + * Create a new task and add it to the list of tasks that are ready to run. + * The function parameters define the memory regions and associated access + * permissions allocated to the task. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file errors. h + * + * Example usage: +
+// Create an xTaskParameters structure that defines the task to be created.
+static const xTaskParameters xCheckTaskParameters =
+{
+	vATask,		// pvTaskCode - the function that implements the task.
+	"ATask",	// pcName - just a text name for the task to assist debugging.
+	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
+	NULL,		// pvParameters - passed into the task function as the function parameters.
+	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+
+	// xRegions - Allocate up to three separate memory regions for access by
+	// the task, with appropriate access permissions.  Different processors have
+	// different memory alignment requirements - refer to the FreeRTOS documentation
+	// for full information.
+	{											
+		// Base address					Length	Parameters
+        { cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
+        { cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
+        { cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
+	}
+};
+
+int main( void )
+{
+xTaskHandle xHandle;
+
+	// Create a task from the const structure defined above.  The task handle
+	// is requested (the second parameter is not NULL) but in this case just for
+	// demonstration purposes as its not actually used.
+	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+
+	// Start the scheduler.
+	vTaskStartScheduler();
+
+	// Will only get here if there was insufficient memory to create the idle
+	// task.
+	for( ;; );
+}
+   
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +#define xTaskCreateRestricted( x, pxCreatedTask ) xTaskGenericCreate( ((x)->pvTaskCode), ((x)->pcName), ((x)->usStackDepth), ((x)->pvParameters), ((x)->uxPriority), (pxCreatedTask), ((x)->puxStackBuffer), ((x)->xRegions) ) + +/** + * task. h + *
+ void vTaskAllocateMPURegions( xTaskHandle xTask, const xMemoryRegion * const pxRegions );
+ * + * Memory regions are assigned to a restricted task when the task is created by + * a call to xTaskCreateRestricted(). These regions can be redefined using + * vTaskAllocateMPURegions(). + * + * @param xTask The handle of the task being updated. + * + * @param xRegions A pointer to an xMemoryRegion structure that contains the + * new memory region definitions. + * + * Example usage: +
+// Define an array of xMemoryRegion structures that configures an MPU region
+// allowing read/write access for 1024 bytes starting at the beginning of the
+// ucOneKByte array.  The other two of the maximum 3 definable regions are
+// unused so set to zero.
+static const xMemoryRegion xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] =
+{											
+	// Base address		Length		Parameters
+	{ ucOneKByte,		1024,		portMPU_REGION_READ_WRITE },
+	{ 0,				0,			0 },
+	{ 0,				0,			0 }
+};
+
+void vATask( void *pvParameters )
+{
+	// This task was created such that it has access to certain regions of
+	// memory as defined by the MPU configuration.  At some point it is
+	// desired that these MPU regions are replaced with that defined in the
+	// xAltRegions const struct above.  Use a call to vTaskAllocateMPURegions()
+	// for this purpose.  NULL is used as the task handle to indicate that this
+	// function should modify the MPU regions of the calling task.
+	vTaskAllocateMPURegions( NULL, xAltRegions );
+	
+	// Now the task can continue its function, but from this point on can only
+	// access its stack and the ucOneKByte array (unless any other statically
+	// defined or shared regions have been declared elsewhere).
+}
+   
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +void vTaskAllocateMPURegions( xTaskHandle xTask, const xMemoryRegion * const pxRegions ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskDelete( xTaskHandle pxTask );
+ * + * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Remove a task from the RTOS real time kernels management. The task being + * deleted will be removed from all ready, blocked, suspended and event lists. + * + * NOTE: The idle task is responsible for freeing the kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to vTaskDelete (). Memory allocated by the + * task code is not automatically freed, and should be freed before the task + * is deleted. + * + * See the demo application file death.c for sample code that utilises + * vTaskDelete (). + * + * @param pxTask The handle of the task to be deleted. Passing NULL will + * cause the calling task to be deleted. + * + * Example usage: +
+ void vOtherFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create the task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // Use the handle to delete the task.
+	 vTaskDelete( xHandle );
+ }
+   
+ * \defgroup vTaskDelete vTaskDelete + * \ingroup Tasks + */ +void vTaskDelete( xTaskHandle pxTaskToDelete ) PRIVILEGED_FUNCTION; + +unsigned portBASE_TYPE vTaskDeleteByPriority(unsigned portBASE_TYPE prio); + +/*----------------------------------------------------------- + * TASK CONTROL API + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskDelay( portTickType xTicksToDelay );
+ * + * Delay a task for a given number of ticks. The actual time that the + * task remains blocked depends on the tick rate. The constant + * portTICK_RATE_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * + * vTaskDelay() specifies a time at which the task wishes to unblock relative to + * the time at which vTaskDelay() is called. For example, specifying a block + * period of 100 ticks will cause the task to unblock 100 ticks after + * vTaskDelay() is called. vTaskDelay() does not therefore provide a good method + * of controlling the frequency of a cyclical task as the path taken through the + * code, as well as other task and interrupt activity, will effect the frequency + * at which vTaskDelay() gets called and therefore the time at which the task + * next executes. See vTaskDelayUntil() for an alternative API function designed + * to facilitate fixed frequency execution. It does this by specifying an + * absolute time (rather than a relative time) at which the calling task should + * unblock. + * + * @param xTicksToDelay The amount of time, in tick periods, that + * the calling task should block. + * + * Example usage: + + void vTaskFunction( void * pvParameters ) + { + void vTaskFunction( void * pvParameters ) + { + // Block for 500ms. + const portTickType xDelay = 500 / portTICK_RATE_MS; + + for( ;; ) + { + // Simply toggle the LED every 500ms, blocking between each toggle. + vToggleLED(); + vTaskDelay( xDelay ); + } + } + + * \defgroup vTaskDelay vTaskDelay + * \ingroup TaskCtrl + */ +void vTaskDelay( portTickType xTicksToDelay ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncrement );
+ * + * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by cyclical + * tasks to ensure a constant execution frequency. + * + * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will + * cause a task to block for the specified number of ticks from the time vTaskDelay () is + * called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed + * execution frequency as the time between a task starting to execute and that task + * calling vTaskDelay () may not be fixed [the task may take a different path though the + * code between calls, or may get interrupted or preempted a different number of times + * each time it executes]. + * + * Whereas vTaskDelay () specifies a wake time relative to the time at which the function + * is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to + * unblock. + * + * The constant portTICK_RATE_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the + * task was last unblocked. The variable must be initialised with the current time + * prior to its first use (see the example below). Following this the variable is + * automatically updated within vTaskDelayUntil (). + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Calling vTaskDelayUntil with the + * same xTimeIncrement parameter value will cause the task to execute with + * a fixed interface period. + * + * Example usage: +
+ // Perform an action every 10 ticks.
+ void vTaskFunction( void * pvParameters )
+ {
+ portTickType xLastWakeTime;
+ const portTickType xFrequency = 10;
+
+	 // Initialise the xLastWakeTime variable with the current time.
+	 xLastWakeTime = xTaskGetTickCount ();
+	 for( ;; )
+	 {
+		 // Wait for the next cycle.
+		 vTaskDelayUntil( &xLastWakeTime, xFrequency );
+
+		 // Perform action here.
+	 }
+ }
+   
+ * \defgroup vTaskDelayUntil vTaskDelayUntil + * \ingroup TaskCtrl + */ +void vTaskDelayUntil( portTickType * const pxPreviousWakeTime, portTickType xTimeIncrement ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );
+ * + * INCLUDE_xTaskPriorityGet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the priority of any task. + * + * @param pxTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of pxTask. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to obtain the priority of the created task.
+	 // It was created with tskIDLE_PRIORITY, but may have changed
+	 // it itself.
+	 if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
+	 {
+		 // The task has changed it's priority.
+	 }
+
+	 // ...
+
+	 // Is our priority higher than the created task?
+	 if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
+	 {
+		 // Our priority (obtained using NULL handle) is higher.
+	 }
+ }
+   
+ * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * \ingroup TaskCtrl + */ +unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );
+ * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Set the priority of any task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param pxTask Handle to the task for which the priority is being set. + * Passing a NULL handle results in the priority of the calling task being set. + * + * @param uxNewPriority The priority to which the task will be set. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to raise the priority of the created task.
+	 vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
+
+	 // ...
+
+	 // Use a NULL handle to raise our priority to the same value.
+	 vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
+ }
+   
+ * \defgroup vTaskPrioritySet vTaskPrioritySet + * \ingroup TaskCtrl + */ +void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskSuspend( xTaskHandle pxTaskToSuspend );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Suspend any task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to vTaskSuspend are not accumulative - + * i.e. calling vTaskSuspend () twice on the same task still only requires one + * call to vTaskResume () to ready the suspended task. + * + * @param pxTaskToSuspend Handle to the task being suspended. Passing a NULL + * handle will cause the calling task to be suspended. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to suspend the created task.
+	 vTaskSuspend( xHandle );
+
+	 // ...
+
+	 // The created task will not run during this period, unless
+	 // another task calls vTaskResume( xHandle ).
+
+	 //...
+
+
+	 // Suspend ourselves.
+	 vTaskSuspend( NULL );
+
+	 // We cannot get here unless another task calls vTaskResume
+	 // with our handle as the parameter.
+ }
+   
+ * \defgroup vTaskSuspend vTaskSuspend + * \ingroup TaskCtrl + */ +void vTaskSuspend( xTaskHandle pxTaskToSuspend ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskResume( xTaskHandle pxTaskToResume );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one of more calls to vTaskSuspend () + * will be made available for running again by a single call to + * vTaskResume (). + * + * @param pxTaskToResume Handle to the task being readied. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to suspend the created task.
+	 vTaskSuspend( xHandle );
+
+	 // ...
+
+	 // The created task will not run during this period, unless
+	 // another task calls vTaskResume( xHandle ).
+
+	 //...
+
+
+	 // Resume the suspended task ourselves.
+	 vTaskResume( xHandle );
+
+	 // The created task will once again get microcontroller processing
+	 // time in accordance with it priority within the system.
+ }
+   
+ * \defgroup vTaskResume vTaskResume + * \ingroup TaskCtrl + */ +void vTaskResume( xTaskHandle pxTaskToResume ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void xTaskResumeFromISR( xTaskHandle pxTaskToResume );
+ * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of vTaskResume() that can be called from within an ISR. + * + * A task that has been suspended by one of more calls to vTaskSuspend () + * will be made available for running again by a single call to + * xTaskResumeFromISR (). + * + * @param pxTaskToResume Handle to the task being readied. + * + * \defgroup vTaskResumeFromISR vTaskResumeFromISR + * \ingroup TaskCtrl + */ +portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * SCHEDULER CONTROL + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskStartScheduler( void );
+ * + * Starts the real time kernel tick processing. After calling the kernel + * has control over which tasks are executed and when. This function + * does not return until an executing task calls vTaskEndScheduler (). + * + * At least one task should be created via a call to xTaskCreate () + * before calling vTaskStartScheduler (). The idle task is created + * automatically when the first application task is created. + * + * See the demo application file main.c for an example of creating + * tasks and starting the kernel. + * + * Example usage: +
+ void vAFunction( void )
+ {
+	 // Create at least one task before starting the kernel.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+	 // Start the real time kernel with preemption.
+	 vTaskStartScheduler ();
+
+	 // Will not get here unless a task calls vTaskEndScheduler ()
+ }
+   
+ * + * \defgroup vTaskStartScheduler vTaskStartScheduler + * \ingroup SchedulerControl + */ +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskEndScheduler( void );
+ * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will + * stop. Execution then resumes from the point where vTaskStartScheduler () + * was called, as if vTaskStartScheduler () had just returned. + * + * See the demo application file main. c in the demo/PC directory for an + * example that uses vTaskEndScheduler (). + * + * vTaskEndScheduler () requires an exit function to be defined within the + * portable layer (see vPortEndScheduler () in port. c for the PC port). This + * performs hardware specific operations such as stopping the kernel tick. + * + * vTaskEndScheduler () will cause all of the resources allocated by the + * kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example usage: +
+ void vTaskCode( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // At some point we want to end the real time kernel processing
+		 // so call ...
+		 vTaskEndScheduler ();
+	 }
+ }
+
+ void vAFunction( void )
+ {
+	 // Create at least one task before starting the kernel.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+	 // Start the real time kernel with preemption.
+	 vTaskStartScheduler ();
+
+	 // Will only get here when the vTaskCode () task has called
+	 // vTaskEndScheduler ().  When we get here we are back to single task
+	 // execution.
+ }
+   
+ * + * \defgroup vTaskEndScheduler vTaskEndScheduler + * \ingroup SchedulerControl + */ +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskSuspendAll( void );
+ * + * Suspends all real time kernel activity while keeping interrupts (including the + * kernel tick) enabled. + * + * After calling vTaskSuspendAll () the calling task will continue to execute + * without risk of being swapped out until a call to xTaskResumeAll () has been + * made. + * + * API functions that have the potential to cause a context switch (for example, + * vTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler + * is suspended. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // ...
+
+		 // At some point the task wants to perform a long operation during
+		 // which it does not want to get swapped out.  It cannot use
+		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+		 // operation may cause interrupts to be missed - including the
+		 // ticks.
+
+		 // Prevent the real time kernel swapping out the task.
+		 vTaskSuspendAll ();
+
+		 // Perform the operation here.  There is no need to use critical
+		 // sections as we have all the microcontroller processing time.
+		 // During this time interrupts will still operate and the kernel
+		 // tick count will be maintained.
+
+		 // ...
+
+		 // The operation is complete.  Restart the kernel.
+		 xTaskResumeAll ();
+	 }
+ }
+   
+ * \defgroup vTaskSuspendAll vTaskSuspendAll + * \ingroup SchedulerControl + */ +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
char xTaskResumeAll( void );
+ * + * Resumes real time kernel activity following a call to vTaskSuspendAll (). + * After a call to vTaskSuspendAll () the kernel will take control of which + * task is executing at any time. + * + * @return If resuming the scheduler caused a context switch then pdTRUE is + * returned, otherwise pdFALSE is returned. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // ...
+
+		 // At some point the task wants to perform a long operation during
+		 // which it does not want to get swapped out.  It cannot use
+		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+		 // operation may cause interrupts to be missed - including the
+		 // ticks.
+
+		 // Prevent the real time kernel swapping out the task.
+		 vTaskSuspendAll ();
+
+		 // Perform the operation here.  There is no need to use critical
+		 // sections as we have all the microcontroller processing time.
+		 // During this time interrupts will still operate and the real
+		 // time kernel tick count will be maintained.
+
+		 // ...
+
+		 // The operation is complete.  Restart the kernel.  We want to force
+		 // a context switch - but there is no point if resuming the scheduler
+		 // caused a context switch already.
+		 if( !xTaskResumeAll () )
+		 {
+			  taskYIELD ();
+		 }
+	 }
+ }
+   
+ * \defgroup xTaskResumeAll xTaskResumeAll + * \ingroup SchedulerControl + */ +signed portBASE_TYPE xTaskResumeAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
signed portBASE_TYPE xTaskIsTaskSuspended( xTaskHandle xTask );
+ * + * Utility task that simply returns pdTRUE if the task referenced by xTask is + * currently in the Suspended state, or pdFALSE if the task referenced by xTask + * is in any other state. + * + */ +signed portBASE_TYPE xTaskIsTaskSuspended( xTaskHandle xTask ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * TASK UTILITIES + *----------------------------------------------------------*/ + +/** + * task. h + *
portTickType xTaskGetTickCount( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * \page xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +portTickType xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
portTickType xTaskGetTickCountFromISR( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * This is a version of xTaskGetTickCount() that is safe to be called from an + * ISR - provided that portTickType is the natural word size of the + * microcontroller being used or interrupt nesting is either not supported or + * not being used. + * + * \page xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +portTickType xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
unsigned short uxTaskGetNumberOfTasks( void );
+ * + * @return The number of tasks that the real time kernel is currently managing. + * This includes all ready, blocked and suspended tasks. A task that + * has been deleted but not yet freed by the idle task will also be + * included in the count. + * + * \page uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks + * \ingroup TaskUtils + */ +unsigned portBASE_TYPE uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
signed char *pcTaskGetTaskName( xTaskHandle xTaskToQuery );
+ * + * @return The text (human readable) name of the task referenced by the handle + * xTaskToQueury. A task can query its own name by either passing in its own + * handle, or by setting xTaskToQuery to NULL. INCLUDE_pcTaskGetTaskName must be + * set to 1 in FreeRTOSConfig.h for pcTaskGetTaskName() to be available. + * + * \page pcTaskGetTaskName pcTaskGetTaskName + * \ingroup TaskUtils + */ +signed char *pcTaskGetTaskName( xTaskHandle xTaskToQuery ); + +/** + * task. h + *
void vTaskList( char *pcWriteBuffer );
+ * + * configUSE_TRACE_FACILITY must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * NOTE: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or + * suspended ('S'). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ascii form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * \page vTaskList vTaskList + * \ingroup TaskUtils + */ +void vTaskList( signed char *pcWriteBuffer ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskGetRunTimeStats( char *pcWriteBuffer );
+ * + * configGENERATE_RUN_TIME_STATS must be defined as 1 for this function + * to be available. The application must also then provide definitions + * for portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE to configure a peripheral timer/counter + * and return the timers current count value respectively. The counter + * should be at least 10 times the frequency of the tick count. + * + * NOTE: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * Calling vTaskGetRunTimeStats() writes the total execution time of each + * task into a buffer, both as an absolute count value and as a percentage + * of the total system execution time. + * + * @param pcWriteBuffer A buffer into which the execution times will be + * written, in ascii form. This buffer is assumed to be large enough to + * contain the generated report. Approximately 40 bytes per task should + * be sufficient. + * + * \page vTaskGetRunTimeStats vTaskGetRunTimeStats + * \ingroup TaskUtils + */ +void vTaskGetRunTimeStats( signed char *pcWriteBuffer ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskStartTrace( char * pcBuffer, unsigned portBASE_TYPE uxBufferSize );
+ * + * Starts a real time kernel activity trace. The trace logs the identity of + * which task is running when. + * + * The trace file is stored in binary format. A separate DOS utility called + * convtrce.exe is used to convert this into a tab delimited text file which + * can be viewed and plotted in a spread sheet. + * + * @param pcBuffer The buffer into which the trace will be written. + * + * @param ulBufferSize The size of pcBuffer in bytes. The trace will continue + * until either the buffer in full, or ulTaskEndTrace () is called. + * + * \page vTaskStartTrace vTaskStartTrace + * \ingroup TaskUtils + */ +void vTaskStartTrace( signed char * pcBuffer, unsigned long ulBufferSize ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
unsigned long ulTaskEndTrace( void );
+ * + * Stops a kernel activity trace. See vTaskStartTrace (). + * + * @return The number of bytes that have been written into the trace buffer. + * + * \page usTaskEndTrace usTaskEndTrace + * \ingroup TaskUtils + */ +unsigned long ulTaskEndTrace( void ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in bytes) + * since the task referenced by xTask was created. + */ +unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask ) PRIVILEGED_FUNCTION; + +/* When using trace macros it is sometimes necessary to include tasks.h before +FreeRTOS.h. When this is done pdTASK_HOOK_CODE will not yet have been defined, +so the following two prototypes will cause a compilation error. This can be +fixed by simply guarding against the inclusion of these two prototypes unless +they are explicitly required by the configUSE_APPLICATION_TASK_TAG configuration +constant. */ +#ifdef configUSE_APPLICATION_TASK_TAG + #if configUSE_APPLICATION_TASK_TAG == 1 + /** + * task.h + *
void vTaskSetApplicationTaskTag( xTaskHandle xTask, pdTASK_HOOK_CODE pxHookFunction );
+ * + * Sets pxHookFunction to be the task hook function used by the task xTask. + * Passing xTask as NULL has the effect of setting the calling tasks hook + * function. + */ + void vTaskSetApplicationTaskTag( xTaskHandle xTask, pdTASK_HOOK_CODE pxHookFunction ) PRIVILEGED_FUNCTION; + + /** + * task.h + *
void xTaskGetApplicationTaskTag( xTaskHandle xTask );
+ * + * Returns the pxHookFunction value assigned to the task xTask. + */ + pdTASK_HOOK_CODE xTaskGetApplicationTaskTag( xTaskHandle xTask ) PRIVILEGED_FUNCTION; + #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ +#endif /* ifdef configUSE_APPLICATION_TASK_TAG */ + +/** + * task.h + *
portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask, pdTASK_HOOK_CODE pxHookFunction );
+ * + * Calls the hook function associated with xTask. Passing xTask as NULL has + * the effect of calling the Running tasks (the calling task) hook function. + * + * pvParameter is passed to the hook function for the task to interpret as it + * wants. + */ +portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask, void *pvParameter ) PRIVILEGED_FUNCTION; + +/** + * xTaskGetIdleTaskHandle() is only available if + * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. + * + * Simply returns the handle of the idle task. It is not valid to call + * xTaskGetIdleTaskHandle() before the scheduler has been started. + */ +xTaskHandle xTaskGetIdleTaskHandle( void ); + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + *----------------------------------------------------------*/ + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Called from the real time kernel tick (either preemptive or cooperative), + * this increments the tick count and checks if any tasks that are blocked + * for a finite period required removing from a blocked list and placing on + * a ready list. + */ +void vTaskIncrementTick( void ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes the calling task from the ready list and places it both + * on the list of tasks waiting for a particular event, and the + * list of delayed tasks. The task will be removed from both lists + * and replaced on the ready list should either the event occur (and + * there be no higher priority tasks waiting on the same event) or + * the delay period expires. + * + * @param pxEventList The list containing tasks that are blocked waiting + * for the event to occur. + * + * @param xTicksToWait The maximum amount of time that the task should wait + * for the event to occur. This is specified in kernel ticks,the constant + * portTICK_RATE_MS can be used to convert kernel ticks into a real time + * period. + */ +void vTaskPlaceOnEventList( const xList * const pxEventList, portTickType xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * This function performs nearly the same function as vTaskPlaceOnEventList(). + * The difference being that this function does not permit tasks to block + * indefinitely, whereas vTaskPlaceOnEventList() does. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +void vTaskPlaceOnEventListRestricted( const xList * const pxEventList, portTickType xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes a task from both the specified event list and the list of blocked + * tasks, and places it on a ready queue. + * + * xTaskRemoveFromEventList () will be called if either an event occurs to + * unblock a task, or the block timeout period expires. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +signed portBASE_TYPE xTaskRemoveFromEventList( const xList * const pxEventList ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Sets the pointer to the current TCB to the TCB of the highest priority task + * that is ready to run. + */ +void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the calling task. + */ +xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +/* + * Capture the current time status for future reference. + */ +void vTaskSetTimeOutState( xTimeOutType * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/* + * Compare the time status now with that previously captured to see if the + * timeout has expired. + */ +portBASE_TYPE xTaskCheckForTimeOut( xTimeOutType * const pxTimeOut, portTickType * const pxTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; + +/* + * Returns the scheduler state as taskSCHEDULER_RUNNING, + * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. + */ +portBASE_TYPE xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; + +/* + * Raises the priority of the mutex holder to that of the calling task should + * the mutex holder have a priority less than the calling task. + */ +void vTaskPriorityInherit( xTaskHandle * const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Set the priority of a task back to its proper priority in the case that it + * inherited a higher priority while it was holding a semaphore. + */ +void vTaskPriorityDisinherit( xTaskHandle * const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Generic version of the task creation function which is in turn called by the + * xTaskCreate() and xTaskCreateRestricted() macros. + */ +signed portBASE_TYPE xTaskGenericCreate( pdTASK_CODE pxTaskCode, const signed char * const pcName, unsigned short usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask, portSTACK_TYPE *puxStackBuffer, const xMemoryRegion * const xRegions ) PRIVILEGED_FUNCTION; + +#ifdef __cplusplus +} +#endif +#endif /* TASK_H */ + + + diff --git a/sdk/Src/OS/RTOS/ports/port_m3.c b/sdk/Src/OS/RTOS/ports/port_m3.c new file mode 100644 index 0000000..66e1b43 --- /dev/null +++ b/sdk/Src/OS/RTOS/ports/port_m3.c @@ -0,0 +1,403 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +/* Standard includes. */ +#include +#include +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "wm_config.h" + +#if TLS_OS_FREERTOS + +#ifndef configMAX_SYSCALL_INTERRUPT_PRIORITY +#define configMAX_SYSCALL_INTERRUPT_PRIORITY 31 /* equivalent to 0xb0, or priority 11. */ +#endif + +#ifndef configKERNEL_INTERRUPT_PRIORITY + #define configKERNEL_INTERRUPT_PRIORITY 255 +#endif +/* Constants required to manipulate the NVIC. */ +#define portNVIC_SYSTICK_CTRL ( ( volatile unsigned long *) 0xe000e010 ) +#define portNVIC_SYSTICK_LOAD ( ( volatile unsigned long *) 0xe000e014 ) +#define portNVIC_INT_CTRL ( ( volatile unsigned long *) 0xe000ed04 ) +#define portNVIC_SYSPRI2 ( ( volatile unsigned long *) 0xe000ed20 ) +#define portNVIC_SYSTICK_CLK 0x00000004 +#define portNVIC_SYSTICK_INT 0x00000002 +#define portNVIC_SYSTICK_ENABLE 0x00000001 +#define portNVIC_PENDSVSET 0x10000000 +#define portNVIC_PENDSV_PRI ( ( ( unsigned long ) configKERNEL_INTERRUPT_PRIORITY ) << 16 ) +#define portNVIC_SYSTICK_PRI ( ( ( unsigned long ) configKERNEL_INTERRUPT_PRIORITY ) << 24 ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) + + +/*-----------------------------------------------------------*/ + +/* The code generated by the Keil compiler does not maintain separate +stack and frame pointers. The portENTER_CRITICAL macro cannot therefore +use the stack as per other ports. Instead a variable is used to keep +track of the critical section nesting. This variable has to be stored +as part of the task context and must be initialised to a non zero value. */ + +#define portNO_CRITICAL_NESTING ( ( unsigned portLONG ) 0 ) +volatile unsigned portLONG ulCriticalNesting = 9999UL; + +/*-----------------------------------------------------------*/ + +/* Setup the timer to generate the tick interrupts. */ +//static void prvSetupTimerInterrupt( void ); + +/* + * The scheduler can only be started from ARM mode, so + * vPortStartFirstSTask() is defined in portISR.c. + */ +extern void vPortStartFirstTask( void ); + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static unsigned portBASE_TYPE uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. + */ +//static void prvSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +void vPortStartFirstTask( void ); + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = 0; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */ + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +__asm void SVC_Handler( void ) +{ + PRESERVE8 + + ldr r3, =pxCurrentTCB /* Restore the context. */ + ldr r1, [r3] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ + ldmia r0!, {r4-r11} /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + msr psp, r0 /* Restore the task stack pointer. */ + isb + mov r0, #0 + msr basepri, r0 + orr r14, #0xd + bx r14 +} +/*-----------------------------------------------------------*/ + +__asm void vPortStartFirstTask( void ) +{ + PRESERVE8 + + /* Use the NVIC offset register to locate the stack. */ + ldr r0, =0xE000ED08 + ldr r0, [r0] + ldr r0, [r0] + /* Set the msp back to the start of the stack. */ + msr msp, r0 + /* Globally enable interrupts. */ + cpsie i + cpsie f + dsb + isb + /* Call SVC to start the first task. */ + svc 0 + nop + nop +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +portBASE_TYPE xPortStartScheduler( void ) +{ + /* Make PendSV, CallSV and SysTick the same priroity as the kernel. */ + *(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI; +#if 0 /*Tick move to wm_main.c*/ + *(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + prvSetupTimerInterrupt(); +#endif + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Start the first task. */ + vPortStartFirstTask(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* It is unlikely that the CM3 port will require this function as there + is nothing to return to. */ +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + /* Set a PendSV to request a context switch. */ + *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET; + __dsb(0xF); + __isb(0xF); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + __dsb(0xF); + __isb(0xF); +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + if (uxCriticalNesting) + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +__asm void PendSV_Handler( void ) +{ + extern uxCriticalNesting; + extern pxCurrentTCB; + extern vTaskSwitchContext; + + PRESERVE8 + + CPSID I + + mrs r0, psp + isb + + ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */ + ldr r2, [r3] + + stmdb r0!, {r4-r11} /* Save the remaining registers. */ + str r0, [r2] /* Save the new top of stack into the first member of the TCB. */ + + stmdb sp!, {r3, r14} +; mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY +; msr basepri, r0 + bl vTaskSwitchContext +; mov r0, #0 +; msr basepri, r0 + ldmia sp!, {r3, r14} + + ldr r1, [r3] + ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ + ldmia r0!, {r4-r11} /* Pop the registers and the critical nesting count. */ + msr psp, r0 + isb + CPSIE I + + bx r14 + nop + nop + nop +} +/*-----------------------------------------------------------*/ + +void OS_CPU_SysTickHandler( void ) +{ +unsigned long ulDummy; + + /* If using preemption, also force a context switch. */ + #if configUSE_PREEMPTION == 1 + *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET; + #endif + + ulDummy = portSET_INTERRUPT_MASK_FROM_ISR(); + { + vTaskIncrementTick(); + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy ); +} +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ + #if 0 +void prvSetupTimerInterrupt( void ) +{ + /* Configure SysTick to interrupt at the requested rate. */ + *(portNVIC_SYSTICK_LOAD) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + *(portNVIC_SYSTICK_CTRL) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; +} +#endif +/*-----------------------------------------------------------*/ + +__asm void portDISABLE_INTERRUPTS( void ) +{ + PRESERVE8 + + +/* + push { r0 } + mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r0 + pop { r0 } +*/ + CPSID I + + bx r14 +} + +/*-----------------------------------------------------------*/ + +__asm void portENABLE_INTERRUPTS( void ) +{ + PRESERVE8 + +/* push { r0 } + mov r0, #0 + msr basepri, r0 + pop { r0 } +*/ + + CPSIE I + + bx r14 +} + +__asm int ulSetInterruptMaskFromISR( void ) +{ + PRESERVE8 +/* + push { r0 } + mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r0 + pop { r0 } +*/ + CPSID I + + bx r14 +} + +__asm void vClearInterruptMaskFromISR( int ulMask ) +{ + PRESERVE8 + +/* push { r0 } + mov r0, #0 + msr basepri, r0 + pop { r0 } +*/ + CPSIE I + + bx r14 +} + + +__asm int portGET_IPSR(void) +{ + PRESERVE8 + + mrs r0, ipsr + bx r14 +} + +void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskame, signed char prio ) +{ + printf("\ntask[%s] prio[%d]stack over flow\n", pcTaskame, prio); +} + +#endif + + diff --git a/sdk/Src/OS/RTOS/ports/port_m3_gcc.c b/sdk/Src/OS/RTOS/ports/port_m3_gcc.c new file mode 100644 index 0000000..90c7fab --- /dev/null +++ b/sdk/Src/OS/RTOS/ports/port_m3_gcc.c @@ -0,0 +1,352 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +/* Standard includes. */ +#include +#include +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "wm_config.h" + +#if TLS_OS_FREERTOS + +#ifndef configMAX_SYSCALL_INTERRUPT_PRIORITY +#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* equivalent to 0xb0, or priority 11. */ +#endif + +#ifndef configKERNEL_INTERRUPT_PRIORITY + #define configKERNEL_INTERRUPT_PRIORITY 255 +#endif +/* Constants required to manipulate the NVIC. */ +#define portNVIC_SYSTICK_CTRL ( ( volatile unsigned long *) 0xe000e010 ) +#define portNVIC_SYSTICK_LOAD ( ( volatile unsigned long *) 0xe000e014 ) +#define portNVIC_INT_CTRL ( ( volatile unsigned long *) 0xe000ed04 ) +#define portNVIC_SYSPRI2 ( ( volatile unsigned long *) 0xe000ed20 ) +#define portNVIC_SYSTICK_CLK 0x00000004 +#define portNVIC_SYSTICK_INT 0x00000002 +#define portNVIC_SYSTICK_ENABLE 0x00000001 +#define portNVIC_PENDSVSET 0x10000000 +#define portNVIC_PENDSV_PRI ( ( ( unsigned long ) configKERNEL_INTERRUPT_PRIORITY ) << 16 ) +#define portNVIC_SYSTICK_PRI ( ( ( unsigned long ) configKERNEL_INTERRUPT_PRIORITY ) << 24 ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) + + +/*-----------------------------------------------------------*/ + +/* The code generated by the Keil compiler does not maintain separate +stack and frame pointers. The portENTER_CRITICAL macro cannot therefore +use the stack as per other ports. Instead a variable is used to keep +track of the critical section nesting. This variable has to be stored +as part of the task context and must be initialised to a non zero value. */ + +#define portNO_CRITICAL_NESTING ( ( unsigned portLONG ) 0 ) +volatile unsigned portLONG ulCriticalNesting = 9999UL; + +/*-----------------------------------------------------------*/ + +/* Setup the timer to generate the tick interrupts. */ +//static void prvSetupTimerInterrupt( void ); + +/* + * The scheduler can only be started from ARM mode, so + * vPortStartFirstSTask() is defined in portISR.c. + */ +extern void vPortStartFirstTask( void ); + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static unsigned portBASE_TYPE uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. + */ +//static void prvSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +void vPortStartFirstTask( void ); + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = 0; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */ + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + + +__attribute__ ((naked)) void SVC_Handler( void ) +{ + __asm volatile ( + " ldr r3, =pxCurrentTCB \n" /* Restore the context. */ + " ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " mov r0, #0 \n" + " msr basepri, r0 \n" + " orr r14, #0xd \n" + " bx r14 \n" + " \n" + ); +} +/*-----------------------------------------------------------*/ + +__attribute__ ((naked)) void vPortStartFirstTask( void ) +{ + __asm volatile ( + " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ + " ldr r0, [r0] \n" + " ldr r0, [r0] \n" + " \n" + " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " \n" + " cpsie i \n" /* Globally enable interrupts. */ + " \n" + " svc 0 \n" /* Call SVC to start the first task. */ + " nop \n" + ); +} +/*-----------------------------------------------------------*/ + + +int ulSetInterruptMaskFromISR( void ) +{ + portDISABLE_INTERRUPTS(); + return 0; +} + +void vClearInterruptMaskFromISR( int ulMask ) +{ + portENABLE_INTERRUPTS(); +} + +/* + * See header file for description. + */ +portBASE_TYPE xPortStartScheduler( void ) +{ + /* Make PendSV, CallSV and SysTick the same priroity as the kernel. */ + *(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI; +#if 0 /*Tick move to wm_main.c*/ + *(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + prvSetupTimerInterrupt(); +#endif + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Start the first task. */ + vPortStartFirstTask(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* It is unlikely that the CM3 port will require this function as there + is nothing to return to. */ +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + /* Set a PendSV to request a context switch. */ + *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET; +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + if (uxCriticalNesting) + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +__attribute__ ((naked)) void PendSV_Handler( void ) +{ + __asm volatile( + " CPSID I \n" + " mrs r0, psp \n" + " ldr r3, =pxCurrentTCB \n" /* Get the location of the current TCB. */ + " ldr r2, [r3] \n" + " stmdb r0!, {r4-r11} \n" /* Save the remaining registers. */ + " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ + " stmdb sp!, {r3, r14} \n" + " bl vTaskSwitchContext \n" + " ldmia sp!, {r3, r14} \n" + " ldr r1, [r3] \n" + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11} \n" /* Pop the registers and the critical nesting count. */ + " msr psp, r0 \n" + " CPSIE I \n" + " bx r14 \n" + " nop \n" + ); + +} +/*-----------------------------------------------------------*/ + +void OS_CPU_SysTickHandler( void ) +{ +unsigned long ulDummy; + /* If using preemption, also force a context switch. */ + #if configUSE_PREEMPTION == 1 + *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET; + #endif + + ulDummy = portSET_INTERRUPT_MASK_FROM_ISR(); + { + vTaskIncrementTick(); + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy ); +} +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ + #if 0 +void prvSetupTimerInterrupt( void ) +{ + /* Configure SysTick to interrupt at the requested rate. */ + *(portNVIC_SYSTICK_LOAD) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + *(portNVIC_SYSTICK_CTRL) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; +} +#endif +/*-----------------------------------------------------------*/ + +__attribute__ ((naked)) void portDISABLE_INTERRUPTS( void ) +{ + __asm volatile( + "push { r0 } \n" + "mrs r0,PRIMASK \n" + "CPSID I \n" + "pop { r0 } \n" + "bx r14 \n" + ); +} + +/*-----------------------------------------------------------*/ + +__attribute__ ((naked)) void portENABLE_INTERRUPTS( void ) +{ + __asm volatile ( + " push { r0 } \n" + " MRS r0,PRIMASK \n" + " CPSIE I \n" + " pop { r0 } \n" + " bx r14 \n" + ); +} + +int portGET_IPSR(void) +{ + int result; + + __asm volatile ("MRS %0, ipsr" : "=r" (result) ); + + return (result); +} + +void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskame, signed char prio ) +{ + printf("\ntask[%s] prio[%d]stack over flow\n", pcTaskame, prio); +} + +#endif + + diff --git a/sdk/Src/OS/RTOS/source/croutine.c b/sdk/Src/OS/RTOS/source/croutine.c new file mode 100644 index 0000000..3d7f225 --- /dev/null +++ b/sdk/Src/OS/RTOS/source/croutine.c @@ -0,0 +1,376 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#include "FreeRTOS.h" +#include "task.h" +#include "croutine.h" +#include "wm_config.h" + +#if TLS_OS_FREERTOS +/* + * Some kernel aware debuggers require data to be viewed to be global, rather + * than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + + +/* Lists for ready and blocked co-routines. --------------------*/ +static xList pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */ +static xList xDelayedCoRoutineList1; /*< Delayed co-routines. */ +static xList xDelayedCoRoutineList2; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */ +static xList * pxDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used. */ +static xList * pxOverflowDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */ +static xList xPendingReadyCoRoutineList; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */ + +/* Other file private variables. --------------------------------*/ +corCRCB * pxCurrentCoRoutine = NULL; +static unsigned portBASE_TYPE uxTopCoRoutineReadyPriority = 0; +static portTickType xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0; + +/* The initial state of the co-routine when it is created. */ +#define corINITIAL_STATE ( 0 ) + +/* + * Place the co-routine represented by pxCRCB into the appropriate ready queue + * for the priority. It is inserted at the end of the list. + * + * This macro accesses the co-routine ready lists and therefore must not be + * used from within an ISR. + */ +#define prvAddCoRoutineToReadyQueue( pxCRCB ) \ +{ \ + if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \ + { \ + uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \ + } \ + vListInsertEnd( ( xList * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \ +} + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first co-routine. + */ +static void prvInitialiseCoRoutineLists( void ); + +/* + * Co-routines that are readied by an interrupt cannot be placed directly into + * the ready lists (there is no mutual exclusion). Instead they are placed in + * in the pending ready list in order that they can later be moved to the ready + * list by the co-routine scheduler. + */ +static void prvCheckPendingReadyList( void ); + +/* + * Macro that looks at the list of co-routines that are currently delayed to + * see if any require waking. + * + * Co-routines are stored in the queue in the order of their wake time - + * meaning once one co-routine has been found whose timer has not expired + * we need not look any further down the list. + */ +static void prvCheckDelayedList( void ); + +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex ) +{ +signed portBASE_TYPE xReturn; +corCRCB *pxCoRoutine; + + /* Allocate the memory that will store the co-routine control block. */ + pxCoRoutine = ( corCRCB * ) pvPortMalloc( sizeof( corCRCB ) ); + if( pxCoRoutine ) + { + /* If pxCurrentCoRoutine is NULL then this is the first co-routine to + be created and the co-routine data structures need initialising. */ + if( pxCurrentCoRoutine == NULL ) + { + pxCurrentCoRoutine = pxCoRoutine; + prvInitialiseCoRoutineLists(); + } + + /* Check the priority is within limits. */ + if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES ) + { + uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1; + } + + /* Fill out the co-routine control block from the function parameters. */ + pxCoRoutine->uxState = corINITIAL_STATE; + pxCoRoutine->uxPriority = uxPriority; + pxCoRoutine->uxIndex = uxIndex; + pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode; + + /* Initialise all the other co-routine control block parameters. */ + vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) ); + vListInitialiseItem( &( pxCoRoutine->xEventListItem ) ); + + /* Set the co-routine control block as a link back from the xListItem. + This is so we can get back to the containing CRCB from a generic item + in a list. */ + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine ); + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxPriority ); + + /* Now the co-routine has been initialised it can be added to the ready + list at the correct priority. */ + prvAddCoRoutineToReadyQueue( pxCoRoutine ); + + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vCoRoutineAddToDelayedList( portTickType xTicksToDelay, xList *pxEventList ) +{ +portTickType xTimeToWake; + + /* Calculate the time to wake - this may overflow but this is + not a problem. */ + xTimeToWake = xCoRoutineTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xCoRoutineTickCount ) + { + /* Wake time has overflowed. Place this item in the + overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedCoRoutineList, ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + current block list. */ + vListInsert( ( xList * ) pxDelayedCoRoutineList, ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + + if( pxEventList ) + { + /* Also add the co-routine to an event list. If this is done then the + function must be called with interrupts disabled. */ + vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) ); + } +} +/*-----------------------------------------------------------*/ + +static void prvCheckPendingReadyList( void ) +{ + /* Are there any co-routines waiting to get moved to the ready list? These + are co-routines that have been readied by an ISR. The ISR cannot access + the ready lists itself. */ + while( listLIST_IS_EMPTY( &xPendingReadyCoRoutineList ) == pdFALSE ) + { + corCRCB *pxUnblockedCRCB; + + /* The pending ready list can be accessed by an ISR. */ + portDISABLE_INTERRUPTS(); + { + pxUnblockedCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( (&xPendingReadyCoRoutineList) ); + vListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + } + portENABLE_INTERRUPTS(); + + vListRemove( &( pxUnblockedCRCB->xGenericListItem ) ); + prvAddCoRoutineToReadyQueue( pxUnblockedCRCB ); + } +} +/*-----------------------------------------------------------*/ + +static void prvCheckDelayedList( void ) +{ +corCRCB *pxCRCB; + + xPassedTicks = xTaskGetTickCount() - xLastTickCount; + while( xPassedTicks ) + { + xCoRoutineTickCount++; + xPassedTicks--; + + /* If the tick count has overflowed we need to swap the ready lists. */ + if( xCoRoutineTickCount == 0 ) + { + xList * pxTemp; + + /* Tick count has overflowed so we need to swap the delay lists. If there are + any items in pxDelayedCoRoutineList here then there is an error! */ + pxTemp = pxDelayedCoRoutineList; + pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList; + pxOverflowDelayedCoRoutineList = pxTemp; + } + + /* See if this tick has made a timeout expire. */ + while( listLIST_IS_EMPTY( pxDelayedCoRoutineList ) == pdFALSE ) + { + pxCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList ); + + if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) ) + { + /* Timeout not yet expired. */ + break; + } + + portDISABLE_INTERRUPTS(); + { + /* The event could have occurred just before this critical + section. If this is the case then the generic list item will + have been moved to the pending ready list and the following + line is still valid. Also the pvContainer parameter will have + been set to NULL so the following lines are also valid. */ + vListRemove( &( pxCRCB->xGenericListItem ) ); + + /* Is the co-routine waiting on an event also? */ + if( pxCRCB->xEventListItem.pvContainer ) + { + vListRemove( &( pxCRCB->xEventListItem ) ); + } + } + portENABLE_INTERRUPTS(); + + prvAddCoRoutineToReadyQueue( pxCRCB ); + } + } + + xLastTickCount = xCoRoutineTickCount; +} +/*-----------------------------------------------------------*/ + +void vCoRoutineSchedule( void ) +{ + /* See if any co-routines readied by events need moving to the ready lists. */ + prvCheckPendingReadyList(); + + /* See if any delayed co-routines have timed out. */ + prvCheckDelayedList(); + + /* Find the highest priority queue that contains ready co-routines. */ + while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) ) + { + if( uxTopCoRoutineReadyPriority == 0 ) + { + /* No more co-routines to check. */ + return; + } + --uxTopCoRoutineReadyPriority; + } + + /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines + of the same priority get an equal share of the processor time. */ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ); + + /* Call the co-routine. */ + ( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex ); + + return; +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseCoRoutineLists( void ) +{ +unsigned portBASE_TYPE uxPriority; + + for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ ) + { + vListInitialise( ( xList * ) &( pxReadyCoRoutineLists[ uxPriority ] ) ); + } + + vListInitialise( ( xList * ) &xDelayedCoRoutineList1 ); + vListInitialise( ( xList * ) &xDelayedCoRoutineList2 ); + vListInitialise( ( xList * ) &xPendingReadyCoRoutineList ); + + /* Start with pxDelayedCoRoutineList using list1 and the + pxOverflowDelayedCoRoutineList using list2. */ + pxDelayedCoRoutineList = &xDelayedCoRoutineList1; + pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xCoRoutineRemoveFromEventList( const xList *pxEventList ) +{ +corCRCB *pxUnblockedCRCB; +signed portBASE_TYPE xReturn; + + /* This function is called from within an interrupt. It can only access + event lists and the pending ready list. This function assumes that a + check has already been made to ensure pxEventList is not empty. */ + pxUnblockedCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + vListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + vListInsertEnd( ( xList * ) &( xPendingReadyCoRoutineList ), &( pxUnblockedCRCB->xEventListItem ) ); + + if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +#endif diff --git a/sdk/Src/OS/RTOS/source/heap_2.c b/sdk/Src/OS/RTOS/source/heap_2.c new file mode 100644 index 0000000..d786a55 --- /dev/null +++ b/sdk/Src/OS/RTOS/source/heap_2.c @@ -0,0 +1,288 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +/* + * A sample implementation of pvPortMalloc() and vPortFree() that permits + * allocated blocks to be freed, but does not combine adjacent free blocks + * into a single larger block. + * + * See heap_1.c and heap_3.c for alternative implementations, and the memory + * management pages of http://www.FreeRTOS.org for more information. + */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "wm_config.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#if TLS_OS_FREERTOS +#if configUSE_HEAP2 +/* Allocate the memory for the heap. The struct is used to force byte +alignment without using any non-portable code. */ +static union xRTOS_HEAP +{ + #if portBYTE_ALIGNMENT == 8 + volatile portDOUBLE dDummy; + #else + volatile unsigned long ulDummy; + #endif + unsigned char ucHeap[ configTOTAL_HEAP_SIZE ]; +} xHeap; + +/* Define the linked list structure. This is used to link free blocks in order +of their size. */ +typedef struct A_BLOCK_LINK +{ + struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */ + size_t xBlockSize; /*<< The size of the free block. */ +} xBlockLink; + + +static const unsigned short heapSTRUCT_SIZE = ( sizeof( xBlockLink ) + portBYTE_ALIGNMENT - ( sizeof( xBlockLink ) % portBYTE_ALIGNMENT ) ); +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) + +/* Create a couple of list links to mark the start and end of the list. */ +static xBlockLink xStart, xEnd; + +/* Keeps track of the number of free bytes remaining, but says nothing about +fragmentation. */ +static size_t xFreeBytesRemaining = configTOTAL_HEAP_SIZE; + +/* STATIC FUNCTIONS ARE DEFINED AS MACROS TO MINIMIZE THE FUNCTION CALL DEPTH. */ + +/* + * Insert a block into the list of free blocks - which is ordered by size of + * the block. Small blocks at the start of the list and large blocks at the end + * of the list. + */ +#define prvInsertBlockIntoFreeList( pxBlockToInsert ) \ +{ \ +xBlockLink *pxIterator; \ +size_t xBlockSize; \ + \ + xBlockSize = pxBlockToInsert->xBlockSize; \ + \ + /* Iterate through the list until a block is found that has a larger size */ \ + /* than the block we are inserting. */ \ + for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \ + { \ + /* There is nothing to do here - just iterate to the correct position. */ \ + } \ + \ + /* Update the list to include the block being inserted in the correct */ \ + /* position. */ \ + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \ + pxIterator->pxNextFreeBlock = pxBlockToInsert; \ +} +/*-----------------------------------------------------------*/ + +#define prvHeapInit() \ +{ \ +xBlockLink *pxFirstFreeBlock; \ + \ + /* xStart is used to hold a pointer to the first item in the list of free */ \ + /* blocks. The void cast is used to prevent compiler warnings. */ \ + xStart.pxNextFreeBlock = ( void * ) xHeap.ucHeap; \ + xStart.xBlockSize = ( size_t ) 0; \ + \ + /* xEnd is used to mark the end of the list of free blocks. */ \ + xEnd.xBlockSize = configTOTAL_HEAP_SIZE; \ + xEnd.pxNextFreeBlock = NULL; \ + \ + /* To start with there is a single free block that is sized to take up the \ + entire heap space. */ \ + pxFirstFreeBlock = ( void * ) xHeap.ucHeap; \ + pxFirstFreeBlock->xBlockSize = configTOTAL_HEAP_SIZE; \ + pxFirstFreeBlock->pxNextFreeBlock = &xEnd; \ +} +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +xBlockLink *pxBlock, *pxPreviousBlock, *pxNewBlockLink; +static portBASE_TYPE xHeapHasBeenInitialised = pdFALSE; +void *pvReturn = NULL; + + vTaskSuspendAll(); + { + /* If this is the first call to malloc then the heap will require + initialisation to setup the list of free blocks. */ + if( xHeapHasBeenInitialised == pdFALSE ) + { + prvHeapInit(); + xHeapHasBeenInitialised = pdTRUE; + } + + /* The wanted size is increased so it can contain a xBlockLink + structure in addition to the requested amount of bytes. */ + if( xWantedSize > 0 ) + { + xWantedSize += heapSTRUCT_SIZE; + + /* Ensure that blocks are always aligned to the required number of bytes. */ + if( xWantedSize & portBYTE_ALIGNMENT_MASK ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); + } + } + + if( ( xWantedSize > 0 ) && ( xWantedSize < configTOTAL_HEAP_SIZE ) ) + { + /* Blocks are stored in byte order - traverse the list from the start + (smallest) block until one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock ) ) + { + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } + + /* If we found the end marker then a block of adequate size was not found. */ + if( pxBlock != &xEnd ) + { + /* Return the memory space - jumping over the xBlockLink structure + at its start. */ + pvReturn = ( void * ) ( ( ( unsigned char * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE ); + + /* This block is being returned for use so must be taken our of the + list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into two. */ + if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) + { + /* This block is to be split into two. Create a new block + following the number of bytes requested. The void cast is + used to prevent byte alignment warnings from the compiler. */ + pxNewBlockLink = ( void * ) ( ( ( unsigned char * ) pxBlock ) + xWantedSize ); + + /* Calculate the sizes of two blocks split from the single + block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); + } + + xFreeBytesRemaining -= pxBlock->xBlockSize; + } + } + } + xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + } + #endif + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ +unsigned char *puc = ( unsigned char * ) pv; +xBlockLink *pxLink; + + if( pv ) + { + /* The memory being freed will have an xBlockLink structure immediately + before it. */ + puc -= heapSTRUCT_SIZE; + + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = ( void * ) puc; + + vTaskSuspendAll(); + { + /* Add this block to the list of free blocks. */ + prvInsertBlockIntoFreeList( ( ( xBlockLink * ) pxLink ) ); + xFreeBytesRemaining += pxLink->xBlockSize; + } + xTaskResumeAll(); + } +} +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize( void ) +{ + return xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks( void ) +{ + /* This just exists to keep the linker quiet. */ +} + +int xPortMemIsKernel(void *mem) +{ + return ((mem >= (void *)xHeap.ucHeap) && (mem <= (void *)(xHeap.ucHeap + configTOTAL_HEAP_SIZE))) ? 1 : 0; +} +#endif +#endif + diff --git a/sdk/Src/OS/RTOS/source/heap_3.c b/sdk/Src/OS/RTOS/source/heap_3.c new file mode 100644 index 0000000..49b6495 --- /dev/null +++ b/sdk/Src/OS/RTOS/source/heap_3.c @@ -0,0 +1,122 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +/* + * Implementation of pvPortMalloc() and vPortFree() that relies on the + * compilers own malloc() and free() implementations. + * + * This file can only be used if the linker is configured to to generate + * a heap memory area. + * + * See heap_2.c and heap_1.c for alternative implementations, and the memory + * management pages of http://www.FreeRTOS.org for more information. + */ + +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "wm_config.h" +#include "wm_mem.h" + + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#if TLS_OS_FREERTOS +#if configUSE_HEAP3 +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +void *pvReturn; + + vTaskSuspendAll(); + { + pvReturn = tls_mem_alloc( xWantedSize ); + } + xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + } + #endif + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ + if( pv ) + { + vTaskSuspendAll(); + { + tls_mem_free( pv ); + } + xTaskResumeAll(); + } +} +#endif +#endif + + diff --git a/sdk/Src/OS/RTOS/source/heap_4.c b/sdk/Src/OS/RTOS/source/heap_4.c new file mode 100644 index 0000000..b7f9378 --- /dev/null +++ b/sdk/Src/OS/RTOS/source/heap_4.c @@ -0,0 +1,600 @@ +/* + * FreeRTOS Kernel + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/* + * A sample implementation of pvPortMalloc() and vPortFree() that combines + * (coalescences) adjacent memory blocks as they are freed, and in so doing + * limits memory fragmentation. + * + * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the + * memory management pages of https://www.FreeRTOS.org for more information. + */ +#include +#include +#include + +#define configSUPPORT_DYNAMIC_ALLOCATION 1 + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "wm_config.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#if configUSE_HEAP4 + +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) + #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 +#endif + +#ifndef configHEAP_CLEAR_MEMORY_ON_FREE + #define configHEAP_CLEAR_MEMORY_ON_FREE 0 +#endif + +/* Block sizes must not get too small. */ +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) ) + +/* Assumes 8bit bytes! */ +#define heapBITS_PER_BYTE ( ( size_t ) 8 ) + +/* Max value that fits in a size_t type. */ +#define heapSIZE_MAX ( ~( ( size_t ) 0 ) ) + +/* Check if multiplying a and b will result in overflow. */ +#define heapMULTIPLY_WILL_OVERFLOW( a, b ) ( ( ( a ) > 0 ) && ( ( b ) > ( heapSIZE_MAX / ( a ) ) ) ) + +/* Check if adding a and b will result in overflow. */ +#define heapADD_WILL_OVERFLOW( a, b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) ) + +/* Check if the subtraction operation ( a - b ) will result in underflow. */ +#define heapSUBTRACT_WILL_UNDERFLOW( a, b ) ( ( a ) < ( b ) ) + +/* MSB of the xBlockSize member of an BlockLink_t structure is used to track + * the allocation status of a block. When MSB of the xBlockSize member of + * an BlockLink_t structure is set then the block belongs to the application. + * When the bit is free the block is still part of the free heap space. */ +#define heapBLOCK_ALLOCATED_BITMASK ( ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ) ) +#define heapBLOCK_SIZE_IS_VALID( xBlockSize ) ( ( ( xBlockSize ) & heapBLOCK_ALLOCATED_BITMASK ) == 0 ) +#define heapBLOCK_IS_ALLOCATED( pxBlock ) ( ( ( pxBlock->xBlockSize ) & heapBLOCK_ALLOCATED_BITMASK ) != 0 ) +#define heapALLOCATE_BLOCK( pxBlock ) ( ( pxBlock->xBlockSize ) |= heapBLOCK_ALLOCATED_BITMASK ) +#define heapFREE_BLOCK( pxBlock ) ( ( pxBlock->xBlockSize ) &= ~heapBLOCK_ALLOCATED_BITMASK ) + +/*-----------------------------------------------------------*/ + +/* Allocate the memory for the heap. */ +#if ( configAPPLICATION_ALLOCATED_HEAP == 1 ) + +/* The application writer has already defined the array used for the RTOS + * heap - probably so it can be placed in a special segment or address. */ + extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; +#else + PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; +#endif /* configAPPLICATION_ALLOCATED_HEAP */ + +/* Define the linked list structure. This is used to link free blocks in order + * of their memory address. */ +typedef struct A_BLOCK_LINK +{ + struct A_BLOCK_LINK * pxNextFreeBlock; /**< The next free block in the list. */ + size_t xBlockSize; /**< The size of the free block. */ +} BlockLink_t; + +/* Setting configENABLE_HEAP_PROTECTOR to 1 enables heap block pointers + * protection using an application supplied canary value to catch heap + * corruption should a heap buffer overflow occur. + */ +#if ( configENABLE_HEAP_PROTECTOR == 1 ) + +/** + * @brief Application provided function to get a random value to be used as canary. + * + * @param pxHeapCanary [out] Output parameter to return the canary value. + */ + extern void vApplicationGetRandomHeapCanary( portPOINTER_SIZE_TYPE * pxHeapCanary ); + +/* Canary value for protecting internal heap pointers. */ + PRIVILEGED_DATA static portPOINTER_SIZE_TYPE xHeapCanary; + +/* Macro to load/store BlockLink_t pointers to memory. By XORing the + * pointers with a random canary value, heap overflows will result + * in randomly unpredictable pointer values which will be caught by + * heapVALIDATE_BLOCK_POINTER assert. */ + #define heapPROTECT_BLOCK_POINTER( pxBlock ) ( ( BlockLink_t * ) ( ( ( portPOINTER_SIZE_TYPE ) ( pxBlock ) ) ^ xHeapCanary ) ) +#else + + #define heapPROTECT_BLOCK_POINTER( pxBlock ) ( pxBlock ) + +#endif /* configENABLE_HEAP_PROTECTOR */ + +/* Assert that a heap block pointer is within the heap bounds. */ +#define heapVALIDATE_BLOCK_POINTER( pxBlock ) \ + configASSERT( ( ( uint8_t * ) ( pxBlock ) >= &( ucHeap[ 0 ] ) ) && \ + ( ( uint8_t * ) ( pxBlock ) <= &( ucHeap[ configTOTAL_HEAP_SIZE - 1 ] ) ) ) + +/*-----------------------------------------------------------*/ + +/* + * Inserts a block of memory that is being freed into the correct position in + * the list of free memory blocks. The block being freed will be merged with + * the block in front it and/or the block behind it if the memory blocks are + * adjacent to each other. + */ +static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) PRIVILEGED_FUNCTION; + +/* + * Called automatically to setup the required heap structures the first time + * pvPortMalloc() is called. + */ +static void prvHeapInit( void ) PRIVILEGED_FUNCTION; + +/*-----------------------------------------------------------*/ + +/* The size of the structure placed at the beginning of each allocated memory + * block must by correctly byte aligned. */ +static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); + +/* Create a couple of list links to mark the start and end of the list. */ +PRIVILEGED_DATA static BlockLink_t xStart; +PRIVILEGED_DATA static BlockLink_t * pxEnd = NULL; + +/* Keeps track of the number of calls to allocate and free memory as well as the + * number of free bytes remaining, but says nothing about fragmentation. */ +PRIVILEGED_DATA static size_t xFreeBytesRemaining = ( size_t ) 0U; +PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = ( size_t ) 0U; +PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = ( size_t ) 0U; +PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = ( size_t ) 0U; + +/*-----------------------------------------------------------*/ + +void * pvPortMalloc( size_t xWantedSize ) +{ + BlockLink_t * pxBlock; + BlockLink_t * pxPreviousBlock; + BlockLink_t * pxNewBlockLink; + void * pvReturn = NULL; + size_t xAdditionalRequiredSize; + size_t xAllocatedBlockSize = 0; + + if( xWantedSize > 0 ) + { + /* The wanted size must be increased so it can contain a BlockLink_t + * structure in addition to the requested amount of bytes. */ + if( heapADD_WILL_OVERFLOW( xWantedSize, xHeapStructSize ) == 0 ) + { + xWantedSize += xHeapStructSize; + + /* Ensure that blocks are always aligned to the required number + * of bytes. */ + if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) + { + /* Byte alignment required. */ + xAdditionalRequiredSize = portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ); + + if( heapADD_WILL_OVERFLOW( xWantedSize, xAdditionalRequiredSize ) == 0 ) + { + xWantedSize += xAdditionalRequiredSize; + } + else + { + xWantedSize = 0; + } + } + } + else + { + xWantedSize = 0; + } + } + + vTaskSuspendAll(); + { + /* If this is the first call to malloc then the heap will require + * initialisation to setup the list of free blocks. */ + if( pxEnd == NULL ) + { + prvHeapInit(); + } + + /* Check the block size we are trying to allocate is not so large that the + * top bit is set. The top bit of the block size member of the BlockLink_t + * structure is used to determine who owns the block - the application or + * the kernel, so it must be free. */ + if( heapBLOCK_SIZE_IS_VALID( xWantedSize ) != 0 ) + { + if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) + { + /* Traverse the list from the start (lowest address) block until + * one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = heapPROTECT_BLOCK_POINTER( xStart.pxNextFreeBlock ); + heapVALIDATE_BLOCK_POINTER( pxBlock ); + + while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER( NULL ) ) ) + { + pxPreviousBlock = pxBlock; + pxBlock = heapPROTECT_BLOCK_POINTER( pxBlock->pxNextFreeBlock ); + heapVALIDATE_BLOCK_POINTER( pxBlock ); + } + + /* If the end marker was reached then a block of adequate size + * was not found. */ + if( pxBlock != pxEnd ) + { + /* Return the memory space pointed to - jumping over the + * BlockLink_t structure at its start. */ + pvReturn = ( void * ) ( ( ( uint8_t * ) heapPROTECT_BLOCK_POINTER( pxPreviousBlock->pxNextFreeBlock ) ) + xHeapStructSize ); + heapVALIDATE_BLOCK_POINTER( pvReturn ); + + /* This block is being returned for use so must be taken out + * of the list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into + * two. */ + configASSERT( heapSUBTRACT_WILL_UNDERFLOW( pxBlock->xBlockSize, xWantedSize ) == 0 ); + + if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) + { + /* This block is to be split into two. Create a new + * block following the number of bytes requested. The void + * cast is used to prevent byte alignment warnings from the + * compiler. */ + pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); + configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 ); + + /* Calculate the sizes of two blocks split from the + * single block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + pxNewBlockLink->pxNextFreeBlock = pxPreviousBlock->pxNextFreeBlock; + pxPreviousBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxNewBlockLink ); + } + + xFreeBytesRemaining -= pxBlock->xBlockSize; + + if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) + { + xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; + } + + xAllocatedBlockSize = pxBlock->xBlockSize; + + /* The block is being returned - it is allocated and owned + * by the application and has no "next" block. */ + heapALLOCATE_BLOCK( pxBlock ); + pxBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( NULL ); + xNumberOfSuccessfulAllocations++; + } + } + } + + // traceMALLOC( pvReturn, xAllocatedBlockSize ); + + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xAllocatedBlockSize; + } + ( void ) xTaskResumeAll(); + + #if ( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + vApplicationMallocFailedHook(); + } + } + #endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */ + + configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 ); + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void * pv ) +{ + uint8_t * puc = ( uint8_t * ) pv; + BlockLink_t * pxLink; + + if( pv != NULL ) + { + /* The memory being freed will have an BlockLink_t structure immediately + * before it. */ + puc -= xHeapStructSize; + + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = ( void * ) puc; + + heapVALIDATE_BLOCK_POINTER( pxLink ); + configASSERT( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 ); + configASSERT( pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER( NULL ) ); + + if( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 ) + { + if( pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER( NULL ) ) + { + /* The block is being returned to the heap - it is no longer + * allocated. */ + heapFREE_BLOCK( pxLink ); + #if ( configHEAP_CLEAR_MEMORY_ON_FREE == 1 ) + { + /* Check for underflow as this can occur if xBlockSize is + * overwritten in a heap block. */ + if( heapSUBTRACT_WILL_UNDERFLOW( pxLink->xBlockSize, xHeapStructSize ) == 0 ) + { + ( void ) memset( puc + xHeapStructSize, 0, pxLink->xBlockSize - xHeapStructSize ); + } + } + #endif + + vTaskSuspendAll(); + { + /* Add this block to the list of free blocks. */ + xFreeBytesRemaining += pxLink->xBlockSize; + // traceFREE( pv, pxLink->xBlockSize ); + prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); + xNumberOfSuccessfulFrees++; + } + ( void ) xTaskResumeAll(); + } + } + } +} +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize( void ) +{ + return xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +size_t xPortGetMinimumEverFreeHeapSize( void ) +{ + return xMinimumEverFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +void xPortResetHeapMinimumEverFreeHeapSize( void ) +{ + xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +int xPortMemIsKernel(void *mem) +{ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks( void ) +{ + /* This just exists to keep the linker quiet. */ +} +/*-----------------------------------------------------------*/ + +void * pvPortCalloc( size_t xNum, + size_t xSize ) +{ + void * pv = NULL; + + if( heapMULTIPLY_WILL_OVERFLOW( xNum, xSize ) == 0 ) + { + pv = pvPortMalloc( xNum * xSize ); + + if( pv != NULL ) + { + ( void ) memset( pv, 0, xNum * xSize ); + } + } + + return pv; +} +/*-----------------------------------------------------------*/ + +static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */ +{ + BlockLink_t * pxFirstFreeBlock; + portPOINTER_SIZE_TYPE uxStartAddress, uxEndAddress; + size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; + + /* Ensure the heap starts on a correctly aligned boundary. */ + uxStartAddress = ( portPOINTER_SIZE_TYPE ) ucHeap; + + if( ( uxStartAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) + { + uxStartAddress += ( portBYTE_ALIGNMENT - 1 ); + uxStartAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ); + xTotalHeapSize -= ( size_t ) ( uxStartAddress - ( portPOINTER_SIZE_TYPE ) ucHeap ); + } + + #if ( configENABLE_HEAP_PROTECTOR == 1 ) + { + vApplicationGetRandomHeapCanary( &( xHeapCanary ) ); + } + #endif + + /* xStart is used to hold a pointer to the first item in the list of free + * blocks. The void cast is used to prevent compiler warnings. */ + xStart.pxNextFreeBlock = ( void * ) heapPROTECT_BLOCK_POINTER( uxStartAddress ); + xStart.xBlockSize = ( size_t ) 0; + + /* pxEnd is used to mark the end of the list of free blocks and is inserted + * at the end of the heap space. */ + uxEndAddress = uxStartAddress + ( portPOINTER_SIZE_TYPE ) xTotalHeapSize; + uxEndAddress -= ( portPOINTER_SIZE_TYPE ) xHeapStructSize; + uxEndAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ); + pxEnd = ( BlockLink_t * ) uxEndAddress; + pxEnd->xBlockSize = 0; + pxEnd->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( NULL ); + + /* To start with there is a single free block that is sized to take up the + * entire heap space, minus the space taken by pxEnd. */ + pxFirstFreeBlock = ( BlockLink_t * ) uxStartAddress; + pxFirstFreeBlock->xBlockSize = ( size_t ) ( uxEndAddress - ( portPOINTER_SIZE_TYPE ) pxFirstFreeBlock ); + pxFirstFreeBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxEnd ); + + /* Only one block exists - and it covers the entire usable heap space. */ + xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; +} +/*-----------------------------------------------------------*/ + +static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */ +{ + BlockLink_t * pxIterator; + uint8_t * puc; + + /* Iterate through the list until a block is found that has a higher address + * than the block being inserted. */ + for( pxIterator = &xStart; heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) < pxBlockToInsert; pxIterator = heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) ) + { + /* Nothing to do here, just iterate to the right position. */ + } + + if( pxIterator != &xStart ) + { + heapVALIDATE_BLOCK_POINTER( pxIterator ); + } + + /* Do the block being inserted, and the block it is being inserted after + * make a contiguous block of memory? */ + puc = ( uint8_t * ) pxIterator; + + if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ) + { + pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; + pxBlockToInsert = pxIterator; + } + + /* Do the block being inserted, and the block it is being inserted before + * make a contiguous block of memory? */ + puc = ( uint8_t * ) pxBlockToInsert; + + if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) ) + { + if( heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) != pxEnd ) + { + /* Form one big block from the two blocks. */ + pxBlockToInsert->xBlockSize += heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock )->xBlockSize; + pxBlockToInsert->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock )->pxNextFreeBlock; + } + else + { + pxBlockToInsert->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxEnd ); + } + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; + } + + /* If the block being inserted plugged a gap, so was merged with the block + * before and the block after, then it's pxNextFreeBlock pointer will have + * already been set, and should not be set here as that would make it point + * to itself. */ + if( pxIterator != pxBlockToInsert ) + { + pxIterator->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxBlockToInsert ); + } +} +/*-----------------------------------------------------------*/ + +#if 0 +void vPortGetHeapStats( HeapStats_t * pxHeapStats ) +{ + BlockLink_t * pxBlock; + size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY; /* portMAX_DELAY used as a portable way of getting the maximum value. */ + + vTaskSuspendAll(); + { + pxBlock = heapPROTECT_BLOCK_POINTER( xStart.pxNextFreeBlock ); + + /* pxBlock will be NULL if the heap has not been initialised. The heap + * is initialised automatically when the first allocation is made. */ + if( pxBlock != NULL ) + { + while( pxBlock != pxEnd ) + { + /* Increment the number of blocks and record the largest block seen + * so far. */ + xBlocks++; + + if( pxBlock->xBlockSize > xMaxSize ) + { + xMaxSize = pxBlock->xBlockSize; + } + + if( pxBlock->xBlockSize < xMinSize ) + { + xMinSize = pxBlock->xBlockSize; + } + + /* Move to the next block in the chain until the last block is + * reached. */ + pxBlock = heapPROTECT_BLOCK_POINTER( pxBlock->pxNextFreeBlock ); + } + } + } + ( void ) xTaskResumeAll(); + + pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize; + pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize; + pxHeapStats->xNumberOfFreeBlocks = xBlocks; + + taskENTER_CRITICAL(); + { + pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining; + pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations; + pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees; + pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining; + } + taskEXIT_CRITICAL(); +} +#endif +/*-----------------------------------------------------------*/ + +/* + * Reset the state in this file. This state is normally initialized at start up. + * This function must be called by the application before restarting the + * scheduler. + */ +void vPortHeapResetState( void ) +{ + pxEnd = NULL; + + xFreeBytesRemaining = ( size_t ) 0U; + xMinimumEverFreeBytesRemaining = ( size_t ) 0U; + xNumberOfSuccessfulAllocations = ( size_t ) 0U; + xNumberOfSuccessfulFrees = ( size_t ) 0U; +} +#endif +/*-----------------------------------------------------------*/ \ No newline at end of file diff --git a/sdk/Src/OS/RTOS/source/list.c b/sdk/Src/OS/RTOS/source/list.c new file mode 100644 index 0000000..36bc045 --- /dev/null +++ b/sdk/Src/OS/RTOS/source/list.c @@ -0,0 +1,194 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#include +#include "FreeRTOS.h" +#include "rtoslist.h" +#include "wm_config.h" + +#if TLS_OS_FREERTOS +/*----------------------------------------------------------- + * PUBLIC LIST API documented in list.h + *----------------------------------------------------------*/ + +void vListInitialise( xList *pxList ) +{ + /* The list structure contains a list item which is used to mark the + end of the list. To initialise the list the list end is inserted + as the only list entry. */ + pxList->pxIndex = ( xListItem * ) &( pxList->xListEnd ); + + /* The list end value is the highest possible value in the list to + ensure it remains at the end of the list. */ + pxList->xListEnd.xItemValue = portMAX_DELAY; + + /* The list end next and previous pointers point to itself so we know + when the list is empty. */ + pxList->xListEnd.pxNext = ( xListItem * ) &( pxList->xListEnd ); + pxList->xListEnd.pxPrevious = ( xListItem * ) &( pxList->xListEnd ); + + pxList->uxNumberOfItems = ( unsigned portBASE_TYPE ) 0U; +} +/*-----------------------------------------------------------*/ + +void vListInitialiseItem( xListItem *pxItem ) +{ + /* Make sure the list item is not recorded as being on a list. */ + pxItem->pvContainer = NULL; +} +/*-----------------------------------------------------------*/ + +void vListInsertEnd( xList *pxList, xListItem *pxNewListItem ) +{ +volatile xListItem * pxIndex; + + /* Insert a new list item into pxList, but rather than sort the list, + makes the new list item the last item to be removed by a call to + pvListGetOwnerOfNextEntry. This means it has to be the item pointed to by + the pxIndex member. */ + pxIndex = pxList->pxIndex; + + pxNewListItem->pxNext = pxIndex->pxNext; + pxNewListItem->pxPrevious = pxList->pxIndex; + pxIndex->pxNext->pxPrevious = ( volatile xListItem * ) pxNewListItem; + pxIndex->pxNext = ( volatile xListItem * ) pxNewListItem; + pxList->pxIndex = ( volatile xListItem * ) pxNewListItem; + + /* Remember which list the item is in. */ + pxNewListItem->pvContainer = ( void * ) pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +void vListInsert( xList *pxList, xListItem *pxNewListItem ) +{ +volatile xListItem *pxIterator; +portTickType xValueOfInsertion; + + /* Insert the new list item into the list, sorted in ulListItem order. */ + xValueOfInsertion = pxNewListItem->xItemValue; + + /* If the list already contains a list item with the same item value then + the new list item should be placed after it. This ensures that TCB's which + are stored in ready lists (all of which have the same ulListItem value) + get an equal share of the CPU. However, if the xItemValue is the same as + the back marker the iteration loop below will not end. This means we need + to guard against this by checking the value first and modifying the + algorithm slightly if necessary. */ + if( xValueOfInsertion == portMAX_DELAY ) + { + pxIterator = pxList->xListEnd.pxPrevious; + } + else + { + /* *** NOTE *********************************************************** + If you find your application is crashing here then likely causes are: + 1) Stack overflow - + see http://www.freertos.org/Stacks-and-stack-overflow-checking.html + 2) Incorrect interrupt priority assignment, especially on Cortex-M3 + parts where numerically high priority values denote low actual + interrupt priories, which can seem counter intuitive. See + configMAX_SYSCALL_INTERRUPT_PRIORITY on http://www.freertos.org/a00110.html + 3) Calling an API function from within a critical section or when + the scheduler is suspended. + 4) Using a queue or semaphore before it has been initialised or + before the scheduler has been started (are interrupts firing + before vTaskStartScheduler() has been called?). + See http://www.freertos.org/FAQHelp.html for more tips. + **********************************************************************/ + + for( pxIterator = ( xListItem * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) + { + /* There is nothing to do here, we are just iterating to the + wanted insertion position. */ + } + } + + pxNewListItem->pxNext = pxIterator->pxNext; + pxNewListItem->pxNext->pxPrevious = ( volatile xListItem * ) pxNewListItem; + pxNewListItem->pxPrevious = pxIterator; + pxIterator->pxNext = ( volatile xListItem * ) pxNewListItem; + + /* Remember which list the item is in. This allows fast removal of the + item later. */ + pxNewListItem->pvContainer = ( void * ) pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +void vListRemove( xListItem *pxItemToRemove ) +{ +xList * pxList; + + pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; + pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; + + /* The list item knows which list it is in. Obtain the list from the list + item. */ + pxList = ( xList * ) pxItemToRemove->pvContainer; + + /* Make sure the index is left pointing to a valid item. */ + if( pxList->pxIndex == pxItemToRemove ) + { + pxList->pxIndex = pxItemToRemove->pxPrevious; + } + + pxItemToRemove->pvContainer = NULL; + ( pxList->uxNumberOfItems )--; +} +#endif +/*-----------------------------------------------------------*/ + diff --git a/sdk/Src/OS/RTOS/source/queue.c b/sdk/Src/OS/RTOS/source/queue.c new file mode 100644 index 0000000..94ca62d --- /dev/null +++ b/sdk/Src/OS/RTOS/source/queue.c @@ -0,0 +1,1605 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#include +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "rtosqueue.h" + +#if ( configUSE_CO_ROUTINES == 1 ) + #include "croutine.h" +#endif +#include "wm_config.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#if TLS_OS_FREERTOS +/*----------------------------------------------------------- + * PUBLIC LIST API documented in list.h + *----------------------------------------------------------*/ + +/* Constants used with the cRxLock and cTxLock structure members. */ +#define queueUNLOCKED ( ( signed portBASE_TYPE ) -1 ) +#define queueLOCKED_UNMODIFIED ( ( signed portBASE_TYPE ) 0 ) + +#define queueERRONEOUS_UNBLOCK ( -1 ) + +/* For internal use only. */ +#define queueSEND_TO_BACK ( 0 ) +#define queueSEND_TO_FRONT ( 1 ) + +/* Effectively make a union out of the xQUEUE structure. */ +#define pxMutexHolder pcTail +#define uxQueueType pcHead +#define uxRecursiveCallCount pcReadFrom +#define queueQUEUE_IS_MUTEX NULL + +/* Semaphores do not actually store or copy data, so have an items size of +zero. */ +#define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( ( unsigned portBASE_TYPE ) 0 ) +#define queueDONT_BLOCK ( ( portTickType ) 0U ) +#define queueMUTEX_GIVE_BLOCK_TIME ( ( portTickType ) 0U ) + +/* + * Definition of the queue used by the scheduler. + * Items are queued by copy, not reference. + */ +#if 0 +typedef struct QueueDefinition +{ + signed char *pcHead; /*< Points to the beginning of the queue storage area. */ + signed char *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + + signed char *pcWriteTo; /*< Points to the free next place in the storage area. */ + signed char *pcReadFrom; /*< Points to the last place that a queued item was read from. */ + + xList xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ + xList xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ + + volatile unsigned portBASE_TYPE uxMessagesWaiting;/*< The number of items currently in the queue. */ + unsigned portBASE_TYPE uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ + unsigned portBASE_TYPE uxItemSize; /*< The size of each items that the queue will hold. */ + + signed portBASE_TYPE xRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + signed portBASE_TYPE xTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + +} xQUEUE; +#endif +/*-----------------------------------------------------------*/ + +/* + * Inside this file xQueueHandle is a pointer to a xQUEUE structure. + * To keep the definition private the API header file defines it as a + * pointer to void. + */ +//typedef xQUEUE * xQueueHandle; + +/* + * Prototypes for public functions are included here so we don't have to + * include the API header file (as it defines xQueueHandle differently). These + * functions are documented in the API header file. + */ +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueGenericSend( xQueueHandle xQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) PRIVILEGED_FUNCTION; +unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; +void vQueueDelete( xQueueHandle xQueue ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void * const pvBuffer, signed portBASE_TYPE *pxTaskWoken ) PRIVILEGED_FUNCTION; +xQueueHandle xQueueCreateMutex( void ) PRIVILEGED_FUNCTION; +xQueueHandle xQueueCreateCountingSemaphore( unsigned portBASE_TYPE uxCountValue, unsigned portBASE_TYPE uxInitialCount ) PRIVILEGED_FUNCTION; +portBASE_TYPE xQueueTakeMutexRecursive( xQueueHandle xMutex, portTickType xBlockTime ) PRIVILEGED_FUNCTION; +portBASE_TYPE xQueueGiveMutexRecursive( xQueueHandle xMutex ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueAltGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueAltGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueIsQueueEmptyFromISR( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueIsQueueFullFromISR( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; +unsigned portBASE_TYPE uxQueueMessagesWaitingFromISR( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; +void vQueueWaitForMessageRestricted( xQueueHandle pxQueue, portTickType xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * Co-routine queue functions differ from task queue functions. Co-routines are + * an optional component. + */ +#if configUSE_CO_ROUTINES == 1 + signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ) PRIVILEGED_FUNCTION; + signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ) PRIVILEGED_FUNCTION; + signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) PRIVILEGED_FUNCTION; + signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) PRIVILEGED_FUNCTION; +#endif + +/* + * The queue registry is just a means for kernel aware debuggers to locate + * queue structures. It has no other purpose so is an optional component. + */ +#if configQUEUE_REGISTRY_SIZE > 0 + + /* The type stored within the queue registry array. This allows a name + to be assigned to each queue making kernel aware debugging a little + more user friendly. */ + typedef struct QUEUE_REGISTRY_ITEM + { + signed char *pcQueueName; + xQueueHandle xHandle; + } xQueueRegistryItem; + + /* The queue registry is simply an array of xQueueRegistryItem structures. + The pcQueueName member of a structure being NULL is indicative of the + array position being vacant. */ + xQueueRegistryItem xQueueRegistry[ configQUEUE_REGISTRY_SIZE ]; + + /* Removes a queue from the registry by simply setting the pcQueueName + member to NULL. */ + static void vQueueUnregisterQueue( xQueueHandle xQueue ) PRIVILEGED_FUNCTION; + void vQueueAddToRegistry( xQueueHandle xQueue, signed char *pcQueueName ) PRIVILEGED_FUNCTION; +#endif + +/* + * Unlocks a queue locked by a call to prvLockQueue. Locking a queue does not + * prevent an ISR from adding or removing items to the queue, but does prevent + * an ISR from removing tasks from the queue event lists. If an ISR finds a + * queue is locked it will instead increment the appropriate queue lock count + * to indicate that a task may require unblocking. When the queue in unlocked + * these lock counts are inspected, and the appropriate action taken. + */ +static void prvUnlockQueue( xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any data in a queue. + * + * @return pdTRUE if the queue contains no items, otherwise pdFALSE. + */ +static signed portBASE_TYPE prvIsQueueEmpty( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any space in a queue. + * + * @return pdTRUE if there is no space, otherwise pdFALSE; + */ +static signed portBASE_TYPE prvIsQueueFull( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Copies an item into the queue, either at the front of the queue or the + * back of the queue. + */ +static void prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xPosition ) PRIVILEGED_FUNCTION; + +/* + * Copies an item out of a queue. + */ +static void prvCopyDataFromQueue( xQUEUE * const pxQueue, const void *pvBuffer ) PRIVILEGED_FUNCTION; +/*-----------------------------------------------------------*/ + +/* + * Macro to mark a queue as locked. Locking a queue prevents an ISR from + * accessing the queue event lists. + */ +#define prvLockQueue( pxQueue ) \ + taskENTER_CRITICAL(); \ + { \ + if( ( pxQueue )->xRxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->xRxLock = queueLOCKED_UNMODIFIED; \ + } \ + if( ( pxQueue )->xTxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->xTxLock = queueLOCKED_UNMODIFIED; \ + } \ + } \ + taskEXIT_CRITICAL() +/*-----------------------------------------------------------*/ + + +/*----------------------------------------------------------- + * PUBLIC QUEUE MANAGEMENT API documented in queue.h + *----------------------------------------------------------*/ + +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ) +{ +xQUEUE *pxNewQueue; +size_t xQueueSizeInBytes; +xQueueHandle xReturn = NULL; + + /* Allocate the new queue structure. */ + if( uxQueueLength > ( unsigned portBASE_TYPE ) 0 ) + { + pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) ); + if( pxNewQueue != NULL ) + { + /* Create the list of pointers to queue items. The queue is one byte + longer than asked for to make wrap checking easier/faster. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1; + + pxNewQueue->pcHead = ( signed char * ) pvPortMalloc( xQueueSizeInBytes ); + if( pxNewQueue->pcHead != NULL ) + { + /* Initialise the queue members as described above where the + queue type is defined. */ + pxNewQueue->pcTail = pxNewQueue->pcHead + ( uxQueueLength * uxItemSize ); + pxNewQueue->uxMessagesWaiting = ( unsigned portBASE_TYPE ) 0U; + pxNewQueue->pcWriteTo = pxNewQueue->pcHead; + pxNewQueue->pcReadFrom = pxNewQueue->pcHead + ( ( uxQueueLength - ( unsigned portBASE_TYPE ) 1U ) * uxItemSize ); + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + pxNewQueue->xRxLock = queueUNLOCKED; + pxNewQueue->xTxLock = queueUNLOCKED; + + /* Likewise ensure the event queues start with the correct state. */ + vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); + + traceQUEUE_CREATE( pxNewQueue ); + xReturn = pxNewQueue; + } + else + { + traceQUEUE_CREATE_FAILED(); + vPortFree( pxNewQueue ); + } + } + } + + configASSERT( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +//��װ�ýӿڵ�Ŀ��������Ľӿڶ��е�ַ���ڲ�����ģ�����֮ǰucos�Ľӿ����ⲿ����� +xQueueHandle xQueueCreateExt( void *QueueStart, unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ) +{ +xQUEUE *pxNewQueue; +//size_t xQueueSizeInBytes; +xQueueHandle xReturn = NULL; + + /* Allocate the new queue structure. */ + if( uxQueueLength > ( unsigned portBASE_TYPE ) 0 ) + { + pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) ); + if( pxNewQueue != NULL ) + { + /* Create the list of pointers to queue items. The queue is one byte + longer than asked for to make wrap checking easier/faster. */ + // xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1; + + pxNewQueue->pcHead = ( signed char * ) QueueStart;//pvPortMalloc( xQueueSizeInBytes ); + if( pxNewQueue->pcHead != NULL ) + { + /* Initialise the queue members as described above where the + queue type is defined. */ + pxNewQueue->pcTail = pxNewQueue->pcHead + ( uxQueueLength * uxItemSize ); + pxNewQueue->uxMessagesWaiting = ( unsigned portBASE_TYPE ) 0U; + pxNewQueue->pcWriteTo = pxNewQueue->pcHead; + pxNewQueue->pcReadFrom = pxNewQueue->pcHead + ( ( uxQueueLength - ( unsigned portBASE_TYPE ) 1U ) * uxItemSize ); + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + pxNewQueue->xRxLock = queueUNLOCKED; + pxNewQueue->xTxLock = queueUNLOCKED; + + /* Likewise ensure the event queues start with the correct state. */ + vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); + + traceQUEUE_CREATE( pxNewQueue ); + xReturn = pxNewQueue; + } + else + { + traceQUEUE_CREATE_FAILED(); + vPortFree( pxNewQueue ); + } + } + } + + configASSERT( xReturn ); + + return xReturn; +} + + +#if ( configUSE_MUTEXES == 1 ) + + xQueueHandle xQueueCreateMutex( void ) + { + xQUEUE *pxNewQueue; + + /* Allocate the new queue structure. */ + pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) ); + if( pxNewQueue != NULL ) + { + /* Information required for priority inheritance. */ + pxNewQueue->pxMutexHolder = NULL; + pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; + + /* Queues used as a mutex no data is actually copied into or out + of the queue. */ + pxNewQueue->pcWriteTo = NULL; + pxNewQueue->pcReadFrom = NULL; + + /* Each mutex has a length of 1 (like a binary semaphore) and + an item size of 0 as nothing is actually copied into or out + of the mutex. */ + pxNewQueue->uxMessagesWaiting = ( unsigned portBASE_TYPE ) 0U; + pxNewQueue->uxLength = ( unsigned portBASE_TYPE ) 1U; + pxNewQueue->uxItemSize = ( unsigned portBASE_TYPE ) 0U; + pxNewQueue->xRxLock = queueUNLOCKED; + pxNewQueue->xTxLock = queueUNLOCKED; + + /* Ensure the event queues start with the correct state. */ + vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); + + /* Start with the semaphore in the expected state. */ + xQueueGenericSend( pxNewQueue, NULL, ( portTickType ) 0U, queueSEND_TO_BACK ); + + traceCREATE_MUTEX( pxNewQueue ); + } + else + { + traceCREATE_MUTEX_FAILED(); + } + + configASSERT( pxNewQueue ); + return pxNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if configUSE_RECURSIVE_MUTEXES == 1 + + portBASE_TYPE xQueueGiveMutexRecursive( xQueueHandle pxMutex ) + { + portBASE_TYPE xReturn; + + configASSERT( pxMutex ); + + /* If this is the task that holds the mutex then pxMutexHolder will not + change outside of this task. If this task does not hold the mutex then + pxMutexHolder can never coincidentally equal the tasks handle, and as + this is the only condition we are interested in it does not matter if + pxMutexHolder is accessed simultaneously by another task. Therefore no + mutual exclusion is required to test the pxMutexHolder variable. */ + if( pxMutex->pxMutexHolder == xTaskGetCurrentTaskHandle() ) + { + traceGIVE_MUTEX_RECURSIVE( pxMutex ); + + /* uxRecursiveCallCount cannot be zero if pxMutexHolder is equal to + the task handle, therefore no underflow check is required. Also, + uxRecursiveCallCount is only modified by the mutex holder, and as + there can only be one, no mutual exclusion is required to modify the + uxRecursiveCallCount member. */ + ( pxMutex->uxRecursiveCallCount )--; + + /* Have we unwound the call count? */ + if( pxMutex->uxRecursiveCallCount == 0 ) + { + /* Return the mutex. This will automatically unblock any other + task that might be waiting to access the mutex. */ + xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK ); + } + + xReturn = pdPASS; + } + else + { + /* We cannot give the mutex because we are not the holder. */ + xReturn = pdFAIL; + + traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if configUSE_RECURSIVE_MUTEXES == 1 + + portBASE_TYPE xQueueTakeMutexRecursive( xQueueHandle pxMutex, portTickType xBlockTime ) + { + portBASE_TYPE xReturn; + + configASSERT( pxMutex ); + + /* Comments regarding mutual exclusion as per those within + xQueueGiveMutexRecursive(). */ + + traceTAKE_MUTEX_RECURSIVE( pxMutex ); + + if( pxMutex->pxMutexHolder == xTaskGetCurrentTaskHandle() ) + { + ( pxMutex->uxRecursiveCallCount )++; + xReturn = pdPASS; + } + else + { + xReturn = xQueueGenericReceive( pxMutex, NULL, xBlockTime, pdFALSE ); + + /* pdPASS will only be returned if we successfully obtained the mutex, + we may have blocked to reach here. */ + if( xReturn == pdPASS ) + { + ( pxMutex->uxRecursiveCallCount )++; + } + else + { + traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + } + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if configUSE_COUNTING_SEMAPHORES == 1 + + xQueueHandle xQueueCreateCountingSemaphore( unsigned portBASE_TYPE uxCountValue, unsigned portBASE_TYPE uxInitialCount ) + { + xQueueHandle pxHandle; + + pxHandle = xQueueCreate( ( unsigned portBASE_TYPE ) uxCountValue, queueSEMAPHORE_QUEUE_ITEM_LENGTH ); + + if( pxHandle != NULL ) + { + pxHandle->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + + configASSERT( pxHandle ); + return pxHandle; + } + +#endif /* configUSE_COUNTING_SEMAPHORES */ +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) +{ +signed portBASE_TYPE xEntryTimeSet = pdFALSE; +xTimeOutType xTimeOut; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) ); + + /* This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? To be running we must be + the highest priority task wanting to access the queue. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + traceQUEUE_SEND( pxQueue ); + prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. Yes it is ok to do + this from within the critical section - the kernel + takes care of that. */ + portYIELD_WITHIN_API(); + } + } + + taskEXIT_CRITICAL(); + + /* Return to the original privilege level before exiting the + function. */ + return pdPASS; + } + else + { + if( xTicksToWait == ( portTickType ) 0 ) + { + /* The queue was full and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + + /* Return to the original privilege level before exiting + the function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was full and a block time was specified so + configure the timeout structure. */ + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + + /* Unlocking the queue means queue events can effect the + event list. It is possible that interrupts occurring now + remove this task from the event list again - but as the + scheduler is suspended the task will go onto the pending + ready last instead of the actual ready list. */ + prvUnlockQueue( pxQueue ); + + /* Resuming the scheduler will move tasks from the pending + ready list into the ready list - so it is feasible that this + task is already in a ready list before it yields - in which + case the yield will not cause a context switch unless there + is also a higher priority task in the pending ready list. */ + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + /* Return to the original privilege level before exiting the + function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + } +} +/*-----------------------------------------------------------*/ + +#if configUSE_ALTERNATIVE_API == 1 + + signed portBASE_TYPE xQueueAltGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) + { + signed portBASE_TYPE xEntryTimeSet = pdFALSE; + xTimeOutType xTimeOut; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) ); + + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? To be running we must be + the highest priority task wanting to access the queue. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + traceQUEUE_SEND( pxQueue ); + prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. */ + portYIELD_WITHIN_API(); + } + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( portTickType ) 0 ) + { + taskEXIT_CRITICAL(); + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + portYIELD_WITHIN_API(); + } + } + else + { + taskEXIT_CRITICAL(); + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + } + taskEXIT_CRITICAL(); + } + } + +#endif /* configUSE_ALTERNATIVE_API */ +/*-----------------------------------------------------------*/ + +#if configUSE_ALTERNATIVE_API == 1 + + signed portBASE_TYPE xQueueAltGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) + { + signed portBASE_TYPE xEntryTimeSet = pdFALSE; + xTimeOutType xTimeOut; + signed char *pcOriginalReadPosition; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) ); + + for( ;; ) + { + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Remember our read position in case we are just peeking. */ + pcOriginalReadPosition = pxQueue->pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + + if( xJustPeeking == pdFALSE ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* We are actually removing data. */ + --( pxQueue->uxMessagesWaiting ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle(); + } + } + #endif + + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE ) + { + portYIELD_WITHIN_API(); + } + } + } + else + { + traceQUEUE_PEEK( pxQueue ); + + /* We are not removing the data, so reset our read + pointer. */ + pxQueue->pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + portYIELD_WITHIN_API(); + } + } + + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( portTickType ) 0 ) + { + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + portENTER_CRITICAL(); + vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); + portEXIT_CRITICAL(); + } + } + #endif + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + portYIELD_WITHIN_API(); + } + } + else + { + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + } + taskEXIT_CRITICAL(); + } + } + + +#endif /* configUSE_ALTERNATIVE_API */ +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ) +{ +signed portBASE_TYPE xReturn; +unsigned portBASE_TYPE uxSavedInterruptStatus; + + configASSERT( pxQueue ); + configASSERT( pxHigherPriorityTaskWoken ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) ); + + /* Similar to xQueueGenericSend, except we don't block if there is no room + in the queue. Also we don't directly wake a task that was blocked on a + queue read, instead we return a flag to say whether a context switch is + required or not (i.e. has a task with a higher priority than us been woken + by this post). */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + traceQUEUE_SEND_FROM_ISR( pxQueue ); + + prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If the queue is locked we do not alter the event list. This will + be done when the queue is unlocked later. */ + if( pxQueue->xTxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + *pxHigherPriorityTaskWoken = pdTRUE; + } + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was posted while it was locked. */ + ++( pxQueue->xTxLock ); + } + + xReturn = pdPASS; + } + else + { + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) +{ +signed portBASE_TYPE xEntryTimeSet = pdFALSE; +xTimeOutType xTimeOut; +signed char *pcOriginalReadPosition; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) ); + + /* This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there data in the queue now? To be running we must be + the highest priority task wanting to access the queue. */ + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Remember our read position in case we are just peeking. */ + pcOriginalReadPosition = pxQueue->pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + + if( xJustPeeking == pdFALSE ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* We are actually removing data. */ + --( pxQueue->uxMessagesWaiting ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle(); + } + } + #endif + + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE ) + { + portYIELD_WITHIN_API(); + } + } + } + else + { + traceQUEUE_PEEK( pxQueue ); + + /* We are not removing the data, so reset our read + pointer. */ + pxQueue->pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + portYIELD_WITHIN_API(); + } + } + + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( portTickType ) 0 ) + { + /* The queue was empty and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + configure the timeout structure. */ + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + portENTER_CRITICAL(); + { + vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); + } + portEXIT_CRITICAL(); + } + } + #endif + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + } +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void * const pvBuffer, signed portBASE_TYPE *pxTaskWoken ) +{ +signed portBASE_TYPE xReturn; +unsigned portBASE_TYPE uxSavedInterruptStatus; + + configASSERT( pxQueue ); + configASSERT( pxTaskWoken ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) ); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* We cannot block from an ISR, so check there is data available. */ + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + --( pxQueue->uxMessagesWaiting ); + + /* If the queue is locked we will not modify the event list. Instead + we update the lock count so the task that unlocks the queue will know + that an ISR has removed data while the queue was locked. */ + if( pxQueue->xRxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than us so + force a context switch. */ + *pxTaskWoken = pdTRUE; + } + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was removed while it was locked. */ + ++( pxQueue->xRxLock ); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle pxQueue ) +{ +unsigned portBASE_TYPE uxReturn; + + configASSERT( pxQueue ); + + taskENTER_CRITICAL(); + uxReturn = pxQueue->uxMessagesWaiting; + taskEXIT_CRITICAL(); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE uxQueueMessagesWaitingFromISR( const xQueueHandle pxQueue ) +{ +unsigned portBASE_TYPE uxReturn; + + configASSERT( pxQueue ); + + uxReturn = pxQueue->uxMessagesWaiting; + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +void vQueueDelete( xQueueHandle pxQueue ) +{ + configASSERT( pxQueue ); + + traceQUEUE_DELETE( pxQueue ); + vQueueUnregisterQueue( pxQueue ); + vPortFree( pxQueue->pcHead ); + vPortFree( pxQueue ); +} + + +void vQueueDeleteExt( xQueueHandle pxQueue ) +{ + configASSERT( pxQueue ); + + traceQUEUE_DELETE( pxQueue ); + vQueueUnregisterQueue( pxQueue ); +// vPortFree( pxQueue->pcHead ); //�ⲿ�ͷ� + vPortFree( pxQueue ); +} + +/*-----------------------------------------------------------*/ + +static void prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xPosition ) +{ + if( pxQueue->uxItemSize == ( unsigned portBASE_TYPE ) 0 ) + { + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* The mutex is no longer being held. */ + vTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder ); + pxQueue->pxMutexHolder = NULL; + } + } + #endif + } + else if( xPosition == queueSEND_TO_BACK ) + { + memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize ); + pxQueue->pcWriteTo += pxQueue->uxItemSize; + if( pxQueue->pcWriteTo >= pxQueue->pcTail ) + { + pxQueue->pcWriteTo = pxQueue->pcHead; + } + } + else + { + memcpy( ( void * ) pxQueue->pcReadFrom, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize ); + pxQueue->pcReadFrom -= pxQueue->uxItemSize; + if( pxQueue->pcReadFrom < pxQueue->pcHead ) + { + pxQueue->pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize ); + } + } + + ++( pxQueue->uxMessagesWaiting ); +} +/*-----------------------------------------------------------*/ + +static void prvCopyDataFromQueue( xQUEUE * const pxQueue, const void *pvBuffer ) +{ + if( pxQueue->uxQueueType != queueQUEUE_IS_MUTEX ) + { + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + } +} +/*-----------------------------------------------------------*/ + +static void prvUnlockQueue( xQueueHandle pxQueue ) +{ + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ + + /* The lock counts contains the number of extra data items placed or + removed from the queue while the queue was locked. When a queue is + locked items can be added or removed, but the event lists cannot be + updated. */ + taskENTER_CRITICAL(); + { + /* See if data was added to the queue while it was locked. */ + while( pxQueue->xTxLock > queueLOCKED_UNMODIFIED ) + { + /* Data was posted while the queue was locked. Are any tasks + blocked waiting for data to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + vTaskMissedYield(); + } + + --( pxQueue->xTxLock ); + } + else + { + break; + } + } + + pxQueue->xTxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); + + /* Do the same for the Rx lock. */ + taskENTER_CRITICAL(); + { + while( pxQueue->xRxLock > queueLOCKED_UNMODIFIED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + vTaskMissedYield(); + } + + --( pxQueue->xRxLock ); + } + else + { + break; + } + } + + pxQueue->xRxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +static signed portBASE_TYPE prvIsQueueEmpty( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + taskENTER_CRITICAL(); + xReturn = ( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ); + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueIsQueueEmptyFromISR( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + configASSERT( pxQueue ); + xReturn = ( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static signed portBASE_TYPE prvIsQueueFull( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + taskENTER_CRITICAL(); + xReturn = ( pxQueue->uxMessagesWaiting == pxQueue->uxLength ); + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueIsQueueFullFromISR( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + configASSERT( pxQueue ); + xReturn = ( pxQueue->uxMessagesWaiting == pxQueue->uxLength ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) +{ +signed portBASE_TYPE xReturn; + + /* If the queue is already full we may have to block. A critical section + is required to prevent an interrupt removing something from the queue + between the check to see if the queue is full and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + /* The queue is full - do we want to block or just leave without + posting? */ + if( xTicksToWait > ( portTickType ) 0 ) + { + /* As this is called from a coroutine we cannot block directly, but + return indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portNOP(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + xReturn = pdPASS; + + /* Were any co-routines waiting for data to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechanism is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The co-routine waiting has a higher priority so record + that a yield might be appropriate. */ + xReturn = errQUEUE_YIELD; + } + } + } + else + { + xReturn = errQUEUE_FULL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) +{ +signed portBASE_TYPE xReturn; + + /* If the queue is already empty we may have to block. A critical section + is required to prevent an interrupt adding something to the queue + between the check to see if the queue is empty and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ) + { + /* There are no messages in the queue, do we want to block or just + leave with nothing? */ + if( xTicksToWait > ( portTickType ) 0 ) + { + /* As this is a co-routine we cannot block directly, but return + indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portNOP(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Data is available from the queue. */ + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + xReturn = pdPASS; + + /* Were any co-routines waiting for space to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechanism is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + xReturn = errQUEUE_YIELD; + } + } + } + else + { + xReturn = pdFAIL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + + + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ) +{ + /* Cannot block within an ISR so if there is no space on the queue then + exit without doing anything. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + + /* We only want to wake one co-routine per ISR, so check that a + co-routine has not already been woken. */ + if( xCoRoutinePreviouslyWoken == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + return pdTRUE; + } + } + } + } + + return xCoRoutinePreviouslyWoken; +} +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxCoRoutineWoken ) +{ +signed portBASE_TYPE xReturn; + + /* We cannot block from an ISR, so check there is data available. If + not then just leave without doing anything. */ + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Copy the data from the queue. */ + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + if( ( *pxCoRoutineWoken ) == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + *pxCoRoutineWoken = pdTRUE; + } + } + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + +#if configQUEUE_REGISTRY_SIZE > 0 + + void vQueueAddToRegistry( xQueueHandle xQueue, signed char *pcQueueName ) + { + unsigned portBASE_TYPE ux; + + /* See if there is an empty space in the registry. A NULL name denotes + a free slot. */ + for( ux = ( unsigned portBASE_TYPE ) 0U; ux < ( unsigned portBASE_TYPE ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].pcQueueName == NULL ) + { + /* Store the information on this queue. */ + xQueueRegistry[ ux ].pcQueueName = pcQueueName; + xQueueRegistry[ ux ].xHandle = xQueue; + break; + } + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if configQUEUE_REGISTRY_SIZE > 0 + + static void vQueueUnregisterQueue( xQueueHandle xQueue ) + { + unsigned portBASE_TYPE ux; + + /* See if the handle of the queue being unregistered in actually in the + registry. */ + for( ux = ( unsigned portBASE_TYPE ) 0U; ux < ( unsigned portBASE_TYPE ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + /* Set the name to NULL to show that this slot if free again. */ + xQueueRegistry[ ux ].pcQueueName = NULL; + break; + } + } + + } + +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_TIMERS == 1 + + void vQueueWaitForMessageRestricted( xQueueHandle pxQueue, portTickType xTicksToWait ) + { + /* This function should not be called by application code hence the + 'Restricted' in its name. It is not part of the public API. It is + designed for use by kernel code, and has special calling requirements. + It can result in vListInsert() being called on a list that can only + possibly ever have one item in it, so the list will be fast, but even + so it should be called with the scheduler locked and not from a critical + section. */ + + /* Only do anything if there are no messages in the queue. This function + will not actually cause the task to block, just place it on a blocked + list. It will not block until the scheduler is unlocked - at which + time a yield will be performed. If an item is added to the queue while + the queue is locked, and the calling task blocks on the queue, then the + calling task will be immediately unblocked when the queue is unlocked. */ + prvLockQueue( pxQueue ); + if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0U ) + { + /* There is nothing in the queue, block for the specified period. */ + vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + } + prvUnlockQueue( pxQueue ); + } + +#endif +#endif diff --git a/sdk/Src/OS/RTOS/source/rtostimers.c b/sdk/Src/OS/RTOS/source/rtostimers.c new file mode 100644 index 0000000..cc096d5 --- /dev/null +++ b/sdk/Src/OS/RTOS/source/rtostimers.c @@ -0,0 +1,721 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include +#include "FreeRTOS.h" +#include "task.h" +#include "rtosqueue.h" +#include "rtostimers.h" +#include "wm_config.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE +#if TLS_OS_FREERTOS +/* This entire source file will be skipped if the application is not configured +to include software timer functionality. This #if is closed at the very bottom +of this file. If you want to include software timer functionality then ensure +configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ +#if ( configUSE_TIMERS == 1 ) + +/* Misc definitions. */ +#define tmrNO_DELAY ( portTickType ) 0U +#if 0 +/* The definition of the timers themselves. */ +typedef struct tmrTimerControl +{ + const signed char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ + xListItem xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ + portTickType xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */ + unsigned portBASE_TYPE uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one shot timer. */ + void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ + tmrTIMER_CALLBACK pxCallbackFunction; /*<< The function that will be called when the timer expires. */ + void *callback_arg; /*added by dave */ +} xTIMER; +#endif +/* The definition of messages that can be sent and received on the timer +queue. */ +typedef struct tmrTimerQueueMessage +{ + portBASE_TYPE xMessageID; /*<< The command being sent to the timer service task. */ + portTickType xMessageValue; /*<< An optional value used by a subset of commands, for example, when changing the period of a timer. */ + xTIMER * pxTimer; /*<< The timer to which the command will be applied. */ +} xTIMER_MESSAGE; + + +/* The list in which active timers are stored. Timers are referenced in expire +time order, with the nearest expiry time at the front of the list. Only the +timer service task is allowed to access xActiveTimerList. */ +PRIVILEGED_DATA static xList xActiveTimerList1; +PRIVILEGED_DATA static xList xActiveTimerList2; +PRIVILEGED_DATA static xList *pxCurrentTimerList; +PRIVILEGED_DATA static xList *pxOverflowTimerList; + +/* A queue that is used to send commands to the timer service task. */ +PRIVILEGED_DATA static xQueueHandle xTimerQueue = NULL; + +#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) + + PRIVILEGED_DATA static xTaskHandle xTimerTaskHandle = NULL; + +#endif + +/*-----------------------------------------------------------*/ + +/* + * Initialise the infrastructure used by the timer service task if it has not + * been initialised already. + */ +static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; + +/* + * The timer service task (daemon). Timer functionality is controlled by this + * task. Other tasks communicate with the timer service task using the + * xTimerQueue queue. + */ +static void prvTimerTask( void *pvParameters ) PRIVILEGED_FUNCTION; + +/* + * Called by the timer service task to interpret and process a command it + * received on the timer queue. + */ +static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION; + +/* + * Insert the timer into either xActiveTimerList1, or xActiveTimerList2, + * depending on if the expire time causes a timer counter overflow. + */ +static portBASE_TYPE prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xTimeNow, portTickType xCommandTime ) PRIVILEGED_FUNCTION; + +/* + * An active timer has reached its expire time. Reload the timer if it is an + * auto reload timer, then call its callback. + */ +static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xTimeNow ) PRIVILEGED_FUNCTION; + +/* + * The tick count has overflowed. Switch the timer lists after ensuring the + * current timer list does not still reference some timers. + */ +static void prvSwitchTimerLists( portTickType xLastTime ) PRIVILEGED_FUNCTION; + +/* + * Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE + * if a tick count overflow occurred since prvSampleTimeNow() was last called. + */ +static portTickType prvSampleTimeNow( portBASE_TYPE *pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION; + +/* + * If the timer list contains any active timers then return the expire time of + * the timer that will expire first and set *pxListWasEmpty to false. If the + * timer list does not contain any timers then return 0 and set *pxListWasEmpty + * to pdTRUE. + */ +static portTickType prvGetNextExpireTime( portBASE_TYPE *pxListWasEmpty ) PRIVILEGED_FUNCTION; + +/* + * If a timer has expired, process it. Otherwise, block the timer service task + * until either a timer does expire or a command is received. + */ +static void prvProcessTimerOrBlockTask( portTickType xNextExpireTime, portBASE_TYPE xListWasEmpty ) PRIVILEGED_FUNCTION; + +/*-----------------------------------------------------------*/ + +portBASE_TYPE xTimerCreateTimerTask( void ) +{ +portBASE_TYPE xReturn = pdFAIL; + + /* This function is called when the scheduler is started if + configUSE_TIMERS is set to 1. Check that the infrastructure used by the + timer service task has been created/initialised. If timers have already + been created then the initialisation will already have been performed. */ + prvCheckForValidListAndQueue(); + + if( xTimerQueue != NULL ) + { + #if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) + { + /* Create the timer task, storing its handle in xTimerTaskHandle so + it can be returned by the xTimerGetTimerDaemonTaskHandle() function. */ + xReturn = xTaskCreate( prvTimerTask, ( const signed char * ) "TIMER", ( unsigned short ) configTIMER_TASK_STACK_DEPTH, NULL, ( unsigned portBASE_TYPE ) configTIMER_TASK_PRIORITY, &xTimerTaskHandle ); + } + #else + { + /* Create the timer task without storing its handle. */ + xReturn = xTaskCreate( prvTimerTask, ( const signed char * ) "TIMER", ( unsigned short ) configTIMER_TASK_STACK_DEPTH, NULL, ( unsigned portBASE_TYPE ) configTIMER_TASK_PRIORITY, NULL); + } + #endif + } + + configASSERT( xReturn ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +xTimerHandle xTimerCreate( const signed char *pcTimerName, portTickType xTimerPeriodInTicks, unsigned portBASE_TYPE uxAutoReload, void *pvTimerID, tmrTIMER_CALLBACK pxCallbackFunction ) +{ +xTIMER *pxNewTimer; + + /* Allocate the timer structure. */ + if( xTimerPeriodInTicks == ( portTickType ) 0U ) + { + pxNewTimer = NULL; + configASSERT( ( xTimerPeriodInTicks > 0 ) ); + } + else + { + pxNewTimer = ( xTIMER * ) pvPortMalloc( sizeof( xTIMER ) ); + if( pxNewTimer != NULL ) + { + /* Ensure the infrastructure used by the timer service task has been + created/initialised. */ + prvCheckForValidListAndQueue(); + + /* Initialise the timer structure members using the function parameters. */ + pxNewTimer->pcTimerName = pcTimerName; + pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; + pxNewTimer->uxAutoReload = uxAutoReload; + pxNewTimer->pvTimerID = pvTimerID; + pxNewTimer->pxCallbackFunction = pxCallbackFunction; + pxNewTimer->callback_arg = NULL; //add by dave + vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + + traceTIMER_CREATE( pxNewTimer ); + } + else + { + traceTIMER_CREATE_FAILED(); + } + } + + return ( xTimerHandle ) pxNewTimer; +} + +xTimerHandle xTimerCreateExt( const signed char *pcTimerName, portTickType xTimerPeriodInTicks, unsigned portBASE_TYPE uxAutoReload, void *pvTimerID, tmrTIMER_CALLBACK pxCallbackFunction, void *callback_arg ) +{ +xTIMER *pxNewTimer; + + /* Allocate the timer structure. */ + if( xTimerPeriodInTicks == ( portTickType ) 0U ) + { + pxNewTimer = NULL; + configASSERT( ( xTimerPeriodInTicks > 0 ) ); + } + else + { + pxNewTimer = ( xTIMER * ) pvPortMalloc( sizeof( xTIMER ) ); + if( pxNewTimer != NULL ) + { + /* Ensure the infrastructure used by the timer service task has been + created/initialised. */ + prvCheckForValidListAndQueue(); + + /* Initialise the timer structure members using the function parameters. */ + pxNewTimer->pcTimerName = pcTimerName; + pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; + pxNewTimer->uxAutoReload = uxAutoReload; + pxNewTimer->pvTimerID = pvTimerID; + pxNewTimer->pxCallbackFunction = pxCallbackFunction; + pxNewTimer->callback_arg = callback_arg; //add by dave + vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + + traceTIMER_CREATE( pxNewTimer ); + } + else + { + traceTIMER_CREATE_FAILED(); + } + } + + return ( xTimerHandle ) pxNewTimer; +} + +/*-----------------------------------------------------------*/ + +portBASE_TYPE xTimerGenericCommand( xTimerHandle xTimer, portBASE_TYPE xCommandID, portTickType xOptionalValue, portBASE_TYPE *pxHigherPriorityTaskWoken, portTickType xBlockTime ) +{ +portBASE_TYPE xReturn = pdFAIL; +xTIMER_MESSAGE xMessage; + + /* Send a message to the timer service task to perform a particular action + on a particular timer definition. */ + if( xTimerQueue != NULL ) + { + /* Send a command to the timer service task to start the xTimer timer. */ + xMessage.xMessageID = xCommandID; + xMessage.xMessageValue = xOptionalValue; + xMessage.pxTimer = ( xTIMER * ) xTimer; + + if( pxHigherPriorityTaskWoken == NULL ) + { + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xBlockTime ); + } + else + { + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY ); + } + } + else + { + xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); + } + + traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) + + xTaskHandle xTimerGetTimerDaemonTaskHandle( void ) + { + /* If xTimerGetTimerDaemonTaskHandle() is called before the scheduler has been + started, then xTimerTaskHandle will be NULL. */ + configASSERT( ( xTimerTaskHandle != NULL ) ); + return xTimerTaskHandle; + } + +#endif +/*-----------------------------------------------------------*/ + +static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xTimeNow ) +{ +xTIMER *pxTimer; +portBASE_TYPE xResult; + + /* Remove the timer from the list of active timers. A check has already + been performed to ensure the list is not empty. */ + pxTimer = ( xTIMER * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); + vListRemove( &( pxTimer->xTimerListItem ) ); + traceTIMER_EXPIRED( pxTimer ); + + /* If the timer is an auto reload timer then calculate the next + expiry time and re-insert the timer in the list of active timers. */ + if( pxTimer->uxAutoReload == ( unsigned portBASE_TYPE ) pdTRUE ) + { + /* This is the only time a timer is inserted into a list using + a time relative to anything other than the current time. It + will therefore be inserted into the correct list relative to + the time this task thinks it is now, even if a command to + switch lists due to a tick count overflow is already waiting in + the timer queue. */ + if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) == pdTRUE ) + { + /* The timer expired before it was added to the active timer + list. Reload it now. */ + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START, xNextExpireTime, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + } + + /* Call the timer callback. */ + //pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer ); + pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer, pxTimer->callback_arg ); //modify by dave +} +/*-----------------------------------------------------------*/ + +static void prvTimerTask( void *pvParameters ) +{ +portTickType xNextExpireTime; +portBASE_TYPE xListWasEmpty; + + /* Just to avoid compiler warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* Query the timers list to see if it contains any timers, and if so, + obtain the time at which the next timer will expire. */ + xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); + + /* If a timer has expired, process it. Otherwise, block this task + until either a timer does expire, or a command is received. */ + prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty ); + + /* Empty the command queue. */ + prvProcessReceivedCommands(); + } +} +/*-----------------------------------------------------------*/ + +static void prvProcessTimerOrBlockTask( portTickType xNextExpireTime, portBASE_TYPE xListWasEmpty ) +{ +portTickType xTimeNow; +portBASE_TYPE xTimerListsWereSwitched; + + vTaskSuspendAll(); + { + /* Obtain the time now to make an assessment as to whether the timer + has expired or not. If obtaining the time causes the lists to switch + then don't process this timer as any timers that remained in the list + when the lists were switched will have been processed within the + prvSampelTimeNow() function. */ + xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + if( xTimerListsWereSwitched == pdFALSE ) + { + /* The tick count has not overflowed, has the timer expired? */ + if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) + { + xTaskResumeAll(); + prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); + } + else + { + /* The tick count has not overflowed, and the next expire + time has not been reached yet. This task should therefore + block to wait for the next expire time or a command to be + received - whichever comes first. The following line cannot + be reached unless xNextExpireTime > xTimeNow, except in the + case when the current timer list is empty. */ + vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) ); + + if( xTaskResumeAll() == pdFALSE ) + { + /* Yield to wait for either a command to arrive, or the block time + to expire. If a command arrived between the critical section being + exited and this yield then the yield will not cause the task + to block. */ + portYIELD_WITHIN_API(); + } + } + } + else + { + xTaskResumeAll(); + } + } +} +/*-----------------------------------------------------------*/ + +static portTickType prvGetNextExpireTime( portBASE_TYPE *pxListWasEmpty ) +{ +portTickType xNextExpireTime; + + /* Timers are listed in expiry time order, with the head of the list + referencing the task that will expire first. Obtain the time at which + the timer with the nearest expiry time will expire. If there are no + active timers then just set the next expire time to 0. That will cause + this task to unblock when the tick count overflows, at which point the + timer lists will be switched and the next expiry time can be + re-assessed. */ + *pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList ); + if( *pxListWasEmpty == pdFALSE ) + { + xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); + } + else + { + /* Ensure the task unblocks when the tick count rolls over. */ + xNextExpireTime = ( portTickType ) 0U; + } + + return xNextExpireTime; +} +/*-----------------------------------------------------------*/ + +static portTickType prvSampleTimeNow( portBASE_TYPE *pxTimerListsWereSwitched ) +{ +portTickType xTimeNow; +static portTickType xLastTime = ( portTickType ) 0U; + + xTimeNow = xTaskGetTickCount(); + if( xTimeNow < xLastTime ) + { + prvSwitchTimerLists( xLastTime ); + *pxTimerListsWereSwitched = pdTRUE; + } + else + { + *pxTimerListsWereSwitched = pdFALSE; + } + + xLastTime = xTimeNow; + + return xTimeNow; +} +/*-----------------------------------------------------------*/ + +static portBASE_TYPE prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xTimeNow, portTickType xCommandTime ) +{ +portBASE_TYPE xProcessTimerNow = pdFALSE; + + listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime ); + listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); + + if( xNextExpiryTime <= xTimeNow ) + { + /* Has the expiry time elapsed between the command to start/reset a + timer was issued, and the time the command was processed? */ + if( ( ( portTickType ) ( xTimeNow - xCommandTime ) ) >= pxTimer->xTimerPeriodInTicks ) + { + /* The time between a command being issued and the command being + processed actually exceeds the timers period. */ + xProcessTimerNow = pdTRUE; + } + else + { + vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) ); + } + } + else + { + if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ) + { + /* If, since the command was issued, the tick count has overflowed + but the expiry time has not, then the timer must have already passed + its expiry time and should be processed immediately. */ + xProcessTimerNow = pdTRUE; + } + else + { + vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); + } + } + + return xProcessTimerNow; +} +/*-----------------------------------------------------------*/ + +static void prvProcessReceivedCommands( void ) +{ +xTIMER_MESSAGE xMessage; +xTIMER *pxTimer; +portBASE_TYPE xTimerListsWereSwitched, xResult; +portTickType xTimeNow; + + /* In this case the xTimerListsWereSwitched parameter is not used, but it + must be present in the function call. */ + //xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + + while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) + { + xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + pxTimer = xMessage.pxTimer; + + /* Is the timer already in a list of active timers? When the command + is trmCOMMAND_PROCESS_TIMER_OVERFLOW, the timer will be NULL as the + command is to the task rather than to an individual timer. */ + if( pxTimer != NULL ) + { + if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) + { + /* The timer is in a list, remove it. */ + vListRemove( &( pxTimer->xTimerListItem ) ); + } + } + + traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.xMessageValue ); + + switch( xMessage.xMessageID ) + { + case tmrCOMMAND_START : + /* Start or restart a timer. */ + if( prvInsertTimerInActiveList( pxTimer, xMessage.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.xMessageValue ) == pdTRUE ) + { + /* The timer expired before it was added to the active timer + list. Process it now. */ + //pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer ); + pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer, pxTimer->callback_arg );//modify by dave + + if( pxTimer->uxAutoReload == ( unsigned portBASE_TYPE ) pdTRUE ) + { + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START, xMessage.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + } + break; + + case tmrCOMMAND_STOP : + /* The timer has already been removed from the active list. + There is nothing to do here. */ + break; + + case tmrCOMMAND_CHANGE_PERIOD : + pxTimer->xTimerPeriodInTicks = xMessage.xMessageValue; + configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) ); + prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow ); + break; + + case tmrCOMMAND_DELETE : + /* The timer has already been removed from the active list, + just free up the memory. */ + vPortFree( pxTimer ); + break; + + default : + /* Don't expect to get here. */ + break; + } + } +} +/*-----------------------------------------------------------*/ + +static void prvSwitchTimerLists( portTickType xLastTime ) +{ +portTickType xNextExpireTime, xReloadTime; +xList *pxTemp; +xTIMER *pxTimer; +portBASE_TYPE xResult; + + /* Remove compiler warnings if configASSERT() is not defined. */ + ( void ) xLastTime; + + /* The tick count has overflowed. The timer lists must be switched. + If there are any timers still referenced from the current timer list + then they must have expired and should be processed before the lists + are switched. */ + while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE ) + { + xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); + + /* Remove the timer from the list. */ + pxTimer = ( xTIMER * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); + vListRemove( &( pxTimer->xTimerListItem ) ); + + /* Execute its callback, then send a command to restart the timer if + it is an auto-reload timer. It cannot be restarted here as the lists + have not yet been switched. */ + //pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer ); + pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer, pxTimer->callback_arg ); //modify by dave + + if( pxTimer->uxAutoReload == ( unsigned portBASE_TYPE ) pdTRUE ) + { + /* Calculate the reload value, and if the reload value results in + the timer going into the same timer list then it has already expired + and the timer should be re-inserted into the current list so it is + processed again within this loop. Otherwise a command should be sent + to restart the timer to ensure it is only inserted into a list after + the lists have been swapped. */ + xReloadTime = ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ); + if( xReloadTime > xNextExpireTime ) + { + listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xReloadTime ); + listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); + vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); + } + else + { + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START, xNextExpireTime, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + } + } + + pxTemp = pxCurrentTimerList; + pxCurrentTimerList = pxOverflowTimerList; + pxOverflowTimerList = pxTemp; +} +/*-----------------------------------------------------------*/ + +static void prvCheckForValidListAndQueue( void ) +{ + /* Check that the list from which active timers are referenced, and the + queue used to communicate with the timer service, have been + initialised. */ + taskENTER_CRITICAL(); + { + if( xTimerQueue == NULL ) + { + vListInitialise( &xActiveTimerList1 ); + vListInitialise( &xActiveTimerList2 ); + pxCurrentTimerList = &xActiveTimerList1; + pxOverflowTimerList = &xActiveTimerList2; + xTimerQueue = xQueueCreate( ( unsigned portBASE_TYPE ) configTIMER_QUEUE_LENGTH, sizeof( xTIMER_MESSAGE ) ); + } + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xTimerIsTimerActive( xTimerHandle xTimer ) +{ +portBASE_TYPE xTimerIsInActiveList; +xTIMER *pxTimer = ( xTIMER * ) xTimer; + + /* Is the timer in the list of active timers? */ + taskENTER_CRITICAL(); + { + /* Checking to see if it is in the NULL list in effect checks to see if + it is referenced from either the current or the overflow timer lists in + one go, but the logic has to be reversed, hence the '!'. */ + xTimerIsInActiveList = !( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) ); + } + taskEXIT_CRITICAL(); + + return xTimerIsInActiveList; +} +/*-----------------------------------------------------------*/ + +void *pvTimerGetTimerID( xTimerHandle xTimer ) +{ +xTIMER *pxTimer = ( xTIMER * ) xTimer; + + return pxTimer->pvTimerID; +} +/*-----------------------------------------------------------*/ + +/* This entire source file will be skipped if the application is not configured +to include software timer functionality. If you want to include software timer +functionality then ensure configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ +#endif /* configUSE_TIMERS == 1 */ +#endif diff --git a/sdk/Src/OS/RTOS/source/tasks.c b/sdk/Src/OS/RTOS/source/tasks.c new file mode 100644 index 0000000..b0dd7de --- /dev/null +++ b/sdk/Src/OS/RTOS/source/tasks.c @@ -0,0 +1,2737 @@ +/* + FreeRTOS V7.0.2 - Copyright (C) 2011 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#include +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "rtostimers.h" +#include "lwip/timers.h" +#include "StackMacros.h" +#include "wm_config.h" +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#if TLS_OS_FREERTOS +const unsigned int HZ = 500; + +/* + * Macro to define the amount of stack available to the idle task. + */ +#define tskIDLE_STACK_SIZE configMINIMAL_STACK_SIZE + +/* + * Task control block. A task control block (TCB) is allocated to each task, + * and stores the context of the task. + */ +typedef struct tskTaskControlBlock +{ + volatile portSTACK_TYPE *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE STRUCT. */ + #endif + + xListItem xGenericListItem; /*< List item used to place the TCB in ready and blocked queues. */ + xListItem xEventListItem; /*< List item used to place the TCB in event lists. */ + unsigned portBASE_TYPE uxPriority; /*< The priority of the task where 0 is the lowest priority. */ + portSTACK_TYPE *pxStack; /*< Points to the start of the stack. */ + portSTACK_TYPE stacksize; + signed char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ + + #if ( portSTACK_GROWTH > 0 ) + portSTACK_TYPE *pxEndOfStack; /*< Used for stack overflow checking on architectures where the stack grows up from low memory. */ + #endif + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + unsigned portBASE_TYPE uxCriticalNesting; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + unsigned portBASE_TYPE uxTCBNumber; /*< This is used for tracing the scheduler and making debugging easier only. */ + #endif + + #if ( configUSE_MUTEXES == 1 ) + unsigned portBASE_TYPE uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + pdTASK_HOOK_CODE pxTaskTag; + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + unsigned long ulRunTimeCounter; /*< Used for calculating how much CPU time each task is utilising. */ + #endif + +} tskTCB; + + +/* + * Some kernel aware debuggers require data to be viewed to be global, rather + * than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + +/*lint -e956 */ +PRIVILEGED_DATA tskTCB * volatile pxCurrentTCB = NULL; + +/* Lists for ready and blocked tasks. --------------------*/ + +PRIVILEGED_DATA static xList pxReadyTasksLists[ configMAX_PRIORITIES ]; /*< Prioritised ready tasks. */ +PRIVILEGED_DATA static xList xDelayedTaskList1; /*< Delayed tasks. */ +PRIVILEGED_DATA static xList xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static xList * volatile pxDelayedTaskList ; /*< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static xList * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static xList xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready queue when the scheduler is resumed. */ + +#if ( INCLUDE_vTaskDelete == 1 ) + + PRIVILEGED_DATA static xList xTasksWaitingTermination; /*< Tasks that have been deleted - but the their memory not yet freed. */ + PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxTasksDeleted = ( unsigned portBASE_TYPE ) 0U; + +#endif + +#if ( INCLUDE_vTaskSuspend == 1 ) + + PRIVILEGED_DATA static xList xSuspendedTaskList; /*< Tasks that are currently suspended. */ + +#endif + +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + + PRIVILEGED_DATA static xTaskHandle xIdleTaskHandle = NULL; + +#endif + +/* File private variables. --------------------------------*/ +PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxCurrentNumberOfTasks = ( unsigned portBASE_TYPE ) 0U; +PRIVILEGED_DATA static volatile portTickType xTickCount = ( portTickType ) 0U; +PRIVILEGED_DATA static unsigned portBASE_TYPE uxTopUsedPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile signed portBASE_TYPE xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxSchedulerSuspended = ( unsigned portBASE_TYPE ) pdFALSE; +PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxMissedTicks = ( unsigned portBASE_TYPE ) 0U; +PRIVILEGED_DATA static volatile portBASE_TYPE xMissedYield = ( portBASE_TYPE ) pdFALSE; +PRIVILEGED_DATA static volatile portBASE_TYPE xNumOfOverflows = ( portBASE_TYPE ) 0; +PRIVILEGED_DATA static unsigned portBASE_TYPE uxTaskNumber = ( unsigned portBASE_TYPE ) 0U; +PRIVILEGED_DATA static portTickType xNextTaskUnblockTime = ( portTickType ) portMAX_DELAY; + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + PRIVILEGED_DATA static char pcStatsString[ 50 ] ; + PRIVILEGED_DATA static unsigned long ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ + static void prvGenerateRunTimeStatsForTasksInList( const signed char *pcWriteBuffer, xList *pxList, unsigned long ulTotalRunTime ) PRIVILEGED_FUNCTION; + +#endif + +/* Debugging and trace facilities private variables and macros. ------------*/ + +/* + * The value used to fill the stack of a task when the task is created. This + * is used purely for checking the high water mark for tasks. + */ +#define tskSTACK_FILL_BYTE ( 0xa5U ) + +/* + * Macros used by vListTask to indicate which state a task is in. + */ +#define tskBLOCKED_CHAR ( ( signed char ) 'B' ) +#define tskREADY_CHAR ( ( signed char ) 'R' ) +#define tskDELETED_CHAR ( ( signed char ) 'D' ) +#define tskSUSPENDED_CHAR ( ( signed char ) 'S' ) + +/* + * Macros and private variables used by the trace facility. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + #define tskSIZE_OF_EACH_TRACE_LINE ( ( unsigned long ) ( sizeof( unsigned long ) + sizeof( unsigned long ) ) ) + PRIVILEGED_DATA static volatile signed char * volatile pcTraceBuffer; + PRIVILEGED_DATA static signed char *pcTraceBufferStart; + PRIVILEGED_DATA static signed char *pcTraceBufferEnd; + PRIVILEGED_DATA static signed portBASE_TYPE xTracing = pdFALSE; + static unsigned portBASE_TYPE uxPreviousTask = 255U; + PRIVILEGED_DATA static char pcStatusString[ 50 ]; + +#endif + +/*-----------------------------------------------------------*/ + +/* + * Macro that writes a trace of scheduler activity to a buffer. This trace + * shows which task is running when and is very useful as a debugging tool. + * As this macro is called each context switch it is a good idea to undefine + * it if not using the facility. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + #define vWriteTraceToBuffer() \ + { \ + if( xTracing != pdFALSE ) \ + { \ + if( uxPreviousTask != pxCurrentTCB->uxTCBNumber ) \ + { \ + if( ( pcTraceBuffer + tskSIZE_OF_EACH_TRACE_LINE ) < pcTraceBufferEnd ) \ + { \ + uxPreviousTask = pxCurrentTCB->uxTCBNumber; \ + *( unsigned long * ) pcTraceBuffer = ( unsigned long ) xTickCount; \ + pcTraceBuffer += sizeof( unsigned long ); \ + *( unsigned long * ) pcTraceBuffer = ( unsigned long ) uxPreviousTask; \ + pcTraceBuffer += sizeof( unsigned long ); \ + } \ + else \ + { \ + xTracing = pdFALSE; \ + } \ + } \ + } \ + } + +#else + + #define vWriteTraceToBuffer() + +#endif +/*-----------------------------------------------------------*/ + +/* + * Place the task represented by pxTCB into the appropriate ready queue for + * the task. It is inserted at the end of the list. One quirk of this is + * that if the task being inserted is at the same priority as the currently + * executing task, then it will only be rescheduled after the currently + * executing task has been rescheduled. + */ +#define prvAddTaskToReadyQueue( pxTCB ) \ + if( ( pxTCB )->uxPriority > uxTopReadyPriority ) \ + { \ + uxTopReadyPriority = ( pxTCB )->uxPriority; \ + } \ + vListInsertEnd( ( xList * ) &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xGenericListItem ) ) +/*-----------------------------------------------------------*/ + +/* + * Macro that looks at the list of tasks that are currently delayed to see if + * any require waking. + * + * Tasks are stored in the queue in the order of their wake time - meaning + * once one tasks has been found whose timer has not expired we need not look + * any further down the list. + */ +#define prvCheckDelayedTasks() \ +{ \ +portTickType xItemValue; \ + \ + /* Is the tick count greater than or equal to the wake time of the first \ + task referenced from the delayed tasks list? */ \ + if( xTickCount >= xNextTaskUnblockTime ) \ + { \ + for( ;; ) \ + { \ + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) \ + { \ + /* The delayed list is empty. Set xNextTaskUnblockTime to the \ + maximum possible value so it is extremely unlikely that the \ + if( xTickCount >= xNextTaskUnblockTime ) test will pass next \ + time through. */ \ + xNextTaskUnblockTime = portMAX_DELAY; \ + break; \ + } \ + else \ + { \ + /* The delayed list is not empty, get the value of the item at \ + the head of the delayed list. This is the time at which the \ + task at the head of the delayed list should be removed from \ + the Blocked state. */ \ + pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); \ + xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) ); \ + \ + if( xTickCount < xItemValue ) \ + { \ + /* It is not time to unblock this item yet, but the item \ + value is the time at which the task at the head of the \ + blocked list should be removed from the Blocked state - \ + so record the item value in xNextTaskUnblockTime. */ \ + xNextTaskUnblockTime = xItemValue; \ + break; \ + } \ + \ + /* It is time to remove the item from the Blocked state. */ \ + vListRemove( &( pxTCB->xGenericListItem ) ); \ + \ + /* Is the task waiting on an event also? */ \ + if( pxTCB->xEventListItem.pvContainer != NULL ) \ + { \ + vListRemove( &( pxTCB->xEventListItem ) ); \ + } \ + prvAddTaskToReadyQueue( pxTCB ); \ + } \ + } \ + } \ +} +/*-----------------------------------------------------------*/ + +/* + * Several functions take an xTaskHandle parameter that can optionally be NULL, + * where NULL is used to indicate that the handle of the currently executing + * task should be used in place of the parameter. This macro simply checks to + * see if the parameter is NULL and returns a pointer to the appropriate TCB. + */ +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( tskTCB * ) pxCurrentTCB : ( tskTCB * ) ( pxHandle ) ) + +/* Callback function prototypes. --------------------------*/ +extern void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskName , signed char prio); +extern void vApplicationTickHook( void ); + +/* File private functions. --------------------------------*/ + +/* + * Utility to ready a TCB for a given task. Mainly just copies the parameters + * into the TCB structure. + */ +static void prvInitialiseTCBVariables( tskTCB *pxTCB, const signed char * const pcName, unsigned portBASE_TYPE uxPriority, const xMemoryRegion * const xRegions, unsigned short usStackDepth ) PRIVILEGED_FUNCTION; + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first task. + */ +static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; + +/* + * The idle task, which as all tasks is implemented as a never ending loop. + * The idle task is automatically created and added to the ready lists upon + * creation of the first user task. + * + * The portTASK_FUNCTION_PROTO() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); + +/* + * Utility to free all memory allocated by the scheduler to hold a TCB, + * including the stack pointed to by the TCB. + * + * This does not free memory allocated by the task itself (i.e. memory + * allocated by calls to pvPortMalloc from within the tasks application code). + */ +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( tskTCB *pxTCB ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Used only by the idle task. This checks to see if anything has been placed + * in the list of tasks waiting to be deleted. If so the task is cleaned up + * and its TCB deleted. + */ +static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; + +/* + * The currently executing task is entering the Blocked state. Add the task to + * either the current or the overflow delayed task list. + */ +static void prvAddCurrentTaskToDelayedList( portTickType xTimeToWake ) PRIVILEGED_FUNCTION; + +/* + * Allocates memory from the heap for a TCB and associated stack. Checks the + * allocation was successful. + */ +static tskTCB *prvAllocateTCBAndStack( unsigned short usStackDepth, portSTACK_TYPE *puxStackBuffer ) PRIVILEGED_FUNCTION; + +/* + * Called from vTaskList. vListTasks details all the tasks currently under + * control of the scheduler. The tasks may be in one of a number of lists. + * prvListTaskWithinSingleList accepts a list and details the tasks from + * within just that list. + * + * THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM + * NORMAL APPLICATION CODE. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + static void prvListTaskWithinSingleList( const signed char *pcWriteBuffer, xList *pxList, signed char cStatus ) PRIVILEGED_FUNCTION; + +#endif + +/* + * When a task is created, the stack of the task is filled with a known value. + * This function determines the 'high water mark' of the task stack by + * determining how much of the stack remains at the original preset value. + */ +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + + static unsigned short usTaskCheckFreeStackSpace( const unsigned char * pucStackByte ) PRIVILEGED_FUNCTION; + +#endif + + +/*lint +e956 */ + + + +/*----------------------------------------------------------- + * TASK CREATION API documented in task.h + *----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskGenericCreate( pdTASK_CODE pxTaskCode, const signed char * const pcName, unsigned short usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask, portSTACK_TYPE *puxStackBuffer, const xMemoryRegion * const xRegions ) +{ +signed portBASE_TYPE xReturn; +tskTCB * pxNewTCB; + + configASSERT( pxTaskCode ); + configASSERT( ( uxPriority < configMAX_PRIORITIES ) ); + + /* Allocate the memory required by the TCB and stack for the new task, + checking that the allocation was successful. */ + pxNewTCB = prvAllocateTCBAndStack( usStackDepth, puxStackBuffer ); + + if( pxNewTCB != NULL ) + { + portSTACK_TYPE *pxTopOfStack; + + #if( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + portBASE_TYPE xRunPrivileged; + if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) + { + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Calculate the top of stack address. This depends on whether the + stack grows from high memory to low (as per the 80x86) or visa versa. + portSTACK_GROWTH is used to make the result positive or negative as + required by the port. */ + #if( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = pxNewTCB->pxStack + ( usStackDepth - ( unsigned short ) 1 ); + pxTopOfStack = ( portSTACK_TYPE * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) ); + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( unsigned long ) pxTopOfStack & ( unsigned long ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + } + #else + { + pxTopOfStack = pxNewTCB->pxStack; + + /* Check the alignment of the stack buffer is correct. */ + configASSERT( ( ( ( unsigned long ) pxNewTCB->pxStack & ( unsigned long ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + /* If we want to use stack checking on architectures that use + a positive stack growth direction then we also need to store the + other extreme of the stack space. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 ); + } + #endif + + /* Setup the newly allocated TCB with the initial state of the task. */ + prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth ); + + /* Initialize the TCB stack to look as if the task was already running, + but had been interrupted by the scheduler. The return address is set + to the start of the task function. Once the stack has been initialised + the top of stack variable is updated. */ + #if( portUSING_MPU_WRAPPERS == 1 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif + + /* Check the alignment of the initialised stack. */ + configASSERT( ( ( ( unsigned long ) pxNewTCB->pxTopOfStack & ( unsigned long ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + if( ( void * ) pxCreatedTask != NULL ) + { + /* Pass the TCB out - in an anonymous way. The calling function/ + task can use this as a handle to delete the task later if + required.*/ + *pxCreatedTask = ( xTaskHandle ) pxNewTCB; + } + + /* We are going to manipulate the task queues to add this task to a + ready list, so must make sure no interrupts occur. */ + taskENTER_CRITICAL(); + { + uxCurrentNumberOfTasks++; + if( pxCurrentTCB == NULL ) + { + /* There are no other tasks, or all the other tasks are in + the suspended state - make this the current task. */ + pxCurrentTCB = pxNewTCB; + + if( uxCurrentNumberOfTasks == ( unsigned portBASE_TYPE ) 1 ) + { + /* This is the first task to be created so do the preliminary + initialisation required. We will not recover if this call + fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + } + else + { + /* If the scheduler is not already running, make this task the + current task if it is the highest priority task to be created + so far. */ + if( xSchedulerRunning == pdFALSE ) + { + if( pxCurrentTCB->uxPriority <= uxPriority ) + { + pxCurrentTCB = pxNewTCB; + } + } + } + + /* Remember the top priority to make context switching faster. Use + the priority in pxNewTCB as this has been capped to a valid value. */ + if( pxNewTCB->uxPriority > uxTopUsedPriority ) + { + uxTopUsedPriority = pxNewTCB->uxPriority; + } + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif + uxTaskNumber++; + + prvAddTaskToReadyQueue( pxNewTCB ); + + xReturn = pdPASS; + traceTASK_CREATE( pxNewTCB ); + } + taskEXIT_CRITICAL(); + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + traceTASK_CREATE_FAILED(); + } + + if( xReturn == pdPASS ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than the current task + then it should run now. */ + if( pxCurrentTCB->uxPriority < uxPriority ) + { + portYIELD_WITHIN_API(); + } + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + void vTaskDelete( xTaskHandle pxTaskToDelete ) + { + tskTCB *pxTCB; + + taskENTER_CRITICAL(); + { + /* Ensure a yield is performed if the current task is being + deleted. */ + if( pxTaskToDelete == pxCurrentTCB ) + { + pxTaskToDelete = NULL; + } + + /* If null is passed in here then we are deleting ourselves. */ + pxTCB = prvGetTCBFromHandle( pxTaskToDelete ); + + /* Remove task from the ready list and place in the termination list. + This will stop the task from be scheduled. The idle task will check + the termination list and free up any memory allocated by the + scheduler for the TCB and stack. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Is the task waiting on an event also? */ + if( pxTCB->xEventListItem.pvContainer != NULL ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + } + + vListInsertEnd( ( xList * ) &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + there is a task that has been deleted and that it should therefore + check the xTasksWaitingTermination list. */ + ++uxTasksDeleted; + + /* Increment the uxTaskNumberVariable also so kernel aware debuggers + can detect that the task lists need re-generating. */ + uxTaskNumber++; + + traceTASK_DELETE( pxTCB ); + } + taskEXIT_CRITICAL(); + + /* Force a reschedule if we have just deleted the current task. */ + if( xSchedulerRunning != pdFALSE ) + { + if( ( void * ) pxTaskToDelete == NULL ) + { + portYIELD_WITHIN_API(); + } + } + } + +tskTCB * vTaskGetTaskByPriority(unsigned long priority); +unsigned portBASE_TYPE vTaskDeleteByPriority(unsigned portBASE_TYPE prio) +{ + tskTCB *pxTCB; + tskTCB *pxTaskToDelete = NULL; + + pxTaskToDelete = vTaskGetTaskByPriority(prio); + if (pxTaskToDelete == NULL) + { + return 0; + } + + taskENTER_CRITICAL(); + { + /* Ensure a yield is performed if the current task is being + deleted. */ + if( pxTaskToDelete == pxCurrentTCB ) + { + pxTaskToDelete = NULL; + } + + /* If null is passed in here then we are deleting ourselves. */ + pxTCB = prvGetTCBFromHandle( pxTaskToDelete ); + + /* Remove task from the ready list and place in the termination list. + This will stop the task from be scheduled. The idle task will check + the termination list and free up any memory allocated by the + scheduler for the TCB and stack. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Is the task waiting on an event also? */ + if( pxTCB->xEventListItem.pvContainer != NULL ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + } + + vListInsertEnd( ( xList * ) &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + there is a task that has been deleted and that it should therefore + check the xTasksWaitingTermination list. */ + ++uxTasksDeleted; + + /* Increment the uxTaskNumberVariable also so kernel aware debuggers + can detect that the task lists need re-generating. */ + uxTaskNumber++; + + traceTASK_DELETE( pxTCB ); + } + taskEXIT_CRITICAL(); + + /* Force a reschedule if we have just deleted the current task. */ + if( xSchedulerRunning != pdFALSE ) + { + if( ( void * ) pxTaskToDelete == NULL ) + { + portYIELD_WITHIN_API(); + } + } + + return 0; +} +#endif + + + + + + +/*----------------------------------------------------------- + * TASK CONTROL API documented in task.h + *----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelayUntil == 1 ) + + void vTaskDelayUntil( portTickType * const pxPreviousWakeTime, portTickType xTimeIncrement ) + { + portTickType xTimeToWake; + portBASE_TYPE xAlreadyYielded, xShouldDelay = pdFALSE; + + configASSERT( pxPreviousWakeTime ); + configASSERT( ( xTimeIncrement > 0U ) ); + + vTaskSuspendAll(); + { + /* Generate the tick time at which the task wants to wake. */ + xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; + + if( xTickCount < *pxPreviousWakeTime ) + { + /* The tick count has overflowed since this function was + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this + is the case it is as if neither time had overflowed. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xTickCount ) ) + { + xShouldDelay = pdTRUE; + } + } + else + { + /* The tick time has not overflowed. In this case we will + delay if either the wake time has overflowed, and/or the + tick time is less than the wake time. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xTickCount ) ) + { + xShouldDelay = pdTRUE; + } + } + + /* Update the wake time ready for the next call. */ + *pxPreviousWakeTime = xTimeToWake; + + if( xShouldDelay != pdFALSE ) + { + traceTASK_DELAY_UNTIL(); + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelay == 1 ) + + void vTaskDelay( portTickType xTicksToDelay ) + { + portTickType xTimeToWake; + signed portBASE_TYPE xAlreadyYielded = pdFALSE; + + /* A delay time of zero just forces a reschedule. */ + if( xTicksToDelay > ( portTickType ) 0U ) + { + vTaskSuspendAll(); + { + traceTASK_DELAY(); + + /* A task that is removed from the event list while the + scheduler is suspended will not get placed in the ready + list or removed from the blocked list until the scheduler + is resumed. + + This task cannot be in an event list as it is the currently + executing task. */ + + /* Calculate the time to wake - this may overflow but this is + not a problem. */ + xTimeToWake = xTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + xAlreadyYielded = xTaskResumeAll(); + } + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ) + { + tskTCB *pxTCB; + unsigned portBASE_TYPE uxReturn; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then we are changing the + priority of the calling function. */ + pxTCB = prvGetTCBFromHandle( pxTask ); + uxReturn = pxTCB->uxPriority; + } + taskEXIT_CRITICAL(); + + return uxReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + + void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ) + { + tskTCB *pxTCB; + unsigned portBASE_TYPE uxCurrentPriority; + portBASE_TYPE xYieldRequired = pdFALSE; + + configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) ); + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= configMAX_PRIORITIES ) + { + uxNewPriority = configMAX_PRIORITIES - ( unsigned portBASE_TYPE ) 1U; + } + + taskENTER_CRITICAL(); + { + if( pxTask == pxCurrentTCB ) + { + pxTask = NULL; + } + + /* If null is passed in here then we are changing the + priority of the calling function. */ + pxTCB = prvGetTCBFromHandle( pxTask ); + + traceTASK_PRIORITY_SET( pxTask, uxNewPriority ); + + #if ( configUSE_MUTEXES == 1 ) + { + uxCurrentPriority = pxTCB->uxBasePriority; + } + #else + { + uxCurrentPriority = pxTCB->uxPriority; + } + #endif + + if( uxCurrentPriority != uxNewPriority ) + { + /* The priority change may have readied a task of higher + priority than the calling task. */ + if( uxNewPriority > uxCurrentPriority ) + { + if( pxTask != NULL ) + { + /* The priority of another task is being raised. If we + were raising the priority of the currently running task + there would be no need to switch as it must have already + been the highest priority task. */ + xYieldRequired = pdTRUE; + } + } + else if( pxTask == NULL ) + { + /* Setting our own priority down means there may now be another + task of higher priority that is ready to execute. */ + xYieldRequired = pdTRUE; + } + + + + #if ( configUSE_MUTEXES == 1 ) + { + /* Only change the priority being used if the task is not + currently using an inherited priority. */ + if( pxTCB->uxBasePriority == pxTCB->uxPriority ) + { + pxTCB->uxPriority = uxNewPriority; + } + + /* The base priority gets set whatever. */ + pxTCB->uxBasePriority = uxNewPriority; + } + #else + { + pxTCB->uxPriority = uxNewPriority; + } + #endif + + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( configMAX_PRIORITIES - ( portTickType ) uxNewPriority ) ); + + /* If the task is in the blocked or suspended list we need do + nothing more than change it's priority variable. However, if + the task is in a ready list it needs to be removed and placed + in the queue appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxCurrentPriority ] ), &( pxTCB->xGenericListItem ) ) ) + { + /* The task is currently in its ready list - remove before adding + it to it's new ready list. As we are in a critical section we + can do this even if the scheduler is suspended. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + } + + if( xYieldRequired == pdTRUE ) + { + portYIELD_WITHIN_API(); + } + } + } + taskEXIT_CRITICAL(); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskSuspend( xTaskHandle pxTaskToSuspend ) + { + tskTCB *pxTCB; + + taskENTER_CRITICAL(); + { + /* Ensure a yield is performed if the current task is being + suspended. */ + if( pxTaskToSuspend == pxCurrentTCB ) + { + pxTaskToSuspend = NULL; + } + + /* If null is passed in here then we are suspending ourselves. */ + pxTCB = prvGetTCBFromHandle( pxTaskToSuspend ); + + traceTASK_SUSPEND( pxTCB ); + + /* Remove task from the ready/delayed list and place in the suspended list. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Is the task waiting on an event also? */ + if( pxTCB->xEventListItem.pvContainer != NULL ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + } + + vListInsertEnd( ( xList * ) &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ); + } + taskEXIT_CRITICAL(); + + if( ( void * ) pxTaskToSuspend == NULL ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* We have just suspended the current task. */ + portYIELD_WITHIN_API(); + } + else + { + /* The scheduler is not running, but the task that was pointed + to by pxCurrentTCB has just been suspended and pxCurrentTCB + must be adjusted to point to a different task. */ + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) + { + /* No other tasks are ready, so set pxCurrentTCB back to + NULL so when the next task is created pxCurrentTCB will + be set to point to it no matter what its relative priority + is. */ + pxCurrentTCB = NULL; + } + else + { + vTaskSwitchContext(); + } + } + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + signed portBASE_TYPE xTaskIsTaskSuspended( xTaskHandle xTask ) + { + portBASE_TYPE xReturn = pdFALSE; + const tskTCB * const pxTCB = ( tskTCB * ) xTask; + + /* It does not make sense to check if the calling task is suspended. */ + configASSERT( xTask ); + + /* Is the task we are attempting to resume actually in the + suspended list? */ + if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ) != pdFALSE ) + { + /* Has the task already been resumed from within an ISR? */ + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) != pdTRUE ) + { + /* Is it in the suspended list because it is in the + Suspended state? It is possible to be in the suspended + list because it is blocked on a task with no timeout + specified. */ + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) == pdTRUE ) + { + xReturn = pdTRUE; + } + } + } + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskResume( xTaskHandle pxTaskToResume ) + { + tskTCB *pxTCB; + + /* It does not make sense to resume the calling task. */ + configASSERT( pxTaskToResume ); + + /* Remove the task from whichever list it is currently in, and place + it in the ready list. */ + pxTCB = ( tskTCB * ) pxTaskToResume; + + /* The parameter cannot be NULL as it is impossible to resume the + currently executing task. */ + if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ) + { + taskENTER_CRITICAL(); + { + if( xTaskIsTaskSuspended( pxTCB ) == pdTRUE ) + { + traceTASK_RESUME( pxTCB ); + + /* As we are in a critical section we can access the ready + lists even if the scheduler is suspended. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + + /* We may have just resumed a higher priority task. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* This yield may not cause the task just resumed to run, but + will leave the lists in the correct state for the next yield. */ + portYIELD_WITHIN_API(); + } + } + } + taskEXIT_CRITICAL(); + } + } + +#endif + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume ) + { + portBASE_TYPE xYieldRequired = pdFALSE; + tskTCB *pxTCB; + + configASSERT( pxTaskToResume ); + + pxTCB = ( tskTCB * ) pxTaskToResume; + + if( xTaskIsTaskSuspended( pxTCB ) == pdTRUE ) + { + traceTASK_RESUME_FROM_ISR( pxTCB ); + + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + xYieldRequired = ( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ); + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + } + else + { + /* We cannot access the delayed or ready lists, so will hold this + task pending until the scheduler is resumed, at which point a + yield will be performed if necessary. */ + vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + } + + return xYieldRequired; + } + +#endif + + + + +/*----------------------------------------------------------- + * PUBLIC SCHEDULER CONTROL documented in task.h + *----------------------------------------------------------*/ + + +void vTaskStartScheduler( void ) +{ +portBASE_TYPE xReturn; + + /* Add the idle task at the lowest priority. */ + #if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + { + /* Create the idle task, storing its handle in xIdleTaskHandle so it can + be returned by the xTaskGetIdleTaskHandle() function. */ + xReturn = xTaskCreate( prvIdleTask, ( signed char * ) "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle ); + } + #else + { + /* Create the idle task without storing its handle. */ + xReturn = xTaskCreate( prvIdleTask, ( signed char * ) "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), NULL ); + } + #endif + + #if ( configUSE_TIMERS == 1 ) + { + if( xReturn == pdPASS ) + { + xReturn = xTimerCreateTimerTask(); + } + } + #endif + + if( xReturn == pdPASS ) + { + /* Interrupts are turned off here, to ensure a tick does not occur + before or during the call to xPortStartScheduler(). The stacks of + the created tasks contain a status word with interrupts switched on + so interrupts will automatically get re-enabled when the first task + starts to run. + + STEPPING THROUGH HERE USING A DEBUGGER CAN CAUSE BIG PROBLEMS IF THE + DEBUGGER ALLOWS INTERRUPTS TO BE PROCESSED. */ + portDISABLE_INTERRUPTS(); + + xSchedulerRunning = pdTRUE; + xTickCount = ( portTickType ) 0U; + + /* If configGENERATE_RUN_TIME_STATS is defined then the following + macro must be defined to configure the timer/counter used to generate + the run time counter time base. */ + portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + + /* Setting up the timer tick is hardware specific and thus in the + portable interface. */ + if( xPortStartScheduler() != pdFALSE ) + { + /* Should not reach here as if the scheduler is running the + function will not return. */ + } + else + { + /* Should only reach here if a task calls xTaskEndScheduler(). */ + } + } + + /* This line will only be reached if the kernel could not be started. */ + configASSERT( xReturn ); +} +/*-----------------------------------------------------------*/ + +void vTaskEndScheduler( void ) +{ + /* Stop the scheduler interrupts and call the portable scheduler end + routine so the original ISRs can be restored if necessary. The port + layer must ensure interrupts enable bit is left in the correct state. */ + portDISABLE_INTERRUPTS(); + xSchedulerRunning = pdFALSE; + vPortEndScheduler(); +} +/*----------------------------------------------------------*/ + +void vTaskSuspendAll( void ) +{ + /* A critical section is not required as the variable is of type + portBASE_TYPE. */ + ++uxSchedulerSuspended; +} +/*----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskResumeAll( void ) +{ +register tskTCB *pxTCB; +signed portBASE_TYPE xAlreadyYielded = pdFALSE; + + /* If uxSchedulerSuspended is zero then this function does not match a + previous call to vTaskSuspendAll(). */ + configASSERT( uxSchedulerSuspended ); + + /* It is possible that an ISR caused a task to be removed from an event + list while the scheduler was suspended. If this was the case then the + removed task will have been added to the xPendingReadyList. Once the + scheduler has been resumed it is safe to move all the pending ready + tasks from this list into their appropriate ready list. */ + taskENTER_CRITICAL(); + { + --uxSchedulerSuspended; + + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + if( uxCurrentNumberOfTasks > ( unsigned portBASE_TYPE ) 0U ) + { + portBASE_TYPE xYieldRequired = pdFALSE; + + /* Move any readied tasks from the pending list into the + appropriate ready list. */ + while( listLIST_IS_EMPTY( ( xList * ) &xPendingReadyList ) == pdFALSE ) + { + pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xPendingReadyList ) ); + vListRemove( &( pxTCB->xEventListItem ) ); + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + + /* If we have moved a task that has a priority higher than + the current task then we should yield. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + } + + /* If any ticks occurred while the scheduler was suspended then + they should be processed now. This ensures the tick count does not + slip, and that any delayed tasks are resumed at the correct time. */ + if( uxMissedTicks > ( unsigned portBASE_TYPE ) 0U ) + { + while( uxMissedTicks > ( unsigned portBASE_TYPE ) 0U ) + { + vTaskIncrementTick(); + --uxMissedTicks; + } + + /* As we have processed some ticks it is appropriate to yield + to ensure the highest priority task that is ready to run is + the task actually running. */ + #if configUSE_PREEMPTION == 1 + { + xYieldRequired = pdTRUE; + } + #endif + } + + if( ( xYieldRequired == pdTRUE ) || ( xMissedYield == pdTRUE ) ) + { + xAlreadyYielded = pdTRUE; + xMissedYield = pdFALSE; + portYIELD_WITHIN_API(); + } + } + } + } + taskEXIT_CRITICAL(); + + return xAlreadyYielded; +} + + + + + + +/*----------------------------------------------------------- + * PUBLIC TASK UTILITIES documented in task.h + *----------------------------------------------------------*/ + + + +portTickType xTaskGetTickCount( void ) +{ +portTickType xTicks; + + /* Critical section required if running on a 16 bit processor. */ + taskENTER_CRITICAL(); + { + xTicks = xTickCount; + } + taskEXIT_CRITICAL(); + + return xTicks; +} +/*-----------------------------------------------------------*/ + +portTickType xTaskGetTickCountFromISR( void ) +{ +portTickType xReturn; +unsigned portBASE_TYPE uxSavedInterruptStatus; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + xReturn = xTickCount; + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE uxTaskGetNumberOfTasks( void ) +{ + /* A critical section is not required because the variables are of type + portBASE_TYPE. */ + return uxCurrentNumberOfTasks; +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_pcTaskGetTaskName == 1 ) + + signed char *pcTaskGetTaskName( xTaskHandle xTaskToQuery ) + { + tskTCB *pxTCB; + + /* If null is passed in here then the name of the calling task is being queried. */ + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + configASSERT( pxTCB ); + return &( pxTCB->pcTaskName[ 0 ] ); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskList( signed char *pcWriteBuffer ) + { + unsigned portBASE_TYPE uxQueue; + + /* This is a VERY costly function that should be used for debug only. + It leaves interrupts disabled for a LONG time. */ + + vTaskSuspendAll(); + { + /* Run through all the lists that could potentially contain a TCB and + report the task name, state and stack high water mark. */ + + //*pcWriteBuffer = ( signed char ) 0x00; + strcpy((char *)pcWriteBuffer,"task\t\tprio\ttask status\tstack total\tstack remain\tstack least remain\r\n"); + //strcat( ( char * ) pcWriteBuffer, ( const char * ) "\r\n" ); + + uxQueue = uxTopUsedPriority + ( unsigned portBASE_TYPE ) 1U; + + do + { + uxQueue--; + + if( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxQueue ] ) ) == pdFALSE ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) &( pxReadyTasksLists[ uxQueue ] ), tskREADY_CHAR ); + } + }while( uxQueue > ( unsigned short ) tskIDLE_PRIORITY ); + + if( listLIST_IS_EMPTY( pxDelayedTaskList ) == pdFALSE ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) pxDelayedTaskList, tskBLOCKED_CHAR ); + } + + if( listLIST_IS_EMPTY( pxOverflowDelayedTaskList ) == pdFALSE ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) pxOverflowDelayedTaskList, tskBLOCKED_CHAR ); + } + + #if( INCLUDE_vTaskDelete == 1 ) + { + if( listLIST_IS_EMPTY( &xTasksWaitingTermination ) == pdFALSE ) + { + prvListTaskWithinSingleList( pcWriteBuffer, &xTasksWaitingTermination, tskDELETED_CHAR ); + } + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( listLIST_IS_EMPTY( &xSuspendedTaskList ) == pdFALSE ) + { + prvListTaskWithinSingleList( pcWriteBuffer, &xSuspendedTaskList, tskSUSPENDED_CHAR ); + } + } + #endif + } + xTaskResumeAll(); + } + +#endif +/*----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + void vTaskGetRunTimeStats( signed char *pcWriteBuffer ) + { + unsigned portBASE_TYPE uxQueue; + unsigned long ulTotalRunTime; + + /* This is a VERY costly function that should be used for debug only. + It leaves interrupts disabled for a LONG time. */ + + vTaskSuspendAll(); + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); + #else + ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* Divide ulTotalRunTime by 100 to make the percentage caluclations + simpler in the prvGenerateRunTimeStatsForTasksInList() function. */ + ulTotalRunTime /= 100UL; + + /* Run through all the lists that could potentially contain a TCB, + generating a table of run timer percentages in the provided + buffer. */ + + *pcWriteBuffer = ( signed char ) 0x00; + strcat( ( char * ) pcWriteBuffer, ( const char * ) "\r\n" ); + + uxQueue = uxTopUsedPriority + ( unsigned portBASE_TYPE ) 1U; + + do + { + uxQueue--; + + if( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxQueue ] ) ) == pdFALSE ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, ( xList * ) &( pxReadyTasksLists[ uxQueue ] ), ulTotalRunTime ); + } + }while( uxQueue > ( unsigned short ) tskIDLE_PRIORITY ); + + if( listLIST_IS_EMPTY( pxDelayedTaskList ) == pdFALSE ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, ( xList * ) pxDelayedTaskList, ulTotalRunTime ); + } + + if( listLIST_IS_EMPTY( pxOverflowDelayedTaskList ) == pdFALSE ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, ( xList * ) pxOverflowDelayedTaskList, ulTotalRunTime ); + } + + #if ( INCLUDE_vTaskDelete == 1 ) + { + if( listLIST_IS_EMPTY( &xTasksWaitingTermination ) == pdFALSE ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, &xTasksWaitingTermination, ulTotalRunTime ); + } + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( listLIST_IS_EMPTY( &xSuspendedTaskList ) == pdFALSE ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, &xSuspendedTaskList, ulTotalRunTime ); + } + } + #endif + } + xTaskResumeAll(); + } + +#endif +/*----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskStartTrace( signed char * pcBuffer, unsigned long ulBufferSize ) + { + configASSERT( pcBuffer ); + configASSERT( ulBufferSize ); + + taskENTER_CRITICAL(); + { + pcTraceBuffer = ( signed char * )pcBuffer; + pcTraceBufferStart = pcBuffer; + pcTraceBufferEnd = pcBuffer + ( ulBufferSize - tskSIZE_OF_EACH_TRACE_LINE ); + xTracing = pdTRUE; + } + taskEXIT_CRITICAL(); + } + +#endif +/*----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + unsigned long ulTaskEndTrace( void ) + { + unsigned long ulBufferLength; + + taskENTER_CRITICAL(); + xTracing = pdFALSE; + taskEXIT_CRITICAL(); + + ulBufferLength = ( unsigned long ) ( pcTraceBuffer - pcTraceBufferStart ); + + return ulBufferLength; + } + +#endif +/*----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + + xTaskHandle xTaskGetIdleTaskHandle( void ) + { + /* If xTaskGetIdleTaskHandle() is called before the scheduler has been + started, then xIdleTaskHandle will be NULL. */ + configASSERT( ( xIdleTaskHandle != NULL ) ); + return xIdleTaskHandle; + } + +#endif + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + * documented in task.h + *----------------------------------------------------------*/ + +void vTaskIncrementTick( void ) +{ +tskTCB * pxTCB; + + /* Called by the portable layer each time a tick interrupt occurs. + Increments the tick then checks to see if the new tick value will cause any + tasks to be unblocked. */ + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + ++xTickCount; + if( xTickCount == ( portTickType ) 0U ) + { + xList *pxTemp; + + /* Tick count has overflowed so we need to swap the delay lists. + If there are any items in pxDelayedTaskList here then there is + an error! */ + configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); + + pxTemp = pxDelayedTaskList; + pxDelayedTaskList = pxOverflowDelayedTaskList; + pxOverflowDelayedTaskList = pxTemp; + xNumOfOverflows++; + + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The new current delayed list is empty. Set + xNextTaskUnblockTime to the maximum possible value so it is + extremely unlikely that the + if( xTickCount >= xNextTaskUnblockTime ) test will pass until + there is an item in the delayed list. */ + xNextTaskUnblockTime = portMAX_DELAY; + } + else + { + /* The new current delayed list is not empty, get the value of + the item at the head of the delayed list. This is the time at + which the task at the head of the delayed list should be removed + from the Blocked state. */ + pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); + xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) ); + } + } + + /* See if this tick has made a timeout expire. */ + prvCheckDelayedTasks(); + } + else + { + ++uxMissedTicks; + + /* The tick hook gets called at regular intervals, even if the + scheduler is locked. */ + #if ( configUSE_TICK_HOOK == 1 ) + { + vApplicationTickHook(); + } + #endif + } + + #if ( configUSE_TICK_HOOK == 1 ) + { + /* Guard against the tick hook being called when the missed tick + count is being unwound (when the scheduler is being unlocked. */ + if( uxMissedTicks == ( unsigned portBASE_TYPE ) 0U ) + { + vApplicationTickHook(); + } + } + #endif + + traceTASK_INCREMENT_TICK( xTickCount ); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + void vTaskSetApplicationTaskTag( xTaskHandle xTask, pdTASK_HOOK_CODE pxHookFunction ) + { + tskTCB *xTCB; + + /* If xTask is NULL then we are setting our own task hook. */ + if( xTask == NULL ) + { + xTCB = ( tskTCB * ) pxCurrentTCB; + } + else + { + xTCB = ( tskTCB * ) xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + xTCB->pxTaskTag = pxHookFunction; + taskEXIT_CRITICAL(); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + pdTASK_HOOK_CODE xTaskGetApplicationTaskTag( xTaskHandle xTask ) + { + tskTCB *xTCB; + pdTASK_HOOK_CODE xReturn; + + /* If xTask is NULL then we are setting our own task hook. */ + if( xTask == NULL ) + { + xTCB = ( tskTCB * ) pxCurrentTCB; + } + else + { + xTCB = ( tskTCB * ) xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + xReturn = xTCB->pxTaskTag; + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask, void *pvParameter ) + { + tskTCB *xTCB; + portBASE_TYPE xReturn; + + /* If xTask is NULL then we are calling our own task hook. */ + if( xTask == NULL ) + { + xTCB = ( tskTCB * ) pxCurrentTCB; + } + else + { + xTCB = ( tskTCB * ) xTask; + } + + if( xTCB->pxTaskTag != NULL ) + { + xReturn = xTCB->pxTaskTag( pvParameter ); + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +void vTaskSwitchContext( void ) +{ + if( uxSchedulerSuspended != ( unsigned portBASE_TYPE ) pdFALSE ) + { + /* The scheduler is currently suspended - do not allow a context + switch. */ + xMissedYield = pdTRUE; + } + else + { + traceTASK_SWITCHED_OUT(); + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + unsigned long ulTempCounter; + + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTempCounter ); + #else + ulTempCounter = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* Add the amount of time the task has been running to the accumulated + time so far. The time the task started running was stored in + ulTaskSwitchedInTime. Note that there is no overflow protection here + so count values are only valid until the timer overflows. Generally + this will be about 1 hour assuming a 1uS timer increment. */ + pxCurrentTCB->ulRunTimeCounter += ( ulTempCounter - ulTaskSwitchedInTime ); + ulTaskSwitchedInTime = ulTempCounter; + } + #endif + + taskFIRST_CHECK_FOR_STACK_OVERFLOW(); + taskSECOND_CHECK_FOR_STACK_OVERFLOW(); + /* Find the highest priority queue that contains ready tasks. */ + while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopReadyPriority ] ) ) ) + { + configASSERT( uxTopReadyPriority ); + --uxTopReadyPriority; + } + /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the tasks of the + same priority get an equal share of the processor time. */ + + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); + traceTASK_SWITCHED_IN(); + vWriteTraceToBuffer(); + } +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnEventList( const xList * const pxEventList, portTickType xTicksToWait ) +{ +portTickType xTimeToWake; + + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED OR THE + SCHEDULER SUSPENDED. */ + + /* Place the event list item of the TCB in the appropriate event list. + This is placed in the list in priority order so the highest priority task + is the first to be woken by the event. */ + vListInsert( ( xList * ) pxEventList, ( xListItem * ) &( pxCurrentTCB->xEventListItem ) ); + + /* We must remove ourselves from the ready list before adding ourselves + to the blocked list as the same list item is used for both lists. We have + exclusive access to the ready lists as the scheduler is locked. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( xTicksToWait == portMAX_DELAY ) + { + /* Add ourselves to the suspended task list instead of a delayed task + list to ensure we are not woken by a timing event. We will block + indefinitely. */ + vListInsertEnd( ( xList * ) &xSuspendedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter. */ + xTimeToWake = xTickCount + xTicksToWait; + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + } + #else + { + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter. */ + xTimeToWake = xTickCount + xTicksToWait; + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + #endif +} +/*-----------------------------------------------------------*/ + +#if configUSE_TIMERS == 1 + + void vTaskPlaceOnEventListRestricted( const xList * const pxEventList, portTickType xTicksToWait ) + { + portTickType xTimeToWake; + + configASSERT( pxEventList ); + + /* This function should not be called by application code hence the + 'Restricted' in its name. It is not part of the public API. It is + designed for use by kernel code, and has special calling requirements - + it should be called from a critical section. */ + + + /* Place the event list item of the TCB in the appropriate event list. + In this case it is assume that this is the only task that is going to + be waiting on this event list, so the faster vListInsertEnd() function + can be used in place of vListInsert. */ + vListInsertEnd( ( xList * ) pxEventList, ( xListItem * ) &( pxCurrentTCB->xEventListItem ) ); + + /* We must remove this task from the ready list before adding it to the + blocked list as the same list item is used for both lists. This + function is called form a critical section. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter. */ + xTimeToWake = xTickCount + xTicksToWait; + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + +#endif /* configUSE_TIMERS */ +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskRemoveFromEventList( const xList * const pxEventList ) +{ +tskTCB *pxUnblockedTCB; +portBASE_TYPE xReturn; + + /* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED OR THE + SCHEDULER SUSPENDED. It can also be called from within an ISR. */ + + /* The event list is sorted in priority order, so we can remove the + first in the list, remove the TCB from the delayed list, and add + it to the ready list. + + If an event is for a queue that is locked then this function will never + get called - the lock count on the queue will get modified instead. This + means we can always expect exclusive access to the event list here. + + This function assumes that a check has already been made to ensure that + pxEventList is not empty. */ + pxUnblockedTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + configASSERT( pxUnblockedTCB ); + vListRemove( &( pxUnblockedTCB->xEventListItem ) ); + + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + vListRemove( &( pxUnblockedTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxUnblockedTCB ); + } + else + { + /* We cannot access the delayed or ready lists, so will hold this + task pending until the scheduler is resumed. */ + vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + } + + if( pxUnblockedTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has + a higher priority than the calling task. This allows + the calling task to know if it should force a context + switch now. */ + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskSetTimeOutState( xTimeOutType * const pxTimeOut ) +{ + configASSERT( pxTimeOut ); + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xTaskCheckForTimeOut( xTimeOutType * const pxTimeOut, portTickType * const pxTicksToWait ) +{ +portBASE_TYPE xReturn; + + configASSERT( pxTimeOut ); + configASSERT( pxTicksToWait ); + + taskENTER_CRITICAL(); + { + #if ( INCLUDE_vTaskSuspend == 1 ) + /* If INCLUDE_vTaskSuspend is set to 1 and the block time specified is + the maximum block time then the task should block indefinitely, and + therefore never time out. */ + if( *pxTicksToWait == portMAX_DELAY ) + { + xReturn = pdFALSE; + } + else /* We are not blocking indefinitely, perform the checks below. */ + #endif + + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( ( portTickType ) xTickCount >= ( portTickType ) pxTimeOut->xTimeOnEntering ) ) + { + /* The tick count is greater than the time at which vTaskSetTimeout() + was called, but has also overflowed since vTaskSetTimeOut() was called. + It must have wrapped all the way around and gone past us again. This + passed since vTaskSetTimeout() was called. */ + xReturn = pdTRUE; + } + else if( ( ( portTickType ) ( ( portTickType ) xTickCount - ( portTickType ) pxTimeOut->xTimeOnEntering ) ) < ( portTickType ) *pxTicksToWait ) + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= ( ( portTickType ) xTickCount - ( portTickType ) pxTimeOut->xTimeOnEntering ); + vTaskSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskMissedYield( void ) +{ + xMissedYield = pdTRUE; +} + +/* + * ----------------------------------------------------------- + * The Idle task. + * ---------------------------------------------------------- + * + * The portTASK_FUNCTION() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION( prvIdleTask, pvParameters ) +{ + /* Stop warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* See if any tasks have been deleted. */ + prvCheckTasksWaitingTermination(); + //printf("\nidle task\n"); + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + see if any other task has become available. If we are using + preemption we don't need to do this as any task becoming available + will automatically get the processor anyway. */ + taskYIELD(); + } + #endif + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + timesliced. If a task that is sharing the idle priority is ready + to run then the idle task should yield before the end of the + timeslice. + + A critical region is not required here as we are just reading from + the list, and an occasional incorrect value will not matter. If + the ready list at the idle priority contains more than one task + then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( unsigned portBASE_TYPE ) 1 ) + { + taskYIELD(); + } + } + #endif + + #if ( configUSE_IDLE_HOOK == 1 ) + { + extern void vApplicationIdleHook( void ); + + /* Call the user defined function from within the idle task. This + allows the application designer to add background functionality + without the overhead of a separate task. + NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, + CALL A FUNCTION THAT MIGHT BLOCK. */ + vApplicationIdleHook(); + } + #endif + } +} /*lint !e715 pvParameters is not accessed but all task functions require the same prototype. */ + + + + + + + +/*----------------------------------------------------------- + * File private functions documented at the top of the file. + *----------------------------------------------------------*/ + + + +static void prvInitialiseTCBVariables( tskTCB *pxTCB, const signed char * const pcName, unsigned portBASE_TYPE uxPriority, const xMemoryRegion * const xRegions, unsigned short usStackDepth ) +{ + /* Store the function name in the TCB. */ + #if configMAX_TASK_NAME_LEN > 1 + { + /* Don't bring strncpy into the build unnecessarily. */ + if(NULL == pcName) + *( char * ) pxTCB->pcTaskName = (char)(configMAX_PRIORITIES - uxPriority); + else + strncpy( ( char * ) pxTCB->pcTaskName, ( const char * ) pcName, ( unsigned short ) configMAX_TASK_NAME_LEN ); + } + #endif + pxTCB->pcTaskName[ ( unsigned short ) configMAX_TASK_NAME_LEN - ( unsigned short ) 1 ] = ( signed char ) '\0'; + + /* This is used as an array index so must ensure it's not too large. First + remove the privilege bit if one is present. */ + + if( uxPriority >= configMAX_PRIORITIES ) + { + uxPriority = configMAX_PRIORITIES - ( unsigned portBASE_TYPE ) 1U; + } + + pxTCB->uxPriority = uxPriority; + #if ( configUSE_MUTEXES == 1 ) + { + pxTCB->uxBasePriority = uxPriority; + } + #endif + + vListInitialiseItem( &( pxTCB->xGenericListItem ) ); + vListInitialiseItem( &( pxTCB->xEventListItem ) ); + + /* Set the pxTCB as a link back from the xListItem. This is so we can get + back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxPriority ); + listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB ); + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + { + pxTCB->uxCriticalNesting = ( unsigned portBASE_TYPE ) 0U; + } + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + { + pxTCB->pxTaskTag = NULL; + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxTCB->ulRunTimeCounter = 0UL; + } + #endif + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth ); + } + #else + { + ( void ) xRegions; + ( void ) usStackDepth; + } + #endif +} +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) + + void vTaskAllocateMPURegions( xTaskHandle xTaskToModify, const xMemoryRegion * const xRegions ) + { + tskTCB *pxTCB; + + if( xTaskToModify == pxCurrentTCB ) + { + xTaskToModify = NULL; + } + + /* If null is passed in here then we are deleting ourselves. */ + pxTCB = prvGetTCBFromHandle( xTaskToModify ); + + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, NULL, 0 ); + } + /*-----------------------------------------------------------*/ +#endif + +static void prvInitialiseTaskLists( void ) +{ +unsigned portBASE_TYPE uxPriority; + + for( uxPriority = ( unsigned portBASE_TYPE ) 0U; uxPriority < configMAX_PRIORITIES; uxPriority++ ) + { + vListInitialise( ( xList * ) &( pxReadyTasksLists[ uxPriority ] ) ); + } + + vListInitialise( ( xList * ) &xDelayedTaskList1 ); + vListInitialise( ( xList * ) &xDelayedTaskList2 ); + vListInitialise( ( xList * ) &xPendingReadyList ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + vListInitialise( ( xList * ) &xTasksWaitingTermination ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + vListInitialise( ( xList * ) &xSuspendedTaskList ); + } + #endif + + /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList + using list2. */ + pxDelayedTaskList = &xDelayedTaskList1; + pxOverflowDelayedTaskList = &xDelayedTaskList2; +} +/*-----------------------------------------------------------*/ + +static void prvCheckTasksWaitingTermination( void ) +{ + #if ( INCLUDE_vTaskDelete == 1 ) + { + portBASE_TYPE xListIsEmpty; + + /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called + too often in the idle task. */ + if( uxTasksDeleted > ( unsigned portBASE_TYPE ) 0U ) + { + vTaskSuspendAll(); + xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); + xTaskResumeAll(); + + if( xListIsEmpty == pdFALSE ) + { + tskTCB *pxTCB; + + taskENTER_CRITICAL(); + { + pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xTasksWaitingTermination ) ); + vListRemove( &( pxTCB->xGenericListItem ) ); + --uxCurrentNumberOfTasks; + --uxTasksDeleted; + } + taskEXIT_CRITICAL(); + + prvDeleteTCB( pxTCB ); + } + } + } + #endif +} +/*-----------------------------------------------------------*/ + +static void prvAddCurrentTaskToDelayedList( portTickType xTimeToWake ) +{ + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + /* If the task entering the blocked state was placed at the head of the + list of blocked tasks then xNextTaskUnblockTime needs to be updated + too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + } +} +/*-----------------------------------------------------------*/ + +static tskTCB *prvAllocateTCBAndStack( unsigned short usStackDepth, portSTACK_TYPE *puxStackBuffer ) +{ +tskTCB *pxNewTCB; + + /* Allocate space for the TCB. Where the memory comes from depends on + the implementation of the port malloc function. */ + pxNewTCB = ( tskTCB * ) pvPortMalloc( sizeof( tskTCB ) ); + + if( pxNewTCB != NULL ) + { + /* Allocate space for the stack used by the task being created. + The base of the stack memory stored in the TCB so the task can + be deleted later if required. */ + pxNewTCB->pxStack = ( portSTACK_TYPE * ) pvPortMallocAligned( ( ( ( size_t )usStackDepth ) * sizeof( portSTACK_TYPE ) ), puxStackBuffer ); + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + else + { + pxNewTCB->stacksize = ( size_t ) usStackDepth * sizeof( portSTACK_TYPE ); + /* Just to help debugging. */ + memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( portSTACK_TYPE ) ); + } + } + + return pxNewTCB; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + static void prvListTaskWithinSingleList( const signed char *pcWriteBuffer, xList *pxList, signed char cStatus ) + { + volatile tskTCB *pxNextTCB, *pxFirstTCB; + unsigned short usStackRemaining; + unsigned short usCurStack = 0; + + /* Write the details of all the TCB's in pxList into the buffer. */ + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + #if ( portSTACK_GROWTH > 0 ) + { + usStackRemaining = usTaskCheckFreeStackSpace( ( unsigned char * ) pxNextTCB->pxEndOfStack ); + } + #else + { + usStackRemaining = usTaskCheckFreeStackSpace( ( unsigned char * ) pxNextTCB->pxStack ); + usStackRemaining*=4; //ת��byte + if(pxNextTCB->pxTopOfStack >= pxNextTCB->pxStack) + { + usCurStack = (pxNextTCB->pxTopOfStack - pxNextTCB->pxStack)*4; //ת��byte + } + } + #endif + + //sprintf( pcStatusString, ( char * ) "%s\t\t%c\t%u\t%u\t%u\r\n", pxNextTCB->pcTaskName, cStatus, ( unsigned int ) pxNextTCB->uxPriority, usStackRemaining, ( unsigned int ) pxNextTCB->uxTCBNumber ); + sprintf( pcStatusString, ( char * ) "%s\t\t%u\t\t%c\t\t%u\t\t%u\t\t%u\t\r\n", pxNextTCB->pcTaskName, ( unsigned int ) (pxNextTCB->uxPriority), cStatus, ( unsigned int )pxNextTCB->stacksize, ( unsigned int )usCurStack,( unsigned int )usStackRemaining ); + strcat( ( char * ) pcWriteBuffer, ( char * ) pcStatusString ); + + } while( pxNextTCB != pxFirstTCB ); + } + +#endif +#if ( INCLUDE_vTaskDelete == 1 ) +static tskTCB * prvListTaskWithinSingleList1(const unsigned long priority, xList *pxList, signed char cStatus ) +{ + tskTCB *pxNextTCB, *pxFirstTCB; + + /* Write the details of all the TCB's in pxList into the buffer. */ + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + if (pxNextTCB->uxPriority == priority) + { + return pxNextTCB; + } + } while( pxNextTCB != pxFirstTCB ); + return NULL; +} + +tskTCB * vTaskGetTaskByPriority(unsigned long priority) +{ + unsigned portBASE_TYPE uxQueue; + tskTCB *ptasktcb = NULL; + + /* This is a VERY costly function that should be used for debug only. + It leaves interrupts disabled for a LONG time. */ + + vTaskSuspendAll(); + { + /* Run through all the lists that could potentially contain a TCB and + report the task name, state and stack high water mark. */ + + uxQueue = uxTopUsedPriority + ( unsigned portBASE_TYPE ) 1U; + do + { + uxQueue--; + if( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxQueue ] ) ) == pdFALSE ) + { + ptasktcb = prvListTaskWithinSingleList1( priority, ( xList * ) &( pxReadyTasksLists[ uxQueue ] ), tskREADY_CHAR ); + if (NULL != ptasktcb) + { + goto ENDTASK; + } + } + }while( uxQueue > ( unsigned short ) tskIDLE_PRIORITY ); + + if( listLIST_IS_EMPTY( pxDelayedTaskList ) == pdFALSE ) + { + ptasktcb = prvListTaskWithinSingleList1( priority, ( xList * ) pxDelayedTaskList, tskBLOCKED_CHAR ); + if (NULL != ptasktcb) + { + goto ENDTASK; + } + } + + if( listLIST_IS_EMPTY( pxOverflowDelayedTaskList ) == pdFALSE ) + { + ptasktcb = prvListTaskWithinSingleList1( priority, ( xList * ) pxOverflowDelayedTaskList, tskBLOCKED_CHAR ); + if (NULL != ptasktcb) + { + goto ENDTASK; + } + } + + #if( INCLUDE_vTaskDelete == 1 ) + { + if( listLIST_IS_EMPTY( &xTasksWaitingTermination ) == pdFALSE ) + { + ptasktcb = prvListTaskWithinSingleList1( priority, &xTasksWaitingTermination, tskDELETED_CHAR ); + if (NULL != ptasktcb) + { + goto ENDTASK; + } + } + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( listLIST_IS_EMPTY( &xSuspendedTaskList ) == pdFALSE ) + { + ptasktcb = prvListTaskWithinSingleList1( priority, &xSuspendedTaskList, tskSUSPENDED_CHAR ); + if (NULL != ptasktcb) + { + goto ENDTASK; + } + } + } + #endif + } +ENDTASK: + xTaskResumeAll(); + + return ptasktcb; +} +#endif +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + static void prvGenerateRunTimeStatsForTasksInList( const signed char *pcWriteBuffer, xList *pxList, unsigned long ulTotalRunTime ) + { + volatile tskTCB *pxNextTCB, *pxFirstTCB; + unsigned long ulStatsAsPercentage; + + /* Write the run time stats of all the TCB's in pxList into the buffer. */ + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + /* Get next TCB in from the list. */ + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + + /* Divide by zero check. */ + if( ulTotalRunTime > 0UL ) + { + /* Has the task run at all? */ + if( pxNextTCB->ulRunTimeCounter == 0UL ) + { + /* The task has used no CPU time at all. */ + sprintf( pcStatsString, ( char * ) "%s\t\t0\t\t0%%\r\n", pxNextTCB->pcTaskName ); + } + else + { + /* What percentage of the total run time has the task used? + This will always be rounded down to the nearest integer. + ulTotalRunTime has already been divided by 100. */ + ulStatsAsPercentage = pxNextTCB->ulRunTimeCounter / ulTotalRunTime; + + if( ulStatsAsPercentage > 0UL ) + { + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + sprintf( pcStatsString, ( char * ) "%s\t\t%lu\t\t%lu%%\r\n", pxNextTCB->pcTaskName, pxNextTCB->ulRunTimeCounter, ulStatsAsPercentage ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + printf() library can be used. */ + sprintf( pcStatsString, ( char * ) "%s\t\t%u\t\t%u%%\r\n", pxNextTCB->pcTaskName, ( unsigned int ) pxNextTCB->ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); + } + #endif + } + else + { + /* If the percentage is zero here then the task has + consumed less than 1% of the total run time. */ + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + sprintf( pcStatsString, ( char * ) "%s\t\t%lu\t\t<1%%\r\n", pxNextTCB->pcTaskName, pxNextTCB->ulRunTimeCounter ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + printf() library can be used. */ + sprintf( pcStatsString, ( char * ) "%s\t\t%u\t\t<1%%\r\n", pxNextTCB->pcTaskName, ( unsigned int ) pxNextTCB->ulRunTimeCounter ); + } + #endif + } + } + + strcat( ( char * ) pcWriteBuffer, ( char * ) pcStatsString ); + } + + } while( pxNextTCB != pxFirstTCB ); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + + static unsigned short usTaskCheckFreeStackSpace( const unsigned char * pucStackByte ) + { + register unsigned short usCount = 0U; + + while( *pucStackByte == tskSTACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + usCount++; + } + + usCount /= sizeof( portSTACK_TYPE ); + + return usCount; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) + + unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask ) + { + tskTCB *pxTCB; + unsigned char *pcEndOfStack; + unsigned portBASE_TYPE uxReturn; + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pcEndOfStack = ( unsigned char * ) pxTCB->pxStack; + } + #else + { + pcEndOfStack = ( unsigned char * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = ( unsigned portBASE_TYPE ) usTaskCheckFreeStackSpace( pcEndOfStack ); + + return uxReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( tskTCB *pxTCB ) + { + /* Free up the memory allocated by the scheduler for the task. It is up to + the task to free any memory allocated at the application level. */ + vPortFreeAligned( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + +#endif + + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) + + xTaskHandle xTaskGetCurrentTaskHandle( void ) + { + xTaskHandle xReturn; + + /* A critical section is not required as this is not called from + an interrupt and the current TCB will always be the same for any + individual execution thread. */ + xReturn = pxCurrentTCB; + + return xReturn; + } + +#endif + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + + portBASE_TYPE xTaskGetSchedulerState( void ) + { + portBASE_TYPE xReturn; + + if( xSchedulerRunning == pdFALSE ) + { + xReturn = taskSCHEDULER_NOT_STARTED; + } + else + { + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + xReturn = taskSCHEDULER_RUNNING; + } + else + { + xReturn = taskSCHEDULER_SUSPENDED; + } + } + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityInherit( xTaskHandle * const pxMutexHolder ) + { + tskTCB * const pxTCB = ( tskTCB * ) pxMutexHolder; + + configASSERT( pxMutexHolder ); + + if( pxTCB->uxPriority < pxCurrentTCB->uxPriority ) + { + /* Adjust the mutex holder state to account for its new priority. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) pxCurrentTCB->uxPriority ); + + /* If the task being modified is in the ready state it will need to + be moved in to a new list. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xGenericListItem ) ) != pdFALSE ) + { + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Inherit the priority before being moved into the new list. */ + pxTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyQueue( pxTCB ); + } + else + { + /* Just inherit the priority. */ + pxTCB->uxPriority = pxCurrentTCB->uxPriority; + } + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityDisinherit( xTaskHandle * const pxMutexHolder ) + { + tskTCB * const pxTCB = ( tskTCB * ) pxMutexHolder; + + if( pxMutexHolder != NULL ) + { + if( pxTCB->uxPriority != pxTCB->uxBasePriority ) + { + /* We must be the running task to be able to give the mutex back. + Remove ourselves from the ready list we currently appear in. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Disinherit the priority before adding ourselves into the new + ready list. */ + pxTCB->uxPriority = pxTCB->uxBasePriority; + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) pxTCB->uxPriority ); + prvAddTaskToReadyQueue( pxTCB ); + } + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + + void vTaskEnterCritical( void ) + { + portDISABLE_INTERRUPTS(); + + if( xSchedulerRunning != pdFALSE ) + { + ( pxCurrentTCB->uxCriticalNesting )++; + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + +void vTaskExitCritical( void ) +{ + if( xSchedulerRunning != pdFALSE ) + { + if( pxCurrentTCB->uxCriticalNesting > 0U ) + { + ( pxCurrentTCB->uxCriticalNesting )--; + + if( pxCurrentTCB->uxCriticalNesting == 0U ) + { + portENABLE_INTERRUPTS(); + } + } + } +} + +#endif + +u32 tls_get_current_task(void) +{ + return pxCurrentTCB->uxPriority; +} + +void show_taskstack(void) +{ + unsigned int *sp = 0; + unsigned int stacksize = 0; + void *cur, *start = 0; + int i = 0; + portDISABLE_INTERRUPTS(); + stacksize = pxCurrentTCB->stacksize/4; + start = pxCurrentTCB->pxStack + stacksize; + cur = (void *)pxCurrentTCB->pxTopOfStack; + printf("Stack Range[%p,%p], used size:%d\n",pxCurrentTCB->pxStack, start, ((unsigned int *)start - (unsigned int *)cur)); + printf("Priority:%ld", configMAX_PRIORITIES - pxCurrentTCB->uxPriority); + if (pxCurrentTCB->pcTaskName[0] != '\0') + { + printf("\nTaskname:%s", pxCurrentTCB->pcTaskName); + } + for (sp = (unsigned int *) cur; sp <= (unsigned int *)start; sp++) + { + if (i%4 == 0) + { + printf("\n%p:", sp); + } + printf("%08x ", *sp); + i++; + } + portENABLE_INTERRUPTS(); +} + +u32 * vTaskStackAddr() +{ + return (u32 *)pxCurrentTCB->pxStack; +} + +u32 vTaskStackSize() +{ + return pxCurrentTCB->stacksize; +} + +char * vTaskName() +{ + return (char*)pxCurrentTCB->pcTaskName; +} + +#else +void *pxCurrentTCB = NULL; +volatile unsigned portLONG ulCriticalNesting = 9999UL; +void vTaskIncrementTick( void ) +{} + +void vTaskSwitchContext( void ) +{} + +#endif +/*-----------------------------------------------------------*/ + + + + diff --git a/sdk/Src/OS/RTOS/wm_osal_rtos.c b/sdk/Src/OS/RTOS/wm_osal_rtos.c new file mode 100644 index 0000000..22e1473 --- /dev/null +++ b/sdk/Src/OS/RTOS/wm_osal_rtos.c @@ -0,0 +1,1213 @@ +/***************************************************************************** +* +* File Name : wm_osal_rtos.h +* +* Description: rtos include Module +* +* Copyright (c) 2014 Winner Microelectronics Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-8-27 +*****************************************************************************/ + +#ifndef WM_OS_RTOS_H +#define WM_OS_RTOS_H + +#include "wm_config.h" +#if TLS_OS_FREERTOS + +//#include "include.h" +#include +#include +#include "wm_type_def.h" +#include "wm_irq.h" +#include "FreeRTOS.h" +#include "task.h" +#include "rtosqueue.h" +#include "semphr.h" +#include "rtostimers.h" +#include "FreeRTOSConfig.h" +#include "wm_osal.h" +#include "wm_mem.h" +/* +********************************************************************************************************* +* CREATE A TASK (Extended Version) +* +* Description: This function is used to have uC/OS-II manage the execution of a task. Tasks can either +* be created prior to the start of multitasking or by a running task. A task cannot be +* created by an ISR. +* +* Arguments : task is a pointer to the task' +* +* name is the task's name +* +* entry is the task's entry function +* +* param is a pointer to an optional data area which can be used to pass parameters to +* the task when the task first executes. Where the task is concerned it thinks +* it was invoked and passed the argument 'param' as follows: +* +* void Task (void *param) +* { +* for (;;) { +* Task code; +* } +* } +* +* stk_start is a pointer to the task's bottom of stack. +* +* stk_size is the size of the stack in number of elements. If OS_STK is set to u8, +* 'stk_size' corresponds to the number of bytes available. If OS_STK is set to +* INT16U, 'stk_size' contains the number of 16-bit entries available. Finally, if +* OS_STK is set to INT32U, 'stk_size' contains the number of 32-bit entries +* available on the stack. +* +* prio is the task's priority. A unique priority MUST be assigned to each task and the +* lower the number, the higher the priority. +* +* flag contains additional information about the behavior of the task. +* +* Returns : TLS_OS_SUCCESS if the function was successful. +* TLS_OS_ERROR +********************************************************************************************************* +*/ +tls_os_status_t tls_os_task_create(tls_os_task_t *task, + const char* name, + void (*entry)(void* param), + void* param, + u8 *stk_start, + u32 stk_size, + u32 prio, + u32 flag) +{ + u8 error; + tls_os_status_t os_status; + + if (((u32)stk_start >= TASK_STACK_USING_MEM_UPPER_RANGE) + ||(((u32)stk_start + stk_size) >= TASK_STACK_USING_MEM_UPPER_RANGE)) + { + printf("\nCurrent Stack [0x%8x, 0x%8x) is NOT in VALID STACK range [0x20000000,0x20028000)\n", (u32)stk_start, (u32)(stk_start + stk_size)); + printf("Please refer to APIs' manul and modify task stack position!!!\n"); + return TLS_OS_ERROR; + } else { + printf("[OS] Create Task '%s' with size %u and priority %lu (%d)\r\n", name, stk_size, configMAX_PRIORITIES - prio, prio); + } + + error = xTaskCreateExt(entry, + (const signed char *)name, + (portSTACK_TYPE *)stk_start, + stk_size/sizeof(u32), + param, + configMAX_PRIORITIES - prio, /*���ȼ��ߵ�һ�£���ucos���ȼ�˳���෴*/ + task ); + if (error == pdTRUE) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + + +/* +********************************************************************************************************* +* DELETE A TASK +* +* Description: This function allows you to delete a task. The calling task can delete itself by +* its own priority number. The deleted task is returned to the dormant state and can be +* re-activated by creating the deleted task again. +* +* Arguments : prio: the task priority +* freefun: function to free resource +* +* Returns : TLS_OS_SUCCESS if the call is successful +* TLS_OS_ERROR +********************************************************************************************************* +*/ +#if ( INCLUDE_vTaskDelete == 1 ) +tls_os_status_t tls_os_task_del(u8 prio,void (*freefun)(void)) +{ + if (0 == vTaskDeleteByPriority(configMAX_PRIORITIES - prio)){ + if (freefun){ + freefun(); + } + return TLS_OS_SUCCESS; + } + + return TLS_OS_ERROR; +} +#endif + +#if (INCLUDE_vTaskSuspend == 1) +/* +********************************************************************************************************* +* SUSPEND A TASK +* +* Description: This function is called to suspend a task. +* +* Arguments : task is a pointer to the task +* +* Returns : TLS_OS_SUCCESS if the requested task is suspended +* +* Note : You should use this function with great care. If you suspend a task that is waiting for +* an event (i.e. a message, a semaphore, a queue ...) you will prevent this task from +* running when the event arrives. +********************************************************************************************************* +*/ + tls_os_status_t tls_os_task_suspend(tls_os_task_t *task) +{ + vTaskSuspend(task); + + return TLS_OS_SUCCESS; +} + +/* +********************************************************************************************************* +* RESUME A SUSPENDED TASK +* +* Description: This function is called to resume a previously suspended task. +* +* Arguments : task is a pointer to the task +* +* Returns : TLS_OS_SUCCESS if the requested task is resumed +* +********************************************************************************************************* +*/ + tls_os_status_t tls_os_task_resume(tls_os_task_t *task) +{ + vTaskResume(task); + + return TLS_OS_SUCCESS; +} +#endif + +/* +********************************************************************************************************* +* CREATE A MUTUAL EXCLUSION SEMAPHORE +* +* Description: This function creates a mutual exclusion semaphore. +* +* Arguments : prio is the priority to use when accessing the mutual exclusion semaphore. In +* other words, when the semaphore is acquired and a higher priority task +* attempts to obtain the semaphore then the priority of the task owning the +* semaphore is raised to this priority. It is assumed that you will specify +* a priority that is LOWER in value than ANY of the tasks competing for the +* mutex. +* +* mutex is a pointer to the event control clock (OS_EVENT) associated with the +* created mutex. +* +* +* Returns :TLS_OS_SUCCESS if the call was successful. +* TLS_OS_ERROR +* +* Note(s) : 1) The LEAST significant 8 bits of '.OSEventCnt' are used to hold the priority number +* of the task owning the mutex or 0xFF if no task owns the mutex. +* +* 2) The MOST significant 8 bits of '.OSEventCnt' are used to hold the priority number +* to use to reduce priority inversion. +********************************************************************************************************* +*/ +#if (1 == configUSE_MUTEXES) + tls_os_status_t tls_os_mutex_create(u8 prio, + tls_os_mutex_t **mutex) +{ + tls_os_status_t os_status; + + *mutex = xSemaphoreCreateMutex(); + + if (*mutex != NULL) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + +/* +********************************************************************************************************* +* DELETE A MUTEX +* +* Description: This function deletes a mutual exclusion semaphore and readies all tasks pending on the it. +* +* Arguments : mutex is a pointer to the event control block associated with the desired mutex. +* +* Returns : TLS_OS_SUCCESS The call was successful and the mutex was deleted +* TLS_OS_ERROR error +* +* Note(s) : 1) This function must be used with care. Tasks that would normally expect the presence of +* the mutex MUST check the return code of OSMutexPend(). +* +* 2) This call can potentially disable interrupts for a long time. The interrupt disable +* time is directly proportional to the number of tasks waiting on the mutex. +* +* 3) Because ALL tasks pending on the mutex will be readied, you MUST be careful because the +* resource(s) will no longer be guarded by the mutex. +* +* 4) IMPORTANT: In the 'OS_DEL_ALWAYS' case, we assume that the owner of the Mutex (if there +* is one) is ready-to-run and is thus NOT pending on another kernel object or +* has delayed itself. In other words, if a task owns the mutex being deleted, +* that task will be made ready-to-run at its original priority. +********************************************************************************************************* +*/ + tls_os_status_t tls_os_mutex_delete(tls_os_mutex_t *mutex) +{ + vSemaphoreDelete((xQUEUE *)mutex); + + return TLS_OS_SUCCESS; +} + +/* +********************************************************************************************************* +* PEND ON MUTUAL EXCLUSION SEMAPHORE +* +* Description: This function waits for a mutual exclusion semaphore. +* +* Arguments : mutex is a pointer to the event control block associated with the desired +* mutex. +* +* wait_time is an optional timeout period (in clock ticks). If non-zero, your task will +* wait for the resource up to the amount of time specified by this argument. +* If you specify 0, however, your task will wait forever at the specified +* mutex or, until the resource becomes available. +* +* +* +* Returns : TLS_OS_SUCCESS The call was successful and your task owns the mutex +* TLS_OS_ERROR +* +* Note(s) : 1) The task that owns the Mutex MUST NOT pend on any other event while it owns the mutex. +* +* 2) You MUST NOT change the priority of the task that owns the mutex +********************************************************************************************************* +*/ +//���������� + tls_os_status_t tls_os_mutex_acquire(tls_os_mutex_t *mutex, + u32 wait_time) +{ + u8 error; + tls_os_status_t os_status; + unsigned int time; + + if(0 == wait_time) + time = portMAX_DELAY; + else + time = wait_time; + error = xSemaphoreTake((xQUEUE *)mutex, time ); + if (error == pdPASS) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + + +/* +********************************************************************************************************* +* POST TO A MUTUAL EXCLUSION SEMAPHORE +* +* Description: This function signals a mutual exclusion semaphore +* +* Arguments : mutex is a pointer to the event control block associated with the desired +* mutex. +* +* Returns : TLS_OS_SUCCESS The call was successful and the mutex was signaled. +* TLS_OS_ERROR +********************************************************************************************************* +*/ + tls_os_status_t tls_os_mutex_release(tls_os_mutex_t *mutex) +{ + u8 error; + tls_os_status_t os_status; + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { + error = xSemaphoreGiveFromISR((xQUEUE *)mutex, &pxHigherPriorityTaskWoken ); + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + portYIELD_FROM_ISR(); + } + } + else + { + error = xSemaphoreGive((xQUEUE *)mutex ); + } + if (error == pdPASS) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + +#endif + +/* +********************************************************************************************************* +* CREATE A SEMAPHORE +* +* Description: This function creates a semaphore. +* +* Arguments :sem is a pointer to the event control block (OS_EVENT) associated with the +* created semaphore +* cnt is the initial value for the semaphore. If the value is 0, no resource is +* available (or no event has occurred). You initialize the semaphore to a +* non-zero value to specify how many resources are available (e.g. if you have +* 10 resources, you would initialize the semaphore to 10). +* +* Returns : TLS_OS_SUCCESS The call was successful +* TLS_OS_ERROR +********************************************************************************************************* +*/ + +#if (1 == configUSE_COUNTING_SEMAPHORES) + tls_os_status_t tls_os_sem_create(tls_os_sem_t **sem, u32 cnt) +{ + tls_os_status_t os_status; + + *sem = xSemaphoreCreateCounting( configSEMAPHORE_INIT_VALUE, cnt ); + + + if (*sem != NULL) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + + +/* +********************************************************************************************************* +* DELETE A SEMAPHORE +* +* Description: This function deletes a semaphore and readies all tasks pending on the semaphore. +* +* Arguments : sem is a pointer to the event control block associated with the desired +* semaphore. +* +* Returns : TLS_OS_SUCCESS The call was successful and the semaphore was deleted +* TLS_OS_ERROR +* +********************************************************************************************************* +*/ + tls_os_status_t tls_os_sem_delete(tls_os_sem_t *sem) +{ + vSemaphoreDelete((xQUEUE *)sem); + + return TLS_OS_SUCCESS; +} + +/* +********************************************************************************************************* +* PEND ON SEMAPHORE +* +* Description: This function waits for a semaphore. +* +* Arguments : sem is a pointer to the event control block associated with the desired +* semaphore. +* +* wait_time is an optional timeout period (in clock ticks). If non-zero, your task will +* wait for the resource up to the amount of time specified by this argument. +* If you specify 0, however, your task will wait forever at the specified +* semaphore or, until the resource becomes available (or the event occurs). +* +* Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ +//�ú������������жϷ�������� + tls_os_status_t tls_os_sem_acquire(tls_os_sem_t *sem, + u32 wait_time) +{ + u8 error; + tls_os_status_t os_status; + unsigned int time; + + if(0 == wait_time) + time = portMAX_DELAY; + else + time = wait_time; + error = xSemaphoreTake((xQUEUE *)sem, time ); + if (error == pdPASS) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + +/* +********************************************************************************************************* +* POST TO A SEMAPHORE +* +* Description: This function signals a semaphore +* +* Arguments : sem is a pointer to the event control block associated with the desired +* semaphore. +* +* Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ + tls_os_status_t tls_os_sem_release(tls_os_sem_t *sem) +{ + u8 error; + tls_os_status_t os_status; + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { + error = xSemaphoreGiveFromISR((xQUEUE *)sem, &pxHigherPriorityTaskWoken ); + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + portYIELD_FROM_ISR(); + } + } + else + { + error = xSemaphoreGive((xQUEUE *)sem ); + } + if (error == pdPASS) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; + +} +#endif + +// ========================================================================= // +// Message Passing // +// ========================================================================= // + +/* +********************************************************************************************************* +* CREATE A MESSAGE QUEUE +* +* Description: This function creates a message queue if free event control blocks are available. +* +* Arguments : queue is a pointer to the event control clock (OS_EVENT) associated with the +* created queue +* +* queue_start is a pointer to the base address of the message queue storage area. The +* storage area MUST be declared as an array of pointers to 'void' as follows +* +* void *MessageStorage[size] +* +* queue_size is the number of elements in the storage area +* +* msg_size +* +* Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ + + tls_os_status_t tls_os_queue_create(tls_os_queue_t **queue, u32 queue_size) +{ + tls_os_status_t os_status; + u32 queuesize = 10; + void *queue_start = NULL; + + if (queue_size) + { + queuesize = queue_size; + } + queue_start = tls_mem_alloc(queuesize *(sizeof(void *))); + if (NULL == queue_start) + { + return TLS_OS_ERROR; + } + + *queue = xQueueCreateExt(queue_start, queuesize, sizeof(void *)); + + if (*queue != NULL) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + +/* +********************************************************************************************************* +* DELETE A MESSAGE QUEUE +* +* Description: This function deletes a message queue and readies all tasks pending on the queue. +* +* Arguments : queue is a pointer to the event control block associated with the desired +* queue. +* +* +* Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ +extern u32 __heap_base; + tls_os_status_t tls_os_queue_delete(tls_os_queue_t *queue) +{ + + if ((u32 *)(((xQUEUE *)queue)->pcHead) >= &__heap_base) //���û�дӶ����룬�����ͷ�) + { + tls_mem_free(((xQUEUE *)queue)->pcHead); + } + vQueueDeleteExt((xQUEUE *)queue); + + return TLS_OS_SUCCESS; +} + +/* +********************************************************************************************************* +* POST MESSAGE TO A QUEUE +* +* Description: This function sends a message to a queue +* +* Arguments : queue is a pointer to the event control block associated with the desired queue +* +* msg is a pointer to the message to send. +* +* msg_size +* Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ + tls_os_status_t tls_os_queue_send(tls_os_queue_t *queue, + void *msg, + u32 msg_size) +{ + u8 error; + tls_os_status_t os_status; + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { + error = xQueueSendFromISR((xQUEUE *) queue, &msg, &pxHigherPriorityTaskWoken ); + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + portYIELD_FROM_ISR(); + } + } + else + { + error = xQueueSend((xQUEUE *)queue, &msg, 0 ); + } + + if (error == pdPASS) + os_status = TLS_OS_SUCCESS; + else { + os_status = TLS_OS_ERROR; + } + + return os_status; +} +/* +********************************************************************************************************* +* PEND ON A QUEUE FOR A MESSAGE +* +* Description: This function waits for a message to be sent to a queue +* +* Arguments : queue is a pointer to the event control block associated with the desired queue +* +* msg is a pointer to the message received +* +* msg_size +* +* wait_time is an optional timeout period (in clock ticks). If non-zero, your task will +* wait for a message to arrive at the queue up to the amount of time +* specified by this argument. If you specify 0, however, your task will wait +* forever at the specified queue or, until a message arrives. +* +* Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ + tls_os_status_t tls_os_queue_receive(tls_os_queue_t *queue, + void **msg, + u32 msg_size, + u32 wait_time) +{ + u8 error; + tls_os_status_t os_status; + unsigned int xTicksToWait; + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + if(0 == wait_time) + xTicksToWait = portMAX_DELAY; + else + xTicksToWait = wait_time; + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { + error = xQueueReceiveFromISR((xQUEUE *)queue, msg, &pxHigherPriorityTaskWoken); + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + portYIELD_FROM_ISR(); + } + } + else + { + error = xQueueReceive((xQUEUE *)queue, msg, xTicksToWait ); + } + + if (error == pdPASS) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + +/* +********************************************************************************************************* +* FLUSH QUEUE +* +* Description : This function is used to flush the contents of the message queue. +* +* Arguments : none +* +* Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +*At present, no use for freeRTOS +********************************************************************************************************* +*/ +tls_os_status_t tls_os_queue_flush(tls_os_queue_t *queue) +{ + return TLS_OS_SUCCESS; +} + +/* +********************************************************************************************************* +* CREATE A MESSAGE MAILBOX +* +* Description: This function creates a message mailbox if free event control blocks are available. +* +* Arguments : mailbox is a pointer to the event control clock (OS_EVENT) associated with the +* created mailbox +* +* mailbox_start is a pointer to a message that you wish to deposit in the mailbox. If +* you set this value to the NULL pointer (i.e. (void *)0) then the mailbox +* will be considered empty. +* +* mailbox_size +* +* msg_size +* +Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ +#if (1 == configUSE_MAILBOX) + tls_os_status_t tls_os_mailbox_create(tls_os_mailbox_t **mailbox, + u32 mailbox_size) +{ + tls_os_status_t os_status; + u32 mbox_size = 1; + + if (mailbox_size) + { + mbox_size = mailbox_size; + } + + *mailbox = xQueueCreate(mbox_size, sizeof(void *)); + + if (*mailbox != NULL) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + +/* +********************************************************************************************************* +* DELETE A MAIBOX +* +* Description: This function deletes a mailbox and readies all tasks pending on the mailbox. +* +* Arguments : mailbox is a pointer to the event control block associated with the desired +* mailbox. +* +* +Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ + + tls_os_status_t tls_os_mailbox_delete(tls_os_mailbox_t *mailbox) +{ + vQueueDelete((xQUEUE *)mailbox); + + return TLS_OS_SUCCESS; +} + + +/* +********************************************************************************************************* +* POST MESSAGE TO A MAILBOX +* +* Description: This function sends a message to a mailbox +* +* Arguments : mailbox is a pointer to the event control block associated with the desired mailbox +* +* msg is a pointer to the message to send. You MUST NOT send a NULL pointer. +* +Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ + tls_os_status_t tls_os_mailbox_send(tls_os_mailbox_t *mailbox, + void *msg) +{ + u8 error; + tls_os_status_t os_status; + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { + error = xQueueSendFromISR( (xQUEUE *)mailbox, &msg, &pxHigherPriorityTaskWoken ); + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + vTaskSwitchContext(); + } + } + else + { + error = xQueueSend( (xQUEUE *)mailbox, &msg, 0 ); + } + + if (error == pdPASS) + os_status = TLS_OS_SUCCESS; + else { + os_status = TLS_OS_ERROR; + } + + return os_status; +} +/* +********************************************************************************************************* +* PEND ON MAILBOX FOR A MESSAGE +* +* Description: This function waits for a message to be sent to a mailbox +* +* Arguments : mailbox is a pointer to the event control block associated with the desired mailbox +* +* msg is a pointer to the message received +* +* wait_time is an optional timeout period (in clock ticks). If non-zero, your task will +* wait for a message to arrive at the mailbox up to the amount of time +* specified by this argument. If you specify 0, however, your task will wait +* forever at the specified mailbox or, until a message arrives. +* +*Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +********************************************************************************************************* +*/ + tls_os_status_t tls_os_mailbox_receive(tls_os_mailbox_t *mailbox, + void **msg, + u32 wait_time) +{ + u8 error; + tls_os_status_t os_status; + unsigned int xTicksToWait; + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + if(0 == wait_time) + xTicksToWait = portMAX_DELAY; + else + xTicksToWait = wait_time; + + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { + error = xQueueReceiveFromISR((xQUEUE *)mailbox, msg, &pxHigherPriorityTaskWoken); + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + portYIELD_FROM_ISR(); + } + } + else + { + error = xQueueReceive( (xQUEUE *)mailbox, msg, xTicksToWait ); + } + + if (error == pdPASS) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + +#endif + +/* +********************************************************************************************************* +* GET CURRENT SYSTEM TIME +* +* Description: This function is used by your application to obtain the current value of the 32-bit +* counter which keeps track of the number of clock ticks. +* +* Arguments : none +* +* Returns : The current value of OSTime +********************************************************************************************************* +*/ + u32 tls_os_get_time(void) +{ + return xTaskGetTickCountFromISR(); +} +u32 os_cnter = 0; +/********************************************************************************************************** +* Description: Disable interrupts by preserving the state of interrupts. +* +* Arguments : none +* +* Returns : cpu_sr +***********************************************************************************************************/ + u32 tls_os_set_critical(void) +{ + os_cnter++; + vPortEnterCritical(); + return 1; +} + +/********************************************************************************************************** +* Description: Enable interrupts by preserving the state of interrupts. +* +* Arguments : cpu_sr +* +* Returns : none +***********************************************************************************************************/ + void tls_os_release_critical(u32 cpu_sr) +{ + if (os_cnter) + os_cnter--; + vPortExitCritical(); +} + +/* +************************************************************************************************************************ +* CREATE A TIMER +* +* Description: This function is called by your application code to create a timer. +* +* Arguments : timer A pointer to an OS_TMR data structure.This is the 'handle' that your application +* will use to reference the timer created. +* +* callback Is a pointer to a callback function that will be called when the timer expires. The +* callback function must be declared as follows: +* +* void MyCallback (OS_TMR *ptmr, void *p_arg); +* +* callback_arg Is an argument (a pointer) that is passed to the callback function when it is called. +* +* period The 'period' being repeated for the timer. +* If you specified 'OS_TMR_OPT_PERIODIC' as an option, when the timer expires, it will +* automatically restart with the same period. +* +* repeat if repeat +* +* pname Is a pointer to an ASCII string that is used to name the timer. Names are useful for +* debugging. +* +*Returns : TLS_OS_SUCCESS +* TLS_OS_ERROR +************************************************************************************************************************ +*/ + tls_os_status_t tls_os_timer_create(tls_os_timer_t **timer, + TLS_OS_TIMER_CALLBACK callback, + void *callback_arg, + u32 period, + bool repeat, + u8 *name) +{ + tls_os_status_t os_status; + + if(0 == period) + period = 1; +#if configUSE_TIMERS + *timer = (xTimerHandle)xTimerCreateExt( (signed char *)name, period, repeat, NULL, callback, callback_arg ); +#endif + if (*timer != NULL) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + + return os_status; +} + +/* +************************************************************************************************************************ +* START A TIMER +* +* Description: This function is called by your application code to start a timer. +* +* Arguments : timer Is a pointer to an OS_TMR +* +************************************************************************************************************************ +*/ + void tls_os_timer_start(tls_os_timer_t *timer) +{ + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { +#if configUSE_TIMERS + xTimerStartFromISR((xTimerHandle)timer, &pxHigherPriorityTaskWoken ); +#endif + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + portYIELD_FROM_ISR(); + } + } + else + { +#if configUSE_TIMERS + xTimerStart((xTimerHandle)timer, 0 ); //no block time +#endif + } +} +/* +************************************************************************************************************************ +* CHANGE A TIMER WAIT TIME +* +* Description: This function is called by your application code to change a timer wait time. +* +* Arguments : timer Is a pointer to an OS_TMR +* +* ticks is the wait time +************************************************************************************************************************ +*/ + void tls_os_timer_change(tls_os_timer_t *timer, u32 ticks) +{ + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + if(0 == ticks) + ticks = 1; + + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { +#if configUSE_TIMERS + xTimerChangePeriodFromISR((xTimerHandle)timer, ticks, &pxHigherPriorityTaskWoken ); + xTimerStartFromISR( (xTimerHandle)timer, &pxHigherPriorityTaskWoken ); +#endif + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + portYIELD_FROM_ISR(); + } + } + else + { +#if configUSE_TIMERS + xTimerChangePeriod((xTimerHandle)timer, ticks, 0 ); + xTimerStart((xTimerHandle)timer, 0 ); +#endif + } +} +/* +************************************************************************************************************************ +* STOP A TIMER +* +* Description: This function is called by your application code to stop a timer. +* +* Arguments : timer Is a pointer to the timer to stop. +* +************************************************************************************************************************ +*/ + void tls_os_timer_stop(tls_os_timer_t *timer) +{ + portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE; + u8 isrcount = 0; + + isrcount = tls_get_isr_count(); + if(isrcount > 0) + { +#if configUSE_TIMERS + xTimerStopFromISR((xTimerHandle)timer, &pxHigherPriorityTaskWoken ); +#endif + if((pdTRUE == pxHigherPriorityTaskWoken) && (1 == isrcount)) + { + portYIELD_FROM_ISR(); + } + } + else + { +#if configUSE_TIMERS + xTimerStop((xTimerHandle)timer, 0 ); +#endif + } +} + + +/* +************************************************************************************************************************ +* Delete A TIMER +* +* Description: This function is called by your application code to delete a timer. +* +* Arguments : timer Is a pointer to the timer to delete. +* +************************************************************************************************************************ +*/ + tls_os_status_t tls_os_timer_delete(tls_os_timer_t *timer) +{ + int ret = 0; + tls_os_status_t os_status; + /* xTimer is already active - delete it. */ + ret = xTimerDelete((xTimerHandle)timer, 10); + if (pdPASS == ret) + os_status = TLS_OS_SUCCESS; + else + os_status = TLS_OS_ERROR; + return os_status; +} + + +/* +********************************************************************************************************* +* DELAY TASK 'n' TICKS +* +* Description: This function is called to delay execution of the currently running task until the +* specified number of system ticks expires. This, of course, directly equates to delaying +* the current task for some time to expire. No delay will result If the specified delay is +* 0. If the specified delay is greater than 0 then, a context switch will result. +* +* Arguments : ticks is the time delay that the task will be suspended in number of clock 'ticks'. +* Note that by specifying 0, the task will not be delayed. +* +* Returns : none +********************************************************************************************************* +*/ + void tls_os_time_delay(u32 ticks) +{ + vTaskDelay(ticks); +} + +/* +********************************************************************************************************* +* task stat info +* +* Description: This function is used to display stat info +* Arguments : +* +* Returns : none +********************************************************************************************************* +*/ +void tls_os_disp_task_stat_info(void) +{ + char *buf = NULL; + + buf = tls_mem_alloc(1024); + if(NULL == buf) + return; +#if configUSE_TRACE_FACILITY + vTaskList((signed char *)buf); +#endif + printf("\n%s",buf); + tls_mem_free(buf); + buf = NULL; +} +/* +********************************************************************************************************* +* OS INIT function +* +* Description: This function is used to init os common resource +* +* Arguments : None; +* +* Returns : None +********************************************************************************************************* +*/ + +void tls_os_init(void *arg) +{ +} +/* +********************************************************************************************************* +* OS scheduler start function +* +* Description: This function is used to start task schedule +* +* Arguments : None; +* +* Returns : None +********************************************************************************************************* +*/ + +void tls_os_start_scheduler(void) +{ + vTaskStartScheduler(); +} +/* +********************************************************************************************************* +* Get OS TYPE +* +* Description: This function is used to get OS type +* +* Arguments : None; +* +* Returns : TLS_OS_TYPE +* OS_UCOSII = 0, +* OS_FREERTOS = 1, +********************************************************************************************************* +*/ + +int tls_os_get_type(void) +{ + return (int)OS_FREERTOS; +} +/* +********************************************************************************************************* +* OS tick handler +* +* Description: This function is tick handler. +* +* Arguments : None; +* +* Returns : None +********************************************************************************************************* +*/ + +void tls_os_time_tick(void *p){ + +} + +/** + * @brief get isr count + * + * @param[in] None + * + * @retval count + * + * @note None + */ + +extern int portGET_IPSR(void); +u8 tls_get_isr_count(void) +{ +// return intr_counter; + return (portGET_IPSR() > 13); +} + +#endif +#endif /* end of WM_OSAL_RTOS_H */ diff --git a/sdk/Src/OS/RTOS/wm_rtos.c b/sdk/Src/OS/RTOS/wm_rtos.c new file mode 100644 index 0000000..c7955d1 --- /dev/null +++ b/sdk/Src/OS/RTOS/wm_rtos.c @@ -0,0 +1,32 @@ +/***************************************************************************** +* +* File Name : wm_rtos.c +* +* Description: rtos compile file sets +* +* Copyright (c) 2014 Winner Microelectronics Co., Ltd. +* All rights reserved. +* +* Author : +* +* Date : 2018-01-15 +*****************************************************************************/ + +#ifndef WM_RTOS_H +#define WM_RTOS_H + +#include "../../rtos/wm_osal_rtos.c" +#include "../../rtos/source/tasks.c" +#include "../../rtos/source/rtostimers.c" +#include "../../rtos/source/queue.c" +#include "../../rtos/source/list.c" +#include "../../rtos/source/heap_4.c" +#include "../../rtos/source/heap_3.c" +#include "../../rtos/source/heap_2.c" +#include "../../rtos/source/croutine.c" +#ifdef __ICCARM__ +#include "../../rtos/ports/port_m3_iar.c" +#else +#include "../../rtos/ports/port_m3.c" +#endif +#endif /* end of WM_RTOS_H */ diff --git a/sdk/ld/link_w600_2m.ld b/sdk/ld/link_w600_2m.ld new file mode 100644 index 0000000..4647b7a --- /dev/null +++ b/sdk/ld/link_w600_2m.ld @@ -0,0 +1,154 @@ +/* Linker script to configure memory regions. + * Need modifying for a specific board. + * FLASH.ORIGIN: starting address of flash + * FLASH.LENGTH: length of flash + * RAM.ORIGIN: starting address of RAM bank 0 + * RAM.LENGTH: length of RAM bank 0 + */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x8010100, LENGTH = 960K /* 960KB-256B */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x38000 /* 224KB */ +} + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.isr_vector)) + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .stack_dummy (COPY): + { + . = ALIGN(8); + __stack_start__ = .; + *(.stack*) + __stack_end__ = .; + } > RAM + + .bss __stack_end__ : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + PROVIDE(end = .); + *(.heap*) + __HeapLimit = .; + } > RAM + + __StackTop = __stack_end__; + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + ASSERT(__StackTop <= 0x20028000, "stack address error") +} diff --git a/sdk/tools/wm_tool.c b/sdk/tools/wm_tool.c new file mode 100644 index 0000000..acf191d --- /dev/null +++ b/sdk/tools/wm_tool.c @@ -0,0 +1,4956 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __MINGW32__ +#include +#else +#include +#include +#include +#endif + +#define WM_TOOL_PATH_MAX 256 +#define WM_TOOL_ONCE_READ_LEN 1024 + +#define WM_TOOL_RUN_IMG_HEADER_LEN 0x100 + +#define WM_TOOL_SECBOOT_IMG_ADDR (0x2100) +#define WM_TOOL_SECBOOT_HEADER_LEN (0x100) +#define WM_TOOL_SECBOOT_HEADER_POS (WM_TOOL_SECBOOT_IMG_ADDR - WM_TOOL_SECBOOT_HEADER_LEN) +#define WM_TOOL_SECBOOT_IMG_AREA_TOTAL_LEN (56 * 1024) + +#define WM_TOOL_IMG_HEAD_MAGIC_NO (0xA0FFFF9F) + +#define WM_TOOL_DEFAULT_BAUD_RATE 115200 + +#define WM_TOOL_DOWNLOAD_TIMEOUT_SEC (60 * 1) + +#define WM_TOOL_USE_1K_XMODEM 1 /* 1 for use 1k_xmodem 0 for xmodem */ + + +/* Xmodem Frame form: <255-blk #><--128 data bytes--> */ +#define XMODEM_SOH 0x01 +#define XMODEM_STX 0x02 +#define XMODEM_EOT 0x04 +#define XMODEM_ACK 0x06 +#define XMODEM_NAK 0x15 +#define XMODEM_CAN 0x18 +#define XMODEM_CRC_CHR 'C' +#define XMODEM_CRC_SIZE 2 /* Crc_High Byte + Crc_Low Byte */ +#define XMODEM_FRAME_ID_SIZE 2 /* Frame_Id + 255-Frame_Id */ +#define XMODEM_DATA_SIZE_SOH 128 /* for Xmodem protocol */ +#define XMODEM_DATA_SIZE_STX 1024 /* for 1K xmodem protocol */ + +#if (WM_TOOL_USE_1K_XMODEM) +#define XMODEM_DATA_SIZE XMODEM_DATA_SIZE_STX +#define XMODEM_HEAD XMODEM_STX +#else +#define XMODEM_DATA_SIZE XMODEM_DATA_SIZE_SOH +#define XMODEM_HEAD XMODEM_SOH +#endif + +#ifdef WM_DEBUG +#define WM_TOOL_DBG_PRINT wm_tool_printf +#else +#define WM_TOOL_DBG_PRINT(...) +#endif + +typedef enum { + WM_TOOL_DL_ACTION_NONE = 0, + WM_TOOL_DL_ACTION_AT, + WM_TOOL_DL_ACTION_RTS +} wm_tool_dl_action_e; + +typedef enum { + WM_TOOL_DL_ERASE_NONE = 0, + WM_TOOL_DL_ERASE_SECBOOT, + WM_TOOL_DL_ERASE_ALL +} wm_tool_dl_erase_e; + +typedef enum { + WM_TOOL_DL_TYPE_IMAGE = 0, + WM_TOOL_DL_TYPE_FLS +} wm_tool_dl_type_e; + +typedef enum { + WM_TOOL_LAYOUT_TYPE_1M = 0, + WM_TOOL_LAYOUT_TYPE_2M = 3 +} wm_tool_layout_type_e; + +typedef enum { + WM_TOOL_ZIP_TYPE_UNCOMPRESS = 0, + WM_TOOL_ZIP_TYPE_COMPRESS +} wm_tool_zip_type_e; + +typedef enum { + WM_TOOL_CRC32_REFLECT_OUTPUT = 1, + WM_TOOL_CRC32_REFLECT_INPUT = 2 +} wm_tool_crc32_reflect_e; + +typedef enum { + WM_TOOL_SHOW_LOG_NONE = 0, + WM_TOOL_SHOW_LOG_STR, + WM_TOOL_SHOW_LOG_HEX +} wm_tool_show_log_e; + +typedef struct { + unsigned int magic_no; + unsigned short img_type; + unsigned short zip_type; + unsigned int run_img_addr; + unsigned int run_img_len; + unsigned int run_org_checksum; + unsigned int upd_img_addr; + unsigned int upd_img_len; + unsigned int upd_checksum; + unsigned int upd_no; + unsigned char ver[16]; + unsigned int hd_checksum; +} wm_tool_firmware_booter_t; + +const static char *wm_tool_version = "1.0.4"; + +static int wm_tool_show_usage = 0; +static int wm_tool_list_com = 0; +static int wm_tool_show_ver = 0; + +static char wm_tool_serial_path[WM_TOOL_PATH_MAX] = "/dev/ttyS0"; +static unsigned int wm_tool_download_serial_rate = WM_TOOL_DEFAULT_BAUD_RATE; +static unsigned int wm_tool_normal_serial_rate = WM_TOOL_DEFAULT_BAUD_RATE; + +static wm_tool_dl_action_e wm_tool_dl_action = WM_TOOL_DL_ACTION_NONE; +static wm_tool_dl_erase_e wm_tool_dl_erase = WM_TOOL_DL_ERASE_NONE; +static wm_tool_dl_type_e wm_tool_dl_type = WM_TOOL_DL_TYPE_IMAGE; +static char *wm_tool_download_image = NULL; + +static char *wm_tool_input_binary = NULL; +static char *wm_tool_output_image = NULL; +static char *wm_tool_secboot_image = NULL; +static unsigned int wm_tool_src_binary_len = 0; +static unsigned int wm_tool_src_binary_crc = 0; +static int wm_tool_is_debug = 0; +static wm_tool_layout_type_e wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_1M; +static wm_tool_zip_type_e wm_tool_zip_type = WM_TOOL_ZIP_TYPE_COMPRESS; +static unsigned int wm_tool_upd_addr = 0x90000; +static unsigned int wm_tool_run_addr = 0x10100; + +static unsigned int wm_tool_file_crc = 0xFFFFFFFF; + +static wm_tool_show_log_e wm_tool_show_log_type = WM_TOOL_SHOW_LOG_NONE; + +#ifdef __MINGW32__ + +#ifndef CBR_2000000 +#define CBR_2000000 2000000 +#endif + +#ifndef CBR_1000000 +#define CBR_1000000 1000000 +#endif + +#ifndef CBR_921600 +#define CBR_921600 921600 +#endif + +#ifndef CBR_460800 +#define CBR_460800 460800 +#endif + +static DWORD wm_tool_uart_block = 0; + +static HANDLE wm_tool_uart_handle = NULL; + +const static int wm_tool_uart_speed_array[] = {CBR_2000000, CBR_1000000, CBR_921600, CBR_460800, CBR_115200, CBR_38400, + CBR_19200, CBR_9600, CBR_4800, CBR_2400, CBR_1200}; +#else /* __MINGW32__ */ + +#ifndef B2000000 +#define B2000000 2000000 +#endif + +#ifndef B1000000 +#define B1000000 1000000 +#endif + +#ifndef B921600 +#define B921600 921600 +#endif + +#ifndef B460800 +#define B460800 460800 +#endif + +static int wm_tool_uart_fd = -1; + +static struct termios wm_tool_saved_serial_cfg; + + +const static int wm_tool_uart_speed_array[] = {B2000000, B1000000, B921600, B460800, B115200, B38400, + B19200, B9600, B4800, B2400, B1200}; + +#endif /* __MINGW32__ */ + +const static int wm_tool_uart_name_array[] = {2000000, 1000000, 921600, 460800, 115200, 38400, + 19200, 9600, 4800, 2400, 1200}; + +const static unsigned char wm_tool_chip_cmd_b115200[] = {0x21, 0x0a, 0x00, 0x97, 0x4b, 0x31, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00}; +const static unsigned char wm_tool_chip_cmd_b460800[] = {0x21, 0x0a, 0x00, 0x07, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0x00}; +const static unsigned char wm_tool_chip_cmd_b921600[] = {0x21, 0x0a, 0x00, 0x5d, 0x50, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x00}; +const static unsigned char wm_tool_chip_cmd_b1000000[] = {0x21, 0x0a, 0x00, 0x5e, 0x3d, 0x31, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00}; +const static unsigned char wm_tool_chip_cmd_b2000000[] = {0x21, 0x0a, 0x00, 0xef, 0x2a, 0x31, 0x00, 0x00, 0x00, 0x80, 0x84, 0x1e, 0x00}; + +const static unsigned char wm_tool_chip_cmd_get_mac[] = {0x21, 0x06, 0x00, 0xea, 0x2d, 0x38, 0x00, 0x00, 0x00}; + +static const unsigned int wm_tool_crc32_tab[] = {0x00000000L, 0x77073096L, 0xee0e612cL, + 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, + 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, + 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, + 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, + 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, + 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, + 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, + 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, + 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, + 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, + 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, + 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, + 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, + 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, + 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, + 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, + 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, + 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, + 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, + 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, + 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, + 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, + 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, + 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, + 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, + 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, + 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, + 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, + 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, + 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, + 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, + 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, + 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, + 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, + 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, + 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, + 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, + 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, + 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, + 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, + 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, + 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, + 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, + 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL}; + +static void wm_tool_stdin_to_uart(void); + +/* ============================ zlib gzip ================================== */ + +#define ALLOC(size) zcalloc((voidp)0, 1, size) +#define TRYFREE(p) {if (p) zcfree((voidp)0, p);} + +#define BINARY 0 +#define ASCII 1 +#define UNKNOWN 2 + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 + +#define REP_3_6 16 +#define REPZ_3_10 17 +#define REPZ_11_138 18 + +#define MAX_BL_BITS 7 + +#define BASE 65521 /* largest prime smaller than 65536 */ +#define NMAX 5552 + +#define DO1(buf) {s1 += *buf++; s2 += s1;} +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); +#define DO16(buf) DO8(buf); DO8(buf); + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +#define DEFLATED 8 + +#ifndef WBITS +# define WBITS 15 /* 32K window */ +#endif + +#ifndef MEM_LEVEL +# define MEM_LEVEL 8 +#endif + +#define Z_BUFSIZE 4096 + +#define GZ_MAGIC_1 0x1f +#define GZ_MAGIC_2 0x8b + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif + +#define Z_NO_FLUSH 0 +#define Z_FINISH 4 + +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +#define LENGTH_CODES 29/* number of length codes, not counting the special END_BLOCK code */ +#define LITERALS 256/* number of literal bytes 0..255 */ +#define L_CODES (LITERALS+1+LENGTH_CODES)/* number of Literal or Length codes, including the END_BLOCK code */ +#define D_CODES 30/* number of distance codes */ +#define BL_CODES 19/* number of codes used to transfer the bit lengths */ +#define HEAP_SIZE (2*L_CODES+1)/* maximum heap size */ + +#define END_BLOCK 256 + +#define MAX_BITS 15 + +#define MAX_MEM_LEVEL 9 + +#define MIN_MATCH 3 +#define MAX_MATCH 258 + +#define Z_UNKNOWN 2 + +#define INIT_STATE 42 +#define BUSY_STATE 113 +#define FINISH_STATE 666 + +#define NIL 0 + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +#define SMALLEST 1 + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) + +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + +#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err) + +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + MIN_MATCH-1]), \ + s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (str)) + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) + +#define check_match(s, start, match, length) + +#define d_code(dist) \ + ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) + +#define FLUSH_BLOCK_ONLY(s, eof) { \ + ct_flush_block(s, (s->block_start >= 0L ? \ + (char*)&s->window[(unsigned)s->block_start] : \ + (char*)Z_NULL), (long)s->strstart - s->block_start, (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ +} + +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return 1; \ +} + +#define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + +#define MAX(a,b) (a >= b ? a : b) + +#define Buf_size (8 * 2*sizeof(char)) + +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +#ifndef local +# define local static +#endif + +#ifndef __P +#define __P(args) args +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidp)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#define Assert(cond,msg) + +#ifndef Byte + typedef unsigned char Byte; /* 8 bits */ +#endif +#ifndef uInt + typedef unsigned int uInt; /* may be 16 or 32 bits */ +#endif +#ifndef uLong + typedef unsigned long uLong; /* 32 bits or more */ +#endif +typedef Byte *voidp; +typedef voidp gzFile; +typedef unsigned char uch; +typedef unsigned long ulg; +typedef unsigned short ush; +typedef ush Pos; +typedef unsigned IPos; + +typedef voidp (*alloc_func) __P((voidp opaque, uInt items, uInt size)); +typedef void (*free_func) __P((voidp opaque, voidp address)); + +typedef uLong (*check_func) __P((uLong check, Byte *buf, uInt len)); + +typedef struct z_stream_s { + Byte *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Byte *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidp opaque; /* private data object passed to zalloc and zfree */ + + Byte data_type; /* best guess about the data type: ascii or binary */ + +} z_stream; + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ +} gz_stream; + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} ct_data; + +struct static_tree_desc_s { + ct_data *static_tree; /* static tree or NULL */ + int *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} tree_desc; + +typedef struct inflate_huft_s inflate_huft; +struct inflate_huft_s { + union { + struct { + char Exop; /* number of extra bits or operation */ + char Bits; /* number of bits in this code or subcode */ + } what; + Byte *pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit machines) */ + union { + uInt Base; /* literal, length base, or distance base */ + inflate_huft *Next; /* pointer to next level of table */ + } more; +}; + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BAD} /* x: got error */ + mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONE, /* finished last block, done */ + INF_ERROR}/* got a data error--stuck here */ + mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uInt *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + } trees; /* if DTREE, decoding info for trees */ + struct inflate_codes_state + *codes; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + Byte *window; /* sliding window */ + Byte *end; /* one byte after sliding window */ + Byte *read; /* window read pointer */ + Byte *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + +typedef struct internal_state { + z_stream *strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Byte *pending_buf; /* output still pending */ + Byte *pending_out; /* next pending byte to output to the stream */ + int pending; /* nb of bytes in the pending buffer */ + uLong adler; /* adler32 of uncompressed data */ + int noheader; /* suppress zlib header and adler32 */ + Byte data_type; /* UNKNOWN, BINARY or ASCII */ + Byte method; /* STORED (for zip only) or DEFLATED */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Byte *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Pos *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Pos *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + + ct_data dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + ct_data dyn_dtree[2*D_CODES+1]; /* distance tree */ + ct_data bl_tree[2*BL_CODES+1]; /* Huffman tree for the bit lengths */ + + tree_desc l_desc; /* descriptor for literal tree */ + tree_desc d_desc; /* descriptor for distance tree */ + tree_desc bl_desc; /* descriptor for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uch *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ush *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + ulg compressed_len; /* total bit length of compressed file */ + uInt matches; /* number of string matches in current block */ + +#ifdef DEBUG + ulg bits_sent; /* bit length of the compressed data */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + /* mode */ + enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + //START, /* make new blocks state */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + //DONE, /* finished check, done */ + //INF_ERROR/* got an error--stay here */ + } + mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct inflate_blocks_state + *blocks; /* if BLOCKS, current state */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ +} deflate_state; + +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; +} config; + +char *z_errmsg[] = { +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +""}; + +local int base_length[LENGTH_CODES]; +local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; +local uch length_code[MAX_MATCH-MIN_MATCH+1]; +local int base_dist[D_CODES]; +local int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; +local ct_data static_ltree[L_CODES+2]; +local uch dist_code[512]; +local int base_dist[D_CODES]; +local ct_data static_dtree[D_CODES]; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local static_tree_desc static_bl_desc = +{(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +local uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + +local config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0}, /* store only */ +/* 1 */ {4, 4, 8, 4}, /* maximum speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8}, +/* 3 */ {4, 6, 32, 32}, + +/* 4 */ {4, 4, 16, 16}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32}, +/* 6 */ {8, 16, 128, 128}, +/* 7 */ {8, 32, 128, 256}, +/* 8 */ {32, 128, 258, 1024}, +/* 9 */ {32, 258, 258, 4096}}; /* maximum compression */ + +#define FIXEDH 530 /* number of hufts used by fixed tables */ +local inflate_huft fixed_mem[FIXEDH]; + +#define next more.Next + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +local voidp zcalloc (opaque, items, size) + voidp opaque; + unsigned items; + unsigned size; +{ + return calloc(items, size); +} + +local void zcfree (opaque, ptr) + voidp opaque; + voidp ptr; +{ + free(ptr); +} + +local uLong crc32(crc, buf, len) + uLong crc; + Byte *buf; + uInt len; +{ + if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + if (len) do { + crc = wm_tool_crc32_tab[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); + } while (--len); + return crc ^ 0xffffffffL; +} + +local void zmemcpy(dest, source, len) + Byte* dest; + Byte* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +local void zmemzero(dest, len) + Byte* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} + +local uLong adler32(adler, buf, len) + uLong adler; + Byte *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + k -= 16; + } + if (k != 0) do { + DO1(buf); + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + +local int read_buf(strm, buf, size) + z_stream *strm; + char *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (!strm->state->noheader) { + strm->state->adler = adler32(strm->state->adler, strm->next_in, len); + } + zmemcpy((Byte *)buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +local int inflate_trees_free(t, z) +inflate_huft *t; /* table to free */ +z_stream *z; /* for zfree function */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register inflate_huft *p, *q; + + /* Don't free fixed trees */ + if (t >= fixed_mem && t <= fixed_mem + FIXEDH) + return Z_OK; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != Z_NULL) + { + q = (--p)->next; + ZFREE(z,p); + p = q; + } + return Z_OK; +} + +local void inflate_codes_free(c, z) +struct inflate_codes_state *c; +z_stream *z; +{ + inflate_trees_free(c->dtree, z); + inflate_trees_free(c->ltree, z); + ZFREE(z, c); +} + +local int inflate_blocks_free(s, z, c) +struct inflate_blocks_state *s; +z_stream *z; +uLong *c; +{ + if (s->checkfn != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == CODES) + inflate_codes_free(s->sub.codes, z); + ZFREE(z, s->window); + ZFREE(z, s); + return Z_OK; +} + +local int deflateEnd (strm) + z_stream *strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + TRY_FREE(strm, strm->state->window); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->pending_buf); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return Z_OK; +} + +local int inflateEnd(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode == BLOCKS) + inflate_blocks_free(z->state->sub.blocks, z, &c); + ZFREE(z, z->state); + z->state = Z_NULL; + return Z_OK; +} + +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE((voidp)s->path); + TRYFREE((voidp)s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { + err = deflateEnd(&(s->stream)); + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + zcfree((voidp)0, (voidp)s); + return err; +} + +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +local uLong getLong (buf) + Byte *buf; +{ + uLong x = 0; + Byte *p = buf+4; + + do { + x <<= 8; + x |= *--p; + } while (p != buf); + return x; +} + +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ush bl_count[]; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1< length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "ct_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse(n, 5); + } +} + +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +local void ct_init(s) + deflate_state *s; +{ + if (static_dtree[0].Len == 0) { + ct_static_init(); /* To do: at compile time */ + } + + s->compressed_len = 0L; + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +local void lm_init (s) + deflate_state *s; +{ + register unsigned j; + + s->window_size = (ulg)2L*s->w_size; + + + /* Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ + s->head[s->hash_size-1] = NIL; + zmemzero((unsigned char*)s->head, (unsigned int)(s->hash_size-1)*sizeof(*s->head)); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = MIN_MATCH-1; + s->match_available = 0; +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif + + s->ins_h = 0; + for (j=0; jins_h, s->window[j]); + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ +} + +local int deflateReset (strm) + z_stream *strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR; + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + s->status = s->noheader ? BUSY_STATE : INIT_STATE; + s->adler = 1; + + ct_init(s); + lm_init(s); + + return Z_OK; +} + +local int deflateInit2 (strm, level, method, windowBits, memLevel, strategy) + z_stream *strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; +{ + deflate_state *s; + int noheader = 0; + + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == Z_NULL) strm->zalloc = zcalloc; + if (strm->zfree == Z_NULL) strm->zfree = zcfree; + + if (level == Z_DEFAULT_COMPRESSION) level = 6; + + if (windowBits < 0) { /* undocumented feature: suppress zlib header */ + noheader = 1; + windowBits = -windowBits; + } + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != DEFLATED || + windowBits < 8 || windowBits > 15 || level < 1 || level > 9) { + return Z_STREAM_ERROR; + } + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state *)s; + s->strm = strm; + + s->noheader = noheader; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Byte*) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Pos*) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Pos*) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + s->pending_buf = (uch*) ZALLOC(strm, s->lit_bufsize, 2*sizeof(ush)); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + strm->msg = z_errmsg[1-Z_MEM_ERROR]; + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = (ush*) &(s->pending_buf[s->lit_bufsize]); + s->l_buf = (uch*) &(s->pending_buf[3*s->lit_bufsize]); + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 32 bits (worst case + * is 15+15+13=33). + */ + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +local int inflateInit2(z, w) +z_stream *z; +int w; +{ + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; + if (z->zalloc == Z_NULL) z->zalloc = zcalloc; + if (z->zfree == Z_NULL) z->zfree = zcfree; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->mode = METHOD; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + z->state->mode = START; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = w; + return Z_OK; +} + +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +local void flush_pending(strm) + z_stream *strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + unsigned more; /* Amount of free space at the end of the window. */ + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = s->w_size; + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + } else if (s->strstart >= s->w_size+MAX_DIST(s)) { + + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + memcpy((char*)s->window, (char*)s->window+s->w_size, + (unsigned)s->w_size); + s->match_start -= s->w_size; + s->strstart -= s->w_size; /* we now have strstart >= MAX_DIST */ + + s->block_start -= (long) s->w_size; + + for (n = 0; n < s->hash_size; n++) { + m = s->head[n]; + s->head[n] = (Pos)(m >= s->w_size ? m-s->w_size : NIL); + } + for (n = 0; n < s->w_size; n++) { + m = s->prev[n]; + s->prev[n] = (Pos)(m >= s->w_size ? m-s->w_size : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += s->w_size; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, (char*)s->window + s->strstart + s->lookahead, + more); + s->lookahead += n; + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local int longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Byte *scan = s->window + s->strstart; /* current string */ + register Byte *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Byte *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ush*)scan; + register ush scan_end = *(ush*)(scan+best_len-1); +#else + register Byte *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + Assert(s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ush*)(match+best_len-1) != scan_end || + *(ush*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + scan++, match++; + do { + } while (*(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= s->nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ush*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = s->prev[cur_match & s->w_mask]) > limit + && --chain_length != 0); + + return best_len; +} + +local int ct_tally (s, dist, lc) + deflate_state *s; + int dist; /* distance of matched string */ + int lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + + /* Try to guess if it is profitable to stop the current block here */ + if (s->level > 2 && (s->last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)s->strstart - s->block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +local void set_data_type(s) + deflate_state *s; +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; + s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII); +} + +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + ct_data *stree = desc->stat_desc->static_tree; + int *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +local void build_tree(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + int new; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + new = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[new].Freq = 1; + s->depth[new] = 0; + s->opt_len--; if (stree) s->static_len -= stree[new].Len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + + return max_blindex; +} + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ +#ifdef DEBUG + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; +#endif + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} + +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +local void copy_block(s, buf, len, header) + deflate_state *s; + char *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} + +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); +} + +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ +} + +ulg ct_flush_block(s, buf, stored_len, eof) + deflate_state *s; + char *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Check if the file is ascii or binary */ + if (s->data_type == UNKNOWN) set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + + + build_tree(s, (tree_desc *)(&(s->d_desc))); + + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + /* If compression failed and this is the first and last block, + * and if the .zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ + + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ + s->compressed_len = (s->compressed_len + 3 + 7) & ~7L; + s->compressed_len += (stored_len + 4) << 3; + + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ + + } else if (static_lenb == opt_lenb) { + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); + s->compressed_len += 3 + s->static_len; + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); + s->compressed_len += 3 + s->opt_len; + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + init_block(s); + + if (eof) { + bi_windup(s); + s->compressed_len += 7; /* align on byte boundary */ + } + + return s->compressed_len >> 3; +} + +local int deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + s->prev_length = MIN_MATCH-1; + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1; + + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(s, s->strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + + if (s->match_length > s->lookahead) s->match_length = s->lookahead; + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + bflush = ct_tally(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (s->match_length <= s->max_insert_length) { + s->match_length--; /* string at strstart already in hash table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since + * the next lookahead bytes will be emitted as literals. + */ + } while (--s->match_length != 0); + s->strstart++; + } else { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + } else { + /* No match, output a literal byte */ + bflush = ct_tally (s, 0, s->window[s->strstart]); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return 0; /* normal exit */ +} + +local int deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1; + + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(s, s->strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + if (s->match_length > s->lookahead) s->match_length = s->lookahead; + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED || + (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR))) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + bflush = ct_tally(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (ct_tally (s, 0, s->window[s->strstart-1])) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return 1; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + if (s->match_available) ct_tally (s, 0, s->window[s->strstart-1]); + + FLUSH_BLOCK(s, flush == Z_FINISH); + return 0; +} + +local int deflate (strm, flush) + z_stream *strm; + int flush; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + if (strm->next_out == Z_NULL || strm->next_in == Z_NULL) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + strm->state->strm = strm; /* just in case */ + + /* Write the zlib header */ + if (strm->state->status == INIT_STATE) { + + uInt header = (DEFLATED + ((strm->state->w_bits-8)<<4)) << 8; + uInt level_flags = (strm->state->level-1) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + header += 31 - (header % 31); + + strm->state->status = BUSY_STATE; + putShortMSB(strm->state, header); + } + + /* Flush as much pending output as possible */ + if (strm->state->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) return Z_OK; + } + + /* User must not provide more input after the first FINISH: */ + if (strm->state->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || + (flush == Z_FINISH && strm->state->status != FINISH_STATE)) { + + if (flush == Z_FINISH) { + strm->state->status = FINISH_STATE; + } + if (strm->state->level <= 3) { + if (deflate_fast(strm->state, flush)) return Z_OK; + } else { + if (deflate_slow(strm->state, flush)) return Z_OK; + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (strm->state->noheader) return Z_STREAM_END; + + /* Write the zlib trailer (adler32) */ + putShortMSB(strm->state, (uInt)(strm->state->adler >> 16)); + putShortMSB(strm->state, (uInt)(strm->state->adler & 0xffff)); + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + strm->state->noheader = 1; /* write the trailer only once! */ + return strm->state->pending != 0 ? Z_OK : Z_STREAM_END; +} + +local int gzflush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if (fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->z_err = deflate(&(s->stream), flush); + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +local gzFile gz_open (path, mode, fd) + char *path; + char *mode; + int fd; +{ + int err; + char *p = mode; + gz_stream *s = (gz_stream *)ALLOC(sizeof(gz_stream)); + + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w') s->mode = 'w'; + } while (*p++); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { + err = deflateInit2(&(s->stream), Z_BEST_COMPRESSION, + DEFLATED, -WBITS, MEM_LEVEL, 0); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = ALLOC(Z_BUFSIZE); + + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + err = inflateInit2(&(s->stream), -WBITS); + s->stream.next_in = s->inbuf = ALLOC(Z_BUFSIZE); + + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? fopen(path, mode) : fdopen(fd, mode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", GZ_MAGIC_1, GZ_MAGIC_2, + DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + } else { + /* Check and skip the header: + */ + Byte c1 = 0, c2 = 0; + Byte method = 0; + Byte flags = 0; + Byte xflags = 0; + Byte time[4]; + Byte osCode; + int c; + + s->stream.avail_in = fread(s->inbuf, 1, 2, s->file); + if (s->stream.avail_in != 2 || s->inbuf[0] != GZ_MAGIC_1 + || s->inbuf[1] != GZ_MAGIC_2) { + s->transparent = 1; + return (gzFile)s; + } + s->stream.avail_in = 0; + err = fscanf(s->file,"%c%c%4c%c%c", &method, &flags, time, &xflags, &osCode); + + if (method != DEFLATED || feof(s->file) || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return (gzFile)s; + } + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + long len; + err = fscanf(s->file, "%c%c", &c1, &c2); + len = c1 + ((long)c2<<8); + fseek(s->file, len, SEEK_CUR); + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = getc(s->file)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = getc(s->file)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + err = fscanf(s->file, "%c%c", &c1, &c2); + } + if (feof(s->file)) { + s->z_err = Z_DATA_ERROR; + } + } + return (gzFile)s; +} + +local gzFile gzopen (path, mode) + char *path; + char *mode; +{ + return gz_open (path, mode, -1); +} + +local int gzwrite (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, buf, len); + + return len - s->stream.avail_in; +} + +local int gzclose (file) + gzFile file; +{ + uInt n; + int err; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { + err = gzflush (file, Z_FINISH); + if (err != Z_OK) return destroy((gz_stream *)file); + + putLong (s->file, s->crc); + putLong (s->file, s->stream.total_in); + + } else if (s->mode == 'r' && s->z_err == Z_STREAM_END) { + + /* slide CRC and original size if they are at the end of inbuf */ + if ((n = s->stream.avail_in) < 8 && !s->z_eof) { + Byte *p = s->inbuf; + Byte *q = s->stream.next_in; + while (n--) { *p++ = *q++; }; + + n = s->stream.avail_in; + n += fread(p, 1, 8, s->file); + s->stream.next_in = s->inbuf; + } + /* check CRC and original size */ + if (n < 8 || + getLong(s->stream.next_in) != s->crc || + getLong(s->stream.next_in + 4) != s->stream.total_out) { + + s->z_err = Z_DATA_ERROR; + } + } + return destroy((gz_stream *)file); +} + +/* ========================================================================= */ + +static int wm_tool_printf(const char *format, ...) +{ + int ret; + va_list ap; + + va_start(ap, format); + ret = vprintf(format, ap); + va_end(ap); + + fflush(stdout); + + return ret; +} + +static unsigned long long wm_tool_crc32_reflect(unsigned long long ref, unsigned char ch) +{ + int i; + unsigned long long value = 0; + + for ( i = 1; i < ( ch + 1 ); i++ ) + { + if( ref & 1 ) + value |= 1 << ( ch - i ); + ref >>= 1; + } + + return value; +} + +static unsigned int wm_tool_crc32(unsigned int crc, unsigned char *buffer, int size, wm_tool_crc32_reflect_e mode) +{ + int i; + unsigned char temp; + + for (i = 0; i < size; i++) + { + if (mode & WM_TOOL_CRC32_REFLECT_INPUT) + { + temp = wm_tool_crc32_reflect(buffer[i], 8); + } + else + { + temp = buffer[i]; + } + crc = wm_tool_crc32_tab[(crc ^ temp) & 0xff] ^ (crc >> 8); + } + + return crc ; +} + +static unsigned int wm_tool_get_crc32(unsigned char *buffer, int size, wm_tool_crc32_reflect_e mode) +{ + wm_tool_file_crc = wm_tool_crc32(wm_tool_file_crc, buffer, size, mode); + if (mode & WM_TOOL_CRC32_REFLECT_OUTPUT) + { + wm_tool_file_crc = wm_tool_crc32_reflect(wm_tool_file_crc, 32); + } + return wm_tool_file_crc; +} + +static unsigned short wm_tool_get_crc16(unsigned char *ptr, unsigned short count) +{ + unsigned short crc, i; + + crc = 0; + + while (count--) + { + crc = crc ^ (int) *ptr++ << 8; + + for (i = 0; i < 8; i++) + { + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + } + + return (crc & 0xFFFF); +} + +static int wm_tool_char_to_hex(char ch) +{ + int hex; + + hex = -1; + + if ((ch >= '0') && (ch <= '9')) + { + hex = ch - '0'; + } + else if ((ch >= 'a') && (ch <= 'f')) + { + hex = ch - 'a' + 0xa; + } + else if ((ch >= 'A') && (ch <= 'F')) + { + hex = ch - 'A' + 0xa; + } + + return hex; +} + +static int wm_tool_str_to_hex_array(char *str, int cnt, unsigned char array[]) +{ + int hex; + unsigned char tmp; + unsigned char *des; + + des = array; + + while (cnt-- > 0) + { + hex = wm_tool_char_to_hex(*str++); + + if (hex < 0) + { + return -1; + } + else + { + tmp = (hex << 4) & 0xf0; + } + + hex = wm_tool_char_to_hex(*str++); + + if (hex < 0) + { + return -1; + } + else + { + tmp = tmp | (hex & 0x0f); + } + + *des++ = (unsigned char) tmp; + } + + return ((*str == 0) ? 0 : -1); +} + +static char *wm_tool_strcasestr(const char *str1, const char *str2) +{ + char *cp = (char *) str1; + char *s1, *s2; + + if (!*str2) return (char *) str1; + + while (*cp) { + s1 = cp; + s2 = (char *) str2; + + while (*s1 && *s2 && !(tolower((int)*s1) - tolower((int)*s2))) s1++, s2++; + if (!*s2) return cp; + cp++; + } + + return NULL; +} + +static int wm_tool_get_file_size(const char* filename) +{ + FILE *fp = fopen(filename, "r"); + if(!fp) return -1; + fseek(fp,0L,SEEK_END); + int size = ftell(fp); + fclose(fp); + return size; +} + +static char *wm_tool_get_name(const char *name) +{ + static char sz_name[WM_TOOL_PATH_MAX] = {0}; + char *p = (char *)name; + char *q = (char *)name; + + do + { +#ifdef __MINGW32__ + p = strchr(p, '\\'); +#else + p = strchr(p, '/'); +#endif + if (p) + { + p++; + q = p; + } + } while (p); + + strncpy(sz_name, q, WM_TOOL_PATH_MAX - 1); + +#ifdef __MINGW32__ + p = wm_tool_strcasestr(sz_name, ".exe"); + if (p) + *p = '\0'; +#endif + + return sz_name; +} + +static void wm_tool_print_usage(const char *name) +{ + wm_tool_printf("Usage: %s [-h] [-v] [-b] [-o] [-sb] [-ct] [-it] [-ua] [-ra] [-df]" + " [-l] [-c] [-ws] [-ds] [-rs] [-eo] [-dl] [-sl]" + "\r\n" + "\r\n" + "WinnerMicro firmware packaging " + "and programming " + "tool\r\n\r\n" + "options:\r\n\r\n" + " -h , show usage\r\n" + " -v , show version\r\n" + "\r\n" + " -b binary , original binary file\r\n" + " -o output_name , output firmware file\r\n" + " the default is the same as the original binary file name\r\n" + " -sb second_boot , second boot file, used to generate fls file\r\n" + " -fc compress_type , whether the firmware is compressed, default is compressed\r\n" + " <0 | 1> or \r\n" + " -it image_type , firmware image layout type, default is 1M layout\r\n" + " <0 | 3> or <1M | 2M>\r\n" + " -ua update_address , upgrade storage location (hexadecimal)\r\n" + " the default is 90000\r\n" + " -ra run_address , runtime position (hexadecimal)\r\n" + " the default is 10100\r\n" + " -df , generate debug firmware for openocd\r\n" + "\r\n" + " -l , list the local serial port\r\n" + " -c serial_name , connect a serial port\r\n" +#if defined(__APPLE__) && defined(__MACH__) + " e.g: tty.usbserial0 tty.usbserial3 tty.usbserial7\r\n" +#elif defined(__MINGW32__) || defined(__CYGWIN__) + " e.g: COM0 COM3 COM7\r\n" +#elif defined(__linux__) + " e.g: ttyUSB0 ttyUSB3 ttyUSB7\r\n" +#endif + " -ws baud_rate , set the serial port speed during normal work, default is 115200\r\n" + " <1200 - 2000000> or <1M | 2M>\r\n" + " -ds baud_rate , set the serial port speed when downloading, default is 115200\r\n" + " <115200 | 460800 | 921600 | 1000000 | 2000000> or <1M | 2M>\r\n" + " -rs reset_action , set device reset method, default is manual control\r\n" + " \r\n" + " none - manual control device reset\r\n" + " at - use the at command to control the device reset\r\n" + " rts - use the serial port rts pin to control the device reset\r\n" + " -eo erase_option , firmware area erase option\r\n" + " \r\n" + " secboot - erase the second boot area\r\n" + " all - erase all areas\r\n" + " -dl download_firmware, firmware file to be downloaded, default download compressed image\r\n" + " -sl display_format , display the log information output from the serial port\r\n" + " <0 | 1> or \r\n" + " str - string mode display\r\n" + " hex - hexadecimal format\r\n" + , wm_tool_get_name(name)); + + return; +} + +static void wm_tool_print_version(const char *name) +{ + wm_tool_printf("%s %s\r\nCopyright (C) 2013 - 2019 WinnerMicro, Inc.\r\n", wm_tool_get_name(name), wm_tool_version); + + return; +} + +static int wm_tool_parse_arv(int argc, char *argv[]) +{ + int opt; + int option_index = 0; + char *opt_string = "hvlc:b:o:"; + int cnt = 0; + + opterr = 1;/* show err info */ + + struct option long_options[] = { + {"dl", required_argument, NULL, 'd'}, + {"ws", required_argument, NULL, 'w'}, + {"ds", required_argument, NULL, 's'}, + {"sb", required_argument, NULL, 'S'}, + {"it", required_argument, NULL, 'i'}, + {"fc", required_argument, NULL, 'C'}, + {"ua", required_argument, NULL, 'u'}, + {"ra", required_argument, NULL, 'r'}, + {"df", no_argument, NULL, 'D'}, + {"rs", required_argument, NULL, 'a'}, + {"eo", required_argument, NULL, 'e'}, + {"sl", required_argument, NULL, 'g'}, + {0, 0, NULL, 0} + }; + + while ( (opt = getopt_long_only(argc, argv, opt_string, long_options, &option_index)) != -1) + { + WM_TOOL_DBG_PRINT("%c-%s\r\n", opt, optarg); + + switch (opt) + { + case '?': + case 'h': + { + wm_tool_show_usage = 1; + break; + } + case 'v': + { + wm_tool_show_ver = 1; + break; + } + case 'l': + { + wm_tool_list_com = 1; + break; + } + case 'c': + { +#if defined(__MINGW32__) + strcpy(wm_tool_serial_path, optarg); +#elif defined(__CYGWIN__) + sprintf(wm_tool_serial_path, "/dev/ttyS%d", atoi(optarg + strlen("COM")) - 1); +#else + sprintf(wm_tool_serial_path, "/dev/%s", optarg); +#endif + break; + } + case 'w': + { + if ('M' == optarg[1]) + wm_tool_normal_serial_rate = (optarg[0] - 0x30) * 1000000; + else + wm_tool_normal_serial_rate = strtol(optarg, NULL, 10); + break; + } + case 's': + { + if ('M' == optarg[1]) + wm_tool_download_serial_rate = (optarg[0] - 0x30) * 1000000; + else + wm_tool_download_serial_rate = strtol(optarg, NULL, 10); + break; + } + case 'a': + { + if (0 == strncmp(optarg, "none", strlen("none"))) + wm_tool_dl_action = WM_TOOL_DL_ACTION_NONE; + else if (0 == strncmp(optarg, "at", strlen("at"))) + wm_tool_dl_action = WM_TOOL_DL_ACTION_AT; + else if (0 == strncmp(optarg, "rts", strlen("rts"))) + wm_tool_dl_action = WM_TOOL_DL_ACTION_RTS; + else + wm_tool_show_usage = 1; + break; + } + case 'e': + { + if (0 == strncmp(optarg, "secboot", strlen("secboot"))) + wm_tool_dl_erase = WM_TOOL_DL_ERASE_SECBOOT; + else if (0 == strncmp(optarg, "all", strlen("all"))) + wm_tool_dl_erase = WM_TOOL_DL_ERASE_ALL; + else + wm_tool_show_usage = 1; + break; + } + case 'd': + { + wm_tool_download_image = strdup(optarg); + if (wm_tool_strcasestr(wm_tool_download_image, ".fls")) + wm_tool_dl_type = WM_TOOL_DL_TYPE_FLS; + break; + } + case 'o': + { + wm_tool_output_image = strdup(optarg); + break; + } + case 'b': + { + wm_tool_input_binary = strdup(optarg); + break; + } + case 'S': + { + wm_tool_secboot_image = strdup(optarg); + break; + } + case 'i': + { + if ('0' == optarg[0]) + wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_1M; + else if ('3' == optarg[0]) + wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_2M; + else if (0 == strncmp(optarg, "1M", strlen("1M"))) + wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_1M; + else if (0 == strncmp(optarg, "2M", strlen("2M"))) + wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_2M; + else + { + if (isdigit((int)optarg[0])) + wm_tool_image_type = optarg[0] - 0x30; + else + wm_tool_show_usage = 1; + } + break; + } + case 'C': + { + if ('0' == optarg[0]) + wm_tool_zip_type = WM_TOOL_ZIP_TYPE_UNCOMPRESS; + else if ('1' == optarg[0]) + wm_tool_zip_type = WM_TOOL_ZIP_TYPE_COMPRESS; + else if (0 == strncmp(optarg, "compress", strlen("compress"))) + wm_tool_zip_type = WM_TOOL_ZIP_TYPE_COMPRESS; + else if (0 == strncmp(optarg, "uncompress", strlen("uncompress"))) + wm_tool_zip_type = WM_TOOL_ZIP_TYPE_UNCOMPRESS; + else + wm_tool_show_usage = 1; + break; + } + case 'u': + { + wm_tool_upd_addr = strtol(optarg, NULL, 16); + break; + } + case 'r': + { + wm_tool_run_addr = strtol(optarg, NULL, 16); + break; + } + case 'D': + { + wm_tool_is_debug = 1; + break; + } + case 'g': + { + if ('0' == optarg[0]) + wm_tool_show_log_type = WM_TOOL_SHOW_LOG_STR; + else if ('1' == optarg[0]) + wm_tool_show_log_type = WM_TOOL_SHOW_LOG_HEX; + else if (0 == strncmp(optarg, "str", strlen("str"))) + wm_tool_show_log_type = WM_TOOL_SHOW_LOG_STR; + else if (0 == strncmp(optarg, "hex", strlen("hex"))) + wm_tool_show_log_type = WM_TOOL_SHOW_LOG_HEX; + else + wm_tool_show_usage = 1; + break; + break; + } + default: + { + wm_tool_show_usage = 1; + break; + } + } + + cnt++; + } + + return cnt; +} + +static int wm_tool_pack_image(const char *outfile) +{ + FILE *fpbin = NULL; + FILE *fpimg = NULL; + char *name = "2.0.0"; + int readlen = 0; + int filelen = 0; + int patch = 0; + wm_tool_firmware_booter_t fbooter; + unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1]; + + fpbin = fopen(wm_tool_input_binary, "rb"); + if (NULL == fpbin) + { + wm_tool_printf("can not open input file [%s].\r\n", wm_tool_input_binary); + return -2; + } + + fpimg = fopen(outfile, "wb+"); + if (NULL == fpimg) + { + wm_tool_printf("open img file error: [%s].\r\n", outfile); + fclose(fpbin); + return -3; + } + + /* --------deal with upgrade image's CRC begin---- */ + wm_tool_file_crc = 0xFFFFFFFF; + while (!feof(fpbin)) + { + memset(buf, 0, sizeof(buf)); + readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin); + if(readlen % 4 != 0) + { + patch = 4 - readlen%4; + readlen += patch; + } + filelen += readlen; + wm_tool_get_crc32((unsigned char*)buf, readlen, 0); + } + /* --------deal with upgrade image's CRC end---- */ + + wm_tool_src_binary_len = filelen; + wm_tool_src_binary_crc = wm_tool_file_crc; + + memset(&fbooter, 0, sizeof(wm_tool_firmware_booter_t)); + strcpy((char *)fbooter.ver, name); + fbooter.magic_no = WM_TOOL_IMG_HEAD_MAGIC_NO; + + fbooter.run_org_checksum = wm_tool_file_crc; + fbooter.img_type = wm_tool_image_type; + fbooter.run_img_len = filelen; + fbooter.run_img_addr = wm_tool_run_addr; + fbooter.zip_type = WM_TOOL_ZIP_TYPE_UNCOMPRESS; + fbooter.upd_img_len = filelen; + fbooter.upd_img_addr = wm_tool_upd_addr; + fbooter.upd_checksum = wm_tool_file_crc; + + /* calculate image's header's CRC */ + wm_tool_file_crc = 0xFFFFFFFF; + wm_tool_get_crc32((unsigned char *)&fbooter, sizeof(wm_tool_firmware_booter_t) - 4, 0); + fbooter.hd_checksum = wm_tool_file_crc; + + /* write image's header to output file */ + fwrite(&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fpimg); + + /* write image to output file */ + fseek(fpbin, 0, SEEK_SET); + while (!feof(fpbin)) + { + readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin); + fwrite(buf, 1, readlen, fpimg); + } + + /* write dummy data to pad 4byte-aligned */ + if (patch > 0) + { + memset(buf, 0, patch); + fwrite(buf, 1, patch, fpimg); + } + + if (fpbin) + { + fclose(fpbin); + } + + if (fpimg) + { + fclose(fpimg); + } + + wm_tool_printf("generate normal image completed.\r\n"); + + return 0; +} + +static int wm_tool_pack_gz_image(const char *gzbin, const char *outfile) +{ + FILE *fpbin = NULL; + FILE *fpimg = NULL; + char *name = "2.0.0"; + int readlen = 0; + int filelen = 0; + int patch = 0; + wm_tool_firmware_booter_t fbooter; + unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1]; + + fpbin = fopen(gzbin, "rb"); + if (NULL == fpbin) + { + wm_tool_printf("can not open input file [%s].\r\n", gzbin); + return -2; + } + + fpimg = fopen(outfile, "wb+"); + if (NULL == fpimg) + { + wm_tool_printf("open img file error: [%s].\r\n", outfile); + fclose(fpbin); + return -3; + } + + /* --------deal with upgrade image's CRC begin---- */ + wm_tool_file_crc = 0xFFFFFFFF; + while (!feof(fpbin)) + { + memset(buf, 0, sizeof(buf)); + readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin); + if(readlen % 4 != 0) + { + patch = 4 - readlen%4; + readlen += patch; + } + filelen += readlen; + wm_tool_get_crc32((unsigned char*)buf, readlen, 0); + } + /* --------deal with upgrade image's CRC end---- */ + + memset(&fbooter, 0, sizeof(wm_tool_firmware_booter_t)); + strcpy((char *)fbooter.ver, name); + fbooter.magic_no = WM_TOOL_IMG_HEAD_MAGIC_NO; + + fbooter.run_org_checksum = wm_tool_src_binary_crc; + fbooter.img_type = wm_tool_image_type; + fbooter.run_img_len = wm_tool_src_binary_len; + fbooter.run_img_addr = wm_tool_run_addr; + fbooter.zip_type = wm_tool_zip_type; + fbooter.upd_img_len = filelen; + fbooter.upd_img_addr = wm_tool_upd_addr; + fbooter.upd_checksum = wm_tool_file_crc; + + /* calculate image's header's CRC */ + wm_tool_file_crc = 0xFFFFFFFF; + wm_tool_get_crc32((unsigned char *)&fbooter, sizeof(wm_tool_firmware_booter_t) - 4, 0); + fbooter.hd_checksum = wm_tool_file_crc; + + /* write image's header to output file */ + fwrite(&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fpimg); + + /* write image to output file */ + fseek(fpbin, 0, SEEK_SET); + while (!feof(fpbin)) + { + readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin); + fwrite(buf, 1, readlen, fpimg); + } + + /* write dummy data to pad 4byte-aligned */ + if (patch > 0) + { + memset(buf, 0, patch); + fwrite(buf, 1, patch, fpimg); + } + + if (fpbin) + { + fclose(fpbin); + } + + if (fpimg) + { + fclose(fpimg); + } + + wm_tool_printf("generate compressed image completed.\r\n"); + + return 0; +} + +static int wm_tool_pack_dbg_image(const char *image, const char *outfile) +{ + FILE *fpimg = NULL; + FILE *fout = NULL; + unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1]; + int readlen = 0; + int i; + wm_tool_firmware_booter_t fbooter; + int appimg_len = 0; + int final_len = 0; + int magic_word = 0; + + fpimg = fopen(image, "rb"); + if (NULL == fpimg) + { + wm_tool_printf("open img file error: [%s].\r\n", image); + return -4; + } + + magic_word = 0; + readlen = fread(&magic_word, 1, 4, fpimg); + if (magic_word != WM_TOOL_IMG_HEAD_MAGIC_NO) + { + wm_tool_printf("input [%s] file magic error.\n", image); + fclose(fpimg); + return -4; + } + + fout = fopen(outfile, "wb+"); + + appimg_len = wm_tool_get_file_size(image); + + /* write 0xFF to output file */ + final_len = WM_TOOL_RUN_IMG_HEADER_LEN + (appimg_len - sizeof(wm_tool_firmware_booter_t)); + for (i = 0; i < (final_len /WM_TOOL_ONCE_READ_LEN); i++) + { + memset(buf, 0xff, WM_TOOL_ONCE_READ_LEN); + fwrite(buf, 1, WM_TOOL_ONCE_READ_LEN, fout); + } + memset(buf, 0xff, final_len % WM_TOOL_ONCE_READ_LEN); + fwrite(buf, 1, final_len % WM_TOOL_ONCE_READ_LEN, fout); + + memset(&fbooter, 0, sizeof(wm_tool_firmware_booter_t)); + + /* write sec img to output file */ + fseek(fpimg, 0, SEEK_SET); + readlen = fread((unsigned char *)&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fpimg); + + fseek(fout, 0, SEEK_SET); + fwrite(&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fout); + fseek(fout, WM_TOOL_RUN_IMG_HEADER_LEN, SEEK_SET); + + while (!feof(fpimg)) + { + readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpimg); + fwrite(buf, 1, readlen, fout); + } + + if (fpimg) + { + fclose(fpimg); + } + + if (fout) + { + fclose(fout); + } + + wm_tool_printf("generate debug image completed.\r\n"); + + return 0; +} + +static int wm_tool_pack_fls(const char *image, const char *outfile) +{ + FILE *fpbin = NULL; + FILE *fpimg = NULL; + FILE *fout = NULL; + unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1]; + int readlen = 0; + int filelen = 0; + int i; + wm_tool_firmware_booter_t fbooter; + int appimg_len = 0; + int final_len = 0; + int magic_word = 0; + + if (!wm_tool_secboot_image) + return 0;/* maybe return 1 */ + + fpbin = fopen(wm_tool_secboot_image,"rb"); + if (NULL == fpbin) + { + wm_tool_printf("can not open input file [%s].\r\n", wm_tool_secboot_image); + return -2; + } + + magic_word = 0; + readlen = fread(&magic_word, 1, 4, fpbin); + if (magic_word != WM_TOOL_IMG_HEAD_MAGIC_NO) + { + wm_tool_printf("input [%s] file magic error.\r\n", wm_tool_secboot_image); + return -3; + } + + fpimg = fopen(image, "rb"); + if (NULL == fpimg) + { + wm_tool_printf("open img file error [%s].\r\n", image); + fclose(fpbin); + return -4; + } + + magic_word = 0; + readlen = fread(&magic_word, 1, 4, fpimg); + if (magic_word != WM_TOOL_IMG_HEAD_MAGIC_NO) + { + wm_tool_printf("input [%s] file magic error.\r\n", image); + return -4; + } + + fout = fopen(outfile, "wb+"); + + appimg_len = wm_tool_get_file_size(image); + + /* write 0xFF to output file */ + final_len = WM_TOOL_SECBOOT_IMG_AREA_TOTAL_LEN + appimg_len; + for (i = 0; i < (final_len /WM_TOOL_ONCE_READ_LEN); i++) + { + memset(buf, 0xff, WM_TOOL_ONCE_READ_LEN); + fwrite(buf, 1, WM_TOOL_ONCE_READ_LEN, fout); + } + memset(buf, 0xff, final_len % WM_TOOL_ONCE_READ_LEN); + fwrite(buf, 1, final_len % WM_TOOL_ONCE_READ_LEN, fout); + + fseek(fout, sizeof(wm_tool_firmware_booter_t), SEEK_SET); + fseek(fpbin, 0, SEEK_SET); + /* write SECBOOT header to output file */ + readlen = fread(buf, 1, sizeof(wm_tool_firmware_booter_t), fpbin); + fwrite(buf, 1, sizeof(wm_tool_firmware_booter_t), fout); + + /* write SECBOOT image to output file */ + fseek(fout, sizeof(wm_tool_firmware_booter_t) + 256, SEEK_SET); + while (!feof(fpbin)) + { + readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin); + fwrite(buf, 1, readlen, fout); + } + + /* write run img header to output file */ + fseek(fout, sizeof(wm_tool_firmware_booter_t) + WM_TOOL_SECBOOT_IMG_AREA_TOTAL_LEN, SEEK_SET); + fseek(fpimg, 0, SEEK_SET); + readlen = fread(buf, 1, sizeof(wm_tool_firmware_booter_t), fpimg); + fwrite(buf, 1, readlen, fout); + + fseek(fout, sizeof(wm_tool_firmware_booter_t) + WM_TOOL_SECBOOT_IMG_AREA_TOTAL_LEN + WM_TOOL_RUN_IMG_HEADER_LEN, SEEK_SET); + /* write run img to output file */ + fseek(fpimg, sizeof(wm_tool_firmware_booter_t), SEEK_SET); + while (!feof(fpimg)) + { + readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpimg); + fwrite(buf, 1, readlen, fout); + } + + /* calculate CRC from secboot header */ + wm_tool_file_crc = 0xFFFFFFFF; + fseek(fout, sizeof(wm_tool_firmware_booter_t), SEEK_SET); + filelen = 0; + while (!feof(fout)) + { + readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fout); + filelen += readlen; + wm_tool_get_crc32((unsigned char*)buf, readlen, 0); + } + + memset(&fbooter, 0, sizeof(wm_tool_firmware_booter_t)); + + fbooter.magic_no = WM_TOOL_IMG_HEAD_MAGIC_NO; + fbooter.run_org_checksum = wm_tool_file_crc; + fbooter.img_type = 0; + fbooter.run_img_len = filelen; + fbooter.run_img_addr = WM_TOOL_SECBOOT_HEADER_POS; + fbooter.zip_type = 0; + fbooter.upd_img_len = 0; + fbooter.upd_img_addr = 0; + fbooter.upd_checksum = 0; + + /* write fls's header to output file */ + wm_tool_file_crc = 0xFFFFFFFF; + wm_tool_get_crc32((unsigned char *)&fbooter, sizeof(wm_tool_firmware_booter_t) - 4, 0); + fbooter.hd_checksum = wm_tool_file_crc; + fseek(fout, 0, SEEK_SET); + fwrite(&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fout); + + if (fpbin) + { + fclose(fpbin); + } + + if (fpimg) + { + fclose(fpimg); + } + + if (fout) + { + fclose(fout); + } + + wm_tool_printf("generate flash file completed.\r\n"); + + return 0; +} + +static int wm_tool_gzip_bin(const char *binary, const char *gzbin) +{ + FILE *bfp; + gzFile gzfp; + int ret; + char buf[1024]; + + bfp = fopen(binary, "rb"); + gzfp = gzopen((char *)gzbin, "wb+"); + + if (!bfp || !gzfp) + { + wm_tool_printf("can not gzip binary.\r\n"); + return -1; + } + + do { + ret = fread(buf, 1, sizeof(buf), bfp); + if (ret > 0) + { + ret = gzwrite(gzfp, (voidp)buf, ret); + if (ret <= 0) + { + wm_tool_printf("can not write gzip binary.\r\n"); + return -2; + } + } + } while (ret > 0); + + gzclose(gzfp); + fclose(bfp); + + wm_tool_printf("compress binary completed.\r\n"); + + return 0; +} + +static int wm_tool_pack_firmware(void) +{ + int ret; + char *path; + char *name; + char *image; + char *gzbin; + char *gzimg; + char *dbgimg; + char *fls; + + if (!wm_tool_input_binary) + return 1; + + if (!wm_tool_output_image) + { + char *r = wm_tool_input_binary; + char *t = wm_tool_input_binary; + do + { + r = strchr(r, '.'); + if (r) + { + t = r; + r++; + } + } while (r); + wm_tool_output_image = malloc(t - wm_tool_input_binary + 1); + if (!wm_tool_output_image) + return -1; + memcpy(wm_tool_output_image, wm_tool_input_binary, t - wm_tool_input_binary); + wm_tool_output_image[t - wm_tool_input_binary] = '\0'; + } + + char *p = wm_tool_output_image; + char *q = wm_tool_output_image; + + do + { + p = strchr(p, '/'); + if (p) + { + p++; + q = p; + } + } while (p); + + /* output file */ + name = strdup(q); + *q = '\0'; + path = strdup(wm_tool_output_image); + + image = malloc(strlen(path) + strlen(name) + strlen(".img") + 1); + if (!image) + return -1; + sprintf(image, "%s%s.img", path, name); + + ret = wm_tool_pack_image(image); + if (ret) + return ret; + + if (WM_TOOL_ZIP_TYPE_COMPRESS == wm_tool_zip_type) + { + gzbin = malloc(strlen(path) + strlen(name) + strlen("_gz.bin") + 1); + gzimg = malloc(strlen(path) + strlen(name) + strlen("_gz.img") + 1); + if (!gzbin || !gzimg) + return -1; + sprintf(gzbin, "%s%s.bin.gz", path, name); + sprintf(gzimg, "%s%s_gz.img", path, name); + + ret = wm_tool_gzip_bin(wm_tool_input_binary, gzbin); + if (ret) + return ret; + + ret = wm_tool_pack_gz_image(gzbin, gzimg); + free(gzbin); + free(gzimg); + if (ret) + return ret; + } + + if (wm_tool_is_debug) + { + dbgimg = malloc(strlen(path) + strlen(name) + strlen("_dbg.img") + 1); + if (!dbgimg) + return -1; + sprintf(dbgimg, "%s%s_dbg.img", path, name); + + ret = wm_tool_pack_dbg_image(image, dbgimg); + free(dbgimg); + if (ret) + return ret; + } + + fls = malloc(strlen(path) + strlen(name) + strlen(".fls") + 1); + if (!fls) + return -1; + sprintf(fls, "%s%s.fls", path, name); + + ret = wm_tool_pack_fls(image, fls); + + free(image); + free(fls); + + return ret; +} + +#ifdef __MINGW32__ +static void wm_tool_delay_ms(int ms) +{ + Sleep(ms); + + return; +} + +static int wm_tool_uart_set_rts(int boolflag) +{ + return EscapeCommFunction(wm_tool_uart_handle,((boolflag) ? SETRTS : CLRRTS)) ? 0 : -1; +} + +static int wm_tool_uart_set_dtr(int boolflag) +{ + return EscapeCommFunction(wm_tool_uart_handle,((boolflag) ? SETDTR : CLRDTR)) ? 0 : -1; +} + +static int wm_tool_uart_set_timeout(void) +{ + BOOL ret; + COMMTIMEOUTS timeout; + + timeout.ReadIntervalTimeout = MAXDWORD; + timeout.ReadTotalTimeoutConstant = 0; + timeout.ReadTotalTimeoutMultiplier = 0; + timeout.WriteTotalTimeoutConstant = 0; + timeout.WriteTotalTimeoutMultiplier = 0; + + ret = SetCommTimeouts(wm_tool_uart_handle, &timeout); + if (ret) + ret = SetCommMask(wm_tool_uart_handle, EV_TXEMPTY); + + return ret ? 0 : -1; +} + +static void wm_tool_uart_set_block(int bolck) +{ + if (bolck) + wm_tool_uart_block = INFINITE; + else + wm_tool_uart_block = 0; + + return; +} + +static int wm_tool_uart_set_speed(int speed) +{ + int i; + int size; + DCB cfg; + + size = sizeof(wm_tool_uart_speed_array) / sizeof(int); + + for (i= 0; i < size; i++) + { + if (speed == wm_tool_uart_name_array[i]) + { + if (GetCommState(wm_tool_uart_handle, &cfg)) + { + cfg.DCBlength = sizeof(DCB); + cfg.BaudRate = wm_tool_uart_speed_array[i]; + cfg.fBinary = TRUE; + cfg.fParity = FALSE; + cfg.fDtrControl = DTR_CONTROL_DISABLE; + cfg.fDsrSensitivity = FALSE; + cfg.fRtsControl = RTS_CONTROL_DISABLE; + cfg.ByteSize = 8; + cfg.StopBits = ONESTOPBIT; + cfg.fAbortOnError = FALSE; + cfg.fOutX = FALSE; + cfg.fInX = FALSE; + cfg.fErrorChar = FALSE; + cfg.fNull = FALSE; + cfg.fOutxCtsFlow = FALSE; + cfg.fOutxDsrFlow = FALSE; + cfg.Parity = NOPARITY; + cfg.fTXContinueOnXoff = FALSE; + + return SetCommState(wm_tool_uart_handle, &cfg) ? 0 : -1; + } + else + { + return -3; + } + } + } + + return -2; +} + +static void wm_tool_uart_clear(void) +{ + PurgeComm(wm_tool_uart_handle, PURGE_RXCLEAR); + + return; +} + +static int wm_tool_uart_open(const char *device) +{ + BOOL ret; + char name[40]; + + sprintf(name,"\\\\.\\%s", device); + + wm_tool_uart_handle = CreateFile(name, GENERIC_WRITE | GENERIC_READ, + 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + + if (wm_tool_uart_handle == INVALID_HANDLE_VALUE) + { + return -1; + } + + ret = SetupComm(wm_tool_uart_handle, 4096, 4096); + if (ret) + { + ret = wm_tool_uart_set_speed(WM_TOOL_DEFAULT_BAUD_RATE); + ret |= wm_tool_uart_set_timeout(); + } + else + { + ret = -1; + } + + return ret; +} + +static int wm_tool_uart_read(void *data, unsigned int size) +{ + BOOL ret; + DWORD wait; + unsigned long len = 0; + COMSTAT state; + DWORD error; + OVERLAPPED event; + + do + { + memset(&event, 0, sizeof(OVERLAPPED)); + + event.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + ClearCommError(wm_tool_uart_handle, &error, &state); + + ret = ReadFile(wm_tool_uart_handle, data, size, &len, &event); + if (!ret) + { + if (ERROR_IO_PENDING == GetLastError()) + { + wait = WaitForSingleObject(event.hEvent, wm_tool_uart_block); + if (WAIT_OBJECT_0 == wait) + ret = TRUE; + } + } + } while (wm_tool_uart_block && !len); + + if (ret) + { + if (len > 0) + return (int)len; + } + + return -1; +} + +static int wm_tool_uart_write(const void *data, unsigned int size) +{ + BOOL ret; + DWORD wait; + OVERLAPPED event; + COMSTAT state; + DWORD error; + unsigned int snd = 0; + unsigned long len = 0; + + memset(&event, 0, sizeof(OVERLAPPED)); + + event.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + do + { + ClearCommError(wm_tool_uart_handle, &error, &state); + + ret = WriteFile(wm_tool_uart_handle, data + snd, size - snd, &len, &event); + if (!ret) + { + if (ERROR_IO_PENDING == GetLastError()) + { + wait = WaitForSingleObject(event.hEvent, INFINITE); + if (WAIT_OBJECT_0 == wait) + ret = TRUE; + } + } + + if (ret) + { + if ((len > 0) && (snd != size)) + { + snd += len; + } + else + { + return size; + } + } + else + { + break; + } + } while (1); + + return -1; +} + +static void wm_tool_uart_close(void) +{ + FlushFileBuffers(wm_tool_uart_handle); + CloseHandle(wm_tool_uart_handle); + wm_tool_uart_handle = NULL; + + return; +} + +static DWORD WINAPI wm_tool_uart_tx_thread(LPVOID arg) +{ + wm_tool_stdin_to_uart(); + + return 0; +} + +static int wm_tool_create_thread(void) +{ + HANDLE thread_handle; + thread_handle = CreateThread(0, 0, wm_tool_uart_tx_thread, NULL, 0, NULL); + CloseHandle(thread_handle); + return 0; +} + +#else + +static void wm_tool_delay_ms(int ms) +{ + usleep(ms * 1000); + + return; +} + +static int wm_tool_uart_set_rts(int boolflag) +{ + int ret; + int controlbits; + + ret = ioctl(wm_tool_uart_fd, TIOCMGET, &controlbits); + if (ret < 0) + { + WM_TOOL_DBG_PRINT("TIOCMGET failed!\r\n"); + return ret; + } + + if (controlbits & TIOCM_RTS) + { + WM_TOOL_DBG_PRINT("TIOCM_RTS!, %d!\r\n", boolflag); + } + else + { + WM_TOOL_DBG_PRINT("~TIOCM_RTS!, %d!\r\n", boolflag); + } + + if (boolflag) + controlbits |= TIOCM_RTS; + else + controlbits &= ~TIOCM_RTS; + + ret = ioctl(wm_tool_uart_fd, TIOCMSET, &controlbits); + if (ret < 0) + { + WM_TOOL_DBG_PRINT("TIOCMSET failed!\r\n"); + } + + if (ret >= 0) + ret = 0; + + return ret; +} + +static int wm_tool_uart_set_dtr(int boolflag) +{ + int ret; + int controlbits; + + ret = ioctl(wm_tool_uart_fd, TIOCMGET, &controlbits); + if (ret < 0) + { + WM_TOOL_DBG_PRINT("TIOCMGET failed!\r\n"); + return ret; + } + + if (controlbits & TIOCM_DTR) + { + WM_TOOL_DBG_PRINT("TIOCM_DTR, %d!\r\n", boolflag); + } + else + { + WM_TOOL_DBG_PRINT("~TIOCM_DTR, %d!\r\n", boolflag); + } + + if (boolflag) + controlbits |= TIOCM_DTR; + else + controlbits &= ~TIOCM_DTR; + + ret = ioctl(wm_tool_uart_fd, TIOCMSET, &controlbits); + if (ret < 0) + { + WM_TOOL_DBG_PRINT("TIOCMSET failed!\r\n"); + } + + if (ret >= 0) + ret = 0; + + return ret; +} + +static void wm_tool_uart_set_block(int bolck) +{ + int comopt; + + comopt = fcntl(wm_tool_uart_fd, F_GETFL, 0); + + if (bolck) + fcntl(wm_tool_uart_fd, F_SETFL, comopt & ~O_NDELAY); + else + fcntl(wm_tool_uart_fd, F_SETFL, comopt | O_NDELAY); + + return; +} + +static int wm_tool_uart_set_speed(int speed) +{ + + int i; + int size; + int status; + struct termios opt; + + tcgetattr(wm_tool_uart_fd, &opt); + + size = sizeof(wm_tool_uart_speed_array) / sizeof(int); + + for (i= 0; i < size; i++) + { + if (speed == wm_tool_uart_name_array[i]) + { + cfsetispeed(&opt, wm_tool_uart_speed_array[i]); + cfsetospeed(&opt, wm_tool_uart_speed_array[i]); + + status = tcsetattr(wm_tool_uart_fd, TCSANOW, &opt); + + if (status != 0) + { + return -1; + } + } + } + + return 0; +} + +static void wm_tool_uart_clear(void) +{ + tcflush(wm_tool_uart_fd, TCIFLUSH); + + return; +} + +static int wm_tool_uart_open(const char *device) +{ + struct termios tty; + + wm_tool_uart_fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (-1 == wm_tool_uart_fd) + { + return -1; + } + + tcgetattr(wm_tool_uart_fd, &wm_tool_saved_serial_cfg); + + wm_tool_uart_set_dtr(0); + + tcgetattr(wm_tool_uart_fd, &tty); + + /* databit 8bit */ + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; + + /* set into raw, no echo mode */ + tty.c_iflag = IGNBRK; + tty.c_lflag = 0; + tty.c_oflag = 0; + tty.c_cflag |= CLOCAL | CREAD; + tty.c_cflag &= ~CRTSCTS; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 5; + + /* have no software flow control */ + tty.c_iflag &= ~(IXON|IXOFF|IXANY); + + /* parity none */ + tty.c_cflag &= ~(PARENB | PARODD); + + /* stopbit 1bit */ + tty.c_cflag &= ~CSTOPB; + + tcsetattr(wm_tool_uart_fd, TCSANOW, &tty); + + return wm_tool_uart_set_speed(WM_TOOL_DEFAULT_BAUD_RATE); +} + +static int wm_tool_uart_read(void *data, unsigned int size) +{ + return read(wm_tool_uart_fd, data, size); +} + +static int wm_tool_uart_write(const void *data, unsigned int size) +{ + int snd = 0; + int ret = 0; + + do + { + ret = write(wm_tool_uart_fd, data + snd, size - snd); + if (ret > 0) + { + snd += ret; + + if (snd == size) + { + return size; + } + } + else + { + break; + } + } while (1); + + return -1; +} + +static void wm_tool_uart_close(void) +{ + tcsetattr(wm_tool_uart_fd, TCSANOW, &wm_tool_saved_serial_cfg); + + tcdrain(wm_tool_uart_fd); + tcflush(wm_tool_uart_fd, TCIOFLUSH); + + close(wm_tool_uart_fd); + wm_tool_uart_fd = -1; + + return; +} + +static void *wm_tool_uart_tx_thread(void *arg) +{ + wm_tool_stdin_to_uart(); + + return NULL; +} + +static int wm_tool_create_thread(void) +{ + pthread_t thread_id; + return pthread_create(&thread_id, NULL, wm_tool_uart_tx_thread, NULL); +} +#endif + +static void wm_tool_stdin_to_uart(void) +{ + int ret; + char buf[WM_TOOL_ONCE_READ_LEN]; + + while (1) + { + if (fgets(buf, WM_TOOL_ONCE_READ_LEN, stdin)) + { + ret = wm_tool_uart_write(buf, strlen(buf)); + if (ret <= 0) + { + WM_TOOL_DBG_PRINT("failed to write [%s].\r\n", buf); + } + } + } + + return; +} + +static int wm_tool_show_log_from_serial(void) +{ + int i; + int ret; + unsigned int j = 0; + unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1]; + + ret = wm_tool_uart_open(wm_tool_serial_path); + if (ret) + { + wm_tool_printf("can not open serial\r\n"); + return ret; + } + + if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_normal_serial_rate) + { + ret = wm_tool_uart_set_speed(wm_tool_normal_serial_rate); + if (ret) + { + wm_tool_printf("can not set serial baud rate.\r\n"); + wm_tool_uart_close(); + return ret; + } + } + + ret = wm_tool_create_thread(); + if (ret) + { + wm_tool_printf("can not create thread.\r\n"); + wm_tool_uart_close(); + return ret; + } + + wm_tool_uart_set_block(0); + + while (1) + { + ret = wm_tool_uart_read(buf, WM_TOOL_ONCE_READ_LEN); + if (ret > 0) + { + if (WM_TOOL_SHOW_LOG_STR == wm_tool_show_log_type) + { + buf[ret] = '\0'; + wm_tool_printf("%s", (char *)buf); + } + else if (WM_TOOL_SHOW_LOG_HEX == wm_tool_show_log_type) + { + for (i = 0; i < ret; i++, j++) + { + wm_tool_printf("%02X ", buf[i]); + if ((j + 1) % 16 == 0) + { + wm_tool_printf("\r\n"); + } + } + } + else + { + break; + } + } + else + { + wm_tool_delay_ms(3); + } + } + + wm_tool_uart_close(); + + return ret; +} + +static int wm_tool_set_wifi_chip_speed(int speed) +{ + int ret; + + if (2000000 == speed) + { + ret = wm_tool_uart_write(wm_tool_chip_cmd_b2000000, sizeof(wm_tool_chip_cmd_b2000000)); + } + else if (1000000 == speed) + { + ret = wm_tool_uart_write(wm_tool_chip_cmd_b1000000, sizeof(wm_tool_chip_cmd_b1000000)); + } + else if (921600 == speed) + { + ret = wm_tool_uart_write(wm_tool_chip_cmd_b921600, sizeof(wm_tool_chip_cmd_b921600)); + } + else if (460800 == speed) + { + ret = wm_tool_uart_write(wm_tool_chip_cmd_b460800, sizeof(wm_tool_chip_cmd_b460800)); + } + else + { + ret = wm_tool_uart_write(wm_tool_chip_cmd_b115200, sizeof(wm_tool_chip_cmd_b115200)); + } + + return ret; +} + +static int wm_tool_send_esc2uart(int ms) +{ + int i; + int err = 0; + unsigned char esc_key = 27; + + for (i = 0; i < (ms / 10); i++) + { + err = wm_tool_uart_write(&esc_key, 1); + wm_tool_delay_ms(10);/* 10-50ms */ + } + + return err; +} + +static int wm_tool_erase_image(wm_tool_dl_erase_e type) +{ + int cnt = 0; + int ret = -1; + unsigned char ch; + unsigned char sec_cmd[] = {0x21, 0x06, 0x00, 0xc7, 0x7c, 0x3f, 0x00, 0x00, 0x00}; + unsigned char rom_cmd[] = {0x21, 0x06, 0x00, 0x41, 0x45, 0x32, 0x00, 0x00, 0x00}; + + WM_TOOL_DBG_PRINT("start erase.\r\n"); + + if (WM_TOOL_DL_ERASE_SECBOOT == type) + { + wm_tool_printf("erase second boot area...\r\n"); + ret = wm_tool_uart_write(sec_cmd, sizeof(sec_cmd)); + } + else if (WM_TOOL_DL_ERASE_ALL == type) + { + wm_tool_printf("erase image area...\r\n"); + ret = wm_tool_uart_write(rom_cmd, sizeof(rom_cmd)); + } + if (ret <= 0) + return -1; + + wm_tool_uart_clear(); + + wm_tool_uart_set_block(1); + + do + { + ret = wm_tool_uart_read(&ch, 1); + if (ret > 0) + { + if (('C' == ch) || ('P' == ch)) + cnt++; + else + cnt = 0; + } + else + { + wm_tool_printf("erase error, errno = %d.\r\n", errno); + return -2; + } + } while (cnt < 3); + + wm_tool_uart_set_block(0); + + wm_tool_printf("erase finish.\r\n"); + + return 0; +} + +static int wm_tool_query_mac(void) +{ + int ret = -1; + int err; + int offset = 0; + char macstr[32] = {0}; + int len = strlen("MAC:AABBCCDDEEFF\n");/* resp format, ROM "Mac:AABBCCDDEEFF\n", SECBOOT "MAC:AABBCCDDEEFF\n" */ + unsigned char macaddr[6] = {0}; + + wm_tool_uart_clear(); + + wm_tool_uart_set_block(1); + + err = wm_tool_uart_write(wm_tool_chip_cmd_get_mac, sizeof(wm_tool_chip_cmd_get_mac)); + if (err > 0) + { + do + { + err = wm_tool_uart_read((unsigned char *)(macstr + offset), sizeof(macstr) - 1 - offset); + if (err > 0) + { + offset += err; + if (offset >= len) + { + macstr[len - 1] = '\0';/* \n -> 0 */ + err = wm_tool_str_to_hex_array(macstr + strlen("MAC:"), 6, macaddr); + if (!err) + { + wm_tool_printf("mac %02X-%02X-%02X-%02X-%02X-%02X.\r\n", macaddr[0], + macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); + + if (!strncmp(macstr, "Mac:", strlen("Mac:")) && + (WM_TOOL_DL_TYPE_FLS != wm_tool_dl_type)) + { + wm_tool_printf("please download the firmware in .fls format.\r\n"); + } + else + { + ret = 0; + } + } + break; + } + } + } while (err > 0); + } + + wm_tool_uart_set_block(0); + + return ret; +} + +static int wm_tool_xmodem_download(const char *image) +{ + FILE *imgfp; + unsigned char packet_data[XMODEM_DATA_SIZE]; + unsigned char frame_data[XMODEM_DATA_SIZE + XMODEM_CRC_SIZE + XMODEM_FRAME_ID_SIZE + 1]; + int complete, retry_num, pack_counter, read_number, write_number = 0, i; + unsigned short crc_value; + unsigned char ack_id; + int sndlen; + int ret = -111; + + imgfp = fopen(image, "rb"); + if (!imgfp) + { + wm_tool_printf("can not open image to download.\r\n"); + return -1; + } + + sndlen = 0; + pack_counter = 0; + complete = 0; + retry_num = 0; + + wm_tool_printf("start download.\r\n"); + wm_tool_printf("0%% ["); + + wm_tool_uart_clear(); + + wm_tool_uart_set_block(1); + + ack_id = XMODEM_ACK; + + while (!complete) + { + WM_TOOL_DBG_PRINT("switch ack_id = %x\r\n", ack_id); + + switch(ack_id) + { + case XMODEM_ACK: + { + retry_num = 0; + pack_counter++; + read_number = fread(packet_data, sizeof(char), XMODEM_DATA_SIZE, imgfp); + + WM_TOOL_DBG_PRINT("fread = %d\r\n", read_number); + + if (read_number > 0) + { + sndlen += read_number; + + if (read_number < XMODEM_DATA_SIZE) + { + WM_TOOL_DBG_PRINT("start filling the last frame!\r\n"); + for ( ; read_number < XMODEM_DATA_SIZE; read_number++) + packet_data[read_number] = 0x0; + } + + frame_data[0] = XMODEM_HEAD; + frame_data[1] = (char)pack_counter; + frame_data[2] = (char)(255 - frame_data[1]); + + for (i = 0; i < XMODEM_DATA_SIZE; i++) + frame_data[i + 3] = packet_data[i]; + + crc_value = wm_tool_get_crc16(packet_data, XMODEM_DATA_SIZE); + + frame_data[XMODEM_DATA_SIZE + 3]=(unsigned char)(crc_value >> 8); + frame_data[XMODEM_DATA_SIZE + 4]=(unsigned char)(crc_value); + + write_number = wm_tool_uart_write(frame_data, XMODEM_DATA_SIZE + 5); + if (write_number <= 0) + wm_tool_printf("write serial error, errno = %d.\r\n", errno); + + WM_TOOL_DBG_PRINT("waiting for ack, %d, %d ...\r\n", pack_counter, write_number); + + while ((wm_tool_uart_read(&ack_id, 1)) <= 0); + + if (ack_id == XMODEM_ACK) + { + WM_TOOL_DBG_PRINT("Ok!\r\n"); + + if (sndlen % 10240 == 0) + { + wm_tool_printf("#"); + } + } + else + { + WM_TOOL_DBG_PRINT("error = %x!\r\n", ack_id); + } + } + else + { + ack_id = XMODEM_EOT; + complete = 1; + + WM_TOOL_DBG_PRINT("waiting for complete ack ...\r\n"); + + while (ack_id != XMODEM_ACK) + { + ack_id = XMODEM_EOT; + + write_number = wm_tool_uart_write(&ack_id, 1); + if (write_number <= 0) + wm_tool_printf("write serial error, errno = %d.\r\n", errno); + + while ((wm_tool_uart_read(&ack_id, 1)) <= 0); + } + + WM_TOOL_DBG_PRINT("ok\r\n"); + + wm_tool_printf("] 100%%\r\n"); + + wm_tool_printf("download completed.\r\n"); + + ret = 0; + } + break; + } + case XMODEM_NAK: + { + if ( retry_num++ > 100) + { + WM_TOOL_DBG_PRINT("retry too many times, quit!\r\n"); + wm_tool_printf("download firmware timeout.\r\n"); + + complete = 1; + } + else + { + write_number = wm_tool_uart_write(frame_data, XMODEM_DATA_SIZE + 5); + if (write_number <= 0) + wm_tool_printf("write serial error, errno = %d.\r\n", errno); + + WM_TOOL_DBG_PRINT("retry for ack, %d, %d ...\r\n", pack_counter, write_number); + + while ((wm_tool_uart_read(&ack_id, 1)) <= 0); + + if (ack_id == XMODEM_ACK) + { + WM_TOOL_DBG_PRINT("ok\r\n"); + } + else + { + WM_TOOL_DBG_PRINT("error!\r\n"); + } + } + break; + } + default: + { + WM_TOOL_DBG_PRINT("fatal error!\r\n"); + WM_TOOL_DBG_PRINT("unknown xmodem protocal [%x].\r\n", ack_id); + wm_tool_printf("\r\ndownload failed, please reset and try again.\r\n"); + + complete = 1; + break; + } + } + } + + wm_tool_uart_set_block(0); + + fclose(imgfp); + + return ret; +} +static int wm_tool_download_firmware(void) +{ + int ret; + int cnt = 0; + int note = 1; + int timeout = 0; + float timeuse; + time_t start, end; + unsigned char ch; + + wm_tool_printf("connecting serial...\r\n"); + + ret = wm_tool_uart_open(wm_tool_serial_path); + if (ret) + { + wm_tool_printf("can not open serial\r\n"); + return ret; + } + + /* In some cases, setting the serial port initialization setting requires a delay. */ + wm_tool_delay_ms(500); + + wm_tool_printf("serial connected.\r\n"); + + if (WM_TOOL_DL_ACTION_AT == wm_tool_dl_action) + { + if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_normal_serial_rate) + wm_tool_uart_set_speed(wm_tool_normal_serial_rate); + +#if 0 /* use erase option */ + if (WM_TOOL_DL_TYPE_FLS == wm_tool_dl_type) + { + ret = wm_tool_uart_write("AT+&FLSW=8002000,0\r\n", strlen("AT+&FLSW=8002000,0\r\n")); + if (ret <= 0) + { + wm_tool_printf("destroy secboot failed.\r\n"); + wm_tool_uart_close(); + return -3; + } + wm_tool_delay_ms(300); + } +#endif + + ret = wm_tool_uart_write("AT+Z\r\n", strlen("AT+Z\r\n")); + if (ret <= 0) + { + wm_tool_printf("reset error.\r\n"); + wm_tool_uart_close(); + return -4; + } + + if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_normal_serial_rate) + wm_tool_uart_set_speed(WM_TOOL_DEFAULT_BAUD_RATE); + } + else if (WM_TOOL_DL_ACTION_RTS == wm_tool_dl_action) + { + ret = wm_tool_uart_set_dtr(0); + ret |= wm_tool_uart_set_rts(1); + wm_tool_delay_ms(50); + ret |= wm_tool_uart_set_dtr(1); + ret |= wm_tool_uart_set_rts(0); + wm_tool_delay_ms(50); + ret |= wm_tool_uart_set_dtr(0); + if (ret < 0) + { + wm_tool_printf("set rts to reboot error.\r\n"); + wm_tool_uart_close(); + return -5; + } + } + + wm_tool_printf("wait serial sync..."); + + wm_tool_send_esc2uart(500);/* used for delay */ + + start = time(NULL); + + do + { + ret = wm_tool_uart_read(&ch, 1); + + WM_TOOL_DBG_PRINT("ret=%d, %x-%c\r\n", ret, ch, ch); + + if (ret > 0) + { + if (('C' == ch) || ('P' == ch)) + cnt++; + else + cnt = 0; + } + else + { + wm_tool_send_esc2uart(30); + } + + end = time(NULL); + timeuse = difftime(end, start); + if (timeuse >= 1) + { + wm_tool_printf("."); + + timeout++; + if ((timeout >= (WM_TOOL_DOWNLOAD_TIMEOUT_SEC / 10)) && note) + { + wm_tool_printf("\r\nplease manually reset the device.\r\n"); + note = 0; + } + else if (timeout > WM_TOOL_DOWNLOAD_TIMEOUT_SEC) + { + wm_tool_uart_close(); + wm_tool_printf("\r\nserial sync timeout.\r\n"); + return -6; + } + + start = time(NULL); + } + } while (cnt < 3); + + wm_tool_printf("\r\nserial sync sucess.\r\n"); + + ret = wm_tool_query_mac(); + if (ret) + { + wm_tool_uart_close(); + wm_tool_printf("download failed.\r\n"); + return ret; + } + + if (WM_TOOL_DL_ERASE_SECBOOT == wm_tool_dl_erase) + { + ret = wm_tool_erase_image(WM_TOOL_DL_ERASE_SECBOOT); + if (ret) + { + wm_tool_uart_close(); + wm_tool_printf("failed to erase.\r\n"); + return ret; + } + } + else if (WM_TOOL_DL_ERASE_ALL == wm_tool_dl_erase) + { + ret = wm_tool_erase_image(WM_TOOL_DL_ERASE_SECBOOT); + ret |= wm_tool_erase_image(WM_TOOL_DL_ERASE_ALL); + if (ret) + { + wm_tool_uart_close(); + wm_tool_printf("failed to erase.\r\n"); + return ret; + } + } + + if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_download_serial_rate) + { + ret = wm_tool_set_wifi_chip_speed(wm_tool_download_serial_rate); + if (ret > 0) + { + wm_tool_delay_ms(1 * 1000); + wm_tool_uart_set_speed(wm_tool_download_serial_rate); + } + } + + ret = wm_tool_xmodem_download(wm_tool_download_image); + if (ret) + { + if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_download_serial_rate) + { + wm_tool_delay_ms(1 * 1000); + wm_tool_set_wifi_chip_speed(WM_TOOL_DEFAULT_BAUD_RATE); + wm_tool_delay_ms(1 * 1000); + } + } + else + { + if (WM_TOOL_DL_TYPE_FLS == wm_tool_dl_type) + { + if (WM_TOOL_DL_ACTION_RTS == wm_tool_dl_action)/* auto reset */ + { + wm_tool_uart_set_dtr(0); + wm_tool_uart_set_rts(1); + wm_tool_delay_ms(50); + wm_tool_uart_set_dtr(1); + wm_tool_uart_set_rts(0); + wm_tool_delay_ms(50); + wm_tool_uart_set_dtr(0); + } + else + { + wm_tool_printf("please manually reset the device.\r\n"); + } + } + } + + wm_tool_uart_close(); + + return ret; +} + +static void wm_tool_show_local_com(void) +{ +#if defined(__APPLE__) && defined(__MACH__) + char *comstr = "tty.usbserial"; +#elif defined(__CYGWIN__) + int num; + char *comstr = "ttyS"; +#elif defined(__linux__) + char *comstr = "ttyUSB"; +#endif + +#if defined(__APPLE__) || defined(__MACH__) || defined(__CYGWIN__) || defined(__linux__) + + DIR *dir; + struct dirent *file; + + dir = opendir("/dev"); + + if (dir) + { + while (NULL != (file = readdir(dir))) + { + + if ((0 == strncmp(file->d_name, comstr, strlen(comstr))) && (DT_CHR == file->d_type)) + { +#if defined(__CYGWIN__) + num = atoi(file->d_name + strlen(comstr)); + wm_tool_printf("COM%d ", num + 1); +#else + wm_tool_printf("%s ", file->d_name); +#endif + } + } + closedir(dir); + wm_tool_printf("\r\n"); + } + +#elif defined(__MINGW32__) + + LONG ret; + HKEY key; + DWORD i = 0; + DWORD knlen; + char kname[WM_TOOL_PATH_MAX] = {0}; + DWORD kvlen; + char kvalue[WM_TOOL_PATH_MAX] = {0}; + REGSAM kmask = KEY_READ; + +#if defined(KEY_WOW64_64KEY) + BOOL is_64; + + if (IsWow64Process(GetCurrentProcess(), &is_64)) + { + if (is_64) + kmask |= KEY_WOW64_64KEY; + } +#endif + + ret = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, NULL, 0, kmask, NULL, &key, NULL); + if (ret != ERROR_SUCCESS) + { + return; + } + + do{ + knlen = sizeof(kname); + kvlen = sizeof(kvalue); + ret = RegEnumValue(key, i, kname, &knlen, 0, NULL, (LPBYTE)kvalue, &kvlen); + if (ret == ERROR_SUCCESS) + { + if (strcmp(kvalue, "") != 0) + { + wm_tool_printf("%s ", kvalue); + } + } + + i++; + } while (ret != ERROR_NO_MORE_ITEMS); + + RegCloseKey(key); + + wm_tool_printf("\r\n"); + +#else + + wm_tool_printf("\r\nunsupported system.\r\n"); + +#endif + + return; +} +static void wm_tool_free_res(void) +{ + if (wm_tool_download_image) + free(wm_tool_download_image); + + if (wm_tool_output_image) + free(wm_tool_output_image); + + if (wm_tool_input_binary) + free(wm_tool_input_binary); + + if (wm_tool_secboot_image) + free(wm_tool_secboot_image); + + return; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + if (!wm_tool_parse_arv(argc, argv)) + { + wm_tool_print_usage(argv[0]); + wm_tool_free_res(); + return 0; + } + + if (wm_tool_show_usage) + { + wm_tool_print_usage(argv[0]); + wm_tool_free_res(); + return 0; + } + + if (wm_tool_show_ver) + { + wm_tool_print_version(argv[0]); + wm_tool_free_res(); + return 0; + } + + if (wm_tool_list_com) + { + wm_tool_show_local_com(); + wm_tool_free_res(); + return 0; + } + + if (wm_tool_input_binary) + { + ret = wm_tool_pack_firmware(); + } + + if (wm_tool_download_image) + { + ret = wm_tool_download_firmware(); + } + + if (wm_tool_show_log_type) + { + ret = wm_tool_show_log_from_serial(); + } + + if (ret > 0) + wm_tool_print_usage(argv[0]); + + wm_tool_free_res(); + + return ret; +} + diff --git a/src/app_main.c b/src/app_main.c new file mode 100644 index 0000000..84d5377 --- /dev/null +++ b/src/app_main.c @@ -0,0 +1,22 @@ +#include + +#include "wm_include.h" + +#define NL_APP_TASK_PRIO 40 +static u32 s_app_stack[512]; + +static void app_task(void *arg) +{ + unsigned int n = 0; + (void)arg; + for (;;) { + printf("ninlight %s alive %u\r\n", NINLIGHT_DEVICE_ID, n++); + tls_os_time_delay(HZ); + } +} + +void UserMain(void) +{ + printf("\r\nninlight %s for %s (built %s)\r\n", NINLIGHT_FW_VERSION, NINLIGHT_DEVICE_ID, NINLIGHT_BUILD_DATE); + tls_os_task_create(NULL, "nl_app", app_task, NULL, (u8 *)s_app_stack, sizeof(s_app_stack), NL_APP_TASK_PRIO, 0); +} diff --git a/src/sys/fwup_hook_default.c b/src/sys/fwup_hook_default.c new file mode 100644 index 0000000..6b4d7c4 --- /dev/null +++ b/src/sys/fwup_hook_default.c @@ -0,0 +1,10 @@ +#include "sys/nl_fwup_hook.h" + +/* Until OTA support (plan 3) provides a strong definition, every update is rejected. */ +__attribute__((weak)) bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len) +{ + (void)sha256; + (void)tail; + (void)tail_len; + return false; +} diff --git a/src/sys/nl_fwup_hook.h b/src/sys/nl_fwup_hook.h new file mode 100644 index 0000000..ff85122 --- /dev/null +++ b/src/sys/nl_fwup_hook.h @@ -0,0 +1,12 @@ +#ifndef NL_FWUP_HOOK_H +#define NL_FWUP_HOOK_H + +#include +#include + +/* Called by the SDK updater (sdk/Platform/Common/fwup/wm_fwup.c) after the image CRC + * matched. sha256 covers the T_BOOTER header plus payload, tail is the 512 byte block + * appended to the image. Return true to accept the image. */ +bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len); + +#endif diff --git a/src/sys/sdk_stubs.c b/src/sys/sdk_stubs.c new file mode 100644 index 0000000..a601f16 --- /dev/null +++ b/src/sys/sdk_stubs.c @@ -0,0 +1,14 @@ +/* ninlight: minimal stand-ins for the two board-init hooks that + * sdk/Platform/Sys/wm_main.c expects the application to provide + * (`extern void EarlyInit(void);` / `extern void pre_gpio_config();`). + * The old QConnex firmware (qconnex_sdk/src/qc_main.c) defined pre_gpio_config() + * as an empty no-op, and used EarlyInit() only to call into its own QConnex + * hardware layer (QCXH_EarlyInit()), which ninlight does not use. See sdk/PATCHES.md. */ + +void EarlyInit(void) +{ +} + +void pre_gpio_config(void) +{ +} diff --git a/tools/docker.sh b/tools/docker.sh new file mode 100755 index 0000000..b12afbd --- /dev/null +++ b/tools/docker.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Runs a command inside the ninlight build container (x86_64, gcc-arm-none-eabi 4.9-2014q4). +# Usage: tools/docker.sh cmake --build build/evk +set -euo pipefail +ROOT=$(cd "$(dirname "$0")/.." && pwd) +docker image inspect ninlight-build >/dev/null 2>&1 || docker build --platform linux/amd64 -t ninlight-build "$ROOT/docker" +exec docker run --rm --platform linux/amd64 -v "$ROOT":/work -w /work ninlight-build "$@" diff --git a/tools/flash.sh b/tools/flash.sh new file mode 100755 index 0000000..3b58853 --- /dev/null +++ b/tools/flash.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Flashes build//ninlight_.fls over the serial port (runs natively on the Mac). +# Usage: tools/flash.sh +set -euo pipefail +DEV=${1:?usage: tools/flash.sh } +PORT=${2:?usage: tools/flash.sh } +ROOT=$(cd "$(dirname "$0")/.." && pwd) +TOOL="$ROOT/build/host-tools/wm_tool" +if [ ! -x "$TOOL" ] || [ "$ROOT/sdk/tools/wm_tool.c" -nt "$TOOL" ]; then + mkdir -p "$(dirname "$TOOL")" + cc -O2 -w -o "$TOOL" "$ROOT/sdk/tools/wm_tool.c" -lpthread +fi +"$TOOL" -c "$PORT" -ds 921600 -rs rts -dl "$ROOT/build/$DEV/ninlight_$DEV.fls" diff --git a/tools/monitor.sh b/tools/monitor.sh new file mode 100755 index 0000000..8f985cf --- /dev/null +++ b/tools/monitor.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Serial console (115200 8N1, local echo). Usage: tools/monitor.sh /dev/cu.usbserial-XXXX +set -euo pipefail +PORT=${1:?usage: tools/monitor.sh } +exec uv run --with pyserial python -m serial.tools.miniterm --echo --eol CR "$PORT" 115200 From 6e6cfeaf3a734308f5003bb8a49c2850e5c58b5f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:00:02 +0200 Subject: [PATCH 005/150] =?UTF-8?q?feat:=20Board-Tabelle=20f=C3=BCr=20alle?= =?UTF-8?q?=20acht=20Ger=C3=A4tetypen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 +- src/board/nl_board.c | 50 +++++++++++++++++++ src/board/nl_board.h | 44 +++++++++++++++++ test/CMakeLists.txt | 1 + test/test_board.c | 114 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/board/nl_board.c create mode 100644 src/board/nl_board.h create mode 100644 test/test_board.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f21e29c..b700633 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,8 @@ set(NL_FW_SOURCES src/util/nl_sha256.c src/util/nl_crc.c src/sys/fwup_hook_default.c - src/sys/sdk_stubs.c) + src/sys/sdk_stubs.c + src/board/nl_board.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS}) diff --git a/src/board/nl_board.c b/src/board/nl_board.c new file mode 100644 index 0000000..a0140a1 --- /dev/null +++ b/src/board/nl_board.c @@ -0,0 +1,50 @@ +#include "board/nl_board.h" + +#include + +#define NONE {-1, NL_PIN_NONE, false, false} +#define CH(c, p) {(c), (p), false, false} +#define CHL(c, p) {(c), (p), true, false} /* active low */ +#define CHA(c, p) {(c), (p), false, true} /* antiphase */ + +/* SM2135EH current codes: RGB high nibble (15 mA = 1, 40 mA = 6, 45 mA = 7), + * WY low nibble (15 mA = 3, 35 mA = 7). Values from qcl_build_config_lamp.h. */ +#define NO_SM NL_PIN_NONE, NL_PIN_NONE, 0 + +const nl_board_t nl_boards[] = { + /* R G B CW WW */ + {"evk", "W600 EVK v1.2", NL_DRV_PWM, true, 2700, 6500, 5000, + {CHL(0, NL_PB(18)), CHL(2, NL_PB(16)), CHL(3, NL_PB(15)), CHL(1, NL_PB(13)), CHL(4, NL_PB(8))}, NO_SM}, + {"e27_rgb", "Klyqa E27 RGB CW/WW", NL_DRV_SM2135EH, true, 2700, 6500, 0, + {NONE, NONE, NONE, NONE, NONE}, NL_PB(13), NL_PB(8), 0x17}, + {"e14_rgb", "Klyqa E14 RGB CW/WW", NL_DRV_SM2135EH, true, 2700, 6500, 0, + {NONE, NONE, NONE, NONE, NONE}, NL_PB(8), NL_PB(13), 0x73}, + {"gu10_rgb", "Klyqa GU10 RGB CW/WW", NL_DRV_SM2135EH, true, 2700, 6500, 0, + {NONE, NONE, NONE, NONE, NONE}, NL_PB(8), NL_PB(13), 0x63}, + {"e27_cw", "Klyqa E27 CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, + {NONE, NONE, NONE, CHA(1, NL_PB(13)), CH(4, NL_PB(8))}, NO_SM}, + {"e14_cw", "Klyqa E14 CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, + {NONE, NONE, NONE, CH(2, NL_PB(16)), CH(4, NL_PB(8))}, NO_SM}, + {"gu10_cw", "Klyqa GU10 CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, + {NONE, NONE, NONE, CH(2, NL_PB(16)), CH(4, NL_PB(8))}, NO_SM}, + /* G95: pin assignment still to be verified on a series bulb (old note: PB16=WW, PB15=CW). */ + {"g95_cw", "Klyqa G95 Vintage CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, + {NONE, NONE, NONE, CH(1, NL_PB(13)), CH(3, NL_PB(15))}, NO_SM}, +}; + +const size_t nl_boards_count = sizeof(nl_boards) / sizeof(nl_boards[0]); + +const nl_board_t *nl_board_find(const char *id) +{ + for (size_t i = 0; i < nl_boards_count; i++) + if (strcmp(nl_boards[i].id, id) == 0) + return &nl_boards[i]; + return NULL; +} + +#ifdef NINLIGHT_DEVICE_ID +const nl_board_t *nl_board_get(void) +{ + return nl_board_find(NINLIGHT_DEVICE_ID); +} +#endif diff --git a/src/board/nl_board.h b/src/board/nl_board.h new file mode 100644 index 0000000..0a26785 --- /dev/null +++ b/src/board/nl_board.h @@ -0,0 +1,44 @@ +#ifndef NL_BOARD_H +#define NL_BOARD_H + +#include +#include +#include + +/* GPIO numbers are identical to enum tls_io_name of the W600 SDK (PA_00 = 0, PB_00 = 16). */ +#define NL_PB(n) ((uint8_t)(16u + (n))) +#define NL_PIN_NONE 0xFFu + +typedef enum { NL_CH_R = 0, NL_CH_G, NL_CH_B, NL_CH_CW, NL_CH_WW, NL_CH_COUNT } nl_channel_t; +typedef enum { NL_DRV_PWM = 0, NL_DRV_SM2135EH } nl_led_driver_t; + +typedef struct { + int8_t pwm_ch; /* PWM unit channel 0..4, -1 = channel not populated */ + uint8_t pin; /* NL_PB(x) or NL_PIN_NONE */ + bool active_low; /* LED lights up while the pin is low */ + bool antiphase; /* inverted PWM output, shifts the pulse (e27_cw cold white) */ +} nl_pwm_chan_t; + +typedef struct { + const char *id; + const char *name; + nl_led_driver_t driver; + bool has_rgb; + uint16_t ct_min; /* Kelvin, warmest */ + uint16_t ct_max; /* Kelvin, coldest */ + uint32_t pwm_freq_hz; /* PWM boards */ + nl_pwm_chan_t pwm[NL_CH_COUNT]; + uint8_t sm_clk_pin; /* SM2135EH boards */ + uint8_t sm_dat_pin; + uint8_t sm_current; /* (rgb_code << 4) | wy_code, see SM2135EH datasheet */ +} nl_board_t; + +extern const nl_board_t nl_boards[]; +extern const size_t nl_boards_count; + +const nl_board_t *nl_board_find(const char *id); + +/* Board selected at build time via NINLIGHT_DEVICE_ID. */ +const nl_board_t *nl_board_get(void); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cab7b54..adbcef2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,3 +24,4 @@ endfunction() nl_test(test_sha256 test_sha256.c ${NL_SRC}/util/nl_sha256.c) nl_test(test_crc test_crc.c ${NL_SRC}/util/nl_crc.c) +nl_test(test_board test_board.c ${NL_SRC}/board/nl_board.c) diff --git a/test/test_board.c b/test/test_board.c new file mode 100644 index 0000000..0ebbbdf --- /dev/null +++ b/test/test_board.c @@ -0,0 +1,114 @@ +#include +#include "unity.h" +#include "board/nl_board.h" + +void setUp(void) {} +void tearDown(void) {} + +static const char *IDS[] = {"evk", "e27_rgb", "e14_rgb", "gu10_rgb", "e27_cw", "e14_cw", "gu10_cw", "g95_cw"}; + +static void test_all_devices_present(void) +{ + TEST_ASSERT_EQUAL_UINT(8, nl_boards_count); + for (size_t i = 0; i < 8; i++) { + const nl_board_t *b = nl_board_find(IDS[i]); + TEST_ASSERT_NOT_NULL_MESSAGE(b, IDS[i]); + TEST_ASSERT_EQUAL_STRING(IDS[i], b->id); + TEST_ASSERT_NOT_NULL(b->name); + } +} + +static void test_unknown_is_null(void) +{ + TEST_ASSERT_NULL(nl_board_find("e27")); + TEST_ASSERT_NULL(nl_board_find("")); +} + +static void test_rgb_flags_and_ct_ranges(void) +{ + for (size_t i = 0; i < nl_boards_count; i++) { + const nl_board_t *b = &nl_boards[i]; + bool rgb = strstr(b->id, "rgb") != NULL || strcmp(b->id, "evk") == 0; + TEST_ASSERT_EQUAL_MESSAGE(rgb, b->has_rgb, b->id); + TEST_ASSERT_EQUAL_UINT16(rgb ? 2700 : 2500, b->ct_min); + TEST_ASSERT_EQUAL_UINT16(rgb ? 6500 : 5000, b->ct_max); + } +} + +static void test_pwm_boards_consistent(void) +{ + for (size_t i = 0; i < nl_boards_count; i++) { + const nl_board_t *b = &nl_boards[i]; + if (b->driver != NL_DRV_PWM) + continue; + TEST_ASSERT_TRUE(b->pwm_freq_hz > 0); + TEST_ASSERT_TRUE(b->pwm[NL_CH_CW].pwm_ch >= 0); + TEST_ASSERT_TRUE(b->pwm[NL_CH_WW].pwm_ch >= 0); + for (int c = NL_CH_R; c <= NL_CH_B; c++) + TEST_ASSERT_EQUAL_MESSAGE(b->has_rgb, b->pwm[c].pwm_ch >= 0, b->id); + for (int c = 0; c < NL_CH_COUNT; c++) { + if (b->pwm[c].pwm_ch < 0) + continue; + TEST_ASSERT_TRUE(b->pwm[c].pwm_ch <= 4); + for (int d = c + 1; d < NL_CH_COUNT; d++) { + if (b->pwm[d].pwm_ch < 0) + continue; + TEST_ASSERT_NOT_EQUAL(b->pwm[c].pwm_ch, b->pwm[d].pwm_ch); + TEST_ASSERT_NOT_EQUAL(b->pwm[c].pin, b->pwm[d].pin); + } + } + } +} + +static void test_sm_boards_consistent(void) +{ + for (size_t i = 0; i < nl_boards_count; i++) { + const nl_board_t *b = &nl_boards[i]; + if (b->driver != NL_DRV_SM2135EH) + continue; + TEST_ASSERT_TRUE(b->has_rgb); + TEST_ASSERT_NOT_EQUAL(NL_PIN_NONE, b->sm_clk_pin); + TEST_ASSERT_NOT_EQUAL(NL_PIN_NONE, b->sm_dat_pin); + TEST_ASSERT_NOT_EQUAL(b->sm_clk_pin, b->sm_dat_pin); + TEST_ASSERT_NOT_EQUAL(0, b->sm_current); + } +} + +static void test_known_values_from_old_firmware(void) +{ + const nl_board_t *evk = nl_board_find("evk"); + TEST_ASSERT_EQUAL(NL_DRV_PWM, evk->driver); + TEST_ASSERT_EQUAL_UINT32(5000, evk->pwm_freq_hz); + TEST_ASSERT_EQUAL_INT8(0, evk->pwm[NL_CH_R].pwm_ch); + TEST_ASSERT_EQUAL_UINT8(NL_PB(18), evk->pwm[NL_CH_R].pin); + TEST_ASSERT_TRUE(evk->pwm[NL_CH_R].active_low); + + const nl_board_t *e27 = nl_board_find("e27_rgb"); + TEST_ASSERT_EQUAL(NL_DRV_SM2135EH, e27->driver); + TEST_ASSERT_EQUAL_UINT8(NL_PB(13), e27->sm_clk_pin); + TEST_ASSERT_EQUAL_UINT8(NL_PB(8), e27->sm_dat_pin); + TEST_ASSERT_EQUAL_HEX8(0x17, e27->sm_current); + TEST_ASSERT_EQUAL_HEX8(0x73, nl_board_find("e14_rgb")->sm_current); + TEST_ASSERT_EQUAL_HEX8(0x63, nl_board_find("gu10_rgb")->sm_current); + + const nl_board_t *e27cw = nl_board_find("e27_cw"); + TEST_ASSERT_TRUE(e27cw->pwm[NL_CH_CW].antiphase); + TEST_ASSERT_EQUAL_UINT8(NL_PB(13), e27cw->pwm[NL_CH_CW].pin); + TEST_ASSERT_EQUAL_UINT32(600, e27cw->pwm_freq_hz); + + const nl_board_t *g95 = nl_board_find("g95_cw"); + TEST_ASSERT_EQUAL_UINT8(NL_PB(15), g95->pwm[NL_CH_WW].pin); + TEST_ASSERT_EQUAL_INT8(3, g95->pwm[NL_CH_WW].pwm_ch); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_all_devices_present); + RUN_TEST(test_unknown_is_null); + RUN_TEST(test_rgb_flags_and_ct_ranges); + RUN_TEST(test_pwm_boards_consistent); + RUN_TEST(test_sm_boards_consistent); + RUN_TEST(test_known_values_from_old_firmware); + return UNITY_END(); +} From 42eeb0e0ebd30bb5e86c4a77bec774520c4cbf25 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:06:19 +0200 Subject: [PATCH 006/150] feat: Farbrechnung mit CIE-Helligkeit und CW/WW-Mischung --- CMakeLists.txt | 3 +- src/light/nl_color.c | 73 ++++++++++++++++++++++++ src/light/nl_color.h | 19 +++++++ src/light/nl_light_types.h | 29 ++++++++++ test/CMakeLists.txt | 1 + test/test_color.c | 112 +++++++++++++++++++++++++++++++++++++ 6 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 src/light/nl_color.c create mode 100644 src/light/nl_color.h create mode 100644 src/light/nl_light_types.h create mode 100644 test/test_color.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b700633..09110c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,8 @@ set(NL_FW_SOURCES src/util/nl_crc.c src/sys/fwup_hook_default.c src/sys/sdk_stubs.c - src/board/nl_board.c) + src/board/nl_board.c + src/light/nl_color.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS}) diff --git a/src/light/nl_color.c b/src/light/nl_color.c new file mode 100644 index 0000000..e559a3a --- /dev/null +++ b/src/light/nl_color.c @@ -0,0 +1,73 @@ +#include "light/nl_color.h" + +uint16_t nl_cie_level(uint16_t bri) +{ + float lightness, y; + uint32_t v; + + if (bri == 0) + return 0; + if (bri >= NL_BRI_SCALE) + return 65535; + lightness = (float)bri / 100.0f; /* 0..100 */ + if (lightness <= 8.0f) { + y = lightness / 903.3f; + } else { + float t = (lightness + 16.0f) / 116.0f; + y = t * t * t; + } + v = (uint32_t)(y * 65535.0f + 0.5f); + return (uint16_t)(v == 0 ? 1 : (v > 65535 ? 65535 : v)); +} + +uint16_t nl_ct_cw_fraction(uint16_t ct, uint16_t ct_min, uint16_t ct_max) +{ + uint32_t m, mw, mc; + + if (ct <= ct_min) + return 0; + if (ct >= ct_max) + return 65535; + m = 1000000u / ct; + mw = 1000000u / ct_min; + mc = 1000000u / ct_max; + return (uint16_t)(((mw - m) * 65535u) / (mw - mc)); +} + +static uint8_t to8(uint32_t lin) +{ + return (uint8_t)((lin * 255u + 32767u) / 65535u); +} + +void nl_visual_to_channels(const nl_visual_t *v, uint16_t ct_min, uint16_t ct_max, uint8_t out[NL_CH_COUNT]) +{ + uint32_t level = nl_cie_level(v->bri); + + for (int i = 0; i < NL_CH_COUNT; i++) + out[i] = 0; + if (level == 0) + return; + + if (v->mode == NL_MODE_COLOR) { + uint32_t lin[3]; + int maxi = 0; + lin[0] = level * v->r / 255u; + lin[1] = level * v->g / 255u; + lin[2] = level * v->b / 255u; + for (int i = 0; i < 3; i++) { + out[NL_CH_R + i] = to8(lin[i]); + if (lin[i] > lin[maxi]) + maxi = i; + } + /* keep the lowest brightness step visible */ + if (!out[NL_CH_R] && !out[NL_CH_G] && !out[NL_CH_B] && lin[maxi] > 0) + out[NL_CH_R + maxi] = 1; + } else { + uint32_t cw = level * nl_ct_cw_fraction(v->ct, ct_min, ct_max) / 65535u; + uint32_t ww = level - cw; + out[NL_CH_CW] = to8(cw); + out[NL_CH_WW] = to8(ww); + if (!out[NL_CH_CW] && !out[NL_CH_WW]) + out[cw >= ww ? NL_CH_CW : NL_CH_WW] = 1; + } +} diff --git a/src/light/nl_color.h b/src/light/nl_color.h new file mode 100644 index 0000000..1d727b0 --- /dev/null +++ b/src/light/nl_color.h @@ -0,0 +1,19 @@ +#ifndef NL_COLOR_H +#define NL_COLOR_H + +#include + +#include "board/nl_board.h" +#include "light/nl_light_types.h" + +/* Perceived brightness (0..NL_BRI_SCALE) to linear light (0..65535), CIE 1931 lightness. + * Every bri > 0 yields at least 1. */ +uint16_t nl_cie_level(uint16_t bri); + +/* Share of cold white (0..65535) for ct, linear in mired between ct_min and ct_max. */ +uint16_t nl_ct_cw_fraction(uint16_t ct, uint16_t ct_min, uint16_t ct_max); + +/* 8 bit channel values for the LED drivers. In white mode cw + ww equals the brightness. */ +void nl_visual_to_channels(const nl_visual_t *v, uint16_t ct_min, uint16_t ct_max, uint8_t out[NL_CH_COUNT]); + +#endif diff --git a/src/light/nl_light_types.h b/src/light/nl_light_types.h new file mode 100644 index 0000000..18bf2e6 --- /dev/null +++ b/src/light/nl_light_types.h @@ -0,0 +1,29 @@ +#ifndef NL_LIGHT_TYPES_H +#define NL_LIGHT_TYPES_H + +#include +#include + +#define NL_BRI_SCALE 10000u /* visual brightness fixed point: 10000 = 100.00 % */ + +typedef enum { NL_MODE_COLOR = 0, NL_MODE_WHITE = 1 } nl_light_mode_t; +typedef enum { NL_POB_LAST = 0, NL_POB_ON = 1, NL_POB_OFF = 2 } nl_pob_t; + +/* Light state as seen by the API (brightness in percent). */ +typedef struct { + bool on; + uint8_t brightness; /* 1..100 */ + nl_light_mode_t mode; + uint8_t r, g, b; + uint16_t ct; /* Kelvin */ +} nl_light_state_t; + +/* What is currently visible; bri = 0 means dark. */ +typedef struct { + uint16_t bri; /* 0..NL_BRI_SCALE */ + nl_light_mode_t mode; + uint8_t r, g, b; + uint16_t ct; +} nl_visual_t; + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index adbcef2..4b174c5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,3 +25,4 @@ endfunction() nl_test(test_sha256 test_sha256.c ${NL_SRC}/util/nl_sha256.c) nl_test(test_crc test_crc.c ${NL_SRC}/util/nl_crc.c) nl_test(test_board test_board.c ${NL_SRC}/board/nl_board.c) +nl_test(test_color test_color.c ${NL_SRC}/light/nl_color.c) diff --git a/test/test_color.c b/test/test_color.c new file mode 100644 index 0000000..6074f3a --- /dev/null +++ b/test/test_color.c @@ -0,0 +1,112 @@ +#include "unity.h" +#include "light/nl_color.h" + +void setUp(void) {} +void tearDown(void) {} + +static nl_visual_t vis(uint16_t bri, nl_light_mode_t mode, uint8_t r, uint8_t g, uint8_t b, uint16_t ct) +{ + nl_visual_t v = {bri, mode, r, g, b, ct}; + return v; +} + +static void test_cie_endpoints(void) +{ + TEST_ASSERT_EQUAL_UINT16(0, nl_cie_level(0)); + TEST_ASSERT_EQUAL_UINT16(65535, nl_cie_level(10000)); + TEST_ASSERT_EQUAL_UINT16(65535, nl_cie_level(12000)); + TEST_ASSERT_UINT16_WITHIN(1, 73, nl_cie_level(100)); /* 1 % -> L=1 -> Y=1/903.3 */ + TEST_ASSERT_UINT16_WITHIN(3, 12071, nl_cie_level(5000)); /* 50 % -> ((66/116)^3) */ +} + +static void test_cie_monotonic(void) +{ + uint16_t prev = 0; + for (uint16_t b = 1; b <= 10000; b++) { + uint16_t v = nl_cie_level(b); + TEST_ASSERT_TRUE(v >= prev); + TEST_ASSERT_TRUE(v > 0); + prev = v; + } +} + +static void test_ct_fraction(void) +{ + TEST_ASSERT_EQUAL_UINT16(0, nl_ct_cw_fraction(2700, 2700, 6500)); + TEST_ASSERT_EQUAL_UINT16(0, nl_ct_cw_fraction(2000, 2700, 6500)); + TEST_ASSERT_EQUAL_UINT16(65535, nl_ct_cw_fraction(6500, 2700, 6500)); + TEST_ASSERT_EQUAL_UINT16(65535, nl_ct_cw_fraction(9000, 2700, 6500)); + /* mired midpoint of 2700 K (370.4) and 6500 K (153.8) is 262.1 mired = 3815 K */ + TEST_ASSERT_UINT16_WITHIN(400, 32768, nl_ct_cw_fraction(3815, 2700, 6500)); +} + +static void test_white_endpoints(void) +{ + uint8_t ch[NL_CH_COUNT]; + nl_visual_t v = vis(10000, NL_MODE_WHITE, 0, 0, 0, 2700); + nl_visual_to_channels(&v, 2700, 6500, ch); + TEST_ASSERT_EQUAL_UINT8(255, ch[NL_CH_WW]); + TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_CW]); + v.ct = 6500; + nl_visual_to_channels(&v, 2700, 6500, ch); + TEST_ASSERT_EQUAL_UINT8(255, ch[NL_CH_CW]); + TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_WW]); +} + +static void test_white_mix_keeps_total(void) +{ + uint8_t ch[NL_CH_COUNT]; + nl_visual_t v = vis(10000, NL_MODE_WHITE, 0, 0, 0, 3815); + nl_visual_to_channels(&v, 2700, 6500, ch); + TEST_ASSERT_UINT8_WITHIN(3, 128, ch[NL_CH_CW]); + TEST_ASSERT_INT_WITHIN(1, 255, ch[NL_CH_CW] + ch[NL_CH_WW]); + TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_R]); + TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_G]); + TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_B]); +} + +static void test_color_mode(void) +{ + uint8_t ch[NL_CH_COUNT]; + nl_visual_t v = vis(10000, NL_MODE_COLOR, 255, 120, 0, 2700); + nl_visual_to_channels(&v, 2700, 6500, ch); + TEST_ASSERT_EQUAL_UINT8(255, ch[NL_CH_R]); + TEST_ASSERT_UINT8_WITHIN(1, 120, ch[NL_CH_G]); + TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_B]); + TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_CW]); + TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_WW]); +} + +static void test_one_percent_is_visible(void) +{ + uint8_t ch[NL_CH_COUNT]; + nl_visual_t v = vis(100, NL_MODE_COLOR, 255, 0, 0, 2700); + nl_visual_to_channels(&v, 2700, 6500, ch); + TEST_ASSERT_EQUAL_UINT8(1, ch[NL_CH_R]); + v = vis(100, NL_MODE_WHITE, 0, 0, 0, 3000); + nl_visual_to_channels(&v, 2700, 6500, ch); + TEST_ASSERT_EQUAL_INT(1, ch[NL_CH_CW] + ch[NL_CH_WW]); +} + +static void test_zero_brightness_is_dark(void) +{ + uint8_t ch[NL_CH_COUNT]; + nl_visual_t v = vis(0, NL_MODE_COLOR, 255, 255, 255, 2700); + nl_visual_to_channels(&v, 2700, 6500, ch); + for (int i = 0; i < NL_CH_COUNT; i++) + TEST_ASSERT_EQUAL_UINT8(0, ch[i]); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_cie_endpoints); + RUN_TEST(test_cie_monotonic); + RUN_TEST(test_ct_fraction); + RUN_TEST(test_white_endpoints); + RUN_TEST(test_white_mix_keeps_total); + RUN_TEST(test_color_mode); + RUN_TEST(test_one_percent_is_visible); + RUN_TEST(test_zero_brightness_is_dark); + return UNITY_END(); +} From 73a7f31cb6dc6901cc4badb9c1a3cf989dc525da Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:11:23 +0200 Subject: [PATCH 007/150] feat: Licht-Engine mit Transitionen, Moduswechsel und Effekten --- CMakeLists.txt | 3 +- src/light/nl_engine.c | 178 +++++++++++++++++++++++++++++++++++++++ src/light/nl_engine.h | 31 +++++++ test/CMakeLists.txt | 1 + test/test_engine.c | 188 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 src/light/nl_engine.c create mode 100644 src/light/nl_engine.h create mode 100644 test/test_engine.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 09110c7..69711e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,8 @@ set(NL_FW_SOURCES src/sys/fwup_hook_default.c src/sys/sdk_stubs.c src/board/nl_board.c - src/light/nl_color.c) + src/light/nl_color.c + src/light/nl_engine.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS}) diff --git a/src/light/nl_engine.c b/src/light/nl_engine.c new file mode 100644 index 0000000..3de3f41 --- /dev/null +++ b/src/light/nl_engine.c @@ -0,0 +1,178 @@ +#include "light/nl_engine.h" + +#include "light/nl_color.h" + +typedef struct { + nl_visual_t from; + nl_visual_t to; + uint32_t dur; + uint32_t elapsed; +} seg_t; + +static nl_engine_cfg_t s_cfg; +static nl_visual_t s_cur; +static seg_t s_seg[2]; +static uint8_t s_nseg; +static uint8_t s_iseg; +static uint8_t s_blink_left; /* remaining blink phases; even = on, odd = off */ +static uint32_t s_blink_t; +static bool s_pulse; +static uint32_t s_pulse_t; + +static nl_visual_t to_visual(const nl_light_state_t *s) +{ + nl_visual_t v; + v.bri = s->on ? (uint16_t)(s->brightness * 100u) : 0; + v.mode = s->mode; + v.r = s->r; + v.g = s->g; + v.b = s->b; + v.ct = s->ct; + return v; +} + +static int32_t lerp(int32_t a, int32_t b, uint32_t e, uint32_t d) +{ + return a + (int32_t)(((int64_t)(b - a) * (int64_t)e) / (int64_t)d); +} + +static void push(const nl_visual_t *from, const nl_visual_t *to, uint32_t dur) +{ + seg_t *s = &s_seg[s_nseg++]; + s->from = *from; + s->to = *to; + s->dur = dur; + s->elapsed = 0; +} + +static void emit(void) +{ + uint8_t ch[NL_CH_COUNT]; + nl_visual_t v; + + if (s_blink_left) { + v.bri = (s_blink_left % 2 == 0) ? NL_BRI_SCALE : 0; + v.mode = NL_MODE_WHITE; + v.r = v.g = v.b = 0; + v.ct = s_cfg.ct_min; + } else if (s_pulse) { + uint32_t half = NL_PULSE_PERIOD_MS / 2; + uint32_t ph = s_pulse_t % NL_PULSE_PERIOD_MS; + uint32_t tri = ph < half ? ph : NL_PULSE_PERIOD_MS - ph; + v.bri = (uint16_t)(1000u + tri * 5000u / half); /* 10 % .. 60 % */ + v.mode = NL_MODE_WHITE; + v.r = v.g = v.b = 0; + v.ct = s_cfg.ct_min; + } else { + v = s_cur; + } + nl_visual_to_channels(&v, s_cfg.ct_min, s_cfg.ct_max, ch); + s_cfg.output(ch, s_cfg.ctx); +} + +void nl_engine_init(const nl_engine_cfg_t *cfg) +{ + s_cfg = *cfg; + s_cur.bri = 0; + s_cur.mode = NL_MODE_WHITE; + s_cur.r = s_cur.g = s_cur.b = 255; + s_cur.ct = cfg->ct_min; + s_nseg = s_iseg = 0; + s_blink_left = 0; + s_blink_t = 0; + s_pulse = false; + s_pulse_t = 0; + emit(); +} + +void nl_engine_set(const nl_light_state_t *target, uint32_t transition_ms) +{ + nl_visual_t tgt = to_visual(target); + + s_nseg = 0; + s_iseg = 0; + if (transition_ms == 0) { + s_cur = tgt; + emit(); + return; + } + if (s_cur.bri == 0) { + nl_visual_t start = tgt; + start.bri = 0; + push(&start, &tgt, transition_ms); + } else if (tgt.bri == 0) { + nl_visual_t dark = s_cur; + dark.bri = 0; + push(&s_cur, &dark, transition_ms); + push(&tgt, &tgt, 0); + } else if (tgt.mode != s_cur.mode) { + nl_visual_t a = s_cur; + nl_visual_t b = tgt; + a.bri = 0; + b.bri = 0; + push(&s_cur, &a, transition_ms / 2); + push(&b, &tgt, transition_ms - transition_ms / 2); + } else { + push(&s_cur, &tgt, transition_ms); + } +} + +void nl_engine_tick(uint32_t elapsed_ms) +{ + uint32_t left = elapsed_ms; + + while (s_iseg < s_nseg) { + seg_t *s = &s_seg[s_iseg]; + if (s->elapsed + left >= s->dur) { + left -= s->dur - s->elapsed; + s->elapsed = s->dur; + s_cur = s->to; + s_iseg++; + continue; + } + s->elapsed += left; + left = 0; + s_cur.bri = (uint16_t)lerp(s->from.bri, s->to.bri, s->elapsed, s->dur); + s_cur.r = (uint8_t)lerp(s->from.r, s->to.r, s->elapsed, s->dur); + s_cur.g = (uint8_t)lerp(s->from.g, s->to.g, s->elapsed, s->dur); + s_cur.b = (uint8_t)lerp(s->from.b, s->to.b, s->elapsed, s->dur); + s_cur.ct = (uint16_t)lerp(s->from.ct, s->to.ct, s->elapsed, s->dur); + s_cur.mode = s->to.mode; + break; + } + + if (s_blink_left) { + s_blink_t += elapsed_ms; + while (s_blink_left && s_blink_t >= NL_BLINK_PHASE_MS) { + s_blink_t -= NL_BLINK_PHASE_MS; + s_blink_left--; + } + } + if (s_pulse) + s_pulse_t += elapsed_ms; + emit(); +} + +bool nl_engine_idle(void) +{ + return s_iseg >= s_nseg && !s_blink_left && !s_pulse; +} + +void nl_engine_current(nl_visual_t *out) +{ + *out = s_cur; +} + +void nl_engine_blink(uint8_t count) +{ + s_blink_left = (uint8_t)(count * 2u); + s_blink_t = 0; + emit(); +} + +void nl_engine_pulse(bool enable) +{ + s_pulse = enable; + s_pulse_t = 0; + emit(); +} diff --git a/src/light/nl_engine.h b/src/light/nl_engine.h new file mode 100644 index 0000000..1c8f59c --- /dev/null +++ b/src/light/nl_engine.h @@ -0,0 +1,31 @@ +#ifndef NL_ENGINE_H +#define NL_ENGINE_H + +#include +#include + +#include "board/nl_board.h" +#include "light/nl_light_types.h" + +#define NL_BLINK_PHASE_MS 300u +#define NL_PULSE_PERIOD_MS 3000u + +typedef void (*nl_output_fn)(const uint8_t ch[NL_CH_COUNT], void *ctx); + +typedef struct { + uint16_t ct_min; + uint16_t ct_max; + nl_output_fn output; + void *ctx; +} nl_engine_cfg_t; + +/* Not thread safe: callers serialise access. */ +void nl_engine_init(const nl_engine_cfg_t *cfg); +void nl_engine_set(const nl_light_state_t *target, uint32_t transition_ms); +void nl_engine_tick(uint32_t elapsed_ms); +bool nl_engine_idle(void); +void nl_engine_current(nl_visual_t *out); +void nl_engine_blink(uint8_t count); +void nl_engine_pulse(bool enable); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4b174c5..10dbdc5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,3 +26,4 @@ nl_test(test_sha256 test_sha256.c ${NL_SRC}/util/nl_sha256.c) nl_test(test_crc test_crc.c ${NL_SRC}/util/nl_crc.c) nl_test(test_board test_board.c ${NL_SRC}/board/nl_board.c) nl_test(test_color test_color.c ${NL_SRC}/light/nl_color.c) +nl_test(test_engine test_engine.c ${NL_SRC}/light/nl_engine.c ${NL_SRC}/light/nl_color.c) diff --git a/test/test_engine.c b/test/test_engine.c new file mode 100644 index 0000000..e5af686 --- /dev/null +++ b/test/test_engine.c @@ -0,0 +1,188 @@ +#include +#include "unity.h" +#include "light/nl_engine.h" + +static uint8_t last[NL_CH_COUNT]; +static int calls; + +static void out(const uint8_t ch[NL_CH_COUNT], void *ctx) +{ + (void)ctx; + memcpy(last, ch, NL_CH_COUNT); + calls++; +} + +static nl_light_state_t st(bool on, uint8_t bri, nl_light_mode_t m, uint8_t r, uint8_t g, uint8_t b, uint16_t ct) +{ + nl_light_state_t s = {on, bri, m, r, g, b, ct}; + return s; +} + +static nl_visual_t cur(void) +{ + nl_visual_t v; + nl_engine_current(&v); + return v; +} + +static int sum(void) +{ + int s = 0; + for (int i = 0; i < NL_CH_COUNT; i++) + s += last[i]; + return s; +} + +void setUp(void) +{ + nl_engine_cfg_t cfg = {2700, 6500, out, NULL}; + memset(last, 0xAA, sizeof last); + calls = 0; + nl_engine_init(&cfg); +} + +void tearDown(void) {} + +static void test_init_is_dark_and_idle(void) +{ + TEST_ASSERT_TRUE(calls >= 1); + TEST_ASSERT_EQUAL_INT(0, sum()); + TEST_ASSERT_EQUAL_UINT16(0, cur().bri); + TEST_ASSERT_TRUE(nl_engine_idle()); +} + +static void test_instant_set(void) +{ + nl_light_state_t s = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 2700); + nl_engine_set(&s, 0); + TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]); + TEST_ASSERT_EQUAL_UINT8(0, last[NL_CH_CW]); + TEST_ASSERT_TRUE(nl_engine_idle()); +} + +static void test_fade_in_is_linear_in_brightness(void) +{ + nl_light_state_t s = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 2700); + nl_engine_set(&s, 1000); + nl_engine_tick(500); + TEST_ASSERT_EQUAL_UINT16(5000, cur().bri); + TEST_ASSERT_FALSE(nl_engine_idle()); + nl_engine_tick(500); + TEST_ASSERT_EQUAL_UINT16(10000, cur().bri); + TEST_ASSERT_TRUE(nl_engine_idle()); + TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]); +} + +static void test_fade_out_keeps_current_color(void) +{ + nl_light_state_t red = st(true, 100, NL_MODE_COLOR, 255, 0, 0, 2700); + nl_light_state_t off = st(false, 100, NL_MODE_WHITE, 0, 0, 0, 4000); + nl_engine_set(&red, 0); + nl_engine_set(&off, 1000); + nl_engine_tick(500); + TEST_ASSERT_EQUAL(NL_MODE_COLOR, cur().mode); + TEST_ASSERT_EQUAL_UINT16(5000, cur().bri); + TEST_ASSERT_TRUE(last[NL_CH_R] > 0); + nl_engine_tick(500); + TEST_ASSERT_EQUAL_INT(0, sum()); + TEST_ASSERT_TRUE(nl_engine_idle()); + TEST_ASSERT_EQUAL(NL_MODE_WHITE, cur().mode); /* target values applied at the end */ +} + +static void test_mode_switch_passes_through_dark(void) +{ + nl_light_state_t red = st(true, 100, NL_MODE_COLOR, 255, 0, 0, 2700); + nl_light_state_t white = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 4000); + nl_engine_set(&red, 0); + nl_engine_set(&white, 1000); + nl_engine_tick(250); + TEST_ASSERT_EQUAL(NL_MODE_COLOR, cur().mode); + TEST_ASSERT_EQUAL_UINT16(5000, cur().bri); + nl_engine_tick(250); + TEST_ASSERT_EQUAL_UINT16(0, cur().bri); + TEST_ASSERT_EQUAL_INT(0, sum()); + nl_engine_tick(250); + TEST_ASSERT_EQUAL(NL_MODE_WHITE, cur().mode); + TEST_ASSERT_EQUAL_UINT16(5000, cur().bri); + nl_engine_tick(250); + TEST_ASSERT_EQUAL_UINT16(10000, cur().bri); + TEST_ASSERT_TRUE(nl_engine_idle()); +} + +static void test_leftover_time_carries_into_next_segment(void) +{ + nl_light_state_t red = st(true, 100, NL_MODE_COLOR, 255, 0, 0, 2700); + nl_light_state_t white = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 4000); + nl_engine_set(&red, 0); + nl_engine_set(&white, 1000); + nl_engine_tick(750); + TEST_ASSERT_EQUAL(NL_MODE_WHITE, cur().mode); + TEST_ASSERT_EQUAL_UINT16(5000, cur().bri); +} + +static void test_new_target_starts_from_current_value(void) +{ + nl_light_state_t full = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 2700); + nl_light_state_t dim = st(true, 20, NL_MODE_WHITE, 0, 0, 0, 2700); + nl_engine_set(&full, 1000); + nl_engine_tick(500); + nl_engine_set(&dim, 1000); + nl_engine_tick(500); + TEST_ASSERT_EQUAL_UINT16(3500, cur().bri); +} + +static void test_color_interpolates(void) +{ + nl_light_state_t red = st(true, 100, NL_MODE_COLOR, 255, 0, 0, 2700); + nl_light_state_t blue = st(true, 100, NL_MODE_COLOR, 0, 0, 255, 2700); + nl_engine_set(&red, 0); + nl_engine_set(&blue, 1000); + nl_engine_tick(500); + TEST_ASSERT_INT_WITHIN(1, 128, cur().r); + TEST_ASSERT_INT_WITHIN(1, 127, cur().b); +} + +static void test_blink_overrides_and_restores(void) +{ + nl_engine_blink(2); + TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]); + TEST_ASSERT_FALSE(nl_engine_idle()); + nl_engine_tick(300); + TEST_ASSERT_EQUAL_INT(0, sum()); + nl_engine_tick(300); + TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]); + nl_engine_tick(300); + TEST_ASSERT_EQUAL_INT(0, sum()); + nl_engine_tick(300); + TEST_ASSERT_EQUAL_INT(0, sum()); /* back to the (dark) state */ + TEST_ASSERT_TRUE(nl_engine_idle()); +} + +static void test_pulse(void) +{ + nl_engine_pulse(true); + uint8_t low = last[NL_CH_WW]; + TEST_ASSERT_TRUE(low > 0); + nl_engine_tick(1500); + TEST_ASSERT_TRUE(last[NL_CH_WW] > low); + TEST_ASSERT_FALSE(nl_engine_idle()); + nl_engine_pulse(false); + TEST_ASSERT_EQUAL_INT(0, sum()); + TEST_ASSERT_TRUE(nl_engine_idle()); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_init_is_dark_and_idle); + RUN_TEST(test_instant_set); + RUN_TEST(test_fade_in_is_linear_in_brightness); + RUN_TEST(test_fade_out_keeps_current_color); + RUN_TEST(test_mode_switch_passes_through_dark); + RUN_TEST(test_leftover_time_carries_into_next_segment); + RUN_TEST(test_new_target_starts_from_current_value); + RUN_TEST(test_color_interpolates); + RUN_TEST(test_blink_overrides_and_restores); + RUN_TEST(test_pulse); + return UNITY_END(); +} From 472d2398acced49da4916d6805d0f605c3c7e74f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:17:21 +0200 Subject: [PATCH 008/150] =?UTF-8?q?fix:=20Engine-Grenzen=20f=C3=BCr=20Segm?= =?UTF-8?q?ente=20und=20Blink-Anzahl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/light/nl_engine.c | 4 ++++ src/light/nl_engine.h | 1 + test/test_engine.c | 13 +++++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/light/nl_engine.c b/src/light/nl_engine.c index 3de3f41..039f89a 100644 --- a/src/light/nl_engine.c +++ b/src/light/nl_engine.c @@ -38,6 +38,8 @@ static int32_t lerp(int32_t a, int32_t b, uint32_t e, uint32_t d) static void push(const nl_visual_t *from, const nl_visual_t *to, uint32_t dur) { + if (s_nseg >= sizeof s_seg / sizeof s_seg[0]) + return; seg_t *s = &s_seg[s_nseg++]; s->from = *from; s->to = *to; @@ -165,6 +167,8 @@ void nl_engine_current(nl_visual_t *out) void nl_engine_blink(uint8_t count) { + if (count > NL_BLINK_MAX) + count = NL_BLINK_MAX; s_blink_left = (uint8_t)(count * 2u); s_blink_t = 0; emit(); diff --git a/src/light/nl_engine.h b/src/light/nl_engine.h index 1c8f59c..6a62cc4 100644 --- a/src/light/nl_engine.h +++ b/src/light/nl_engine.h @@ -8,6 +8,7 @@ #include "light/nl_light_types.h" #define NL_BLINK_PHASE_MS 300u +#define NL_BLINK_MAX 100u #define NL_PULSE_PERIOD_MS 3000u typedef void (*nl_output_fn)(const uint8_t ch[NL_CH_COUNT], void *ctx); diff --git a/test/test_engine.c b/test/test_engine.c index e5af686..84963a0 100644 --- a/test/test_engine.c +++ b/test/test_engine.c @@ -171,6 +171,18 @@ static void test_pulse(void) TEST_ASSERT_TRUE(nl_engine_idle()); } +static void test_blink_max_clamped(void) +{ + nl_engine_blink(200); /* > NL_BLINK_MAX, should clamp to 100 */ + TEST_ASSERT_FALSE(nl_engine_idle()); + /* After 99 blinks (99*2 phases = 198), should still be blinking */ + nl_engine_tick(300 * 99 * 2); + TEST_ASSERT_FALSE(nl_engine_idle()); + /* After 2 more phases to complete the last blink (total 100 blinks), should be idle */ + nl_engine_tick(300 * 2); + TEST_ASSERT_TRUE(nl_engine_idle()); +} + int main(void) { UNITY_BEGIN(); @@ -184,5 +196,6 @@ int main(void) RUN_TEST(test_color_interpolates); RUN_TEST(test_blink_overrides_and_restores); RUN_TEST(test_pulse); + RUN_TEST(test_blink_max_clamped); return UNITY_END(); } From e0bde7bee36aec5668393e8da9844170a0076a20 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:20:49 +0200 Subject: [PATCH 009/150] feat: Licht-Steuerung mit Fade-Regeln und Einschaltverhalten --- CMakeLists.txt | 4 +- src/light/nl_boot_state.c | 44 ++++++++ src/light/nl_boot_state.h | 17 +++ src/light/nl_control.c | 98 ++++++++++++++++++ src/light/nl_control.h | 48 +++++++++ test/CMakeLists.txt | 3 + test/test_boot_state.c | 75 ++++++++++++++ test/test_control.c | 213 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 src/light/nl_boot_state.c create mode 100644 src/light/nl_boot_state.h create mode 100644 src/light/nl_control.c create mode 100644 src/light/nl_control.h create mode 100644 test/test_boot_state.c create mode 100644 test/test_control.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 69711e4..cc7a368 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,9 @@ set(NL_FW_SOURCES src/sys/sdk_stubs.c src/board/nl_board.c src/light/nl_color.c - src/light/nl_engine.c) + src/light/nl_engine.c + src/light/nl_control.c + src/light/nl_boot_state.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS}) diff --git a/src/light/nl_boot_state.c b/src/light/nl_boot_state.c new file mode 100644 index 0000000..e9792d5 --- /dev/null +++ b/src/light/nl_boot_state.c @@ -0,0 +1,44 @@ +#include "light/nl_boot_state.h" + +static uint16_t clamp_ct(uint16_t ct, const nl_board_t *b) +{ + if (ct < b->ct_min) + return b->ct_min; + if (ct > b->ct_max) + return b->ct_max; + return ct; +} + +nl_light_state_t nl_boot_default_state(const nl_board_t *b) +{ + nl_light_state_t s; + s.on = true; + s.brightness = 100; + s.mode = NL_MODE_WHITE; + s.r = s.g = s.b = 255; + s.ct = clamp_ct(2700, b); + return s; +} + +nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b) +{ + nl_light_state_t s = last ? *last : nl_boot_default_state(b); + + if (s.brightness < 1 || s.brightness > 100) + s.brightness = 100; + if (!b->has_rgb || (s.mode != NL_MODE_COLOR && s.mode != NL_MODE_WHITE)) + s.mode = NL_MODE_WHITE; + s.ct = clamp_ct(s.ct, b); + + if (pob == NL_POB_ON) + s.on = true; + else if (pob == NL_POB_OFF) + s.on = false; + return s; +} + +bool nl_light_state_equal(const nl_light_state_t *a, const nl_light_state_t *b) +{ + return a->on == b->on && a->brightness == b->brightness && a->mode == b->mode && + a->r == b->r && a->g == b->g && a->b == b->b && a->ct == b->ct; +} diff --git a/src/light/nl_boot_state.h b/src/light/nl_boot_state.h new file mode 100644 index 0000000..b7686fa --- /dev/null +++ b/src/light/nl_boot_state.h @@ -0,0 +1,17 @@ +#ifndef NL_BOOT_STATE_H +#define NL_BOOT_STATE_H + +#include + +#include "board/nl_board.h" +#include "light/nl_light_types.h" + +/* Warm white, 100 %, on. */ +nl_light_state_t nl_boot_default_state(const nl_board_t *b); + +/* State to apply after power-up. last == NULL when nothing is stored. Stored values are sanitised. */ +nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b); + +bool nl_light_state_equal(const nl_light_state_t *a, const nl_light_state_t *b); + +#endif diff --git a/src/light/nl_control.c b/src/light/nl_control.c new file mode 100644 index 0000000..0ed78df --- /dev/null +++ b/src/light/nl_control.c @@ -0,0 +1,98 @@ +#include "light/nl_control.h" + +#include "light/nl_boot_state.h" +#include "light/nl_engine.h" + +static const nl_board_t *s_board; +static nl_light_state_t s_state; +static uint16_t s_fade_in; +static uint16_t s_fade_out; +static bool s_dirty; +static uint32_t s_changed_at; + +void nl_ctrl_init(const nl_board_t *board, uint16_t fade_in_ms, uint16_t fade_out_ms) +{ + s_board = board; + s_state = nl_boot_default_state(board); + s_state.on = false; + s_dirty = false; + s_changed_at = 0; + nl_ctrl_set_fades(fade_in_ms, fade_out_ms); +} + +void nl_ctrl_set_fades(uint16_t fade_in_ms, uint16_t fade_out_ms) +{ + s_fade_in = fade_in_ms > NL_MAX_FADE_MS ? NL_MAX_FADE_MS : fade_in_ms; + s_fade_out = fade_out_ms > NL_MAX_FADE_MS ? NL_MAX_FADE_MS : fade_out_ms; +} + +void nl_ctrl_boot(const nl_light_state_t *initial) +{ + s_state = *initial; + s_dirty = false; + nl_engine_set(&s_state, s_state.on ? s_fade_in : 0); +} + +nl_ctrl_err_t nl_ctrl_apply(const nl_light_change_t *c, uint32_t now_ms) +{ + nl_light_state_t n = s_state; + uint32_t dur; + + if (c->has_color && c->has_ct) + return NL_CTRL_ERR_COLOR_AND_CT; + if (c->has_color && !s_board->has_rgb) + return NL_CTRL_ERR_NO_RGB; + if (c->has_brightness && (c->brightness < 1 || c->brightness > 100)) + return NL_CTRL_ERR_BRIGHTNESS; + if (c->has_transition && c->transition_ms > NL_MAX_TRANSITION_MS) + return NL_CTRL_ERR_TRANSITION; + + if (c->has_on) + n.on = c->on; + if (c->has_brightness) + n.brightness = c->brightness; + if (c->has_color) { + n.r = c->r; + n.g = c->g; + n.b = c->b; + n.mode = NL_MODE_COLOR; + } + if (c->has_ct) { + n.ct = c->ct < s_board->ct_min ? s_board->ct_min : (c->ct > s_board->ct_max ? s_board->ct_max : c->ct); + n.mode = NL_MODE_WHITE; + } + + if (c->has_transition) + dur = c->transition_ms; + else if (!s_state.on && n.on) + dur = s_fade_in; + else if (s_state.on && !n.on) + dur = s_fade_out; + else + dur = 0; + + s_state = n; + nl_engine_set(&s_state, dur); + s_dirty = true; + s_changed_at = now_ms; + return NL_CTRL_OK; +} + +void nl_ctrl_get(nl_light_state_t *out) +{ + *out = s_state; +} + +bool nl_ctrl_persist_due(uint32_t now_ms, nl_light_state_t *out) +{ + if (!s_dirty || (uint32_t)(now_ms - s_changed_at) < NL_PERSIST_DELAY_MS) + return false; + *out = s_state; + return true; +} + +void nl_ctrl_persisted(const nl_light_state_t *written) +{ + if (nl_light_state_equal(written, &s_state)) + s_dirty = false; +} diff --git a/src/light/nl_control.h b/src/light/nl_control.h new file mode 100644 index 0000000..7dd2432 --- /dev/null +++ b/src/light/nl_control.h @@ -0,0 +1,48 @@ +#ifndef NL_CONTROL_H +#define NL_CONTROL_H + +#include +#include + +#include "board/nl_board.h" +#include "light/nl_light_types.h" + +#define NL_MAX_TRANSITION_MS 60000u +#define NL_MAX_FADE_MS 10000u +#define NL_PERSIST_DELAY_MS 3000u + +typedef struct { + bool has_on; + bool on; + bool has_brightness; + uint8_t brightness; + bool has_color; + uint8_t r, g, b; + bool has_ct; + uint16_t ct; + bool has_transition; + uint32_t transition_ms; +} nl_light_change_t; + +typedef enum { + NL_CTRL_OK = 0, + NL_CTRL_ERR_NO_RGB, + NL_CTRL_ERR_COLOR_AND_CT, + NL_CTRL_ERR_BRIGHTNESS, + NL_CTRL_ERR_TRANSITION +} nl_ctrl_err_t; + +/* The engine must be initialised before. Not thread safe: callers serialise access. */ +void nl_ctrl_init(const nl_board_t *board, uint16_t fade_in_ms, uint16_t fade_out_ms); +void nl_ctrl_set_fades(uint16_t fade_in_ms, uint16_t fade_out_ms); +void nl_ctrl_boot(const nl_light_state_t *initial); +nl_ctrl_err_t nl_ctrl_apply(const nl_light_change_t *c, uint32_t now_ms); +void nl_ctrl_get(nl_light_state_t *out); + +/* True once NL_PERSIST_DELAY_MS after the last change; out receives the state to store. */ +bool nl_ctrl_persist_due(uint32_t now_ms, nl_light_state_t *out); + +/* Clears the dirty flag only if nothing changed since `written` was taken. */ +void nl_ctrl_persisted(const nl_light_state_t *written); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 10dbdc5..b2bc48a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,3 +27,6 @@ nl_test(test_crc test_crc.c ${NL_SRC}/util/nl_crc.c) nl_test(test_board test_board.c ${NL_SRC}/board/nl_board.c) nl_test(test_color test_color.c ${NL_SRC}/light/nl_color.c) nl_test(test_engine test_engine.c ${NL_SRC}/light/nl_engine.c ${NL_SRC}/light/nl_color.c) +nl_test(test_boot_state test_boot_state.c ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/board/nl_board.c) +nl_test(test_control test_control.c ${NL_SRC}/light/nl_control.c ${NL_SRC}/light/nl_boot_state.c + ${NL_SRC}/light/nl_engine.c ${NL_SRC}/light/nl_color.c ${NL_SRC}/board/nl_board.c) diff --git a/test/test_boot_state.c b/test/test_boot_state.c new file mode 100644 index 0000000..df7ba2b --- /dev/null +++ b/test/test_boot_state.c @@ -0,0 +1,75 @@ +#include "unity.h" +#include "light/nl_boot_state.h" + +void setUp(void) {} +void tearDown(void) {} + +static nl_light_state_t red_off(void) +{ + nl_light_state_t s = {false, 40, NL_MODE_COLOR, 255, 0, 0, 3000}; + return s; +} + +static void test_default_state(void) +{ + nl_light_state_t d = nl_boot_default_state(nl_board_find("evk")); + TEST_ASSERT_TRUE(d.on); + TEST_ASSERT_EQUAL_UINT8(100, d.brightness); + TEST_ASSERT_EQUAL(NL_MODE_WHITE, d.mode); + TEST_ASSERT_EQUAL_UINT16(2700, d.ct); + TEST_ASSERT_EQUAL_UINT16(2700, nl_boot_default_state(nl_board_find("e27_cw")).ct); +} + +static void test_last_restores_everything(void) +{ + nl_light_state_t last = red_off(); + nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("evk")); + TEST_ASSERT_TRUE(nl_light_state_equal(&last, &s)); +} + +static void test_last_without_saved_state_uses_default(void) +{ + const nl_board_t *b = nl_board_find("evk"); + nl_light_state_t s = nl_boot_state(NL_POB_LAST, NULL, b); + nl_light_state_t d = nl_boot_default_state(b); + TEST_ASSERT_TRUE(nl_light_state_equal(&d, &s)); +} + +static void test_on_forces_on_with_last_values(void) +{ + nl_light_state_t last = red_off(); + nl_light_state_t s = nl_boot_state(NL_POB_ON, &last, nl_board_find("evk")); + TEST_ASSERT_TRUE(s.on); + TEST_ASSERT_EQUAL_UINT8(40, s.brightness); + TEST_ASSERT_EQUAL(NL_MODE_COLOR, s.mode); +} + +static void test_off_forces_off(void) +{ + nl_light_state_t last = red_off(); + last.on = true; + nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("evk")); + TEST_ASSERT_FALSE(s.on); + TEST_ASSERT_EQUAL_UINT8(40, s.brightness); +} + +static void test_sanitizes_stored_values(void) +{ + nl_light_state_t last = {true, 0, NL_MODE_COLOR, 1, 2, 3, 9000}; + nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("e27_cw")); + TEST_ASSERT_EQUAL_UINT8(100, s.brightness); /* invalid 0 -> 100 */ + TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode); /* no RGB on this board */ + TEST_ASSERT_EQUAL_UINT16(5000, s.ct); /* clamped */ +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_default_state); + RUN_TEST(test_last_restores_everything); + RUN_TEST(test_last_without_saved_state_uses_default); + RUN_TEST(test_on_forces_on_with_last_values); + RUN_TEST(test_off_forces_off); + RUN_TEST(test_sanitizes_stored_values); + return UNITY_END(); +} diff --git a/test/test_control.c b/test/test_control.c new file mode 100644 index 0000000..9adb124 --- /dev/null +++ b/test/test_control.c @@ -0,0 +1,213 @@ +#include +#include "unity.h" +#include "light/nl_boot_state.h" +#include "light/nl_control.h" +#include "light/nl_engine.h" + +static void out(const uint8_t ch[NL_CH_COUNT], void *ctx) +{ + (void)ch; + (void)ctx; +} + +static void start(const char *board_id, bool on) +{ + const nl_board_t *b = nl_board_find(board_id); + nl_engine_cfg_t cfg = {b->ct_min, b->ct_max, out, NULL}; + nl_engine_init(&cfg); + nl_ctrl_init(b, 500, 800); + nl_light_state_t s = nl_boot_default_state(b); + s.on = on; + nl_ctrl_boot(&s); + nl_engine_tick(10000); /* finish a possible boot fade */ +} + +static uint16_t bri(void) +{ + nl_visual_t v; + nl_engine_current(&v); + return v.bri; +} + +static nl_light_change_t chg(void) +{ + nl_light_change_t c; + memset(&c, 0, sizeof c); + return c; +} + +void setUp(void) { start("evk", false); } +void tearDown(void) {} + +static void test_turn_on_uses_fade_in(void) +{ + nl_light_change_t c = chg(); + c.has_on = true; + c.on = true; + TEST_ASSERT_EQUAL(NL_CTRL_OK, nl_ctrl_apply(&c, 0)); + nl_engine_tick(250); + TEST_ASSERT_EQUAL_UINT16(5000, bri()); + nl_engine_tick(250); + TEST_ASSERT_EQUAL_UINT16(10000, bri()); +} + +static void test_turn_off_uses_fade_out(void) +{ + start("evk", true); + nl_light_change_t c = chg(); + c.has_on = true; + c.on = false; + nl_ctrl_apply(&c, 0); + nl_engine_tick(400); + TEST_ASSERT_EQUAL_UINT16(5000, bri()); +} + +static void test_explicit_transition_wins(void) +{ + nl_light_change_t c = chg(); + c.has_on = true; + c.on = true; + c.has_transition = true; + c.transition_ms = 100; + nl_ctrl_apply(&c, 0); + nl_engine_tick(100); + TEST_ASSERT_EQUAL_UINT16(10000, bri()); +} + +static void test_brightness_change_without_transition_is_instant(void) +{ + start("evk", true); + nl_light_change_t c = chg(); + c.has_brightness = true; + c.brightness = 50; + nl_ctrl_apply(&c, 0); + TEST_ASSERT_EQUAL_UINT16(5000, bri()); +} + +static void test_brightness_while_off_stays_off(void) +{ + nl_light_change_t c = chg(); + c.has_brightness = true; + c.brightness = 30; + TEST_ASSERT_EQUAL(NL_CTRL_OK, nl_ctrl_apply(&c, 0)); + nl_light_state_t s; + nl_ctrl_get(&s); + TEST_ASSERT_FALSE(s.on); + TEST_ASSERT_EQUAL_UINT8(30, s.brightness); + TEST_ASSERT_EQUAL_UINT16(0, bri()); +} + +static void test_validation(void) +{ + nl_light_change_t c = chg(); + c.has_brightness = true; + c.brightness = 0; + TEST_ASSERT_EQUAL(NL_CTRL_ERR_BRIGHTNESS, nl_ctrl_apply(&c, 0)); + c.brightness = 101; + TEST_ASSERT_EQUAL(NL_CTRL_ERR_BRIGHTNESS, nl_ctrl_apply(&c, 0)); + + c = chg(); + c.has_transition = true; + c.transition_ms = 60001; + TEST_ASSERT_EQUAL(NL_CTRL_ERR_TRANSITION, nl_ctrl_apply(&c, 0)); + + c = chg(); + c.has_color = true; + c.has_ct = true; + TEST_ASSERT_EQUAL(NL_CTRL_ERR_COLOR_AND_CT, nl_ctrl_apply(&c, 0)); +} + +static void test_color_rejected_on_white_board(void) +{ + start("e27_cw", true); + nl_light_change_t c = chg(); + c.has_color = true; + c.r = 255; + TEST_ASSERT_EQUAL(NL_CTRL_ERR_NO_RGB, nl_ctrl_apply(&c, 0)); + nl_light_state_t s; + nl_ctrl_get(&s); + TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode); +} + +static void test_mode_follows_color_or_ct(void) +{ + nl_light_change_t c = chg(); + nl_light_state_t s; + c.has_color = true; + c.r = 10; + c.g = 20; + c.b = 30; + nl_ctrl_apply(&c, 0); + nl_ctrl_get(&s); + TEST_ASSERT_EQUAL(NL_MODE_COLOR, s.mode); + TEST_ASSERT_EQUAL_UINT8(20, s.g); + + c = chg(); + c.has_ct = true; + c.ct = 1000; + nl_ctrl_apply(&c, 0); + nl_ctrl_get(&s); + TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode); + TEST_ASSERT_EQUAL_UINT16(2700, s.ct); /* clamped to ct_min */ +} + +static void test_persist_debounce(void) +{ + nl_light_state_t s; + nl_light_change_t c = chg(); + c.has_brightness = true; + c.brightness = 70; + + TEST_ASSERT_FALSE(nl_ctrl_persist_due(100000, &s)); /* boot is not dirty */ + nl_ctrl_apply(&c, 1000); + TEST_ASSERT_FALSE(nl_ctrl_persist_due(3999, &s)); + TEST_ASSERT_TRUE(nl_ctrl_persist_due(4000, &s)); + TEST_ASSERT_EQUAL_UINT8(70, s.brightness); + nl_ctrl_persisted(&s); + TEST_ASSERT_FALSE(nl_ctrl_persist_due(9000, &s)); +} + +static void test_change_during_write_stays_dirty(void) +{ + nl_light_state_t written; + nl_light_change_t c = chg(); + c.has_brightness = true; + c.brightness = 70; + nl_ctrl_apply(&c, 0); + TEST_ASSERT_TRUE(nl_ctrl_persist_due(3000, &written)); + c.brightness = 20; /* arrives while the flash write is running */ + nl_ctrl_apply(&c, 3001); + nl_ctrl_persisted(&written); /* old state written */ + TEST_ASSERT_FALSE(nl_ctrl_persist_due(3002, &written)); + TEST_ASSERT_TRUE(nl_ctrl_persist_due(6001, &written)); + TEST_ASSERT_EQUAL_UINT8(20, written.brightness); +} + +static void test_boot_fades_in_when_on(void) +{ + const nl_board_t *b = nl_board_find("evk"); + nl_engine_cfg_t cfg = {b->ct_min, b->ct_max, out, NULL}; + nl_engine_init(&cfg); + nl_ctrl_init(b, 500, 800); + nl_light_state_t s = nl_boot_default_state(b); + nl_ctrl_boot(&s); + nl_engine_tick(250); + TEST_ASSERT_EQUAL_UINT16(5000, bri()); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_turn_on_uses_fade_in); + RUN_TEST(test_turn_off_uses_fade_out); + RUN_TEST(test_explicit_transition_wins); + RUN_TEST(test_brightness_change_without_transition_is_instant); + RUN_TEST(test_brightness_while_off_stays_off); + RUN_TEST(test_validation); + RUN_TEST(test_color_rejected_on_white_board); + RUN_TEST(test_mode_follows_color_or_ct); + RUN_TEST(test_persist_debounce); + RUN_TEST(test_change_during_write_stays_dirty); + RUN_TEST(test_boot_fades_in_when_on); + return UNITY_END(); +} From 91721ac47d0108870b2a4aef58276907422c1247 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:24:43 +0200 Subject: [PATCH 010/150] feat: Flash-HAL-Interface und Settings-Store mit A/B-Slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementiert: - Flash HAL (nl_flash_read/write/erase_sector) Interfaces - Flash Layout mit A/B-Settings Slots und weiteren Regions - Test-Fake für Flash mit Fehlerinjection (Stromausfall-Simulation) - Settings-Store mit CRC32 Recovery und Slot-Alternierung - Vollständige Test-Suite mit Corruption/Power-Loss-Tests --- src/config/nl_flash.h | 16 +++++ src/config/nl_flash_layout.h | 12 ++++ src/config/nl_settings.c | 117 +++++++++++++++++++++++++++++++++++ src/config/nl_settings.h | 30 +++++++++ test/CMakeLists.txt | 1 + test/fake_flash.c | 60 ++++++++++++++++++ test/fake_flash.h | 19 ++++++ test/test_settings.c | 105 +++++++++++++++++++++++++++++++ 8 files changed, 360 insertions(+) create mode 100644 src/config/nl_flash.h create mode 100644 src/config/nl_flash_layout.h create mode 100644 src/config/nl_settings.c create mode 100644 src/config/nl_settings.h create mode 100644 test/fake_flash.c create mode 100644 test/fake_flash.h create mode 100644 test/test_settings.c diff --git a/src/config/nl_flash.h b/src/config/nl_flash.h new file mode 100644 index 0000000..5003a58 --- /dev/null +++ b/src/config/nl_flash.h @@ -0,0 +1,16 @@ +#ifndef NL_FLASH_H +#define NL_FLASH_H + +#include + +#define NL_FLASH_SECTOR_SIZE 4096u + +/* All functions return 0 on success, < 0 on error. Addresses are absolute. */ +int nl_flash_read(uint32_t addr, void *buf, uint32_t len); +/* The target bytes must be erased (0xFF); the stores never rely on implicit erases. */ +int nl_flash_write(uint32_t addr, const void *buf, uint32_t len); +int nl_flash_erase_sector(uint32_t addr); +/* Start of the ninlight flash region (USER_ADDR_START + one sector on the W600). */ +uint32_t nl_flash_region_base(void); + +#endif diff --git a/src/config/nl_flash_layout.h b/src/config/nl_flash_layout.h new file mode 100644 index 0000000..f1f795f --- /dev/null +++ b/src/config/nl_flash_layout.h @@ -0,0 +1,12 @@ +#ifndef NL_FLASH_LAYOUT_H +#define NL_FLASH_LAYOUT_H + +/* Offsets inside the ninlight flash region, one 4 KB sector each. */ +#define NL_FL_SETTINGS_A 0x0000u +#define NL_FL_SETTINGS_B 0x1000u +#define NL_FL_STATE_0 0x2000u +#define NL_FL_STATE_1 0x3000u +#define NL_FL_RESETCNT 0x4000u +#define NL_FL_REGION_SIZE 0x5000u + +#endif diff --git a/src/config/nl_settings.c b/src/config/nl_settings.c new file mode 100644 index 0000000..38138c3 --- /dev/null +++ b/src/config/nl_settings.c @@ -0,0 +1,117 @@ +#include "config/nl_settings.h" + +#include +#include + +#include "config/nl_flash.h" +#include "light/nl_light_types.h" +#include "util/nl_crc.h" + +#ifndef NINLIGHT_TOKEN_DEFAULT +#define NINLIGHT_TOKEN_DEFAULT "aabbccddeeff0011223344" +#endif + +#define NL_SETTINGS_MAGIC 0x31534C4Eu /* "NLS1" */ +#define NL_SETTINGS_VERSION 1u + +typedef struct { + uint32_t magic; + uint16_t version; + uint16_t length; + uint32_t seq; +} rec_hdr_t; + +typedef struct { + rec_hdr_t hdr; + nl_settings_t data; + uint32_t crc; +} rec_t; + +static uint32_t s_addr[2]; + +void nl_settings_defaults(nl_settings_t *s) +{ + memset(s, 0, sizeof *s); + strncpy(s->token, NINLIGHT_TOKEN_DEFAULT, NL_TOKEN_LEN - 1); + s->power_on_behaviour = NL_POB_LAST; + s->fade_in_ms = 500; + s->fade_out_ms = 500; +} + +void nl_settings_init(uint32_t addr_a, uint32_t addr_b) +{ + s_addr[0] = addr_a; + s_addr[1] = addr_b; +} + +static bool read_rec(int slot, rec_t *r) +{ + if (nl_flash_read(s_addr[slot], r, sizeof *r) != 0) + return false; + if (r->hdr.magic != NL_SETTINGS_MAGIC || r->hdr.version != NL_SETTINGS_VERSION || + r->hdr.length != sizeof(nl_settings_t)) + return false; + return nl_crc32(0, r, offsetof(rec_t, crc)) == r->crc; +} + +bool nl_settings_load(nl_settings_t *out) +{ + rec_t a, b; + bool va = read_rec(0, &a); + bool vb = read_rec(1, &b); + const rec_t *best; + + if (!va && !vb) { + nl_settings_defaults(out); + return false; + } + best = (va && (!vb || a.hdr.seq > b.hdr.seq)) ? &a : &b; + *out = best->data; + out->wifi_ssid[NL_SSID_LEN - 1] = '\0'; + out->wifi_pass[NL_PASS_LEN - 1] = '\0'; + out->token[NL_TOKEN_LEN - 1] = '\0'; + out->name[NL_NAME_LEN - 1] = '\0'; + return true; +} + +int nl_settings_save(const nl_settings_t *s) +{ + static rec_t a, b, r; /* keep ~700 bytes off the task stack */ + bool va = read_rec(0, &a); + bool vb = read_rec(1, &b); + uint32_t seq; + int slot; + + if (va && vb) { + slot = a.hdr.seq <= b.hdr.seq ? 0 : 1; + seq = (a.hdr.seq > b.hdr.seq ? a.hdr.seq : b.hdr.seq) + 1; + } else if (va) { + slot = 1; + seq = a.hdr.seq + 1; + } else if (vb) { + slot = 0; + seq = b.hdr.seq + 1; + } else { + slot = 0; + seq = 1; + } + + memset(&r, 0, sizeof r); + r.hdr.magic = NL_SETTINGS_MAGIC; + r.hdr.version = NL_SETTINGS_VERSION; + r.hdr.length = sizeof(nl_settings_t); + r.hdr.seq = seq; + r.data = *s; + r.crc = nl_crc32(0, &r, offsetof(rec_t, crc)); + + if (nl_flash_erase_sector(s_addr[slot]) != 0) + return -1; + return nl_flash_write(s_addr[slot], &r, sizeof r); +} + +int nl_settings_erase(void) +{ + int ra = nl_flash_erase_sector(s_addr[0]); + int rb = nl_flash_erase_sector(s_addr[1]); + return (ra == 0 && rb == 0) ? 0 : -1; +} diff --git a/src/config/nl_settings.h b/src/config/nl_settings.h new file mode 100644 index 0000000..7c58b5e --- /dev/null +++ b/src/config/nl_settings.h @@ -0,0 +1,30 @@ +#ifndef NL_SETTINGS_H +#define NL_SETTINGS_H + +#include +#include + +#define NL_SSID_LEN 33 +#define NL_PASS_LEN 65 +#define NL_TOKEN_LEN 65 +#define NL_NAME_LEN 33 + +typedef struct { + char wifi_ssid[NL_SSID_LEN]; + char wifi_pass[NL_PASS_LEN]; + char token[NL_TOKEN_LEN]; + char name[NL_NAME_LEN]; + uint8_t auth_all; + uint8_t power_on_behaviour; /* nl_pob_t */ + uint16_t fade_in_ms; + uint16_t fade_out_ms; +} nl_settings_t; + +void nl_settings_defaults(nl_settings_t *s); +void nl_settings_init(uint32_t addr_a, uint32_t addr_b); +/* Returns false (and fills defaults) when no valid record exists. */ +bool nl_settings_load(nl_settings_t *out); +int nl_settings_save(const nl_settings_t *s); +int nl_settings_erase(void); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b2bc48a..a580d77 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,3 +30,4 @@ nl_test(test_engine test_engine.c ${NL_SRC}/light/nl_engine.c ${NL_SRC}/light/nl nl_test(test_boot_state test_boot_state.c ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/board/nl_board.c) nl_test(test_control test_control.c ${NL_SRC}/light/nl_control.c ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/light/nl_engine.c ${NL_SRC}/light/nl_color.c ${NL_SRC}/board/nl_board.c) +nl_test(test_settings test_settings.c fake_flash.c ${NL_SRC}/config/nl_settings.c ${NL_SRC}/util/nl_crc.c) diff --git a/test/fake_flash.c b/test/fake_flash.c new file mode 100644 index 0000000..58f2296 --- /dev/null +++ b/test/fake_flash.c @@ -0,0 +1,60 @@ +#include "fake_flash.h" + +#include + +uint8_t fake_flash_mem[FAKE_FLASH_SIZE]; +unsigned fake_flash_erases[FAKE_FLASH_SECTORS]; +unsigned fake_flash_dirty_writes; +static long s_fail_after = -1; + +void fake_flash_reset(void) +{ + memset(fake_flash_mem, 0xFF, sizeof fake_flash_mem); + memset(fake_flash_erases, 0, sizeof fake_flash_erases); + fake_flash_dirty_writes = 0; + s_fail_after = -1; +} + +void fake_flash_fail_after(long bytes) +{ + s_fail_after = bytes; +} + +int nl_flash_read(uint32_t addr, void *buf, uint32_t len) +{ + if (addr + len > FAKE_FLASH_SIZE) + return -1; + memcpy(buf, &fake_flash_mem[addr], len); + return 0; +} + +int nl_flash_write(uint32_t addr, const void *buf, uint32_t len) +{ + const uint8_t *p = (const uint8_t *)buf; + if (addr + len > FAKE_FLASH_SIZE) + return -1; + for (uint32_t i = 0; i < len; i++) { + if (s_fail_after == 0) + return -1; + if (s_fail_after > 0) + s_fail_after--; + if (fake_flash_mem[addr + i] != 0xFF) + fake_flash_dirty_writes++; + fake_flash_mem[addr + i] &= p[i]; /* NOR: bits only go from 1 to 0 */ + } + return 0; +} + +int nl_flash_erase_sector(uint32_t addr) +{ + if (addr % NL_FLASH_SECTOR_SIZE || addr >= FAKE_FLASH_SIZE || s_fail_after == 0) + return -1; + memset(&fake_flash_mem[addr], 0xFF, NL_FLASH_SECTOR_SIZE); + fake_flash_erases[addr / NL_FLASH_SECTOR_SIZE]++; + return 0; +} + +uint32_t nl_flash_region_base(void) +{ + return 0; +} diff --git a/test/fake_flash.h b/test/fake_flash.h new file mode 100644 index 0000000..93372ac --- /dev/null +++ b/test/fake_flash.h @@ -0,0 +1,19 @@ +#ifndef FAKE_FLASH_H +#define FAKE_FLASH_H + +#include + +#include "config/nl_flash.h" + +#define FAKE_FLASH_SECTORS 8u +#define FAKE_FLASH_SIZE (FAKE_FLASH_SECTORS * NL_FLASH_SECTOR_SIZE) + +extern uint8_t fake_flash_mem[FAKE_FLASH_SIZE]; +extern unsigned fake_flash_erases[FAKE_FLASH_SECTORS]; +extern unsigned fake_flash_dirty_writes; /* writes onto non-erased bytes (store bug) */ + +void fake_flash_reset(void); +/* Simulate power loss: after `bytes` more written bytes every write/erase fails. -1 = never. */ +void fake_flash_fail_after(long bytes); + +#endif diff --git a/test/test_settings.c b/test/test_settings.c new file mode 100644 index 0000000..7ccab69 --- /dev/null +++ b/test/test_settings.c @@ -0,0 +1,105 @@ +#include +#include "unity.h" +#include "fake_flash.h" +#include "config/nl_flash_layout.h" +#include "config/nl_settings.h" +#include "light/nl_light_types.h" + +void setUp(void) +{ + fake_flash_reset(); + nl_settings_init(NL_FL_SETTINGS_A, NL_FL_SETTINGS_B); +} + +void tearDown(void) +{ + TEST_ASSERT_EQUAL_UINT(0, fake_flash_dirty_writes); +} + +static nl_settings_t sample(const char *name) +{ + nl_settings_t s; + nl_settings_defaults(&s); + strcpy(s.wifi_ssid, "ninHome"); + strcpy(s.wifi_pass, "secret"); + strcpy(s.name, name); + s.auth_all = 1; + s.power_on_behaviour = NL_POB_ON; + s.fade_in_ms = 1200; + return s; +} + +static void test_defaults(void) +{ + nl_settings_t s; + TEST_ASSERT_FALSE(nl_settings_load(&s)); + TEST_ASSERT_EQUAL_STRING("", s.wifi_ssid); + TEST_ASSERT_EQUAL_STRING("aabbccddeeff0011223344", s.token); + TEST_ASSERT_EQUAL_STRING("", s.name); + TEST_ASSERT_EQUAL_UINT8(0, s.auth_all); + TEST_ASSERT_EQUAL_UINT8(NL_POB_LAST, s.power_on_behaviour); + TEST_ASSERT_EQUAL_UINT16(500, s.fade_in_ms); + TEST_ASSERT_EQUAL_UINT16(500, s.fade_out_ms); +} + +static void test_save_and_load(void) +{ + nl_settings_t in = sample("Wohnzimmer"), out; + TEST_ASSERT_EQUAL_INT(0, nl_settings_save(&in)); + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_MEMORY(&in, &out, sizeof in); +} + +static void test_alternates_slots(void) +{ + nl_settings_t s, out; + s = sample("a"); nl_settings_save(&s); + s = sample("b"); nl_settings_save(&s); + s = sample("c"); nl_settings_save(&s); + TEST_ASSERT_EQUAL_UINT(2, fake_flash_erases[0]); + TEST_ASSERT_EQUAL_UINT(1, fake_flash_erases[1]); + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_STRING("c", out.name); +} + +static void test_power_loss_keeps_previous(void) +{ + nl_settings_t s = sample("old"), out; + nl_settings_save(&s); + s = sample("new"); + fake_flash_fail_after(50); + TEST_ASSERT_NOT_EQUAL(0, nl_settings_save(&s)); + fake_flash_fail_after(-1); + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_STRING("old", out.name); +} + +static void test_corrupt_newest_falls_back(void) +{ + nl_settings_t s, out; + s = sample("first"); nl_settings_save(&s); /* slot A */ + s = sample("second"); nl_settings_save(&s); /* slot B */ + fake_flash_mem[NL_FL_SETTINGS_B + 40] ^= 0x01; + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_STRING("first", out.name); +} + +static void test_erase(void) +{ + nl_settings_t s = sample("x"), out; + nl_settings_save(&s); + TEST_ASSERT_EQUAL_INT(0, nl_settings_erase()); + TEST_ASSERT_FALSE(nl_settings_load(&out)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_defaults); + RUN_TEST(test_save_and_load); + RUN_TEST(test_alternates_slots); + RUN_TEST(test_power_loss_keeps_previous); + RUN_TEST(test_corrupt_newest_falls_back); + RUN_TEST(test_erase); + return UNITY_END(); +} From b1b4ec214bb1762ecf0c65fce718260185b32c65 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:30:26 +0200 Subject: [PATCH 011/150] =?UTF-8?q?feat:=20Ring-Log=20f=C3=BCr=20den=20Lic?= =?UTF-8?q?htzustand?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/nl_state_log.c | 152 ++++++++++++++++++++++++++++++++++++++ src/config/nl_state_log.h | 18 +++++ test/CMakeLists.txt | 2 + test/test_state_log.c | 141 +++++++++++++++++++++++++++++++++++ 4 files changed, 313 insertions(+) create mode 100644 src/config/nl_state_log.c create mode 100644 src/config/nl_state_log.h create mode 100644 test/test_state_log.c diff --git a/src/config/nl_state_log.c b/src/config/nl_state_log.c new file mode 100644 index 0000000..9b24fff --- /dev/null +++ b/src/config/nl_state_log.c @@ -0,0 +1,152 @@ +#include "config/nl_state_log.h" + +#include + +#include "light/nl_boot_state.h" +#include "util/nl_crc.h" + +#define ENTRY_MAGIC 0xA5u + +static uint32_t s_sector[2]; +static bool s_have; +static nl_light_state_t s_latest; +static uint32_t s_seq; +static uint32_t s_active; +static uint32_t s_next; /* next free slot in the active sector */ + +static uint32_t slot_addr(uint32_t sec, uint32_t slot) +{ + return s_sector[sec] + slot * NL_STATE_ENTRY_SIZE; +} + +static void encode(const nl_light_state_t *s, uint32_t seq, uint8_t e[NL_STATE_ENTRY_SIZE]) +{ + memset(e, 0, NL_STATE_ENTRY_SIZE); + e[0] = ENTRY_MAGIC; + e[1] = (uint8_t)((s->on ? 1u : 0u) | (s->mode == NL_MODE_WHITE ? 2u : 0u)); + e[2] = s->brightness; + e[3] = s->r; + e[4] = s->g; + e[5] = s->b; + e[6] = (uint8_t)(s->ct & 0xFFu); + e[7] = (uint8_t)(s->ct >> 8); + e[8] = (uint8_t)seq; + e[9] = (uint8_t)(seq >> 8); + e[10] = (uint8_t)(seq >> 16); + e[11] = (uint8_t)(seq >> 24); + e[15] = nl_crc8(e, NL_STATE_ENTRY_SIZE - 1); +} + +static bool decode(const uint8_t e[NL_STATE_ENTRY_SIZE], nl_light_state_t *s, uint32_t *seq) +{ + if (e[0] != ENTRY_MAGIC || nl_crc8(e, NL_STATE_ENTRY_SIZE - 1) != e[15]) + return false; + s->on = (e[1] & 1u) != 0; + s->mode = (e[1] & 2u) ? NL_MODE_WHITE : NL_MODE_COLOR; + s->brightness = e[2]; + s->r = e[3]; + s->g = e[4]; + s->b = e[5]; + s->ct = (uint16_t)(e[6] | (e[7] << 8)); + *seq = (uint32_t)e[8] | ((uint32_t)e[9] << 8) | ((uint32_t)e[10] << 16) | ((uint32_t)e[11] << 24); + return true; +} + +static bool is_free(const uint8_t e[NL_STATE_ENTRY_SIZE]) +{ + for (uint32_t i = 0; i < NL_STATE_ENTRY_SIZE; i++) + if (e[i] != 0xFF) + return false; + return true; +} + +static uint32_t next_free_slot(uint32_t sec) +{ + uint8_t e[NL_STATE_ENTRY_SIZE]; + uint32_t next = 0; + for (uint32_t slot = 0; slot < NL_STATE_LOG_SLOTS; slot++) { + if (nl_flash_read(slot_addr(sec, slot), e, sizeof e) != 0 || !is_free(e)) + next = slot + 1; + } + return next; +} + +void nl_state_log_init(uint32_t sector0, uint32_t sector1) +{ + uint8_t e[NL_STATE_ENTRY_SIZE]; + nl_light_state_t st; + uint32_t seq; + + s_sector[0] = sector0; + s_sector[1] = sector1; + s_have = false; + s_seq = 0; + s_active = 0; + for (uint32_t sec = 0; sec < 2; sec++) { + for (uint32_t slot = 0; slot < NL_STATE_LOG_SLOTS; slot++) { + if (nl_flash_read(slot_addr(sec, slot), e, sizeof e) != 0) + continue; + if (decode(e, &st, &seq) && (!s_have || seq > s_seq)) { + s_have = true; + s_seq = seq; + s_latest = st; + s_active = sec; + } + } + } + s_next = next_free_slot(s_active); +} + +bool nl_state_log_load(nl_light_state_t *out) +{ + if (!s_have) + return false; + *out = s_latest; + return true; +} + +int nl_state_log_append(const nl_light_state_t *s) +{ + uint8_t e[NL_STATE_ENTRY_SIZE]; + uint32_t sec = s_active; + int r; + + if (s_have && nl_light_state_equal(&s_latest, s)) + return 0; + + if (!s_have) { + sec = 0; + if (s_active != 0 || s_next != 0) { + if (nl_flash_erase_sector(s_sector[0]) != 0) + return -1; + } + s_next = 0; + } else if (s_next >= NL_STATE_LOG_SLOTS) { + sec = 1u - s_active; + if (nl_flash_erase_sector(s_sector[sec]) != 0) + return -1; + s_next = 0; + } + + encode(s, s_seq + 1, e); + r = nl_flash_write(slot_addr(sec, s_next), e, sizeof e); + s_active = sec; + s_next++; /* also skip a half written slot */ + if (r != 0) + return -1; + s_seq++; + s_latest = *s; + s_have = true; + return 0; +} + +int nl_state_log_erase(void) +{ + int r0 = nl_flash_erase_sector(s_sector[0]); + int r1 = nl_flash_erase_sector(s_sector[1]); + s_have = false; + s_seq = 0; + s_active = 0; + s_next = 0; + return (r0 == 0 && r1 == 0) ? 0 : -1; +} diff --git a/src/config/nl_state_log.h b/src/config/nl_state_log.h new file mode 100644 index 0000000..ac2b75d --- /dev/null +++ b/src/config/nl_state_log.h @@ -0,0 +1,18 @@ +#ifndef NL_STATE_LOG_H +#define NL_STATE_LOG_H + +#include +#include + +#include "config/nl_flash.h" +#include "light/nl_light_types.h" + +#define NL_STATE_ENTRY_SIZE 16u +#define NL_STATE_LOG_SLOTS (NL_FLASH_SECTOR_SIZE / NL_STATE_ENTRY_SIZE) + +void nl_state_log_init(uint32_t sector0, uint32_t sector1); +bool nl_state_log_load(nl_light_state_t *out); +int nl_state_log_append(const nl_light_state_t *s); +int nl_state_log_erase(void); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a580d77..a377612 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -31,3 +31,5 @@ nl_test(test_boot_state test_boot_state.c ${NL_SRC}/light/nl_boot_state.c ${NL_S nl_test(test_control test_control.c ${NL_SRC}/light/nl_control.c ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/light/nl_engine.c ${NL_SRC}/light/nl_color.c ${NL_SRC}/board/nl_board.c) nl_test(test_settings test_settings.c fake_flash.c ${NL_SRC}/config/nl_settings.c ${NL_SRC}/util/nl_crc.c) +nl_test(test_state_log test_state_log.c fake_flash.c ${NL_SRC}/config/nl_state_log.c + ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/util/nl_crc.c) diff --git a/test/test_state_log.c b/test/test_state_log.c new file mode 100644 index 0000000..534e6f2 --- /dev/null +++ b/test/test_state_log.c @@ -0,0 +1,141 @@ +#include +#include "unity.h" +#include "fake_flash.h" +#include "config/nl_flash_layout.h" +#include "config/nl_state_log.h" +#include "light/nl_boot_state.h" + +static nl_light_state_t st(uint8_t bri) +{ + nl_light_state_t s = {true, bri, NL_MODE_COLOR, 10, 20, 30, 3000}; + return s; +} + +static void reboot(void) +{ + nl_state_log_init(NL_FL_STATE_0, NL_FL_STATE_1); +} + +void setUp(void) +{ + fake_flash_reset(); + reboot(); +} + +void tearDown(void) +{ + TEST_ASSERT_EQUAL_UINT(0, fake_flash_dirty_writes); +} + +static void test_empty(void) +{ + nl_light_state_t s; + TEST_ASSERT_FALSE(nl_state_log_load(&s)); +} + +static void test_append_and_reload(void) +{ + nl_light_state_t in = st(42), out; + in.mode = NL_MODE_WHITE; + in.on = false; + TEST_ASSERT_EQUAL_INT(0, nl_state_log_append(&in)); + reboot(); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_TRUE(nl_light_state_equal(&in, &out)); +} + +static void test_equal_state_is_not_written_again(void) +{ + nl_light_state_t s = st(42); + nl_state_log_append(&s); + nl_state_log_append(&s); + for (int i = 0; i < 16; i++) + TEST_ASSERT_EQUAL_HEX8(0xFF, fake_flash_mem[NL_FL_STATE_0 + 16 + i]); +} + +static void test_wraps_into_second_sector(void) +{ + nl_light_state_t s, out; + for (int i = 0; i < 300; i++) { + s = st((uint8_t)(1 + i % 100)); + s.r = (uint8_t)i; /* make every entry distinct */ + TEST_ASSERT_EQUAL_INT(0, nl_state_log_append(&s)); + } + TEST_ASSERT_EQUAL_UINT(1, fake_flash_erases[NL_FL_STATE_1 / NL_FLASH_SECTOR_SIZE]); + reboot(); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_TRUE(nl_light_state_equal(&s, &out)); + /* keep going after the reboot */ + s = st(77); + TEST_ASSERT_EQUAL_INT(0, nl_state_log_append(&s)); + reboot(); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_EQUAL_UINT8(77, out.brightness); +} + +static void test_wraps_back_to_first_sector(void) +{ + nl_light_state_t s, out; + for (int i = 0; i < 600; i++) { + s = st(50); + s.r = (uint8_t)i; + s.g = (uint8_t)(i >> 8); + TEST_ASSERT_EQUAL_INT(0, nl_state_log_append(&s)); + } + TEST_ASSERT_EQUAL_UINT(1, fake_flash_erases[NL_FL_STATE_0 / NL_FLASH_SECTOR_SIZE]); + reboot(); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_TRUE(nl_light_state_equal(&s, &out)); +} + +static void test_power_loss_during_append(void) +{ + nl_light_state_t a = st(10), b = st(20), c = st(30), out; + nl_state_log_append(&a); + fake_flash_fail_after(5); + TEST_ASSERT_NOT_EQUAL(0, nl_state_log_append(&b)); + fake_flash_fail_after(-1); + reboot(); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_EQUAL_UINT8(10, out.brightness); + TEST_ASSERT_EQUAL_INT(0, nl_state_log_append(&c)); + reboot(); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_EQUAL_UINT8(30, out.brightness); +} + +static void test_garbage_in_empty_log_is_erased_first(void) +{ + nl_light_state_t s = st(60), out; + fake_flash_mem[NL_FL_STATE_0 + 3] = 0x12; /* leftovers from the old firmware */ + reboot(); + TEST_ASSERT_FALSE(nl_state_log_load(&out)); + TEST_ASSERT_EQUAL_INT(0, nl_state_log_append(&s)); + reboot(); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_EQUAL_UINT8(60, out.brightness); +} + +static void test_erase(void) +{ + nl_light_state_t s = st(60), out; + nl_state_log_append(&s); + TEST_ASSERT_EQUAL_INT(0, nl_state_log_erase()); + TEST_ASSERT_FALSE(nl_state_log_load(&out)); + reboot(); + TEST_ASSERT_FALSE(nl_state_log_load(&out)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_empty); + RUN_TEST(test_append_and_reload); + RUN_TEST(test_equal_state_is_not_written_again); + RUN_TEST(test_wraps_into_second_sector); + RUN_TEST(test_wraps_back_to_first_sector); + RUN_TEST(test_power_loss_during_append); + RUN_TEST(test_garbage_in_empty_log_is_erased_first); + RUN_TEST(test_erase); + return UNITY_END(); +} From 35b59a64edaae89486bb8383015627539349c63a Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:35:56 +0200 Subject: [PATCH 012/150] =?UTF-8?q?fix:=20Zustands-Log=20bei=20fehlgeschla?= =?UTF-8?q?genem=20L=C3=B6schen=20neu=20einlesen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/nl_state_log.c | 15 ++++++++++----- test/test_state_log.c | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/config/nl_state_log.c b/src/config/nl_state_log.c index 9b24fff..b75adfd 100644 --- a/src/config/nl_state_log.c +++ b/src/config/nl_state_log.c @@ -144,9 +144,14 @@ int nl_state_log_erase(void) { int r0 = nl_flash_erase_sector(s_sector[0]); int r1 = nl_flash_erase_sector(s_sector[1]); - s_have = false; - s_seq = 0; - s_active = 0; - s_next = 0; - return (r0 == 0 && r1 == 0) ? 0 : -1; + if (r0 == 0 && r1 == 0) { + s_have = false; + s_seq = 0; + s_active = 0; + s_next = 0; + return 0; + } + /* If erase failed, re-scan the flash to restore consistency */ + nl_state_log_init(s_sector[0], s_sector[1]); + return -1; } diff --git a/test/test_state_log.c b/test/test_state_log.c index 534e6f2..9e3a57a 100644 --- a/test/test_state_log.c +++ b/test/test_state_log.c @@ -126,6 +126,21 @@ static void test_erase(void) TEST_ASSERT_FALSE(nl_state_log_load(&out)); } +static void test_erase_failure_rescans_flash(void) +{ + nl_light_state_t old = st(10), new = st(20), out; + TEST_ASSERT_EQUAL_INT(0, nl_state_log_append(&old)); + fake_flash_fail_after(0); + TEST_ASSERT_NOT_EQUAL(0, nl_state_log_erase()); + fake_flash_fail_after(-1); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_EQUAL_UINT8(10, out.brightness); + TEST_ASSERT_EQUAL_INT(0, nl_state_log_append(&new)); + reboot(); + TEST_ASSERT_TRUE(nl_state_log_load(&out)); + TEST_ASSERT_EQUAL_UINT8(20, out.brightness); +} + int main(void) { UNITY_BEGIN(); @@ -137,5 +152,6 @@ int main(void) RUN_TEST(test_power_loss_during_append); RUN_TEST(test_garbage_in_empty_log_is_erased_first); RUN_TEST(test_erase); + RUN_TEST(test_erase_failure_rescans_flash); return UNITY_END(); } From 663825c16599e4676147683e5910cdb7ecf73e04 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:39:01 +0200 Subject: [PATCH 013/150] =?UTF-8?q?feat:=20Power-Cycle-Z=C3=A4hler=20f?= =?UTF-8?q?=C3=BCr=20den=20Werksreset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/nl_reset_counter.c | 58 +++++++++++++++++++++++++++ src/config/nl_reset_counter.h | 13 ++++++ test/CMakeLists.txt | 1 + test/test_reset_counter.c | 75 +++++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 src/config/nl_reset_counter.c create mode 100644 src/config/nl_reset_counter.h create mode 100644 test/test_reset_counter.c diff --git a/src/config/nl_reset_counter.c b/src/config/nl_reset_counter.c new file mode 100644 index 0000000..6ea2f35 --- /dev/null +++ b/src/config/nl_reset_counter.c @@ -0,0 +1,58 @@ +#include "config/nl_reset_counter.h" + +#include "config/nl_flash.h" + +#define REC_TAG 0x5A000000u +#define REC_MASK 0xFF000000u +#define REC_FREE 0xFFFFFFFFu +#define REC_SLOTS (NL_FLASH_SECTOR_SIZE / 4u) + +static uint32_t s_sector; +static uint32_t s_next; +static uint8_t s_count; + +void nl_resetcnt_init(uint32_t sector) +{ + uint32_t w; + + s_sector = sector; + s_next = 0; + s_count = 0; + for (uint32_t i = 0; i < REC_SLOTS; i++) { + if (nl_flash_read(sector + i * 4u, &w, 4) != 0 || w == REC_FREE) + continue; + s_next = i + 1; + s_count = ((w & REC_MASK) == REC_TAG) ? (uint8_t)(w & 0xFFu) : 0; + } +} + +static int put(uint8_t count) +{ + uint32_t w = REC_TAG | count; + int r; + + if (s_next >= REC_SLOTS) { + if (nl_flash_erase_sector(s_sector) != 0) + return -1; + s_next = 0; + } + r = nl_flash_write(s_sector + s_next * 4u, &w, 4); + s_next++; + if (r == 0) + s_count = count; + return r; +} + +uint8_t nl_resetcnt_boot(void) +{ + uint8_t c = s_count < 255 ? (uint8_t)(s_count + 1) : 255; + put(c); + return c; +} + +int nl_resetcnt_clear(void) +{ + if (s_count == 0) + return 0; + return put(0); +} diff --git a/src/config/nl_reset_counter.h b/src/config/nl_reset_counter.h new file mode 100644 index 0000000..9845687 --- /dev/null +++ b/src/config/nl_reset_counter.h @@ -0,0 +1,13 @@ +#ifndef NL_RESET_COUNTER_H +#define NL_RESET_COUNTER_H + +#include + +#define NL_FACTORY_RESET_BOOTS 5u +#define NL_RESETCNT_CLEAR_MS 5000u + +void nl_resetcnt_init(uint32_t sector); +uint8_t nl_resetcnt_boot(void); +int nl_resetcnt_clear(void); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a377612..e2416fa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -33,3 +33,4 @@ nl_test(test_control test_control.c ${NL_SRC}/light/nl_control.c ${NL_SRC}/light nl_test(test_settings test_settings.c fake_flash.c ${NL_SRC}/config/nl_settings.c ${NL_SRC}/util/nl_crc.c) nl_test(test_state_log test_state_log.c fake_flash.c ${NL_SRC}/config/nl_state_log.c ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/util/nl_crc.c) +nl_test(test_reset_counter test_reset_counter.c fake_flash.c ${NL_SRC}/config/nl_reset_counter.c) diff --git a/test/test_reset_counter.c b/test/test_reset_counter.c new file mode 100644 index 0000000..b3ad9d3 --- /dev/null +++ b/test/test_reset_counter.c @@ -0,0 +1,75 @@ +#include "unity.h" +#include "fake_flash.h" +#include "config/nl_flash_layout.h" +#include "config/nl_reset_counter.h" + +static uint8_t boot(void) +{ + nl_resetcnt_init(NL_FL_RESETCNT); + return nl_resetcnt_boot(); +} + +void setUp(void) { fake_flash_reset(); } + +void tearDown(void) +{ + TEST_ASSERT_EQUAL_UINT(0, fake_flash_dirty_writes); +} + +static void test_first_boot_is_one(void) +{ + TEST_ASSERT_EQUAL_UINT8(1, boot()); +} + +static void test_fast_boots_count_up(void) +{ + for (uint8_t i = 1; i <= 5; i++) + TEST_ASSERT_EQUAL_UINT8(i, boot()); +} + +static void test_clear_resets(void) +{ + boot(); + boot(); + TEST_ASSERT_EQUAL_INT(0, nl_resetcnt_clear()); + TEST_ASSERT_EQUAL_UINT8(1, boot()); +} + +static void test_clear_twice_writes_once(void) +{ + boot(); + nl_resetcnt_clear(); + nl_resetcnt_clear(); + /* slot 0: count 1, slot 1: count 0, slot 2 still free */ + TEST_ASSERT_EQUAL_HEX8(0xFF, fake_flash_mem[NL_FL_RESETCNT + 8]); +} + +static void test_wraps_after_full_sector(void) +{ + for (int i = 0; i < 700; i++) { + boot(); + nl_resetcnt_clear(); + } + TEST_ASSERT_EQUAL_UINT(1, fake_flash_erases[NL_FL_RESETCNT / NL_FLASH_SECTOR_SIZE]); + TEST_ASSERT_EQUAL_UINT8(1, boot()); + TEST_ASSERT_EQUAL_UINT8(2, boot()); +} + +static void test_garbage_counts_as_zero(void) +{ + fake_flash_mem[NL_FL_RESETCNT + 0] = 0x12; + fake_flash_mem[NL_FL_RESETCNT + 3] = 0x00; + TEST_ASSERT_EQUAL_UINT8(1, boot()); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_first_boot_is_one); + RUN_TEST(test_fast_boots_count_up); + RUN_TEST(test_clear_resets); + RUN_TEST(test_clear_twice_writes_once); + RUN_TEST(test_wraps_after_full_sector); + RUN_TEST(test_garbage_counts_as_zero); + return UNITY_END(); +} From 1074a8f03437c8be91ef579fdcaaee17c6bbeb8d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:43:08 +0200 Subject: [PATCH 014/150] =?UTF-8?q?feat:=20Parser=20f=C3=BCr=20die=20UART-?= =?UTF-8?q?Testkonsole?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sys/nl_console_cmd.c | 120 +++++++++++++++++++++++++++++++++++++++ src/sys/nl_console_cmd.h | 34 +++++++++++ test/CMakeLists.txt | 1 + test/test_console_cmd.c | 98 ++++++++++++++++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 src/sys/nl_console_cmd.c create mode 100644 src/sys/nl_console_cmd.h create mode 100644 test/test_console_cmd.c diff --git a/src/sys/nl_console_cmd.c b/src/sys/nl_console_cmd.c new file mode 100644 index 0000000..5819895 --- /dev/null +++ b/src/sys/nl_console_cmd.c @@ -0,0 +1,120 @@ +#include "sys/nl_console_cmd.h" + +#include +#include +#include + +#define MAX_TOK 6 + +const char nl_console_help[] = + "on [ms] | off [ms] | bri <1-100> [ms] | rgb [ms] | ct [ms]\r\n" + "pob last|on|off | fade | blink [n] | pulse on|off\r\n" + "state | factory | reboot | help\r\n"; + +static bool num(const char *s, int32_t lo, int32_t hi, int32_t *out) +{ + char *end; + long v = strtol(s, &end, 10); + if (*s == '\0' || *end != '\0' || v < lo || v > hi) + return false; + *out = (int32_t)v; + return true; +} + +/* Parses tok[first..n-1] into arg[]: `req` required values in [lo,hi], optionally one + * trailing transition in [0,60000]. */ +static bool args(nl_cmd_t *c, char **tok, int n, int req, int32_t lo, int32_t hi, bool opt_ms) +{ + int given = n - 1; + if (given < req || given > req + (opt_ms ? 1 : 0)) + return false; + for (int i = 0; i < req; i++) + if (!num(tok[1 + i], lo, hi, &c->arg[i])) + return false; + if (given > req && !num(tok[1 + req], 0, 60000, &c->arg[req])) + return false; + c->argc = (uint8_t)given; + return true; +} + +static bool word(const char *s, const char *const *words, int count, int32_t *out) +{ + for (int i = 0; i < count; i++) + if (strcmp(s, words[i]) == 0) { + *out = i; + return true; + } + return false; +} + +nl_cmd_t nl_console_parse(const char *line) +{ + static const char *const POB[] = {"last", "on", "off"}; + static const char *const OFFON[] = {"off", "on"}; + nl_cmd_t c; + char buf[64]; + char *tok[MAX_TOK]; + char *save = NULL; + int n = 0; + bool ok; + + memset(&c, 0, sizeof c); + strncpy(buf, line, sizeof buf - 1); + buf[sizeof buf - 1] = '\0'; + for (char *t = strtok_r(buf, " \t\r\n", &save); t; t = strtok_r(NULL, " \t\r\n", &save)) { + if (n == MAX_TOK) { + c.type = NL_CMD_ERROR; + return c; + } + tok[n++] = t; + } + if (n == 0) + return c; /* NL_CMD_NONE */ + + if (!strcmp(tok[0], "on")) { + c.type = NL_CMD_ON; + ok = args(&c, tok, n, 0, 0, 0, true); + } else if (!strcmp(tok[0], "off")) { + c.type = NL_CMD_OFF; + ok = args(&c, tok, n, 0, 0, 0, true); + } else if (!strcmp(tok[0], "bri")) { + c.type = NL_CMD_BRI; + ok = args(&c, tok, n, 1, 1, 100, true); + } else if (!strcmp(tok[0], "rgb")) { + c.type = NL_CMD_RGB; + ok = args(&c, tok, n, 3, 0, 255, true); + } else if (!strcmp(tok[0], "ct")) { + c.type = NL_CMD_CT; + ok = args(&c, tok, n, 1, 1000, 20000, true); + } else if (!strcmp(tok[0], "fade")) { + c.type = NL_CMD_FADE; + ok = args(&c, tok, n, 2, 0, 10000, false); + } else if (!strcmp(tok[0], "pob")) { + c.type = NL_CMD_POB; + ok = n == 2 && word(tok[1], POB, 3, &c.arg[0]); + c.argc = 1; + } else if (!strcmp(tok[0], "pulse")) { + c.type = NL_CMD_PULSE; + ok = n == 2 && word(tok[1], OFFON, 2, &c.arg[0]); + c.argc = 1; + } else if (!strcmp(tok[0], "blink")) { + c.type = NL_CMD_BLINK; + c.arg[0] = 3; + c.argc = 1; + ok = n == 1 || (n == 2 && num(tok[1], 1, 10, &c.arg[0])); + } else { + static const struct { const char *name; nl_cmd_type_t type; } SIMPLE[] = { + {"state", NL_CMD_STATE}, {"factory", NL_CMD_FACTORY}, {"reboot", NL_CMD_REBOOT}, {"help", NL_CMD_HELP}}; + ok = false; + for (size_t i = 0; i < sizeof SIMPLE / sizeof SIMPLE[0]; i++) + if (!strcmp(tok[0], SIMPLE[i].name)) { + c.type = SIMPLE[i].type; + ok = n == 1; + } + } + if (!ok) { + memset(&c, 0, sizeof c); + c.type = NL_CMD_ERROR; + } + return c; +} diff --git a/src/sys/nl_console_cmd.h b/src/sys/nl_console_cmd.h new file mode 100644 index 0000000..a400451 --- /dev/null +++ b/src/sys/nl_console_cmd.h @@ -0,0 +1,34 @@ +#ifndef NL_CONSOLE_CMD_H +#define NL_CONSOLE_CMD_H + +#include + +typedef enum { + NL_CMD_NONE = 0, + NL_CMD_ON, + NL_CMD_OFF, + NL_CMD_BRI, + NL_CMD_RGB, + NL_CMD_CT, + NL_CMD_POB, + NL_CMD_FADE, + NL_CMD_STATE, + NL_CMD_BLINK, + NL_CMD_PULSE, + NL_CMD_FACTORY, + NL_CMD_REBOOT, + NL_CMD_HELP, + NL_CMD_ERROR +} nl_cmd_type_t; + +typedef struct { + nl_cmd_type_t type; + int32_t arg[4]; + uint8_t argc; +} nl_cmd_t; + +nl_cmd_t nl_console_parse(const char *line); + +extern const char nl_console_help[]; + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e2416fa..4aa35d1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -34,3 +34,4 @@ nl_test(test_settings test_settings.c fake_flash.c ${NL_SRC}/config/nl_settings. nl_test(test_state_log test_state_log.c fake_flash.c ${NL_SRC}/config/nl_state_log.c ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/util/nl_crc.c) nl_test(test_reset_counter test_reset_counter.c fake_flash.c ${NL_SRC}/config/nl_reset_counter.c) +nl_test(test_console_cmd test_console_cmd.c ${NL_SRC}/sys/nl_console_cmd.c) diff --git a/test/test_console_cmd.c b/test/test_console_cmd.c new file mode 100644 index 0000000..b8d1faf --- /dev/null +++ b/test/test_console_cmd.c @@ -0,0 +1,98 @@ +#include "unity.h" +#include "sys/nl_console_cmd.h" + +void setUp(void) {} +void tearDown(void) {} + +static void test_empty_and_unknown(void) +{ + TEST_ASSERT_EQUAL(NL_CMD_NONE, nl_console_parse("").type); + TEST_ASSERT_EQUAL(NL_CMD_NONE, nl_console_parse(" \r\n").type); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("dance").type); +} + +static void test_on_off(void) +{ + nl_cmd_t c = nl_console_parse("on"); + TEST_ASSERT_EQUAL(NL_CMD_ON, c.type); + TEST_ASSERT_EQUAL_UINT8(0, c.argc); + c = nl_console_parse("off 1500\r"); + TEST_ASSERT_EQUAL(NL_CMD_OFF, c.type); + TEST_ASSERT_EQUAL_UINT8(1, c.argc); + TEST_ASSERT_EQUAL_INT32(1500, c.arg[0]); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("on 60001").type); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("on abc").type); +} + +static void test_bri(void) +{ + nl_cmd_t c = nl_console_parse("bri 50 200"); + TEST_ASSERT_EQUAL(NL_CMD_BRI, c.type); + TEST_ASSERT_EQUAL_UINT8(2, c.argc); + TEST_ASSERT_EQUAL_INT32(50, c.arg[0]); + TEST_ASSERT_EQUAL_INT32(200, c.arg[1]); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("bri 0").type); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("bri").type); +} + +static void test_rgb(void) +{ + nl_cmd_t c = nl_console_parse("rgb 255 0 128 1000"); + TEST_ASSERT_EQUAL(NL_CMD_RGB, c.type); + TEST_ASSERT_EQUAL_UINT8(4, c.argc); + TEST_ASSERT_EQUAL_INT32(128, c.arg[2]); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("rgb 255 0").type); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("rgb 256 0 0").type); +} + +static void test_ct(void) +{ + nl_cmd_t c = nl_console_parse("ct 2700"); + TEST_ASSERT_EQUAL(NL_CMD_CT, c.type); + TEST_ASSERT_EQUAL_INT32(2700, c.arg[0]); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("ct 500").type); +} + +static void test_pob_fade_blink_pulse(void) +{ + TEST_ASSERT_EQUAL_INT32(0, nl_console_parse("pob last").arg[0]); + TEST_ASSERT_EQUAL_INT32(1, nl_console_parse("pob on").arg[0]); + TEST_ASSERT_EQUAL_INT32(2, nl_console_parse("pob off").arg[0]); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("pob maybe").type); + + nl_cmd_t c = nl_console_parse("fade 300 800"); + TEST_ASSERT_EQUAL(NL_CMD_FADE, c.type); + TEST_ASSERT_EQUAL_INT32(800, c.arg[1]); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("fade 300").type); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("fade 300 10001").type); + + c = nl_console_parse("blink"); + TEST_ASSERT_EQUAL(NL_CMD_BLINK, c.type); + TEST_ASSERT_EQUAL_INT32(3, c.arg[0]); + TEST_ASSERT_EQUAL_INT32(5, nl_console_parse("blink 5").arg[0]); + + TEST_ASSERT_EQUAL_INT32(1, nl_console_parse("pulse on").arg[0]); + TEST_ASSERT_EQUAL_INT32(0, nl_console_parse("pulse off").arg[0]); +} + +static void test_simple_commands(void) +{ + TEST_ASSERT_EQUAL(NL_CMD_STATE, nl_console_parse("state").type); + TEST_ASSERT_EQUAL(NL_CMD_FACTORY, nl_console_parse("factory").type); + TEST_ASSERT_EQUAL(NL_CMD_REBOOT, nl_console_parse("reboot").type); + TEST_ASSERT_EQUAL(NL_CMD_HELP, nl_console_parse("help").type); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("state now").type); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_empty_and_unknown); + RUN_TEST(test_on_off); + RUN_TEST(test_bri); + RUN_TEST(test_rgb); + RUN_TEST(test_ct); + RUN_TEST(test_pob_fade_blink_pulse); + RUN_TEST(test_simple_commands); + return UNITY_END(); +} From 757620f22237774706356da123f16a3ae54631f9 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 13:51:31 +0200 Subject: [PATCH 015/150] feat: LED-Treiber, Licht-Task und UART-Testkonsole --- CMakeLists.txt | 13 +++- docs/hardware-checklist.md | 28 +++++++ src/app_main.c | 94 ++++++++++++++++++++-- src/config/nl_flash_w600.c | 7 ++ src/drivers/nl_led.c | 24 ++++++ src/drivers/nl_led.h | 17 ++++ src/drivers/nl_pwm_leds.c | 94 ++++++++++++++++++++++ src/drivers/nl_sm2135eh.c | 87 +++++++++++++++++++++ src/sys/nl_app.h | 15 ++++ src/sys/nl_console.c | 154 +++++++++++++++++++++++++++++++++++++ src/sys/nl_console.h | 7 ++ 11 files changed, 530 insertions(+), 10 deletions(-) create mode 100644 docs/hardware-checklist.md create mode 100644 src/config/nl_flash_w600.c create mode 100644 src/drivers/nl_led.c create mode 100644 src/drivers/nl_led.h create mode 100644 src/drivers/nl_pwm_leds.c create mode 100644 src/drivers/nl_sm2135eh.c create mode 100644 src/sys/nl_app.h create mode 100644 src/sys/nl_console.c create mode 100644 src/sys/nl_console.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cc7a368..bcf99cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,13 +28,20 @@ set(NL_FW_SOURCES src/app_main.c src/util/nl_sha256.c src/util/nl_crc.c - src/sys/fwup_hook_default.c - src/sys/sdk_stubs.c src/board/nl_board.c src/light/nl_color.c src/light/nl_engine.c src/light/nl_control.c - src/light/nl_boot_state.c) + src/light/nl_boot_state.c + src/config/nl_settings.c + src/config/nl_flash_w600.c + src/drivers/nl_led.c + src/drivers/nl_pwm_leds.c + src/drivers/nl_sm2135eh.c + src/sys/nl_console_cmd.c + src/sys/nl_console.c + src/sys/fwup_hook_default.c + src/sys/sdk_stubs.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS}) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md new file mode 100644 index 0000000..9488be3 --- /dev/null +++ b/docs/hardware-checklist.md @@ -0,0 +1,28 @@ +# Hardware-Checkliste (EVK) + +Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.sh evk `. + +## Licht (Plan 1, Task 11) +- [ ] Nach dem Boot blendet warmweiß 100 % in 0,5 s ein +- [ ] `rgb 255 0 0` → nur Rot, `rgb 0 255 0` → nur Grün, `rgb 0 0 255` → nur Blau +- [ ] `ct 2700` → nur Warmweiß, `ct 6500` → nur Kaltweiß, `ct 4000` → Mischung +- [ ] `bri 1` sichtbar sehr dunkel, `bri 100` hell; `bri 1 3000` → gleichmäßig wirkender Fade +- [ ] `off 2000` → blendet mit aktueller Farbe aus; `on 2000` → blendet ein +- [ ] `rgb 255 0 0` dann `ct 3000 2000` → erst dunkel, dann weiß (Moduswechsel) +- [ ] `blink 3` → 3× Blinken, danach alter Zustand; `pulse on` / `pulse off` +- [ ] Kein sichtbares Flackern bei konstanten Werten (5 kHz) + +## Persistenz und Boot (Plan 1, Task 12) +- [ ] `pob last`: Farbe setzen, 4 s warten, Strom aus/an → gleicher Zustand, mit Fade +- [ ] `pob off`: Strom aus/an → bleibt aus; `on` → letzte Farbe +- [ ] `pob on` bei ausgeschaltetem Licht: Strom aus/an → an +- [ ] `fade 2000 2000` bleibt nach Reboot erhalten (`state`) +- [ ] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`state`) +- [ ] Normaler Boot nach > 5 s Laufzeit → kein Werksreset +- [ ] Watchdog: `reboot` funktioniert; nach einem Hänger (nicht provozierbar) wäre Reset nach 20 s + +## Später (Lampen) +- [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) +- [ ] E27 CW: Gegenphase CW sichtbar flackerfrei +- [ ] G95: Pinbelegung WW=PB15/CW=PB13 prüfen +- [ ] Farbtemperaturbereiche aller Varianten diff --git a/src/app_main.c b/src/app_main.c index 84d5377..5eeabf3 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -2,21 +2,101 @@ #include "wm_include.h" -#define NL_APP_TASK_PRIO 40 -static u32 s_app_stack[512]; +#include "board/nl_board.h" +#include "config/nl_settings.h" +#include "drivers/nl_led.h" +#include "light/nl_boot_state.h" +#include "light/nl_control.h" +#include "light/nl_engine.h" +#include "sys/nl_app.h" +#include "sys/nl_console.h" + +#define NL_LIGHT_PRIO 30 +#define NL_APP_PRIO 40 + +static tls_os_mutex_t *s_lock; +static nl_settings_t s_settings; +static volatile uint32_t s_light_beat; +static u32 s_app_stack[1024]; +static u32 s_light_stack[512]; + +void nl_app_lock(void) { tls_os_mutex_acquire(s_lock, 0); } +void nl_app_unlock(void) { tls_os_mutex_release(s_lock); } + +uint32_t nl_app_now_ms(void) +{ + return (uint32_t)((uint64_t)tls_os_get_time() * 1000u / HZ); +} + +nl_settings_t *nl_app_settings(void) { return &s_settings; } + +int nl_app_save_settings(void) +{ + return 0; /* persistence follows in task 12 */ +} + +void nl_app_factory_reset(void) +{ + printf("factory reset: not implemented yet\r\n"); +} + +static void led_out(const uint8_t ch[NL_CH_COUNT], void *ctx) +{ + (void)ctx; + nl_led_set(ch); +} + +static void light_task(void *arg) +{ + uint32_t last = nl_app_now_ms(); + (void)arg; + for (;;) { + uint32_t now; + tls_os_time_delay(HZ / 100 ? HZ / 100 : 1); /* ~10 ms */ + now = nl_app_now_ms(); + nl_app_lock(); + nl_engine_tick(now - last); + nl_app_unlock(); + last = now; + s_light_beat++; + } +} static void app_task(void *arg) { - unsigned int n = 0; + const nl_board_t *board = nl_board_get(); + nl_engine_cfg_t cfg; + nl_light_state_t initial; + (void)arg; - for (;;) { - printf("ninlight %s alive %u\r\n", NINLIGHT_DEVICE_ID, n++); + nl_settings_defaults(&s_settings); + + nl_led_init(board); + cfg.ct_min = board->ct_min; + cfg.ct_max = board->ct_max; + cfg.output = led_out; + cfg.ctx = NULL; + nl_app_lock(); + nl_engine_init(&cfg); + nl_ctrl_init(board, s_settings.fade_in_ms, s_settings.fade_out_ms); + initial = nl_boot_default_state(board); + nl_ctrl_boot(&initial); + nl_app_unlock(); + + tls_os_task_create(NULL, "nl_light", light_task, NULL, (u8 *)s_light_stack, sizeof(s_light_stack), NL_LIGHT_PRIO, 0); + nl_console_start(); + + for (;;) tls_os_time_delay(HZ); - } } void UserMain(void) { printf("\r\nninlight %s for %s (built %s)\r\n", NINLIGHT_FW_VERSION, NINLIGHT_DEVICE_ID, NINLIGHT_BUILD_DATE); - tls_os_task_create(NULL, "nl_app", app_task, NULL, (u8 *)s_app_stack, sizeof(s_app_stack), NL_APP_TASK_PRIO, 0); + if (nl_board_get() == NULL) { + printf("FATAL: unknown board %s\r\n", NINLIGHT_DEVICE_ID); + return; + } + tls_os_mutex_create(0, &s_lock); + tls_os_task_create(NULL, "nl_app", app_task, NULL, (u8 *)s_app_stack, sizeof(s_app_stack), NL_APP_PRIO, 0); } diff --git a/src/config/nl_flash_w600.c b/src/config/nl_flash_w600.c new file mode 100644 index 0000000..78a44e3 --- /dev/null +++ b/src/config/nl_flash_w600.c @@ -0,0 +1,7 @@ +#include "config/nl_flash.h" + +/* Temporary stub, replaced by the real W600 implementation in task 12. */ +int nl_flash_read(uint32_t addr, void *buf, uint32_t len) { (void)addr; (void)buf; (void)len; return -1; } +int nl_flash_write(uint32_t addr, const void *buf, uint32_t len) { (void)addr; (void)buf; (void)len; return -1; } +int nl_flash_erase_sector(uint32_t addr) { (void)addr; return -1; } +uint32_t nl_flash_region_base(void) { return 0; } diff --git a/src/drivers/nl_led.c b/src/drivers/nl_led.c new file mode 100644 index 0000000..cb7ef3f --- /dev/null +++ b/src/drivers/nl_led.c @@ -0,0 +1,24 @@ +#include "drivers/nl_led.h" + +#include + +static const nl_board_t *s_board; + +void nl_led_init(const nl_board_t *board) +{ + s_board = board; + if (board->driver == NL_DRV_PWM) + nl_pwm_leds_init(board); + else + nl_sm2135eh_init(board); +} + +void nl_led_set(const uint8_t ch[NL_CH_COUNT]) +{ + if (s_board == NULL) + return; + if (s_board->driver == NL_DRV_PWM) + nl_pwm_leds_set(ch); + else + nl_sm2135eh_set(ch); +} diff --git a/src/drivers/nl_led.h b/src/drivers/nl_led.h new file mode 100644 index 0000000..3d14fd8 --- /dev/null +++ b/src/drivers/nl_led.h @@ -0,0 +1,17 @@ +#ifndef NL_LED_H +#define NL_LED_H + +#include + +#include "board/nl_board.h" + +void nl_led_init(const nl_board_t *board); +void nl_led_set(const uint8_t ch[NL_CH_COUNT]); + +/* back ends */ +void nl_pwm_leds_init(const nl_board_t *board); +void nl_pwm_leds_set(const uint8_t ch[NL_CH_COUNT]); +void nl_sm2135eh_init(const nl_board_t *board); +void nl_sm2135eh_set(const uint8_t ch[NL_CH_COUNT]); + +#endif diff --git a/src/drivers/nl_pwm_leds.c b/src/drivers/nl_pwm_leds.c new file mode 100644 index 0000000..85b2459 --- /dev/null +++ b/src/drivers/nl_pwm_leds.c @@ -0,0 +1,94 @@ +#include + +#include "wm_include.h" +#include "wm_gpio_afsel.h" + +#include "drivers/nl_led.h" + +static const nl_board_t *s_board; +static bool s_on_pwm[NL_CH_COUNT]; /* pin currently routed to the PWM unit */ +static uint8_t s_last[NL_CH_COUNT]; +static bool s_have_last; + +static void route_to_pwm(int8_t ch, uint8_t pin) +{ + enum tls_io_name io = (enum tls_io_name)pin; + switch (ch) { + case 0: wm_pwm1_config(io); break; + case 1: wm_pwm2_config(io); break; + case 2: wm_pwm3_config(io); break; + case 3: wm_pwm4_config(io); break; + case 4: wm_pwm5_config(io); break; + default: break; + } +} + +static void drive_gpio(const nl_pwm_chan_t *c, bool led_on) +{ + bool level = c->active_low ? !led_on : led_on; + tls_gpio_cfg((enum tls_io_name)c->pin, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_FLOATING); + tls_gpio_write((enum tls_io_name)c->pin, level ? 1 : 0); +} + +void nl_pwm_leds_init(const nl_board_t *board) +{ + s_board = board; + s_have_last = false; + for (int i = 0; i < NL_CH_COUNT; i++) { + const nl_pwm_chan_t *c = &board->pwm[i]; + s_on_pwm[i] = false; + if (c->pwm_ch < 0) + continue; + if (c->antiphase) + tls_pwm_ineverse_init((u8)c->pwm_ch, board->pwm_freq_hz, 128, 0); + else + tls_pwm_init((u8)c->pwm_ch, board->pwm_freq_hz, 128, 0); + tls_pwm_start((u8)c->pwm_ch); + drive_gpio(c, false); /* dark until the engine sets a value */ + } +} + +void nl_pwm_leds_set(const uint8_t ch[NL_CH_COUNT]) +{ + u32 cpu_sr; + uint32_t cmp, ch4; + + if (s_have_last && memcmp(ch, s_last, NL_CH_COUNT) == 0) + return; + memcpy(s_last, ch, NL_CH_COUNT); + s_have_last = true; + + /* 1. update all duty cycles at once */ + cpu_sr = tls_os_set_critical(); + cmp = tls_reg_read32(HR_PWM_CMPDAT); + ch4 = tls_reg_read32(HR_PWM_CH4_REG2); + for (int i = 0; i < NL_CH_COUNT; i++) { + const nl_pwm_chan_t *c = &s_board->pwm[i]; + uint8_t v = ch[i], duty; + if (c->pwm_ch < 0 || v == 0 || v == 255) + continue; + duty = (c->active_low != c->antiphase) ? (uint8_t)(255 - v) : v; + if (c->pwm_ch == 4) + ch4 = (ch4 & ~0x0000FF00u) | ((uint32_t)duty << 8); + else + cmp = (cmp & ~(0xFFu << (c->pwm_ch * 8))) | ((uint32_t)duty << (c->pwm_ch * 8)); + } + tls_reg_write32(HR_PWM_CMPDAT, cmp); + tls_reg_write32(HR_PWM_CH4_REG2, ch4); + tls_os_release_critical(cpu_sr); + + /* 2. switch pins between GPIO (0 / 255) and PWM */ + for (int i = 0; i < NL_CH_COUNT; i++) { + const nl_pwm_chan_t *c = &s_board->pwm[i]; + uint8_t v = ch[i]; + if (c->pwm_ch < 0) + continue; + if (v == 0 || v == 255) { + drive_gpio(c, v == 255); + s_on_pwm[i] = false; + } else if (!s_on_pwm[i]) { + route_to_pwm(c->pwm_ch, c->pin); + s_on_pwm[i] = true; + } + } +} diff --git a/src/drivers/nl_sm2135eh.c b/src/drivers/nl_sm2135eh.c new file mode 100644 index 0000000..da0f95e --- /dev/null +++ b/src/drivers/nl_sm2135eh.c @@ -0,0 +1,87 @@ +#include + +#include "wm_include.h" + +#include "drivers/nl_led.h" + +#define SM_ADDR_CURRENT 0xC0u +#define SM_ADDR_WY 0xC5u +#define SM_CHANNEL_WY 0x80u /* channel select bit 7: 0 = RGB, 1 = WY */ + +extern void delay_us(unsigned int us); /* provided by libwlan.a */ + +static uint8_t s_clk, s_dat, s_current; +static uint8_t s_last[NL_CH_COUNT]; +static bool s_have_last; + +static void pin(uint8_t p, int v) +{ + tls_gpio_write((enum tls_io_name)p, (u8)v); +} + +static void bus_start(void) +{ + pin(s_clk, 1); pin(s_dat, 1); delay_us(1); + pin(s_dat, 0); delay_us(1); + pin(s_clk, 0); delay_us(1); +} + +static void bus_stop(void) +{ + pin(s_clk, 0); pin(s_dat, 0); delay_us(1); + pin(s_clk, 1); delay_us(1); + pin(s_dat, 1); delay_us(1); +} + +static void write_byte(uint8_t b) +{ + for (uint8_t m = 0x80; m; m >>= 1) { + pin(s_dat, (b & m) ? 1 : 0); delay_us(1); + pin(s_clk, 1); delay_us(1); + pin(s_clk, 0); delay_us(1); + } + /* ack clock; the SM2135EH does not drive an ack */ + pin(s_dat, 0); delay_us(1); + pin(s_clk, 1); delay_us(1); + pin(s_clk, 0); pin(s_dat, 0); +} + +static void sm_send(const uint8_t *d, int n) +{ + bus_start(); + for (int i = 0; i < n; i++) + write_byte(d[i]); + bus_stop(); +} + +void nl_sm2135eh_init(const nl_board_t *board) +{ + uint8_t off[NL_CH_COUNT] = {0}; + s_clk = board->sm_clk_pin; + s_dat = board->sm_dat_pin; + s_current = board->sm_current; + s_have_last = false; + tls_gpio_cfg((enum tls_io_name)s_clk, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + tls_gpio_cfg((enum tls_io_name)s_dat, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH); + nl_sm2135eh_set(off); +} + +void nl_sm2135eh_set(const uint8_t ch[NL_CH_COUNT]) +{ + bool white = ch[NL_CH_CW] || ch[NL_CH_WW]; + + if (s_have_last && memcmp(ch, s_last, NL_CH_COUNT) == 0) + return; + memcpy(s_last, ch, NL_CH_COUNT); + s_have_last = true; + + if (!white) { + uint8_t d[6] = {SM_ADDR_CURRENT, s_current, 0x00, ch[NL_CH_B], ch[NL_CH_G], ch[NL_CH_R]}; + sm_send(d, 6); + } else { + uint8_t h[3] = {SM_ADDR_CURRENT, s_current, SM_CHANNEL_WY}; + uint8_t d[3] = {SM_ADDR_WY, ch[NL_CH_WW], ch[NL_CH_CW]}; + sm_send(h, 3); + sm_send(d, 3); + } +} diff --git a/src/sys/nl_app.h b/src/sys/nl_app.h new file mode 100644 index 0000000..548bf54 --- /dev/null +++ b/src/sys/nl_app.h @@ -0,0 +1,15 @@ +#ifndef NL_APP_H +#define NL_APP_H + +#include + +#include "config/nl_settings.h" + +void nl_app_lock(void); +void nl_app_unlock(void); +uint32_t nl_app_now_ms(void); +nl_settings_t *nl_app_settings(void); +int nl_app_save_settings(void); +void nl_app_factory_reset(void); + +#endif diff --git a/src/sys/nl_console.c b/src/sys/nl_console.c new file mode 100644 index 0000000..2b8b61f --- /dev/null +++ b/src/sys/nl_console.c @@ -0,0 +1,154 @@ +#include +#include + +#include "wm_include.h" +#include "wm_watchdog.h" + +#include "light/nl_control.h" +#include "light/nl_engine.h" +#include "sys/nl_app.h" +#include "sys/nl_console.h" +#include "sys/nl_console_cmd.h" + +#define NL_CONSOLE_PRIO 45 +static u32 s_stack[768]; + +static void print_state(void) +{ + nl_light_state_t s; + nl_settings_t *cfg = nl_app_settings(); + nl_app_lock(); + nl_ctrl_get(&s); + nl_app_unlock(); + printf("state: %s bri=%u mode=%s rgb=%u,%u,%u ct=%u | pob=%u fade=%u/%u\r\n", + s.on ? "on" : "off", s.brightness, s.mode == NL_MODE_COLOR ? "color" : "white", + s.r, s.g, s.b, s.ct, cfg->power_on_behaviour, cfg->fade_in_ms, cfg->fade_out_ms); +} + +static void set_transition(nl_light_change_t *ch, const nl_cmd_t *c, uint8_t index) +{ + if (c->argc > index) { + ch->has_transition = true; + ch->transition_ms = (uint32_t)c->arg[index]; + } +} + +static void exec(const nl_cmd_t *c) +{ + nl_light_change_t ch; + nl_ctrl_err_t err; + nl_settings_t *cfg = nl_app_settings(); + + memset(&ch, 0, sizeof ch); + switch (c->type) { + case NL_CMD_NONE: + return; + case NL_CMD_ON: + case NL_CMD_OFF: + ch.has_on = true; + ch.on = c->type == NL_CMD_ON; + set_transition(&ch, c, 0); + break; + case NL_CMD_BRI: + ch.has_brightness = true; + ch.brightness = (uint8_t)c->arg[0]; + set_transition(&ch, c, 1); + break; + case NL_CMD_RGB: + ch.has_color = true; + ch.r = (uint8_t)c->arg[0]; + ch.g = (uint8_t)c->arg[1]; + ch.b = (uint8_t)c->arg[2]; + set_transition(&ch, c, 3); + break; + case NL_CMD_CT: + ch.has_ct = true; + ch.ct = (uint16_t)c->arg[0]; + set_transition(&ch, c, 1); + break; + case NL_CMD_POB: + nl_app_lock(); + cfg->power_on_behaviour = (uint8_t)c->arg[0]; + nl_app_unlock(); + printf(nl_app_save_settings() == 0 ? "ok\r\n" : "error: save failed\r\n"); + print_state(); + return; + case NL_CMD_FADE: + nl_app_lock(); + cfg->fade_in_ms = (uint16_t)c->arg[0]; + cfg->fade_out_ms = (uint16_t)c->arg[1]; + nl_ctrl_set_fades(cfg->fade_in_ms, cfg->fade_out_ms); + nl_app_unlock(); + printf(nl_app_save_settings() == 0 ? "ok\r\n" : "error: save failed\r\n"); + print_state(); + return; + case NL_CMD_STATE: + print_state(); + return; + case NL_CMD_BLINK: + nl_app_lock(); + nl_engine_blink((uint8_t)c->arg[0]); + nl_app_unlock(); + return; + case NL_CMD_PULSE: + nl_app_lock(); + nl_engine_pulse(c->arg[0] != 0); + nl_app_unlock(); + return; + case NL_CMD_FACTORY: + nl_app_factory_reset(); + return; + case NL_CMD_REBOOT: + tls_sys_reset(); + return; + case NL_CMD_HELP: + printf("%s", nl_console_help); + return; + default: + printf("error: unknown command or bad argument (try help)\r\n"); + return; + } + + nl_app_lock(); + err = nl_ctrl_apply(&ch, nl_app_now_ms()); + nl_app_unlock(); + if (err == NL_CTRL_OK) + printf("ok\r\n"); + else + printf("error %d\r\n", (int)err); + print_state(); +} + +static void console_task(void *arg) +{ + char line[64]; + int len = 0; + u8 buf[16]; + + (void)arg; + printf("console ready, type help\r\n"); + for (;;) { + int n = tls_uart_read(TLS_UART_0, buf, sizeof buf); + for (int i = 0; i < n; i++) { + char c = (char)buf[i]; + if (c == '\r' || c == '\n') { + if (len) { + nl_cmd_t cmd; + line[len] = '\0'; + cmd = nl_console_parse(line); + exec(&cmd); + len = 0; + } + } else if (len < (int)sizeof(line) - 1) { + line[len++] = c; + } + } + tls_os_time_delay(HZ / 50); /* 20 ms */ + } +} + +void nl_console_start(void) +{ + tls_uart_port_init(TLS_UART_0, NULL, 0); + tls_os_task_create(NULL, "nl_con", console_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_CONSOLE_PRIO, 0); +} diff --git a/src/sys/nl_console.h b/src/sys/nl_console.h new file mode 100644 index 0000000..767d380 --- /dev/null +++ b/src/sys/nl_console.h @@ -0,0 +1,7 @@ +#ifndef NL_CONSOLE_H +#define NL_CONSOLE_H + +/* Line based test console on UART0 (115200 8N1), shares the port with printf. */ +void nl_console_start(void); + +#endif From 55b162b0a0de7d5d243cf7d661c101dc3613b50d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 16:01:41 +0200 Subject: [PATCH 016/150] feat: Persistenz, Einschaltverhalten, Werksreset und Watchdog --- CMakeLists.txt | 2 ++ src/app_main.c | 74 ++++++++++++++++++++++++++++++++++---- src/config/nl_flash_w600.c | 32 ++++++++++++++--- 3 files changed, 96 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bcf99cc..5978588 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ set(NL_FW_SOURCES src/light/nl_control.c src/light/nl_boot_state.c src/config/nl_settings.c + src/config/nl_state_log.c + src/config/nl_reset_counter.c src/config/nl_flash_w600.c src/drivers/nl_led.c src/drivers/nl_pwm_leds.c diff --git a/src/app_main.c b/src/app_main.c index 5eeabf3..9c7ef10 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -1,9 +1,14 @@ #include #include "wm_include.h" +#include "wm_watchdog.h" #include "board/nl_board.h" +#include "config/nl_flash.h" +#include "config/nl_flash_layout.h" +#include "config/nl_reset_counter.h" #include "config/nl_settings.h" +#include "config/nl_state_log.h" #include "drivers/nl_led.h" #include "light/nl_boot_state.h" #include "light/nl_control.h" @@ -32,12 +37,24 @@ nl_settings_t *nl_app_settings(void) { return &s_settings; } int nl_app_save_settings(void) { - return 0; /* persistence follows in task 12 */ + int r; + nl_app_lock(); + r = nl_settings_save(&s_settings); + nl_app_unlock(); + return r; } void nl_app_factory_reset(void) { - printf("factory reset: not implemented yet\r\n"); + printf("factory reset\r\n"); + nl_app_lock(); + nl_engine_blink(3); + nl_app_unlock(); + tls_os_time_delay(2 * HZ); /* 3 blinks = 1.8 s, the light task keeps running */ + nl_settings_erase(); + nl_state_log_erase(); + nl_resetcnt_clear(); + tls_sys_reset(); } static void led_out(const uint8_t ch[NL_CH_COUNT], void *ctx) @@ -65,29 +82,72 @@ static void light_task(void *arg) static void app_task(void *arg) { const nl_board_t *board = nl_board_get(); + uint32_t base = nl_flash_region_base(); nl_engine_cfg_t cfg; - nl_light_state_t initial; + nl_light_state_t last, initial, pending; + bool have_last, counter_cleared = false; + uint8_t boots; + uint32_t boot_ms, beat_seen; (void)arg; - nl_settings_defaults(&s_settings); + nl_resetcnt_init(base + NL_FL_RESETCNT); + boots = nl_resetcnt_boot(); + nl_settings_init(base + NL_FL_SETTINGS_A, base + NL_FL_SETTINGS_B); + nl_settings_load(&s_settings); + nl_state_log_init(base + NL_FL_STATE_0, base + NL_FL_STATE_1); + have_last = nl_state_log_load(&last); + printf("boot #%u, settings: pob=%u fade=%u/%u, last state: %s\r\n", boots, + s_settings.power_on_behaviour, s_settings.fade_in_ms, s_settings.fade_out_ms, + have_last ? "yes" : "no"); nl_led_init(board); cfg.ct_min = board->ct_min; cfg.ct_max = board->ct_max; cfg.output = led_out; cfg.ctx = NULL; + initial = nl_boot_state((nl_pob_t)s_settings.power_on_behaviour, have_last ? &last : NULL, board); nl_app_lock(); nl_engine_init(&cfg); nl_ctrl_init(board, s_settings.fade_in_ms, s_settings.fade_out_ms); - initial = nl_boot_default_state(board); nl_ctrl_boot(&initial); nl_app_unlock(); tls_os_task_create(NULL, "nl_light", light_task, NULL, (u8 *)s_light_stack, sizeof(s_light_stack), NL_LIGHT_PRIO, 0); + + if (boots >= NL_FACTORY_RESET_BOOTS) + nl_app_factory_reset(); /* does not return */ + + tls_watchdog_init(20u * 1000u * 1000u); /* 20 s */ nl_console_start(); - for (;;) - tls_os_time_delay(HZ); + boot_ms = nl_app_now_ms(); + beat_seen = s_light_beat; + for (;;) { + uint32_t now; + bool due; + + tls_os_time_delay(HZ / 10); /* 100 ms */ + now = nl_app_now_ms(); + + if (!counter_cleared && now - boot_ms >= NL_RESETCNT_CLEAR_MS) { + nl_resetcnt_clear(); + counter_cleared = true; + } + + nl_app_lock(); + due = nl_ctrl_persist_due(now, &pending); + nl_app_unlock(); + if (due && nl_state_log_append(&pending) == 0) { + nl_app_lock(); + nl_ctrl_persisted(&pending); + nl_app_unlock(); + } + + if (s_light_beat != beat_seen) { + beat_seen = s_light_beat; + tls_watchdog_clr(); + } + } } void UserMain(void) diff --git a/src/config/nl_flash_w600.c b/src/config/nl_flash_w600.c index 78a44e3..20d7518 100644 --- a/src/config/nl_flash_w600.c +++ b/src/config/nl_flash_w600.c @@ -1,7 +1,29 @@ +#include "wm_include.h" +#include "wm_flash_map.h" +#include "wm_internal_flash.h" + #include "config/nl_flash.h" -/* Temporary stub, replaced by the real W600 implementation in task 12. */ -int nl_flash_read(uint32_t addr, void *buf, uint32_t len) { (void)addr; (void)buf; (void)len; return -1; } -int nl_flash_write(uint32_t addr, const void *buf, uint32_t len) { (void)addr; (void)buf; (void)len; return -1; } -int nl_flash_erase_sector(uint32_t addr) { (void)addr; return -1; } -uint32_t nl_flash_region_base(void) { return 0; } +int nl_flash_read(uint32_t addr, void *buf, uint32_t len) +{ + return tls_fls_read(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; +} + +int nl_flash_write(uint32_t addr, const void *buf, uint32_t len) +{ + /* tls_fls_write only erases when the target is not 0xFF; the stores always write erased areas. */ + return tls_fls_write(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; +} + +int nl_flash_erase_sector(uint32_t addr) +{ + if (addr % NL_FLASH_SECTOR_SIZE) + return -1; + return tls_fls_erase(addr / INSIDE_FLS_SECTOR_SIZE) == TLS_FLS_STATUS_OK ? 0 : -1; +} + +uint32_t nl_flash_region_base(void) +{ + /* The first user sector is TLS_FLASH_PARAM_DEFAULT (SDK), ninlight starts one sector later. */ + return USER_ADDR_START + NL_FLASH_SECTOR_SIZE; +} From 21dcdcb13b0324c751e32cfbe81ddf53f11edc00 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 16:10:09 +0200 Subject: [PATCH 017/150] fix: Flash-Zugriffe der ninlight-Tasks per Mutex serialisieren --- src/config/nl_flash_w600.c | 54 +++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/config/nl_flash_w600.c b/src/config/nl_flash_w600.c index 20d7518..ce122aa 100644 --- a/src/config/nl_flash_w600.c +++ b/src/config/nl_flash_w600.c @@ -4,22 +4,70 @@ #include "config/nl_flash.h" +/* + * The ninlight flash region lies above FLASH_1M_END_ADDR, where the SDK routes + * tls_fls_read/write/erase to the external SPI flash path without taking its own + * fls_lock (see sdk/Platform/Drivers/internalflash/wm_internal_fls.c). The app task + * (periodic nl_state_log_append) and the console task (nl_app_save_settings, + * nl_app_factory_reset) can therefore issue concurrent SPI transactions and corrupt + * each other. Serialise all flash accesses with our own mutex, created lazily on + * first use so no caller has to be changed. + */ +static tls_os_mutex_t *s_flash_lock; + +static tls_os_mutex_t *nl_flash_lock(void) +{ + u32 cpu_sr; + tls_os_mutex_t *lock; + + cpu_sr = tls_os_set_critical(); + if (s_flash_lock == NULL) + tls_os_mutex_create(0, &s_flash_lock); + lock = s_flash_lock; + tls_os_release_critical(cpu_sr); + + return lock; +} + int nl_flash_read(uint32_t addr, void *buf, uint32_t len) { - return tls_fls_read(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; + tls_os_mutex_t *lock = nl_flash_lock(); + int r; + + tls_os_mutex_acquire(lock, 0); + r = tls_fls_read(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; + tls_os_mutex_release(lock); + + return r; } int nl_flash_write(uint32_t addr, const void *buf, uint32_t len) { /* tls_fls_write only erases when the target is not 0xFF; the stores always write erased areas. */ - return tls_fls_write(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; + tls_os_mutex_t *lock = nl_flash_lock(); + int r; + + tls_os_mutex_acquire(lock, 0); + r = tls_fls_write(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; + tls_os_mutex_release(lock); + + return r; } int nl_flash_erase_sector(uint32_t addr) { + tls_os_mutex_t *lock; + int r; + if (addr % NL_FLASH_SECTOR_SIZE) return -1; - return tls_fls_erase(addr / INSIDE_FLS_SECTOR_SIZE) == TLS_FLS_STATUS_OK ? 0 : -1; + + lock = nl_flash_lock(); + tls_os_mutex_acquire(lock, 0); + r = tls_fls_erase(addr / INSIDE_FLS_SECTOR_SIZE) == TLS_FLS_STATUS_OK ? 0 : -1; + tls_os_mutex_release(lock); + + return r; } uint32_t nl_flash_region_base(void) From 7502f4f02239ba8afe084b37db28e49aab8b35bd Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 16:14:15 +0200 Subject: [PATCH 018/150] fix: Flash-Mutex beim Boot statt im kritischen Abschnitt anlegen --- src/app_main.c | 1 + src/config/nl_flash.h | 3 +++ src/config/nl_flash_w600.c | 44 ++++++++++++++++++-------------------- test/fake_flash.c | 4 ++++ 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/app_main.c b/src/app_main.c index 9c7ef10..2a21dd7 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -90,6 +90,7 @@ static void app_task(void *arg) uint32_t boot_ms, beat_seen; (void)arg; + nl_flash_init(); nl_resetcnt_init(base + NL_FL_RESETCNT); boots = nl_resetcnt_boot(); nl_settings_init(base + NL_FL_SETTINGS_A, base + NL_FL_SETTINGS_B); diff --git a/src/config/nl_flash.h b/src/config/nl_flash.h index 5003a58..0b9e324 100644 --- a/src/config/nl_flash.h +++ b/src/config/nl_flash.h @@ -5,6 +5,9 @@ #define NL_FLASH_SECTOR_SIZE 4096u +/* Must be called once before any other nl_flash_* call, before other tasks start. */ +void nl_flash_init(void); + /* All functions return 0 on success, < 0 on error. Addresses are absolute. */ int nl_flash_read(uint32_t addr, void *buf, uint32_t len); /* The target bytes must be erased (0xFF); the stores never rely on implicit erases. */ diff --git a/src/config/nl_flash_w600.c b/src/config/nl_flash_w600.c index ce122aa..453ff02 100644 --- a/src/config/nl_flash_w600.c +++ b/src/config/nl_flash_w600.c @@ -10,33 +10,30 @@ * fls_lock (see sdk/Platform/Drivers/internalflash/wm_internal_fls.c). The app task * (periodic nl_state_log_append) and the console task (nl_app_save_settings, * nl_app_factory_reset) can therefore issue concurrent SPI transactions and corrupt - * each other. Serialise all flash accesses with our own mutex, created lazily on - * first use so no caller has to be changed. + * each other. Serialise all flash accesses with our own mutex. + * + * The mutex is created once in nl_flash_init(), called as the very first statement + * of app_task() before any other task can touch flash - not lazily from inside a + * critical section, since tls_os_mutex_create() allocates and can block, which must + * not happen with interrupts disabled. If nl_flash_init() was forgotten (s_flash_lock + * still NULL), the accessors fall through unlocked rather than crash. */ static tls_os_mutex_t *s_flash_lock; -static tls_os_mutex_t *nl_flash_lock(void) +void nl_flash_init(void) { - u32 cpu_sr; - tls_os_mutex_t *lock; - - cpu_sr = tls_os_set_critical(); - if (s_flash_lock == NULL) - tls_os_mutex_create(0, &s_flash_lock); - lock = s_flash_lock; - tls_os_release_critical(cpu_sr); - - return lock; + tls_os_mutex_create(0, &s_flash_lock); } int nl_flash_read(uint32_t addr, void *buf, uint32_t len) { - tls_os_mutex_t *lock = nl_flash_lock(); int r; - tls_os_mutex_acquire(lock, 0); + if (s_flash_lock) + tls_os_mutex_acquire(s_flash_lock, 0); r = tls_fls_read(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; - tls_os_mutex_release(lock); + if (s_flash_lock) + tls_os_mutex_release(s_flash_lock); return r; } @@ -44,28 +41,29 @@ int nl_flash_read(uint32_t addr, void *buf, uint32_t len) int nl_flash_write(uint32_t addr, const void *buf, uint32_t len) { /* tls_fls_write only erases when the target is not 0xFF; the stores always write erased areas. */ - tls_os_mutex_t *lock = nl_flash_lock(); int r; - tls_os_mutex_acquire(lock, 0); + if (s_flash_lock) + tls_os_mutex_acquire(s_flash_lock, 0); r = tls_fls_write(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; - tls_os_mutex_release(lock); + if (s_flash_lock) + tls_os_mutex_release(s_flash_lock); return r; } int nl_flash_erase_sector(uint32_t addr) { - tls_os_mutex_t *lock; int r; if (addr % NL_FLASH_SECTOR_SIZE) return -1; - lock = nl_flash_lock(); - tls_os_mutex_acquire(lock, 0); + if (s_flash_lock) + tls_os_mutex_acquire(s_flash_lock, 0); r = tls_fls_erase(addr / INSIDE_FLS_SECTOR_SIZE) == TLS_FLS_STATUS_OK ? 0 : -1; - tls_os_mutex_release(lock); + if (s_flash_lock) + tls_os_mutex_release(s_flash_lock); return r; } diff --git a/test/fake_flash.c b/test/fake_flash.c index 58f2296..a827f76 100644 --- a/test/fake_flash.c +++ b/test/fake_flash.c @@ -20,6 +20,10 @@ void fake_flash_fail_after(long bytes) s_fail_after = bytes; } +void nl_flash_init(void) +{ +} + int nl_flash_read(uint32_t addr, void *buf, uint32_t len) { if (addr + len > FAKE_FLASH_SIZE) From b986bb10b9b666136f1a9fe68087fcc808e80d19 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 16:30:47 +0200 Subject: [PATCH 019/150] =?UTF-8?q?build:=20Build=20aller=20Ger=C3=A4te=20?= =?UTF-8?q?und=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++ tools/build_all.sh | 13 +++++++++++ 2 files changed, 70 insertions(+) create mode 100644 README.md create mode 100755 tools/build_all.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..ac375f1 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# ninlight + +Lokale Firmware für die alten Klyqa-Lampen mit WinnerMicro W600 (E27/E14/GU10 RGB+CW, E27/E14/GU10 CW, G95 Vintage) und das W600-EVK. + +## Voraussetzungen + +- **Docker Desktop** (primärer Build-Pfad, erste Ausführung baut das Image `ninlight-build`) +- **CMake ≥ 3.20**, Ninja, host C compiler (für Host-Tests) +- **uv** (für die serielle Konsole) +- ARM GNU Toolchain mit newlib: `brew install --cask gcc-arm-embedded` (nur Fallback für nativen Build) + +## Bauen + +Docker (empfohlen – kein lokaler ARM-Compiler nötig): + + tools/docker.sh cmake -S . -B build/evk -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi.cmake -DNINLIGHT_DEVICE=evk + tools/docker.sh cmake --build build/evk + tools/docker.sh tools/build_all.sh # alle 8 Geräte + +Native Builds (mit lokal installierter ARM-Toolchain): + + cmake -S . -B build/evk -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi.cmake -DNINLIGHT_DEVICE=evk + cmake --build build/evk + +**Geräte:** evk, e27_rgb, e14_rgb, gu10_rgb, e27_cw, e14_cw, gu10_cw, g95_cw. + +**Lokale Einstellungen** (Token, später WLAN): `config.local.cmake.example` → `config.local.cmake`. + +## Tests + +Host-basierte Unit-Tests (laufen nativ auf dem Mac): + + tools/test_host.sh + +## Flashen und Konsole + +Verfügbare Ports anzeigen: + + build/host-tools/wm_tool -l + +Firmware flashen: + + tools/flash.sh + +Serielle Konsole (115200 8N1, lokales Echo): + + tools/monitor.sh /dev/cu. + +Verfügbare Befehle in der Konsole: `help`. + +## Werksreset + +Gerät 5× hintereinander einschalten, jeweils kürzer als 5 Sekunden. + +## Hardwaretests + +Manuelle Checkliste: `docs/hardware-checklist.md`. diff --git a/tools/build_all.sh b/tools/build_all.sh new file mode 100755 index 0000000..b0a65bf --- /dev/null +++ b/tools/build_all.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Builds the firmware for all devices. BUILD_ROOT overrides the output directory (default: build). +set -euo pipefail +ROOT=$(cd "$(dirname "$0")/.." && pwd) +OUT=${BUILD_ROOT:-$ROOT/build} +DEVICES=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw) +for d in "${DEVICES[@]}"; do + echo "== $d" + cmake -S "$ROOT" -B "$OUT/$d" -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="$ROOT/cmake/arm-none-eabi.cmake" -DNINLIGHT_DEVICE="$d" >/dev/null + cmake --build "$OUT/$d" +done +ls -1 "$OUT"/*/ninlight_*.fls From 64acc6b1d93dd5fccf754aebe0ad19db648ffd36 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 16:45:15 +0200 Subject: [PATCH 020/150] fix: Flash-Skript an EVK-Befund angepasst, Checkliste abgehakt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Flash-Skript flasht jetzt das App-Image (ninlight__gz.img) mit 460800 baud - -rs none statt -rs rts, Reset-Taste manuell nötig - Optionales Argument 'full' für Vollbild-Flash (blank chips nur) - Checkliste Light und Persistenz als bestanden markiert, Testdatum notiert --- README.md | 6 +++++- docs/hardware-checklist.md | 29 +++++++++++++++-------------- tools/flash.sh | 18 +++++++++++++----- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index ac375f1..c543ac4 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ Docker (empfohlen – kein lokaler ARM-Compiler nötig): tools/docker.sh cmake --build build/evk tools/docker.sh tools/build_all.sh # alle 8 Geräte +Für die tägliche Entwicklung: nur das Gerät bauen, das du verwendest (z.B. `tools/docker.sh cmake --build build/evk`). Der emulierte Docker-Build ist langsam — `build_all.sh` ist nur für Releases gedacht. + Native Builds (mit lokal installierter ARM-Toolchain): cmake -S . -B build/evk -G Ninja -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi.cmake -DNINLIGHT_DEVICE=evk @@ -38,10 +40,12 @@ Verfügbare Ports anzeigen: build/host-tools/wm_tool -l -Firmware flashen: +Firmware flashen (Standard: App-Image): tools/flash.sh +Das Skript druckt `Bitte bei "please manually reset the device" die Reset-Taste drücken.` — wenn wm_tool diese Nachricht zeigt, musst du die Reset-Taste am EVK drücken (Baud 460800). Für blank/erased Chips: `tools/flash.sh full` (flasht stattdessen das Vollbild mit secboot). + Serielle Konsole (115200 8N1, lokales Echo): tools/monitor.sh /dev/cu. diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 9488be3..7d41c5c 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -3,23 +3,24 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.sh evk `. ## Licht (Plan 1, Task 11) -- [ ] Nach dem Boot blendet warmweiß 100 % in 0,5 s ein -- [ ] `rgb 255 0 0` → nur Rot, `rgb 0 255 0` → nur Grün, `rgb 0 0 255` → nur Blau -- [ ] `ct 2700` → nur Warmweiß, `ct 6500` → nur Kaltweiß, `ct 4000` → Mischung -- [ ] `bri 1` sichtbar sehr dunkel, `bri 100` hell; `bri 1 3000` → gleichmäßig wirkender Fade -- [ ] `off 2000` → blendet mit aktueller Farbe aus; `on 2000` → blendet ein -- [ ] `rgb 255 0 0` dann `ct 3000 2000` → erst dunkel, dann weiß (Moduswechsel) -- [ ] `blink 3` → 3× Blinken, danach alter Zustand; `pulse on` / `pulse off` -- [ ] Kein sichtbares Flackern bei konstanten Werten (5 kHz) +- [x] Nach dem Boot blendet warmweiß 100 % in 0,5 s ein +- [x] `rgb 255 0 0` → nur Rot, `rgb 0 255 0` → nur Grün, `rgb 0 0 255` → nur Blau +- [x] `ct 2700` → nur Warmweiß, `ct 6500` → nur Kaltweiß, `ct 4000` → Mischung +- [x] `bri 1` sichtbar sehr dunkel, `bri 100` hell; `bri 1 3000` → gleichmäßig wirkender Fade +- [x] `off 2000` → blendet mit aktueller Farbe aus; `on 2000` → blendet ein +- [x] `rgb 255 0 0` dann `ct 3000 2000` → erst dunkel, dann weiß (Moduswechsel) +- [x] `blink 3` → 3× Blinken, danach alter Zustand; `pulse on` / `pulse off` +- [x] Kein sichtbares Flackern bei konstanten Werten (5 kHz) ## Persistenz und Boot (Plan 1, Task 12) -- [ ] `pob last`: Farbe setzen, 4 s warten, Strom aus/an → gleicher Zustand, mit Fade -- [ ] `pob off`: Strom aus/an → bleibt aus; `on` → letzte Farbe -- [ ] `pob on` bei ausgeschaltetem Licht: Strom aus/an → an -- [ ] `fade 2000 2000` bleibt nach Reboot erhalten (`state`) -- [ ] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`state`) -- [ ] Normaler Boot nach > 5 s Laufzeit → kein Werksreset +- [x] `pob last`: Farbe setzen, 4 s warten, Strom aus/an → gleicher Zustand, mit Fade +- [x] `pob off`: Strom aus/an → bleibt aus; `on` → letzte Farbe +- [x] `pob on` bei ausgeschaltetem Licht: Strom aus/an → an +- [x] `fade 2000 2000` bleibt nach Reboot erhalten (`state`) +- [x] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`state`) +- [x] Normaler Boot nach > 5 s Laufzeit → kein Werksreset - [ ] Watchdog: `reboot` funktioniert; nach einem Hänger (nicht provozierbar) wäre Reset nach 20 s +- [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F) ## Später (Lampen) - [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) diff --git a/tools/flash.sh b/tools/flash.sh index 3b58853..13ef62a 100755 --- a/tools/flash.sh +++ b/tools/flash.sh @@ -1,13 +1,21 @@ #!/usr/bin/env bash -# Flashes build//ninlight_.fls over the serial port (runs natively on the Mac). -# Usage: tools/flash.sh +# Flashes app image (ninlight__gz.img) over the serial port (runs natively on the Mac). +# For blank/erased chips only: pass "full" as third argument to flash ninlight_.fls instead. +# Usage: tools/flash.sh [full] set -euo pipefail -DEV=${1:?usage: tools/flash.sh } -PORT=${2:?usage: tools/flash.sh } +DEV=${1:?usage: tools/flash.sh [full]} +PORT=${2:?usage: tools/flash.sh [full]} +MODE=${3:-app} ROOT=$(cd "$(dirname "$0")/.." && pwd) TOOL="$ROOT/build/host-tools/wm_tool" if [ ! -x "$TOOL" ] || [ "$ROOT/sdk/tools/wm_tool.c" -nt "$TOOL" ]; then mkdir -p "$(dirname "$TOOL")" cc -O2 -w -o "$TOOL" "$ROOT/sdk/tools/wm_tool.c" -lpthread fi -"$TOOL" -c "$PORT" -ds 921600 -rs rts -dl "$ROOT/build/$DEV/ninlight_$DEV.fls" +if [ "$MODE" = "full" ]; then + echo "Bitte bei \"please manually reset the device\" die Reset-Taste drücken." + "$TOOL" -c "$PORT" -ds 460800 -rs none -eo secboot -dl "$ROOT/build/$DEV/ninlight_$DEV.fls" +else + echo "Bitte bei \"please manually reset the device\" die Reset-Taste drücken." + "$TOOL" -c "$PORT" -ds 460800 -rs none -dl "$ROOT/build/$DEV/ninlight_${DEV}_gz.img" +fi From 488dac9167cd416233e3bb66e496b6064dc09824 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 17:05:08 +0200 Subject: [PATCH 021/150] =?UTF-8?q?fix:=20LED-Init=20an=20den=20Anfang=20v?= =?UTF-8?q?on=20app=5Ftask=20verschieben=20(Boot-Reihenfolge=20Spec=20?= =?UTF-8?q?=C2=A73.4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nl_led_init(board) läuft jetzt direkt nach nl_flash_init(), noch vor dem Reset-Counter/Settings/State-Log-Scan, damit die LEDs von Anfang an dunkel bleiben statt erst nach dem Flash-Scan initialisiert zu werden. --- src/app_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app_main.c b/src/app_main.c index 2a21dd7..bf0b7a3 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -91,6 +91,7 @@ static void app_task(void *arg) (void)arg; nl_flash_init(); + nl_led_init(board); /* hardware init first (spec 3.4): LEDs dark before any flash/state scan */ nl_resetcnt_init(base + NL_FL_RESETCNT); boots = nl_resetcnt_boot(); nl_settings_init(base + NL_FL_SETTINGS_A, base + NL_FL_SETTINGS_B); @@ -101,7 +102,6 @@ static void app_task(void *arg) s_settings.power_on_behaviour, s_settings.fade_in_ms, s_settings.fade_out_ms, have_last ? "yes" : "no"); - nl_led_init(board); cfg.ct_min = board->ct_min; cfg.ct_max = board->ct_max; cfg.output = led_out; From 4f270ad79b2c7eb492195b2658cb921c746e131b Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 20:53:25 +0200 Subject: [PATCH 022/150] =?UTF-8?q?fix:=20Watchdog=20wirksam=20machen,=20F?= =?UTF-8?q?actory-Reset=20im=20App-Task,=20Resetz=C3=A4hler-Retry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - vApplicationIdleHook() füttert den Watchdog nicht mehr (sdk/Platform/Sys/wm_main.c, dokumentiert als Patch 4 in sdk/PATCHES.md). Bisher fütterte der Idle-Task den Watchdog bei jedem Leerlauf, auch wenn ein Task deadlockt (z.B. nl_app_lock() hält und nie freigibt) - der Watchdog konnte so nie greifen. - Neues Konsolenkommando `wdtest` (Parser in nl_console_cmd.c/.h, Ausführung in nl_console.c, Host-Test in test_console_cmd.c): hält nl_app_lock() für immer, damit light_task und app_task blockieren und nur der Watchdog-Reset den Chip retten kann. - nl_app_request_factory_reset() (nl_app.h) setzt nur ein volatile Flag; app_task prüft es in der Hauptschleife und ruft nl_app_factory_reset() selbst auf, da nur der App-Task die Flash-Stores besitzt. Das Konsolenkommando `factory` ruft jetzt diese Funktion statt nl_app_factory_reset() direkt aufzurufen, um die Race mit nl_state_log_append()/nl_resetcnt_clear() im App-Task zu vermeiden. Der Bootpfad (Bootzähler >= 5) ruft weiterhin direkt nl_app_factory_reset() auf. - nl_resetcnt_clear() wird nur noch als erledigt markiert, wenn es 0 zurückgibt, damit ein fehlgeschlagener Clear in der nächsten Schleifeniteration erneut versucht wird. --- sdk/PATCHES.md | 11 +++++++++++ sdk/Platform/Sys/wm_main.c | 6 ++++-- src/app_main.c | 12 ++++++++---- src/sys/nl_app.h | 7 +++++++ src/sys/nl_console.c | 7 ++++++- src/sys/nl_console_cmd.c | 5 +++-- src/sys/nl_console_cmd.h | 1 + test/test_console_cmd.c | 2 ++ 8 files changed, 42 insertions(+), 9 deletions(-) diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index f59f75c..d52e7d8 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -14,3 +14,14 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ (`qconnex_sdk/src/qc_main.c`) war `pre_gpio_config()` bereits ein No-Op; `EarlyInit()` rief dort nur in die QConnex-Hardwareschicht (`QCXH_EarlyInit()`), die ninlight nicht verwendet. Board-spezifische Frühinitialisierung (falls nötig) folgt in einem späteren Plan. +4. `Platform/Sys/wm_main.c`: `vApplicationIdleHook()` hat bisher jedes Mal `tls_watchdog_clr()` + aufgerufen, wenn der Idle-Task lief - also immer dann, wenn kein anderer Task rechenbereit war. + Ein Task-Deadlock (z.B. ein Task, der `nl_app_lock()` hält und nie freigibt) hätte den Watchdog + damit nie ausgelöst, weil der Idle-Task munter weiter gefüttert hätte. Der `tls_watchdog_clr()`- + Aufruf wurde entfernt; `wfi` bleibt. Der einzige verbleibende `tls_watchdog_clr()`-Aufruf im SDK + ist in `Platform/Common/fwup/wm_fwup.c` (Firmware-Update-Task) - das ist unkritisch, da er nur + während eines laufenden OTA-Updates feuert und den `app_task`-Heartbeat in `src/app_main.c` + nicht ersetzt (der Watchdog wird sonst ausschließlich dort über den `s_light_beat`-Heartbeat + des `light_task` gefüttert). Getestet mit dem Konsolenkommando `wdtest` + (src/sys/nl_console_cmd.c, src/sys/nl_console.c): es hält `nl_app_lock()` für immer, wodurch + `light_task` und `app_task` blockieren und der Watchdog den Chip zurücksetzen muss. diff --git a/sdk/Platform/Sys/wm_main.c b/sdk/Platform/Sys/wm_main.c index 0420580..41b6fba 100644 --- a/sdk/Platform/Sys/wm_main.c +++ b/sdk/Platform/Sys/wm_main.c @@ -136,8 +136,10 @@ void tls_os_timer_init(void) void vApplicationIdleHook( void ) { - /* clear watch dog interrupt */ - tls_watchdog_clr(); + /* ninlight patch 4: do NOT feed the watchdog here. The idle task runs whenever + * no other task is ready, so clearing the watchdog from here would keep clearing + * it even while an app task is deadlocked (e.g. stuck holding nl_app_lock) and + * defeat the whole point of the watchdog. See sdk/PATCHES.md item 4. */ #if !defined(__CC_ARM) __asm volatile ("wfi"); diff --git a/src/app_main.c b/src/app_main.c index bf0b7a3..874a246 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -22,6 +22,7 @@ static tls_os_mutex_t *s_lock; static nl_settings_t s_settings; static volatile uint32_t s_light_beat; +static volatile bool s_factory_reset_requested; static u32 s_app_stack[1024]; static u32 s_light_stack[512]; @@ -44,6 +45,8 @@ int nl_app_save_settings(void) return r; } +void nl_app_request_factory_reset(void) { s_factory_reset_requested = true; } + void nl_app_factory_reset(void) { printf("factory reset\r\n"); @@ -130,10 +133,11 @@ static void app_task(void *arg) tls_os_time_delay(HZ / 10); /* 100 ms */ now = nl_app_now_ms(); - if (!counter_cleared && now - boot_ms >= NL_RESETCNT_CLEAR_MS) { - nl_resetcnt_clear(); - counter_cleared = true; - } + if (!counter_cleared && now - boot_ms >= NL_RESETCNT_CLEAR_MS) + counter_cleared = nl_resetcnt_clear() == 0; + + if (s_factory_reset_requested) + nl_app_factory_reset(); /* does not return */ nl_app_lock(); due = nl_ctrl_persist_due(now, &pending); diff --git a/src/sys/nl_app.h b/src/sys/nl_app.h index 548bf54..73b1969 100644 --- a/src/sys/nl_app.h +++ b/src/sys/nl_app.h @@ -12,4 +12,11 @@ nl_settings_t *nl_app_settings(void); int nl_app_save_settings(void); void nl_app_factory_reset(void); +/* Only sets a volatile flag; the app task (which owns the flash stores) checks it + * once per loop iteration and calls nl_app_factory_reset() itself. This avoids the + * race with the app task's own nl_state_log_append()/nl_resetcnt_clear() calls. + * The console's `factory` command and, in a later plan, REST callers must use this + * function instead of calling nl_app_factory_reset() directly from another task. */ +void nl_app_request_factory_reset(void); + #endif diff --git a/src/sys/nl_console.c b/src/sys/nl_console.c index 2b8b61f..a172489 100644 --- a/src/sys/nl_console.c +++ b/src/sys/nl_console.c @@ -96,11 +96,16 @@ static void exec(const nl_cmd_t *c) nl_app_unlock(); return; case NL_CMD_FACTORY: - nl_app_factory_reset(); + nl_app_request_factory_reset(); return; case NL_CMD_REBOOT: tls_sys_reset(); return; + case NL_CMD_WDTEST: + printf("wdtest: holding app lock forever\r\n"); + nl_app_lock(); /* deliberately never released: light_task and app_task stall on it, + * so only the watchdog reset can recover the chip */ + return; case NL_CMD_HELP: printf("%s", nl_console_help); return; diff --git a/src/sys/nl_console_cmd.c b/src/sys/nl_console_cmd.c index 5819895..42106fb 100644 --- a/src/sys/nl_console_cmd.c +++ b/src/sys/nl_console_cmd.c @@ -9,7 +9,7 @@ const char nl_console_help[] = "on [ms] | off [ms] | bri <1-100> [ms] | rgb [ms] | ct [ms]\r\n" "pob last|on|off | fade | blink [n] | pulse on|off\r\n" - "state | factory | reboot | help\r\n"; + "state | factory | reboot | wdtest | help\r\n"; static bool num(const char *s, int32_t lo, int32_t hi, int32_t *out) { @@ -104,7 +104,8 @@ nl_cmd_t nl_console_parse(const char *line) ok = n == 1 || (n == 2 && num(tok[1], 1, 10, &c.arg[0])); } else { static const struct { const char *name; nl_cmd_type_t type; } SIMPLE[] = { - {"state", NL_CMD_STATE}, {"factory", NL_CMD_FACTORY}, {"reboot", NL_CMD_REBOOT}, {"help", NL_CMD_HELP}}; + {"state", NL_CMD_STATE}, {"factory", NL_CMD_FACTORY}, {"reboot", NL_CMD_REBOOT}, + {"help", NL_CMD_HELP}, {"wdtest", NL_CMD_WDTEST}}; ok = false; for (size_t i = 0; i < sizeof SIMPLE / sizeof SIMPLE[0]; i++) if (!strcmp(tok[0], SIMPLE[i].name)) { diff --git a/src/sys/nl_console_cmd.h b/src/sys/nl_console_cmd.h index a400451..8f00bf3 100644 --- a/src/sys/nl_console_cmd.h +++ b/src/sys/nl_console_cmd.h @@ -18,6 +18,7 @@ typedef enum { NL_CMD_FACTORY, NL_CMD_REBOOT, NL_CMD_HELP, + NL_CMD_WDTEST, NL_CMD_ERROR } nl_cmd_type_t; diff --git a/test/test_console_cmd.c b/test/test_console_cmd.c index b8d1faf..aafa921 100644 --- a/test/test_console_cmd.c +++ b/test/test_console_cmd.c @@ -81,7 +81,9 @@ static void test_simple_commands(void) TEST_ASSERT_EQUAL(NL_CMD_FACTORY, nl_console_parse("factory").type); TEST_ASSERT_EQUAL(NL_CMD_REBOOT, nl_console_parse("reboot").type); TEST_ASSERT_EQUAL(NL_CMD_HELP, nl_console_parse("help").type); + TEST_ASSERT_EQUAL(NL_CMD_WDTEST, nl_console_parse("wdtest").type); TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("state now").type); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("wdtest now").type); } int main(void) From 9ce51354899d345032dbd1f24398755bb1137063 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 20:53:34 +0200 Subject: [PATCH 023/150] =?UTF-8?q?fix:=20R=C3=BCckgabewert=20von=20tls=5F?= =?UTF-8?q?os=5Fmutex=5Fcreate=20in=20UserMain=20pr=C3=BCfen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schlägt die Mutex-Erstellung für s_lock fehl, wird jetzt "FATAL: cannot create app lock" ausgegeben und UserMain kehrt zurück, statt mit einem NULL-Mutex weiterzumachen und den App-Task zu starten. --- src/app_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app_main.c b/src/app_main.c index 874a246..91ad5d4 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -162,6 +162,9 @@ void UserMain(void) printf("FATAL: unknown board %s\r\n", NINLIGHT_DEVICE_ID); return; } - tls_os_mutex_create(0, &s_lock); + if (tls_os_mutex_create(0, &s_lock) != TLS_OS_SUCCESS) { + printf("FATAL: cannot create app lock\r\n"); + return; + } tls_os_task_create(NULL, "nl_app", app_task, NULL, (u8 *)s_app_stack, sizeof(s_app_stack), NL_APP_PRIO, 0); } From d03b571bb82722e888bf47773b13ca7874fdf72c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 20:53:43 +0200 Subject: [PATCH 024/150] =?UTF-8?q?docs:=20irref=C3=BChrende=20Kommentare?= =?UTF-8?q?=20zu=20Flash-Locking=20und=20G95-Pinbelegung=20korrigieren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nl_flash_w600.c: der Header-Kommentar behauptete, tls_spifls_*/tls_fls_* kämen ganz ohne Locking aus - tatsächlich ist nur das Pin-Switching für die >1MB-Region (ioclose/cs/ck-Switching in wm_internal_fls.c) unsynchronisiert, unser Mutex serialisiert ausschließlich ninlights eigene Tasks, nicht SDK-interne Flash-Nutzer wie den Parameter-Store oder fwup (für Plan 2 vorgemerkt). Auch der Kommentar auf nl_flash_region_base() war ungenau. nl_board.c: G95-Kommentar eindeutiger formuliert (aktuelle Tabelle CW=PB13/WW=PB15, alte Lampen ggf. PB16=WW/PB15=CW). --- src/board/nl_board.c | 3 ++- src/config/nl_flash_w600.c | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/board/nl_board.c b/src/board/nl_board.c index a0140a1..f061612 100644 --- a/src/board/nl_board.c +++ b/src/board/nl_board.c @@ -27,7 +27,8 @@ const nl_board_t nl_boards[] = { {NONE, NONE, NONE, CH(2, NL_PB(16)), CH(4, NL_PB(8))}, NO_SM}, {"gu10_cw", "Klyqa GU10 CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, {NONE, NONE, NONE, CH(2, NL_PB(16)), CH(4, NL_PB(8))}, NO_SM}, - /* G95: pin assignment still to be verified on a series bulb (old note: PB16=WW, PB15=CW). */ + /* G95: current table: CW=PB13, WW=PB15 (new assignment); old bulbs may use + * PB16=WW, PB15=CW - verify on a series bulb. */ {"g95_cw", "Klyqa G95 Vintage CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, {NONE, NONE, NONE, CH(1, NL_PB(13)), CH(3, NL_PB(15))}, NO_SM}, }; diff --git a/src/config/nl_flash_w600.c b/src/config/nl_flash_w600.c index 453ff02..ef0a79e 100644 --- a/src/config/nl_flash_w600.c +++ b/src/config/nl_flash_w600.c @@ -5,12 +5,19 @@ #include "config/nl_flash.h" /* - * The ninlight flash region lies above FLASH_1M_END_ADDR, where the SDK routes - * tls_fls_read/write/erase to the external SPI flash path without taking its own - * fls_lock (see sdk/Platform/Drivers/internalflash/wm_internal_fls.c). The app task + * The ninlight flash region lies above FLASH_1M_END_ADDR, where the SDK's + * tls_fls_read/write/erase switch the flash pins (CS/CK/DI/DO ioclose/open, see the + * tls_spifls_*_switch() calls around the >1 MB branch in + * sdk/Platform/Drivers/internalflash/wm_internal_fls.c) to reach the external SPI + * flash - and that pin switching is not serialised by the SDK itself. The app task * (periodic nl_state_log_append) and the console task (nl_app_save_settings, * nl_app_factory_reset) can therefore issue concurrent SPI transactions and corrupt - * each other. Serialise all flash accesses with our own mutex. + * each other. Serialise all of *ninlight's own* flash accesses with our own mutex. + * + * This mutex only covers ninlight's own tasks. Other SDK-internal users of the + * tls_fls_* / tls_spifls_* functions in the same >1 MB region (e.g. the params + * store, fwup) are not serialised against us or each other by this lock - tracked + * for plan 2. * * The mutex is created once in nl_flash_init(), called as the very first statement * of app_task() before any other task can touch flash - not lazily from inside a @@ -70,6 +77,9 @@ int nl_flash_erase_sector(uint32_t addr) uint32_t nl_flash_region_base(void) { - /* The first user sector is TLS_FLASH_PARAM_DEFAULT (SDK), ninlight starts one sector later. */ + /* TLS_FLASH_PARAM_DEFAULT is set by the SDK to the same address as USER_ADDR_START + * (see sdk/Platform/Common/fwup/wm_fwup.c) - i.e. the first user sector doubles as + * the SDK's own parameter-store sector. ninlight must not touch that sector, so its + * region starts one sector after USER_ADDR_START. */ return USER_ADDR_START + NL_FLASH_SECTOR_SIZE; } From e19ee98ee8eee48ecadfbeef3b020f2949158671 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 20:53:50 +0200 Subject: [PATCH 025/150] docs: Hardware-Checkliste und README aktualisieren MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hardware-checklist.md: Watchdog-Zeile beschreibt jetzt den wdtest-Testablauf (Neustart nach ca. 20-40s), bleibt unabgehakt; neuer Punkt unter "Später (Lampen)" zum SM2135EH-Standby-Bit. README.md: tools/flash.sh full als auf Hardware noch ungetestet markiert. --- README.md | 2 +- docs/hardware-checklist.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c543ac4..0dff849 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Firmware flashen (Standard: App-Image): tools/flash.sh -Das Skript druckt `Bitte bei "please manually reset the device" die Reset-Taste drücken.` — wenn wm_tool diese Nachricht zeigt, musst du die Reset-Taste am EVK drücken (Baud 460800). Für blank/erased Chips: `tools/flash.sh full` (flasht stattdessen das Vollbild mit secboot). +Das Skript druckt `Bitte bei "please manually reset the device" die Reset-Taste drücken.` — wenn wm_tool diese Nachricht zeigt, musst du die Reset-Taste am EVK drücken (Baud 460800). Für blank/erased Chips: `tools/flash.sh full` (flasht stattdessen das Vollbild mit secboot, auf Hardware noch ungetestet). Serielle Konsole (115200 8N1, lokales Echo): diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 7d41c5c..79bc740 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -19,10 +19,11 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] `fade 2000 2000` bleibt nach Reboot erhalten (`state`) - [x] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`state`) - [x] Normaler Boot nach > 5 s Laufzeit → kein Werksreset -- [ ] Watchdog: `reboot` funktioniert; nach einem Hänger (nicht provozierbar) wäre Reset nach 20 s +- [ ] Watchdog: `wdtest` → Neustart nach ca. 20–40 s (erste Ablaufzeit löst IRQ aus, Reset beim zweiten Ablauf) - [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F) ## Später (Lampen) +- [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) - [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) - [ ] E27 CW: Gegenphase CW sichtbar flackerfrei - [ ] G95: Pinbelegung WW=PB15/CW=PB13 prüfen From dce9ad2946423f15986c1b7316d4356ee3894665 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 21:33:50 +0200 Subject: [PATCH 026/150] docs: Watchdog-Test am EVK bestanden --- docs/hardware-checklist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 79bc740..a6c8076 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -19,7 +19,7 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] `fade 2000 2000` bleibt nach Reboot erhalten (`state`) - [x] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`state`) - [x] Normaler Boot nach > 5 s Laufzeit → kein Werksreset -- [ ] Watchdog: `wdtest` → Neustart nach ca. 20–40 s (erste Ablaufzeit löst IRQ aus, Reset beim zweiten Ablauf) +- [x] Watchdog: `wdtest` → Neustart nach ca. 20–40 s (erste Ablaufzeit löst IRQ aus, Reset beim zweiten Ablauf) — getestet 2026-09-11 mit e19ee98: Neustart nach 40,4 s - [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F) ## Später (Lampen) From 0648b3a252f93dc958e844b17610f5cc834bdf31 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 22:10:28 +0200 Subject: [PATCH 027/150] fix: Flash-Lock im SDK, Store-Lock, Flush vor Reboot --- sdk/PATCHES.md | 2 + .../Drivers/internalflash/wm_internal_fls.c | 18 ++++++ src/app_main.c | 61 +++++++++++++++++-- src/sys/nl_app.h | 6 ++ src/sys/nl_console.c | 2 +- 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index d52e7d8..a8a54e1 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -25,3 +25,5 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ des `light_task` gefüttert). Getestet mit dem Konsolenkommando `wdtest` (src/sys/nl_console_cmd.c, src/sys/nl_console.c): es hält `nl_app_lock()` für immer, wodurch `light_task` und `app_task` blockieren und der Watchdog den Chip zurücksetzen muss. +5. `Platform/Drivers/internalflash/wm_internal_fls.c`: Semaphor `s_fls2_lock` serialisiert den Flash-Pfad + oberhalb von 1 MB (Pin-Umschaltung via `tls_spifls_cs/ck/di/do_switch` war ungeschützt). diff --git a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c index 496f26f..0a1e512 100644 --- a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c +++ b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c @@ -19,6 +19,12 @@ #include "wm_gpio.h" #include "wm_io.h" +/* ninlight patch 5: the >1 MB (second flash) path switches the SPI pins without any lock; + * serialise it for all callers (SDK params, fwup, ninlight). See sdk/PATCHES.md. */ +static tls_os_sem_t *s_fls2_lock = NULL; +#define FLS2_LOCK() do { if (s_fls2_lock) tls_os_sem_acquire(s_fls2_lock, 0); } while (0) +#define FLS2_UNLOCK() do { if (s_fls2_lock) tls_os_sem_release(s_fls2_lock); } while (0) + void tls_spifls_cs_switch(enum tls_io_name flashcs, int openflag); void tls_spifls_di_switch(enum tls_io_name flashdi, int openflag); void tls_spifls_do_switch(enum tls_io_name flashdo, int openflag); @@ -611,6 +617,7 @@ int tls_fls_read(u32 addr, u8 *buf, u32 len) { lenfor2M = len - lenfor1M; lenfor2M = (addrfor2M + lenfor2M) <= (FLASH_BASE_ADDR | flashtotalsize) ? lenfor2M : ((FLASH_BASE_ADDR | flashtotalsize) - addrfor2M); + FLS2_LOCK(); tls_spifls_cs_switch(WM_IO_PA_02, 1); tls_spifls_ck_switch(WM_IO_PA_11, 1); tls_spifls_di_switch(WM_IO_PA_03, 1); @@ -620,6 +627,7 @@ int tls_fls_read(u32 addr, u8 *buf, u32 len) tls_spifls_do_switch(WM_IO_PA_09, 0); tls_spifls_ck_switch(WM_IO_PA_11, 0); tls_spifls_cs_switch(WM_IO_PA_02, 0); + FLS2_UNLOCK(); return err; } @@ -756,6 +764,7 @@ int tls_fls_write(u32 addr, u8 *buf, u32 len) { lenfor2M = len - lenfor1M; lenfor2M = (addrfor2M + lenfor2M) <= (FLASH_BASE_ADDR | flashtotalsize) ? lenfor2M : ((FLASH_BASE_ADDR | flashtotalsize) - addrfor2M); + FLS2_LOCK(); tls_spifls_cs_switch(WM_IO_PA_02, 1); tls_spifls_ck_switch(WM_IO_PA_11, 1); tls_spifls_di_switch(WM_IO_PA_03, 1); @@ -765,6 +774,7 @@ int tls_fls_write(u32 addr, u8 *buf, u32 len) tls_spifls_do_switch(WM_IO_PA_09, 0); tls_spifls_ck_switch(WM_IO_PA_11, 0); tls_spifls_cs_switch(WM_IO_PA_02, 0); + FLS2_UNLOCK(); return err; @@ -805,6 +815,7 @@ int tls_fls_erase(u32 sector) } else if (sector < (flashtotalsize / INSIDE_FLS_SECTOR_SIZE + INSIDE_FLS_BASE_ADDR / INSIDE_FLS_SECTOR_SIZE)) { + FLS2_LOCK(); tls_spifls_cs_switch(WM_IO_PA_02, 1); tls_spifls_ck_switch(WM_IO_PA_11, 1); tls_spifls_di_switch(WM_IO_PA_03, 1); @@ -814,6 +825,7 @@ int tls_fls_erase(u32 sector) tls_spifls_do_switch(WM_IO_PA_09, 0); tls_spifls_ck_switch(WM_IO_PA_11, 0); tls_spifls_cs_switch(WM_IO_PA_02, 0); + FLS2_UNLOCK(); return err; } @@ -854,6 +866,7 @@ static void tls_fls_flush_sector(void) else if (gsSector < (flashtotalsize / INSIDE_FLS_SECTOR_SIZE + INSIDE_FLS_BASE_ADDR / INSIDE_FLS_SECTOR_SIZE)) { addr = gsSector * INSIDE_FLS_SECTOR_SIZE; + FLS2_LOCK(); tls_spifls_cs_switch(WM_IO_PA_02, 1); tls_spifls_ck_switch(WM_IO_PA_11, 1); tls_spifls_di_switch(WM_IO_PA_03, 1); @@ -863,6 +876,7 @@ static void tls_fls_flush_sector(void) tls_spifls_do_switch(WM_IO_PA_09, 0); tls_spifls_ck_switch(WM_IO_PA_11, 0); tls_spifls_cs_switch(WM_IO_PA_02, 0); + FLS2_UNLOCK(); } //gsSecOffset = 0; @@ -1042,6 +1056,7 @@ int tls_fls_chip_erase(void) if (inner2flashsize) { + FLS2_LOCK(); tls_spifls_cs_switch(WM_IO_PA_02, 1); tls_spifls_ck_switch(WM_IO_PA_11, 1); tls_spifls_di_switch(WM_IO_PA_03, 1); @@ -1051,6 +1066,7 @@ int tls_fls_chip_erase(void) tls_spifls_do_switch(WM_IO_PA_09, 0); tls_spifls_ck_switch(WM_IO_PA_11, 0); tls_spifls_cs_switch(WM_IO_PA_02, 0); + FLS2_UNLOCK(); } tls_mem_free(cache); @@ -1157,6 +1173,8 @@ int tls_fls_init(void) TLS_DBGPRT_ERR("create semaphore @fls_lock fail!\n"); return TLS_FLS_STATUS_ENOMEM; } + tls_os_sem_create(&s_fls2_lock, 1); + inside_fls = fls; inner1flashsize = getFlashDensity(); diff --git a/src/app_main.c b/src/app_main.c index 91ad5d4..bf1f023 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -20,6 +20,7 @@ #define NL_APP_PRIO 40 static tls_os_mutex_t *s_lock; +static tls_os_mutex_t *s_store_lock; /* serialises all nl_settings_* / nl_state_log_* / nl_resetcnt_* calls */ static nl_settings_t s_settings; static volatile uint32_t s_light_beat; static volatile bool s_factory_reset_requested; @@ -29,6 +30,9 @@ static u32 s_light_stack[512]; void nl_app_lock(void) { tls_os_mutex_acquire(s_lock, 0); } void nl_app_unlock(void) { tls_os_mutex_release(s_lock); } +static void store_lock(void) { tls_os_mutex_acquire(s_store_lock, 0); } +static void store_unlock(void) { tls_os_mutex_release(s_store_lock); } + uint32_t nl_app_now_ms(void) { return (uint32_t)((uint64_t)tls_os_get_time() * 1000u / HZ); @@ -38,13 +42,41 @@ nl_settings_t *nl_app_settings(void) { return &s_settings; } int nl_app_save_settings(void) { + static nl_settings_t copy; /* guarded by the store lock */ int r; + + store_lock(); nl_app_lock(); - r = nl_settings_save(&s_settings); + copy = s_settings; nl_app_unlock(); + r = nl_settings_save(©); + store_unlock(); return r; } +void nl_app_flush(void) +{ + nl_light_state_t st; + + store_lock(); + nl_app_lock(); + nl_ctrl_get(&st); + nl_app_unlock(); + if (nl_state_log_append(&st) == 0) { /* no write when unchanged */ + nl_app_lock(); + nl_ctrl_persisted(&st); + nl_app_unlock(); + } + store_unlock(); +} + +void nl_app_reboot(void) +{ + printf("reboot\r\n"); + nl_app_flush(); + tls_sys_reset(); +} + void nl_app_request_factory_reset(void) { s_factory_reset_requested = true; } void nl_app_factory_reset(void) @@ -54,9 +86,11 @@ void nl_app_factory_reset(void) nl_engine_blink(3); nl_app_unlock(); tls_os_time_delay(2 * HZ); /* 3 blinks = 1.8 s, the light task keeps running */ + store_lock(); nl_settings_erase(); nl_state_log_erase(); nl_resetcnt_clear(); + store_unlock(); tls_sys_reset(); } @@ -95,12 +129,14 @@ static void app_task(void *arg) (void)arg; nl_flash_init(); nl_led_init(board); /* hardware init first (spec 3.4): LEDs dark before any flash/state scan */ + store_lock(); nl_resetcnt_init(base + NL_FL_RESETCNT); boots = nl_resetcnt_boot(); nl_settings_init(base + NL_FL_SETTINGS_A, base + NL_FL_SETTINGS_B); nl_settings_load(&s_settings); nl_state_log_init(base + NL_FL_STATE_0, base + NL_FL_STATE_1); have_last = nl_state_log_load(&last); + store_unlock(); printf("boot #%u, settings: pob=%u fade=%u/%u, last state: %s\r\n", boots, s_settings.power_on_behaviour, s_settings.fade_in_ms, s_settings.fade_out_ms, have_last ? "yes" : "no"); @@ -133,8 +169,11 @@ static void app_task(void *arg) tls_os_time_delay(HZ / 10); /* 100 ms */ now = nl_app_now_ms(); - if (!counter_cleared && now - boot_ms >= NL_RESETCNT_CLEAR_MS) + if (!counter_cleared && now - boot_ms >= NL_RESETCNT_CLEAR_MS) { + store_lock(); counter_cleared = nl_resetcnt_clear() == 0; + store_unlock(); + } if (s_factory_reset_requested) nl_app_factory_reset(); /* does not return */ @@ -142,10 +181,16 @@ static void app_task(void *arg) nl_app_lock(); due = nl_ctrl_persist_due(now, &pending); nl_app_unlock(); - if (due && nl_state_log_append(&pending) == 0) { - nl_app_lock(); - nl_ctrl_persisted(&pending); - nl_app_unlock(); + if (due) { + int r; + store_lock(); + r = nl_state_log_append(&pending); + store_unlock(); + if (r == 0) { + nl_app_lock(); + nl_ctrl_persisted(&pending); + nl_app_unlock(); + } } if (s_light_beat != beat_seen) { @@ -166,5 +211,9 @@ void UserMain(void) printf("FATAL: cannot create app lock\r\n"); return; } + if (tls_os_mutex_create(0, &s_store_lock) != TLS_OS_SUCCESS) { + printf("FATAL: cannot create store lock\r\n"); + return; + } tls_os_task_create(NULL, "nl_app", app_task, NULL, (u8 *)s_app_stack, sizeof(s_app_stack), NL_APP_PRIO, 0); } diff --git a/src/sys/nl_app.h b/src/sys/nl_app.h index 73b1969..c9e18a9 100644 --- a/src/sys/nl_app.h +++ b/src/sys/nl_app.h @@ -9,7 +9,13 @@ void nl_app_lock(void); void nl_app_unlock(void); uint32_t nl_app_now_ms(void); nl_settings_t *nl_app_settings(void); +/* Copies the settings under the app lock and writes them under the store lock; never call + * while holding the app lock. */ int nl_app_save_settings(void); +/* Writes a pending light state immediately (no write when unchanged). */ +void nl_app_flush(void); +/* Flushes pending state, then resets the chip. Does not return. */ +void nl_app_reboot(void); void nl_app_factory_reset(void); /* Only sets a volatile flag; the app task (which owns the flash stores) checks it diff --git a/src/sys/nl_console.c b/src/sys/nl_console.c index a172489..2dc074e 100644 --- a/src/sys/nl_console.c +++ b/src/sys/nl_console.c @@ -99,7 +99,7 @@ static void exec(const nl_cmd_t *c) nl_app_request_factory_reset(); return; case NL_CMD_REBOOT: - tls_sys_reset(); + nl_app_reboot(); return; case NL_CMD_WDTEST: printf("wdtest: holding app lock forever\r\n"); From 4a23df1b8df011901ae57fc164a9b604e526f0bf Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 22:18:14 +0200 Subject: [PATCH 028/150] =?UTF-8?q?feat:=20cJSON,=20Ger=C3=A4tenamen=20und?= =?UTF-8?q?=20HTTP-Parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 +- cmake/sdk.cmake | 2 +- src/net/nl_http.c | 128 ++ src/net/nl_http.h | 30 + src/net/nl_names.c | 20 + src/net/nl_names.h | 14 + test/CMakeLists.txt | 7 + test/test_http.c | 118 ++ test/test_names.c | 45 + third_party/cJSON/LICENSE | 19 + third_party/cJSON/cJSON.c | 3143 +++++++++++++++++++++++++++++++++++++ third_party/cJSON/cJSON.h | 300 ++++ 12 files changed, 3829 insertions(+), 2 deletions(-) create mode 100644 src/net/nl_http.c create mode 100644 src/net/nl_http.h create mode 100644 src/net/nl_names.c create mode 100644 src/net/nl_names.h create mode 100644 test/test_http.c create mode 100644 test/test_names.c create mode 100644 third_party/cJSON/LICENSE create mode 100644 third_party/cJSON/cJSON.c create mode 100644 third_party/cJSON/cJSON.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5978588..46bf1df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,10 @@ set(NL_FW_SOURCES src/sys/nl_console_cmd.c src/sys/nl_console.c src/sys/fwup_hook_default.c - src/sys/sdk_stubs.c) + src/sys/sdk_stubs.c + src/net/nl_names.c + src/net/nl_http.c + third_party/cJSON/cJSON.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS}) diff --git a/cmake/sdk.cmake b/cmake/sdk.cmake index 4fc16be..31189ab 100644 --- a/cmake/sdk.cmake +++ b/cmake/sdk.cmake @@ -30,7 +30,7 @@ include_directories( ${SDK_DIR}/Platform/Common/crypto ${SDK_DIR}/Platform/Inc ${SDK_DIR}/Platform/Sys ${SDK_DIR}/Src/App/OTA ${SDK_DIR}/Src/App/dhcpserver ${SDK_DIR}/Src/App/dnsserver ${SDK_DIR}/Src/App/httpclient ${SDK_DIR}/Src/Network/lwip2.0.3/include ${SDK_DIR}/Src/Network/api2.0.3 - ${CMAKE_SOURCE_DIR}/src) + ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/third_party/cJSON) foreach(dir Platform/Common Platform/Drivers Platform/Sys Src/Network Src/OS/RTOS Src/App/dhcpserver Src/App/dnsserver Src/App/httpclient Src/App/OTA) diff --git a/src/net/nl_http.c b/src/net/nl_http.c new file mode 100644 index 0000000..ae44b42 --- /dev/null +++ b/src/net/nl_http.c @@ -0,0 +1,128 @@ +#include "net/nl_http.h" + +#include +#include +#include +#include + +static const char *find(const char *buf, size_t len, const char *pat) +{ + size_t n = strlen(pat); + for (size_t i = 0; i + n <= len; i++) + if (memcmp(buf + i, pat, n) == 0) + return buf + i; + return NULL; +} + +/* Case-insensitive "name:" match at the start of a header line; returns the value start. */ +static const char *header_value(const char *line, const char *line_end, const char *name) +{ + size_t n = strlen(name); + if ((size_t)(line_end - line) <= n || line[n] != ':') + return NULL; + for (size_t i = 0; i < n; i++) + if (tolower((unsigned char)line[i]) != name[i]) + return NULL; + line += n + 1; + while (line < line_end && (*line == ' ' || *line == '\t')) + line++; + return line; +} + +nl_http_parse_t nl_http_parse(const char *buf, size_t len, nl_http_request_t *req) +{ + const char *hdr_end = find(buf, len, "\r\n\r\n"); + const char *p, *line_end, *sp1, *sp2; + size_t header_len, content_length = 0, plen; + + if (!hdr_end) + return len >= NL_HTTP_MAX_REQUEST ? NL_HTTP_TOO_LARGE : NL_HTTP_INCOMPLETE; + + memset(req, 0, sizeof *req); + header_len = (size_t)(hdr_end + 4 - buf); + + /* request line: METHOD SP PATH SP HTTP/1.x */ + line_end = find(buf, header_len, "\r\n"); + sp1 = memchr(buf, ' ', (size_t)(line_end - buf)); + if (!sp1) + return NL_HTTP_BAD_REQUEST; + sp2 = memchr(sp1 + 1, ' ', (size_t)(line_end - sp1 - 1)); + if (!sp2 || (size_t)(line_end - sp2 - 1) < 8 || memcmp(sp2 + 1, "HTTP/1.", 7) != 0) + return NL_HTTP_BAD_REQUEST; + + if (sp1 - buf == 3 && memcmp(buf, "GET", 3) == 0) + req->method = NL_HTTP_GET; + else if (sp1 - buf == 4 && memcmp(buf, "POST", 4) == 0) + req->method = NL_HTTP_POST; + else if (sp1 - buf == 3 && memcmp(buf, "PUT", 3) == 0) + req->method = NL_HTTP_PUT; + else + req->method = NL_HTTP_OTHER; + + p = memchr(sp1 + 1, '?', (size_t)(sp2 - sp1 - 1)); + plen = (size_t)((p ? p : sp2) - (sp1 + 1)); + if (plen == 0 || plen >= sizeof req->path || sp1[1] != '/') + return NL_HTTP_BAD_REQUEST; + memcpy(req->path, sp1 + 1, plen); + + /* headers */ + for (p = line_end + 2; p < hdr_end; p = line_end + 2) { + const char *v; + line_end = find(p, (size_t)(hdr_end + 2 - p), "\r\n"); + if ((v = header_value(p, line_end, "content-length")) != NULL) { + if (v == line_end) + return NL_HTTP_BAD_REQUEST; + content_length = 0; + for (; v < line_end; v++) { + if (!isdigit((unsigned char)*v)) + return NL_HTTP_BAD_REQUEST; + content_length = content_length * 10u + (size_t)(*v - '0'); + if (content_length > NL_HTTP_MAX_BODY) + return NL_HTTP_TOO_LARGE; + } + } else if ((v = header_value(p, line_end, "authorization")) != NULL) { + size_t n; + if (line_end - v > 7 && strncasecmp(v, "Bearer ", 7) == 0) + v += 7; + n = (size_t)(line_end - v); + if (n >= sizeof req->auth) + n = sizeof req->auth - 1; + memcpy(req->auth, v, n); + req->auth[n] = '\0'; + } + } + + if (header_len + content_length > NL_HTTP_MAX_REQUEST) + return NL_HTTP_TOO_LARGE; + if (len < header_len + content_length) + return NL_HTTP_INCOMPLETE; + req->body = buf + header_len; + req->body_len = content_length; + return NL_HTTP_COMPLETE; +} + +const char *nl_http_reason(int status) +{ + switch (status) { + case 200: return "OK"; + case 202: return "Accepted"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 413: return "Payload Too Large"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 503: return "Service Unavailable"; + default: return "Unknown"; + } +} + +size_t nl_http_response(char *out, size_t cap, int status, const char *json_body) +{ + int n = snprintf(out, cap, + "HTTP/1.1 %d %s\r\nContent-Type: application/json\r\nContent-Length: %u\r\n" + "Connection: close\r\n\r\n%s", + status, nl_http_reason(status), (unsigned)strlen(json_body), json_body); + return (n < 0 || (size_t)n >= cap) ? 0 : (size_t)n; +} diff --git a/src/net/nl_http.h b/src/net/nl_http.h new file mode 100644 index 0000000..afb4174 --- /dev/null +++ b/src/net/nl_http.h @@ -0,0 +1,30 @@ +#ifndef NL_HTTP_H +#define NL_HTTP_H + +#include + +#include "config/nl_settings.h" + +#define NL_HTTP_MAX_REQUEST 2048 /* request line + headers + body */ +#define NL_HTTP_MAX_BODY 1024 + +typedef enum { NL_HTTP_GET = 0, NL_HTTP_POST, NL_HTTP_PUT, NL_HTTP_OTHER } nl_http_method_t; +typedef enum { NL_HTTP_INCOMPLETE = 0, NL_HTTP_COMPLETE, NL_HTTP_BAD_REQUEST, NL_HTTP_TOO_LARGE } nl_http_parse_t; + +typedef struct { + nl_http_method_t method; + char path[64]; /* without query string */ + char auth[NL_TOKEN_LEN]; /* Authorization value without "Bearer ", "" if absent */ + const char *body; /* points into the caller's buffer */ + size_t body_len; +} nl_http_request_t; + +/* Parses the first `len` bytes of `buf` (need not be NUL terminated). */ +nl_http_parse_t nl_http_parse(const char *buf, size_t len, nl_http_request_t *req); + +/* Builds a complete response; returns its length or 0 when it does not fit into `cap`. */ +size_t nl_http_response(char *out, size_t cap, int status, const char *json_body); + +const char *nl_http_reason(int status); + +#endif diff --git a/src/net/nl_names.c b/src/net/nl_names.c new file mode 100644 index 0000000..41d3508 --- /dev/null +++ b/src/net/nl_names.c @@ -0,0 +1,20 @@ +#include "net/nl_names.h" + +#include + +void nl_names_mac_suffix(const uint8_t mac[6], char out[5]) +{ + snprintf(out, 5, "%02x%02x", mac[4], mac[5]); +} + +void nl_names_hostname(const uint8_t mac[6], char *out, size_t len) +{ + char suffix[5]; + nl_names_mac_suffix(mac, suffix); + snprintf(out, len, "ninlight-%s", suffix); +} + +void nl_names_mac_str(const uint8_t mac[6], char out[18]) +{ + snprintf(out, 18, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} diff --git a/src/net/nl_names.h b/src/net/nl_names.h new file mode 100644 index 0000000..b1a8585 --- /dev/null +++ b/src/net/nl_names.h @@ -0,0 +1,14 @@ +#ifndef NL_NAMES_H +#define NL_NAMES_H + +#include +#include + +/* Last two MAC bytes as lowercase hex, e.g. "681f". */ +void nl_names_mac_suffix(const uint8_t mac[6], char out[5]); +/* "ninlight-", used as mDNS hostname and onboarding AP SSID. */ +void nl_names_hostname(const uint8_t mac[6], char *out, size_t len); +/* "28:6d:cd:5c:68:1f" */ +void nl_names_mac_str(const uint8_t mac[6], char out[18]); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4aa35d1..6862723 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,6 +11,11 @@ FetchContent_Declare(unity FetchContent_MakeAvailable(unity) enable_testing() + +# cJSON is third-party code: build it without -Werror. +add_library(nl_cjson STATIC ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/cJSON/cJSON.c) +target_include_directories(nl_cjson PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/cJSON) + set(NL_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../src) function(nl_test name) @@ -35,3 +40,5 @@ nl_test(test_state_log test_state_log.c fake_flash.c ${NL_SRC}/config/nl_state_l ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/util/nl_crc.c) nl_test(test_reset_counter test_reset_counter.c fake_flash.c ${NL_SRC}/config/nl_reset_counter.c) nl_test(test_console_cmd test_console_cmd.c ${NL_SRC}/sys/nl_console_cmd.c) +nl_test(test_names test_names.c ${NL_SRC}/net/nl_names.c) +nl_test(test_http test_http.c ${NL_SRC}/net/nl_http.c) diff --git a/test/test_http.c b/test/test_http.c new file mode 100644 index 0000000..572bbaf --- /dev/null +++ b/test/test_http.c @@ -0,0 +1,118 @@ +#include +#include "unity.h" +#include "net/nl_http.h" + +void setUp(void) {} +void tearDown(void) {} + +static nl_http_parse_t parse(const char *s, nl_http_request_t *r) +{ + return nl_http_parse(s, strlen(s), r); +} + +static void test_simple_get(void) +{ + nl_http_request_t r; + TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, parse("GET /api/v1/device/state HTTP/1.1\r\nHost: x\r\n\r\n", &r)); + TEST_ASSERT_EQUAL(NL_HTTP_GET, r.method); + TEST_ASSERT_EQUAL_STRING("/api/v1/device/state", r.path); + TEST_ASSERT_EQUAL_STRING("", r.auth); + TEST_ASSERT_EQUAL_UINT(0, r.body_len); +} + +static void test_post_body_arrives_in_parts(void) +{ + const char *full = "POST /api/v1/device/state HTTP/1.1\r\nContent-Length: 11\r\n\r\n{\"on\":true}"; + nl_http_request_t r; + TEST_ASSERT_EQUAL(NL_HTTP_INCOMPLETE, nl_http_parse(full, 20, &r)); + TEST_ASSERT_EQUAL(NL_HTTP_INCOMPLETE, nl_http_parse(full, strlen(full) - 1, &r)); + TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, nl_http_parse(full, strlen(full), &r)); + TEST_ASSERT_EQUAL(NL_HTTP_POST, r.method); + TEST_ASSERT_EQUAL_UINT(11, r.body_len); + TEST_ASSERT_EQUAL_MEMORY("{\"on\":true}", r.body, 11); +} + +static void test_headers_case_insensitive_and_bearer(void) +{ + nl_http_request_t r; + TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, + parse("PUT /api/v1/system/command HTTP/1.1\r\nauthorization: Bearer abc123\r\n" + "CONTENT-LENGTH: 2\r\n\r\n{}", &r)); + TEST_ASSERT_EQUAL(NL_HTTP_PUT, r.method); + TEST_ASSERT_EQUAL_STRING("abc123", r.auth); + TEST_ASSERT_EQUAL_UINT(2, r.body_len); +} + +static void test_plain_token_and_query_string(void) +{ + nl_http_request_t r; + TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, + parse("GET /api/v1/system/info?x=1 HTTP/1.0\r\nAuthorization: aabbccddeeff0011223344\r\n\r\n", &r)); + TEST_ASSERT_EQUAL_STRING("/api/v1/system/info", r.path); + TEST_ASSERT_EQUAL_STRING("aabbccddeeff0011223344", r.auth); +} + +static void test_other_method(void) +{ + nl_http_request_t r; + TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, parse("DELETE /api/v1/device/state HTTP/1.1\r\n\r\n", &r)); + TEST_ASSERT_EQUAL(NL_HTTP_OTHER, r.method); +} + +static void test_body_too_large(void) +{ + nl_http_request_t r; + TEST_ASSERT_EQUAL(NL_HTTP_TOO_LARGE, parse("POST / HTTP/1.1\r\nContent-Length: 1025\r\n\r\n", &r)); +} + +static void test_headers_never_end(void) +{ + static char buf[NL_HTTP_MAX_REQUEST]; + nl_http_request_t r; + memset(buf, 'a', sizeof buf); + memcpy(buf, "GET / HTTP/1.1\r\n", 16); + TEST_ASSERT_EQUAL(NL_HTTP_TOO_LARGE, nl_http_parse(buf, sizeof buf, &r)); + TEST_ASSERT_EQUAL(NL_HTTP_INCOMPLETE, nl_http_parse(buf, 100, &r)); +} + +static void test_bad_requests(void) +{ + nl_http_request_t r; + char longpath[200]; + TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse("GET /x\r\n\r\n", &r)); + TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse("GET /x FTP/1.1\r\n\r\n", &r)); + TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse("\r\n\r\n", &r)); + TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse("POST / HTTP/1.1\r\nContent-Length: abc\r\n\r\n", &r)); + strcpy(longpath, "GET /"); + memset(longpath + 5, 'p', 100); + strcpy(longpath + 105, " HTTP/1.1\r\n\r\n"); + TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse(longpath, &r)); +} + +static void test_response(void) +{ + char out[256]; + size_t n = nl_http_response(out, sizeof out, 404, "{\"error\":\"not found\"}"); + TEST_ASSERT_EQUAL_STRING("HTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\n" + "Content-Length: 21\r\nConnection: close\r\n\r\n{\"error\":\"not found\"}", out); + TEST_ASSERT_EQUAL_UINT(strlen(out), n); + TEST_ASSERT_EQUAL_UINT(0, nl_http_response(out, 20, 200, "{}")); + TEST_ASSERT_EQUAL_STRING("Accepted", nl_http_reason(202)); + TEST_ASSERT_EQUAL_STRING("Payload Too Large", nl_http_reason(413)); + TEST_ASSERT_EQUAL_STRING("Not Implemented", nl_http_reason(501)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_simple_get); + RUN_TEST(test_post_body_arrives_in_parts); + RUN_TEST(test_headers_case_insensitive_and_bearer); + RUN_TEST(test_plain_token_and_query_string); + RUN_TEST(test_other_method); + RUN_TEST(test_body_too_large); + RUN_TEST(test_headers_never_end); + RUN_TEST(test_bad_requests); + RUN_TEST(test_response); + return UNITY_END(); +} diff --git a/test/test_names.c b/test/test_names.c new file mode 100644 index 0000000..a313b79 --- /dev/null +++ b/test/test_names.c @@ -0,0 +1,45 @@ +#include "unity.h" +#include "net/nl_names.h" + +void setUp(void) {} +void tearDown(void) {} + +static const uint8_t MAC[6] = {0x28, 0x6D, 0xCD, 0x5C, 0x68, 0x1F}; + +static void test_suffix(void) +{ + char s[5]; + nl_names_mac_suffix(MAC, s); + TEST_ASSERT_EQUAL_STRING("681f", s); +} + +static void test_hostname(void) +{ + char h[24]; + nl_names_hostname(MAC, h, sizeof h); + TEST_ASSERT_EQUAL_STRING("ninlight-681f", h); +} + +static void test_hostname_truncates_safely(void) +{ + char h[8]; + nl_names_hostname(MAC, h, sizeof h); + TEST_ASSERT_EQUAL_STRING("ninligh", h); +} + +static void test_mac_str(void) +{ + char m[18]; + nl_names_mac_str(MAC, m); + TEST_ASSERT_EQUAL_STRING("28:6d:cd:5c:68:1f", m); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_suffix); + RUN_TEST(test_hostname); + RUN_TEST(test_hostname_truncates_safely); + RUN_TEST(test_mac_str); + return UNITY_END(); +} diff --git a/third_party/cJSON/LICENSE b/third_party/cJSON/LICENSE new file mode 100644 index 0000000..4723447 --- /dev/null +++ b/third_party/cJSON/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/cJSON/cJSON.c b/third_party/cJSON/cJSON.c new file mode 100644 index 0000000..0277666 --- /dev/null +++ b/third_party/cJSON/cJSON.c @@ -0,0 +1,3143 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + item->valuestring = NULL; + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + item->string = NULL; + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = (a) > (b) ? (a) : (b); + return ((a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else if(d == (double)item->valueint) + { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + output = NULL; + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; + } + + if (printed != NULL) + { + hooks->deallocate(printed); + printed = NULL; + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + p.buffer = NULL; + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + if (cannot_access_at_index(input_buffer, 1)) + { + goto fail; /* nothing comes after the comma */ + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0 || newitem == NULL) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + if (after_inserted != array->child && after_inserted->prev == NULL) { + /* return false if after_inserted is a corrupted array item */ + return false; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { + return false; + } + + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); + object = NULL; +} diff --git a/third_party/cJSON/cJSON.h b/third_party/cJSON/cJSON.h new file mode 100644 index 0000000..88cf0bc --- /dev/null +++ b/third_party/cJSON/cJSON.h @@ -0,0 +1,300 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 18 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif From dc23d3d8874e2e6cbd4784fca9a4e6ab8203ff64 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 22:24:24 +0200 Subject: [PATCH 029/150] feat: REST-API mit Routing, Auth und Handlern --- CMakeLists.txt | 1 + src/net/nl_api.c | 391 ++++++++++++++++++++++++++++++++++++++++++++ src/net/nl_api.h | 56 +++++++ test/CMakeLists.txt | 3 + test/fake_api_ctx.c | 104 ++++++++++++ test/fake_api_ctx.h | 21 +++ test/test_api.c | 338 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 914 insertions(+) create mode 100644 src/net/nl_api.c create mode 100644 src/net/nl_api.h create mode 100644 test/fake_api_ctx.c create mode 100644 test/fake_api_ctx.h create mode 100644 test/test_api.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 46bf1df..0cf3065 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ set(NL_FW_SOURCES src/sys/sdk_stubs.c src/net/nl_names.c src/net/nl_http.c + src/net/nl_api.c third_party/cJSON/cJSON.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) diff --git a/src/net/nl_api.c b/src/net/nl_api.c new file mode 100644 index 0000000..0577003 --- /dev/null +++ b/src/net/nl_api.c @@ -0,0 +1,391 @@ +#include "net/nl_api.h" + +#include +#include + +#include "cJSON.h" + +#define P_STATE "/api/v1/device/state" +#define P_INFO "/api/v1/system/info" +#define P_SETTINGS "/api/v1/system/settings" +#define P_COMMAND "/api/v1/system/command" + +static const char *const POB_NAMES[] = {"last", "on", "off"}; + +static void reply(nl_api_response_t *r, int status, const char *json) +{ + r->status = status; + snprintf(r->body, sizeof r->body, "%s", json); +} + +static void reply_error(nl_api_response_t *r, int status, const char *msg) +{ + r->status = status; + snprintf(r->body, sizeof r->body, "{\"error\":\"%s\"}", msg); +} + +/* Takes ownership of obj. */ +static void reply_json(nl_api_response_t *r, int status, cJSON *obj) +{ + if (obj && cJSON_PrintPreallocated(obj, r->body, (int)sizeof r->body, 0)) + r->status = status; + else + reply_error(r, 500, "response too large"); + cJSON_Delete(obj); +} + +/* Compares in time independent of where the first mismatch is. */ +static bool token_ok(const char *given, const char *expected) +{ + size_t lg = strlen(given), le = strlen(expected); + unsigned diff = (lg != le || le == 0) ? 1u : 0u; + for (size_t i = 0; i < NL_TOKEN_LEN - 1; i++) { + unsigned char a = i < lg ? (unsigned char)given[i] : 0; + unsigned char b = i < le ? (unsigned char)expected[i] : 0; + diff |= (unsigned)(a ^ b); + } + return diff == 0; +} + +/* 1 = present and valid, 0 = absent, -1 = present but invalid */ +static int get_int(const cJSON *obj, const char *name, double lo, double hi, long *out) +{ + const cJSON *v = cJSON_GetObjectItemCaseSensitive(obj, name); + if (!v) + return 0; + if (!cJSON_IsNumber(v) || v->valuedouble != (double)(long)v->valuedouble || v->valuedouble < lo || + v->valuedouble > hi) + return -1; + *out = (long)v->valuedouble; + return 1; +} + +static int get_string(const cJSON *obj, const char *name, size_t min, size_t max, const char **out) +{ + const cJSON *v = cJSON_GetObjectItemCaseSensitive(obj, name); + size_t n; + if (!v) + return 0; + if (!cJSON_IsString(v) || !v->valuestring) + return -1; + n = strlen(v->valuestring); + if (n < min || n > max) + return -1; + *out = v->valuestring; + return 1; +} + +static cJSON *parse_body(const nl_http_request_t *req, nl_api_response_t *resp) +{ + cJSON *j = req->body_len ? cJSON_ParseWithLength(req->body, req->body_len) : NULL; + if (!j || !cJSON_IsObject(j)) { + cJSON_Delete(j); + reply_error(resp, 400, "invalid json"); + return NULL; + } + return j; +} + +static cJSON *state_json(const nl_api_ctx_t *ctx) +{ + nl_light_state_t st; + nl_settings_t s; + cJSON *o, *c, *cap; + int ct[2]; + + ctx->get_state(&st); + ctx->get_settings(&s); + o = cJSON_CreateObject(); + cJSON_AddBoolToObject(o, "on", st.on); + cJSON_AddNumberToObject(o, "brightness", st.brightness); + cJSON_AddStringToObject(o, "mode", st.mode == NL_MODE_COLOR ? "color" : "white"); + c = cJSON_AddObjectToObject(o, "color"); + cJSON_AddNumberToObject(c, "r", st.r); + cJSON_AddNumberToObject(c, "g", st.g); + cJSON_AddNumberToObject(c, "b", st.b); + cJSON_AddNumberToObject(o, "color_temp", st.ct); + cJSON_AddStringToObject(o, "power_on_behaviour", + POB_NAMES[s.power_on_behaviour <= NL_POB_OFF ? s.power_on_behaviour : NL_POB_LAST]); + cJSON_AddNumberToObject(o, "fade_in", s.fade_in_ms); + cJSON_AddNumberToObject(o, "fade_out", s.fade_out_ms); + cap = cJSON_AddObjectToObject(o, "capabilities"); + cJSON_AddBoolToObject(cap, "color", ctx->board->has_rgb); + ct[0] = ctx->board->ct_min; + ct[1] = ctx->board->ct_max; + cJSON_AddItemToObject(cap, "color_temp", cJSON_CreateIntArray(ct, 2)); + return o; +} + +static void handle_post_state(const nl_api_ctx_t *ctx, const nl_http_request_t *req, nl_api_response_t *resp) +{ + cJSON *j = parse_body(req, resp); + const cJSON *it; + nl_light_change_t ch; + nl_settings_t s; + const char *err = NULL; + bool light = false, settings = false; + long v, r, g, b; + + if (!j) + return; + memset(&ch, 0, sizeof ch); + ctx->get_settings(&s); + + it = cJSON_GetObjectItemCaseSensitive(j, "on"); + if (it) { + if (!cJSON_IsBool(it)) { err = "invalid on"; goto out; } + ch.has_on = true; + ch.on = cJSON_IsTrue(it) != 0; + light = true; + } + switch (get_int(j, "brightness", 1, 100, &v)) { + case -1: err = "invalid brightness"; goto out; + case 1: ch.has_brightness = true; ch.brightness = (uint8_t)v; light = true; break; + default: break; + } + it = cJSON_GetObjectItemCaseSensitive(j, "color"); + if (it) { + if (!cJSON_IsObject(it) || get_int(it, "r", 0, 255, &r) != 1 || get_int(it, "g", 0, 255, &g) != 1 || + get_int(it, "b", 0, 255, &b) != 1) { + err = "invalid color"; + goto out; + } + ch.has_color = true; + ch.r = (uint8_t)r; + ch.g = (uint8_t)g; + ch.b = (uint8_t)b; + light = true; + } + switch (get_int(j, "color_temp", 1000, 20000, &v)) { + case -1: err = "invalid color_temp"; goto out; + case 1: ch.has_ct = true; ch.ct = (uint16_t)v; light = true; break; + default: break; + } + switch (get_int(j, "transition", 0, NL_MAX_TRANSITION_MS, &v)) { + case -1: err = "invalid transition"; goto out; + case 1: ch.has_transition = true; ch.transition_ms = (uint32_t)v; break; + default: break; + } + it = cJSON_GetObjectItemCaseSensitive(j, "power_on_behaviour"); + if (it) { + int idx = -1; + for (int i = 0; i < 3 && cJSON_IsString(it); i++) + if (strcmp(it->valuestring, POB_NAMES[i]) == 0) + idx = i; + if (idx < 0) { err = "invalid power_on_behaviour"; goto out; } + s.power_on_behaviour = (uint8_t)idx; + settings = true; + } + switch (get_int(j, "fade_in", 0, NL_MAX_FADE_MS, &v)) { + case -1: err = "invalid fade_in"; goto out; + case 1: s.fade_in_ms = (uint16_t)v; settings = true; break; + default: break; + } + switch (get_int(j, "fade_out", 0, NL_MAX_FADE_MS, &v)) { + case -1: err = "invalid fade_out"; goto out; + case 1: s.fade_out_ms = (uint16_t)v; settings = true; break; + default: break; + } + + if (light) { + switch (ctx->apply(&ch)) { + case NL_CTRL_OK: break; + case NL_CTRL_ERR_NO_RGB: err = "color not supported"; goto out; + case NL_CTRL_ERR_COLOR_AND_CT: err = "color and color_temp are exclusive"; goto out; + case NL_CTRL_ERR_BRIGHTNESS: err = "invalid brightness"; goto out; + default: err = "invalid transition"; goto out; + } + } + if (settings && ctx->save_settings(&s) != 0) { + cJSON_Delete(j); + reply_error(resp, 500, "save failed"); + return; + } +out: + cJSON_Delete(j); + if (err) + reply_error(resp, 400, err); + else + reply_json(resp, 200, state_json(ctx)); +} + +static void handle_info(const nl_api_ctx_t *ctx, nl_api_response_t *resp) +{ + nl_sys_info_t in; + cJSON *o, *w; + + memset(&in, 0, sizeof in); + ctx->get_info(&in); + o = cJSON_CreateObject(); + cJSON_AddStringToObject(o, "device_type", ctx->board->id); + cJSON_AddStringToObject(o, "product_name", ctx->board->name); + cJSON_AddStringToObject(o, "fw_version", ctx->fw_version); + cJSON_AddStringToObject(o, "build_date", ctx->build_date); + cJSON_AddStringToObject(o, "mac", in.mac); + cJSON_AddStringToObject(o, "name", in.name); + w = cJSON_AddObjectToObject(o, "wifi"); + cJSON_AddStringToObject(w, "mode", in.wifi_mode ? in.wifi_mode : "sta"); + cJSON_AddStringToObject(w, "ssid", in.ssid); + cJSON_AddNumberToObject(w, "rssi", in.rssi); + cJSON_AddStringToObject(w, "ip", in.ip); + if (in.sta_ip[0]) + cJSON_AddStringToObject(w, "sta_ip", in.sta_ip); + else + cJSON_AddNullToObject(w, "sta_ip"); + if (in.wifi_error) + cJSON_AddStringToObject(w, "error", in.wifi_error); + else + cJSON_AddNullToObject(w, "error"); + cJSON_AddNumberToObject(o, "uptime", in.uptime_s); + cJSON_AddNumberToObject(o, "free_heap", in.free_heap); + cJSON_AddStringToObject(o, "reset_reason", "unknown"); + if (in.last_ota_error) + cJSON_AddStringToObject(o, "last_ota_error", in.last_ota_error); + else + cJSON_AddNullToObject(o, "last_ota_error"); + reply_json(resp, 200, o); +} + +static void handle_get_settings(const nl_api_ctx_t *ctx, nl_api_response_t *resp) +{ + nl_settings_t s; + cJSON *o, *w; + + ctx->get_settings(&s); + o = cJSON_CreateObject(); + cJSON_AddStringToObject(o, "name", s.name); + w = cJSON_AddObjectToObject(o, "wifi"); + cJSON_AddStringToObject(w, "ssid", s.wifi_ssid); + cJSON_AddBoolToObject(o, "auth_all", s.auth_all != 0); + reply_json(resp, 200, o); +} + +static void handle_put_settings(const nl_api_ctx_t *ctx, const nl_http_request_t *req, nl_api_response_t *resp) +{ + cJSON *j = parse_body(req, resp); + const char *type = NULL, *a = NULL, *b = NULL, *err = NULL; + nl_settings_t s; + int pr = 0; + + if (!j) + return; + ctx->get_settings(&s); + if (get_string(j, "type", 1, 32, &type) != 1) { + err = "invalid type"; + } else if (strcmp(type, "wifi") == 0) { + if (get_string(j, "ssid", 1, NL_SSID_LEN - 1, &a) != 1) { + err = "invalid ssid"; + } else if ((pr = get_string(j, "password", 0, NL_PASS_LEN - 1, &b)) < 0) { + err = "invalid password"; + } else { + if (ctx->wifi_configure(a, pr == 1 ? b : "") != 0) + reply_error(resp, 503, "wifi busy"); + else + reply(resp, 202, "{\"status\":\"connecting\"}"); + cJSON_Delete(j); + return; + } + } else if (strcmp(type, "device_name") == 0) { + if (get_string(j, "name", 1, NL_NAME_LEN - 1, &a) != 1) + err = "invalid name"; + else + strcpy(s.name, a); + } else if (strcmp(type, "token") == 0) { + if (get_string(j, "token", 8, NL_TOKEN_LEN - 1, &a) != 1) + err = "invalid token"; + else + strcpy(s.token, a); + } else if (strcmp(type, "auth") == 0) { + const cJSON *all = cJSON_GetObjectItemCaseSensitive(j, "all"); + if (!cJSON_IsBool(all)) + err = "invalid all"; + else + s.auth_all = cJSON_IsTrue(all) ? 1 : 0; + } else { + err = "invalid type"; + } + cJSON_Delete(j); + if (err) + reply_error(resp, 400, err); + else if (ctx->save_settings(&s) != 0) + reply_error(resp, 500, "save failed"); + else + reply(resp, 200, "{\"ok\":true}"); +} + +static void handle_command(const nl_api_ctx_t *ctx, const nl_http_request_t *req, nl_api_response_t *resp) +{ + cJSON *j = parse_body(req, resp); + const cJSON *cmd; + const char *type = NULL, *url = NULL; + + if (!j) + return; + cmd = cJSON_GetObjectItemCaseSensitive(j, "command"); + if (!cJSON_IsObject(cmd) || get_string(cmd, "type", 1, 32, &type) != 1) { + reply_error(resp, 400, "invalid command"); + } else if (strcmp(type, "fw_update") == 0) { + if (get_string(cmd, "url", 8, 255, &url) != 1 || strncmp(url, "http://", 7) != 0) + reply_error(resp, 400, "invalid url"); + else if (ctx->fw_update(url) != 0) + reply_error(resp, 501, "fw_update not available"); + else + reply(resp, 202, "{\"status\":\"updating\"}"); + } else if (strcmp(type, "reboot") == 0) { + reply(resp, 200, "{\"ok\":true}"); + resp->after = NL_API_AFTER_REBOOT; + } else if (strcmp(type, "factory_reset") == 0) { + ctx->factory_reset(); + reply(resp, 200, "{\"ok\":true}"); + } else if (strcmp(type, "ping") == 0) { + ctx->ping(); + reply(resp, 200, "{\"ok\":true}"); + } else { + reply_error(resp, 400, "invalid command"); + } + cJSON_Delete(j); +} + +void nl_api_handle(const nl_api_ctx_t *ctx, const nl_http_request_t *req, nl_api_response_t *resp) +{ + nl_settings_t s; + bool authed; + + memset(resp, 0, sizeof *resp); + ctx->get_settings(&s); + authed = ctx->open_access() || token_ok(req->auth, s.token); + + if (strcmp(req->path, P_STATE) == 0) { + if (s.auth_all && !authed) + reply_error(resp, 401, "unauthorized"); + else if (req->method == NL_HTTP_GET) + reply_json(resp, 200, state_json(ctx)); + else if (req->method == NL_HTTP_POST) + handle_post_state(ctx, req, resp); + else + reply_error(resp, 405, "method not allowed"); + } else if (strcmp(req->path, P_INFO) == 0) { + if (req->method == NL_HTTP_GET) + handle_info(ctx, resp); + else + reply_error(resp, 405, "method not allowed"); + } else if (strcmp(req->path, P_SETTINGS) == 0) { + if (!authed) + reply_error(resp, 401, "unauthorized"); + else if (req->method == NL_HTTP_GET) + handle_get_settings(ctx, resp); + else if (req->method == NL_HTTP_PUT) + handle_put_settings(ctx, req, resp); + else + reply_error(resp, 405, "method not allowed"); + } else if (strcmp(req->path, P_COMMAND) == 0) { + if (!authed) + reply_error(resp, 401, "unauthorized"); + else if (req->method == NL_HTTP_PUT) + handle_command(ctx, req, resp); + else + reply_error(resp, 405, "method not allowed"); + } else { + reply_error(resp, 404, "not found"); + } +} diff --git a/src/net/nl_api.h b/src/net/nl_api.h new file mode 100644 index 0000000..83980ee --- /dev/null +++ b/src/net/nl_api.h @@ -0,0 +1,56 @@ +#ifndef NL_API_H +#define NL_API_H + +#include +#include + +#include "board/nl_board.h" +#include "config/nl_settings.h" +#include "light/nl_control.h" +#include "light/nl_light_types.h" +#include "net/nl_http.h" + +#define NL_API_BODY_MAX 1024 + +typedef struct { + char mac[18]; + char name[NL_NAME_LEN]; + const char *wifi_mode; /* "sta" | "ap" | "apsta" */ + char ssid[NL_SSID_LEN]; + int rssi; /* dBm, 0 when not connected */ + char ip[16]; /* address the device answers on, "" if none */ + char sta_ip[16]; /* station address, "" when not connected (reported as null) */ + const char *wifi_error; /* NULL | "auth" | "not_found" | "timeout" */ + uint32_t uptime_s; + uint32_t free_heap; + const char *last_ota_error; /* NULL until OTA exists */ +} nl_sys_info_t; + +/* Everything the API needs from the application; implemented by src/sys/nl_app_api.c. */ +typedef struct { + const nl_board_t *board; + const char *fw_version; + const char *build_date; + void (*get_state)(nl_light_state_t *out); + nl_ctrl_err_t (*apply)(const nl_light_change_t *c); + void (*get_settings)(nl_settings_t *out); + int (*save_settings)(const nl_settings_t *s); /* 0 = ok; wifi fields are ignored */ + int (*wifi_configure)(const char *ssid, const char *pass); /* 0 = onboarding test started */ + bool (*open_access)(void); /* unconfigured onboarding AP */ + void (*get_info)(nl_sys_info_t *out); + void (*factory_reset)(void); /* request only */ + void (*ping)(void); + int (*fw_update)(const char *url); /* 0 = started, <0 = unavailable */ +} nl_api_ctx_t; + +typedef enum { NL_API_AFTER_NONE = 0, NL_API_AFTER_REBOOT } nl_api_after_t; + +typedef struct { + int status; + nl_api_after_t after; /* action for the server once the response has been sent */ + char body[NL_API_BODY_MAX]; +} nl_api_response_t; + +void nl_api_handle(const nl_api_ctx_t *ctx, const nl_http_request_t *req, nl_api_response_t *resp); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6862723..57924ba 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -42,3 +42,6 @@ nl_test(test_reset_counter test_reset_counter.c fake_flash.c ${NL_SRC}/config/nl nl_test(test_console_cmd test_console_cmd.c ${NL_SRC}/sys/nl_console_cmd.c) nl_test(test_names test_names.c ${NL_SRC}/net/nl_names.c) nl_test(test_http test_http.c ${NL_SRC}/net/nl_http.c) +nl_test(test_api test_api.c fake_api_ctx.c ${NL_SRC}/net/nl_api.c ${NL_SRC}/board/nl_board.c + ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/config/nl_settings.c ${NL_SRC}/util/nl_crc.c fake_flash.c) +target_link_libraries(test_api PRIVATE nl_cjson) diff --git a/test/fake_api_ctx.c b/test/fake_api_ctx.c new file mode 100644 index 0000000..2e6d5d6 --- /dev/null +++ b/test/fake_api_ctx.c @@ -0,0 +1,104 @@ +#include "fake_api_ctx.h" + +#include + +#include "light/nl_boot_state.h" + +nl_light_state_t fake_state; +nl_settings_t fake_settings; +nl_sys_info_t fake_info; +nl_light_change_t fake_last_change; +nl_ctrl_err_t fake_apply_result; +int fake_apply_calls, fake_save_calls, fake_save_result; +int fake_wifi_calls, fake_wifi_result; +char fake_wifi_ssid[NL_SSID_LEN], fake_wifi_pass[NL_PASS_LEN]; +int fake_factory_calls, fake_ping_calls, fake_fw_result; +char fake_fw_url[256]; +bool fake_open; + +static void get_state(nl_light_state_t *out) { *out = fake_state; } + +static nl_ctrl_err_t apply(const nl_light_change_t *c) +{ + fake_apply_calls++; + fake_last_change = *c; + if (fake_apply_result != NL_CTRL_OK) + return fake_apply_result; + if (c->has_on) fake_state.on = c->on; + if (c->has_brightness) fake_state.brightness = c->brightness; + if (c->has_color) { fake_state.r = c->r; fake_state.g = c->g; fake_state.b = c->b; fake_state.mode = NL_MODE_COLOR; } + if (c->has_ct) { fake_state.ct = c->ct; fake_state.mode = NL_MODE_WHITE; } + return NL_CTRL_OK; +} + +static void get_settings(nl_settings_t *out) { *out = fake_settings; } + +static int save_settings(const nl_settings_t *s) +{ + fake_save_calls++; + if (fake_save_result == 0) + fake_settings = *s; + return fake_save_result; +} + +static int wifi_configure(const char *ssid, const char *pass) +{ + fake_wifi_calls++; + strncpy(fake_wifi_ssid, ssid, sizeof fake_wifi_ssid - 1); + strncpy(fake_wifi_pass, pass, sizeof fake_wifi_pass - 1); + return fake_wifi_result; +} + +static bool open_access(void) { return fake_open; } +static void get_info(nl_sys_info_t *out) { *out = fake_info; } +static void factory_reset(void) { fake_factory_calls++; } +static void ping(void) { fake_ping_calls++; } + +static int fw_update(const char *url) +{ + strncpy(fake_fw_url, url, sizeof fake_fw_url - 1); + return fake_fw_result; +} + +nl_api_ctx_t fake_ctx; + +void fake_api_reset(const char *board_id) +{ + const nl_board_t *b = nl_board_find(board_id); + fake_state = nl_boot_default_state(b); + nl_settings_defaults(&fake_settings); + strcpy(fake_settings.name, "Wohnzimmer"); + strcpy(fake_settings.wifi_ssid, "ninHome"); + memset(&fake_info, 0, sizeof fake_info); + strcpy(fake_info.mac, "28:6d:cd:5c:68:1f"); + strcpy(fake_info.name, "Wohnzimmer"); + fake_info.wifi_mode = "sta"; + strcpy(fake_info.ssid, "ninHome"); + fake_info.rssi = -58; + strcpy(fake_info.ip, "192.168.1.50"); + fake_info.uptime_s = 123; + fake_info.free_heap = 40000; + memset(&fake_last_change, 0, sizeof fake_last_change); + fake_apply_result = NL_CTRL_OK; + fake_apply_calls = fake_save_calls = fake_save_result = 0; + fake_wifi_calls = fake_wifi_result = 0; + fake_wifi_ssid[0] = fake_wifi_pass[0] = '\0'; + fake_factory_calls = fake_ping_calls = 0; + fake_fw_result = -1; + fake_fw_url[0] = '\0'; + fake_open = false; + + fake_ctx.board = b; + fake_ctx.fw_version = "1.2.3"; + fake_ctx.build_date = "2026-09-11"; + fake_ctx.get_state = get_state; + fake_ctx.apply = apply; + fake_ctx.get_settings = get_settings; + fake_ctx.save_settings = save_settings; + fake_ctx.wifi_configure = wifi_configure; + fake_ctx.open_access = open_access; + fake_ctx.get_info = get_info; + fake_ctx.factory_reset = factory_reset; + fake_ctx.ping = ping; + fake_ctx.fw_update = fw_update; +} diff --git a/test/fake_api_ctx.h b/test/fake_api_ctx.h new file mode 100644 index 0000000..9abfebd --- /dev/null +++ b/test/fake_api_ctx.h @@ -0,0 +1,21 @@ +#ifndef FAKE_API_CTX_H +#define FAKE_API_CTX_H + +#include "net/nl_api.h" + +extern nl_api_ctx_t fake_ctx; +extern nl_light_state_t fake_state; +extern nl_settings_t fake_settings; +extern nl_sys_info_t fake_info; +extern nl_light_change_t fake_last_change; +extern nl_ctrl_err_t fake_apply_result; +extern int fake_apply_calls, fake_save_calls, fake_save_result; +extern int fake_wifi_calls, fake_wifi_result; +extern char fake_wifi_ssid[NL_SSID_LEN], fake_wifi_pass[NL_PASS_LEN]; +extern int fake_factory_calls, fake_ping_calls, fake_fw_result; +extern char fake_fw_url[256]; +extern bool fake_open; + +void fake_api_reset(const char *board_id); + +#endif diff --git a/test/test_api.c b/test/test_api.c new file mode 100644 index 0000000..794d609 --- /dev/null +++ b/test/test_api.c @@ -0,0 +1,338 @@ +#include +#include "unity.h" +#include "cJSON.h" +#include "fake_api_ctx.h" + +#define TOKEN "aabbccddeeff0011223344" + +static nl_api_response_t resp; +static cJSON *doc; + +static void call(nl_http_method_t m, const char *path, const char *auth, const char *body) +{ + nl_http_request_t r; + memset(&r, 0, sizeof r); + r.method = m; + strcpy(r.path, path); + strcpy(r.auth, auth ? auth : ""); + r.body = body; + r.body_len = body ? strlen(body) : 0; + nl_api_handle(&fake_ctx, &r, &resp); + cJSON_Delete(doc); + doc = cJSON_Parse(resp.body); +} + +static const cJSON *field(const char *name) { return cJSON_GetObjectItemCaseSensitive(doc, name); } +static const char *error_text(void) { return cJSON_GetStringValue(field("error")); } + +void setUp(void) { fake_api_reset("evk"); doc = NULL; } +void tearDown(void) { cJSON_Delete(doc); doc = NULL; } + +/* --- routing --- */ +static void test_unknown_path_and_method(void) +{ + call(NL_HTTP_GET, "/api/v1/nope", NULL, NULL); + TEST_ASSERT_EQUAL_INT(404, resp.status); + TEST_ASSERT_EQUAL_STRING("not found", error_text()); + call(NL_HTTP_PUT, "/api/v1/device/state", NULL, "{}"); + TEST_ASSERT_EQUAL_INT(405, resp.status); + call(NL_HTTP_POST, "/api/v1/system/info", NULL, "{}"); + TEST_ASSERT_EQUAL_INT(405, resp.status); +} + +/* --- device/state --- */ +static void test_get_state(void) +{ + const cJSON *cap; + fake_settings.fade_in_ms = 700; + call(NL_HTTP_GET, "/api/v1/device/state", NULL, NULL); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_TRUE(cJSON_IsTrue(field("on"))); + TEST_ASSERT_EQUAL_INT(100, field("brightness")->valueint); + TEST_ASSERT_EQUAL_STRING("white", cJSON_GetStringValue(field("mode"))); + TEST_ASSERT_EQUAL_INT(255, cJSON_GetObjectItem(field("color"), "g")->valueint); + TEST_ASSERT_EQUAL_INT(2700, field("color_temp")->valueint); + TEST_ASSERT_EQUAL_STRING("last", cJSON_GetStringValue(field("power_on_behaviour"))); + TEST_ASSERT_EQUAL_INT(700, field("fade_in")->valueint); + TEST_ASSERT_EQUAL_INT(500, field("fade_out")->valueint); + cap = field("capabilities"); + TEST_ASSERT_TRUE(cJSON_IsTrue(cJSON_GetObjectItem(cap, "color"))); + TEST_ASSERT_EQUAL_INT(2700, cJSON_GetArrayItem(cJSON_GetObjectItem(cap, "color_temp"), 0)->valueint); + TEST_ASSERT_EQUAL_INT(6500, cJSON_GetArrayItem(cJSON_GetObjectItem(cap, "color_temp"), 1)->valueint); +} + +static void test_white_board_capabilities(void) +{ + fake_api_reset("e27_cw"); + call(NL_HTTP_GET, "/api/v1/device/state", NULL, NULL); + TEST_ASSERT_TRUE(cJSON_IsFalse(cJSON_GetObjectItem(field("capabilities"), "color"))); +} + +static void test_post_state_applies_change(void) +{ + call(NL_HTTP_POST, "/api/v1/device/state", NULL, "{\"on\":false,\"brightness\":50,\"transition\":800}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_INT(1, fake_apply_calls); + TEST_ASSERT_TRUE(fake_last_change.has_on); + TEST_ASSERT_FALSE(fake_last_change.on); + TEST_ASSERT_EQUAL_UINT8(50, fake_last_change.brightness); + TEST_ASSERT_TRUE(fake_last_change.has_transition); + TEST_ASSERT_EQUAL_UINT32(800, fake_last_change.transition_ms); + TEST_ASSERT_TRUE(cJSON_IsFalse(field("on"))); + TEST_ASSERT_EQUAL_INT(50, field("brightness")->valueint); + TEST_ASSERT_EQUAL_INT(0, fake_save_calls); +} + +static void test_post_color_and_ct(void) +{ + call(NL_HTTP_POST, "/api/v1/device/state", NULL, "{\"color\":{\"r\":255,\"g\":120,\"b\":0}}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_TRUE(fake_last_change.has_color); + TEST_ASSERT_EQUAL_UINT8(120, fake_last_change.g); + TEST_ASSERT_EQUAL_STRING("color", cJSON_GetStringValue(field("mode"))); + call(NL_HTTP_POST, "/api/v1/device/state", NULL, "{\"color_temp\":4000}"); + TEST_ASSERT_TRUE(fake_last_change.has_ct); + TEST_ASSERT_EQUAL_UINT16(4000, fake_last_change.ct); +} + +static void test_post_validation(void) +{ + static const struct { const char *body; const char *err; } CASES[] = { + {"not json", "invalid json"}, + {"[1,2]", "invalid json"}, + {"{\"on\":1}", "invalid on"}, + {"{\"brightness\":0}", "invalid brightness"}, + {"{\"brightness\":101}", "invalid brightness"}, + {"{\"brightness\":1.5}", "invalid brightness"}, + {"{\"brightness\":\"50\"}", "invalid brightness"}, + {"{\"color\":{\"r\":1,\"g\":2}}", "invalid color"}, + {"{\"color\":{\"r\":1,\"g\":2,\"b\":256}}", "invalid color"}, + {"{\"color_temp\":999}", "invalid color_temp"}, + {"{\"transition\":60001}", "invalid transition"}, + {"{\"power_on_behaviour\":\"maybe\"}", "invalid power_on_behaviour"}, + {"{\"fade_in\":10001}", "invalid fade_in"}, + {"{\"fade_out\":-1}", "invalid fade_out"}, + }; + for (size_t i = 0; i < sizeof CASES / sizeof CASES[0]; i++) { + call(NL_HTTP_POST, "/api/v1/device/state", NULL, CASES[i].body); + TEST_ASSERT_EQUAL_INT_MESSAGE(400, resp.status, CASES[i].body); + TEST_ASSERT_EQUAL_STRING_MESSAGE(CASES[i].err, error_text(), CASES[i].body); + } + call(NL_HTTP_POST, "/api/v1/device/state", NULL, NULL); + TEST_ASSERT_EQUAL_STRING("invalid json", error_text()); + TEST_ASSERT_EQUAL_INT(0, fake_apply_calls); + TEST_ASSERT_EQUAL_INT(0, fake_save_calls); +} + +static void test_post_control_errors(void) +{ + fake_apply_result = NL_CTRL_ERR_NO_RGB; + call(NL_HTTP_POST, "/api/v1/device/state", NULL, "{\"color\":{\"r\":1,\"g\":2,\"b\":3},\"fade_in\":100}"); + TEST_ASSERT_EQUAL_INT(400, resp.status); + TEST_ASSERT_EQUAL_STRING("color not supported", error_text()); + TEST_ASSERT_EQUAL_INT(0, fake_save_calls); /* settings not saved after a failed change */ + fake_apply_result = NL_CTRL_ERR_COLOR_AND_CT; + call(NL_HTTP_POST, "/api/v1/device/state", NULL, "{\"color\":{\"r\":1,\"g\":2,\"b\":3},\"color_temp\":3000}"); + TEST_ASSERT_EQUAL_STRING("color and color_temp are exclusive", error_text()); +} + +static void test_post_settings_fields(void) +{ + call(NL_HTTP_POST, "/api/v1/device/state", NULL, + "{\"power_on_behaviour\":\"on\",\"fade_in\":1000,\"fade_out\":0,\"transition\":300}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_INT(0, fake_apply_calls); /* transition alone is ignored */ + TEST_ASSERT_EQUAL_INT(1, fake_save_calls); + TEST_ASSERT_EQUAL_UINT8(NL_POB_ON, fake_settings.power_on_behaviour); + TEST_ASSERT_EQUAL_UINT16(1000, fake_settings.fade_in_ms); + TEST_ASSERT_EQUAL_UINT16(0, fake_settings.fade_out_ms); + TEST_ASSERT_EQUAL_STRING("on", cJSON_GetStringValue(field("power_on_behaviour"))); +} + +static void test_post_save_failure(void) +{ + fake_save_result = -1; + call(NL_HTTP_POST, "/api/v1/device/state", NULL, "{\"fade_in\":100}"); + TEST_ASSERT_EQUAL_INT(500, resp.status); + TEST_ASSERT_EQUAL_STRING("save failed", error_text()); +} + +static void test_auth_all_protects_device_state(void) +{ + fake_settings.auth_all = 1; + call(NL_HTTP_GET, "/api/v1/device/state", NULL, NULL); + TEST_ASSERT_EQUAL_INT(401, resp.status); + TEST_ASSERT_EQUAL_STRING("unauthorized", error_text()); + call(NL_HTTP_GET, "/api/v1/device/state", TOKEN, NULL); + TEST_ASSERT_EQUAL_INT(200, resp.status); + fake_open = true; + call(NL_HTTP_GET, "/api/v1/device/state", NULL, NULL); + TEST_ASSERT_EQUAL_INT(200, resp.status); +} + +/* --- system/info --- */ +static void test_info_is_open(void) +{ + const cJSON *w; + call(NL_HTTP_GET, "/api/v1/system/info", NULL, NULL); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_STRING("evk", cJSON_GetStringValue(field("device_type"))); + TEST_ASSERT_EQUAL_STRING("W600 EVK v1.2", cJSON_GetStringValue(field("product_name"))); + TEST_ASSERT_EQUAL_STRING("1.2.3", cJSON_GetStringValue(field("fw_version"))); + TEST_ASSERT_EQUAL_STRING("2026-09-11", cJSON_GetStringValue(field("build_date"))); + TEST_ASSERT_EQUAL_STRING("28:6d:cd:5c:68:1f", cJSON_GetStringValue(field("mac"))); + TEST_ASSERT_EQUAL_STRING("Wohnzimmer", cJSON_GetStringValue(field("name"))); + w = field("wifi"); + TEST_ASSERT_EQUAL_STRING("sta", cJSON_GetStringValue(cJSON_GetObjectItem(w, "mode"))); + TEST_ASSERT_EQUAL_STRING("ninHome", cJSON_GetStringValue(cJSON_GetObjectItem(w, "ssid"))); + TEST_ASSERT_EQUAL_INT(-58, cJSON_GetObjectItem(w, "rssi")->valueint); + TEST_ASSERT_EQUAL_STRING("192.168.1.50", cJSON_GetStringValue(cJSON_GetObjectItem(w, "ip"))); + TEST_ASSERT_TRUE(cJSON_IsNull(cJSON_GetObjectItem(w, "sta_ip"))); + TEST_ASSERT_TRUE(cJSON_IsNull(cJSON_GetObjectItem(w, "error"))); + TEST_ASSERT_EQUAL_INT(123, field("uptime")->valueint); + TEST_ASSERT_EQUAL_INT(40000, field("free_heap")->valueint); + TEST_ASSERT_EQUAL_STRING("unknown", cJSON_GetStringValue(field("reset_reason"))); + TEST_ASSERT_TRUE(cJSON_IsNull(field("last_ota_error"))); +} + +static void test_info_with_sta_ip_and_error(void) +{ + strcpy(fake_info.sta_ip, "192.168.1.50"); + fake_info.wifi_error = "auth"; + call(NL_HTTP_GET, "/api/v1/system/info", NULL, NULL); + TEST_ASSERT_EQUAL_STRING("192.168.1.50", cJSON_GetStringValue(cJSON_GetObjectItem(field("wifi"), "sta_ip"))); + TEST_ASSERT_EQUAL_STRING("auth", cJSON_GetStringValue(cJSON_GetObjectItem(field("wifi"), "error"))); +} + +/* --- system/settings --- */ +static void test_settings_auth(void) +{ + call(NL_HTTP_GET, "/api/v1/system/settings", NULL, NULL); + TEST_ASSERT_EQUAL_INT(401, resp.status); + call(NL_HTTP_GET, "/api/v1/system/settings", "aabb", NULL); + TEST_ASSERT_EQUAL_INT(401, resp.status); + call(NL_HTTP_GET, "/api/v1/system/settings", TOKEN "x", NULL); + TEST_ASSERT_EQUAL_INT(401, resp.status); + call(NL_HTTP_GET, "/api/v1/system/settings", TOKEN, NULL); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_STRING("Wohnzimmer", cJSON_GetStringValue(field("name"))); + TEST_ASSERT_EQUAL_STRING("ninHome", cJSON_GetStringValue(cJSON_GetObjectItem(field("wifi"), "ssid"))); + TEST_ASSERT_TRUE(cJSON_IsFalse(field("auth_all"))); + TEST_ASSERT_NULL(cJSON_GetObjectItem(field("wifi"), "password")); /* never exposed */ + fake_open = true; + call(NL_HTTP_GET, "/api/v1/system/settings", NULL, NULL); + TEST_ASSERT_EQUAL_INT(200, resp.status); +} + +static void test_settings_wifi(void) +{ + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"ninIOT\",\"password\":\"secret12\"}"); + TEST_ASSERT_EQUAL_INT(202, resp.status); + TEST_ASSERT_EQUAL_STRING("connecting", cJSON_GetStringValue(field("status"))); + TEST_ASSERT_EQUAL_STRING("ninIOT", fake_wifi_ssid); + TEST_ASSERT_EQUAL_STRING("secret12", fake_wifi_pass); + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"open\"}"); + TEST_ASSERT_EQUAL_INT(202, resp.status); + TEST_ASSERT_EQUAL_STRING("", fake_wifi_pass); + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, + "{\"type\":\"wifi\",\"ssid\":\"123456789012345678901234567890123\"}"); + TEST_ASSERT_EQUAL_STRING("invalid ssid", error_text()); + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"x\",\"password\":5}"); + TEST_ASSERT_EQUAL_STRING("invalid password", error_text()); + fake_wifi_result = -1; + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"x\"}"); + TEST_ASSERT_EQUAL_INT(503, resp.status); + TEST_ASSERT_EQUAL_STRING("wifi busy", error_text()); +} + +static void test_settings_name_token_auth(void) +{ + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"device_name\",\"name\":\"Küche\"}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_TRUE(cJSON_IsTrue(field("ok"))); + TEST_ASSERT_EQUAL_STRING("Küche", fake_settings.name); + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"device_name\",\"name\":\"\"}"); + TEST_ASSERT_EQUAL_STRING("invalid name", error_text()); + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, + "{\"type\":\"device_name\",\"name\":\"123456789012345678901234567890123\"}"); + TEST_ASSERT_EQUAL_STRING("invalid name", error_text()); + + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"token\",\"token\":\"1234567\"}"); + TEST_ASSERT_EQUAL_STRING("invalid token", error_text()); + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"token\",\"token\":\"12345678\"}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_STRING("12345678", fake_settings.token); + + call(NL_HTTP_PUT, "/api/v1/system/settings", "12345678", "{\"type\":\"auth\",\"all\":true}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_UINT8(1, fake_settings.auth_all); + call(NL_HTTP_PUT, "/api/v1/system/settings", "12345678", "{\"type\":\"auth\",\"all\":\"yes\"}"); + TEST_ASSERT_EQUAL_STRING("invalid all", error_text()); + call(NL_HTTP_PUT, "/api/v1/system/settings", "12345678", "{\"type\":\"colour\"}"); + TEST_ASSERT_EQUAL_STRING("invalid type", error_text()); + call(NL_HTTP_PUT, "/api/v1/system/settings", "12345678", "{}"); + TEST_ASSERT_EQUAL_STRING("invalid type", error_text()); + fake_save_result = -1; + call(NL_HTTP_PUT, "/api/v1/system/settings", "12345678", "{\"type\":\"device_name\",\"name\":\"x\"}"); + TEST_ASSERT_EQUAL_INT(500, resp.status); +} + +/* --- system/command --- */ +static void test_commands(void) +{ + call(NL_HTTP_GET, "/api/v1/system/command", TOKEN, NULL); + TEST_ASSERT_EQUAL_INT(405, resp.status); + call(NL_HTTP_PUT, "/api/v1/system/command", NULL, "{\"command\":{\"type\":\"ping\"}}"); + TEST_ASSERT_EQUAL_INT(401, resp.status); + + call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"ping\"}}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_INT(1, fake_ping_calls); + TEST_ASSERT_EQUAL(NL_API_AFTER_NONE, resp.after); + + call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"reboot\"}}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL(NL_API_AFTER_REBOOT, resp.after); + + call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"factory_reset\"}}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_INT(1, fake_factory_calls); + + call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"fw_update\",\"url\":\"http://x/y.img\"}}"); + TEST_ASSERT_EQUAL_INT(501, resp.status); + TEST_ASSERT_EQUAL_STRING("fw_update not available", error_text()); + TEST_ASSERT_EQUAL_STRING("http://x/y.img", fake_fw_url); + fake_fw_result = 0; + call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"fw_update\",\"url\":\"http://x/y.img\"}}"); + TEST_ASSERT_EQUAL_INT(202, resp.status); + TEST_ASSERT_EQUAL_STRING("updating", cJSON_GetStringValue(field("status"))); + call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"fw_update\",\"url\":\"ftp://x\"}}"); + TEST_ASSERT_EQUAL_STRING("invalid url", error_text()); + + call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"dance\"}}"); + TEST_ASSERT_EQUAL_STRING("invalid command", error_text()); + call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"type\":\"reboot\"}"); + TEST_ASSERT_EQUAL_STRING("invalid command", error_text()); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_unknown_path_and_method); + RUN_TEST(test_get_state); + RUN_TEST(test_white_board_capabilities); + RUN_TEST(test_post_state_applies_change); + RUN_TEST(test_post_color_and_ct); + RUN_TEST(test_post_validation); + RUN_TEST(test_post_control_errors); + RUN_TEST(test_post_settings_fields); + RUN_TEST(test_post_save_failure); + RUN_TEST(test_auth_all_protects_device_state); + RUN_TEST(test_info_is_open); + RUN_TEST(test_info_with_sta_ip_and_error); + RUN_TEST(test_settings_auth); + RUN_TEST(test_settings_wifi); + RUN_TEST(test_settings_name_token_auth); + RUN_TEST(test_commands); + return UNITY_END(); +} From 2bdeaed98d3b248f4e31cb091acae5a56cac4449 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 22:35:30 +0200 Subject: [PATCH 030/150] feat: WLAN-Zustandsautomat mit Onboarding, Backoff und AP-Fallback --- CMakeLists.txt | 1 + src/net/nl_wifi_sm.c | 182 +++++++++++++++++++++++++++++++++++++++++++ src/net/nl_wifi_sm.h | 59 ++++++++++++++ test/CMakeLists.txt | 1 + test/test_wifi_sm.c | 172 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 415 insertions(+) create mode 100644 src/net/nl_wifi_sm.c create mode 100644 src/net/nl_wifi_sm.h create mode 100644 test/test_wifi_sm.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cf3065..64c799e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ set(NL_FW_SOURCES src/net/nl_names.c src/net/nl_http.c src/net/nl_api.c + src/net/nl_wifi_sm.c third_party/cJSON/cJSON.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) diff --git a/src/net/nl_wifi_sm.c b/src/net/nl_wifi_sm.c new file mode 100644 index 0000000..dd5d38f --- /dev/null +++ b/src/net/nl_wifi_sm.c @@ -0,0 +1,182 @@ +#include "net/nl_wifi_sm.h" + +#include + +static bool due(uint32_t now, uint32_t at) +{ + return (int32_t)(now - at) >= 0; +} + +static nl_wifi_actions_t none(void) +{ + nl_wifi_actions_t a; + memset(&a, 0, sizeof a); + return a; +} + +static void schedule_retry(nl_wifi_sm_t *sm, uint32_t now) +{ + sm->retry_armed = true; + sm->retry_at = now + sm->backoff; + sm->backoff = sm->backoff * 2u > NL_WIFI_BACKOFF_MAX_MS ? NL_WIFI_BACKOFF_MAX_MS : sm->backoff * 2u; +} + +static void begin_connecting(nl_wifi_sm_t *sm, uint32_t now) +{ + sm->state = NL_WIFI_ST_STA_CONNECTING; + sm->since = now; + sm->backoff = NL_WIFI_BACKOFF_MIN_MS; + sm->retry_armed = false; +} + +static void arm_ap_stop(nl_wifi_sm_t *sm, uint32_t at) +{ + if (sm->ap_up) { + sm->ap_stop_armed = true; + sm->ap_stop_at = at; + } +} + +static void fail_test(nl_wifi_sm_t *sm, nl_wifi_error_t err, uint32_t now, nl_wifi_actions_t *a) +{ + sm->error = err; + if (sm->have_creds) { + sm->state = NL_WIFI_ST_APSTA_FALLBACK; + sm->since = now; + sm->backoff = NL_WIFI_BACKOFF_MIN_MS; + sm->retry_armed = false; + a->connect = true; /* old credentials */ + } else { + sm->state = NL_WIFI_ST_AP_ONBOARDING; + } +} + +nl_wifi_actions_t nl_wifi_sm_start(nl_wifi_sm_t *sm, bool have_creds, uint32_t now) +{ + nl_wifi_actions_t a = none(); + + memset(sm, 0, sizeof *sm); + sm->have_creds = have_creds; + if (have_creds) { + begin_connecting(sm, now); + a.connect = true; + } else { + sm->state = NL_WIFI_ST_AP_ONBOARDING; + sm->ap_up = true; + sm->pulsing = true; + a.start_ap = true; + a.pulse_on = true; + } + return a; +} + +nl_wifi_actions_t nl_wifi_sm_event(nl_wifi_sm_t *sm, nl_wifi_event_t ev, nl_wifi_error_t err, uint32_t now) +{ + nl_wifi_actions_t a = none(); + + switch (sm->state) { + case NL_WIFI_ST_STA_CONNECTING: + case NL_WIFI_ST_APSTA_FALLBACK: + if (ev == NL_WIFI_EV_CONNECTED) { + sm->state = NL_WIFI_ST_STA_CONNECTED; + sm->retry_armed = false; + sm->backoff = NL_WIFI_BACKOFF_MIN_MS; + arm_ap_stop(sm, now + NL_WIFI_AP_CLOSE_FALLBACK_MS); + } else { + schedule_retry(sm, now); + } + break; + case NL_WIFI_ST_STA_CONNECTED: + if (ev != NL_WIFI_EV_CONNECTED) { + begin_connecting(sm, now); + schedule_retry(sm, now); + } + break; + case NL_WIFI_ST_ONBOARD_TEST: + if (ev == NL_WIFI_EV_CONNECTED) { + a.save_test_creds = true; + sm->have_creds = true; + sm->error = NL_WIFI_ERR_NONE; + sm->state = NL_WIFI_ST_STA_CONNECTED; + sm->backoff = NL_WIFI_BACKOFF_MIN_MS; + arm_ap_stop(sm, now + NL_WIFI_AP_CLOSE_ONBOARD_MS); + if (sm->pulsing) { + sm->pulsing = false; + a.pulse_off = true; + } + } else if (ev == NL_WIFI_EV_JOIN_FAILED) { + fail_test(sm, err == NL_WIFI_ERR_NONE ? NL_WIFI_ERR_TIMEOUT : err, now, &a); + } + break; + case NL_WIFI_ST_AP_ONBOARDING: + default: + break; + } + return a; +} + +nl_wifi_actions_t nl_wifi_sm_tick(nl_wifi_sm_t *sm, uint32_t now) +{ + nl_wifi_actions_t a = none(); + + if (sm->ap_stop_armed && due(now, sm->ap_stop_at)) { + sm->ap_stop_armed = false; + sm->ap_up = false; + a.stop_ap = true; + } + if (sm->state == NL_WIFI_ST_STA_CONNECTING && due(now, sm->since + NL_WIFI_FALLBACK_MS)) { + sm->state = NL_WIFI_ST_APSTA_FALLBACK; + if (!sm->ap_up) { + sm->ap_up = true; + a.start_ap = true; + } + } + if ((sm->state == NL_WIFI_ST_STA_CONNECTING || sm->state == NL_WIFI_ST_APSTA_FALLBACK) && sm->retry_armed && + due(now, sm->retry_at)) { + sm->retry_armed = false; + a.connect = true; + } + if (sm->state == NL_WIFI_ST_ONBOARD_TEST && due(now, sm->deadline)) + fail_test(sm, NL_WIFI_ERR_TIMEOUT, now, &a); + return a; +} + +nl_wifi_actions_t nl_wifi_sm_onboard(nl_wifi_sm_t *sm, uint32_t now) +{ + nl_wifi_actions_t a = none(); + + if (!sm->ap_up) { + sm->ap_up = true; + a.start_ap = true; + } + sm->state = NL_WIFI_ST_ONBOARD_TEST; + sm->error = NL_WIFI_ERR_NONE; + sm->deadline = now + NL_WIFI_ONBOARD_TIMEOUT_MS; + sm->retry_armed = false; + sm->ap_stop_armed = false; + a.connect = true; + a.use_test_creds = true; + return a; +} + +bool nl_wifi_sm_open_access(const nl_wifi_sm_t *sm) +{ + return sm->state == NL_WIFI_ST_AP_ONBOARDING || (sm->state == NL_WIFI_ST_ONBOARD_TEST && !sm->have_creds); +} + +const char *nl_wifi_sm_mode(const nl_wifi_sm_t *sm) +{ + if (sm->state == NL_WIFI_ST_AP_ONBOARDING) + return "ap"; + return sm->ap_up ? "apsta" : "sta"; +} + +const char *nl_wifi_error_name(nl_wifi_error_t e) +{ + switch (e) { + case NL_WIFI_ERR_AUTH: return "auth"; + case NL_WIFI_ERR_NOT_FOUND: return "not_found"; + case NL_WIFI_ERR_TIMEOUT: return "timeout"; + default: return NULL; + } +} diff --git a/src/net/nl_wifi_sm.h b/src/net/nl_wifi_sm.h new file mode 100644 index 0000000..ff6fe65 --- /dev/null +++ b/src/net/nl_wifi_sm.h @@ -0,0 +1,59 @@ +#ifndef NL_WIFI_SM_H +#define NL_WIFI_SM_H + +#include +#include + +#define NL_WIFI_FALLBACK_MS 300000u /* 5 min without connection -> AP+STA */ +#define NL_WIFI_ONBOARD_TIMEOUT_MS 30000u +#define NL_WIFI_AP_CLOSE_ONBOARD_MS 60000u +#define NL_WIFI_AP_CLOSE_FALLBACK_MS 10000u +#define NL_WIFI_BACKOFF_MIN_MS 1000u +#define NL_WIFI_BACKOFF_MAX_MS 60000u + +typedef enum { + NL_WIFI_ST_AP_ONBOARDING = 0, + NL_WIFI_ST_STA_CONNECTING, + NL_WIFI_ST_STA_CONNECTED, + NL_WIFI_ST_APSTA_FALLBACK, + NL_WIFI_ST_ONBOARD_TEST +} nl_wifi_state_t; + +typedef enum { NL_WIFI_ERR_NONE = 0, NL_WIFI_ERR_AUTH, NL_WIFI_ERR_NOT_FOUND, NL_WIFI_ERR_TIMEOUT } nl_wifi_error_t; +typedef enum { NL_WIFI_EV_CONNECTED = 0, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_EV_DISCONNECTED } nl_wifi_event_t; + +/* What the driver has to do after a state machine call. */ +typedef struct { + bool start_ap; + bool stop_ap; + bool connect; /* connect the station ... */ + bool use_test_creds; /* ... with the onboarding credentials instead of the stored ones */ + bool save_test_creds; /* onboarding succeeded: persist the onboarding credentials */ + bool pulse_on; + bool pulse_off; +} nl_wifi_actions_t; + +typedef struct { + nl_wifi_state_t state; + nl_wifi_error_t error; /* result of the last onboarding test */ + bool have_creds; + bool ap_up; + bool pulsing; + bool retry_armed; + bool ap_stop_armed; + uint32_t since; /* start of the current connecting phase */ + uint32_t retry_at; + uint32_t backoff; + uint32_t deadline; /* onboarding test */ + uint32_t ap_stop_at; +} nl_wifi_sm_t; + +nl_wifi_actions_t nl_wifi_sm_start(nl_wifi_sm_t *sm, bool have_creds, uint32_t now); +nl_wifi_actions_t nl_wifi_sm_event(nl_wifi_sm_t *sm, nl_wifi_event_t ev, nl_wifi_error_t err, uint32_t now); +nl_wifi_actions_t nl_wifi_sm_tick(nl_wifi_sm_t *sm, uint32_t now); +nl_wifi_actions_t nl_wifi_sm_onboard(nl_wifi_sm_t *sm, uint32_t now); +bool nl_wifi_sm_open_access(const nl_wifi_sm_t *sm); +const char *nl_wifi_sm_mode(const nl_wifi_sm_t *sm); +const char *nl_wifi_error_name(nl_wifi_error_t e); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 57924ba..a771b37 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -45,3 +45,4 @@ nl_test(test_http test_http.c ${NL_SRC}/net/nl_http.c) nl_test(test_api test_api.c fake_api_ctx.c ${NL_SRC}/net/nl_api.c ${NL_SRC}/board/nl_board.c ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/config/nl_settings.c ${NL_SRC}/util/nl_crc.c fake_flash.c) target_link_libraries(test_api PRIVATE nl_cjson) +nl_test(test_wifi_sm test_wifi_sm.c ${NL_SRC}/net/nl_wifi_sm.c) diff --git a/test/test_wifi_sm.c b/test/test_wifi_sm.c new file mode 100644 index 0000000..24ba7d8 --- /dev/null +++ b/test/test_wifi_sm.c @@ -0,0 +1,172 @@ +#include +#include "unity.h" +#include "net/nl_wifi_sm.h" + +static nl_wifi_sm_t sm; +static nl_wifi_actions_t a; + +void setUp(void) { memset(&sm, 0, sizeof sm); } +void tearDown(void) {} + +static void test_start_without_credentials_opens_ap(void) +{ + a = nl_wifi_sm_start(&sm, false, 0); + TEST_ASSERT_TRUE(a.start_ap); + TEST_ASSERT_TRUE(a.pulse_on); + TEST_ASSERT_FALSE(a.connect); + TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); + TEST_ASSERT_TRUE(nl_wifi_sm_open_access(&sm)); + TEST_ASSERT_EQUAL_STRING("ap", nl_wifi_sm_mode(&sm)); +} + +static void test_start_with_credentials_connects(void) +{ + a = nl_wifi_sm_start(&sm, true, 0); + TEST_ASSERT_TRUE(a.connect); + TEST_ASSERT_FALSE(a.use_test_creds); + TEST_ASSERT_FALSE(a.start_ap); + TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); + a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 3000); + TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTED, sm.state); + TEST_ASSERT_EQUAL_STRING("sta", nl_wifi_sm_mode(&sm)); +} + +static void test_retry_with_backoff(void) +{ + nl_wifi_sm_start(&sm, true, 0); + nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 1000); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 1999).connect); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 2000).connect); + nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 3000); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 4999).connect); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 5000).connect); /* 2 s */ + nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 6000); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 10000).connect); /* 4 s */ + for (int i = 0; i < 10; i++) + nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 20000); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 79999).connect); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 80000).connect); /* capped at 60 s */ +} + +static void test_fallback_after_five_minutes(void) +{ + nl_wifi_sm_start(&sm, true, 0); + nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_NOT_FOUND, 1000); + a = nl_wifi_sm_tick(&sm, NL_WIFI_FALLBACK_MS - 1); + TEST_ASSERT_FALSE(a.start_ap); + a = nl_wifi_sm_tick(&sm, NL_WIFI_FALLBACK_MS); + TEST_ASSERT_TRUE(a.start_ap); + TEST_ASSERT_FALSE(a.pulse_on); + TEST_ASSERT_EQUAL(NL_WIFI_ST_APSTA_FALLBACK, sm.state); + TEST_ASSERT_EQUAL_STRING("apsta", nl_wifi_sm_mode(&sm)); + TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); + /* retries continue */ + nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_NOT_FOUND, 400000); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 460000).connect); + /* reconnect closes the AP 10 s later */ + a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 500000); + TEST_ASSERT_FALSE(a.stop_ap); + TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTED, sm.state); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 509999).stop_ap); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 510000).stop_ap); + TEST_ASSERT_EQUAL_STRING("sta", nl_wifi_sm_mode(&sm)); +} + +static void test_disconnect_while_connected(void) +{ + nl_wifi_sm_start(&sm, true, 0); + nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 1000); + nl_wifi_sm_event(&sm, NL_WIFI_EV_DISCONNECTED, NL_WIFI_ERR_NONE, 100000); + TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTING, sm.state); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 101000).connect); + /* fallback timer restarts at the disconnect */ + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 100000 + NL_WIFI_FALLBACK_MS - 1).start_ap); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 100000 + NL_WIFI_FALLBACK_MS).start_ap); +} + +static void test_onboarding_success(void) +{ + nl_wifi_sm_start(&sm, false, 0); + a = nl_wifi_sm_onboard(&sm, 5000); + TEST_ASSERT_FALSE(a.start_ap); /* already up */ + TEST_ASSERT_TRUE(a.connect); + TEST_ASSERT_TRUE(a.use_test_creds); + TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); + TEST_ASSERT_TRUE(nl_wifi_sm_open_access(&sm)); + TEST_ASSERT_EQUAL_STRING("apsta", nl_wifi_sm_mode(&sm)); + TEST_ASSERT_FALSE(nl_wifi_sm_event(&sm, NL_WIFI_EV_DISCONNECTED, NL_WIFI_ERR_NONE, 6000).save_test_creds); + a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 9000); + TEST_ASSERT_TRUE(a.save_test_creds); + TEST_ASSERT_TRUE(a.pulse_off); + TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTED, sm.state); + TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 9000 + NL_WIFI_AP_CLOSE_ONBOARD_MS - 1).stop_ap); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 9000 + NL_WIFI_AP_CLOSE_ONBOARD_MS).stop_ap); +} + +static void test_onboarding_failure_without_credentials(void) +{ + nl_wifi_sm_start(&sm, false, 0); + nl_wifi_sm_onboard(&sm, 5000); + a = nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 8000); + TEST_ASSERT_FALSE(a.pulse_off); + TEST_ASSERT_FALSE(a.stop_ap); + TEST_ASSERT_FALSE(a.connect); + TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); + TEST_ASSERT_EQUAL(NL_WIFI_ERR_AUTH, sm.error); + TEST_ASSERT_EQUAL_STRING("auth", nl_wifi_error_name(sm.error)); + TEST_ASSERT_TRUE(nl_wifi_sm_open_access(&sm)); +} + +static void test_onboarding_timeout(void) +{ + nl_wifi_sm_start(&sm, false, 0); + nl_wifi_sm_onboard(&sm, 5000); + TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); + nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS - 1); + TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); + nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS); + TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); + TEST_ASSERT_EQUAL_STRING("timeout", nl_wifi_error_name(sm.error)); +} + +static void test_onboarding_while_connected_falls_back_to_old_credentials(void) +{ + nl_wifi_sm_start(&sm, true, 0); + nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 1000); + a = nl_wifi_sm_onboard(&sm, 2000); + TEST_ASSERT_TRUE(a.start_ap); + TEST_ASSERT_FALSE(a.pulse_on); + TEST_ASSERT_TRUE(a.use_test_creds); + TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); + a = nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_NOT_FOUND, 4000); + TEST_ASSERT_TRUE(a.connect); + TEST_ASSERT_FALSE(a.use_test_creds); + TEST_ASSERT_EQUAL(NL_WIFI_ST_APSTA_FALLBACK, sm.state); + TEST_ASSERT_EQUAL_STRING("not_found", nl_wifi_error_name(sm.error)); + a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 6000); + TEST_ASSERT_FALSE(a.save_test_creds); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 16000).stop_ap); +} + +static void test_error_names(void) +{ + TEST_ASSERT_NULL(nl_wifi_error_name(NL_WIFI_ERR_NONE)); + TEST_ASSERT_EQUAL_STRING("not_found", nl_wifi_error_name(NL_WIFI_ERR_NOT_FOUND)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_start_without_credentials_opens_ap); + RUN_TEST(test_start_with_credentials_connects); + RUN_TEST(test_retry_with_backoff); + RUN_TEST(test_fallback_after_five_minutes); + RUN_TEST(test_disconnect_while_connected); + RUN_TEST(test_onboarding_success); + RUN_TEST(test_onboarding_failure_without_credentials); + RUN_TEST(test_onboarding_timeout); + RUN_TEST(test_onboarding_while_connected_falls_back_to_old_credentials); + RUN_TEST(test_error_names); + return UNITY_END(); +} From 2f7cadaeee35552eea7acc7bd0e56fc34bd032f7 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 22:42:49 +0200 Subject: [PATCH 031/150] fix: Verbindungs-Timeout im WLAN-Zustandsautomaten --- src/net/nl_wifi_sm.c | 15 +++++++++++++++ src/net/nl_wifi_sm.h | 3 +++ test/test_wifi_sm.c | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/net/nl_wifi_sm.c b/src/net/nl_wifi_sm.c index dd5d38f..930831f 100644 --- a/src/net/nl_wifi_sm.c +++ b/src/net/nl_wifi_sm.c @@ -46,6 +46,8 @@ static void fail_test(nl_wifi_sm_t *sm, nl_wifi_error_t err, uint32_t now, nl_wi sm->backoff = NL_WIFI_BACKOFF_MIN_MS; sm->retry_armed = false; a->connect = true; /* old credentials */ + sm->awaiting = true; + sm->connect_at = now; } else { sm->state = NL_WIFI_ST_AP_ONBOARDING; } @@ -60,6 +62,8 @@ nl_wifi_actions_t nl_wifi_sm_start(nl_wifi_sm_t *sm, bool have_creds, uint32_t n if (have_creds) { begin_connecting(sm, now); a.connect = true; + sm->awaiting = true; + sm->connect_at = now; } else { sm->state = NL_WIFI_ST_AP_ONBOARDING; sm->ap_up = true; @@ -77,6 +81,7 @@ nl_wifi_actions_t nl_wifi_sm_event(nl_wifi_sm_t *sm, nl_wifi_event_t ev, nl_wifi switch (sm->state) { case NL_WIFI_ST_STA_CONNECTING: case NL_WIFI_ST_APSTA_FALLBACK: + sm->awaiting = false; if (ev == NL_WIFI_EV_CONNECTED) { sm->state = NL_WIFI_ST_STA_CONNECTED; sm->retry_armed = false; @@ -131,10 +136,20 @@ nl_wifi_actions_t nl_wifi_sm_tick(nl_wifi_sm_t *sm, uint32_t now) a.start_ap = true; } } + if ((sm->state == NL_WIFI_ST_STA_CONNECTING || sm->state == NL_WIFI_ST_APSTA_FALLBACK) && sm->awaiting && !sm->retry_armed && + due(now, sm->connect_at + NL_WIFI_CONNECT_TIMEOUT_MS)) { + sm->awaiting = false; + uint32_t timeout_at = sm->connect_at + NL_WIFI_CONNECT_TIMEOUT_MS; + sm->retry_armed = true; + sm->retry_at = timeout_at + sm->backoff; + sm->backoff = sm->backoff * 2u > NL_WIFI_BACKOFF_MAX_MS ? NL_WIFI_BACKOFF_MAX_MS : sm->backoff * 2u; + } if ((sm->state == NL_WIFI_ST_STA_CONNECTING || sm->state == NL_WIFI_ST_APSTA_FALLBACK) && sm->retry_armed && due(now, sm->retry_at)) { sm->retry_armed = false; a.connect = true; + sm->awaiting = true; + sm->connect_at = now; } if (sm->state == NL_WIFI_ST_ONBOARD_TEST && due(now, sm->deadline)) fail_test(sm, NL_WIFI_ERR_TIMEOUT, now, &a); diff --git a/src/net/nl_wifi_sm.h b/src/net/nl_wifi_sm.h index ff6fe65..478d16e 100644 --- a/src/net/nl_wifi_sm.h +++ b/src/net/nl_wifi_sm.h @@ -10,6 +10,7 @@ #define NL_WIFI_AP_CLOSE_FALLBACK_MS 10000u #define NL_WIFI_BACKOFF_MIN_MS 1000u #define NL_WIFI_BACKOFF_MAX_MS 60000u +#define NL_WIFI_CONNECT_TIMEOUT_MS 30000u /* no event after a connect -> treat as failed */ typedef enum { NL_WIFI_ST_AP_ONBOARDING = 0, @@ -41,11 +42,13 @@ typedef struct { bool pulsing; bool retry_armed; bool ap_stop_armed; + bool awaiting; /* waiting for response to a connect */ uint32_t since; /* start of the current connecting phase */ uint32_t retry_at; uint32_t backoff; uint32_t deadline; /* onboarding test */ uint32_t ap_stop_at; + uint32_t connect_at; /* when the pending connect was issued */ } nl_wifi_sm_t; nl_wifi_actions_t nl_wifi_sm_start(nl_wifi_sm_t *sm, bool have_creds, uint32_t now); diff --git a/test/test_wifi_sm.c b/test/test_wifi_sm.c index 24ba7d8..b306afd 100644 --- a/test/test_wifi_sm.c +++ b/test/test_wifi_sm.c @@ -155,6 +155,25 @@ static void test_error_names(void) TEST_ASSERT_EQUAL_STRING("not_found", nl_wifi_error_name(NL_WIFI_ERR_NOT_FOUND)); } +static void test_connect_without_any_event_retries(void) +{ + nl_wifi_sm_start(&sm, true, 0); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 29999).connect); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 30000).connect); /* retry scheduled at 31000 */ + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 31000).connect); + /* next timeout: 31000 + 30000 + 2000 backoff = 63000 */ + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 62999).connect); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 63000).connect); + /* fallback after 300000 ms, and attempts continue */ + a = nl_wifi_sm_tick(&sm, 300000); + TEST_ASSERT_TRUE(a.start_ap); + TEST_ASSERT_EQUAL(NL_WIFI_ST_APSTA_FALLBACK, sm.state); + TEST_ASSERT_EQUAL_STRING("apsta", nl_wifi_sm_mode(&sm)); + /* another attempt fires later */ + a = nl_wifi_sm_tick(&sm, 360000); + TEST_ASSERT_TRUE(a.connect); +} + int main(void) { UNITY_BEGIN(); @@ -168,5 +187,6 @@ int main(void) RUN_TEST(test_onboarding_timeout); RUN_TEST(test_onboarding_while_connected_falls_back_to_old_credentials); RUN_TEST(test_error_names); + RUN_TEST(test_connect_without_any_event_retries); return UNITY_END(); } From 4c2f9e537485c3f0ae2bd533fcdb30ac0355c6a9 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 22:50:43 +0200 Subject: [PATCH 032/150] feat: WLAN-Treiber mit Onboarding-AP, Build-Credentials und Befehl net --- CMakeLists.txt | 26 +++- config.local.cmake.example | 2 +- src/app_main.c | 26 ++++ src/net/nl_mdns.c | 5 + src/net/nl_mdns.h | 9 ++ src/net/nl_wifi.h | 29 +++++ src/net/nl_wifi_w600.c | 250 +++++++++++++++++++++++++++++++++++++ src/sys/nl_app.h | 3 + src/sys/nl_console.c | 8 ++ src/sys/nl_console_cmd.c | 4 +- src/sys/nl_console_cmd.h | 1 + test/test_console_cmd.c | 2 + 12 files changed, 361 insertions(+), 4 deletions(-) create mode 100644 src/net/nl_mdns.c create mode 100644 src/net/nl_mdns.h create mode 100644 src/net/nl_wifi.h create mode 100644 src/net/nl_wifi_w600.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 64c799e..26e4fe7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,25 @@ if(NOT DEFINED NINLIGHT_TOKEN) set(NINLIGHT_TOKEN "aabbccddeeff0011223344") endif() +if(NOT DEFINED NINLIGHT_WIFI_SSID) + set(NINLIGHT_WIFI_SSID "") +endif() +if(NOT DEFINED NINLIGHT_WIFI_PASSWORD) + set(NINLIGHT_WIFI_PASSWORD "") +endif() +if(NOT DEFINED NINLIGHT_AP_PASSWORD) + set(NINLIGHT_AP_PASSWORD "ninlight") +endif() +string(LENGTH "${NINLIGHT_AP_PASSWORD}" _nl_ap_len) +if(_nl_ap_len LESS 8) + message(FATAL_ERROR "NINLIGHT_AP_PASSWORD needs at least 8 characters (WPA2)") +endif() +foreach(_v NINLIGHT_WIFI_SSID NINLIGHT_WIFI_PASSWORD NINLIGHT_AP_PASSWORD NINLIGHT_TOKEN) + if("${${_v}}" MATCHES "[\"\\\\]") + message(FATAL_ERROR "${_v} must not contain \" or \\") + endif() +endforeach() + include(cmake/version.cmake) include(cmake/sdk.cmake) include(cmake/image.cmake) @@ -48,6 +67,8 @@ set(NL_FW_SOURCES src/net/nl_http.c src/net/nl_api.c src/net/nl_wifi_sm.c + src/net/nl_wifi_w600.c + src/net/nl_mdns.c third_party/cJSON/cJSON.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) @@ -57,7 +78,10 @@ target_compile_definitions(${NL_FW} PRIVATE NINLIGHT_DEVICE_ID="${NINLIGHT_DEVICE}" NINLIGHT_FW_VERSION="${NINLIGHT_FW_VERSION}" NINLIGHT_BUILD_DATE="${NINLIGHT_BUILD_DATE}" - NINLIGHT_TOKEN_DEFAULT="${NINLIGHT_TOKEN}") + NINLIGHT_TOKEN_DEFAULT="${NINLIGHT_TOKEN}" + NINLIGHT_WIFI_SSID="${NINLIGHT_WIFI_SSID}" + NINLIGHT_WIFI_PASSWORD="${NINLIGHT_WIFI_PASSWORD}" + NINLIGHT_AP_PASSWORD="${NINLIGHT_AP_PASSWORD}") target_link_options(${NL_FW} PRIVATE -static -nostartfiles -T${SDK_DIR}/ld/link_w600_2m.ld -Wl,-Map=${CMAKE_BINARY_DIR}/${NL_FW}.map) diff --git a/config.local.cmake.example b/config.local.cmake.example index 3265eb4..f16aa0e 100644 --- a/config.local.cmake.example +++ b/config.local.cmake.example @@ -1,6 +1,6 @@ # Copy to config.local.cmake (gitignored) and adjust. # set(NINLIGHT_TOKEN "change-me-please") -# Used from plan 2 on: +# WLAN credentials compiled into the image (stored settings win); AP password >= 8 chars # set(NINLIGHT_WIFI_SSID "MyWifi") # set(NINLIGHT_WIFI_PASSWORD "secret") # set(NINLIGHT_AP_PASSWORD "ninlight") diff --git a/src/app_main.c b/src/app_main.c index bf1f023..2c2337a 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -1,4 +1,5 @@ #include +#include #include "wm_include.h" #include "wm_watchdog.h" @@ -13,6 +14,8 @@ #include "light/nl_boot_state.h" #include "light/nl_control.h" #include "light/nl_engine.h" +#include "net/nl_mdns.h" +#include "net/nl_wifi.h" #include "sys/nl_app.h" #include "sys/nl_console.h" @@ -79,6 +82,17 @@ void nl_app_reboot(void) void nl_app_request_factory_reset(void) { s_factory_reset_requested = true; } +int nl_app_set_wifi_credentials(const char *ssid, const char *pass) +{ + nl_app_lock(); + strncpy(s_settings.wifi_ssid, ssid, NL_SSID_LEN - 1); + s_settings.wifi_ssid[NL_SSID_LEN - 1] = '\0'; + strncpy(s_settings.wifi_pass, pass, NL_PASS_LEN - 1); + s_settings.wifi_pass[NL_PASS_LEN - 1] = '\0'; + nl_app_unlock(); + return nl_app_save_settings(); +} + void nl_app_factory_reset(void) { printf("factory reset\r\n"); @@ -160,6 +174,18 @@ static void app_task(void *arg) tls_watchdog_init(20u * 1000u * 1000u); /* 20 s */ nl_console_start(); + { + static nl_settings_t boot; /* copy: the network task may change s_settings later */ + nl_app_lock(); + boot = s_settings; + nl_app_unlock(); + nl_mdns_set_name(boot.name); + if (boot.wifi_ssid[0]) + nl_wifi_start(boot.wifi_ssid, boot.wifi_pass, NINLIGHT_AP_PASSWORD); + else + nl_wifi_start(NINLIGHT_WIFI_SSID, NINLIGHT_WIFI_PASSWORD, NINLIGHT_AP_PASSWORD); + } + boot_ms = nl_app_now_ms(); beat_seen = s_light_beat; for (;;) { diff --git a/src/net/nl_mdns.c b/src/net/nl_mdns.c new file mode 100644 index 0000000..e4e2004 --- /dev/null +++ b/src/net/nl_mdns.c @@ -0,0 +1,5 @@ +#include "net/nl_mdns.h" + +/* Placeholder until the mDNS responder lands (plan 2, task 7). */ +void nl_mdns_on_ip_up(void) {} +void nl_mdns_set_name(const char *name) { (void)name; } diff --git a/src/net/nl_mdns.h b/src/net/nl_mdns.h new file mode 100644 index 0000000..7380eac --- /dev/null +++ b/src/net/nl_mdns.h @@ -0,0 +1,9 @@ +#ifndef NL_MDNS_H +#define NL_MDNS_H + +/* Called by the network task once the station got an IP (again). */ +void nl_mdns_on_ip_up(void); +/* Device name for the TXT record; re-announces when mDNS is running. */ +void nl_mdns_set_name(const char *name); + +#endif diff --git a/src/net/nl_wifi.h b/src/net/nl_wifi.h new file mode 100644 index 0000000..e64a30d --- /dev/null +++ b/src/net/nl_wifi.h @@ -0,0 +1,29 @@ +#ifndef NL_WIFI_H +#define NL_WIFI_H + +#include +#include + +#include "config/nl_settings.h" + +typedef struct { + const char *mode; /* "sta" | "ap" | "apsta" */ + char ssid[NL_SSID_LEN]; /* configured station SSID */ + int rssi; /* dBm, 0 when not connected */ + char ip[16]; /* station IP, else the AP IP when the AP is up, else "" */ + char sta_ip[16]; /* "" when the station has no IP */ + const char *error; /* result of the last onboarding test or NULL */ +} nl_wifi_status_t; + +/* Starts the network task. An empty ssid starts the onboarding AP. Strings are copied. */ +void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass); +/* Starts an onboarding test with new credentials (from the HTTP task). 0 = queued. */ +int nl_wifi_request_onboard(const char *ssid, const char *pass); +/* True while the unconfigured onboarding AP is open (API without token). */ +bool nl_wifi_open_access(void); +void nl_wifi_status(nl_wifi_status_t *out); +/* "ninlight-", valid after nl_wifi_start(). */ +const char *nl_wifi_hostname(void); +void nl_wifi_mac(uint8_t mac[6]); + +#endif diff --git a/src/net/nl_wifi_w600.c b/src/net/nl_wifi_w600.c new file mode 100644 index 0000000..4f0ac7d --- /dev/null +++ b/src/net/nl_wifi_w600.c @@ -0,0 +1,250 @@ +#include +#include + +#include "wm_include.h" +#include "wm_efuse.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" /* struct netif is only forward-declared by wm_netif2.0.3.h */ + +#include "light/nl_engine.h" +#include "net/nl_mdns.h" +#include "net/nl_names.h" +#include "net/nl_wifi.h" +#include "net/nl_wifi_sm.h" +#include "sys/nl_app.h" + +#define NL_NET_PRIO 35 +#define NL_NET_QUEUE 8 +#define MSG_NETIF 0x100u /* | netif status */ +#define MSG_ONBOARD 0x200u + +static u32 s_stack[768]; +static tls_os_queue_t *s_queue; +static tls_os_mutex_t *s_lock; /* guards s_sm and the credential buffers */ +static nl_wifi_sm_t s_sm; +static bool s_started; +static uint8_t s_mac[6]; +static char s_hostname[24]; +static char s_ssid[NL_SSID_LEN], s_pass[NL_PASS_LEN]; +static char s_test_ssid[NL_SSID_LEN], s_test_pass[NL_PASS_LEN]; +static char s_ap_pass[NL_PASS_LEN]; + +static void lock(void) { tls_os_mutex_acquire(s_lock, 0); } +static void unlock(void) { tls_os_mutex_release(s_lock); } + +static void copy(char *dst, const char *src, size_t size) +{ + strncpy(dst, src, size - 1); + dst[size - 1] = '\0'; +} + +/* SDK system task context: only hand the event over. */ +static void netif_event(u8 status) +{ + tls_os_queue_send(s_queue, (void *)(MSG_NETIF | status), 0); +} + +static void start_ap(void) +{ + struct tls_softap_info_t ap; + struct tls_ip_info_t ip; + size_t n = strlen(s_ap_pass); + int r; + + memset(&ap, 0, sizeof ap); + memset(&ip, 0, sizeof ip); + copy((char *)ap.ssid, s_hostname, sizeof ap.ssid); + ap.encrypt = IEEE80211_ENCRYT_CCMP_WPA2; + ap.channel = 6; + ap.keyinfo.format = 1; /* ascii */ + ap.keyinfo.index = 1; + ap.keyinfo.key_len = (u8)n; + memcpy(ap.keyinfo.key, s_ap_pass, n); + ip.ip_addr[0] = 192; ip.ip_addr[1] = 168; ip.ip_addr[2] = 4; ip.ip_addr[3] = 1; + ip.netmask[0] = 255; ip.netmask[1] = 255; ip.netmask[2] = 255; ip.netmask[3] = 0; + memcpy(ip.dnsname, "local", 6); + r = tls_wifi_softap_create(&ap, &ip); + printf("wifi: AP %s %s\r\n", s_hostname, r == WM_SUCCESS ? "up" : "failed"); +} + +static void connect_station(bool test) +{ + char ssid[NL_SSID_LEN], pass[NL_PASS_LEN]; + + lock(); + copy(ssid, test ? s_test_ssid : s_ssid, sizeof ssid); + copy(pass, test ? s_test_pass : s_pass, sizeof pass); + unlock(); + printf("wifi: connecting to %s\r\n", ssid); + tls_wifi_connect((u8 *)ssid, (u8)strlen(ssid), (u8 *)pass, (u8)strlen(pass)); +} + +static void pulse(bool on) +{ + nl_app_lock(); + nl_engine_pulse(on); + nl_app_unlock(); +} + +static void execute(nl_wifi_actions_t a) +{ + if (a.save_test_creds) { + char ssid[NL_SSID_LEN], pass[NL_PASS_LEN]; + lock(); + copy(s_ssid, s_test_ssid, sizeof s_ssid); + copy(s_pass, s_test_pass, sizeof s_pass); + copy(ssid, s_ssid, sizeof ssid); + copy(pass, s_pass, sizeof pass); + unlock(); + printf("wifi: onboarding ok, saving credentials for %s\r\n", ssid); + nl_app_set_wifi_credentials(ssid, pass); + } + if (a.pulse_on) + pulse(true); + if (a.pulse_off) + pulse(false); + if (a.start_ap) + start_ap(); + if (a.stop_ap) { + printf("wifi: AP down\r\n"); + tls_wifi_softap_destroy(); + } + if (a.connect) + connect_station(a.use_test_creds); +} + +static nl_wifi_error_t join_error(void) +{ + switch (tls_wifi_get_errno()) { + case WM_WIFI_EKEY: return NL_WIFI_ERR_AUTH; + case WM_WIFI_ENOAP: return NL_WIFI_ERR_NOT_FOUND; + default: return NL_WIFI_ERR_TIMEOUT; + } +} + +static void net_task(void *arg) +{ + (void)arg; + for (;;) { + void *msg = NULL; + nl_wifi_actions_t ev, tick; + uint32_t m = 0, now; + bool ip_up = false; + + memset(&ev, 0, sizeof ev); + if (tls_os_queue_receive(s_queue, &msg, 0, HZ / 10) == TLS_OS_SUCCESS) + m = (uint32_t)msg; + now = nl_app_now_ms(); + + lock(); + if (m == (MSG_NETIF | NETIF_IP_NET_UP)) { + ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, now); + ip_up = true; + } else if (m == (MSG_NETIF | NETIF_WIFI_JOIN_FAILED)) { + ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_JOIN_FAILED, join_error(), now); + } else if (m == (MSG_NETIF | NETIF_WIFI_DISCONNECTED)) { + ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_DISCONNECTED, NL_WIFI_ERR_NONE, now); + } else if (m == MSG_ONBOARD) { + ev = nl_wifi_sm_onboard(&s_sm, now); + } + tick = nl_wifi_sm_tick(&s_sm, now); + unlock(); + + if (m & MSG_NETIF) + printf("wifi: netif event %u\r\n", (unsigned)(m & 0xFFu)); + execute(ev); + execute(tick); + if (ip_up) + nl_mdns_on_ip_up(); + } +} + +void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass) +{ + u8 auto_mode = WIFI_AUTO_CNT_OFF; + nl_wifi_actions_t a; + struct netif *nif; + + tls_get_mac_addr(s_mac); + nl_names_hostname(s_mac, s_hostname, sizeof s_hostname); + copy(s_ssid, ssid, sizeof s_ssid); + copy(s_pass, pass, sizeof s_pass); + copy(s_ap_pass, ap_pass, sizeof s_ap_pass); + + /* The SDK would otherwise reconnect on its own with credentials from its parameter area. */ + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_mode); + if (auto_mode != WIFI_AUTO_CNT_OFF) { + auto_mode = WIFI_AUTO_CNT_OFF; + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_mode); + } + tls_wifi_disconnect(); + nif = tls_get_netif(); + if (nif) + nif->hostname = s_hostname; /* DHCP host name */ + + tls_os_mutex_create(0, &s_lock); + tls_os_queue_create(&s_queue, NL_NET_QUEUE); + tls_netif_add_status_event(netif_event); + + lock(); + a = nl_wifi_sm_start(&s_sm, s_ssid[0] != '\0', nl_app_now_ms()); + unlock(); + s_started = true; + printf("wifi: %s, hostname %s\r\n", s_ssid[0] ? "station" : "onboarding AP", s_hostname); + execute(a); + tls_os_task_create(NULL, "nl_net", net_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_NET_PRIO, 0); +} + +int nl_wifi_request_onboard(const char *ssid, const char *pass) +{ + if (!s_started) + return -1; + lock(); + copy(s_test_ssid, ssid, sizeof s_test_ssid); + copy(s_test_pass, pass, sizeof s_test_pass); + unlock(); + return tls_os_queue_send(s_queue, (void *)MSG_ONBOARD, 0) == TLS_OS_SUCCESS ? 0 : -1; +} + +bool nl_wifi_open_access(void) +{ + bool r; + if (!s_started) + return false; + lock(); + r = nl_wifi_sm_open_access(&s_sm); + unlock(); + return r; +} + +void nl_wifi_status(nl_wifi_status_t *out) +{ + struct tls_ethif *e = tls_netif_get_ethif(); + bool ap_up = false; + + memset(out, 0, sizeof *out); + out->mode = "sta"; + if (s_started) { + lock(); + out->mode = nl_wifi_sm_mode(&s_sm); + out->error = nl_wifi_error_name(s_sm.error); + copy(out->ssid, s_ssid, sizeof out->ssid); + ap_up = s_sm.ap_up || s_sm.state == NL_WIFI_ST_AP_ONBOARDING; + unlock(); + } + if (e && e->status) { + struct tls_curr_bss_t bss; + ip4addr_ntoa_r(ip_2_ip4(&e->ip_addr), out->sta_ip, sizeof out->sta_ip); + memset(&bss, 0, sizeof bss); + tls_wifi_get_current_bss(&bss); + out->rssi = (int)(int8_t)bss.rssi; + } + if (out->sta_ip[0]) + copy(out->ip, out->sta_ip, sizeof out->ip); + else if (ap_up) + copy(out->ip, "192.168.4.1", sizeof out->ip); +} + +const char *nl_wifi_hostname(void) { return s_hostname; } + +void nl_wifi_mac(uint8_t mac[6]) { memcpy(mac, s_mac, 6); } diff --git a/src/sys/nl_app.h b/src/sys/nl_app.h index c9e18a9..0f454af 100644 --- a/src/sys/nl_app.h +++ b/src/sys/nl_app.h @@ -25,4 +25,7 @@ void nl_app_factory_reset(void); * function instead of calling nl_app_factory_reset() directly from another task. */ void nl_app_request_factory_reset(void); +/* Stores new station credentials (from a successful onboarding test) and persists them. */ +int nl_app_set_wifi_credentials(const char *ssid, const char *pass); + #endif diff --git a/src/sys/nl_console.c b/src/sys/nl_console.c index 2dc074e..8317b4f 100644 --- a/src/sys/nl_console.c +++ b/src/sys/nl_console.c @@ -6,6 +6,7 @@ #include "light/nl_control.h" #include "light/nl_engine.h" +#include "net/nl_wifi.h" #include "sys/nl_app.h" #include "sys/nl_console.h" #include "sys/nl_console_cmd.h" @@ -101,6 +102,13 @@ static void exec(const nl_cmd_t *c) case NL_CMD_REBOOT: nl_app_reboot(); return; + case NL_CMD_NET: { + nl_wifi_status_t w; + nl_wifi_status(&w); + printf("net: mode=%s ssid=%s ip=%s sta_ip=%s rssi=%d error=%s host=%s\r\n", w.mode, w.ssid, w.ip, + w.sta_ip[0] ? w.sta_ip : "-", w.rssi, w.error ? w.error : "-", nl_wifi_hostname()); + return; + } case NL_CMD_WDTEST: printf("wdtest: holding app lock forever\r\n"); nl_app_lock(); /* deliberately never released: light_task and app_task stall on it, diff --git a/src/sys/nl_console_cmd.c b/src/sys/nl_console_cmd.c index 42106fb..fcee502 100644 --- a/src/sys/nl_console_cmd.c +++ b/src/sys/nl_console_cmd.c @@ -9,7 +9,7 @@ const char nl_console_help[] = "on [ms] | off [ms] | bri <1-100> [ms] | rgb [ms] | ct [ms]\r\n" "pob last|on|off | fade | blink [n] | pulse on|off\r\n" - "state | factory | reboot | wdtest | help\r\n"; + "state | net | factory | reboot | wdtest | help\r\n"; static bool num(const char *s, int32_t lo, int32_t hi, int32_t *out) { @@ -105,7 +105,7 @@ nl_cmd_t nl_console_parse(const char *line) } else { static const struct { const char *name; nl_cmd_type_t type; } SIMPLE[] = { {"state", NL_CMD_STATE}, {"factory", NL_CMD_FACTORY}, {"reboot", NL_CMD_REBOOT}, - {"help", NL_CMD_HELP}, {"wdtest", NL_CMD_WDTEST}}; + {"help", NL_CMD_HELP}, {"wdtest", NL_CMD_WDTEST}, {"net", NL_CMD_NET}}; ok = false; for (size_t i = 0; i < sizeof SIMPLE / sizeof SIMPLE[0]; i++) if (!strcmp(tok[0], SIMPLE[i].name)) { diff --git a/src/sys/nl_console_cmd.h b/src/sys/nl_console_cmd.h index 8f00bf3..0684d1c 100644 --- a/src/sys/nl_console_cmd.h +++ b/src/sys/nl_console_cmd.h @@ -19,6 +19,7 @@ typedef enum { NL_CMD_REBOOT, NL_CMD_HELP, NL_CMD_WDTEST, + NL_CMD_NET, NL_CMD_ERROR } nl_cmd_type_t; diff --git a/test/test_console_cmd.c b/test/test_console_cmd.c index aafa921..d744ab8 100644 --- a/test/test_console_cmd.c +++ b/test/test_console_cmd.c @@ -82,8 +82,10 @@ static void test_simple_commands(void) TEST_ASSERT_EQUAL(NL_CMD_REBOOT, nl_console_parse("reboot").type); TEST_ASSERT_EQUAL(NL_CMD_HELP, nl_console_parse("help").type); TEST_ASSERT_EQUAL(NL_CMD_WDTEST, nl_console_parse("wdtest").type); + TEST_ASSERT_EQUAL(NL_CMD_NET, nl_console_parse("net").type); TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("state now").type); TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("wdtest now").type); + TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("net now").type); } int main(void) From 15f709594b529a1ea12bc4db8162a84653113005 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 22:59:34 +0200 Subject: [PATCH 033/150] feat: HTTP-Server auf Port 3333 mit App-API-Kontext --- CMakeLists.txt | 2 + src/app_main.c | 4 ++ src/net/nl_http_server.c | 104 +++++++++++++++++++++++++++++++ src/net/nl_http_server.h | 11 ++++ src/sys/nl_app_api.c | 131 +++++++++++++++++++++++++++++++++++++++ src/sys/nl_app_api.h | 9 +++ 6 files changed, 261 insertions(+) create mode 100644 src/net/nl_http_server.c create mode 100644 src/net/nl_http_server.h create mode 100644 src/sys/nl_app_api.c create mode 100644 src/sys/nl_app_api.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 26e4fe7..23b4194 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,8 @@ set(NL_FW_SOURCES src/net/nl_wifi_sm.c src/net/nl_wifi_w600.c src/net/nl_mdns.c + src/net/nl_http_server.c + src/sys/nl_app_api.c third_party/cJSON/cJSON.c) set(NL_FW ninlight_${NINLIGHT_DEVICE}) diff --git a/src/app_main.c b/src/app_main.c index 2c2337a..b92ef87 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -14,9 +14,11 @@ #include "light/nl_boot_state.h" #include "light/nl_control.h" #include "light/nl_engine.h" +#include "net/nl_http_server.h" #include "net/nl_mdns.h" #include "net/nl_wifi.h" #include "sys/nl_app.h" +#include "sys/nl_app_api.h" #include "sys/nl_console.h" #define NL_LIGHT_PRIO 30 @@ -186,6 +188,8 @@ static void app_task(void *arg) nl_wifi_start(NINLIGHT_WIFI_SSID, NINLIGHT_WIFI_PASSWORD, NINLIGHT_AP_PASSWORD); } + nl_http_server_start(nl_app_api_ctx()); + boot_ms = nl_app_now_ms(); beat_seen = s_light_beat; for (;;) { diff --git a/src/net/nl_http_server.c b/src/net/nl_http_server.c new file mode 100644 index 0000000..084fcf0 --- /dev/null +++ b/src/net/nl_http_server.c @@ -0,0 +1,104 @@ +#include + +#include "wm_include.h" +#include "wm_sockets.h" + +#include "net/nl_http.h" +#include "net/nl_http_server.h" +#include "sys/nl_app.h" + +#define NL_HTTP_PRIO 42 + +static u32 s_stack[1536]; /* 6 KB: cJSON + API handler */ +static char s_buf[NL_HTTP_MAX_REQUEST]; +static char s_out[NL_API_BODY_MAX + 160]; +static nl_api_response_t s_resp; +static const nl_api_ctx_t *s_ctx; + +static void send_all(int fd, const char *p, size_t n) +{ + while (n) { + int w = send(fd, p, n, 0); + if (w <= 0) + return; + p += w; + n -= (size_t)w; + } +} + +static void simple_error(int status, const char *msg) +{ + s_resp.status = status; + s_resp.after = NL_API_AFTER_NONE; + strcpy(s_resp.body, msg); +} + +static void serve(int fd) +{ + struct timeval tv; + nl_http_request_t req; + nl_http_parse_t pr = NL_HTTP_INCOMPLETE; + size_t len = 0, out; + + tv.tv_sec = 5; + tv.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + while (pr == NL_HTTP_INCOMPLETE && len < sizeof s_buf) { + int n = recv(fd, s_buf + len, sizeof s_buf - len, 0); + if (n <= 0) + return; /* timeout or closed: drop silently */ + len += (size_t)n; + pr = nl_http_parse(s_buf, len, &req); + } + if (pr == NL_HTTP_COMPLETE) + nl_api_handle(s_ctx, &req, &s_resp); + else if (pr == NL_HTTP_BAD_REQUEST) + simple_error(400, "{\"error\":\"bad request\"}"); + else + simple_error(413, "{\"error\":\"request too large\"}"); + out = nl_http_response(s_out, sizeof s_out, s_resp.status, s_resp.body); + if (out) + send_all(fd, s_out, out); +} + +static void server_task(void *arg) +{ + (void)arg; + for (;;) { + struct sockaddr_in addr; + int ls = socket(AF_INET, SOCK_STREAM, 0); + + memset(&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(NL_HTTP_PORT); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if (ls < 0 || bind(ls, (struct sockaddr *)&addr, sizeof addr) != 0 || listen(ls, 4) != 0) { + if (ls >= 0) + closesocket(ls); + tls_os_time_delay(HZ); + continue; + } + for (;;) { + struct sockaddr_in peer; + socklen_t plen = sizeof peer; + int fd = accept(ls, (struct sockaddr *)&peer, &plen); + if (fd < 0) { + tls_os_time_delay(HZ / 10); + continue; + } + s_resp.after = NL_API_AFTER_NONE; + serve(fd); + closesocket(fd); + if (s_resp.after == NL_API_AFTER_REBOOT) { + tls_os_time_delay(HZ / 2); /* let the TCP stack flush the response */ + nl_app_reboot(); + } + } + } +} + +void nl_http_server_start(const nl_api_ctx_t *ctx) +{ + s_ctx = ctx; + tls_os_task_create(NULL, "nl_http", server_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_HTTP_PRIO, 0); +} diff --git a/src/net/nl_http_server.h b/src/net/nl_http_server.h new file mode 100644 index 0000000..326e3dc --- /dev/null +++ b/src/net/nl_http_server.h @@ -0,0 +1,11 @@ +#ifndef NL_HTTP_SERVER_H +#define NL_HTTP_SERVER_H + +#include "net/nl_api.h" + +#define NL_HTTP_PORT 3333 + +/* Serves one connection at a time (listen backlog 4, 5 s receive timeout). */ +void nl_http_server_start(const nl_api_ctx_t *ctx); + +#endif diff --git a/src/sys/nl_app_api.c b/src/sys/nl_app_api.c new file mode 100644 index 0000000..08cc389 --- /dev/null +++ b/src/sys/nl_app_api.c @@ -0,0 +1,131 @@ +#include + +#include "wm_include.h" +#include "wm_mem.h" + +#include "cJSON.h" +#include "light/nl_control.h" +#include "light/nl_engine.h" +#include "net/nl_mdns.h" +#include "net/nl_names.h" +#include "net/nl_wifi.h" +#include "sys/nl_app.h" +#include "sys/nl_app_api.h" + +static void *json_alloc(size_t n) { return tls_mem_alloc(n); } +static void json_free(void *p) { tls_mem_free(p); } + +static void get_state(nl_light_state_t *out) +{ + nl_app_lock(); + nl_ctrl_get(out); + nl_app_unlock(); +} + +static nl_ctrl_err_t apply(const nl_light_change_t *c) +{ + nl_ctrl_err_t e; + nl_app_lock(); + e = nl_ctrl_apply(c, nl_app_now_ms()); + nl_app_unlock(); + return e; +} + +static void get_settings(nl_settings_t *out) +{ + nl_app_lock(); + *out = *nl_app_settings(); + nl_app_unlock(); +} + +/* The API never changes Wi-Fi credentials through here (that goes through the onboarding test), + * so the current ones are kept even if the network task stored new ones meanwhile. */ +static int save_settings(const nl_settings_t *s) +{ + nl_settings_t *cur; + char name[NL_NAME_LEN]; + bool name_changed; + + nl_app_lock(); + cur = nl_app_settings(); + name_changed = strcmp(cur->name, s->name) != 0; + { + nl_settings_t merged = *s; + memcpy(merged.wifi_ssid, cur->wifi_ssid, sizeof merged.wifi_ssid); + memcpy(merged.wifi_pass, cur->wifi_pass, sizeof merged.wifi_pass); + *cur = merged; + } + nl_ctrl_set_fades(cur->fade_in_ms, cur->fade_out_ms); + memcpy(name, cur->name, sizeof name); + nl_app_unlock(); + if (name_changed) + nl_mdns_set_name(name); + return nl_app_save_settings(); +} + +static int wifi_configure(const char *ssid, const char *pass) { return nl_wifi_request_onboard(ssid, pass); } +static bool open_access(void) { return nl_wifi_open_access(); } + +static void get_info(nl_sys_info_t *out) +{ + nl_wifi_status_t w; + uint8_t mac[6]; + + memset(out, 0, sizeof *out); + nl_wifi_mac(mac); + nl_names_mac_str(mac, out->mac); + nl_app_lock(); + memcpy(out->name, nl_app_settings()->name, sizeof out->name); + nl_app_unlock(); + nl_wifi_status(&w); + out->wifi_mode = w.mode; + memcpy(out->ssid, w.ssid, sizeof out->ssid); + out->rssi = w.rssi; + memcpy(out->ip, w.ip, sizeof out->ip); + memcpy(out->sta_ip, w.sta_ip, sizeof out->sta_ip); + out->wifi_error = w.error; + out->uptime_s = nl_app_now_ms() / 1000u; + out->free_heap = tls_mem_get_avail_heapsize(); + out->last_ota_error = NULL; +} + +static void factory_reset(void) { nl_app_request_factory_reset(); } + +static void ping(void) +{ + nl_app_lock(); + nl_engine_blink(3); + nl_app_unlock(); +} + +static int fw_update(const char *url) +{ + (void)url; + return -1; /* OTA arrives in plan 3 */ +} + +const nl_api_ctx_t *nl_app_api_ctx(void) +{ + static nl_api_ctx_t ctx; + static bool ready; + + if (!ready) { + cJSON_Hooks hooks = {json_alloc, json_free}; + cJSON_InitHooks(&hooks); + ctx.board = nl_board_get(); + ctx.fw_version = NINLIGHT_FW_VERSION; + ctx.build_date = NINLIGHT_BUILD_DATE; + ctx.get_state = get_state; + ctx.apply = apply; + ctx.get_settings = get_settings; + ctx.save_settings = save_settings; + ctx.wifi_configure = wifi_configure; + ctx.open_access = open_access; + ctx.get_info = get_info; + ctx.factory_reset = factory_reset; + ctx.ping = ping; + ctx.fw_update = fw_update; + ready = true; + } + return &ctx; +} diff --git a/src/sys/nl_app_api.h b/src/sys/nl_app_api.h new file mode 100644 index 0000000..b205b16 --- /dev/null +++ b/src/sys/nl_app_api.h @@ -0,0 +1,9 @@ +#ifndef NL_APP_API_H +#define NL_APP_API_H + +#include "net/nl_api.h" + +/* API context backed by the running application. First call installs the cJSON allocator. */ +const nl_api_ctx_t *nl_app_api_ctx(void); + +#endif From c068156e6c9f65d2043f64e8a065fef75704f457 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:03:01 +0200 Subject: [PATCH 034/150] =?UTF-8?q?fix:=20Send-Timeout=20f=C3=BCr=20den=20?= =?UTF-8?q?HTTP-Server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/net/nl_http_server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net/nl_http_server.c b/src/net/nl_http_server.c index 084fcf0..2fbc0ca 100644 --- a/src/net/nl_http_server.c +++ b/src/net/nl_http_server.c @@ -43,6 +43,7 @@ static void serve(int fd) tv.tv_sec = 5; tv.tv_usec = 0; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); while (pr == NL_HTTP_INCOMPLETE && len < sizeof s_buf) { int n = recv(fd, s_buf + len, sizeof s_buf - len, 0); if (n <= 0) From 27a6d6caf259de78b3b3a110fad050f64fa1303f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:06:04 +0200 Subject: [PATCH 035/150] =?UTF-8?q?feat:=20mDNS-Responder=20f=C3=BCr=20=5F?= =?UTF-8?q?ninlight.=5Ftcp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/net/nl_mdns.c | 83 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/src/net/nl_mdns.c b/src/net/nl_mdns.c index e4e2004..8bb62cd 100644 --- a/src/net/nl_mdns.c +++ b/src/net/nl_mdns.c @@ -1,5 +1,80 @@ -#include "net/nl_mdns.h" +#include +#include -/* Placeholder until the mDNS responder lands (plan 2, task 7). */ -void nl_mdns_on_ip_up(void) {} -void nl_mdns_set_name(const char *name) { (void)name; } +#include "wm_include.h" +#include "lwip/apps/mdns.h" +#include "lwip/tcpip.h" + +#include "config/nl_settings.h" +#include "net/nl_http_server.h" +#include "net/nl_mdns.h" +#include "net/nl_names.h" +#include "net/nl_wifi.h" + +#define NL_MDNS_TTL 120 + +static bool s_started; +static char s_name[NL_NAME_LEN]; +static char s_mac[18]; + +static void add_txt(struct mdns_service *service, const char *key, const char *value) +{ + char item[64]; + int n = snprintf(item, sizeof item, "%s=%s", key, value); + if (n > 0) + mdns_resp_add_service_txtitem(service, item, (u8_t)(n < (int)sizeof item ? n : (int)sizeof item - 1)); +} + +/* tcpip thread */ +static void txt_cb(struct mdns_service *service, void *data) +{ + char name[NL_NAME_LEN]; + u32 sr; + + (void)data; + sr = tls_os_set_critical(); + memcpy(name, s_name, sizeof name); + tls_os_release_critical(sr); + add_txt(service, "type", NINLIGHT_DEVICE_ID); + add_txt(service, "fw", NINLIGHT_FW_VERSION); + add_txt(service, "mac", s_mac); + add_txt(service, "name", name); +} + +/* tcpip thread */ +static void announce(void *arg) +{ + struct netif *nif = tls_get_netif(); + + (void)arg; + if (!nif) + return; + if (!s_started) { + mdns_resp_init(); + if (mdns_resp_add_netif(nif, nl_wifi_hostname(), NL_MDNS_TTL) == ERR_OK && + mdns_resp_add_service(nif, nl_wifi_hostname(), "_ninlight", DNSSD_PROTO_TCP, NL_HTTP_PORT, NL_MDNS_TTL, + txt_cb, NULL) == ERR_OK) + s_started = true; + printf("mdns: %s.local _ninlight._tcp %s\r\n", nl_wifi_hostname(), s_started ? "up" : "failed"); + } else { + mdns_resp_netif_settings_changed(nif); /* re-announce: new IP or TXT */ + } +} + +void nl_mdns_on_ip_up(void) +{ + uint8_t mac[6]; + nl_wifi_mac(mac); + nl_names_mac_str(mac, s_mac); + tcpip_callback(announce, NULL); +} + +void nl_mdns_set_name(const char *name) +{ + u32 sr = tls_os_set_critical(); + strncpy(s_name, name, sizeof s_name - 1); + s_name[sizeof s_name - 1] = '\0'; + tls_os_release_critical(sr); + if (s_started) + tcpip_callback(announce, NULL); +} From f11ac8d9cca0599132084962e117d6f9febde9b9 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:10:51 +0200 Subject: [PATCH 036/150] fix: mDNS-Init nur einmal, Teilfehler wiederholbar --- src/net/nl_mdns.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/net/nl_mdns.c b/src/net/nl_mdns.c index 8bb62cd..70f8c87 100644 --- a/src/net/nl_mdns.c +++ b/src/net/nl_mdns.c @@ -13,7 +13,9 @@ #define NL_MDNS_TTL 120 -static bool s_started; +static bool s_inited; +static bool s_netif_added; +static bool s_service_added; static char s_name[NL_NAME_LEN]; static char s_mac[18]; @@ -45,20 +47,30 @@ static void txt_cb(struct mdns_service *service, void *data) static void announce(void *arg) { struct netif *nif = tls_get_netif(); + bool attempted = false; (void)arg; if (!nif) return; - if (!s_started) { + + if (!s_inited) { mdns_resp_init(); - if (mdns_resp_add_netif(nif, nl_wifi_hostname(), NL_MDNS_TTL) == ERR_OK && - mdns_resp_add_service(nif, nl_wifi_hostname(), "_ninlight", DNSSD_PROTO_TCP, NL_HTTP_PORT, NL_MDNS_TTL, - txt_cb, NULL) == ERR_OK) - s_started = true; - printf("mdns: %s.local _ninlight._tcp %s\r\n", nl_wifi_hostname(), s_started ? "up" : "failed"); - } else { - mdns_resp_netif_settings_changed(nif); /* re-announce: new IP or TXT */ + s_inited = true; } + if (!s_netif_added) { + attempted = true; + s_netif_added = mdns_resp_add_netif(nif, nl_wifi_hostname(), NL_MDNS_TTL) == ERR_OK; + } + if (s_netif_added && !s_service_added) { + attempted = true; + s_service_added = mdns_resp_add_service(nif, nl_wifi_hostname(), "_ninlight", DNSSD_PROTO_TCP, NL_HTTP_PORT, + NL_MDNS_TTL, txt_cb, NULL) == ERR_OK; + } + + if (attempted) + printf("mdns: %s.local _ninlight._tcp %s\r\n", nl_wifi_hostname(), s_service_added ? "up" : "failed"); + else + mdns_resp_netif_settings_changed(nif); /* re-announce: new IP or TXT */ } void nl_mdns_on_ip_up(void) @@ -75,6 +87,6 @@ void nl_mdns_set_name(const char *name) strncpy(s_name, name, sizeof s_name - 1); s_name[sizeof s_name - 1] = '\0'; tls_os_release_critical(sr); - if (s_started) + if (s_service_added) tcpip_callback(announce, NULL); } From de49647f9d9574c611e70a56f71c771e505f5713 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:14:15 +0200 Subject: [PATCH 037/150] docs: Bruno-Collection, REST-Doku und Netzwerk-Checkliste --- README.md | 16 ++ bruno/ninlight.yml | 395 +++++++++++++++++++++++++++++++++++++ docs/hardware-checklist.md | 13 ++ 3 files changed, 424 insertions(+) create mode 100644 bruno/ninlight.yml diff --git a/README.md b/README.md index 0dff849..93f9499 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,22 @@ Serielle Konsole (115200 8N1, lokales Echo): Verfügbare Befehle in der Konsole: `help`. +## Netzwerk & REST + +- WLAN fest einbauen: `NINLIGHT_WIFI_SSID` / `NINLIGHT_WIFI_PASSWORD` in `config.local.cmake` (gespeicherte Einstellungen haben Vorrang). +- Ohne WLAN-Daten (oder nach Werksreset) öffnet die Lampe den AP `ninlight-` (Passwort `NINLIGHT_AP_PASSWORD`, Default `ninlight`) und pulsiert warmweiß. Onboarding: + + curl -X PUT http://192.168.4.1:3333/api/v1/system/settings \ + -d '{"type":"wifi","ssid":"MeinWLAN","password":"geheim123"}' + curl http://192.168.4.1:3333/api/v1/system/info # wifi.sta_ip / wifi.error + + Nach Erfolg schließt der AP nach 60 s. Ohne Verbindung für 5 min öffnet die Lampe den AP zusätzlich (Fallback). +- REST auf Port 3333, Präfix `/api/v1`: `GET|POST device/state`, `GET system/info`, `GET|PUT system/settings` und `PUT system/command` (beide mit Header `Authorization: `, Default-Token `aabbccddeeff0011223344`). +- Beispiel: `curl -X POST http://ninlight-681f.local:3333/api/v1/device/state -d '{"on":true,"brightness":40,"color_temp":2700}'` +- Suchen: `dns-sd -B _ninlight._tcp` (macOS), Details `dns-sd -L ninlight-681f _ninlight._tcp`. +- Bruno: `bruno/ninlight.yml` importieren, Environment „EVK (mDNS)" bzw. „Onboarding-AP" wählen. +- Konsole: `net` zeigt Modus, SSID, IP, RSSI und den letzten Onboarding-Fehler. + ## Werksreset Gerät 5× hintereinander einschalten, jeweils kürzer als 5 Sekunden. diff --git a/bruno/ninlight.yml b/bruno/ninlight.yml new file mode 100644 index 0000000..ad9fa1d --- /dev/null +++ b/bruno/ninlight.yml @@ -0,0 +1,395 @@ +opencollection: 1.0.0 +info: + name: ninlight +config: + environments: + - name: EVK (mDNS) + variables: + - name: device_address + value: ninlight-681f.local + - name: device_port + value: '3333' + - name: device_token + value: aabbccddeeff0011223344 + - name: Onboarding-AP + variables: + - name: device_address + value: 192.168.4.1 + - name: device_port + value: '3333' + - name: device_token + value: aabbccddeeff0011223344 +items: + - info: + name: Device + type: folder + seq: 1 + items: + - info: + name: Get State + type: http + seq: 1 + http: + method: GET + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Power On + type: http + seq: 2 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "on": true + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Power Off + type: http + seq: 3 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "on": false + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Brightness 50 + type: http + seq: 4 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "brightness": 50 + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Color Red + type: http + seq: 5 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "color": { + "r": 255, + "g": 0, + "b": 0 + } + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Color Temp 2700 + type: http + seq: 6 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "color_temp": 2700 + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Fade to 100 % in 3 s + type: http + seq: 7 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "on": true, + "brightness": 100, + "transition": 3000 + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Power-On Behaviour + type: http + seq: 8 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "power_on_behaviour": "last" + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Fade Times + type: http + seq: 9 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "fade_in": 500, + "fade_out": 800 + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: System + type: folder + seq: 2 + items: + - info: + name: Get System Info + type: http + seq: 1 + http: + method: GET + url: '{{device_address}}:{{device_port}}/api/v1/system/info' + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Get System Settings + type: http + seq: 2 + http: + method: GET + url: '{{device_address}}:{{device_port}}/api/v1/system/settings' + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Set WiFi + type: http + seq: 3 + http: + method: PUT + url: '{{device_address}}:{{device_port}}/api/v1/system/settings' + body: + type: json + data: |- + { + "type": "wifi", + "ssid": "MeinWLAN", + "password": "geheim123" + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Set Device Name + type: http + seq: 4 + http: + method: PUT + url: '{{device_address}}:{{device_port}}/api/v1/system/settings' + body: + type: json + data: |- + { + "type": "device_name", + "name": "Wohnzimmer" + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Set Token + type: http + seq: 5 + http: + method: PUT + url: '{{device_address}}:{{device_port}}/api/v1/system/settings' + body: + type: json + data: |- + { + "type": "token", + "token": "neues-token-123" + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Require Auth for All + type: http + seq: 6 + http: + method: PUT + url: '{{device_address}}:{{device_port}}/api/v1/system/settings' + body: + type: json + data: |- + { + "type": "auth", + "all": true + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Commands + type: folder + seq: 7 + items: + - info: + name: Ping + type: http + seq: 1 + http: + method: PUT + url: '{{device_address}}:{{device_port}}/api/v1/system/command' + body: + type: json + data: |- + { + "command": { + "type": "ping" + } + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Reboot + type: http + seq: 2 + http: + method: PUT + url: '{{device_address}}:{{device_port}}/api/v1/system/command' + body: + type: json + data: |- + { + "command": { + "type": "reboot" + } + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Factory Reset + type: http + seq: 3 + http: + method: PUT + url: '{{device_address}}:{{device_port}}/api/v1/system/command' + body: + type: json + data: |- + { + "command": { + "type": "factory_reset" + } + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Firmware Update + type: http + seq: 4 + http: + method: PUT + url: '{{device_address}}:{{device_port}}/api/v1/system/command' + body: + type: json + data: |- + { + "command": { + "type": "fw_update", + "url": "http://192.168.1.12:8000/ninlight_evk_ota.img" + } + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 +request: + headers: + - name: Authorization + value: '{{device_token}}' +bundled: true +extensions: + bruno: + ignore: + - node_modules + - .git + presets: + requestType: http + requestUrl: '{{device_address}}:{{device_port}}/api/v1/' diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index a6c8076..c79c307 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -22,6 +22,19 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] Watchdog: `wdtest` → Neustart nach ca. 20–40 s (erste Ablaufzeit löst IRQ aus, Reset beim zweiten Ablauf) — getestet 2026-09-11 mit e19ee98: Neustart nach 40,4 s - [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F) +## Netzwerk & REST (Plan 2) +- [ ] Mit Build-Credentials: Boot → `net` zeigt `mode=sta` und eine IP, RSSI plausibel (−30 … −90) +- [ ] `dns-sd -B _ninlight._tcp` findet `ninlight-`, `dns-sd -L` zeigt TXT type/fw/mac/name +- [ ] Bruno: alle Device-Requests wirken sichtbar, `Get State` spiegelt den Zustand +- [ ] Ungültiger Body (`{"brightness":0}`) → 400, `system/settings` ohne Token → 401 +- [ ] `bri 30` per REST, sofort `Reboot` per REST → nach dem Boot 30 % (Flush) +- [ ] Werksreset → AP `ninlight-` sichtbar, Lampe pulsiert, Beitritt mit `ninlight` +- [ ] Onboarding mit falschem Passwort → `system/info` zeigt `wifi.error = "auth"`, AP bleibt +- [ ] Onboarding korrekt → `202`, Lampe verbindet, Pulsieren stoppt, AP nach 60 s weg +- [ ] Router aus > 5 min → AP erscheint (`mode=apsta`); Router an → Verbindung, AP nach 10 s weg +- [ ] Namensänderung per REST → TXT `name=` aktualisiert (`dns-sd -L`) +- [ ] `auth_all` an → `GET device/state` ohne Token 401, mit Token 200 + ## Später (Lampen) - [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) - [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) From 89c764514aa97d459155091e76813e69f99cb62b Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:50:45 +0200 Subject: [PATCH 038/150] fix: cJSON-Verschachtelung begrenzen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C1: Ein POST an /api/v1/device/state mit tief verschachteltem JSON (z.B. 20 Ebenen "[[[[...") lässt cJSONs rekursiven Parser den 6-KB-Stack des HTTP-Tasks überlaufen - CJSON_NESTING_LIMIT war mit 1000 viel zu hoch und FreeRTOS' Stack-Overflow-Checking ist aus. Begrenzt auf 8 Ebenen, sowohl für die Firmware (third_party/cJSON/cJSON.c) als auch für die Host-Tests (nl_cjson), damit beide dasselbe Verhalten zeigen. Test: test_post_validation deckt jetzt einen Body mit 20 verschachtelten Arrays ab (400 "invalid json"). --- CMakeLists.txt | 4 ++++ test/CMakeLists.txt | 2 ++ test/test_api.c | 1 + 3 files changed, 7 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23b4194..d777537 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,10 @@ set(NL_FW_SOURCES src/sys/nl_app_api.c third_party/cJSON/cJSON.c) +# C1: deeply nested JSON must not overflow the 6 KB HTTP task stack (parse_value recurses). +set_source_files_properties(third_party/cJSON/cJSON.c PROPERTIES + COMPILE_DEFINITIONS "CJSON_NESTING_LIMIT=8") + set(NL_FW ninlight_${NINLIGHT_DEVICE}) add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS}) set_target_properties(${NL_FW} PROPERTIES SUFFIX ".elf") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a771b37..6b98238 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,6 +15,8 @@ enable_testing() # cJSON is third-party code: build it without -Werror. add_library(nl_cjson STATIC ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/cJSON/cJSON.c) target_include_directories(nl_cjson PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/cJSON) +# C1: match the firmware's nesting limit so host tests exercise the same behaviour. +target_compile_definitions(nl_cjson PUBLIC CJSON_NESTING_LIMIT=8) set(NL_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../src) diff --git a/test/test_api.c b/test/test_api.c index 794d609..2e6fd05 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -100,6 +100,7 @@ static void test_post_validation(void) static const struct { const char *body; const char *err; } CASES[] = { {"not json", "invalid json"}, {"[1,2]", "invalid json"}, + {"{\"a\":[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]}", "invalid json"}, /* C1: 20 levels > CJSON_NESTING_LIMIT */ {"{\"on\":1}", "invalid on"}, {"{\"brightness\":0}", "invalid brightness"}, {"{\"brightness\":101}", "invalid brightness"}, From 3250b899864444f17301fac2f0af7417d988ec07 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:51:03 +0200 Subject: [PATCH 039/150] fix: Onboarding-Test trennt die Verbindung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I1: fail_test() kehrte ohne gespeicherte Zugangsdaten stillschweigend nach AP_ONBOARDING zurück, ohne den Stationsversuch zu trennen; ein danach doch noch eintreffendes CONNECTED wurde in AP_ONBOARDING ignoriert, aber der Treiber rief nl_mdns_on_ip_up() unabhängig vom Zustand auf - die Lampe landete offen im LAN, ohne dass Zugangsdaten gespeichert wurden. nl_wifi_actions_t bekommt ein neues Feld `disconnect`: - fail_test() ohne Zugangsdaten setzt es (Timeout/Fehler beendet den Stationsversuch). - Ein CONNECTED-Event in AP_ONBOARDING gilt als Ausreißer eines bereits gescheiterten Versuchs und wird ebenfalls getrennt (Zustand bleibt unverändert). - M1: nl_wifi_sm_onboard() trennt jetzt immer vor dem Testverbindungsaufbau, damit veraltete Events eines vorherigen Versuchs nicht mehr zählen; der Treiber (nl_wifi_request_onboard) meldet zusätzlich "wifi busy" (503), solange bereits ein Onboarding-Test läuft. Der Treiber (src/net/nl_wifi_w600.c) führt `disconnect` vor `connect` aus und ruft nl_mdns_on_ip_up() nur noch, wenn der Zustand nach der Verarbeitung tatsächlich STA_CONNECTED ist. Tests (test_wifi_sm.c): Timeout/Fehler ohne Zugangsdaten -> disconnect; CONNECTED während AP_ONBOARDING -> disconnect, Zustand unverändert; erneutes Onboarding während laufendem Test -> disconnect vor dem neuen connect. --- src/net/nl_wifi_sm.c | 5 +++++ src/net/nl_wifi_sm.h | 1 + test/test_wifi_sm.c | 32 +++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/net/nl_wifi_sm.c b/src/net/nl_wifi_sm.c index 930831f..373d80a 100644 --- a/src/net/nl_wifi_sm.c +++ b/src/net/nl_wifi_sm.c @@ -50,6 +50,7 @@ static void fail_test(nl_wifi_sm_t *sm, nl_wifi_error_t err, uint32_t now, nl_wi sm->connect_at = now; } else { sm->state = NL_WIFI_ST_AP_ONBOARDING; + a->disconnect = true; /* I1: no stored creds -> drop the stray station attempt */ } } @@ -114,6 +115,9 @@ nl_wifi_actions_t nl_wifi_sm_event(nl_wifi_sm_t *sm, nl_wifi_event_t ev, nl_wifi } break; case NL_WIFI_ST_AP_ONBOARDING: + if (ev == NL_WIFI_EV_CONNECTED) + a.disconnect = true; /* I1: stray success of a timed-out/failed attempt */ + break; default: break; } @@ -169,6 +173,7 @@ nl_wifi_actions_t nl_wifi_sm_onboard(nl_wifi_sm_t *sm, uint32_t now) sm->deadline = now + NL_WIFI_ONBOARD_TIMEOUT_MS; sm->retry_armed = false; sm->ap_stop_armed = false; + a.disconnect = true; /* M1: drop any stale station attempt so its events don't count */ a.connect = true; a.use_test_creds = true; return a; diff --git a/src/net/nl_wifi_sm.h b/src/net/nl_wifi_sm.h index 478d16e..5db82f1 100644 --- a/src/net/nl_wifi_sm.h +++ b/src/net/nl_wifi_sm.h @@ -30,6 +30,7 @@ typedef struct { bool connect; /* connect the station ... */ bool use_test_creds; /* ... with the onboarding credentials instead of the stored ones */ bool save_test_creds; /* onboarding succeeded: persist the onboarding credentials */ + bool disconnect; /* drop a stray/failed station attempt (before any connect in the same set) */ bool pulse_on; bool pulse_off; } nl_wifi_actions_t; diff --git a/test/test_wifi_sm.c b/test/test_wifi_sm.c index b306afd..57a48d4 100644 --- a/test/test_wifi_sm.c +++ b/test/test_wifi_sm.c @@ -112,6 +112,7 @@ static void test_onboarding_failure_without_credentials(void) TEST_ASSERT_FALSE(a.pulse_off); TEST_ASSERT_FALSE(a.stop_ap); TEST_ASSERT_FALSE(a.connect); + TEST_ASSERT_TRUE(a.disconnect); /* I1: stray station attempt must not linger */ TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); TEST_ASSERT_EQUAL(NL_WIFI_ERR_AUTH, sm.error); TEST_ASSERT_EQUAL_STRING("auth", nl_wifi_error_name(sm.error)); @@ -125,9 +126,22 @@ static void test_onboarding_timeout(void) TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS - 1); TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); - nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS); + a = nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS); TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); TEST_ASSERT_EQUAL_STRING("timeout", nl_wifi_error_name(sm.error)); + TEST_ASSERT_TRUE(a.disconnect); /* I1: timed-out onboarding test leaves no station attempt running */ +} + +static void test_onboarding_connected_event_while_ap_onboarding_is_stray(void) +{ + /* I1: fail_test() returns to AP_ONBOARDING; a late CONNECTED for the timed-out + * attempt must be treated as stray and disconnected, not accepted. */ + nl_wifi_sm_start(&sm, false, 0); + nl_wifi_sm_onboard(&sm, 5000); + nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS); /* -> AP_ONBOARDING */ + a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 40000); + TEST_ASSERT_TRUE(a.disconnect); + TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); } static void test_onboarding_while_connected_falls_back_to_old_credentials(void) @@ -149,6 +163,20 @@ static void test_onboarding_while_connected_falls_back_to_old_credentials(void) TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 16000).stop_ap); } +static void test_onboard_while_test_running_disconnects_stale_attempt(void) +{ + /* M1: a repeated onboarding request while a test connect is already in flight + * must disconnect first so stale events from the previous attempt don't count. */ + nl_wifi_sm_start(&sm, false, 0); + nl_wifi_sm_onboard(&sm, 5000); + TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); + a = nl_wifi_sm_onboard(&sm, 6000); + TEST_ASSERT_TRUE(a.disconnect); + TEST_ASSERT_TRUE(a.connect); + TEST_ASSERT_TRUE(a.use_test_creds); + TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); +} + static void test_error_names(void) { TEST_ASSERT_NULL(nl_wifi_error_name(NL_WIFI_ERR_NONE)); @@ -185,7 +213,9 @@ int main(void) RUN_TEST(test_onboarding_success); RUN_TEST(test_onboarding_failure_without_credentials); RUN_TEST(test_onboarding_timeout); + RUN_TEST(test_onboarding_connected_event_while_ap_onboarding_is_stray); RUN_TEST(test_onboarding_while_connected_falls_back_to_old_credentials); + RUN_TEST(test_onboard_while_test_running_disconnects_stale_attempt); RUN_TEST(test_error_names); RUN_TEST(test_connect_without_any_event_retries); return UNITY_END(); From 5f45b9e996ac305f55c96bda3f667e5376fad2fc Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:51:11 +0200 Subject: [PATCH 040/150] =?UTF-8?q?fix:=20Gesamt-Timeout=20f=C3=BCr=20HTTP?= =?UTF-8?q?-Anfragen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I2: SO_RCVTIMEO=5s war ein Timeout pro recv(), nicht für die gesamte Anfrage. Ein Client, der 1 Byte alle 4,9 s schickt, konnte den einzigen HTTP-Server-Task damit stundenlang belegen (Spec §7.2 will 5 s pro Header). serve() rechnet jetzt eine absolute Deadline (nl_app_now_ms() + 5000 ms) und setzt SO_RCVTIMEO vor jedem recv() auf die verbleibende Zeit (mindestens 1 ms); ist die Deadline überschritten, wird die Verbindung sofort verworfen. Nicht host-testbar (nl_http_server.c bindet SDK-Sockets ein); über den Firmware-Build verifiziert. --- src/net/nl_http_server.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/net/nl_http_server.c b/src/net/nl_http_server.c index 2fbc0ca..0e89f45 100644 --- a/src/net/nl_http_server.c +++ b/src/net/nl_http_server.c @@ -33,19 +33,35 @@ static void simple_error(int status, const char *msg) strcpy(s_resp.body, msg); } +#define NL_HTTP_REQUEST_TIMEOUT_MS 5000u + static void serve(int fd) { struct timeval tv; nl_http_request_t req; nl_http_parse_t pr = NL_HTTP_INCOMPLETE; size_t len = 0, out; + uint32_t deadline = nl_app_now_ms() + NL_HTTP_REQUEST_TIMEOUT_MS; tv.tv_sec = 5; tv.tv_usec = 0; - setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); + /* I2: 5 s is a deadline for the whole header+body read, not a per-recv() timeout - + * otherwise a client trickling in 1 byte per <5 s occupies the single server task forever. */ while (pr == NL_HTTP_INCOMPLETE && len < sizeof s_buf) { - int n = recv(fd, s_buf + len, sizeof s_buf - len, 0); + uint32_t now = nl_app_now_ms(); + uint32_t remaining_ms; + int n; + + if ((int32_t)(deadline - now) <= 0) + return; /* overall deadline passed: drop silently */ + remaining_ms = deadline - now; + tv.tv_sec = remaining_ms / 1000u; + tv.tv_usec = (remaining_ms % 1000u) * 1000u; + if (tv.tv_sec == 0 && tv.tv_usec == 0) + tv.tv_usec = 1000; /* at least 1 ms */ + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + n = recv(fd, s_buf + len, sizeof s_buf - len, 0); if (n <= 0) return; /* timeout or closed: drop silently */ len += (size_t)n; From d09862312167eb8e645bf24e677d5b83ba2d708c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:51:38 +0200 Subject: [PATCH 041/150] =?UTF-8?q?fix:=20SDK-SoftAP-Parameter=20l=C3=B6sc?= =?UTF-8?q?hen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C2: tls_wifi_softap_create() setzt das SOFTAP-Bit (IEEE80211_MODE_AP = 4) in TLS_PARAM_ID_WPROTOCOL und schreibt es in den Flash; tls_wifi_softap_destroy() löscht es nie wieder. Solange das Bit gesetzt bleibt, erstellt sys_net_status_changed() (sdk/Platform/Sys/tls_sys.c:519-525) bei jedem NETIF_IP_NET_UP über tls_auto_reconnect_softap() erneut den Onboarding-AP mit den SDK-eigenen (öffentlichen Default-)Parametern - unabhängig vom Auto-Connect-Flag. Nach jedem Onboarding/Fallback käme der AP `ninlight-` mit dem öffentlichen Default-Passwort bei jedem Reconnect/Reboot zurück, offen fürs LAN (SDK-NAPT). Verifiziert in sdk/Platform/Sys/tls_sys.c, sdk/Include/Platform/wm_params.h und sdk/Platform/Common/Params/wm_param.c: - TLS_PARAM_ID_WPROTOCOL (10) ist ein u8, TLS_PARAM_IEEE80211_SOFTAP == 4 == IEEE80211_MODE_AP (sdk/Include/WiFi/wm_wifi.h). - tls_param_get()/tls_param_set(id, argv, to_flash) - genau die Signaturen aus dem Finding. - tls_param_to_default() (tls_param_load_factory_default() + param_to_flash()) arbeitet ausschließlich auf `struct tls_sys_param` (Wi-Fi/Netzwerk-Konfig); es liest/schreibt nirgends die MAC-Adresse oder RF-Kalibrierdaten - die liegen in einer komplett getrennten, efuse-gestützten FT_PARAM_ST-Struktur in sdk/Platform/Drivers/efuse/wm_efuse.c. tls_param_to_default() ist damit für den Werksreset sicher. Fix (nl_wifi_w600.c): 1. clear_softap_param(): liest TLS_PARAM_ID_WPROTOCOL, löscht bei gesetztem IEEE80211_MODE_AP-Bit und persistiert das (to_flash=TRUE). Aufgerufen in nl_wifi_start() (vor allem anderen) und nach jedem tls_wifi_softap_destroy(). 2. nl_wifi_start(): zerstört einen bereits vom SDK selbst erzeugten SoftAP (tls_wifi_softap_get_state() != WM_WIFI_DISCONNECTED), bevor unsere eigene State Machine startet. 3. Backstop im Netzwerk-Task: NETIF_WIFI_SOFTAP_SUCCESS, während die State Machine sagt, der AP solle unten sein (s_sm.ap_up == false) -> AP wieder zerstören und Param erneut löschen (geloggt). 4. Werksreset (nl_app_factory_reset, src/app_main.c): setzt zusätzlich tls_param_to_default() (siehe Verifikation oben) und dokumentiert das inline. Nebenbei im selben Treiber erledigt, weil sie denselben Codepfad betreffen: - M2: connect_station() loggt den Rückgabewert von tls_wifi_connect(), wenn er nicht WM_SUCCESS ist. - M3: start_ap() merkt sich einen fehlgeschlagenen tls_wifi_softap_create() (`s_ap_create_pending`) und wiederholt ihn alle 5 s, solange die State Machine den AP oben haben will. Nicht host-testbar (bindet SDK-Header ein); über den Firmware-Build verifiziert (baut sauber für evk, keine neuen Warnungen). --- src/app_main.c | 7 ++++ src/net/nl_wifi_w600.c | 79 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/app_main.c b/src/app_main.c index b92ef87..8392ba9 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -107,6 +107,13 @@ void nl_app_factory_reset(void) nl_state_log_erase(); nl_resetcnt_clear(); store_unlock(); + /* C2: also wipe the SDK's own Wi-Fi params (SSID/key/WPROTOCOL, incl. any leftover + * softap bit) - verified in sdk/Platform/Common/Params/wm_param.c that + * tls_param_to_default() (tls_param_load_factory_default() + param_to_flash()) only + * touches struct tls_sys_param (Wi-Fi/network config); it never touches the MAC + * address or RF calibration data, which live in a separate efuse-backed FT_PARAM_ST + * area (sdk/Platform/Drivers/efuse/wm_efuse.c) that this call never reads or writes. */ + tls_param_to_default(); tls_sys_reset(); } diff --git a/src/net/nl_wifi_w600.c b/src/net/nl_wifi_w600.c index 4f0ac7d..cc8d6e6 100644 --- a/src/net/nl_wifi_w600.c +++ b/src/net/nl_wifi_w600.c @@ -28,6 +28,8 @@ static char s_hostname[24]; static char s_ssid[NL_SSID_LEN], s_pass[NL_PASS_LEN]; static char s_test_ssid[NL_SSID_LEN], s_test_pass[NL_PASS_LEN]; static char s_ap_pass[NL_PASS_LEN]; +static bool s_ap_create_pending; /* M3: tls_wifi_softap_create() failed, retry it */ +static uint32_t s_ap_create_retry_at; static void lock(void) { tls_os_mutex_acquire(s_lock, 0); } static void unlock(void) { tls_os_mutex_release(s_lock); } @@ -38,12 +40,32 @@ static void copy(char *dst, const char *src, size_t size) dst[size - 1] = '\0'; } +static bool due_now(uint32_t now, uint32_t at) { return (int32_t)(now - at) >= 0; } + /* SDK system task context: only hand the event over. */ static void netif_event(u8 status) { tls_os_queue_send(s_queue, (void *)(MSG_NETIF | status), 0); } +/* C2: the SDK sets the SOFTAP bit in TLS_PARAM_ID_WPROTOCOL when tls_wifi_softap_create() + * runs and persists it to flash; tls_wifi_softap_destroy() only sends a supplicant event + * and never clears it. As long as the bit stays set, sys_net_status_changed() in + * sdk/Platform/Sys/tls_sys.c (NETIF_IP_NET_UP case) recreates the AP with the SDK's own + * (public default) softap params on every station IP-up - independent of our auto-connect + * flag. Clear the bit (and persist that) whenever we know the AP should be down. */ +static void clear_softap_param(void) +{ + u8 proto = 0; + + tls_param_get(TLS_PARAM_ID_WPROTOCOL, &proto, TRUE); + if (proto & IEEE80211_MODE_AP) { + proto &= (u8)~IEEE80211_MODE_AP; + tls_param_set(TLS_PARAM_ID_WPROTOCOL, &proto, TRUE); + printf("wifi: cleared stale SDK softap param (wprotocol now %u)\r\n", proto); + } +} + static void start_ap(void) { struct tls_softap_info_t ap; @@ -65,18 +87,24 @@ static void start_ap(void) memcpy(ip.dnsname, "local", 6); r = tls_wifi_softap_create(&ap, &ip); printf("wifi: AP %s %s\r\n", s_hostname, r == WM_SUCCESS ? "up" : "failed"); + /* M3: retry a failed AP create instead of leaving onboarding/fallback without an AP. */ + s_ap_create_pending = r != WM_SUCCESS; + s_ap_create_retry_at = nl_app_now_ms() + 5000u; } static void connect_station(bool test) { char ssid[NL_SSID_LEN], pass[NL_PASS_LEN]; + int r; lock(); copy(ssid, test ? s_test_ssid : s_ssid, sizeof ssid); copy(pass, test ? s_test_pass : s_pass, sizeof pass); unlock(); printf("wifi: connecting to %s\r\n", ssid); - tls_wifi_connect((u8 *)ssid, (u8)strlen(ssid), (u8 *)pass, (u8)strlen(pass)); + r = tls_wifi_connect((u8 *)ssid, (u8)strlen(ssid), (u8 *)pass, (u8)strlen(pass)); + if (r != WM_SUCCESS) /* M2: surface connect-call failures, not just later join events */ + printf("wifi: tls_wifi_connect failed, err=%d\r\n", r); } static void pulse(bool on) @@ -108,6 +136,12 @@ static void execute(nl_wifi_actions_t a) if (a.stop_ap) { printf("wifi: AP down\r\n"); tls_wifi_softap_destroy(); + s_ap_create_pending = false; + clear_softap_param(); /* C2: every softap teardown clears the persisted SOFTAP bit */ + } + if (a.disconnect) { + printf("wifi: dropping stray/failed station attempt\r\n"); + tls_wifi_disconnect(); } if (a.connect) connect_station(a.use_test_creds); @@ -129,7 +163,7 @@ static void net_task(void *arg) void *msg = NULL; nl_wifi_actions_t ev, tick; uint32_t m = 0, now; - bool ip_up = false; + bool got_ip_event = false, connected_now, ap_wanted; memset(&ev, 0, sizeof ev); if (tls_os_queue_receive(s_queue, &msg, 0, HZ / 10) == TLS_OS_SUCCESS) @@ -139,7 +173,7 @@ static void net_task(void *arg) lock(); if (m == (MSG_NETIF | NETIF_IP_NET_UP)) { ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, now); - ip_up = true; + got_ip_event = true; } else if (m == (MSG_NETIF | NETIF_WIFI_JOIN_FAILED)) { ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_JOIN_FAILED, join_error(), now); } else if (m == (MSG_NETIF | NETIF_WIFI_DISCONNECTED)) { @@ -148,14 +182,31 @@ static void net_task(void *arg) ev = nl_wifi_sm_onboard(&s_sm, now); } tick = nl_wifi_sm_tick(&s_sm, now); + /* I1: a late/stray IP-up must not announce mDNS unless the SM actually settled on STA_CONNECTED. */ + connected_now = s_sm.state == NL_WIFI_ST_STA_CONNECTED; + ap_wanted = s_sm.ap_up; + if (m == (MSG_NETIF | NETIF_WIFI_SOFTAP_SUCCESS) && !ap_wanted) { + /* C2 backstop: the SDK brought its own softap up behind our back (e.g. the + * clear in nl_wifi_start raced a pending sys_net_status_changed()) while our + * state machine does not want an AP. Tear it down and clear the param again. */ + printf("wifi: unexpected SDK softap while AP should be down, destroying it\r\n"); + tls_wifi_softap_destroy(); + clear_softap_param(); + } unlock(); if (m & MSG_NETIF) printf("wifi: netif event %u\r\n", (unsigned)(m & 0xFFu)); execute(ev); execute(tick); - if (ip_up) + if (got_ip_event && connected_now) nl_mdns_on_ip_up(); + + /* M3: retry a softap create that failed, as long as the SM still wants the AP up. */ + if (s_ap_create_pending && ap_wanted && due_now(now, s_ap_create_retry_at)) { + printf("wifi: retrying AP create\r\n"); + start_ap(); + } } } @@ -165,6 +216,8 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass) nl_wifi_actions_t a; struct netif *nif; + clear_softap_param(); /* C2: drop any softap bit left over from a previous onboarding/fallback */ + tls_get_mac_addr(s_mac); nl_names_hostname(s_mac, s_hostname, sizeof s_hostname); copy(s_ssid, ssid, sizeof s_ssid); @@ -178,6 +231,13 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass) tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_mode); } tls_wifi_disconnect(); + if (tls_wifi_softap_get_state() != WM_WIFI_DISCONNECTED) { + /* C2: the SDK already brought up its own softap (e.g. from NETIF_IP_NET_UP before + * we got a chance to run) - tear it down, our state machine owns AP lifecycle. */ + printf("wifi: destroying SDK-created softap left over from boot\r\n"); + tls_wifi_softap_destroy(); + clear_softap_param(); + } nif = tls_get_netif(); if (nif) nif->hostname = s_hostname; /* DHCP host name */ @@ -197,12 +257,19 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass) int nl_wifi_request_onboard(const char *ssid, const char *pass) { + bool busy; + if (!s_started) return -1; lock(); - copy(s_test_ssid, ssid, sizeof s_test_ssid); - copy(s_test_pass, pass, sizeof s_test_pass); + busy = s_sm.state == NL_WIFI_ST_ONBOARD_TEST; + if (!busy) { + copy(s_test_ssid, ssid, sizeof s_test_ssid); + copy(s_test_pass, pass, sizeof s_test_pass); + } unlock(); + if (busy) /* M1: API returns 503 "wifi busy" instead of queuing on top of a running test */ + return -1; return tls_os_queue_send(s_queue, (void *)MSG_ONBOARD, 0) == TLS_OS_SUCCESS ? 0 : -1; } From 2e90b8fd067b3f493d1193d517f48d64c0ee9296 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:52:03 +0200 Subject: [PATCH 042/150] =?UTF-8?q?fix:=20kleinere=20Netzwerk-H=C3=A4rtung?= =?UTF-8?q?en?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M2: WLAN-Passwort muss leer (offenes Netz) oder 8..64 Zeichen lang sein (400 "invalid password" bei 1..7 Zeichen). CMake prüft NINLIGHT_WIFI_PASSWORD ebenso (0 oder 8..63 Zeichen) und der Zeichen-Blacklist-Check bekommt `;` dazu. M4: Ein Authorization-Wert, der länger als NL_TOKEN_LEN-1 (64) ist, wird von nl_http_parse() nicht mehr stillschweigend abgeschnitten, sondern setzt `auth_too_long` in nl_http_request_t; nl_api_handle() behandelt eine solche Anfrage als unauthentifiziert (außer bei offenem Zugriff), damit ein abgeschnittener Wert nie zufällig auf ein kürzeres Token passt. M5: get_int() prüfte die (long)-Cast-Gleichheit vor dem Bereichscheck - bei sehr großen oder NaN-Werten ist der Cast von double nach long undefiniertes Verhalten. Bereichs- und NaN-Check laufen jetzt vor dem Cast. T1: sdk/Platform/Drivers/internalflash/wm_internal_fls.c: tls_fls_init() ignorierte den Rückgabewert von tls_os_sem_create(&s_fls2_lock, 1) - jetzt eine TLS_DBGPRT_ERR-Meldung, falls das fehlschlägt (dokumentiert in sdk/PATCHES.md). Tests: test_api.c (Passwortlängen, auth_too_long, 1e300-Brightness), test_http.c (auth_too_long wird gesetzt). Für M5 zeigt der Host-Test kein sichtbares RED, weil der (double)->(long)-Cast auf dieser Plattform/diesem Compiler keinen Crash auslöst (implementation-defined statt UB-Crash) - der Fix wird trotzdem angewendet, da er auf dem Zielcompiler (arm-none-eabi 4.9) nicht garantiert sicher ist. --- CMakeLists.txt | 9 +++++-- sdk/PATCHES.md | 4 ++++ .../Drivers/internalflash/wm_internal_fls.c | 3 ++- src/net/nl_api.c | 19 +++++++++++---- src/net/nl_http.c | 4 +++- src/net/nl_http.h | 2 ++ test/test_api.c | 24 +++++++++++++++++++ test/test_http.c | 17 +++++++++++++ 8 files changed, 74 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d777537..ea31c4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,9 +33,14 @@ string(LENGTH "${NINLIGHT_AP_PASSWORD}" _nl_ap_len) if(_nl_ap_len LESS 8) message(FATAL_ERROR "NINLIGHT_AP_PASSWORD needs at least 8 characters (WPA2)") endif() +# M2: WPA2 station passwords must be empty (open network) or 8..63 chars. +string(LENGTH "${NINLIGHT_WIFI_PASSWORD}" _nl_wifi_pass_len) +if(NOT _nl_wifi_pass_len EQUAL 0 AND (_nl_wifi_pass_len LESS 8 OR _nl_wifi_pass_len GREATER 63)) + message(FATAL_ERROR "NINLIGHT_WIFI_PASSWORD must be empty or 8..63 characters (WPA2)") +endif() foreach(_v NINLIGHT_WIFI_SSID NINLIGHT_WIFI_PASSWORD NINLIGHT_AP_PASSWORD NINLIGHT_TOKEN) - if("${${_v}}" MATCHES "[\"\\\\]") - message(FATAL_ERROR "${_v} must not contain \" or \\") + if("${${_v}}" MATCHES "[\"\\\\;]") + message(FATAL_ERROR "${_v} must not contain \", \\ or ;") endif() endforeach() diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index a8a54e1..14027b5 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -27,3 +27,7 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ `light_task` und `app_task` blockieren und der Watchdog den Chip zurücksetzen muss. 5. `Platform/Drivers/internalflash/wm_internal_fls.c`: Semaphor `s_fls2_lock` serialisiert den Flash-Pfad oberhalb von 1 MB (Pin-Umschaltung via `tls_spifls_cs/ck/di/do_switch` war ungeschützt). + T1 (Plan 2 final review): `tls_fls_init()` ignorierte den Rückgabewert von + `tls_os_sem_create(&s_fls2_lock, 1)` stillschweigend - jetzt eine `TLS_DBGPRT_ERR`-Meldung, + falls das Anlegen fehlschlägt (dann bliebe der Bereich oberhalb von 1 MB unprotokolliert + ungeschützt). diff --git a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c index 0a1e512..d3d8e9d 100644 --- a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c +++ b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c @@ -1173,7 +1173,8 @@ int tls_fls_init(void) TLS_DBGPRT_ERR("create semaphore @fls_lock fail!\n"); return TLS_FLS_STATUS_ENOMEM; } - tls_os_sem_create(&s_fls2_lock, 1); + if (tls_os_sem_create(&s_fls2_lock, 1) != TLS_OS_SUCCESS) + TLS_DBGPRT_ERR("create semaphore @s_fls2_lock fail! flash access above 1 MB is unprotected!\n"); inside_fls = fls; diff --git a/src/net/nl_api.c b/src/net/nl_api.c index 0577003..9e78be2 100644 --- a/src/net/nl_api.c +++ b/src/net/nl_api.c @@ -51,12 +51,19 @@ static bool token_ok(const char *given, const char *expected) static int get_int(const cJSON *obj, const char *name, double lo, double hi, long *out) { const cJSON *v = cJSON_GetObjectItemCaseSensitive(obj, name); + double d; if (!v) return 0; - if (!cJSON_IsNumber(v) || v->valuedouble != (double)(long)v->valuedouble || v->valuedouble < lo || - v->valuedouble > hi) + if (!cJSON_IsNumber(v)) return -1; - *out = (long)v->valuedouble; + d = v->valuedouble; + /* M5: reject NaN and out-of-range values before casting to (long) - a huge or NaN + * double is undefined behaviour to cast, so lo/hi must be checked first. */ + if (d != d || d < lo || d > hi) + return -1; + if (d != (double)(long)d) + return -1; + *out = (long)d; return 1; } @@ -277,6 +284,9 @@ static void handle_put_settings(const nl_api_ctx_t *ctx, const nl_http_request_t err = "invalid ssid"; } else if ((pr = get_string(j, "password", 0, NL_PASS_LEN - 1, &b)) < 0) { err = "invalid password"; + } else if (pr == 1 && strlen(b) != 0 && strlen(b) < 8) { + /* M2: WPA2 passwords must be empty (open network) or 8..64 chars */ + err = "invalid password"; } else { if (ctx->wifi_configure(a, pr == 1 ? b : "") != 0) reply_error(resp, 503, "wifi busy"); @@ -353,7 +363,8 @@ void nl_api_handle(const nl_api_ctx_t *ctx, const nl_http_request_t *req, nl_api memset(resp, 0, sizeof *resp); ctx->get_settings(&s); - authed = ctx->open_access() || token_ok(req->auth, s.token); + /* M4: a truncated Authorization value must never accidentally match a shorter token. */ + authed = ctx->open_access() || (!req->auth_too_long && token_ok(req->auth, s.token)); if (strcmp(req->path, P_STATE) == 0) { if (s.auth_all && !authed) diff --git a/src/net/nl_http.c b/src/net/nl_http.c index ae44b42..af92669 100644 --- a/src/net/nl_http.c +++ b/src/net/nl_http.c @@ -85,8 +85,10 @@ nl_http_parse_t nl_http_parse(const char *buf, size_t len, nl_http_request_t *re if (line_end - v > 7 && strncasecmp(v, "Bearer ", 7) == 0) v += 7; n = (size_t)(line_end - v); - if (n >= sizeof req->auth) + if (n >= sizeof req->auth) { + req->auth_too_long = true; /* M4: never silently truncate a bearer token */ n = sizeof req->auth - 1; + } memcpy(req->auth, v, n); req->auth[n] = '\0'; } diff --git a/src/net/nl_http.h b/src/net/nl_http.h index afb4174..a58d523 100644 --- a/src/net/nl_http.h +++ b/src/net/nl_http.h @@ -1,6 +1,7 @@ #ifndef NL_HTTP_H #define NL_HTTP_H +#include #include #include "config/nl_settings.h" @@ -15,6 +16,7 @@ typedef struct { nl_http_method_t method; char path[64]; /* without query string */ char auth[NL_TOKEN_LEN]; /* Authorization value without "Bearer ", "" if absent */ + bool auth_too_long; /* Authorization value did not fit into auth[] (truncated) */ const char *body; /* points into the caller's buffer */ size_t body_len; } nl_http_request_t; diff --git a/test/test_api.c b/test/test_api.c index 2e6fd05..aedac44 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -106,6 +106,7 @@ static void test_post_validation(void) {"{\"brightness\":101}", "invalid brightness"}, {"{\"brightness\":1.5}", "invalid brightness"}, {"{\"brightness\":\"50\"}", "invalid brightness"}, + {"{\"brightness\":1e300}", "invalid brightness"}, /* M5: range check before the (long) cast */ {"{\"color\":{\"r\":1,\"g\":2}}", "invalid color"}, {"{\"color\":{\"r\":1,\"g\":2,\"b\":256}}", "invalid color"}, {"{\"color_temp\":999}", "invalid color_temp"}, @@ -171,6 +172,20 @@ static void test_auth_all_protects_device_state(void) TEST_ASSERT_EQUAL_INT(200, resp.status); } +static void test_auth_too_long_is_unauthenticated(void) +{ + /* M4: even a token that starts with the real one but was truncated at NL_TOKEN_LEN - 1 + * (i.e. the header carried more than that) must not authenticate. */ + nl_http_request_t r; + memset(&r, 0, sizeof r); + r.method = NL_HTTP_GET; + strcpy(r.path, "/api/v1/system/settings"); + strcpy(r.auth, TOKEN); /* would match on its own */ + r.auth_too_long = true; + nl_api_handle(&fake_ctx, &r, &resp); + TEST_ASSERT_EQUAL_INT(401, resp.status); +} + /* --- system/info --- */ static void test_info_is_open(void) { @@ -240,6 +255,14 @@ static void test_settings_wifi(void) TEST_ASSERT_EQUAL_STRING("invalid ssid", error_text()); call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"x\",\"password\":5}"); TEST_ASSERT_EQUAL_STRING("invalid password", error_text()); + /* M2: password must be empty (open network) or 8..64 chars, never 1..7 */ + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"x\",\"password\":\"short\"}"); + TEST_ASSERT_EQUAL_INT(400, resp.status); + TEST_ASSERT_EQUAL_STRING("invalid password", error_text()); + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"x\",\"password\":\"\"}"); + TEST_ASSERT_EQUAL_INT(202, resp.status); + call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"x\",\"password\":\"12345678\"}"); + TEST_ASSERT_EQUAL_INT(202, resp.status); fake_wifi_result = -1; call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"x\"}"); TEST_ASSERT_EQUAL_INT(503, resp.status); @@ -329,6 +352,7 @@ int main(void) RUN_TEST(test_post_settings_fields); RUN_TEST(test_post_save_failure); RUN_TEST(test_auth_all_protects_device_state); + RUN_TEST(test_auth_too_long_is_unauthenticated); RUN_TEST(test_info_is_open); RUN_TEST(test_info_with_sta_ip_and_error); RUN_TEST(test_settings_auth); diff --git a/test/test_http.c b/test/test_http.c index 572bbaf..05f1863 100644 --- a/test/test_http.c +++ b/test/test_http.c @@ -1,3 +1,4 @@ +#include #include #include "unity.h" #include "net/nl_http.h" @@ -41,6 +42,21 @@ static void test_headers_case_insensitive_and_bearer(void) TEST_ASSERT_EQUAL(NL_HTTP_PUT, r.method); TEST_ASSERT_EQUAL_STRING("abc123", r.auth); TEST_ASSERT_EQUAL_UINT(2, r.body_len); + TEST_ASSERT_FALSE(r.auth_too_long); +} + +static void test_auth_too_long_sets_flag(void) +{ + /* M4: token max is 64 chars (NL_TOKEN_LEN - 1); one char beyond that must be flagged + * rather than silently truncated and possibly matching by accident. */ + char hdr[128]; + nl_http_request_t r; + char token[66]; + memset(token, 'a', sizeof token - 1); + token[sizeof token - 1] = '\0'; /* 65 chars: one more than NL_TOKEN_LEN - 1 */ + snprintf(hdr, sizeof hdr, "GET / HTTP/1.1\r\nAuthorization: %s\r\n\r\n", token); + TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, parse(hdr, &r)); + TEST_ASSERT_TRUE(r.auth_too_long); } static void test_plain_token_and_query_string(void) @@ -108,6 +124,7 @@ int main(void) RUN_TEST(test_simple_get); RUN_TEST(test_post_body_arrives_in_parts); RUN_TEST(test_headers_case_insensitive_and_bearer); + RUN_TEST(test_auth_too_long_sets_flag); RUN_TEST(test_plain_token_and_query_string); RUN_TEST(test_other_method); RUN_TEST(test_body_too_large); From aae7a7462dec630fa8526006e524ed608ad3240d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 23:52:18 +0200 Subject: [PATCH 043/150] =?UTF-8?q?docs:=20Checkliste=20Netzwerk=20erg?= =?UTF-8?q?=C3=A4nzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M9: Hardware-Checkliste um Einträge für C1 (verschachteltes JSON, 400, Lampe läuft weiter), C2 (Onboarding, 60 s warten, Reboot + Router-Neustart -> AP darf nicht wiederkommen) und einen Hinweis auf den Build ohne NINLIGHT_WIFI_SSID für Onboarding-Tests ergänzt. `bri 30`-Eintrag nennt jetzt explizit den REST-Weg (`{"brightness":30}`) neben der Konsole. README: klargestellt, dass der Onboarding-Aufruf ohne Authorization-Header nur im unkonfigurierten AP funktioniert - im Fallback-AP (Zugangsdaten bereits vorhanden) ist der Token-Header wie bei jedem anderen Endpunkt erforderlich. --- README.md | 3 +++ docs/hardware-checklist.md | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 93f9499..1a1e698 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,9 @@ Verfügbare Befehle in der Konsole: `help`. -d '{"type":"wifi","ssid":"MeinWLAN","password":"geheim123"}' curl http://192.168.4.1:3333/api/v1/system/info # wifi.sta_ip / wifi.error + Der Aufruf ohne `Authorization`-Header funktioniert nur im unkonfigurierten AP (noch keine + gespeicherten Zugangsdaten). Im Fallback-AP (Router war > 5 min weg, Lampe hat aber bereits + Zugangsdaten) ist der Token-Header wie bei jedem anderen Endpunkt erforderlich. Nach Erfolg schließt der AP nach 60 s. Ohne Verbindung für 5 min öffnet die Lampe den AP zusätzlich (Fallback). - REST auf Port 3333, Präfix `/api/v1`: `GET|POST device/state`, `GET system/info`, `GET|PUT system/settings` und `PUT system/command` (beide mit Header `Authorization: `, Default-Token `aabbccddeeff0011223344`). - Beispiel: `curl -X POST http://ninlight-681f.local:3333/api/v1/device/state -d '{"on":true,"brightness":40,"color_temp":2700}'` diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index c79c307..aebb849 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -27,13 +27,16 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [ ] `dns-sd -B _ninlight._tcp` findet `ninlight-`, `dns-sd -L` zeigt TXT type/fw/mac/name - [ ] Bruno: alle Device-Requests wirken sichtbar, `Get State` spiegelt den Zustand - [ ] Ungültiger Body (`{"brightness":0}`) → 400, `system/settings` ohne Token → 401 -- [ ] `bri 30` per REST, sofort `Reboot` per REST → nach dem Boot 30 % (Flush) +- [ ] `bri 30` per REST (`{"brightness":30}`) oder Konsole `bri 30`, dann sofort `Reboot` per REST → nach dem Boot 30 % (Flush) - [ ] Werksreset → AP `ninlight-` sichtbar, Lampe pulsiert, Beitritt mit `ninlight` - [ ] Onboarding mit falschem Passwort → `system/info` zeigt `wifi.error = "auth"`, AP bleibt - [ ] Onboarding korrekt → `202`, Lampe verbindet, Pulsieren stoppt, AP nach 60 s weg - [ ] Router aus > 5 min → AP erscheint (`mode=apsta`); Router an → Verbindung, AP nach 10 s weg - [ ] Namensänderung per REST → TXT `name=` aktualisiert (`dns-sd -L`) - [ ] `auth_all` an → `GET device/state` ohne Token 401, mit Token 200 +- [ ] Onboarding-Tests: Build ohne `NINLIGHT_WIFI_SSID` in `config.local.cmake` +- [ ] C1: `curl -d '[[[[[[[[[[[[[[[[[[[[' http://:3333/api/v1/device/state` → 400, Lampe läuft unverändert weiter (kein Absturz/Reboot) +- [ ] C2: Onboarding ok, 60 s warten, dann Reboot und Router-Neustart → AP `ninlight-` darf nicht wiederkommen (Scan am Mac) ## Später (Lampen) - [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) From 6754cdfb6644bf368d4d46847b1bb84ef1a94431 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 00:32:37 +0200 Subject: [PATCH 044/150] fix: RSSI mit Vorzeichen melden --- src/net/nl_wifi_w600.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/net/nl_wifi_w600.c b/src/net/nl_wifi_w600.c index cc8d6e6..03fb0f2 100644 --- a/src/net/nl_wifi_w600.c +++ b/src/net/nl_wifi_w600.c @@ -304,7 +304,9 @@ void nl_wifi_status(nl_wifi_status_t *out) ip4addr_ntoa_r(ip_2_ip4(&e->ip_addr), out->sta_ip, sizeof out->sta_ip); memset(&bss, 0, sizeof bss); tls_wifi_get_current_bss(&bss); - out->rssi = (int)(int8_t)bss.rssi; + /* W600 SDK reports RSSI magnitude as unsigned; EVK ninIOT measured ~-50 dBm as 50. + * Report negative for 0 < bss.rssi <= 127, unchanged for > 127 (already two's complement). */ + out->rssi = (bss.rssi > 0 && bss.rssi <= 127) ? -(int)bss.rssi : (int)(int8_t)bss.rssi; } if (out->sta_ip[0]) copy(out->ip, out->sta_ip, sizeof out->ip); From ed374fc4906c562f34e08b22af3eba07b098f85f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 00:32:42 +0200 Subject: [PATCH 045/150] docs: Netzwerk-Checkliste nach EVK-Session --- docs/hardware-checklist.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index aebb849..4a031b9 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -23,20 +23,20 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F) ## Netzwerk & REST (Plan 2) -- [ ] Mit Build-Credentials: Boot → `net` zeigt `mode=sta` und eine IP, RSSI plausibel (−30 … −90) -- [ ] `dns-sd -B _ninlight._tcp` findet `ninlight-`, `dns-sd -L` zeigt TXT type/fw/mac/name -- [ ] Bruno: alle Device-Requests wirken sichtbar, `Get State` spiegelt den Zustand -- [ ] Ungültiger Body (`{"brightness":0}`) → 400, `system/settings` ohne Token → 401 -- [ ] `bri 30` per REST (`{"brightness":30}`) oder Konsole `bri 30`, dann sofort `Reboot` per REST → nach dem Boot 30 % (Flush) -- [ ] Werksreset → AP `ninlight-` sichtbar, Lampe pulsiert, Beitritt mit `ninlight` -- [ ] Onboarding mit falschem Passwort → `system/info` zeigt `wifi.error = "auth"`, AP bleibt -- [ ] Onboarding korrekt → `202`, Lampe verbindet, Pulsieren stoppt, AP nach 60 s weg +- [x] Mit Build-Credentials: Boot → `net` zeigt `mode=sta` und eine IP, RSSI plausibel (−30 … −90) — 2026-09-12 +- [x] `dns-sd -B _ninlight._tcp` findet `ninlight-`, `dns-sd -L` zeigt TXT type/fw/mac/name — 2026-09-12 +- [x] Bruno: alle Device-Requests wirken sichtbar, `Get State` spiegelt den Zustand — 2026-09-12 +- [x] Ungültiger Body (`{"brightness":0}`) → 400, `system/settings` ohne Token → 401 — 2026-09-12 +- [x] `bri 30` per REST (`{"brightness":30}`) oder Konsole `bri 30`, dann sofort `Reboot` per REST → nach dem Boot 30 % (Flush) — 2026-09-12 +- [x] Werksreset → AP `ninlight-` sichtbar, Lampe pulsiert, Beitritt mit `ninlight` — 2026-09-12 +- [x] Onboarding mit falschem Passwort → `system/info` zeigt `wifi.error = "auth"`, AP bleibt — 2026-09-12 +- [x] Onboarding korrekt → `202`, Lampe verbindet, Pulsieren stoppt, AP nach 60 s weg — 2026-09-12 - [ ] Router aus > 5 min → AP erscheint (`mode=apsta`); Router an → Verbindung, AP nach 10 s weg -- [ ] Namensänderung per REST → TXT `name=` aktualisiert (`dns-sd -L`) -- [ ] `auth_all` an → `GET device/state` ohne Token 401, mit Token 200 -- [ ] Onboarding-Tests: Build ohne `NINLIGHT_WIFI_SSID` in `config.local.cmake` -- [ ] C1: `curl -d '[[[[[[[[[[[[[[[[[[[[' http://:3333/api/v1/device/state` → 400, Lampe läuft unverändert weiter (kein Absturz/Reboot) -- [ ] C2: Onboarding ok, 60 s warten, dann Reboot und Router-Neustart → AP `ninlight-` darf nicht wiederkommen (Scan am Mac) +- [x] Namensänderung per REST → TXT `name=` aktualisiert (`dns-sd -L`) — 2026-09-12 +- [x] `auth_all` an → `GET device/state` ohne Token 401, mit Token 200 — 2026-09-12 +- [x] Onboarding-Tests: Build ohne `NINLIGHT_WIFI_SSID` in `config.local.cmake` — 2026-09-12 +- [x] C1: `curl -d '[[[[[[[[[[[[[[[[[[[[' http://:3333/api/v1/device/state` → 400, Lampe läuft unverändert weiter (kein Absturz/Reboot) — 2026-09-12 +- [x] C2: Onboarding ok, 60 s warten, dann Reboot und Router-Neustart → AP `ninlight-` darf nicht wiederkommen (Scan am Mac) — 2026-09-12 (Reboot geprüft, Router-Neustart offen) ## Später (Lampen) - [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) From 6e9fb09455c60116b999485264917613352631f2 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 00:55:20 +0200 Subject: [PATCH 046/150] =?UTF-8?q?test:=20Settings-Format=20l=C3=A4ngento?= =?UTF-8?q?lerant=20machen,=20ota=5Fpending=20erg=C3=A4nzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit read_rec() akzeptiert jetzt Records mit hdr.length <= sizeof(nl_settings_t): CRC wird nur über die tatsächlich geschriebenen Bytes geprüft, fehlende Felder bleiben auf 0 (Default). Ein zu langer (unbekannter) Record wird weiter abgelehnt. Ein durch massive Kürzung "leer" gelesener Record (fade_in_ms, fade_out_ms und token allesamt 0) fällt komplett auf die Defaults zurück. Neues Feld ota_pending (nach fade_out_ms) für den kommenden OTA-Ablauf. --- src/config/nl_settings.c | 35 ++++++++++++++++++++++++---- src/config/nl_settings.h | 1 + test/test_settings.c | 49 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/config/nl_settings.c b/src/config/nl_settings.c index 38138c3..d2947fd 100644 --- a/src/config/nl_settings.c +++ b/src/config/nl_settings.c @@ -44,14 +44,35 @@ void nl_settings_init(uint32_t addr_a, uint32_t addr_b) s_addr[1] = addr_b; } +/* Reads a record and validates it. `r->data` is filled from the on-flash payload, + * zero-extended when an older/shorter layout is found (hdr.length < sizeof(nl_settings_t)): + * the CRC only covers the bytes actually written, so it is checked over the stored length, + * not over the current struct size. A record whose length exceeds the current struct + * (an unknown, newer layout) is rejected outright. */ static bool read_rec(int slot, rec_t *r) { - if (nl_flash_read(s_addr[slot], r, sizeof *r) != 0) + rec_hdr_t hdr; + uint32_t crc, stored_crc; + + if (nl_flash_read(s_addr[slot], &hdr, sizeof hdr) != 0) return false; - if (r->hdr.magic != NL_SETTINGS_MAGIC || r->hdr.version != NL_SETTINGS_VERSION || - r->hdr.length != sizeof(nl_settings_t)) + if (hdr.magic != NL_SETTINGS_MAGIC || hdr.version != NL_SETTINGS_VERSION || + hdr.length > sizeof(nl_settings_t)) return false; - return nl_crc32(0, r, offsetof(rec_t, crc)) == r->crc; + + memset(r, 0, sizeof *r); + r->hdr = hdr; + if (nl_flash_read(s_addr[slot] + sizeof hdr, &r->data, hdr.length) != 0) + return false; + if (nl_flash_read(s_addr[slot] + sizeof hdr + hdr.length, &stored_crc, sizeof stored_crc) != 0) + return false; + + crc = nl_crc32(0, &hdr, sizeof hdr); + crc = nl_crc32(crc, &r->data, hdr.length); + if (crc != stored_crc) + return false; + r->crc = stored_crc; + return true; } bool nl_settings_load(nl_settings_t *out) @@ -71,6 +92,12 @@ bool nl_settings_load(nl_settings_t *out) out->wifi_pass[NL_PASS_LEN - 1] = '\0'; out->token[NL_TOKEN_LEN - 1] = '\0'; out->name[NL_NAME_LEN - 1] = '\0'; + /* A record so severely truncated that fade_in_ms/fade_out_ms/token all read back as + * zero carries no usable data; treat it as if nothing was stored at all. */ + if (out->fade_in_ms == 0 && out->fade_out_ms == 0 && out->token[0] == '\0') { + nl_settings_defaults(out); + return false; + } return true; } diff --git a/src/config/nl_settings.h b/src/config/nl_settings.h index 7c58b5e..5b5f5ba 100644 --- a/src/config/nl_settings.h +++ b/src/config/nl_settings.h @@ -18,6 +18,7 @@ typedef struct { uint8_t power_on_behaviour; /* nl_pob_t */ uint16_t fade_in_ms; uint16_t fade_out_ms; + uint8_t ota_pending; /* set before an OTA; the next boot restores the last state */ } nl_settings_t; void nl_settings_defaults(nl_settings_t *s); diff --git a/test/test_settings.c b/test/test_settings.c index 7ccab69..e596e41 100644 --- a/test/test_settings.c +++ b/test/test_settings.c @@ -4,6 +4,7 @@ #include "config/nl_flash_layout.h" #include "config/nl_settings.h" #include "light/nl_light_types.h" +#include "util/nl_crc.h" void setUp(void) { @@ -84,6 +85,51 @@ static void test_corrupt_newest_falls_back(void) TEST_ASSERT_EQUAL_STRING("first", out.name); } +static void test_load_accepts_shorter_record(void) +{ + nl_settings_t in, out; + uint32_t crc; + /* simulate a record written by an older firmware: length = sizeof - 4, payload truncated */ + in = sample("alt"); + nl_settings_save(&in); + { + uint16_t shorter = (uint16_t)(sizeof(nl_settings_t) - 4); + memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 6], &shorter, 2); /* header.length */ + crc = nl_crc32(0, &fake_flash_mem[NL_FL_SETTINGS_A], 12 + shorter); + memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 12 + shorter], &crc, 4); + } + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_STRING("alt", out.name); + TEST_ASSERT_EQUAL_STRING("ninHome", out.wifi_ssid); +} + +static void test_load_rejects_longer_record(void) +{ + nl_settings_t in = sample("x"), out; + uint16_t longer = (uint16_t)(sizeof(nl_settings_t) + 1); + uint32_t crc; + nl_settings_save(&in); + memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 6], &longer, 2); /* header.length */ + /* recompute the CRC over the (bogus, longer) length so the length check itself is + * what rejects the record, not a CRC mismatch left over from the original length */ + crc = nl_crc32(0, &fake_flash_mem[NL_FL_SETTINGS_A], 12 + longer); + memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 12 + longer], &crc, 4); + TEST_ASSERT_FALSE(nl_settings_load(&out)); /* unknown newer layout -> defaults */ +} + +static void test_ota_pending_default_and_roundtrip(void) +{ + nl_settings_t in, out; + TEST_ASSERT_FALSE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_UINT8(0, out.ota_pending); + + in = sample("ota"); + in.ota_pending = 1; + TEST_ASSERT_EQUAL_INT(0, nl_settings_save(&in)); + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_UINT8(1, out.ota_pending); +} + static void test_erase(void) { nl_settings_t s = sample("x"), out; @@ -100,6 +146,9 @@ int main(void) RUN_TEST(test_alternates_slots); RUN_TEST(test_power_loss_keeps_previous); RUN_TEST(test_corrupt_newest_falls_back); + RUN_TEST(test_load_accepts_shorter_record); + RUN_TEST(test_load_rejects_longer_record); + RUN_TEST(test_ota_pending_default_and_roundtrip); RUN_TEST(test_erase); return UNITY_END(); } From bf8f76ecc3d79b9e874c73772c67f5747e65c985 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 00:56:38 +0200 Subject: [PATCH 047/150] build: Firmware-Version bei jedem Build neu erzeugen NINLIGHT_FW_VERSION kam bisher aus einer configure-Zeit-Variable, die nur bei erneutem CMake-Lauf aktualisiert wurde - nach einem Commit ohne Re-Konfiguration zeigte Banner/system/info also eine veraltete Git-Version. cmake/write_version.cmake schreibt build/generated/nl_version.h bei jedem Build frisch (add_custom_target ALL), app_main.c/nl_app_api.c/nl_mdns.c inkludieren das Makro jetzt von dort statt aus target_compile_definitions. --- CMakeLists.txt | 5 +++-- cmake/version.cmake | 18 +++++++----------- cmake/write_version.cmake | 16 ++++++++++++++++ src/app_main.c | 2 ++ src/net/nl_mdns.c | 2 ++ src/sys/nl_app_api.c | 2 ++ 6 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 cmake/write_version.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ea31c4e..4e0f5a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,12 +87,13 @@ add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS set_target_properties(${NL_FW} PROPERTIES SUFFIX ".elf") target_compile_definitions(${NL_FW} PRIVATE NINLIGHT_DEVICE_ID="${NINLIGHT_DEVICE}" - NINLIGHT_FW_VERSION="${NINLIGHT_FW_VERSION}" NINLIGHT_BUILD_DATE="${NINLIGHT_BUILD_DATE}" NINLIGHT_TOKEN_DEFAULT="${NINLIGHT_TOKEN}" NINLIGHT_WIFI_SSID="${NINLIGHT_WIFI_SSID}" NINLIGHT_WIFI_PASSWORD="${NINLIGHT_WIFI_PASSWORD}" NINLIGHT_AP_PASSWORD="${NINLIGHT_AP_PASSWORD}") +target_include_directories(${NL_FW} PRIVATE ${CMAKE_BINARY_DIR}/generated) +add_dependencies(${NL_FW} nl_version) target_link_options(${NL_FW} PRIVATE -static -nostartfiles -T${SDK_DIR}/ld/link_w600_2m.ld -Wl,-Map=${CMAKE_BINARY_DIR}/${NL_FW}.map) @@ -100,4 +101,4 @@ target_link_libraries(${NL_FW} PRIVATE -Wl,--whole-archive ${SDK_DIR}/Lib/GNU/libwlan.a -Wl,--no-whole-archive m) nl_add_images(${NL_FW}) -message(STATUS "ninlight ${NINLIGHT_FW_VERSION} for ${NINLIGHT_DEVICE}") +message(STATUS "ninlight for ${NINLIGHT_DEVICE} (version regenerated at every build, see ${NL_VERSION_HEADER})") diff --git a/cmake/version.cmake b/cmake/version.cmake index f0aa868..1a9917c 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -1,12 +1,8 @@ -execute_process( - COMMAND git describe --tags --always --dirty - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE NINLIGHT_FW_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) -if(NOT NINLIGHT_FW_VERSION) - set(NINLIGHT_FW_VERSION "0.0.0-dev") -endif() -string(REGEX REPLACE "^v" "" NINLIGHT_FW_VERSION "${NINLIGHT_FW_VERSION}") -# The OTA trailer reserves 16 bytes incl. terminator for the version. -string(SUBSTRING "${NINLIGHT_FW_VERSION}" 0 15 NINLIGHT_FW_VERSION) +# The version is regenerated on every build so the OTA trailer and system/info match the binary. +set(NL_VERSION_HEADER ${CMAKE_BINARY_DIR}/generated/nl_version.h) +add_custom_target(nl_version ALL + BYPRODUCTS ${NL_VERSION_HEADER} + COMMAND ${CMAKE_COMMAND} -DOUT=${NL_VERSION_HEADER} -DSRC=${CMAKE_SOURCE_DIR} + -P ${CMAKE_SOURCE_DIR}/cmake/write_version.cmake + COMMENT "Updating nl_version.h") string(TIMESTAMP NINLIGHT_BUILD_DATE "%Y-%m-%d") diff --git a/cmake/write_version.cmake b/cmake/write_version.cmake new file mode 100644 index 0000000..3137306 --- /dev/null +++ b/cmake/write_version.cmake @@ -0,0 +1,16 @@ +execute_process(COMMAND git describe --tags --always --dirty WORKING_DIRECTORY ${SRC} + OUTPUT_VARIABLE V OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) +if(NOT V) + set(V "0.0.0-dev") +endif() +string(REGEX REPLACE "^v" "" V "${V}") +string(SUBSTRING "${V}" 0 15 V) +set(CONTENT "#define NINLIGHT_FW_VERSION \"${V}\"\n") +if(EXISTS ${OUT}) + file(READ ${OUT} OLD) +else() + set(OLD "") +endif() +if(NOT OLD STREQUAL CONTENT) + file(WRITE ${OUT} "${CONTENT}") +endif() diff --git a/src/app_main.c b/src/app_main.c index 8392ba9..93db9e6 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -21,6 +21,8 @@ #include "sys/nl_app_api.h" #include "sys/nl_console.h" +#include "nl_version.h" + #define NL_LIGHT_PRIO 30 #define NL_APP_PRIO 40 diff --git a/src/net/nl_mdns.c b/src/net/nl_mdns.c index 70f8c87..73ae544 100644 --- a/src/net/nl_mdns.c +++ b/src/net/nl_mdns.c @@ -11,6 +11,8 @@ #include "net/nl_names.h" #include "net/nl_wifi.h" +#include "nl_version.h" + #define NL_MDNS_TTL 120 static bool s_inited; diff --git a/src/sys/nl_app_api.c b/src/sys/nl_app_api.c index 08cc389..c761bcc 100644 --- a/src/sys/nl_app_api.c +++ b/src/sys/nl_app_api.c @@ -12,6 +12,8 @@ #include "sys/nl_app.h" #include "sys/nl_app_api.h" +#include "nl_version.h" + static void *json_alloc(size_t n) { return tls_mem_alloc(n); } static void json_free(void *p) { tls_mem_free(p); } From e5145626b02a9c0a90f039cb9a3bf80f4b5688ac Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 00:57:08 +0200 Subject: [PATCH 048/150] fix: reset_reason aus system/info entfernen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Das Feld lieferte ohnehin nur den festen String "unknown" - kein SDK-Aufruf lieferte je einen echten Reset-Grund. README/hardware-checklist erwähnten es nicht. --- src/net/nl_api.c | 1 - test/test_api.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/net/nl_api.c b/src/net/nl_api.c index 9e78be2..6116406 100644 --- a/src/net/nl_api.c +++ b/src/net/nl_api.c @@ -245,7 +245,6 @@ static void handle_info(const nl_api_ctx_t *ctx, nl_api_response_t *resp) cJSON_AddNullToObject(w, "error"); cJSON_AddNumberToObject(o, "uptime", in.uptime_s); cJSON_AddNumberToObject(o, "free_heap", in.free_heap); - cJSON_AddStringToObject(o, "reset_reason", "unknown"); if (in.last_ota_error) cJSON_AddStringToObject(o, "last_ota_error", in.last_ota_error); else diff --git a/test/test_api.c b/test/test_api.c index aedac44..cf79187 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -207,7 +207,7 @@ static void test_info_is_open(void) TEST_ASSERT_TRUE(cJSON_IsNull(cJSON_GetObjectItem(w, "error"))); TEST_ASSERT_EQUAL_INT(123, field("uptime")->valueint); TEST_ASSERT_EQUAL_INT(40000, field("free_heap")->valueint); - TEST_ASSERT_EQUAL_STRING("unknown", cJSON_GetStringValue(field("reset_reason"))); + TEST_ASSERT_NULL(cJSON_GetObjectItem(doc, "reset_reason")); TEST_ASSERT_TRUE(cJSON_IsNull(field("last_ota_error"))); } From 8805aa764d92edfa940978ae32c05f5a10d80fc4 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 00:59:40 +0200 Subject: [PATCH 049/150] =?UTF-8?q?fix:=20H=C3=A4rtungen=20aus=20dem=20Pla?= =?UTF-8?q?n-2-Review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - wm_internal_fls.c: TLS_DBGPRT_ERR ist in diesem Build wegkompiliert, daher printf() für die fehlgeschlagene s_fls2_lock-Anlage; PATCHES.md korrigiert. - nl_wifi_w600.c: der SoftAP-Backstop (Zerstören eines unerwarteten SDK-Softap) lief bisher innerhalb von lock()/unlock() - tls_wifi_softap_destroy() und clear_softap_param() greifen aber auf SDK-Zustand zu, nicht auf s_sm/die Credential-Puffer, die s_lock eigentlich schützt. Jetzt wird im Lock nur noch ein lokales Flag gesetzt, die SDK-Aufrufe laufen danach. - clear_softap_param() liest TLS_PARAM_ID_WPROTOCOL jetzt aus SRAM (FALSE) statt Flash (TRUE) - das Schreiben bleibt weiterhin geflasht. - app_main.c füttert den Watchdog erst, wenn Licht-, Netz- und HTTP-Task jeweils seit dem letzten Feed einen Fortschritt gemeldet haben. Dafür bekommen nl_wifi_w600.c (nl_net_beat) und nl_http_server.c (nl_http_beat) je einen volatile Heartbeat-Zähler; der HTTP-Listen-Socket bekommt ein SO_RCVTIMEO von 2 s auf accept(), damit er auch ohne Anfragen tickt. --- sdk/PATCHES.md | 6 ++--- .../Drivers/internalflash/wm_internal_fls.c | 2 +- src/app_main.c | 26 ++++++++++++++++--- src/net/nl_http_server.c | 14 ++++++++++ src/net/nl_http_server.h | 7 +++++ src/net/nl_wifi.h | 4 +++ src/net/nl_wifi_w600.c | 21 ++++++++++----- 7 files changed, 65 insertions(+), 15 deletions(-) diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index 14027b5..46ff0ea 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -28,6 +28,6 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ 5. `Platform/Drivers/internalflash/wm_internal_fls.c`: Semaphor `s_fls2_lock` serialisiert den Flash-Pfad oberhalb von 1 MB (Pin-Umschaltung via `tls_spifls_cs/ck/di/do_switch` war ungeschützt). T1 (Plan 2 final review): `tls_fls_init()` ignorierte den Rückgabewert von - `tls_os_sem_create(&s_fls2_lock, 1)` stillschweigend - jetzt eine `TLS_DBGPRT_ERR`-Meldung, - falls das Anlegen fehlschlägt (dann bliebe der Bereich oberhalb von 1 MB unprotokolliert - ungeschützt). + `tls_os_sem_create(&s_fls2_lock, 1)` stillschweigend - jetzt eine `printf()`-Meldung + (`TLS_DBGPRT_ERR` ist in diesem Build wegkompiliert), falls das Anlegen fehlschlägt + (dann bliebe der Bereich oberhalb von 1 MB unprotokolliert ungeschützt). diff --git a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c index d3d8e9d..8b6f5c8 100644 --- a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c +++ b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c @@ -1174,7 +1174,7 @@ int tls_fls_init(void) return TLS_FLS_STATUS_ENOMEM; } if (tls_os_sem_create(&s_fls2_lock, 1) != TLS_OS_SUCCESS) - TLS_DBGPRT_ERR("create semaphore @s_fls2_lock fail! flash access above 1 MB is unprotected!\n"); + printf("wm_internal_fls: s_fls2_lock create failed\r\n"); inside_fls = fls; diff --git a/src/app_main.c b/src/app_main.c index 93db9e6..82bee8b 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -148,8 +148,9 @@ static void app_task(void *arg) nl_engine_cfg_t cfg; nl_light_state_t last, initial, pending; bool have_last, counter_cleared = false; + bool light_alive = false, net_alive = false, http_alive = false; uint8_t boots; - uint32_t boot_ms, beat_seen; + uint32_t boot_ms, light_seen, net_seen, http_seen; (void)arg; nl_flash_init(); @@ -200,7 +201,9 @@ static void app_task(void *arg) nl_http_server_start(nl_app_api_ctx()); boot_ms = nl_app_now_ms(); - beat_seen = s_light_beat; + light_seen = s_light_beat; + net_seen = nl_net_beat; + http_seen = nl_http_beat; for (;;) { uint32_t now; bool due; @@ -232,8 +235,23 @@ static void app_task(void *arg) } } - if (s_light_beat != beat_seen) { - beat_seen = s_light_beat; + /* Feed the watchdog only once the light, network and HTTP tasks have each made + * progress since the last feed - a single stuck task (e.g. nl_app_lock() held + * forever) must still starve the watchdog, not just the light task. */ + if (s_light_beat != light_seen) { + light_seen = s_light_beat; + light_alive = true; + } + if (nl_net_beat != net_seen) { + net_seen = nl_net_beat; + net_alive = true; + } + if (nl_http_beat != http_seen) { + http_seen = nl_http_beat; + http_alive = true; + } + if (light_alive && net_alive && http_alive) { + light_alive = net_alive = http_alive = false; tls_watchdog_clr(); } } diff --git a/src/net/nl_http_server.c b/src/net/nl_http_server.c index 0e89f45..89e7cf0 100644 --- a/src/net/nl_http_server.c +++ b/src/net/nl_http_server.c @@ -15,6 +15,10 @@ static char s_out[NL_API_BODY_MAX + 160]; static nl_api_response_t s_resp; static const nl_api_ctx_t *s_ctx; +volatile uint32_t nl_http_beat; + +#define NL_HTTP_ACCEPT_TIMEOUT_S 2 + static void send_all(int fd, const char *p, size_t n) { while (n) { @@ -95,17 +99,27 @@ static void server_task(void *arg) tls_os_time_delay(HZ); continue; } + { + /* Without traffic accept() would otherwise block forever, so this timeout is + * what keeps nl_http_beat ticking (and the watchdog fed) while idle. */ + struct timeval tv; + tv.tv_sec = NL_HTTP_ACCEPT_TIMEOUT_S; + tv.tv_usec = 0; + setsockopt(ls, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + } for (;;) { struct sockaddr_in peer; socklen_t plen = sizeof peer; int fd = accept(ls, (struct sockaddr *)&peer, &plen); if (fd < 0) { + nl_http_beat++; /* accept() timeout (or a transient error): task is still alive */ tls_os_time_delay(HZ / 10); continue; } s_resp.after = NL_API_AFTER_NONE; serve(fd); closesocket(fd); + nl_http_beat++; if (s_resp.after == NL_API_AFTER_REBOOT) { tls_os_time_delay(HZ / 2); /* let the TCP stack flush the response */ nl_app_reboot(); diff --git a/src/net/nl_http_server.h b/src/net/nl_http_server.h index 326e3dc..32b5f84 100644 --- a/src/net/nl_http_server.h +++ b/src/net/nl_http_server.h @@ -1,10 +1,17 @@ #ifndef NL_HTTP_SERVER_H #define NL_HTTP_SERVER_H +#include + #include "net/nl_api.h" #define NL_HTTP_PORT 3333 +/* Incremented once per accept() timeout and once per handled connection; app_main's + * watchdog feed checks it to make sure the HTTP task is still alive, not just the light + * task. The listen socket's 2 s accept timeout keeps this ticking even without requests. */ +extern volatile uint32_t nl_http_beat; + /* Serves one connection at a time (listen backlog 4, 5 s receive timeout). */ void nl_http_server_start(const nl_api_ctx_t *ctx); diff --git a/src/net/nl_wifi.h b/src/net/nl_wifi.h index e64a30d..6b3a11e 100644 --- a/src/net/nl_wifi.h +++ b/src/net/nl_wifi.h @@ -15,6 +15,10 @@ typedef struct { const char *error; /* result of the last onboarding test or NULL */ } nl_wifi_status_t; +/* Incremented once per net_task loop iteration; app_main's watchdog feed checks it to make + * sure the network task is still alive, not just the light task. */ +extern volatile uint32_t nl_net_beat; + /* Starts the network task. An empty ssid starts the onboarding AP. Strings are copied. */ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass); /* Starts an onboarding test with new credentials (from the HTTP task). 0 = queued. */ diff --git a/src/net/nl_wifi_w600.c b/src/net/nl_wifi_w600.c index 03fb0f2..4ccf356 100644 --- a/src/net/nl_wifi_w600.c +++ b/src/net/nl_wifi_w600.c @@ -31,6 +31,8 @@ static char s_ap_pass[NL_PASS_LEN]; static bool s_ap_create_pending; /* M3: tls_wifi_softap_create() failed, retry it */ static uint32_t s_ap_create_retry_at; +volatile uint32_t nl_net_beat; + static void lock(void) { tls_os_mutex_acquire(s_lock, 0); } static void unlock(void) { tls_os_mutex_release(s_lock); } @@ -58,7 +60,7 @@ static void clear_softap_param(void) { u8 proto = 0; - tls_param_get(TLS_PARAM_ID_WPROTOCOL, &proto, TRUE); + tls_param_get(TLS_PARAM_ID_WPROTOCOL, &proto, FALSE); if (proto & IEEE80211_MODE_AP) { proto &= (u8)~IEEE80211_MODE_AP; tls_param_set(TLS_PARAM_ID_WPROTOCOL, &proto, TRUE); @@ -163,7 +165,7 @@ static void net_task(void *arg) void *msg = NULL; nl_wifi_actions_t ev, tick; uint32_t m = 0, now; - bool got_ip_event = false, connected_now, ap_wanted; + bool got_ip_event = false, connected_now, ap_wanted, unexpected_softap; memset(&ev, 0, sizeof ev); if (tls_os_queue_receive(s_queue, &msg, 0, HZ / 10) == TLS_OS_SUCCESS) @@ -185,15 +187,19 @@ static void net_task(void *arg) /* I1: a late/stray IP-up must not announce mDNS unless the SM actually settled on STA_CONNECTED. */ connected_now = s_sm.state == NL_WIFI_ST_STA_CONNECTED; ap_wanted = s_sm.ap_up; - if (m == (MSG_NETIF | NETIF_WIFI_SOFTAP_SUCCESS) && !ap_wanted) { - /* C2 backstop: the SDK brought its own softap up behind our back (e.g. the - * clear in nl_wifi_start raced a pending sys_net_status_changed()) while our - * state machine does not want an AP. Tear it down and clear the param again. */ + /* C2 backstop: the SDK brought its own softap up behind our back (e.g. the clear in + * nl_wifi_start raced a pending sys_net_status_changed()) while our state machine + * does not want an AP. Only latch the fact here; tls_wifi_softap_destroy() and + * clear_softap_param() touch the SDK's own state and must not run while s_lock + * (which only guards s_sm and the credential buffers) is held. */ + unexpected_softap = m == (MSG_NETIF | NETIF_WIFI_SOFTAP_SUCCESS) && !ap_wanted; + unlock(); + + if (unexpected_softap) { printf("wifi: unexpected SDK softap while AP should be down, destroying it\r\n"); tls_wifi_softap_destroy(); clear_softap_param(); } - unlock(); if (m & MSG_NETIF) printf("wifi: netif event %u\r\n", (unsigned)(m & 0xFFu)); @@ -207,6 +213,7 @@ static void net_task(void *arg) printf("wifi: retrying AP create\r\n"); start_ap(); } + nl_net_beat++; } } From c96e0442c0a9e5a485c9b939b9867ba0aba37a52 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:11:18 +0200 Subject: [PATCH 050/150] =?UTF-8?q?fix:=20fehlende=20Felder=20in=20gek?= =?UTF-8?q?=C3=BCrzten=20Settings-Records=20auf=20echte=20Defaults=20setze?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit read_rec() füllte den Puffer bisher mit memset(0) statt mit nl_settings_defaults() vor, bevor die tatsächlich vorhandenen hdr.length Bytes darübergeschrieben werden. Ein gekürzter Record, dessen Länge vor fade_in_ms/fade_out_ms endet, aber token noch enthält, lief damit mit fade_in_ms=0/fade_out_ms=0 statt der spezifizierten 500 ms - genau der Fall, für den die Längentoleranz gedacht war. Die davor eingebaute "leerer Record" Sonderbehandlung in nl_settings_load() (fade_in_ms==0 && fade_out_ms==0 && token[0]=='\0' -> volle Defaults) ist dadurch überflüssig geworden und entfernt. test_load_accepts_shorter_record kürzt jetzt bis vor fade_in_ms und prüft explizit auf die echten Defaults (500/500/0) statt nur auf Felder vor dem gekürzten Bereich. test_load_rejects_longer_record verzichtet auf die tote CRC-Neuberechnung, da read_rec() zu lange Records schon anhand der Länge verwirft, bevor Payload oder CRC gelesen werden. --- src/config/nl_settings.c | 22 ++++++++++------------ test/test_settings.c | 19 ++++++++++++------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/config/nl_settings.c b/src/config/nl_settings.c index d2947fd..aa41d47 100644 --- a/src/config/nl_settings.c +++ b/src/config/nl_settings.c @@ -44,11 +44,15 @@ void nl_settings_init(uint32_t addr_a, uint32_t addr_b) s_addr[1] = addr_b; } -/* Reads a record and validates it. `r->data` is filled from the on-flash payload, - * zero-extended when an older/shorter layout is found (hdr.length < sizeof(nl_settings_t)): - * the CRC only covers the bytes actually written, so it is checked over the stored length, - * not over the current struct size. A record whose length exceeds the current struct - * (an unknown, newer layout) is rejected outright. */ +/* Reads a record and validates it. `r->data` is pre-filled with nl_settings_defaults() + * and only the bytes actually on flash (hdr.length, possibly less than sizeof(nl_settings_t) + * for a record written by an older firmware) are copied over that. Fields a shorter record + * never had (e.g. fade_in_ms/fade_out_ms/token added later) therefore keep their real + * default (500 ms, NINLIGHT_TOKEN_DEFAULT, ...), not zero - a zeroed fade time or an empty + * token would be a behaviour change, not just "unset". The CRC only covers the bytes + * actually written, so it is checked over the stored length, not over the current struct + * size. A record whose length exceeds the current struct (an unknown, newer layout) is + * rejected outright, before either payload or CRC is even read. */ static bool read_rec(int slot, rec_t *r) { rec_hdr_t hdr; @@ -60,8 +64,8 @@ static bool read_rec(int slot, rec_t *r) hdr.length > sizeof(nl_settings_t)) return false; - memset(r, 0, sizeof *r); r->hdr = hdr; + nl_settings_defaults(&r->data); if (nl_flash_read(s_addr[slot] + sizeof hdr, &r->data, hdr.length) != 0) return false; if (nl_flash_read(s_addr[slot] + sizeof hdr + hdr.length, &stored_crc, sizeof stored_crc) != 0) @@ -92,12 +96,6 @@ bool nl_settings_load(nl_settings_t *out) out->wifi_pass[NL_PASS_LEN - 1] = '\0'; out->token[NL_TOKEN_LEN - 1] = '\0'; out->name[NL_NAME_LEN - 1] = '\0'; - /* A record so severely truncated that fade_in_ms/fade_out_ms/token all read back as - * zero carries no usable data; treat it as if nothing was stored at all. */ - if (out->fade_in_ms == 0 && out->fade_out_ms == 0 && out->token[0] == '\0') { - nl_settings_defaults(out); - return false; - } return true; } diff --git a/test/test_settings.c b/test/test_settings.c index e596e41..67a6949 100644 --- a/test/test_settings.c +++ b/test/test_settings.c @@ -1,3 +1,4 @@ +#include #include #include "unity.h" #include "fake_flash.h" @@ -89,11 +90,14 @@ static void test_load_accepts_shorter_record(void) { nl_settings_t in, out; uint32_t crc; - /* simulate a record written by an older firmware: length = sizeof - 4, payload truncated */ + /* simulate a record written by a firmware that predates fade_in_ms/fade_out_ms/ + * ota_pending: length ends right before fade_in_ms. Those fields must come back as + * their real defaults (500 ms / 0), not as zero just because the record didn't carry + * them - that's the whole point of the length tolerance. */ in = sample("alt"); nl_settings_save(&in); { - uint16_t shorter = (uint16_t)(sizeof(nl_settings_t) - 4); + uint16_t shorter = (uint16_t)offsetof(nl_settings_t, fade_in_ms); memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 6], &shorter, 2); /* header.length */ crc = nl_crc32(0, &fake_flash_mem[NL_FL_SETTINGS_A], 12 + shorter); memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 12 + shorter], &crc, 4); @@ -101,19 +105,20 @@ static void test_load_accepts_shorter_record(void) TEST_ASSERT_TRUE(nl_settings_load(&out)); TEST_ASSERT_EQUAL_STRING("alt", out.name); TEST_ASSERT_EQUAL_STRING("ninHome", out.wifi_ssid); + TEST_ASSERT_EQUAL_UINT16(500, out.fade_in_ms); + TEST_ASSERT_EQUAL_UINT16(500, out.fade_out_ms); + TEST_ASSERT_EQUAL_UINT8(0, out.ota_pending); } static void test_load_rejects_longer_record(void) { nl_settings_t in = sample("x"), out; uint16_t longer = (uint16_t)(sizeof(nl_settings_t) + 1); - uint32_t crc; nl_settings_save(&in); + /* read_rec() rejects hdr.length > sizeof(nl_settings_t) outright, before it even reads + * the payload or the CRC - so the stored CRC is irrelevant here and deliberately left + * as whatever nl_settings_save() wrote for the original (shorter) length. */ memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 6], &longer, 2); /* header.length */ - /* recompute the CRC over the (bogus, longer) length so the length check itself is - * what rejects the record, not a CRC mismatch left over from the original length */ - crc = nl_crc32(0, &fake_flash_mem[NL_FL_SETTINGS_A], 12 + longer); - memcpy(&fake_flash_mem[NL_FL_SETTINGS_A + 12 + longer], &crc, 4); TEST_ASSERT_FALSE(nl_settings_load(&out)); /* unknown newer layout -> defaults */ } From e3be3e84c10f9cd8898d0be097d231fd145ed105 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:16:04 +0200 Subject: [PATCH 051/150] =?UTF-8?q?feat:=20OTA-Trailer=20bauen=20und=20pr?= =?UTF-8?q?=C3=BCfen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + src/util/nl_fwup_trailer.c | 74 +++++++++++++++++++++++++++++ src/util/nl_fwup_trailer.h | 28 +++++++++++ test/CMakeLists.txt | 1 + test/test_fwup_trailer.c | 95 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 src/util/nl_fwup_trailer.c create mode 100644 src/util/nl_fwup_trailer.h create mode 100644 test/test_fwup_trailer.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e0f5a1..2285a04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ set(NL_FW_SOURCES src/app_main.c src/util/nl_sha256.c src/util/nl_crc.c + src/util/nl_fwup_trailer.c src/board/nl_board.c src/light/nl_color.c src/light/nl_engine.c diff --git a/src/util/nl_fwup_trailer.c b/src/util/nl_fwup_trailer.c new file mode 100644 index 0000000..c372c5f --- /dev/null +++ b/src/util/nl_fwup_trailer.c @@ -0,0 +1,74 @@ +#include "util/nl_fwup_trailer.h" + +#include + +/* NB: This layout is mirrored in Python at tools/mkimage/nl_mkimage.py (Task 3), + since there are two languages involved. Keep both in sync. */ + +#define OFF_MAGIC 0u +#define OFF_VERSION 4u +#define OFF_DEVICE 5u +#define OFF_FW 21u +#define OFF_SHA 37u + +static bool copy_field(uint8_t *dst, const char *src, uint32_t len) +{ + uint32_t n = (uint32_t)strlen(src); + if (n >= len) + return false; + memset(dst, 0, len); + memcpy(dst, src, n); + return true; +} + +bool nl_trailer_build(uint8_t out[NL_TRAILER_SIZE], const char *device, const char *version, const uint8_t sha256[32]) +{ + memset(out, 0xFF, NL_TRAILER_SIZE); + memcpy(&out[OFF_MAGIC], "NINL", 4); + out[OFF_VERSION] = NL_TRAILER_VERSION; + if (!copy_field(&out[OFF_DEVICE], device, NL_TRAILER_DEVICE_LEN) || + !copy_field(&out[OFF_FW], version, NL_TRAILER_VERSION_LEN)) + return false; + memcpy(&out[OFF_SHA], sha256, 32); + return true; +} + +static bool terminated(const uint8_t *field, uint32_t len) +{ + for (uint32_t i = 0; i < len; i++) + if (field[i] == 0) + return true; + return false; +} + +nl_trailer_err_t nl_trailer_verify(const uint8_t *tail, uint32_t tail_len, const char *device, const uint8_t sha256[32]) +{ + if (tail == NULL || tail_len < NL_TRAILER_SIZE) + return NL_TRAILER_ERR_SIZE; + if (memcmp(&tail[OFF_MAGIC], "NINL", 4) != 0) + return NL_TRAILER_ERR_MAGIC; + if (tail[OFF_VERSION] != NL_TRAILER_VERSION) + return NL_TRAILER_ERR_VERSION; + if (!terminated(&tail[OFF_DEVICE], NL_TRAILER_DEVICE_LEN) || + strcmp((const char *)&tail[OFF_DEVICE], device) != 0) + return NL_TRAILER_ERR_DEVICE; + if (memcmp(&tail[OFF_SHA], sha256, 32) != 0) + return NL_TRAILER_ERR_HASH; + return NL_TRAILER_OK; +} + +void nl_trailer_version(const uint8_t *tail, char *dst) +{ + memcpy(dst, &tail[OFF_FW], NL_TRAILER_VERSION_LEN); + dst[NL_TRAILER_VERSION_LEN - 1] = '\0'; +} + +const char *nl_trailer_err_name(nl_trailer_err_t e) +{ + switch (e) { + case NL_TRAILER_ERR_DEVICE: return "device_mismatch"; + case NL_TRAILER_ERR_HASH: return "hash"; + case NL_TRAILER_OK: return NULL; + default: return "trailer"; + } +} diff --git a/src/util/nl_fwup_trailer.h b/src/util/nl_fwup_trailer.h new file mode 100644 index 0000000..73e4aa0 --- /dev/null +++ b/src/util/nl_fwup_trailer.h @@ -0,0 +1,28 @@ +#ifndef NL_FWUP_TRAILER_H +#define NL_FWUP_TRAILER_H + +#include +#include + +#define NL_TRAILER_SIZE 512u +#define NL_TRAILER_VERSION 1u +#define NL_TRAILER_DEVICE_LEN 16u +#define NL_TRAILER_VERSION_LEN 16u + +typedef enum { + NL_TRAILER_OK = 0, + NL_TRAILER_ERR_SIZE, + NL_TRAILER_ERR_MAGIC, + NL_TRAILER_ERR_VERSION, + NL_TRAILER_ERR_DEVICE, + NL_TRAILER_ERR_HASH +} nl_trailer_err_t; + +/* "NINL" | version | device[16] | fw_version[16] | sha256[32] | 0xFF padding */ +bool nl_trailer_build(uint8_t out[NL_TRAILER_SIZE], const char *device, const char *version, const uint8_t sha256[32]); +nl_trailer_err_t nl_trailer_verify(const uint8_t *tail, uint32_t tail_len, const char *device, const uint8_t sha256[32]); +void nl_trailer_version(const uint8_t *tail, char *dst); /* dst: NL_TRAILER_VERSION_LEN bytes */ +/* Maps to the last_ota_error vocabulary; NULL for NL_TRAILER_OK. */ +const char *nl_trailer_err_name(nl_trailer_err_t e); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6b98238..1c4a5c8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -48,3 +48,4 @@ nl_test(test_api test_api.c fake_api_ctx.c ${NL_SRC}/net/nl_api.c ${NL_SRC}/boar ${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/config/nl_settings.c ${NL_SRC}/util/nl_crc.c fake_flash.c) target_link_libraries(test_api PRIVATE nl_cjson) nl_test(test_wifi_sm test_wifi_sm.c ${NL_SRC}/net/nl_wifi_sm.c) +nl_test(test_fwup_trailer test_fwup_trailer.c ${NL_SRC}/util/nl_fwup_trailer.c) diff --git a/test/test_fwup_trailer.c b/test/test_fwup_trailer.c new file mode 100644 index 0000000..04e02a6 --- /dev/null +++ b/test/test_fwup_trailer.c @@ -0,0 +1,95 @@ +#include +#include "unity.h" +#include "util/nl_fwup_trailer.h" + +void setUp(void) {} +void tearDown(void) {} + +static uint8_t HASH[32]; +static uint8_t tail[NL_TRAILER_SIZE]; + +static void fill_hash(void) +{ + for (int i = 0; i < 32; i++) + HASH[i] = (uint8_t)(i * 3 + 1); +} + +static void test_build_layout(void) +{ + fill_hash(); + TEST_ASSERT_TRUE(nl_trailer_build(tail, "e27_rgb", "1.2.3", HASH)); + TEST_ASSERT_EQUAL_MEMORY("NINL", tail, 4); + TEST_ASSERT_EQUAL_UINT8(NL_TRAILER_VERSION, tail[4]); + TEST_ASSERT_EQUAL_STRING("e27_rgb", (const char *)&tail[5]); + TEST_ASSERT_EQUAL_STRING("1.2.3", (const char *)&tail[21]); + TEST_ASSERT_EQUAL_UINT8_ARRAY(HASH, &tail[37], 32); + for (uint32_t i = 69; i < NL_TRAILER_SIZE; i++) + TEST_ASSERT_EQUAL_HEX8(0xFF, tail[i]); +} + +static void test_build_rejects_too_long_strings(void) +{ + fill_hash(); + TEST_ASSERT_FALSE(nl_trailer_build(tail, "0123456789abcdef", "1.0", HASH)); /* 16 chars, no room for NUL */ + TEST_ASSERT_FALSE(nl_trailer_build(tail, "evk", "0123456789abcdef", HASH)); + TEST_ASSERT_TRUE(nl_trailer_build(tail, "012345678901234", "012345678901234", HASH)); /* 15 chars fit */ +} + +static void test_verify_accepts_matching(void) +{ + fill_hash(); + nl_trailer_build(tail, "evk", "1.0.0", HASH); + TEST_ASSERT_EQUAL(NL_TRAILER_OK, nl_trailer_verify(tail, NL_TRAILER_SIZE, "evk", HASH)); +} + +static void test_verify_rejects(void) +{ + char version[NL_TRAILER_VERSION_LEN]; + uint8_t other[32]; + + fill_hash(); + memcpy(other, HASH, 32); + other[5] ^= 0x01; + nl_trailer_build(tail, "evk", "1.0.0", HASH); + + TEST_ASSERT_EQUAL(NL_TRAILER_ERR_SIZE, nl_trailer_verify(tail, 256, "evk", HASH)); + TEST_ASSERT_EQUAL(NL_TRAILER_ERR_DEVICE, nl_trailer_verify(tail, NL_TRAILER_SIZE, "gu10_cw", HASH)); + TEST_ASSERT_EQUAL(NL_TRAILER_ERR_HASH, nl_trailer_verify(tail, NL_TRAILER_SIZE, "evk", other)); + nl_trailer_version(tail, version); + TEST_ASSERT_EQUAL_STRING("1.0.0", version); + + tail[4] = 2; + TEST_ASSERT_EQUAL(NL_TRAILER_ERR_VERSION, nl_trailer_verify(tail, NL_TRAILER_SIZE, "evk", HASH)); + tail[0] = 'X'; + TEST_ASSERT_EQUAL(NL_TRAILER_ERR_MAGIC, nl_trailer_verify(tail, NL_TRAILER_SIZE, "evk", HASH)); +} + +static void test_verify_needs_terminated_device_string(void) +{ + fill_hash(); + nl_trailer_build(tail, "evk", "1.0.0", HASH); + memset(&tail[5], 'a', NL_TRAILER_DEVICE_LEN); /* no NUL inside the field */ + TEST_ASSERT_EQUAL(NL_TRAILER_ERR_DEVICE, nl_trailer_verify(tail, NL_TRAILER_SIZE, "aaaaaaaaaaaaaaaa", HASH)); +} + +static void test_error_names(void) +{ + TEST_ASSERT_EQUAL_STRING("trailer", nl_trailer_err_name(NL_TRAILER_ERR_MAGIC)); + TEST_ASSERT_EQUAL_STRING("trailer", nl_trailer_err_name(NL_TRAILER_ERR_SIZE)); + TEST_ASSERT_EQUAL_STRING("trailer", nl_trailer_err_name(NL_TRAILER_ERR_VERSION)); + TEST_ASSERT_EQUAL_STRING("device_mismatch", nl_trailer_err_name(NL_TRAILER_ERR_DEVICE)); + TEST_ASSERT_EQUAL_STRING("hash", nl_trailer_err_name(NL_TRAILER_ERR_HASH)); + TEST_ASSERT_NULL(nl_trailer_err_name(NL_TRAILER_OK)); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_build_layout); + RUN_TEST(test_build_rejects_too_long_strings); + RUN_TEST(test_verify_accepts_matching); + RUN_TEST(test_verify_rejects); + RUN_TEST(test_verify_needs_terminated_device_string); + RUN_TEST(test_error_names); + return UNITY_END(); +} From 76b9c3fef1465a0d4e4c2310869f14e00a1e936d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:21:42 +0200 Subject: [PATCH 052/150] =?UTF-8?q?test:=20nl=5Fmkimage=20f=C3=BCr=20OTA-T?= =?UTF-8?q?railer-Erzeugung=20mit=20Tests=20anlegen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: nl_mkimage.py implementieren (Gegenstück zu nl_fwup_trailer.c aus Task 2) --- tools/mkimage/nl_mkimage.py | 48 +++++++++++++++++++ tools/mkimage/pyproject.toml | 7 +++ tools/mkimage/test_nl_mkimage.py | 55 ++++++++++++++++++++++ tools/mkimage/uv.lock | 79 ++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+) create mode 100755 tools/mkimage/nl_mkimage.py create mode 100644 tools/mkimage/pyproject.toml create mode 100644 tools/mkimage/test_nl_mkimage.py create mode 100644 tools/mkimage/uv.lock diff --git a/tools/mkimage/nl_mkimage.py b/tools/mkimage/nl_mkimage.py new file mode 100755 index 0000000..5d5afc0 --- /dev/null +++ b/tools/mkimage/nl_mkimage.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +"""Appends the ninlight OTA trailer to a `_gz.img` produced by wm_tool. + +Layout (512 bytes): "NINL" | version(1) | device[16] | fw_version[16] | sha256[32] | 0xFF padding. +The hash covers the complete `_gz.img` (T_BOOTER header + payload), which is exactly what the +firmware hashes while writing the image to flash. +""" +import argparse +import hashlib +import sys +from pathlib import Path + +TRAILER_SIZE = 512 +TRAILER_VERSION = 1 +FIELD = 16 + + +def field(value: str, name: str) -> bytes: + raw = value.encode() + if len(raw) >= FIELD: + sys.exit(f"error: {name} '{value}' is too long (max {FIELD - 1} bytes)") + return raw.ljust(FIELD, b"\x00") + + +def main() -> None: + p = argparse.ArgumentParser(description="append the ninlight OTA trailer") + p.add_argument("--image", required=True, type=Path, help="input _gz.img") + p.add_argument("--device", required=True, help="device id, e.g. e27_rgb") + p.add_argument("--version", required=True, help="firmware version") + p.add_argument("--out", required=True, type=Path, help="output _ota.img") + a = p.parse_args() + + data = a.image.read_bytes() + if len(data) > TRAILER_SIZE and data[-TRAILER_SIZE : -TRAILER_SIZE + 4] == b"NINL": + sys.exit("error: image already carries a trailer") + + trailer = bytearray(b"\xff" * TRAILER_SIZE) + trailer[0:4] = b"NINL" + trailer[4] = TRAILER_VERSION + trailer[5:21] = field(a.device, "device") + trailer[21:37] = field(a.version, "version") + trailer[37:69] = hashlib.sha256(data).digest() + a.out.write_bytes(data + bytes(trailer)) + print(f"{a.out} ({len(data) + TRAILER_SIZE} bytes, device={a.device}, version={a.version})") + + +if __name__ == "__main__": + main() diff --git a/tools/mkimage/pyproject.toml b/tools/mkimage/pyproject.toml new file mode 100644 index 0000000..aeee3bd --- /dev/null +++ b/tools/mkimage/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "nl-mkimage" +version = "0.1.0" +requires-python = ">=3.11" + +[dependency-groups] +dev = ["pytest>=8"] diff --git a/tools/mkimage/test_nl_mkimage.py b/tools/mkimage/test_nl_mkimage.py new file mode 100644 index 0000000..9609de1 --- /dev/null +++ b/tools/mkimage/test_nl_mkimage.py @@ -0,0 +1,55 @@ +import hashlib +import subprocess +import sys +from pathlib import Path + +SCRIPT = Path(__file__).with_name("nl_mkimage.py") + + +def build(tmp_path, payload=b"\x01\x02\x03" * 100, device="evk", version="1.2.3"): + src = tmp_path / "fw_gz.img" + src.write_bytes(payload) + out = tmp_path / "fw_ota.img" + subprocess.run( + [sys.executable, str(SCRIPT), "--image", str(src), "--device", device, + "--version", version, "--out", str(out)], + check=True, + ) + return src, out + + +def test_appends_512_byte_trailer(tmp_path): + src, out = build(tmp_path) + data = out.read_bytes() + assert len(data) == len(src.read_bytes()) + 512 + assert data[: len(src.read_bytes())] == src.read_bytes() + trailer = data[-512:] + assert trailer[0:4] == b"NINL" + assert trailer[4] == 1 + assert trailer[5:21].rstrip(b"\x00") == b"evk" + assert trailer[21:37].rstrip(b"\x00") == b"1.2.3" + assert trailer[37:69] == hashlib.sha256(src.read_bytes()).digest() + assert set(trailer[69:]) == {0xFF} + + +def test_rejects_long_fields(tmp_path): + src = tmp_path / "fw_gz.img" + src.write_bytes(b"x") + r = subprocess.run( + [sys.executable, str(SCRIPT), "--image", str(src), "--device", "0123456789abcdef", + "--version", "1", "--out", str(tmp_path / "o.img")], + capture_output=True, text=True, + ) + assert r.returncode != 0 + assert "device" in r.stderr + + +def test_refuses_image_that_already_has_a_trailer(tmp_path): + _, out = build(tmp_path) + r = subprocess.run( + [sys.executable, str(SCRIPT), "--image", str(out), "--device", "evk", + "--version", "1.2.3", "--out", str(tmp_path / "again.img")], + capture_output=True, text=True, + ) + assert r.returncode != 0 + assert "trailer" in r.stderr diff --git a/tools/mkimage/uv.lock b/tools/mkimage/uv.lock new file mode 100644 index 0000000..7030ede --- /dev/null +++ b/tools/mkimage/uv.lock @@ -0,0 +1,79 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "nl-mkimage" +version = "0.1.0" +source = { virtual = "." } + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [{ name = "pytest", specifier = ">=8" }] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] From fabca1a6b40f9f954098820feb08eb1c36e45b4c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:23:03 +0200 Subject: [PATCH 053/150] feat: OTA-Image mit Trailer im Build erzeugen --- cmake/image.cmake | 4 ++++ cmake/run_mkimage.cmake | 8 ++++++++ tools/build_all.sh | 1 + 3 files changed, 13 insertions(+) create mode 100644 cmake/run_mkimage.cmake diff --git a/cmake/image.cmake b/cmake/image.cmake index 7cfb8fe..8e20c0a 100644 --- a/cmake/image.cmake +++ b/cmake/image.cmake @@ -15,6 +15,10 @@ function(nl_add_images target) $ ${out}.bin COMMAND ${NL_WM_TOOL} -b ${out}.bin -sb ${SDK_DIR}/Bin/secboot.img -fc compress -it 2M -ua 100000 -ra 10100 -df -o ${out} + COMMAND ${CMAKE_COMMAND} -DVERSION_HEADER=${CMAKE_BINARY_DIR}/generated/nl_version.h + -DMKIMAGE=${CMAKE_SOURCE_DIR}/tools/mkimage/nl_mkimage.py -DIMAGE=${out}_gz.img + -DDEVICE=${NINLIGHT_DEVICE} -DOUT=${out}_ota.img + -P ${CMAKE_SOURCE_DIR}/cmake/run_mkimage.cmake COMMAND ${CMAKE_SIZE} $ COMMENT "Packing ${target}.fls and ${target}_gz.img") endfunction() diff --git a/cmake/run_mkimage.cmake b/cmake/run_mkimage.cmake new file mode 100644 index 0000000..a0ac4ec --- /dev/null +++ b/cmake/run_mkimage.cmake @@ -0,0 +1,8 @@ +file(READ ${VERSION_HEADER} V) +string(REGEX MATCH "\"([^\"]*)\"" _m "${V}") +set(VERSION "${CMAKE_MATCH_1}") +execute_process(COMMAND python3 ${MKIMAGE} --image ${IMAGE} --device ${DEVICE} --version ${VERSION} --out ${OUT} + RESULT_VARIABLE rc) +if(NOT rc EQUAL 0) + message(FATAL_ERROR "nl_mkimage failed (${rc})") +endif() diff --git a/tools/build_all.sh b/tools/build_all.sh index b0a65bf..3879b88 100755 --- a/tools/build_all.sh +++ b/tools/build_all.sh @@ -11,3 +11,4 @@ for d in "${DEVICES[@]}"; do cmake --build "$OUT/$d" done ls -1 "$OUT"/*/ninlight_*.fls +ls -1 "$OUT"/*/ninlight_*_ota.img From 4cd7fde0cde5087e6ae1f74343cc2dfd89938258 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:23:33 +0200 Subject: [PATCH 054/150] fix: ls-Zeile in build_all.sh statt Duplikat um ota.img erweitern --- tools/build_all.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/build_all.sh b/tools/build_all.sh index 3879b88..a74cf56 100755 --- a/tools/build_all.sh +++ b/tools/build_all.sh @@ -10,5 +10,4 @@ for d in "${DEVICES[@]}"; do -DCMAKE_TOOLCHAIN_FILE="$ROOT/cmake/arm-none-eabi.cmake" -DNINLIGHT_DEVICE="$d" >/dev/null cmake --build "$OUT/$d" done -ls -1 "$OUT"/*/ninlight_*.fls -ls -1 "$OUT"/*/ninlight_*_ota.img +ls -1 "$OUT"/*/ninlight_*.fls "$OUT"/*/ninlight_*_ota.img From ebee9e8e61906410169fe04940d9c4e3ddb7ae00 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:28:27 +0200 Subject: [PATCH 055/150] fix: Versions-Regex in run_mkimage.cmake an NINLIGHT_FW_VERSION verankern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vorher traf die Regex das erste Stringliteral im generierten Header, unabhängig vom Makronamen. Zusätzlich bricht ein leerer Regex-Treffer jetzt hörbar ab, statt eine leere Version stillschweigend in den OTA-Trailer zu schreiben. --- cmake/run_mkimage.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/run_mkimage.cmake b/cmake/run_mkimage.cmake index a0ac4ec..821de76 100644 --- a/cmake/run_mkimage.cmake +++ b/cmake/run_mkimage.cmake @@ -1,6 +1,9 @@ file(READ ${VERSION_HEADER} V) -string(REGEX MATCH "\"([^\"]*)\"" _m "${V}") +string(REGEX MATCH "NINLIGHT_FW_VERSION[ \t]+\"([^\"]*)\"" _m "${V}") set(VERSION "${CMAKE_MATCH_1}") +if(NOT VERSION) + message(FATAL_ERROR "run_mkimage: NINLIGHT_FW_VERSION not found in ${VERSION_HEADER}") +endif() execute_process(COMMAND python3 ${MKIMAGE} --image ${IMAGE} --device ${DEVICE} --version ${VERSION} --out ${OUT} RESULT_VARIABLE rc) if(NOT rc EQUAL 0) From aa8151c3d6a6ec31137b0ec5d09515bb5ea770dd Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:36:57 +0200 Subject: [PATCH 056/150] =?UTF-8?q?feat:=20OTA=20=C3=BCber=20HTTP=20mit=20?= =?UTF-8?q?Trailer-Pr=C3=BCfung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- sdk/PATCHES.md | 4 +- src/app_main.c | 10 ++++ src/sys/fwup_hook_default.c | 10 ---- src/sys/nl_app_api.c | 9 +-- src/sys/nl_fwup_hook.h | 3 +- src/sys/nl_ota.c | 113 ++++++++++++++++++++++++++++++++++++ src/sys/nl_ota.h | 14 +++++ 8 files changed, 146 insertions(+), 19 deletions(-) delete mode 100644 src/sys/fwup_hook_default.c create mode 100644 src/sys/nl_ota.c create mode 100644 src/sys/nl_ota.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2285a04..0fae211 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ set(NL_FW_SOURCES src/drivers/nl_sm2135eh.c src/sys/nl_console_cmd.c src/sys/nl_console.c - src/sys/fwup_hook_default.c + src/sys/nl_ota.c src/sys/sdk_stubs.c src/net/nl_names.c src/net/nl_http.c diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index 46ff0ea..1077e86 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -6,7 +6,9 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ 1. `Platform/Common/fwup/wm_fwup.c`: wolfSSL-SHA256 durch `nl_sha256_*` ersetzt, die QConnex-RSA-Prüfung `Check_OTA_Image()` durch den Hook `nl_fwup_verify_tail()` (src/sys/nl_fwup_hook.h). Das Image-Format - (Header + Payload + 512-Byte-Endblock) bleibt unverändert. + (Header + Payload + 512-Byte-Endblock) bleibt unverändert. Die starke Definition des Hooks liegt seit + Plan 3 in `src/sys/nl_ota.c` (Trailer-Prüfung über `nl_trailer_verify()`); die bisherige `weak`-Variante + `src/sys/fwup_hook_default.c` ist entfallen. 2. `Src/OS/RTOS/include/FreeRTOSConfig.h`: `configGENERATE_RUN_TIME_STATS 0`, QConnex-Debug-Hooks entfernt. 3. `undefined reference` beim Linken auf `EarlyInit` und `pre_gpio_config` (von `sdk/Platform/Sys/wm_main.c` als `extern`-Hooks erwartet, aber nur von der App bereitgestellt): diff --git a/src/app_main.c b/src/app_main.c index 82bee8b..d2dc86b 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -20,6 +20,7 @@ #include "sys/nl_app.h" #include "sys/nl_app_api.h" #include "sys/nl_console.h" +#include "sys/nl_ota.h" #include "nl_version.h" @@ -163,6 +164,14 @@ static void app_task(void *arg) nl_state_log_init(base + NL_FL_STATE_0, base + NL_FL_STATE_1); have_last = nl_state_log_load(&last); store_unlock(); + if (s_settings.ota_pending) { + /* We came back from an update attempt: restore the light the user had on. */ + s_settings.ota_pending = 0; + printf("boot after ota attempt\r\n"); + store_lock(); + (void)nl_settings_save(&s_settings); + store_unlock(); + } printf("boot #%u, settings: pob=%u fade=%u/%u, last state: %s\r\n", boots, s_settings.power_on_behaviour, s_settings.fade_in_ms, s_settings.fade_out_ms, have_last ? "yes" : "no"); @@ -184,6 +193,7 @@ static void app_task(void *arg) nl_app_factory_reset(); /* does not return */ tls_watchdog_init(20u * 1000u * 1000u); /* 20 s */ + nl_ota_init(); nl_console_start(); { diff --git a/src/sys/fwup_hook_default.c b/src/sys/fwup_hook_default.c deleted file mode 100644 index 6b4d7c4..0000000 --- a/src/sys/fwup_hook_default.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "sys/nl_fwup_hook.h" - -/* Until OTA support (plan 3) provides a strong definition, every update is rejected. */ -__attribute__((weak)) bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len) -{ - (void)sha256; - (void)tail; - (void)tail_len; - return false; -} diff --git a/src/sys/nl_app_api.c b/src/sys/nl_app_api.c index c761bcc..f6d5fc4 100644 --- a/src/sys/nl_app_api.c +++ b/src/sys/nl_app_api.c @@ -11,6 +11,7 @@ #include "net/nl_wifi.h" #include "sys/nl_app.h" #include "sys/nl_app_api.h" +#include "sys/nl_ota.h" #include "nl_version.h" @@ -88,7 +89,7 @@ static void get_info(nl_sys_info_t *out) out->wifi_error = w.error; out->uptime_s = nl_app_now_ms() / 1000u; out->free_heap = tls_mem_get_avail_heapsize(); - out->last_ota_error = NULL; + out->last_ota_error = nl_ota_last_error(); } static void factory_reset(void) { nl_app_request_factory_reset(); } @@ -100,11 +101,7 @@ static void ping(void) nl_app_unlock(); } -static int fw_update(const char *url) -{ - (void)url; - return -1; /* OTA arrives in plan 3 */ -} +static int fw_update(const char *url) { return nl_ota_start(url); } const nl_api_ctx_t *nl_app_api_ctx(void) { diff --git a/src/sys/nl_fwup_hook.h b/src/sys/nl_fwup_hook.h index ff85122..6d02275 100644 --- a/src/sys/nl_fwup_hook.h +++ b/src/sys/nl_fwup_hook.h @@ -6,7 +6,8 @@ /* Called by the SDK updater (sdk/Platform/Common/fwup/wm_fwup.c) after the image CRC * matched. sha256 covers the T_BOOTER header plus payload, tail is the 512 byte block - * appended to the image. Return true to accept the image. */ + * appended to the image. Return true to accept the image. + * Strong definition: src/sys/nl_ota.c (checks the trailer via nl_trailer_verify()). */ bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len); #endif diff --git a/src/sys/nl_ota.c b/src/sys/nl_ota.c new file mode 100644 index 0000000..906b550 --- /dev/null +++ b/src/sys/nl_ota.c @@ -0,0 +1,113 @@ +#include +#include + +#include "wm_include.h" +#include "wm_http_fwup.h" + +#include "config/nl_settings.h" +#include "sys/nl_app.h" +#include "sys/nl_fwup_hook.h" +#include "sys/nl_ota.h" +#include "util/nl_fwup_trailer.h" + +/* t_http_fwup() (sdk/Src/App/OTA/wm_http_fwup.c) has no declaration in any SDK header. */ +extern int t_http_fwup(char *url); + +#define NL_OTA_URL_MAX 256 +#define NL_OTA_STACK 2048 /* words */ +#define NL_OTA_PRIO 34 + +static char s_url[NL_OTA_URL_MAX]; +static volatile bool s_busy; +static const char * volatile s_error; +static u32 s_stack[NL_OTA_STACK]; + +void nl_ota_init(void) +{ + s_busy = false; + s_error = NULL; +} + +bool nl_ota_busy(void) { return s_busy; } +const char *nl_ota_last_error(void) { return s_error; } +void nl_ota_set_error(const char *e) { s_error = e; } + +/* Called by the SDK firmware-update path once the image has been written and hashed. + sha256 covers the whole payload (T_BOOTER header + compressed image), tail is the + 512 byte end block that used to carry the QConnex RSA signature. */ +bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len) +{ + nl_trailer_err_t e = nl_trailer_verify(tail, tail_len, NINLIGHT_DEVICE_ID, sha256); + + if (e != NL_TRAILER_OK) { + s_error = nl_trailer_err_name(e); + printf("ota: trailer rejected (%s)\r\n", s_error); + return false; + } + { + char v[NL_TRAILER_VERSION_LEN]; + nl_trailer_version(tail, v); + printf("ota: trailer ok, incoming version %s\r\n", v); + } + return true; +} + +static void ota_task(void *arg) +{ + int rc; + + (void)arg; + printf("ota: downloading %s\r\n", s_url); + rc = t_http_fwup(s_url); + /* Only reached on failure - the SDK resets the chip itself after a good image. */ + if (s_error == NULL) + s_error = "download"; + printf("ota: failed (rc=%d, %s)\r\n", rc, s_error); + { + nl_settings_t *s; + nl_app_lock(); + s = nl_app_settings(); + s->ota_pending = 0; + nl_app_unlock(); + (void)nl_app_save_settings(); + } + s_busy = false; + tls_os_task_del(NL_OTA_PRIO, NULL); +} + +int nl_ota_start(const char *url) +{ + size_t n; + + if (url == NULL || strncmp(url, "http://", 7) != 0) + return -1; + n = strlen(url); + if (n == 0 || n >= sizeof s_url) + return -1; + if (s_busy) { + s_error = "busy"; + return -1; + } + s_busy = true; + s_error = NULL; + memcpy(s_url, url, n + 1); + + /* Persist the open light state and mark the pending update before the flash gets busy. */ + nl_app_flush(); + { + nl_settings_t *s; + nl_app_lock(); + s = nl_app_settings(); + s->ota_pending = 1; + nl_app_unlock(); + (void)nl_app_save_settings(); + } + + if (tls_os_task_create(NULL, "nl_ota", ota_task, NULL, (u8 *)s_stack, sizeof(s_stack), + NL_OTA_PRIO, 0) != TLS_OS_SUCCESS) { + s_busy = false; + s_error = "busy"; + return -1; + } + return 0; +} diff --git a/src/sys/nl_ota.h b/src/sys/nl_ota.h new file mode 100644 index 0000000..e1e6c7f --- /dev/null +++ b/src/sys/nl_ota.h @@ -0,0 +1,14 @@ +#ifndef NL_OTA_H +#define NL_OTA_H + +#include + +void nl_ota_init(void); +/* Starts a download task for an http:// URL. 0 = started, -1 = busy or invalid. */ +int nl_ota_start(const char *url); +bool nl_ota_busy(void); +/* NULL, or one of: download, crc, trailer, device_mismatch, hash, busy */ +const char *nl_ota_last_error(void); +void nl_ota_set_error(const char *e); + +#endif From 37c05d2f90b6a5192515d83f6fee094911ab2ba5 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:56:58 +0200 Subject: [PATCH 057/150] =?UTF-8?q?fix:=20Fix-Runde=201=20f=C3=BCr=20Task?= =?UTF-8?q?=204=20-=20Reset=20nur=20bei=20akzeptiertem=20OTA-Image,=20h?= =?UTF-8?q?=C3=A4ngende=20Downloads/Sessions=20begrenzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdk/PATCHES.md | 39 ++++++++++++++++++++++++--- sdk/Platform/Common/fwup/wm_fwup.c | 23 ++++++++++++++-- sdk/Src/App/OTA/wm_http_fwup.c | 27 +++++++++++++++++-- src/app_main.c | 4 ++- src/net/nl_api.c | 10 ++++--- src/sys/nl_ota.c | 42 +++++++++++++++++++++--------- test/test_api.c | 4 +-- 7 files changed, 122 insertions(+), 27 deletions(-) diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index 1077e86..7329f9f 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -9,6 +9,28 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ (Header + Payload + 512-Byte-Endblock) bleibt unverändert. Die starke Definition des Hooks liegt seit Plan 3 in `src/sys/nl_ota.c` (Trailer-Prüfung über `nl_trailer_verify()`); die bisherige `weak`-Variante `src/sys/fwup_hook_default.c` ist entfallen. + Fix-Runde 1 (Task-4-Review) hat drei weitere Stellen in diesem Bereich korrigiert: + - `wm_fwup.c`, Bedingung um `nl_fwup_verify_tail()`: Checksummen- und Trailer-Prüfung liefen in + einer kurzschließenden `||`-Bedingung; bei falscher Checksumme wurde `nl_fwup_verify_tail()` nie + aufgerufen. Beide Fälle jetzt getrennt behandelt; ein Checksummenfehler ruft + `nl_ota_set_error("crc")` auf, bevor `goto request_finish` springt (sonst hätte `ota_task()` ihn + als generisches `"download"` gemeldet). + - `wm_fwup.c`, `request_finish:`-Block: `tls_sys_reset()` lief unbedingt, sobald der Transfer zu + Ende war - auch wenn `nl_fwup_verify_tail()` oder die Checksummenprüfung das Image gerade + abgelehnt hatten. Die Lampe hätte also trotz abgelehntem Image sofort neu gestartet, `ota_task()` + wäre nie zu Ende gelaufen und `last_ota_error` hätte nie `trailer`/`device_mismatch`/`hash` + gemeldet. Der Reset läuft jetzt nur noch, wenn `fwup->current_state` keinen Fehler-Bit-Satz + (`TLS_FWUP_STATE_ERROR`) trägt. + - `Src/App/OTA/wm_http_fwup.c`, `HANDLE_BODY`: Scheitert `tls_fwup_img_header_check()`, blieb + `nRetCode` auf `HTTP_CLIENT_SUCCESS` stehen, wodurch die abschließende + `if(nRetCode) socket_fwup_err(...)`-Bereinigung nie lief und die SDK-eigene fwup-Session + (`fwup->busy`) für jeden weiteren Versuch blockiert blieb. `nRetCode` wird jetzt in diesem Fall + explizit auf `HTTP_CLIENT_ERROR_BAD_HEADER` gesetzt. + - `Src/App/OTA/wm_http_fwup.c`, `SHUTDOWN_LINK`: Ein abbrechender Download sprang unbegrenzt oft + zurück nach `PREPARE_PACKET` (Resume per Range-Header) - schaltet der Server während der + Übertragung ab, hängt `t_http_fwup()` (und damit der aufrufende Task) für immer. Eine großzügige + Obergrenze (`NL_OTA_MAX_RETRY_ROUNDS`, 200 Resume-Runden) lässt `http_fwup()` danach mit + `HTTP_CLIENT_ERROR_SOCKET_TIME_OUT` zurückkehren, statt endlos zu warten. 2. `Src/OS/RTOS/include/FreeRTOSConfig.h`: `configGENERATE_RUN_TIME_STATS 0`, QConnex-Debug-Hooks entfernt. 3. `undefined reference` beim Linken auf `EarlyInit` und `pre_gpio_config` (von `sdk/Platform/Sys/wm_main.c` als `extern`-Hooks erwartet, aber nur von der App bereitgestellt): @@ -21,10 +43,19 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ Ein Task-Deadlock (z.B. ein Task, der `nl_app_lock()` hält und nie freigibt) hätte den Watchdog damit nie ausgelöst, weil der Idle-Task munter weiter gefüttert hätte. Der `tls_watchdog_clr()`- Aufruf wurde entfernt; `wfi` bleibt. Der einzige verbleibende `tls_watchdog_clr()`-Aufruf im SDK - ist in `Platform/Common/fwup/wm_fwup.c` (Firmware-Update-Task) - das ist unkritisch, da er nur - während eines laufenden OTA-Updates feuert und den `app_task`-Heartbeat in `src/app_main.c` - nicht ersetzt (der Watchdog wird sonst ausschließlich dort über den `s_light_beat`-Heartbeat - des `light_task` gefüttert). Getestet mit dem Konsolenkommando `wdtest` + ist in `Platform/Common/fwup/wm_fwup.c` (Firmware-Update-Task). + **Einschätzung aktualisiert (Task 4, Fix-Runde 1):** Als dieser Punkt geschrieben wurde, gab es noch + keinen OTA-Pfad - das Fenster war rein theoretisch. Mit Task 4 ist es real und kann durch einen + langsamen oder hängenden Download mehrere Minuten dauern: Solange `wm_fwup.c` seinen eigenen + `tls_watchdog_clr()` aufruft, wird der Watchdog gefüttert, **egal** ob `light_task`, `nl_wifi`/`nl_mdns` + oder `nl_http_server` in dieser Zeit echt hängen - der App-Task-Heartbeat-Mechanismus in + `src/app_main.c` (der genau das eigentlich verhindern soll) greift währenddessen nicht, weil der + Watchdog gar nicht bei ihm ankommt. Das ist kein Rückschritt gegenüber vorher (vor Task 4 gab es + dieses Fenster schlicht nicht), aber die bisherige Einstufung „unkritisch" war nur so lange richtig, + wie der Fall real nie eintrat. Kein Fix in dieser Runde - der Watchdog-Heartbeat müsste dafür wissen, + ob der fwup-Task selbst noch vorankommt (z. B. über einen eigenen Beat-Zähler, den `app_task` + zusätzlich zu `light`/`net`/`http` prüft); das ist ein eigenständiger Task für einen späteren Plan. + Getestet mit dem Konsolenkommando `wdtest` (src/sys/nl_console_cmd.c, src/sys/nl_console.c): es hält `nl_app_lock()` für immer, wodurch `light_task` und `app_task` blockieren und der Watchdog den Chip zurücksetzen muss. 5. `Platform/Drivers/internalflash/wm_internal_fls.c`: Semaphor `s_fls2_lock` serialisiert den Flash-Pfad diff --git a/sdk/Platform/Common/fwup/wm_fwup.c b/sdk/Platform/Common/fwup/wm_fwup.c index 3894ccc..3c9b894 100644 --- a/sdk/Platform/Common/fwup/wm_fwup.c +++ b/sdk/Platform/Common/fwup/wm_fwup.c @@ -172,6 +172,7 @@ static void tls_fwup_img_update_header(T_BOOTER* img_param) /* ninlight: SHA256 + tail verification hook instead of the QConnex RSA check (see sdk/PATCHES.md) */ #include "util/nl_sha256.h" #include "sys/nl_fwup_hook.h" +#include "sys/nl_ota.h" /* nl_ota_set_error() - see sdk/PATCHES.md point 1 */ static void fwup_scheduler(void *data) @@ -440,9 +441,20 @@ static void fwup_scheduler(void *data) printf("%02x ", bptr[i]); printf("\n"); #endif - if (org_checksum != image_checksum || !nl_fwup_verify_tail(image_sha256, RSA_SHA256, 512)) // QC_CHANGE_MK + if (org_checksum != image_checksum) // QC_CHANGE_MK { + /* ninlight: a checksum failure never reaches nl_fwup_verify_tail() below, + * so report it here explicitly - otherwise last_ota_error would fall back + * to the generic "download" instead of the more precise "crc" (see + * sdk/PATCHES.md point 1). */ TLS_DBGPRT_ERR("varify incorrect[0x%02x, but 0x%02x]\n", org_checksum, image_checksum); + nl_ota_set_error("crc"); + request->status = TLS_FWUP_REQ_STATUS_FCRC; + fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; + goto request_finish; + } + else if (!nl_fwup_verify_tail(image_sha256, RSA_SHA256, 512)) // QC_CHANGE_MK + { request->status = TLS_FWUP_REQ_STATUS_FCRC; fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; goto request_finish; @@ -478,7 +490,14 @@ request_finish: { request->complete(request, request->arg); } - if(fwup->updated_len >= (fwup->total_len)) + /* ninlight: only reset when the image was actually accepted. Before this fix the + * condition below fired unconditionally once the transfer reached its end - + * including a request that got rejected by nl_fwup_verify_tail() (trailer/device/ + * hash mismatch) or by the checksum check above (goto request_finish also lands + * here). Resetting in that case would reboot into the OLD firmware and never let + * ota_task() report the failure - so last_ota_error stayed NULL forever. See + * sdk/PATCHES.md point 1. */ + if(fwup->updated_len >= (fwup->total_len) && !(fwup->current_state & TLS_FWUP_STATE_ERROR)) { fwup_update_autoflag(); tls_sys_reset(); diff --git a/sdk/Src/App/OTA/wm_http_fwup.c b/sdk/Src/App/OTA/wm_http_fwup.c index 97be5fc..53bbd82 100644 --- a/sdk/Src/App/OTA/wm_http_fwup.c +++ b/sdk/Src/App/OTA/wm_http_fwup.c @@ -16,9 +16,15 @@ enum ota_state {PREPARE_PACKET, SETUP_LINK_AND_REQ, RECV_RSP, HANDLE_HEADER, HANDLE_BODY, SHUTDOWN_LINK, QUIT_OTA}; +/* ninlight: SHUTDOWN_LINK resumes an unfinished transfer via PREPARE_PACKET forever - a server + * that closes the connection early (or never sends the full body) used to hang this task, and + * with it the caller of t_http_fwup(), permanently (see sdk/PATCHES.md point 1). Cap the number + * of resume rounds generously so a slow-but-healthy download still completes. */ +#define NL_OTA_MAX_RETRY_ROUNDS 200 + int http_fwup(HTTPParameters ClientParams) { - char* Buffer = NULL; + char* Buffer = NULL; char token[32]; char headRange[32] = {0}; int nRetCode = 0; @@ -26,6 +32,7 @@ int http_fwup(HTTPParameters ClientParams) u32 partLen; u32 totalLen = 0; u32 recvLen = 0; + u32 retryRounds = 0; T_BOOTER *booter; #if USE_BREAK_POINT_RESUME == 0 u32 breakFlag = 0; @@ -124,6 +131,13 @@ int http_fwup(HTTPParameters ClientParams) } else { + /* ninlight: nRetCode is still HTTP_CLIENT_SUCCESS here (the read itself + * worked, only the image header is bad), so the "if(nRetCode)" cleanup + * at the end of this function used to be skipped entirely - leaving + * socket_fwup_err()/tls_fwup_exit() uncalled and the fwup session stuck + * busy for every later attempt. Force the cleanup path (see + * sdk/PATCHES.md point 1). */ + nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; now_state = QUIT_OTA; break; } @@ -191,7 +205,16 @@ int http_fwup(HTTPParameters ClientParams) { if(pHTTP){ HTTPClientCloseRequest(&pHTTP); - (recvLen == totalLen)?(now_state = QUIT_OTA):(now_state = PREPARE_PACKET); + if (recvLen == totalLen) { + now_state = QUIT_OTA; + } else if (++retryRounds > NL_OTA_MAX_RETRY_ROUNDS) { + /* ninlight: give up instead of resuming forever, see the comment above + * NL_OTA_MAX_RETRY_ROUNDS. */ + nRetCode = HTTP_CLIENT_ERROR_SOCKET_TIME_OUT; + now_state = QUIT_OTA; + } else { + now_state = PREPARE_PACKET; + } } } break; diff --git a/src/app_main.c b/src/app_main.c index d2dc86b..b497394 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -165,7 +165,9 @@ static void app_task(void *arg) have_last = nl_state_log_load(&last); store_unlock(); if (s_settings.ota_pending) { - /* We came back from an update attempt: restore the light the user had on. */ + /* We came back from an update attempt (accepted or rejected). Just clear the flag so it + * doesn't stick in flash forever; nl_boot_state() below restores the light from `last`/ + * power_on_behaviour as on any other boot, no special-casing needed here. */ s_settings.ota_pending = 0; printf("boot after ota attempt\r\n"); store_lock(); diff --git a/src/net/nl_api.c b/src/net/nl_api.c index 6116406..c53d325 100644 --- a/src/net/nl_api.c +++ b/src/net/nl_api.c @@ -336,9 +336,13 @@ static void handle_command(const nl_api_ctx_t *ctx, const nl_http_request_t *req } else if (strcmp(type, "fw_update") == 0) { if (get_string(cmd, "url", 8, 255, &url) != 1 || strncmp(url, "http://", 7) != 0) reply_error(resp, 400, "invalid url"); - else if (ctx->fw_update(url) != 0) - reply_error(resp, 501, "fw_update not available"); - else + else if (ctx->fw_update(url) != 0) { + /* fw_update() itself can genuinely fail now (busy, or a rejected URL) - 409 with the + * reason, not a blanket "not implemented". */ + nl_sys_info_t in; + ctx->get_info(&in); + reply_error(resp, 409, in.last_ota_error ? in.last_ota_error : "fw_update busy or invalid url"); + } else reply(resp, 202, "{\"status\":\"updating\"}"); } else if (strcmp(type, "reboot") == 0) { reply(resp, 200, "{\"ok\":true}"); diff --git a/src/sys/nl_ota.c b/src/sys/nl_ota.c index 906b550..60731fa 100644 --- a/src/sys/nl_ota.c +++ b/src/sys/nl_ota.c @@ -34,22 +34,30 @@ void nl_ota_set_error(const char *e) { s_error = e; } /* Called by the SDK firmware-update path once the image has been written and hashed. sha256 covers the whole payload (T_BOOTER header + compressed image), tail is the - 512 byte end block that used to carry the QConnex RSA signature. */ + 512 byte end block that used to carry the QConnex RSA signature. + NB: this runs on the SDK's own fwup_scheduler task (tls_fwup_init(), stack + FWUP_TASK_STK_SIZE*4, ~2 KB) - NOT on the 8 KB nl_ota task stack. Keep this hook minimal: + one short printf, no extra locals (a version buffer used to sit here and was removed). */ bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len) { nl_trailer_err_t e = nl_trailer_verify(tail, tail_len, NINLIGHT_DEVICE_ID, sha256); - if (e != NL_TRAILER_OK) { + if (e != NL_TRAILER_OK) s_error = nl_trailer_err_name(e); - printf("ota: trailer rejected (%s)\r\n", s_error); - return false; - } - { - char v[NL_TRAILER_VERSION_LEN]; - nl_trailer_version(tail, v); - printf("ota: trailer ok, incoming version %s\r\n", v); - } - return true; + printf("ota: trailer %s\r\n", e == NL_TRAILER_OK ? "ok" : s_error); + return e == NL_TRAILER_OK; +} + +/* Logs the URL without any "user:pass@" userinfo, so credentials embedded in the URL never + reach the console. s_url is validated by nl_ota_start() to start with "http://". */ +static void log_url(const char *url) +{ + const char *at = strchr(url + 7, '@'); + + if (at != NULL) + printf("ota: downloading http://%s\r\n", at + 1); + else + printf("ota: downloading %s\r\n", url); } static void ota_task(void *arg) @@ -57,7 +65,7 @@ static void ota_task(void *arg) int rc; (void)arg; - printf("ota: downloading %s\r\n", s_url); + log_url(s_url); rc = t_http_fwup(s_url); /* Only reached on failure - the SDK resets the chip itself after a good image. */ if (s_error == NULL) @@ -82,7 +90,7 @@ int nl_ota_start(const char *url) if (url == NULL || strncmp(url, "http://", 7) != 0) return -1; n = strlen(url); - if (n == 0 || n >= sizeof s_url) + if (n <= 7 || n >= sizeof s_url) /* n == 7 is bare "http://" with no host */ return -1; if (s_busy) { s_error = "busy"; @@ -105,6 +113,14 @@ int nl_ota_start(const char *url) if (tls_os_task_create(NULL, "nl_ota", ota_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_OTA_PRIO, 0) != TLS_OS_SUCCESS) { + /* The task never ran, so clear the ota_pending flag we just persisted above - otherwise + * the next boot wrongly logs "boot after ota attempt" for an update that never started. */ + nl_settings_t *s; + nl_app_lock(); + s = nl_app_settings(); + s->ota_pending = 0; + nl_app_unlock(); + (void)nl_app_save_settings(); s_busy = false; s_error = "busy"; return -1; diff --git a/test/test_api.c b/test/test_api.c index cf79187..3ff2981 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -323,8 +323,8 @@ static void test_commands(void) TEST_ASSERT_EQUAL_INT(1, fake_factory_calls); call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"fw_update\",\"url\":\"http://x/y.img\"}}"); - TEST_ASSERT_EQUAL_INT(501, resp.status); - TEST_ASSERT_EQUAL_STRING("fw_update not available", error_text()); + TEST_ASSERT_EQUAL_INT(409, resp.status); + TEST_ASSERT_EQUAL_STRING("fw_update busy or invalid url", error_text()); TEST_ASSERT_EQUAL_STRING("http://x/y.img", fake_fw_url); fake_fw_result = 0; call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"fw_update\",\"url\":\"http://x/y.img\"}}"); From 2d5095574d4edbb63d6d59281e2bcb8cbfbac2f3 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 02:04:58 +0200 Subject: [PATCH 058/150] =?UTF-8?q?fix:=20Fix-Runde=202=20f=C3=BCr=20Task?= =?UTF-8?q?=204=20-=20Ablehnung=20sofort=20statt=20nach=20voller=20Resume-?= =?UTF-8?q?Schleife,=20Reset-Guard=20auch=20bei=20OOM=20greifen=20lassen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdk/PATCHES.md | 18 ++++++++++++++++++ sdk/Platform/Common/fwup/wm_fwup.c | 8 ++++++++ sdk/Src/App/OTA/wm_http_fwup.c | 11 ++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index 7329f9f..c7b9c22 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -31,6 +31,24 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ Übertragung ab, hängt `t_http_fwup()` (und damit der aufrufende Task) für immer. Eine großzügige Obergrenze (`NL_OTA_MAX_RETRY_ROUNDS`, 200 Resume-Runden) lässt `http_fwup()` danach mit `HTTP_CLIENT_ERROR_SOCKET_TIME_OUT` zurückkehren, statt endlos zu warten. + + Fix-Runde 2 (Re-Review von Fix-Runde 1) hat zwei Nachwirkungen der obigen Änderungen behoben: + - `Src/App/OTA/wm_http_fwup.c`, `HANDLE_BODY`: Lehnt `socket_fwup_recv()` einen Chunk ab (Checksummen- + /Trailer-Fehler oder ein anderer fataler fwup-Fehler), sprang der Code bisher nach `SHUTDOWN_LINK` + - da `recvLen` dabei nie den vollen `totalLen` erreicht, startete das eine komplette + Resume-Schleife (bis zu `NL_OTA_MAX_RETRY_ROUNDS` = 200 Range-Requests), deren Daten die fwup-Task + wegen des bereits gesetzten Fehler-Bits (siehe unten) jedes Mal sofort verwarf. `t_http_fwup()` kam + also erst nach Minuten mit dem richtigen Fehler zurück, `s_busy`/`ota_pending` standen so lange. + Springt jetzt direkt nach `QUIT_OTA` (mit `nRetCode = ret`, damit die `socket_fwup_err()`- + Bereinigung am Funktionsende trotzdem läuft) - der Abbruch ist damit sofort sichtbar, nicht erst + nach der vollen Resume-Schleife. + - `Platform/Common/fwup/wm_fwup.c`, Trailer-Puffer-Allokation (`RSA_SHA256 = tls_mem_alloc(512)`): + Schlug das fehl, sprang der Code nach `request_finish`, ohne ein Fehler-Bit zu setzen. Mit dem + Fix-Runde-1-Reset-Guard (siehe oben, `!(fwup->current_state & TLS_FWUP_STATE_ERROR)`) wäre das der + letzte verbliebene Weg gewesen, an `nl_fwup_verify_tail()` vorbei trotzdem zu resetten - der + Trailer wäre dann nie geprüft worden. Setzt jetzt `TLS_FWUP_STATE_ERROR_MEM` / + `TLS_FWUP_REQ_STATUS_FMEM` (wie die andere Allokationsprüfung in dieser Funktion) und + `nl_ota_set_error("download")` (kein spezifischerer Wert aus dem Vokabular passt hier). 2. `Src/OS/RTOS/include/FreeRTOSConfig.h`: `configGENERATE_RUN_TIME_STATS 0`, QConnex-Debug-Hooks entfernt. 3. `undefined reference` beim Linken auf `EarlyInit` und `pre_gpio_config` (von `sdk/Platform/Sys/wm_main.c` als `extern`-Hooks erwartet, aber nur von der App bereitgestellt): diff --git a/sdk/Platform/Common/fwup/wm_fwup.c b/sdk/Platform/Common/fwup/wm_fwup.c index 3c9b894..de54dc9 100644 --- a/sdk/Platform/Common/fwup/wm_fwup.c +++ b/sdk/Platform/Common/fwup/wm_fwup.c @@ -418,6 +418,14 @@ static void fwup_scheduler(void *data) RSA_SHA256 = tls_mem_alloc(left); if(!RSA_SHA256) { + /* ninlight: this used to jump to request_finish without setting any + * error bit. With the C1 fix (see below / sdk/PATCHES.md point 1) the + * request_finish reset only fires when no error bit is set - so this + * was the one remaining way to reach a reset with the trailer never + * actually checked. Report it like the other allocation failure above. */ + request->status = TLS_FWUP_REQ_STATUS_FMEM; + fwup->current_state |= TLS_FWUP_STATE_ERROR_MEM; + nl_ota_set_error("download"); /* no more specific value applies */ goto request_finish; } while (left > 0) diff --git a/sdk/Src/App/OTA/wm_http_fwup.c b/sdk/Src/App/OTA/wm_http_fwup.c index 53bbd82..4e7f0db 100644 --- a/sdk/Src/App/OTA/wm_http_fwup.c +++ b/sdk/Src/App/OTA/wm_http_fwup.c @@ -181,7 +181,16 @@ int http_fwup(HTTPParameters ClientParams) // Send received data to fwup thread and deal with socket issues. s8 ret = socket_fwup_recv(0, p, ERR_OK); if(ret != ERR_OK){ - now_state = SHUTDOWN_LINK; + /* ninlight: socket_fwup_recv() rejected this chunk (the fwup task hit a + * checksum/trailer mismatch or another fatal error, see wm_fwup.c) - recvLen + * never advances past this point, so going through SHUTDOWN_LINK used to start + * a full Range-resume retry (up to NL_OTA_MAX_RETRY_ROUNDS rounds) whose data + * the fwup task would just keep discarding. Bail out to QUIT_OTA directly so + * the caller gets the real error immediately instead of after minutes. Force + * nRetCode nonzero so the socket_fwup_err() cleanup below still runs. See + * sdk/PATCHES.md point 1. */ + nRetCode = ret; + now_state = QUIT_OTA; break; } else{ From 7474cd77e18d8a606574711f610a11f9f8068cd1 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 02:11:56 +0200 Subject: [PATCH 059/150] =?UTF-8?q?fix:=20Fix-Runde=203=20f=C3=BCr=20Task?= =?UTF-8?q?=204=20-=20WLAN-Aussetzer=20bleibt=20resumable=20statt=20sofort?= =?UTF-8?q?igem=20Abbruch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdk/PATCHES.md | 38 ++++++++++++++++++++++++++-------- sdk/Src/App/OTA/wm_http_fwup.c | 36 ++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index c7b9c22..175a02b 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -33,15 +33,14 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ `HTTP_CLIENT_ERROR_SOCKET_TIME_OUT` zurückkehren, statt endlos zu warten. Fix-Runde 2 (Re-Review von Fix-Runde 1) hat zwei Nachwirkungen der obigen Änderungen behoben: - - `Src/App/OTA/wm_http_fwup.c`, `HANDLE_BODY`: Lehnt `socket_fwup_recv()` einen Chunk ab (Checksummen- - /Trailer-Fehler oder ein anderer fataler fwup-Fehler), sprang der Code bisher nach `SHUTDOWN_LINK` - - da `recvLen` dabei nie den vollen `totalLen` erreicht, startete das eine komplette - Resume-Schleife (bis zu `NL_OTA_MAX_RETRY_ROUNDS` = 200 Range-Requests), deren Daten die fwup-Task - wegen des bereits gesetzten Fehler-Bits (siehe unten) jedes Mal sofort verwarf. `t_http_fwup()` kam - also erst nach Minuten mit dem richtigen Fehler zurück, `s_busy`/`ota_pending` standen so lange. - Springt jetzt direkt nach `QUIT_OTA` (mit `nRetCode = ret`, damit die `socket_fwup_err()`- - Bereinigung am Funktionsende trotzdem läuft) - der Abbruch ist damit sofort sichtbar, nicht erst - nach der vollen Resume-Schleife. + - `Src/App/OTA/wm_http_fwup.c`, `HANDLE_BODY`: Lehnt `socket_fwup_recv()` einen Chunk mit `ERR_ABRT` + ab, sprang der Code bisher nach `SHUTDOWN_LINK` - da `recvLen` dabei nie den vollen `totalLen` + erreicht, startete das eine komplette Resume-Schleife (bis zu `NL_OTA_MAX_RETRY_ROUNDS` = 200 + Range-Requests). Ursprünglich (Fix-Runde 2) mit der Begründung „die fwup-Task hat abgelehnt" + beschrieben - das war **zu ungenau**: `ERR_ABRT` bedeutet nicht zwingend eine echte fwup-Ablehnung, + siehe die Korrektur in Fix-Runde 3 unten. Springt seit Fix-Runde 3 nur noch bei einer **echten** + Ablehnung direkt nach `QUIT_OTA`; ein leerer Read (Verbindungsabriss/WLAN-Aussetzer) läuft weiterhin + in den Resume. - `Platform/Common/fwup/wm_fwup.c`, Trailer-Puffer-Allokation (`RSA_SHA256 = tls_mem_alloc(512)`): Schlug das fehl, sprang der Code nach `request_finish`, ohne ein Fehler-Bit zu setzen. Mit dem Fix-Runde-1-Reset-Guard (siehe oben, `!(fwup->current_state & TLS_FWUP_STATE_ERROR)`) wäre das der @@ -49,6 +48,27 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ Trailer wäre dann nie geprüft worden. Setzt jetzt `TLS_FWUP_STATE_ERROR_MEM` / `TLS_FWUP_REQ_STATUS_FMEM` (wie die andere Allokationsprüfung in dieser Funktion) und `nl_ota_set_error("download")` (kein spezifischerer Wert aus dem Vokabular passt hier). + + Fix-Runde 3 (Re-Review von Fix-Runde 2) hat eine echte Regression aus Runde 2 korrigiert: + - `Src/App/OTA/wm_http_fwup.c`, `HANDLE_BODY`: `socket_fwup_recv()` liefert `ERR_ABRT` nicht nur bei + einer echten fwup-Ablehnung (`tls_fwup_request_sync()` meldet Checksummen-/Trailer-/Hash-Fehler, + oder `tls_fwup_enter()` lehnt eine noch belegte Session ab), sondern auch für ein leeres Paket: + `wm_socket_fwup.c` macht aus `current_pack.datalen == 0` per `goto err` denselben `ERR_ABRT`. Ein + leeres Paket entsteht regelmäßig **ohne** jeden fwup-Fehler, wenn `HTTPClient.c`s + `HTTPIntrnRecv()` (Server schließt mitten im Body, oder `recv()` liefert wegen eines + ECONNRESET/WLAN-Aussetzers 0 Bytes) oder der `nSocketEvents < 0`-Zweig `*nLength = 0` + zurückgeben, aber weiterhin `HTTP_CLIENT_SUCCESS`/`HTTP_CLIENT_EOS` melden. Der Fix-Runde-2-Fix + (QUIT_OTA sofort bei `ERR_ABRT`) hätte also genau den häufigsten Auslöser des Resume-Mechanismus + aus I1 (Runde 1) - einen kurzen WLAN-Aussetzer - fälschlich als endgültigen Abbruch behandelt, + obwohl kein Byte falsch geprüft oder geflasht wurde. Fix: Ein `nSize == 0`-Read wird jetzt schon + **vor** dem `pbuf_alloc()`/`socket_fwup_recv()`-Aufruf abgefangen und geht wie vor Runde 2 nach + `SHUTDOWN_LINK` (resumable); `socket_fwup_recv()` bekommt damit nie mehr ein leeres Paket, sein + `ERR_ABRT` ist ab dann eindeutig eine echte Ablehnung und geht weiterhin sofort nach `QUIT_OTA`. + Diese Unterscheidung über den SDK-eigenen Aufrufer zu treffen ist der direkteste verfügbare Weg - + `tls_fwup_current_state(session_id)` bräuchte die (in `wm_socket_fwup.c` statische, nicht + exportierte) `session_id`, wäre also ein größerer Eingriff für denselben Effekt. Nebenbei: + `nRetCode = ret` (ein lwIP-`err_t` in einer sonst `HTTP_CLIENT_*`-Codes tragenden Variable) durch + `HTTP_CLIENT_ERROR_BAD_STATE` ersetzt. 2. `Src/OS/RTOS/include/FreeRTOSConfig.h`: `configGENERATE_RUN_TIME_STATS 0`, QConnex-Debug-Hooks entfernt. 3. `undefined reference` beim Linken auf `EarlyInit` und `pre_gpio_config` (von `sdk/Platform/Sys/wm_main.c` als `extern`-Hooks erwartet, aber nur von der App bereitgestellt): diff --git a/sdk/Src/App/OTA/wm_http_fwup.c b/sdk/Src/App/OTA/wm_http_fwup.c index 4e7f0db..a6da5de 100644 --- a/sdk/Src/App/OTA/wm_http_fwup.c +++ b/sdk/Src/App/OTA/wm_http_fwup.c @@ -147,6 +147,20 @@ int http_fwup(HTTPParameters ClientParams) now_state = SHUTDOWN_LINK; break; } + if (nSize == 0) { + /* ninlight: a zero-length read is not a fwup rejection - it's what + * HTTPClient.c's HTTPIntrnRecv()/nSocketEvents<0 paths report for a server + * that closed mid-body or a transient socket hiccup (WLAN dropout), while + * still returning HTTP_CLIENT_SUCCESS/HTTP_CLIENT_EOS. Passing an empty + * packet into socket_fwup_recv() would turn it into the exact same ERR_ABRT + * as a genuine checksum/trailer rejection (wm_socket_fwup.c: datalen==0 goes + * to the same "goto err" as a failed tls_fwup_request_sync()) - the QUIT_OTA- + * on-ERR_ABRT fix below must not see this case, or a routine WLAN dropout + * would abort the update instead of resuming via SHUTDOWN_LINK/PREPARE_PACKET + * like before. See sdk/PATCHES.md point 1 (Fix-Runde 3). */ + now_state = SHUTDOWN_LINK; + break; + } #if USE_BREAK_POINT_RESUME == 0 if(breakFlag == 1){ breakLen += nSize; @@ -181,15 +195,19 @@ int http_fwup(HTTPParameters ClientParams) // Send received data to fwup thread and deal with socket issues. s8 ret = socket_fwup_recv(0, p, ERR_OK); if(ret != ERR_OK){ - /* ninlight: socket_fwup_recv() rejected this chunk (the fwup task hit a - * checksum/trailer mismatch or another fatal error, see wm_fwup.c) - recvLen - * never advances past this point, so going through SHUTDOWN_LINK used to start - * a full Range-resume retry (up to NL_OTA_MAX_RETRY_ROUNDS rounds) whose data - * the fwup task would just keep discarding. Bail out to QUIT_OTA directly so - * the caller gets the real error immediately instead of after minutes. Force - * nRetCode nonzero so the socket_fwup_err() cleanup below still runs. See - * sdk/PATCHES.md point 1. */ - nRetCode = ret; + /* ninlight: nSize > 0 is guaranteed here (see the nSize==0 check above), so + * this ERR_ABRT is NOT the "empty packet" case - it means + * wm_socket_fwup.c's socket_fwup_recv() actually rejected non-empty data: + * either tls_fwup_enter() refused the session (e.g. still busy from a + * previous attempt) on the first chunk, or tls_fwup_request_sync() reported a + * checksum/trailer/hash mismatch on a later one (see wm_fwup.c). Either way + * recvLen never advances past this point, so going through SHUTDOWN_LINK used + * to start a full Range-resume retry (up to NL_OTA_MAX_RETRY_ROUNDS rounds) + * whose data the fwup task would just keep discarding. Bail out to QUIT_OTA + * directly so the caller gets the real error immediately instead of after + * minutes. Force nRetCode nonzero so the socket_fwup_err() cleanup below + * still runs. See sdk/PATCHES.md point 1. */ + nRetCode = HTTP_CLIENT_ERROR_BAD_STATE; now_state = QUIT_OTA; break; } From 8fa4af7eb5c4717178330442bd689d7ec671eae6 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 02:24:58 +0200 Subject: [PATCH 060/150] feat: Recovery-Download beim Booten --- CMakeLists.txt | 18 +++++++- src/app_main.c | 2 + src/net/nl_recovery.c | 98 ++++++++++++++++++++++++++++++++++++++++++ src/net/nl_recovery.h | 10 +++++ src/net/nl_wifi.h | 6 +++ src/net/nl_wifi_w600.c | 40 +++++++++++++++++ 6 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 src/net/nl_recovery.c create mode 100644 src/net/nl_recovery.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fae211..1d9a88e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,18 @@ string(LENGTH "${NINLIGHT_WIFI_PASSWORD}" _nl_wifi_pass_len) if(NOT _nl_wifi_pass_len EQUAL 0 AND (_nl_wifi_pass_len LESS 8 OR _nl_wifi_pass_len GREATER 63)) message(FATAL_ERROR "NINLIGHT_WIFI_PASSWORD must be empty or 8..63 characters (WPA2)") endif() -foreach(_v NINLIGHT_WIFI_SSID NINLIGHT_WIFI_PASSWORD NINLIGHT_AP_PASSWORD NINLIGHT_TOKEN) +if(NOT DEFINED NINLIGHT_RECOVERY_SSID) + set(NINLIGHT_RECOVERY_SSID "ninlight-recovery") +endif() +if(NOT DEFINED NINLIGHT_RECOVERY_PASSWORD) + set(NINLIGHT_RECOVERY_PASSWORD "ninlight-recovery") +endif() +string(LENGTH "${NINLIGHT_RECOVERY_PASSWORD}" _nl_rec_len) +if(_nl_rec_len LESS 8) + message(FATAL_ERROR "NINLIGHT_RECOVERY_PASSWORD needs at least 8 characters (WPA2)") +endif() +foreach(_v NINLIGHT_WIFI_SSID NINLIGHT_WIFI_PASSWORD NINLIGHT_AP_PASSWORD NINLIGHT_TOKEN + NINLIGHT_RECOVERY_SSID NINLIGHT_RECOVERY_PASSWORD) if("${${_v}}" MATCHES "[\"\\\\;]") message(FATAL_ERROR "${_v} must not contain \", \\ or ;") endif() @@ -74,6 +85,7 @@ set(NL_FW_SOURCES src/net/nl_api.c src/net/nl_wifi_sm.c src/net/nl_wifi_w600.c + src/net/nl_recovery.c src/net/nl_mdns.c src/net/nl_http_server.c src/sys/nl_app_api.c @@ -92,7 +104,9 @@ target_compile_definitions(${NL_FW} PRIVATE NINLIGHT_TOKEN_DEFAULT="${NINLIGHT_TOKEN}" NINLIGHT_WIFI_SSID="${NINLIGHT_WIFI_SSID}" NINLIGHT_WIFI_PASSWORD="${NINLIGHT_WIFI_PASSWORD}" - NINLIGHT_AP_PASSWORD="${NINLIGHT_AP_PASSWORD}") + NINLIGHT_AP_PASSWORD="${NINLIGHT_AP_PASSWORD}" + NINLIGHT_RECOVERY_SSID="${NINLIGHT_RECOVERY_SSID}" + NINLIGHT_RECOVERY_PASSWORD="${NINLIGHT_RECOVERY_PASSWORD}") target_include_directories(${NL_FW} PRIVATE ${CMAKE_BINARY_DIR}/generated) add_dependencies(${NL_FW} nl_version) target_link_options(${NL_FW} PRIVATE diff --git a/src/app_main.c b/src/app_main.c index b497394..a26409e 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -16,6 +16,7 @@ #include "light/nl_engine.h" #include "net/nl_http_server.h" #include "net/nl_mdns.h" +#include "net/nl_recovery.h" #include "net/nl_wifi.h" #include "sys/nl_app.h" #include "sys/nl_app_api.h" @@ -196,6 +197,7 @@ static void app_task(void *arg) tls_watchdog_init(20u * 1000u * 1000u); /* 20 s */ nl_ota_init(); + nl_recovery_try(); /* may not return: a successful recovery resets the chip */ nl_console_start(); { diff --git a/src/net/nl_recovery.c b/src/net/nl_recovery.c new file mode 100644 index 0000000..73a1176 --- /dev/null +++ b/src/net/nl_recovery.c @@ -0,0 +1,98 @@ +#include "net/nl_recovery.h" + +#include +#include + +#include "wm_include.h" +#include "wm_watchdog.h" + +#include "net/nl_wifi.h" +#include "sys/nl_ota.h" + +#define NL_RECOVERY_SCAN_MS 3000u +#define NL_RECOVERY_JOIN_MS 15000u +#define NL_RECOVERY_BUF 2048u + +static volatile bool s_scan_done; + +/* tls_wifi_scan_result_cb_register() takes a plain void(void) callback (see + sdk/Include/WiFi/wm_wifi.h) - it carries no status/result argument. */ +static void scan_done_cb(void) +{ + s_scan_done = true; +} + +static bool recovery_ap_present(void) +{ + char *buf = tls_mem_alloc(NL_RECOVERY_BUF); + struct tls_scan_bss_t *res; + const char *want = NINLIGHT_RECOVERY_SSID; + uint32_t want_len = (uint32_t)strlen(want); + uint32_t waited = 0; + uint32_t max_entries, n; + bool found = false; + u32 i; + + if (buf == NULL) + return false; + s_scan_done = false; + tls_wifi_scan_result_cb_register(scan_done_cb); + if (tls_wifi_scan() != WM_SUCCESS) { + tls_wifi_scan_result_cb_register(NULL); + tls_mem_free(buf); + return false; + } + while (!s_scan_done && waited < NL_RECOVERY_SCAN_MS) { + tls_watchdog_clr(); /* boot phase: only this path runs, the light_task heartbeat + gate in app_task() does not watch us yet */ + tls_os_time_delay(HZ / 10); + waited += 100; + } + if (s_scan_done && tls_wifi_get_scan_rslt((u8 *)buf, NL_RECOVERY_BUF) == WM_SUCCESS) { + res = (struct tls_scan_bss_t *)buf; + /* struct tls_bss_info_t has no length field - res->bss is a fixed-size-element + array (struct tls_scan_bss_t::bss[1] is a flexible-array-style head), so index + it directly. Cap by what actually fits in buf in case res->count ever reports + more entries than tls_wifi_get_scan_rslt() copied into this buffer size. */ + max_entries = (uint32_t)((NL_RECOVERY_BUF - offsetof(struct tls_scan_bss_t, bss)) + / sizeof(struct tls_bss_info_t)); + n = res->count < max_entries ? res->count : max_entries; + for (i = 0; i < n; i++) { + struct tls_bss_info_t *bss = &res->bss[i]; + if (bss->ssid_len == want_len && memcmp(bss->ssid, want, want_len) == 0) { + found = true; + break; + } + } + } + tls_wifi_scan_result_cb_register(NULL); + tls_mem_free(buf); + return found; +} + +void nl_recovery_try(void) +{ + char url[128]; + + if (!recovery_ap_present()) + return; + printf("recovery: station found, joining\r\n"); + if (!nl_wifi_join_blocking(NINLIGHT_RECOVERY_SSID, NINLIGHT_RECOVERY_PASSWORD, NL_RECOVERY_JOIN_MS)) { + printf("recovery: join failed\r\n"); + nl_ota_set_error("download"); + return; + } + snprintf(url, sizeof url, "http://192.168.8.1/ninlight_%s_ota.img", NINLIGHT_DEVICE_ID); + printf("recovery: fetching %s\r\n", url); + if (nl_ota_start(url) != 0) { + printf("recovery: cannot start download\r\n"); + return; + } + /* Give the download task the boot phase; on success the SDK resets the chip. */ + while (nl_ota_busy()) { + tls_watchdog_clr(); + tls_os_time_delay(HZ / 2); + } + printf("recovery: update failed (%s), continuing normal boot\r\n", + nl_ota_last_error() ? nl_ota_last_error() : "unknown"); +} diff --git a/src/net/nl_recovery.h b/src/net/nl_recovery.h new file mode 100644 index 0000000..bcedfa8 --- /dev/null +++ b/src/net/nl_recovery.h @@ -0,0 +1,10 @@ +#ifndef NL_RECOVERY_H +#define NL_RECOVERY_H + +/* Looks for the recovery station for at most 3 s. When it is there, joins it and + starts the recovery download; the chip resets on success, so this call may not return. + Must run before nl_wifi_start(): it drives the SDK Wi-Fi APIs directly, without the + nl_wifi_sm state machine, and feeds the watchdog itself while it waits. */ +void nl_recovery_try(void); + +#endif diff --git a/src/net/nl_wifi.h b/src/net/nl_wifi.h index 6b3a11e..2b0a61b 100644 --- a/src/net/nl_wifi.h +++ b/src/net/nl_wifi.h @@ -21,6 +21,12 @@ extern volatile uint32_t nl_net_beat; /* Starts the network task. An empty ssid starts the onboarding AP. Strings are copied. */ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass); +/* Joins a station directly (no nl_wifi_sm) and blocks until it gets an IP or timeout_ms + * elapses; returns true on success. For use before nl_wifi_start() only - e.g. the boot + * recovery path in nl_recovery.c - since it drives the SDK Wi-Fi calls itself instead of + * going through the state machine, and must not race nl_wifi_start()'s own netif status + * event registration and s_lock/s_queue setup. Feeds the watchdog itself while it waits. */ +bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_ms); /* Starts an onboarding test with new credentials (from the HTTP task). 0 = queued. */ int nl_wifi_request_onboard(const char *ssid, const char *pass); /* True while the unconfigured onboarding AP is open (API without token). */ diff --git a/src/net/nl_wifi_w600.c b/src/net/nl_wifi_w600.c index 4ccf356..454a342 100644 --- a/src/net/nl_wifi_w600.c +++ b/src/net/nl_wifi_w600.c @@ -3,6 +3,7 @@ #include "wm_include.h" #include "wm_efuse.h" +#include "wm_watchdog.h" #include "lwip/ip_addr.h" #include "lwip/netif.h" /* struct netif is only forward-declared by wm_netif2.0.3.h */ @@ -324,3 +325,42 @@ void nl_wifi_status(nl_wifi_status_t *out) const char *nl_wifi_hostname(void) { return s_hostname; } void nl_wifi_mac(uint8_t mac[6]) { memcpy(mac, s_mac, 6); } + +/* --- boot recovery: join a station directly, ahead of nl_wifi_start()/nl_wifi_sm --- */ + +static volatile bool s_recovery_join_done; +static volatile bool s_recovery_join_ok; + +static void recovery_join_event(u8 status) +{ + if (status == NETIF_IP_NET_UP) { + s_recovery_join_ok = true; + s_recovery_join_done = true; + } else if (status == NETIF_WIFI_JOIN_FAILED) { + s_recovery_join_done = true; + } +} + +bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_ms) +{ + uint32_t waited = 0; + + s_recovery_join_done = false; + s_recovery_join_ok = false; + tls_netif_add_status_event(recovery_join_event); + if (tls_wifi_connect((u8 *)ssid, (u8)strlen(ssid), (u8 *)pass, (u8)strlen(pass)) != WM_SUCCESS) { + tls_netif_remove_status_event(recovery_join_event); + return false; + } + while (!s_recovery_join_done && waited < timeout_ms) { + tls_watchdog_clr(); /* boot phase: called before app_task's own heartbeat gate exists */ + tls_os_time_delay(HZ / 10); + waited += 100; + } + tls_netif_remove_status_event(recovery_join_event); + if (!s_recovery_join_ok) { + tls_wifi_disconnect(); /* cancel a stuck/timed-out join attempt */ + return false; + } + return true; +} From 247d610fe882967f432ee04073553a482022ee7c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 02:38:02 +0200 Subject: [PATCH 061/150] =?UTF-8?q?fix:=20Recovery-Rettungsweg=20h=C3=A4rt?= =?UTF-8?q?en=20(Watchdog-Cap,=20Konsole,=20Softap-Bit,=20Scan-Retry)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 11 +++++- config.local.cmake.example | 3 ++ src/app_main.c | 2 +- src/net/nl_recovery.c | 78 +++++++++++++++++++++++++++++++------- src/net/nl_wifi_w600.c | 7 ++++ 5 files changed, 85 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d9a88e..f500947 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,9 +44,16 @@ endif() if(NOT DEFINED NINLIGHT_RECOVERY_PASSWORD) set(NINLIGHT_RECOVERY_PASSWORD "ninlight-recovery") endif() +# Unlike NINLIGHT_WIFI_SSID, the recovery SSID is compared byte-for-byte against scan +# results (nl_recovery.c) and must fit struct tls_bss_info_t::ssid[32]; empty would match +# any hidden/empty-SSID AP in the scan, so it must not be empty either. +string(LENGTH "${NINLIGHT_RECOVERY_SSID}" _nl_rec_ssid_len) +if(_nl_rec_ssid_len EQUAL 0 OR _nl_rec_ssid_len GREATER 32) + message(FATAL_ERROR "NINLIGHT_RECOVERY_SSID must be 1..32 characters") +endif() string(LENGTH "${NINLIGHT_RECOVERY_PASSWORD}" _nl_rec_len) -if(_nl_rec_len LESS 8) - message(FATAL_ERROR "NINLIGHT_RECOVERY_PASSWORD needs at least 8 characters (WPA2)") +if(_nl_rec_len LESS 8 OR _nl_rec_len GREATER 63) + message(FATAL_ERROR "NINLIGHT_RECOVERY_PASSWORD must be 8..63 characters (WPA2)") endif() foreach(_v NINLIGHT_WIFI_SSID NINLIGHT_WIFI_PASSWORD NINLIGHT_AP_PASSWORD NINLIGHT_TOKEN NINLIGHT_RECOVERY_SSID NINLIGHT_RECOVERY_PASSWORD) diff --git a/config.local.cmake.example b/config.local.cmake.example index f16aa0e..db9207b 100644 --- a/config.local.cmake.example +++ b/config.local.cmake.example @@ -4,5 +4,8 @@ # set(NINLIGHT_WIFI_SSID "MyWifi") # set(NINLIGHT_WIFI_PASSWORD "secret") # set(NINLIGHT_AP_PASSWORD "ninlight") +# Boot recovery station (plan 3): SSID 1..32 chars, password 8..63 chars (WPA2) +# set(NINLIGHT_RECOVERY_SSID "ninlight-recovery") +# set(NINLIGHT_RECOVERY_PASSWORD "ninlight-recovery") # Used from plan 3/4 on (path to the old RSA signing key): # set(NINLIGHT_MIGRATION_KEY "/path/to/fw_sign.pem") diff --git a/src/app_main.c b/src/app_main.c index a26409e..e17b9e8 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -197,8 +197,8 @@ static void app_task(void *arg) tls_watchdog_init(20u * 1000u * 1000u); /* 20 s */ nl_ota_init(); + nl_console_start(); /* keep the serial fallback alive during nl_recovery_try() below */ nl_recovery_try(); /* may not return: a successful recovery resets the chip */ - nl_console_start(); { static nl_settings_t boot; /* copy: the network task may change s_settings later */ diff --git a/src/net/nl_recovery.c b/src/net/nl_recovery.c index 73a1176..65271aa 100644 --- a/src/net/nl_recovery.c +++ b/src/net/nl_recovery.c @@ -12,6 +12,15 @@ #define NL_RECOVERY_SCAN_MS 3000u #define NL_RECOVERY_JOIN_MS 15000u #define NL_RECOVERY_BUF 2048u +/* Generous cap on how long the download itself may run. wm_http_fwup.c's own resume loop + can already take up to ~20 minutes on a slow/aborted transfer (200 rounds x up to 5 s + receive timeout / 2 s retry delay), and a real hang - e.g. its unbounded + "while (!p) { tls_os_time_delay(1); pbuf_alloc(...) }" retry on allocation failure + (wm_http_fwup.c:178) - never returns from t_http_fwup() at all. Past this cap we stop + feeding the watchdog (see below) instead of waiting indefinitely: a watchdog reset is + strictly better than a silent hang with no Wi-Fi and no console, and a fresh boot simply + retries recovery from scratch. */ +#define NL_RECOVERY_DOWNLOAD_MAX_MS (10u * 60u * 1000u) static volatile bool s_scan_done; @@ -32,24 +41,50 @@ static bool recovery_ap_present(void) uint32_t max_entries, n; bool found = false; u32 i; + int scan_rc; if (buf == NULL) return false; s_scan_done = false; tls_wifi_scan_result_cb_register(scan_done_cb); - if (tls_wifi_scan() != WM_SUCCESS) { - tls_wifi_scan_result_cb_register(NULL); - tls_mem_free(buf); - return false; - } - while (!s_scan_done && waited < NL_RECOVERY_SCAN_MS) { + + /* wm_wifi.h: "If not SUCCESS, user needs to call this function again to trigger scan" - + retry a transient failure (e.g. WM_WIFI_SCANNING_BUSY) instead of silently skipping + the whole recovery attempt, but stay inside the shared 3 s budget. */ + for (;;) { + scan_rc = tls_wifi_scan(); + if (scan_rc == WM_SUCCESS) + break; + if (waited >= NL_RECOVERY_SCAN_MS) { + printf("recovery: tls_wifi_scan() failed (rc=%d), giving up\r\n", scan_rc); + tls_wifi_scan_result_cb_register(NULL); + tls_mem_free(buf); + return false; + } tls_watchdog_clr(); /* boot phase: only this path runs, the light_task heartbeat gate in app_task() does not watch us yet */ tls_os_time_delay(HZ / 10); waited += 100; } - if (s_scan_done && tls_wifi_get_scan_rslt((u8 *)buf, NL_RECOVERY_BUF) == WM_SUCCESS) { + + while (!s_scan_done && waited < NL_RECOVERY_SCAN_MS) { + tls_watchdog_clr(); + tls_os_time_delay(HZ / 10); + waited += 100; + } + + if (!s_scan_done) { + /* Without this line, a scan whose callback simply never fired in time is + indistinguishable in the log from "no AP found" - the EVK verification needs + exactly that distinction. */ + printf("recovery: scan result not ready after %u ms, giving up\r\n", (unsigned)waited); + } else if (tls_wifi_get_scan_rslt((u8 *)buf, NL_RECOVERY_BUF) != WM_SUCCESS) { + printf("recovery: scan done after %u ms, but tls_wifi_get_scan_rslt() failed\r\n", + (unsigned)waited); + } else { res = (struct tls_scan_bss_t *)buf; + printf("recovery: scan done after %u ms, %u AP(s)\r\n", (unsigned)waited, + (unsigned)res->count); /* struct tls_bss_info_t has no length field - res->bss is a fixed-size-element array (struct tls_scan_bss_t::bss[1] is a flexible-array-style head), so index it directly. Cap by what actually fits in buf in case res->count ever reports @@ -88,11 +123,28 @@ void nl_recovery_try(void) printf("recovery: cannot start download\r\n"); return; } - /* Give the download task the boot phase; on success the SDK resets the chip. */ - while (nl_ota_busy()) { - tls_watchdog_clr(); - tls_os_time_delay(HZ / 2); + /* Give the download task the boot phase; on success the SDK resets the chip. Past + NL_RECOVERY_DOWNLOAD_MAX_MS, stop feeding the watchdog instead of continuing to wait - + see the comment on that constant. */ + { + uint32_t waited = 0; + bool feeding = true; + + while (nl_ota_busy()) { + if (feeding) { + if (waited >= NL_RECOVERY_DOWNLOAD_MAX_MS) { + printf("recovery: download still running after %u ms, letting the watchdog reset\r\n", + (unsigned)NL_RECOVERY_DOWNLOAD_MAX_MS); + feeding = false; + } else { + tls_watchdog_clr(); + } + } + tls_os_time_delay(HZ / 2); + waited += 500; + } + if (feeding) + printf("recovery: update failed (%s), continuing normal boot\r\n", + nl_ota_last_error() ? nl_ota_last_error() : "unknown"); } - printf("recovery: update failed (%s), continuing normal boot\r\n", - nl_ota_last_error() ? nl_ota_last_error() : "unknown"); } diff --git a/src/net/nl_wifi_w600.c b/src/net/nl_wifi_w600.c index 454a342..f3201a7 100644 --- a/src/net/nl_wifi_w600.c +++ b/src/net/nl_wifi_w600.c @@ -345,6 +345,13 @@ bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_ { uint32_t waited = 0; + /* C2: if a stale SDK softap bit from a previous onboarding/fallback boot is still set + * in TLS_PARAM_ID_WPROTOCOL, sys_net_status_changed() (sdk/Platform/Sys/tls_sys.c) + * recreates the SDK's own default-params softap the moment this join reaches + * NETIF_IP_NET_UP - exactly the "lamp stuck in onboarding" case recovery exists for. + * Clear it once, up front, same as nl_wifi_start() does. */ + clear_softap_param(); + s_recovery_join_done = false; s_recovery_join_ok = false; tls_netif_add_status_event(recovery_join_event); From 682235f3ab08b7a9d29b2bd1a0b717cd1fca3933 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 02:45:27 +0200 Subject: [PATCH 062/150] docs: OTA und Recovery dokumentieren --- README.md | 60 ++++++++++++++++++++++++++++++++++++++ bruno/ninlight.yml | 9 +++++- docs/hardware-checklist.md | 11 +++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a1e698..11874ef 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,66 @@ Verfügbare Befehle in der Konsole: `help`. - Bruno: `bruno/ninlight.yml` importieren, Environment „EVK (mDNS)" bzw. „Onboarding-AP" wählen. - Konsole: `net` zeigt Modus, SSID, IP, RSSI und den letzten Onboarding-Fehler. +## Firmware-Update + +Der Build erzeugt pro Gerät zwei Images: + +- **`_gz.img`** — komprimiertes App-Image für das serielle Flashen mit `tools/flash.sh` (siehe oben). +- **`_ota.img`** — dasselbe `_gz.img`, um einen 512-Byte-Trailer ergänzt (`tools/mkimage/nl_mkimage.py`, + automatisch bei jedem Build via `cmake/run_mkimage.cmake`). Der Trailer enthält Gerätetyp, Firmware-Version + und den SHA256 über das komplette `_gz.img` — das ist genau der Hash, den die Firmware beim Schreiben ins + Flash prüft. + +Netz-Update über die REST-API: + + curl -X PUT http://ninlight-681f.local:3333/api/v1/system/command \ + -H 'Authorization: aabbccddeeff0011223344' \ + -d '{"command":{"type":"fw_update","url":"http://:8000/pfad/zur/ninlight_evk_ota.img"}}' + +Antwort `202 {"status":"updating"}` startet den Download im Hintergrund; ein per HTTP-Server bereitgestelltes +`_ota.img` reicht (z.B. `python3 -m http.server 8000`). Läuft der Trailer- bzw. Hash-Check durch, resettet die +Lampe und bootet mit der neuen Version. Wird das Image abgelehnt, gibt es **keinen** Reset — die Lampe läuft +mit der alten Firmware weiter und trägt den Grund in `last_ota_error` (`GET system/info`) ein. Ein +Verbindungsabbruch mitten im Download löst dagegen einen Wiederaufnahmeversuch per HTTP-Range aus (begrenzte +Anzahl Runden); erst wenn auch das ausgeht, landet der Versuch bei `last_ota_error = download`. + +Werte von `last_ota_error` (`null`, solange nichts fehlgeschlagen ist): + +| Wert | Bedeutung | +|---|---| +| `download` | Verbindung/Transfer gescheitert (auch nach ausgeschöpftem Wiederaufnahmeversuch) | +| `crc` | Prüfsumme des SDK-eigenen Update-Mechanismus stimmt nicht | +| `trailer` | Trailer fehlt, ist zu kurz oder hat eine falsche Magic/Version | +| `device_mismatch` | Trailer nennt einen anderen Gerätetyp | +| `hash` | SHA256 im Trailer stimmt nicht mit dem übertragenen Image überein | +| `busy` | es läuft bereits ein Update, oder die übergebene URL war ungültig | + +Ein erneuter `fw_update`-Aufruf mit einem gültigen Image funktioniert nach einer Ablehnung sofort, ohne +manuellen Reboot. + +## Recovery + +Findet die Firmware beim Booten keinen Weg, sich selbst zu aktualisieren (z.B. weil das reguläre WLAN nicht +erreichbar ist), sucht sie kurz nach einem Rettungs-Zugangspunkt: SSID/Passwort sind Build-Parameter +(`NINLIGHT_RECOVERY_SSID`/`NINLIGHT_RECOVERY_PASSWORD` in `config.local.cmake`, Default für beide +`ninlight-recovery`). Wird die Station gefunden, verbindet sich die Lampe und lädt +`http://192.168.8.1/ninlight__ota.img` (z.B. `ninlight_evk_ota.img`) — der Dateiname enthält den +Gerätetyp, `` ist der Wert von `NINLIGHT_DEVICE` aus dem Build. + +Bei jedem Boot scannt die Firmware dafür bis zu ~3 Sekunden nach der Station und loggt das Ergebnis +(gefunden, nichts gefunden, oder Scan fehlgeschlagen). Ist keine Station in Reichweite, verlängert sich der +Boot nur um diese Scan-Zeit; danach startet der normale WLAN-Aufbau wie gewohnt. Über dem Download selbst +liegt eine harte Zeitobergrenze — läuft sie ab, wird der Watchdog nicht mehr bedient und der Chip startet neu, +statt unbeobachtet hängen zu bleiben; der nächste Boot versucht Recovery erneut. + +**Vertrauensgrenze:** Der Trailer prüft Gerätetyp und SHA256 — das ist kein kryptografisches Signaturverfahren. +Der Download läuft über `http://` ohne TLS, und der Recovery-Kanal ist ausschließlich durch das oben genannte +WLAN-Passwort geschützt, das mit einem öffentlichen Default im Repo steht. Wer im selben WLAN ist und das +API-Token kennt (bzw. beim Recovery in Funkreichweite ist und das Passwort kennt), kann der Lampe beliebige +Firmware unterschieben. Das ist eine bewusste Entscheidung dieses Projekts: Ziel ist, die Signaturprüfung der +alten, abgekündigten Original-Firmware zu umgehen, damit die Lampen überhaupt weiter nutzbar bleiben — nicht, +ein neues, gehärtetes Sicherheitsmodell zu bauen. + ## Werksreset Gerät 5× hintereinander einschalten, jeweils kürzer als 5 Sekunden. diff --git a/bruno/ninlight.yml b/bruno/ninlight.yml index ad9fa1d..1ffd180 100644 --- a/bruno/ninlight.yml +++ b/bruno/ninlight.yml @@ -11,6 +11,8 @@ config: value: '3333' - name: device_token value: aabbccddeeff0011223344 + - name: ota_url + value: http://192.168.1.12:8000/ninlight_evk_ota.img - name: Onboarding-AP variables: - name: device_address @@ -19,6 +21,8 @@ config: value: '3333' - name: device_token value: aabbccddeeff0011223344 + - name: ota_url + value: http://192.168.1.12:8000/ninlight_evk_ota.img items: - info: name: Device @@ -205,6 +209,9 @@ items: timeout: 0 followRedirects: true maxRedirects: 5 + docs: |- + Enthält `last_ota_error`: `null`, solange kein Update fehlgeschlagen ist, sonst einer von + `download`, `crc`, `trailer`, `device_mismatch`, `hash`, `busy`. Kein `reset_reason` mehr im Feld-Satz. - info: name: Get System Settings type: http @@ -372,7 +379,7 @@ items: { "command": { "type": "fw_update", - "url": "http://192.168.1.12:8000/ninlight_evk_ota.img" + "url": "{{ota_url}}" } } settings: diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 4a031b9..a57ac82 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -38,6 +38,17 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] C1: `curl -d '[[[[[[[[[[[[[[[[[[[[' http://:3333/api/v1/device/state` → 400, Lampe läuft unverändert weiter (kein Absturz/Reboot) — 2026-09-12 - [x] C2: Onboarding ok, 60 s warten, dann Reboot und Router-Neustart → AP `ninlight-` darf nicht wiederkommen (Scan am Mac) — 2026-09-12 (Reboot geprüft, Router-Neustart offen) +## OTA und Recovery (Plan 3) +- [ ] OTA: Update über `fw_update` läuft durch, danach meldet `system/info` die neue Version +- [ ] OTA: Image eines anderen Gerätetyps wird sofort abgelehnt (`last_ota_error` = `device_mismatch`), kein Reset, Lampe läuft mit der alten Firmware weiter +- [ ] OTA: Image mit verfälschtem Byte wird abgelehnt (`last_ota_error` = `hash` oder `crc`), kein Reset +- [ ] OTA: HTTP-Server während des Downloads abschalten → Wiederaufnahmeversuch (Range-Resume) sichtbar in der Konsole, nach Ausschöpfen der Runden `last_ota_error` = `download`, Lampe bootet normal +- [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler +- [ ] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen +- [ ] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) +- [ ] Recovery: Download hängt/dauert sehr lange → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben +- [ ] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten) + ## Später (Lampen) - [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) - [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) From b601ddeb9bdccfa0f0e6096be7693908108ca3ed Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 02:51:08 +0200 Subject: [PATCH 063/150] =?UTF-8?q?fix:=20Fix-Runde=201=20f=C3=BCr=20Task?= =?UTF-8?q?=207=20-=20busy-Beschreibung=20korrigieren,=20fw=5Fupdate-Fehle?= =?UTF-8?q?rantwort=20dokumentieren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++++-- bruno/ninlight.yml | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 11874ef..ddfe0b0 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,10 @@ Netz-Update über die REST-API: -d '{"command":{"type":"fw_update","url":"http://:8000/pfad/zur/ninlight_evk_ota.img"}}' Antwort `202 {"status":"updating"}` startet den Download im Hintergrund; ein per HTTP-Server bereitgestelltes -`_ota.img` reicht (z.B. `python3 -m http.server 8000`). Läuft der Trailer- bzw. Hash-Check durch, resettet die +`_ota.img` reicht (z.B. `python3 -m http.server 8000`). Eine ungültige URL (kein `http://`-Präfix, oder zu +kurz/lang) beantwortet die API sofort mit `400`, ohne dass ein Download versucht wird. Läuft bereits ein +Update, oder scheitert der Start aus einem anderen Grund, antwortet sie mit `409` und dem Grund im Body +(gleicher Wert wie `last_ota_error`). Läuft der Trailer- bzw. Hash-Check durch, resettet die Lampe und bootet mit der neuen Version. Wird das Image abgelehnt, gibt es **keinen** Reset — die Lampe läuft mit der alten Firmware weiter und trägt den Grund in `last_ota_error` (`GET system/info`) ein. Ein Verbindungsabbruch mitten im Download löst dagegen einen Wiederaufnahmeversuch per HTTP-Range aus (begrenzte @@ -103,7 +106,7 @@ Werte von `last_ota_error` (`null`, solange nichts fehlgeschlagen ist): | `trailer` | Trailer fehlt, ist zu kurz oder hat eine falsche Magic/Version | | `device_mismatch` | Trailer nennt einen anderen Gerätetyp | | `hash` | SHA256 im Trailer stimmt nicht mit dem übertragenen Image überein | -| `busy` | es läuft bereits ein Update, oder die übergebene URL war ungültig | +| `busy` | es läuft bereits ein Update | Ein erneuter `fw_update`-Aufruf mit einem gültigen Image funktioniert nach einer Ablehnung sofort, ohne manuellen Reboot. diff --git a/bruno/ninlight.yml b/bruno/ninlight.yml index 1ffd180..8412270 100644 --- a/bruno/ninlight.yml +++ b/bruno/ninlight.yml @@ -211,7 +211,8 @@ items: maxRedirects: 5 docs: |- Enthält `last_ota_error`: `null`, solange kein Update fehlgeschlagen ist, sonst einer von - `download`, `crc`, `trailer`, `device_mismatch`, `hash`, `busy`. Kein `reset_reason` mehr im Feld-Satz. + `download`, `crc`, `trailer`, `device_mismatch`, `hash`, `busy` (`busy` heißt: es läuft bereits + ein Update). Kein `reset_reason` mehr im Feld-Satz. - info: name: Get System Settings type: http @@ -387,6 +388,10 @@ items: timeout: 0 followRedirects: true maxRedirects: 5 + docs: |- + Erfolg: `202 {"status":"updating"}`, Download startet im Hintergrund. Ungültige URL (kein + `http://`-Präfix, zu kurz/lang): `400`. Läuft bereits ein Update oder scheitert der Start + sonstwie: `409` mit dem Grund im Body (gleicher Wert wie `last_ota_error`, z.B. `"busy"`). request: headers: - name: Authorization From 1822014e48d19c750d897c4837df2822b38aacc4 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 03:13:36 +0200 Subject: [PATCH 064/150] feat: Licht nach einem OTA wie bei power_on_behaviour=last wiederherstellen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec 8.2 verlangt, dass ein durch ein Update ausgelöster Neustart nicht wie ein Stromausfall wirkt: Das Flag ota_pending wurde bisher nur geschrieben und beim nächsten Boot wieder gelöscht, nl_boot_state() wendet aber weiterhin das normale power_on_behaviour an. Eine eingeschaltete Lampe mit pob=off kam nach einem Update aus zurück, mit pob=on umgekehrt. nl_boot_state() bekommt dafür ein Argument after_ota; ist ein gespeicherter Zustand vorhanden, wird er unverändert übernommen. Ohne gespeicherten Zustand gelten weiterhin die normalen Regeln. Hosttests: vier neue Fälle in test_boot_state.c (pob=off/on über ein OTA, Sanitisierung bleibt aktiv, kein gespeicherter Zustand). --- src/app_main.c | 15 ++++++----- src/light/nl_boot_state.c | 9 ++++++- src/light/nl_boot_state.h | 8 ++++-- test/test_boot_state.c | 53 +++++++++++++++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 14 deletions(-) diff --git a/src/app_main.c b/src/app_main.c index e17b9e8..8b1bee2 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -149,7 +149,7 @@ static void app_task(void *arg) uint32_t base = nl_flash_region_base(); nl_engine_cfg_t cfg; nl_light_state_t last, initial, pending; - bool have_last, counter_cleared = false; + bool have_last, after_ota, counter_cleared = false; bool light_alive = false, net_alive = false, http_alive = false; uint8_t boots; uint32_t boot_ms, light_seen, net_seen, http_seen; @@ -165,10 +165,12 @@ static void app_task(void *arg) nl_state_log_init(base + NL_FL_STATE_0, base + NL_FL_STATE_1); have_last = nl_state_log_load(&last); store_unlock(); - if (s_settings.ota_pending) { - /* We came back from an update attempt (accepted or rejected). Just clear the flag so it - * doesn't stick in flash forever; nl_boot_state() below restores the light from `last`/ - * power_on_behaviour as on any other boot, no special-casing needed here. */ + after_ota = s_settings.ota_pending != 0; + if (after_ota) { + /* We came back from an update attempt (accepted or rejected). Clear the flag so it does + * not stick in flash forever; nl_boot_state() below uses `after_ota` to restore the light + * effectively as power_on_behaviour = last (spec 8.2: a reboot caused by an update is not + * a power cut). */ s_settings.ota_pending = 0; printf("boot after ota attempt\r\n"); store_lock(); @@ -183,7 +185,8 @@ static void app_task(void *arg) cfg.ct_max = board->ct_max; cfg.output = led_out; cfg.ctx = NULL; - initial = nl_boot_state((nl_pob_t)s_settings.power_on_behaviour, have_last ? &last : NULL, board); + initial = nl_boot_state((nl_pob_t)s_settings.power_on_behaviour, have_last ? &last : NULL, board, + after_ota); nl_app_lock(); nl_engine_init(&cfg); nl_ctrl_init(board, s_settings.fade_in_ms, s_settings.fade_out_ms); diff --git a/src/light/nl_boot_state.c b/src/light/nl_boot_state.c index e9792d5..1141cf9 100644 --- a/src/light/nl_boot_state.c +++ b/src/light/nl_boot_state.c @@ -20,7 +20,8 @@ nl_light_state_t nl_boot_default_state(const nl_board_t *b) return s; } -nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b) +nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b, + bool after_ota) { nl_light_state_t s = last ? *last : nl_boot_default_state(b); @@ -30,6 +31,12 @@ nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const s.mode = NL_MODE_WHITE; s.ct = clamp_ct(s.ct, b); + /* Spec 8.2: after an OTA the light is restored effectively as power_on_behaviour = last - + * a reboot triggered by an update must not look like a power cut to the user. Without a + * stored state there is nothing to restore, so the normal rules apply. */ + if (after_ota && last) + return s; + if (pob == NL_POB_ON) s.on = true; else if (pob == NL_POB_OFF) diff --git a/src/light/nl_boot_state.h b/src/light/nl_boot_state.h index b7686fa..6e89019 100644 --- a/src/light/nl_boot_state.h +++ b/src/light/nl_boot_state.h @@ -9,8 +9,12 @@ /* Warm white, 100 %, on. */ nl_light_state_t nl_boot_default_state(const nl_board_t *b); -/* State to apply after power-up. last == NULL when nothing is stored. Stored values are sanitised. */ -nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b); +/* State to apply after power-up. last == NULL when nothing is stored. Stored values are sanitised. + * after_ota: this boot follows a firmware update (settings flag ota_pending). Spec 8.2 - such a + * reboot is not a power cut, so the light comes back effectively as power_on_behaviour = last, + * whatever pob says. */ +nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b, + bool after_ota); bool nl_light_state_equal(const nl_light_state_t *a, const nl_light_state_t *b); diff --git a/test/test_boot_state.c b/test/test_boot_state.c index df7ba2b..c46db57 100644 --- a/test/test_boot_state.c +++ b/test/test_boot_state.c @@ -23,14 +23,14 @@ static void test_default_state(void) static void test_last_restores_everything(void) { nl_light_state_t last = red_off(); - nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("evk")); + nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("evk"), false); TEST_ASSERT_TRUE(nl_light_state_equal(&last, &s)); } static void test_last_without_saved_state_uses_default(void) { const nl_board_t *b = nl_board_find("evk"); - nl_light_state_t s = nl_boot_state(NL_POB_LAST, NULL, b); + nl_light_state_t s = nl_boot_state(NL_POB_LAST, NULL, b, false); nl_light_state_t d = nl_boot_default_state(b); TEST_ASSERT_TRUE(nl_light_state_equal(&d, &s)); } @@ -38,7 +38,7 @@ static void test_last_without_saved_state_uses_default(void) static void test_on_forces_on_with_last_values(void) { nl_light_state_t last = red_off(); - nl_light_state_t s = nl_boot_state(NL_POB_ON, &last, nl_board_find("evk")); + nl_light_state_t s = nl_boot_state(NL_POB_ON, &last, nl_board_find("evk"), false); TEST_ASSERT_TRUE(s.on); TEST_ASSERT_EQUAL_UINT8(40, s.brightness); TEST_ASSERT_EQUAL(NL_MODE_COLOR, s.mode); @@ -48,7 +48,7 @@ static void test_off_forces_off(void) { nl_light_state_t last = red_off(); last.on = true; - nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("evk")); + nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("evk"), false); TEST_ASSERT_FALSE(s.on); TEST_ASSERT_EQUAL_UINT8(40, s.brightness); } @@ -56,12 +56,51 @@ static void test_off_forces_off(void) static void test_sanitizes_stored_values(void) { nl_light_state_t last = {true, 0, NL_MODE_COLOR, 1, 2, 3, 9000}; - nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("e27_cw")); + nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("e27_cw"), false); TEST_ASSERT_EQUAL_UINT8(100, s.brightness); /* invalid 0 -> 100 */ TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode); /* no RGB on this board */ TEST_ASSERT_EQUAL_UINT16(5000, s.ct); /* clamped */ } +/* Spec 8.2: a reboot caused by an OTA is not a power cut - the light comes back effectively as + * power_on_behaviour = last, whatever pob says. */ +static void test_after_ota_ignores_pob_off(void) +{ + nl_light_state_t last = red_off(); + nl_light_state_t s; + last.on = true; + s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("evk"), true); + TEST_ASSERT_TRUE(s.on); + TEST_ASSERT_TRUE(nl_light_state_equal(&last, &s)); +} + +static void test_after_ota_ignores_pob_on(void) +{ + nl_light_state_t last = red_off(); /* stored state: off */ + nl_light_state_t s = nl_boot_state(NL_POB_ON, &last, nl_board_find("evk"), true); + TEST_ASSERT_FALSE(s.on); + TEST_ASSERT_TRUE(nl_light_state_equal(&last, &s)); +} + +static void test_after_ota_still_sanitizes(void) +{ + nl_light_state_t last = {true, 200, NL_MODE_COLOR, 1, 2, 3, 9000}; + nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("e27_cw"), true); + TEST_ASSERT_TRUE(s.on); + TEST_ASSERT_EQUAL_UINT8(100, s.brightness); + TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode); + TEST_ASSERT_EQUAL_UINT16(5000, s.ct); +} + +/* Nothing stored (first boot after a migration): there is no "last" to restore, so the normal + * power_on_behaviour rules apply even with the flag set. */ +static void test_after_ota_without_saved_state_uses_pob(void) +{ + const nl_board_t *b = nl_board_find("evk"); + nl_light_state_t s = nl_boot_state(NL_POB_OFF, NULL, b, true); + TEST_ASSERT_FALSE(s.on); +} + int main(void) { UNITY_BEGIN(); @@ -71,5 +110,9 @@ int main(void) RUN_TEST(test_on_forces_on_with_last_values); RUN_TEST(test_off_forces_off); RUN_TEST(test_sanitizes_stored_values); + RUN_TEST(test_after_ota_ignores_pob_off); + RUN_TEST(test_after_ota_ignores_pob_on); + RUN_TEST(test_after_ota_still_sanitizes); + RUN_TEST(test_after_ota_without_saved_state_uses_pob); return UNITY_END(); } From cf1e6a351c9777e0cbdb49365c3044422b1737ca Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 03:14:16 +0200 Subject: [PATCH 065/150] fix: Recovery-Rettungsweg gegen Endlosschleife, Werksreset und Fremdnetz absichern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C1: nl_recovery_try() verglich nichts mit der laufenden Firmware. Vor einer laufenden Rettungsstation - laut Spec 9 der Normalfall, solange eine Charge migriert - lud die Lampe bei jedem Boot dasselbe Image erneut und startete neu. nl_ota_start_recovery() setzt jetzt ein Flag, das nl_fwup_verify_tail() nur im Recovery-Pfad auswerten lässt: Trägt der Trailer dieselbe Version wie die laufende Firmware (nl_trailer_version(), bisher ungenutzt), wird das Image abgelehnt und der Boot läuft normal weiter. Ein absichtliches Neuflashen derselben Version über die REST-API bleibt möglich. Kein neuer Wert im last_ota_error-Vokabular (Spec 8.2); unterschieden wird über die Konsole. C1/I2: boot_ms wurde erst nach nl_recovery_try() gesetzt und der Power-Cycle-Zähler erst 5 s danach gelöscht - bei erfolgreicher Recovery also nie. Nach NL_FACTORY_RESET_BOOTS Runden folgte ein Werksreset. boot_ms startet jetzt am Anfang von app_task (damit gilt wieder das in Spec 3.4/5.5 zugesagte 5-s-Fenster statt faktisch ~8 s), und nl_app_clear_reset_counter() löscht den Zähler zusätzlich, sobald die Rettungsstation gefunden ist. I3: tls_sys_auto_mode_run() stößt direkt nach UserMain() einen SDK-eigenen Verbindungsversuch an; das dabei entstehende NETIF_IP_NET_UP war in recovery_join_event() nicht vom eigenen Join zu unterscheiden, die Lampe hätte im falschen Netz geladen. nl_wifi_disable_sdk_auto_connect() schaltet das Flag ab und kappt eine bestehende Verbindung jetzt am Anfang von nl_recovery_try(); zusätzlich prüft nl_wifi_join_blocking() über tls_wifi_get_current_bss(), dass die Verbindung wirklich zur erwarteten SSID gehört. --- src/app_main.c | 23 +++++++++++++++------- src/net/nl_recovery.c | 16 +++++++++++++++- src/net/nl_wifi.h | 5 +++++ src/net/nl_wifi_w600.c | 43 +++++++++++++++++++++++++++++++++++------- src/sys/nl_app.h | 6 ++++++ src/sys/nl_ota.c | 40 +++++++++++++++++++++++++++++++++++---- src/sys/nl_ota.h | 5 +++++ 7 files changed, 119 insertions(+), 19 deletions(-) diff --git a/src/app_main.c b/src/app_main.c index 8b1bee2..d3125bd 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -33,6 +33,7 @@ static tls_os_mutex_t *s_store_lock; /* serialises all nl_settings_* / nl_state_ static nl_settings_t s_settings; static volatile uint32_t s_light_beat; static volatile bool s_factory_reset_requested; +static volatile bool s_counter_cleared; static u32 s_app_stack[1024]; static u32 s_light_stack[512]; @@ -88,6 +89,15 @@ void nl_app_reboot(void) void nl_app_request_factory_reset(void) { s_factory_reset_requested = true; } +void nl_app_clear_reset_counter(void) +{ + if (s_counter_cleared) + return; + store_lock(); + s_counter_cleared = nl_resetcnt_clear() == 0; + store_unlock(); +} + int nl_app_set_wifi_credentials(const char *ssid, const char *pass) { nl_app_lock(); @@ -149,12 +159,15 @@ static void app_task(void *arg) uint32_t base = nl_flash_region_base(); nl_engine_cfg_t cfg; nl_light_state_t last, initial, pending; - bool have_last, after_ota, counter_cleared = false; + bool have_last, after_ota; bool light_alive = false, net_alive = false, http_alive = false; uint8_t boots; uint32_t boot_ms, light_seen, net_seen, http_seen; (void)arg; + /* I2: the 5 s of spec 3.4 step 7 / 5.5 count from power-up, not from "after the recovery + * scan" - otherwise the documented "< 5 s per power cycle" window silently becomes ~8 s. */ + boot_ms = nl_app_now_ms(); nl_flash_init(); nl_led_init(board); /* hardware init first (spec 3.4): LEDs dark before any flash/state scan */ store_lock(); @@ -217,7 +230,6 @@ static void app_task(void *arg) nl_http_server_start(nl_app_api_ctx()); - boot_ms = nl_app_now_ms(); light_seen = s_light_beat; net_seen = nl_net_beat; http_seen = nl_http_beat; @@ -228,11 +240,8 @@ static void app_task(void *arg) tls_os_time_delay(HZ / 10); /* 100 ms */ now = nl_app_now_ms(); - if (!counter_cleared && now - boot_ms >= NL_RESETCNT_CLEAR_MS) { - store_lock(); - counter_cleared = nl_resetcnt_clear() == 0; - store_unlock(); - } + if (!s_counter_cleared && now - boot_ms >= NL_RESETCNT_CLEAR_MS) + nl_app_clear_reset_counter(); if (s_factory_reset_requested) nl_app_factory_reset(); /* does not return */ diff --git a/src/net/nl_recovery.c b/src/net/nl_recovery.c index 65271aa..8117a5a 100644 --- a/src/net/nl_recovery.c +++ b/src/net/nl_recovery.c @@ -109,9 +109,21 @@ void nl_recovery_try(void) { char url[128]; + /* I3: the SDK's auto-connect may already be joining its own stored AP in parallel (see + nl_wifi_disable_sdk_auto_connect()). Switch it off and drop that connection before we + scan, so everything that follows is our own doing. */ + nl_wifi_disable_sdk_auto_connect(); + if (!recovery_ap_present()) return; printf("recovery: station found, joining\r\n"); + /* C1: from here on this boot is a recovery boot - it may end in a flash + reset that never + returns to app_task, so the power-cycle counter must be cleared now instead of after the + 5 s deadline. Otherwise every recovery round would count as another short power cycle and + NL_FACTORY_RESET_BOOTS rounds in front of the station would wipe Wi-Fi, token and name. + Trade-off: while a rescue station is in range, the "5 x power cycle" factory reset does + not work - a lamp in front of the station is being serviced, not being reset. */ + nl_app_clear_reset_counter(); if (!nl_wifi_join_blocking(NINLIGHT_RECOVERY_SSID, NINLIGHT_RECOVERY_PASSWORD, NL_RECOVERY_JOIN_MS)) { printf("recovery: join failed\r\n"); nl_ota_set_error("download"); @@ -119,7 +131,9 @@ void nl_recovery_try(void) } snprintf(url, sizeof url, "http://192.168.8.1/ninlight_%s_ota.img", NINLIGHT_DEVICE_ID); printf("recovery: fetching %s\r\n", url); - if (nl_ota_start(url) != 0) { + /* C1: recovery refuses an image that carries the running version - the station serves the + same file to every lamp and stays up for a whole migration batch. */ + if (nl_ota_start_recovery(url) != 0) { printf("recovery: cannot start download\r\n"); return; } diff --git a/src/net/nl_wifi.h b/src/net/nl_wifi.h index 2b0a61b..7704815 100644 --- a/src/net/nl_wifi.h +++ b/src/net/nl_wifi.h @@ -21,6 +21,11 @@ extern volatile uint32_t nl_net_beat; /* Starts the network task. An empty ssid starts the onboarding AP. Strings are copied. */ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass); +/* Switches the SDK's own auto-connect off and drops any connection it may already have brought + * up on its own. nl_wifi_start() does this too, but the boot recovery path runs before that and + * must not have the SDK joining its stored AP in parallel (I3, final review). Safe to call any + * number of times; only writes the SDK parameter when it is actually set. */ +void nl_wifi_disable_sdk_auto_connect(void); /* Joins a station directly (no nl_wifi_sm) and blocks until it gets an IP or timeout_ms * elapses; returns true on success. For use before nl_wifi_start() only - e.g. the boot * recovery path in nl_recovery.c - since it drives the SDK Wi-Fi calls itself instead of diff --git a/src/net/nl_wifi_w600.c b/src/net/nl_wifi_w600.c index f3201a7..acf0493 100644 --- a/src/net/nl_wifi_w600.c +++ b/src/net/nl_wifi_w600.c @@ -69,6 +69,25 @@ static void clear_softap_param(void) } } +/* I3: tls_sys_auto_mode_run() (sdk/Platform/Sys/tls_sys.c) kicks off an SDK-driven join right + * after UserMain() whenever the persisted auto-connect flag is on - and tls_sys.c:264-267 turns a + * temporary WIFI_AUTO_CNT_TMP_OFF back into WIFI_AUTO_CNT_ON and persists that, so the factory + * default "off" is not something we may rely on. Any such join is indistinguishable from our own + * in the netif status events, so switch it off (and drop a connection it already made) before the + * recovery scan, not only in nl_wifi_start(). */ +void nl_wifi_disable_sdk_auto_connect(void) +{ + u8 auto_mode = WIFI_AUTO_CNT_OFF; + + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_mode); + if (auto_mode != WIFI_AUTO_CNT_OFF) { + printf("wifi: SDK auto-connect was %u, switching it off\r\n", auto_mode); + auto_mode = WIFI_AUTO_CNT_OFF; + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_mode); + } + tls_wifi_disconnect(); +} + static void start_ap(void) { struct tls_softap_info_t ap; @@ -220,7 +239,6 @@ static void net_task(void *arg) void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass) { - u8 auto_mode = WIFI_AUTO_CNT_OFF; nl_wifi_actions_t a; struct netif *nif; @@ -233,12 +251,7 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass) copy(s_ap_pass, ap_pass, sizeof s_ap_pass); /* The SDK would otherwise reconnect on its own with credentials from its parameter area. */ - tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_mode); - if (auto_mode != WIFI_AUTO_CNT_OFF) { - auto_mode = WIFI_AUTO_CNT_OFF; - tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_mode); - } - tls_wifi_disconnect(); + nl_wifi_disable_sdk_auto_connect(); if (tls_wifi_softap_get_state() != WM_WIFI_DISCONNECTED) { /* C2: the SDK already brought up its own softap (e.g. from NETIF_IP_NET_UP before * we got a chance to run) - tear it down, our state machine owns AP lifecycle. */ @@ -369,5 +382,21 @@ bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_ tls_wifi_disconnect(); /* cancel a stuck/timed-out join attempt */ return false; } + /* I3 backstop: NETIF_IP_NET_UP carries no SSID, so an SDK-initiated join to its own stored AP + * would look exactly like ours - and the lamp would then fetch http://192.168.8.1/... from a + * foreign network. tls_wifi_get_current_bss() does report the SSID actually associated with + * (sdk/Include/WiFi/wm_wifi.h:190), so verify it before the caller starts a download. */ + { + struct tls_curr_bss_t bss; + size_t n = strlen(ssid); + + memset(&bss, 0, sizeof bss); + tls_wifi_get_current_bss(&bss); + if (bss.ssid_len != (u8)n || memcmp(bss.ssid, ssid, n) != 0) { + printf("wifi: joined a different network than %s, dropping the connection\r\n", ssid); + tls_wifi_disconnect(); + return false; + } + } return true; } diff --git a/src/sys/nl_app.h b/src/sys/nl_app.h index 0f454af..df355e7 100644 --- a/src/sys/nl_app.h +++ b/src/sys/nl_app.h @@ -28,4 +28,10 @@ void nl_app_request_factory_reset(void); /* Stores new station credentials (from a successful onboarding test) and persists them. */ int nl_app_set_wifi_credentials(const char *ssid, const char *pass); +/* Clears the power-cycle counter ahead of the regular 5 s deadline (spec 5.5), idempotent. + * Used by the boot recovery path once it knows this boot is a recovery boot: a successful + * recovery never returns to the app task, so without this the counter would keep growing + * over recovery cycles and trigger a factory reset after NL_FACTORY_RESET_BOOTS rounds. */ +void nl_app_clear_reset_counter(void); + #endif diff --git a/src/sys/nl_ota.c b/src/sys/nl_ota.c index 60731fa..99360b0 100644 --- a/src/sys/nl_ota.c +++ b/src/sys/nl_ota.c @@ -10,6 +10,8 @@ #include "sys/nl_ota.h" #include "util/nl_fwup_trailer.h" +#include "nl_version.h" + /* t_http_fwup() (sdk/Src/App/OTA/wm_http_fwup.c) has no declaration in any SDK header. */ extern int t_http_fwup(char *url); @@ -19,8 +21,10 @@ extern int t_http_fwup(char *url); static char s_url[NL_OTA_URL_MAX]; static volatile bool s_busy; +static volatile bool s_reject_running_version; /* recovery path only, see nl_ota_start_recovery() */ static const char * volatile s_error; static u32 s_stack[NL_OTA_STACK]; +static char s_tail_version[NL_TRAILER_VERSION_LEN]; /* used by the hook, kept off its 2 KB stack */ void nl_ota_init(void) { @@ -42,10 +46,28 @@ bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t { nl_trailer_err_t e = nl_trailer_verify(tail, tail_len, NINLIGHT_DEVICE_ID, sha256); - if (e != NL_TRAILER_OK) + if (e != NL_TRAILER_OK) { s_error = nl_trailer_err_name(e); - printf("ota: trailer %s\r\n", e == NL_TRAILER_OK ? "ok" : s_error); - return e == NL_TRAILER_OK; + printf("ota: trailer %s\r\n", s_error); + return false; + } + /* C1: recovery only - the rescue station serves the same image to every lamp that boots in + * front of it, so an image that already matches the running version must be refused here; + * otherwise the lamp flashes and reboots in an endless loop. No new last_ota_error value is + * invented for this (the vocabulary is fixed in spec 8.2); the log line below is what tells + * the two cases apart. */ + if (s_reject_running_version) { + nl_trailer_version(tail, s_tail_version); + if (strcmp(s_tail_version, NINLIGHT_FW_VERSION) == 0) { + printf("ota: recovery image already carries the running version %s, not flashing\r\n", + s_tail_version); + s_error = "trailer"; + return false; + } + printf("ota: recovery image version %s (running %s)\r\n", s_tail_version, NINLIGHT_FW_VERSION); + } + printf("ota: trailer ok\r\n"); + return true; } /* Logs the URL without any "user:pass@" userinfo, so credentials embedded in the URL never @@ -83,7 +105,7 @@ static void ota_task(void *arg) tls_os_task_del(NL_OTA_PRIO, NULL); } -int nl_ota_start(const char *url) +static int ota_start(const char *url, bool reject_running_version) { size_t n; @@ -98,6 +120,7 @@ int nl_ota_start(const char *url) } s_busy = true; s_error = NULL; + s_reject_running_version = reject_running_version; memcpy(s_url, url, n + 1); /* Persist the open light state and mark the pending update before the flash gets busy. */ @@ -122,8 +145,17 @@ int nl_ota_start(const char *url) nl_app_unlock(); (void)nl_app_save_settings(); s_busy = false; + /* Minor (final review): no value in the fixed vocabulary of spec 8.2 describes "could not + * create the download task" (out of RAM/TCBs), and "download" would claim a transfer that + * never happened. `busy` stays the closest fit - the chip really is too busy to start one - + * and this log line is what distinguishes it from "an update is already running". */ + printf("ota: cannot create download task (out of resources)\r\n"); s_error = "busy"; return -1; } return 0; } + +int nl_ota_start(const char *url) { return ota_start(url, false); } + +int nl_ota_start_recovery(const char *url) { return ota_start(url, true); } diff --git a/src/sys/nl_ota.h b/src/sys/nl_ota.h index e1e6c7f..62d0d6b 100644 --- a/src/sys/nl_ota.h +++ b/src/sys/nl_ota.h @@ -6,6 +6,11 @@ void nl_ota_init(void); /* Starts a download task for an http:// URL. 0 = started, -1 = busy or invalid. */ int nl_ota_start(const char *url); +/* Same, but for the boot recovery path (spec 8.3): an image whose trailer carries the running + * firmware version is rejected instead of flashed. The rescue station stays up while a whole + * batch of lamps migrates, so without this check every boot would re-flash the same image + * forever. A deliberate re-flash of the same version stays possible over the REST API. */ +int nl_ota_start_recovery(const char *url); bool nl_ota_busy(void); /* NULL, or one of: download, crc, trailer, device_mismatch, hash, busy */ const char *nl_ota_last_error(void); From 8af58d01240cb2b5325124c189b41112a94769df Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 03:14:31 +0200 Subject: [PATCH 066/150] fix: HTTP-Server-Heartbeat auch im Fehlerpfad ticken MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schlägt socket(), bind() oder listen() fehl, schlief die äußere Schleife eine Sekunde und versuchte es erneut, ohne nl_http_beat zu erhöhen. Da app_task den Watchdog nur füttert, wenn alle drei Heartbeats vorankommen, resettete der Chip nach 20 s, bootete, scheiterte wieder - dauerhaft. Der Retry selbst ist die Fehlerbehandlung, nicht der Reboot. --- src/net/nl_http_server.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/net/nl_http_server.c b/src/net/nl_http_server.c index 89e7cf0..9eb7020 100644 --- a/src/net/nl_http_server.c +++ b/src/net/nl_http_server.c @@ -96,6 +96,11 @@ static void server_task(void *arg) if (ls < 0 || bind(ls, (struct sockaddr *)&addr, sizeof addr) != 0 || listen(ls, 4) != 0) { if (ls >= 0) closesocket(ls); + /* I4: keep the heartbeat ticking while retrying. app_task only feeds the watchdog + * once all three heartbeats moved, so without this a failing socket()/bind()/listen() + * (e.g. no netif yet, or lwIP temporarily out of memory) would reset the chip after + * 20 s, boot, fail again - forever. The retry itself is the recovery, not a reboot. */ + nl_http_beat++; tls_os_time_delay(HZ); continue; } From a400cc1649179eae2234834dfa670b2ad02c890f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 03:14:31 +0200 Subject: [PATCH 067/150] fix: CRC im Settings-Record dort schreiben, wo der Leser sie erwartet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nl_settings_save() rechnete die CRC über [0, offsetof(rec_t, crc)) und legte sie dort ab, read_rec() liest sie bei sizeof(rec_hdr_t) + hdr.length. Beide Offsets stimmen nur überein, solange sizeof(nl_settings_t) ein Vielfaches von 4 ist (aktuell 204). Ein zusätzliches uint16_t - in Plan 4 (Migration) sehr wahrscheinlich - macht 206, und jeder gespeicherte Record fiele beim nächsten Boot still durch die CRC: WLAN, Token und Name wären auf allen Lampen weg. Geschrieben wird jetzt explizit bei NL_SETTINGS_CRC_OFF(length), also exakt an der Leseposition; dazu eine Übersetzungszeit-Zusicherung, dass die Nutzdaten direkt hinter dem Header beginnen (die CRC läuft auf beiden Seiten über einen zusammenhängenden Block). Test: test_crc_offset_matches_reader prüft die Symmetrie unabhängig von der Strukturgröße. Gegenprobe mit einem zusätzlichen uint16_t im Struct: mit der alten Schreibposition fallen vier Settings-Tests, mit der neuen keiner. --- src/config/nl_settings.c | 24 +++++++++++++++++++----- test/test_settings.c | 25 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/config/nl_settings.c b/src/config/nl_settings.c index aa41d47..5376a16 100644 --- a/src/config/nl_settings.c +++ b/src/config/nl_settings.c @@ -24,9 +24,20 @@ typedef struct { typedef struct { rec_hdr_t hdr; nl_settings_t data; - uint32_t crc; + uint32_t crc; /* NB: only a size reserve - the CRC is written at NL_SETTINGS_CRC_OFF() */ } rec_t; +/* Where read_rec() expects the CRC, and therefore where nl_settings_save() must put it: directly + * behind the `length` payload bytes, not at offsetof(rec_t, crc). Both offsets are only equal + * while sizeof(nl_settings_t) is a multiple of 4 (204 today). Adding a single uint16_t would move + * offsetof(rec_t, crc) two bytes further than the reader looks and every stored record would + * silently fail its CRC on the next boot - Wi-Fi, token and name gone on every lamp. */ +#define NL_SETTINGS_CRC_OFF(len) (sizeof(rec_hdr_t) + (uint32_t)(len)) + +/* The CRC runs over header and payload as one contiguous block on both sides, so the payload + * really has to start right behind the header (no padding). Compile-time, gnu99-compatible. */ +typedef char nl_settings_layout_check[offsetof(rec_t, data) == sizeof(rec_hdr_t) ? 1 : -1]; + static uint32_t s_addr[2]; void nl_settings_defaults(nl_settings_t *s) @@ -68,7 +79,7 @@ static bool read_rec(int slot, rec_t *r) nl_settings_defaults(&r->data); if (nl_flash_read(s_addr[slot] + sizeof hdr, &r->data, hdr.length) != 0) return false; - if (nl_flash_read(s_addr[slot] + sizeof hdr + hdr.length, &stored_crc, sizeof stored_crc) != 0) + if (nl_flash_read(s_addr[slot] + NL_SETTINGS_CRC_OFF(hdr.length), &stored_crc, sizeof stored_crc) != 0) return false; crc = nl_crc32(0, &hdr, sizeof hdr); @@ -104,7 +115,7 @@ int nl_settings_save(const nl_settings_t *s) static rec_t a, b, r; /* keep ~700 bytes off the task stack */ bool va = read_rec(0, &a); bool vb = read_rec(1, &b); - uint32_t seq; + uint32_t seq, crc, crc_off; int slot; if (va && vb) { @@ -127,11 +138,14 @@ int nl_settings_save(const nl_settings_t *s) r.hdr.length = sizeof(nl_settings_t); r.hdr.seq = seq; r.data = *s; - r.crc = nl_crc32(0, &r, offsetof(rec_t, crc)); + /* Write the CRC exactly where read_rec() reads it (see NL_SETTINGS_CRC_OFF). */ + crc_off = NL_SETTINGS_CRC_OFF(r.hdr.length); + crc = nl_crc32(0, &r, crc_off); + memcpy((uint8_t *)&r + crc_off, &crc, sizeof crc); if (nl_flash_erase_sector(s_addr[slot]) != 0) return -1; - return nl_flash_write(s_addr[slot], &r, sizeof r); + return nl_flash_write(s_addr[slot], &r, crc_off + sizeof crc); } int nl_settings_erase(void) diff --git a/test/test_settings.c b/test/test_settings.c index 67a6949..4ccbbfe 100644 --- a/test/test_settings.c +++ b/test/test_settings.c @@ -135,6 +135,30 @@ static void test_ota_pending_default_and_roundtrip(void) TEST_ASSERT_EQUAL_UINT8(1, out.ota_pending); } +/* I6: the writer must place the CRC exactly where the reader looks for it - directly behind + * `length` payload bytes. Both sides only agreed by accident while sizeof(nl_settings_t) is a + * multiple of 4; one extra uint16_t in the struct used to move the written CRC two bytes past + * the read offset, which would have failed every stored record's CRC after an update. This test + * nails the invariant independently of the struct size. */ +static void test_crc_offset_matches_reader(void) +{ + nl_settings_t in = sample("crc"), out; + uint16_t length; + uint32_t crc, stored; + const uint8_t *rec = &fake_flash_mem[NL_FL_SETTINGS_A]; + + TEST_ASSERT_EQUAL_INT(0, nl_settings_save(&in)); + memcpy(&length, rec + 6, 2); /* header.length */ + TEST_ASSERT_EQUAL_UINT16(sizeof(nl_settings_t), length); + + crc = nl_crc32(0, rec, 12u + length); /* header + exactly `length` payload bytes */ + memcpy(&stored, rec + 12u + length, 4); /* where read_rec() takes the CRC from */ + TEST_ASSERT_EQUAL_HEX32(crc, stored); + + TEST_ASSERT_TRUE(nl_settings_load(&out)); /* and the record really does read back */ + TEST_ASSERT_EQUAL_MEMORY(&in, &out, sizeof in); +} + static void test_erase(void) { nl_settings_t s = sample("x"), out; @@ -154,6 +178,7 @@ int main(void) RUN_TEST(test_load_accepts_shorter_record); RUN_TEST(test_load_rejects_longer_record); RUN_TEST(test_ota_pending_default_and_roundtrip); + RUN_TEST(test_crc_offset_matches_reader); RUN_TEST(test_erase); return UNITY_END(); } From f434c013e706a52e862f154320018e92c8f0db82 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 03:15:01 +0200 Subject: [PATCH 068/150] build: Build-Datum zusammen mit der Version bei jedem Build erzeugen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NINLIGHT_BUILD_DATE entstand als string(TIMESTAMP) zur Konfigurationszeit, während die Version seit bf8f76e bei jedem Build neu geschrieben wird - system/info.build_date konnte dadurch tagealt sein. Das Datum wandert in cmake/write_version.cmake und damit in denselben generierten Header wie die Version. --- CMakeLists.txt | 1 - cmake/version.cmake | 5 +++-- cmake/write_version.cmake | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f500947..bae47fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,7 +107,6 @@ add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS set_target_properties(${NL_FW} PROPERTIES SUFFIX ".elf") target_compile_definitions(${NL_FW} PRIVATE NINLIGHT_DEVICE_ID="${NINLIGHT_DEVICE}" - NINLIGHT_BUILD_DATE="${NINLIGHT_BUILD_DATE}" NINLIGHT_TOKEN_DEFAULT="${NINLIGHT_TOKEN}" NINLIGHT_WIFI_SSID="${NINLIGHT_WIFI_SSID}" NINLIGHT_WIFI_PASSWORD="${NINLIGHT_WIFI_PASSWORD}" diff --git a/cmake/version.cmake b/cmake/version.cmake index 1a9917c..e73907e 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -1,8 +1,9 @@ -# The version is regenerated on every build so the OTA trailer and system/info match the binary. +# Version *and* build date are regenerated on every build, so the OTA trailer and system/info +# match the binary. The build date used to be a configure-time string(TIMESTAMP), which left +# system/info.build_date days behind the actual build after the version generation moved here. set(NL_VERSION_HEADER ${CMAKE_BINARY_DIR}/generated/nl_version.h) add_custom_target(nl_version ALL BYPRODUCTS ${NL_VERSION_HEADER} COMMAND ${CMAKE_COMMAND} -DOUT=${NL_VERSION_HEADER} -DSRC=${CMAKE_SOURCE_DIR} -P ${CMAKE_SOURCE_DIR}/cmake/write_version.cmake COMMENT "Updating nl_version.h") -string(TIMESTAMP NINLIGHT_BUILD_DATE "%Y-%m-%d") diff --git a/cmake/write_version.cmake b/cmake/write_version.cmake index 3137306..4f81854 100644 --- a/cmake/write_version.cmake +++ b/cmake/write_version.cmake @@ -5,7 +5,8 @@ if(NOT V) endif() string(REGEX REPLACE "^v" "" V "${V}") string(SUBSTRING "${V}" 0 15 V) -set(CONTENT "#define NINLIGHT_FW_VERSION \"${V}\"\n") +string(TIMESTAMP BUILD_DATE "%Y-%m-%d") +set(CONTENT "#define NINLIGHT_FW_VERSION \"${V}\"\n#define NINLIGHT_BUILD_DATE \"${BUILD_DATE}\"\n") if(EXISTS ${OUT}) file(READ ${OUT} OLD) else() From 49d6b329b033d496c784c1c0095adcac2bf62ed0 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 03:27:13 +0200 Subject: [PATCH 069/150] fix: printf statt wegkompilierter TLS_DBGPRT_ERR-Meldung bei fls_lock-Fehler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wie beim s_fls2_lock-Fehlerpfad (T1, Plan 2) blieb auch ein Fehlschlag beim Anlegen des primaeren fls_lock-Semaphors unsichtbar, weil TLS_DBGPRT_ERR in diesem Build wegkompiliert ist. tls_fls_init() haette TLS_FLS_STATUS_ENOMEM stumm zurueckgegeben. Ergaenzt außerdem den Zeitobergrenzen-Kommentar in nl_recovery.c: die Obergrenze schuetzt nur gegen einen echten Stillstand, nicht gegen einen langsamen, aber laufenden Download - wm_fwup.c bedient den Watchdog bei jeder empfangenen Nachricht selbst. --- .../Drivers/internalflash/wm_internal_fls.c | 5 ++++- src/net/nl_recovery.c | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c index 8b6f5c8..02837b4 100644 --- a/sdk/Platform/Drivers/internalflash/wm_internal_fls.c +++ b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c @@ -1170,7 +1170,10 @@ int tls_fls_init(void) if (err != TLS_OS_SUCCESS) { tls_mem_free(fls); - TLS_DBGPRT_ERR("create semaphore @fls_lock fail!\n"); + /* ninlight patch 5: TLS_DBGPRT_ERR is compiled out in this build, so this failure - the + * whole flash layer stays without its lock and tls_fls_init() returns an error nobody + * prints - would be silent. See sdk/PATCHES.md. */ + printf("wm_internal_fls: fls_lock create failed\r\n"); return TLS_FLS_STATUS_ENOMEM; } if (tls_os_sem_create(&s_fls2_lock, 1) != TLS_OS_SUCCESS) diff --git a/src/net/nl_recovery.c b/src/net/nl_recovery.c index 8117a5a..64a9c4d 100644 --- a/src/net/nl_recovery.c +++ b/src/net/nl_recovery.c @@ -7,19 +7,24 @@ #include "wm_watchdog.h" #include "net/nl_wifi.h" +#include "sys/nl_app.h" #include "sys/nl_ota.h" #define NL_RECOVERY_SCAN_MS 3000u #define NL_RECOVERY_JOIN_MS 15000u #define NL_RECOVERY_BUF 2048u -/* Generous cap on how long the download itself may run. wm_http_fwup.c's own resume loop - can already take up to ~20 minutes on a slow/aborted transfer (200 rounds x up to 5 s - receive timeout / 2 s retry delay), and a real hang - e.g. its unbounded +/* Cap on how long this task keeps feeding the watchdog while the download runs. wm_http_fwup.c's + own resume loop can already take up to ~20 minutes on a slow/aborted transfer (200 rounds x up + to 5 s receive timeout / 2 s retry delay), and a real hang - e.g. its unbounded "while (!p) { tls_os_time_delay(1); pbuf_alloc(...) }" retry on allocation failure - (wm_http_fwup.c:178) - never returns from t_http_fwup() at all. Past this cap we stop - feeding the watchdog (see below) instead of waiting indefinitely: a watchdog reset is - strictly better than a silent hang with no Wi-Fi and no console, and a fresh boot simply - retries recovery from scratch. */ + (wm_http_fwup.c:178) - never returns from t_http_fwup() at all. Past this cap we stop feeding + the watchdog: a watchdog reset is strictly better than a silent hang with no Wi-Fi and no + console, and a fresh boot simply retries recovery from scratch. + I5 (final review), important limitation: this does NOT put a hard time limit on the download. + The SDK's own fwup task calls tls_watchdog_clr() for every message it receives + (sdk/Platform/Common/fwup/wm_fwup.c:201), so a slow but *progressing* transfer keeps the + watchdog alive well past this cap. Only a real stall - the peer stays connected but sends + nothing more, or the allocation loop above spins - actually runs into the reset. */ #define NL_RECOVERY_DOWNLOAD_MAX_MS (10u * 60u * 1000u) static volatile bool s_scan_done; From 3841a3473c210075271f4e89e98468a3d8caaca6 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 03:27:33 +0200 Subject: [PATCH 070/150] docs: Recovery-Zeitobergrenze, aktiven Boot-Scan und offene Pruefpunkte nachziehen sdk/PATCHES.md, README.md und docs/hardware-checklist.md beschrieben teils einen ueberholten Stand: die Watchdog-Einschaetzung in PATCHES.md Punkt 4 stammte noch von vor dem Drei-Heartbeat-Gate in app_main.c und vor der Zeitobergrenze in nl_recovery.c; README behauptete eine harte Obergrenze ueber dem gesamten Download statt nur ueber einem echten Stillstand; der Boot-Scan ist aktiv (~3 s), nicht passiv wie an einer Stelle der Spec beschrieben (Spec bleibt unveraendert). PATCHES.md Punkt 1 ergaenzt den sys/nl_ota.h-Include in wm_fwup.c, Punkt 5 den fls_lock-printf. hardware-checklist.md: Recovery-Zeile auf "Gegenstelle sendet nichts mehr" praezisiert; die beiden Werksreset-Haken aus Plan 1 zurueckgesetzt, weil der vorgelagerte Recovery-Scan das Boot-Timing verschiebt und sie neu geprueft werden muessen; acht neue Pruefpunkte fuer Plan 3 ergaenzt (Zweitversuch nach Ablehnung, REST-Erreichbarkeit waehrend/nach Ablehnung, Lichtverhalten ueber OTA mit pob=off/on, fw_version gegen Trailer, echter Rettungsfall, Rettungsstation nach Erfolg angelassen, AP-Modus + auth_all) sowie ein Betriebshinweis zu NINLIGHT_RECOVERY_PASSWORD/NINLIGHT_TOKEN fuer den Flottenbau. --- README.md | 14 ++++++++----- docs/hardware-checklist.md | 15 +++++++++++--- sdk/PATCHES.md | 41 +++++++++++++++++++++++++++----------- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index ddfe0b0..01c5070 100644 --- a/README.md +++ b/README.md @@ -120,11 +120,15 @@ erreichbar ist), sucht sie kurz nach einem Rettungs-Zugangspunkt: SSID/Passwort `http://192.168.8.1/ninlight__ota.img` (z.B. `ninlight_evk_ota.img`) — der Dateiname enthält den Gerätetyp, `` ist der Wert von `NINLIGHT_DEVICE` aus dem Build. -Bei jedem Boot scannt die Firmware dafür bis zu ~3 Sekunden nach der Station und loggt das Ergebnis -(gefunden, nichts gefunden, oder Scan fehlgeschlagen). Ist keine Station in Reichweite, verlängert sich der -Boot nur um diese Scan-Zeit; danach startet der normale WLAN-Aufbau wie gewohnt. Über dem Download selbst -liegt eine harte Zeitobergrenze — läuft sie ab, wird der Watchdog nicht mehr bedient und der Chip startet neu, -statt unbeobachtet hängen zu bleiben; der nächste Boot versucht Recovery erneut. +Bei jedem Boot scannt die Firmware dafür bis zu ~3 Sekunden aktiv (`tls_wifi_scan()`, kein passiver +Scan) nach der Station und loggt das Ergebnis (gefunden, nichts gefunden, oder Scan fehlgeschlagen). +Ist keine Station in Reichweite, verlängert sich der Boot nur um diese Scan-Zeit; danach startet der +normale WLAN-Aufbau wie gewohnt. Über dem Download selbst liegt keine harte Zeitobergrenze — ein +langsamer, aber weiter laufender Transfer füttert den Watchdog über den SDK-eigenen Update-Task mit +und darf beliebig lange dauern. Erst ein echter Stillstand (die Gegenstelle bleibt verbunden, sendet +aber keine Daten mehr, oder eine interne Allokations-Warteschleife hängt) lässt den Watchdog nach +10 Minuten auslaufen; der Chip startet dann neu, statt unbeobachtet zu hängen, und der nächste Boot +versucht Recovery erneut. **Vertrauensgrenze:** Der Trailer prüft Gerätetyp und SHA256 — das ist kein kryptografisches Signaturverfahren. Der Download läuft über `http://` ohne TLS, und der Recovery-Kanal ist ausschließlich durch das oben genannte diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index a57ac82..e2b74a4 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -17,8 +17,8 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] `pob off`: Strom aus/an → bleibt aus; `on` → letzte Farbe - [x] `pob on` bei ausgeschaltetem Licht: Strom aus/an → an - [x] `fade 2000 2000` bleibt nach Reboot erhalten (`state`) -- [x] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`state`) -- [x] Normaler Boot nach > 5 s Laufzeit → kein Werksreset +- [ ] 5× Strom an/aus mit jeweils < 5 s Laufzeit → 3× Blinken, danach Defaults (`state`) (erneut zu prüfen: der vorgelagerte Recovery-Scan aus Plan 3 verschiebt jeden Boot um bis zu ~3 s, das Timing der 5-Sekunden-Grenze ist damit nicht mehr das von Plan 1) +- [ ] Normaler Boot nach > 5 s Laufzeit → kein Werksreset (erneut zu prüfen, gleicher Grund: Recovery-Scan verschiebt die Boot-Zeitrechnung) - [x] Watchdog: `wdtest` → Neustart nach ca. 20–40 s (erste Ablaufzeit löst IRQ aus, Reset beim zweiten Ablauf) — getestet 2026-09-11 mit e19ee98: Neustart nach 40,4 s - [x] Getestet am 2026-09-11 mit Firmware 7502f4f am EVK (MAC 28-6D-CD-5C-68-1F) @@ -46,8 +46,17 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler - [ ] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen - [ ] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) -- [ ] Recovery: Download hängt/dauert sehr lange → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben +- [ ] Recovery: Gegenstelle bleibt verbunden, sendet aber keine Daten mehr (nicht: ein langsamer, aber laufender Download) → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben - [ ] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten) +- [ ] Zweiter `fw_update` mit gültigem Image direkt nach einer Ablehnung, ohne zwischenzeitlichen Reboot → läuft durch, `system/info` meldet danach die neue Version +- [ ] Lampe bleibt während und nach einer Ablehnung über REST erreichbar: `system/info` und `device/state` antworten beide weiterhin mit 200 +- [ ] Lichtverhalten über ein OTA hinweg (Spec §8.2, ein Update ist kein Stromausfall): einmal mit `power_on_behaviour = off`, einmal `= on` — die Lampe ist nach dem Update jeweils in demselben Zustand (an/aus, Farbe/Helligkeit), in dem sie vor dem Update war +- [ ] `system/info.fw_version` nach einem OTA stimmt mit der Version im Trailer des geflashten Images überein (Trailer-Version z.B. mit `tools/mkimage` prüfen) +- [ ] Recovery im echten Rettungsfall: Lampe ohne gültige WLAN-Konfiguration bzw. im Onboarding-AP findet die Station, zieht das Image und startet mit der neuen Firmware +- [ ] Rettungsstation nach erfolgreicher Recovery angelassen lassen, Lampe neu booten lassen → kein erneutes Flashen (Version stimmt bereits überein) und kein Werksreset (Power-Cycle-Zähler wurde vor dem Recovery-Versuch gelöscht) +- [ ] OTA im AP-/Fallback-Modus (`mode=apsta`, kein Router erreichbar) und mit `auth_all = true` → Update läuft durch wie im normalen `sta`-Modus + +Für den echten Flottenbau `NINLIGHT_RECOVERY_PASSWORD` und `NINLIGHT_TOKEN` in `config.local.cmake` setzen — die Defaults sind nur fürs EVK gedacht. ## Später (Lampen) - [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index 175a02b..86f5868 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -8,7 +8,8 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ `Check_OTA_Image()` durch den Hook `nl_fwup_verify_tail()` (src/sys/nl_fwup_hook.h). Das Image-Format (Header + Payload + 512-Byte-Endblock) bleibt unverändert. Die starke Definition des Hooks liegt seit Plan 3 in `src/sys/nl_ota.c` (Trailer-Prüfung über `nl_trailer_verify()`); die bisherige `weak`-Variante - `src/sys/fwup_hook_default.c` ist entfallen. + `src/sys/fwup_hook_default.c` ist entfallen. Seit Plan 3 bindet `wm_fwup.c` außerdem `sys/nl_ota.h` + ein (für den `nl_fwup_verify_tail()`-Hook) — der SDK-Code linkt damit direkt gegen App-Code. Fix-Runde 1 (Task-4-Review) hat drei weitere Stellen in diesem Bereich korrigiert: - `wm_fwup.c`, Bedingung um `nl_fwup_verify_tail()`: Checksummen- und Trailer-Prüfung liefen in einer kurzschließenden `||`-Bedingung; bei falscher Checksumme wurde `nl_fwup_verify_tail()` nie @@ -82,17 +83,29 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ damit nie ausgelöst, weil der Idle-Task munter weiter gefüttert hätte. Der `tls_watchdog_clr()`- Aufruf wurde entfernt; `wfi` bleibt. Der einzige verbleibende `tls_watchdog_clr()`-Aufruf im SDK ist in `Platform/Common/fwup/wm_fwup.c` (Firmware-Update-Task). - **Einschätzung aktualisiert (Task 4, Fix-Runde 1):** Als dieser Punkt geschrieben wurde, gab es noch - keinen OTA-Pfad - das Fenster war rein theoretisch. Mit Task 4 ist es real und kann durch einen - langsamen oder hängenden Download mehrere Minuten dauern: Solange `wm_fwup.c` seinen eigenen - `tls_watchdog_clr()` aufruft, wird der Watchdog gefüttert, **egal** ob `light_task`, `nl_wifi`/`nl_mdns` - oder `nl_http_server` in dieser Zeit echt hängen - der App-Task-Heartbeat-Mechanismus in - `src/app_main.c` (der genau das eigentlich verhindern soll) greift währenddessen nicht, weil der - Watchdog gar nicht bei ihm ankommt. Das ist kein Rückschritt gegenüber vorher (vor Task 4 gab es - dieses Fenster schlicht nicht), aber die bisherige Einstufung „unkritisch" war nur so lange richtig, - wie der Fall real nie eintrat. Kein Fix in dieser Runde - der Watchdog-Heartbeat müsste dafür wissen, - ob der fwup-Task selbst noch vorankommt (z. B. über einen eigenen Beat-Zähler, den `app_task` - zusätzlich zu `light`/`net`/`http` prüft); das ist ein eigenständiger Task für einen späteren Plan. + **Einschätzung aktualisiert (Plan 3, Abschluss-Review):** Als dieser Punkt geschrieben wurde, gab es + noch keinen OTA-Pfad - das Fenster war rein theoretisch. Inzwischen gilt: + - Im normalen Betrieb (`app_task()` in `src/app_main.c`) füttert nicht mehr jeder Durchlauf den + Watchdog, sondern erst, wenn `light_task`, `nl_wifi`/`nl_mdns` und `nl_http_server` seit dem + letzten Füttern *alle drei* einen eigenen Beat-Zähler erhöht haben - ein einzelner hängender Task + (z.B. `nl_app_lock()` für immer gehalten) lässt den Watchdog jetzt tatsächlich auslaufen. Das war + zum Zeitpunkt der ursprünglichen Einschätzung noch nicht gebaut. + - Solange `t_http_fwup()` läuft, ruft der SDK-eigene fwup-Task (`Platform/Common/fwup/wm_fwup.c:201`) + bei **jeder** empfangenen Nachricht selbst `tls_watchdog_clr()` auf - unabhängig vom Drei-Heartbeat- + Gate oben. Das Fenster aus der ursprünglichen Einschätzung besteht also während eines laufenden + OTA- oder Recovery-Downloads weiterhin: ein `light_task`/`nl_wifi`/`nl_http_server`-Hänger während + dieser Zeit wird vom Watchdog nicht erkannt, weil der fwup-Task ihn unabhängig davon bedient. + - Für den Recovery-Pfad (`src/net/nl_recovery.c`, `NL_RECOVERY_DOWNLOAD_MAX_MS`) gibt es seit Plan 3 + zusätzlich eine Obergrenze von 10 Minuten, nach der die Boot-Task selbst aufhört, den Watchdog zu + bedienen - das deckt aber nur einen echten Stillstand ab (Gegenstelle bleibt verbunden, sendet aber + nichts mehr, oder die `pbuf_alloc()`-Warteschleife in `wm_http_fwup.c` hängt), nicht das Fenster + oben: ein *vorankommender* Download hält den Watchdog über `wm_fwup.c` unabhängig von dieser + Obergrenze am Leben. Für den regulären OTA-Pfad (`fw_update` per REST) gibt es keine entsprechende + Obergrenze, da dort ein hängender Download die Lampe im Normalbetrieb belässt statt sie in einen + rettungslosen Boot-Zustand zu bringen. + Kein Fix in dieser Runde - der Watchdog-Heartbeat müsste dafür wissen, ob der fwup-Task selbst noch + vorankommt (z. B. über einen eigenen Beat-Zähler, den `app_task` zusätzlich zu `light`/`net`/`http` + prüft); das ist ein eigenständiger Task für einen späteren Plan. Getestet mit dem Konsolenkommando `wdtest` (src/sys/nl_console_cmd.c, src/sys/nl_console.c): es hält `nl_app_lock()` für immer, wodurch `light_task` und `app_task` blockieren und der Watchdog den Chip zurücksetzen muss. @@ -102,3 +115,7 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ `tls_os_sem_create(&s_fls2_lock, 1)` stillschweigend - jetzt eine `printf()`-Meldung (`TLS_DBGPRT_ERR` ist in diesem Build wegkompiliert), falls das Anlegen fehlschlägt (dann bliebe der Bereich oberhalb von 1 MB unprotokolliert ungeschützt). + I5 (Plan 3, Abschluss-Review): Aus demselben Grund (`TLS_DBGPRT_ERR` wegkompiliert) fehlte auch beim + Anlegen von `fls_lock` (dem primären Flash-Semaphor, wenige Zeilen darüber in `tls_fls_init()`) jede + sichtbare Meldung - ein Fehlschlag hätte `tls_fls_init()` mit `TLS_FLS_STATUS_ENOMEM` stumm scheitern + lassen. Jetzt ebenfalls ein `printf()`. From 6588085679656c48696613fad95b967d3d2ef522 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 10:23:54 +0200 Subject: [PATCH 071/150] fix: flash.sh akzeptiert den Port auch mit /dev/-Praefix Co-Authored-By: Claude Opus 5 --- tools/flash.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/flash.sh b/tools/flash.sh index 13ef62a..a7c8ec7 100755 --- a/tools/flash.sh +++ b/tools/flash.sh @@ -6,6 +6,8 @@ set -euo pipefail DEV=${1:?usage: tools/flash.sh [full]} PORT=${2:?usage: tools/flash.sh [full]} MODE=${3:-app} +# wm_tool prepends /dev/ itself - accept either spelling. +PORT=${PORT#/dev/} ROOT=$(cd "$(dirname "$0")/.." && pwd) TOOL="$ROOT/build/host-tools/wm_tool" if [ ! -x "$TOOL" ] || [ "$ROOT/sdk/tools/wm_tool.c" -nt "$TOOL" ]; then From d56f1d25d2dab1d9757a3c618e9ed6b34bf0a652 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 10:38:01 +0200 Subject: [PATCH 072/150] fix: SHA256-Kontext pro Image initialisieren statt einmal beim Task-Start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der fwup-Task überlebt jede Update-Sitzung. Weil nl_sha256_init() am Taskanfang stand, benutzte ab dem zweiten Update-Versuch pro Boot ein bereits finalisierter Kontext weiter - der Trailer-Hash konnte dann gar nicht mehr passen und jedes weitere Update scheiterte mit last_ota_error=hash. Am EVK gefunden: nach einer Ablehnung war ein Reboot nötig, bevor überhaupt wieder ein Update gelingen konnte. Co-Authored-By: Claude Opus 5 --- sdk/PATCHES.md | 7 + sdk/Platform/Common/fwup/wm_fwup.c | 2068 ++++++++++++++-------------- 2 files changed, 1043 insertions(+), 1032 deletions(-) diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index 86f5868..45d928e 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -70,6 +70,13 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ exportierte) `session_id`, wäre also ein größerer Eingriff für denselben Effekt. Nebenbei: `nRetCode = ret` (ein lwIP-`err_t` in einer sonst `HTTP_CLIENT_*`-Codes tragenden Variable) durch `HTTP_CLIENT_ERROR_BAD_STATE` ersetzt. + **Fix vom 2026-09-12 (am EVK gefunden):** `nl_sha256_init()` stand am Anfang von `fwup_scheduler()`, + also einmal beim Task-Start. Der Task überlebt aber jede Update-Sitzung, sodass ab dem zweiten Versuch + pro Boot ein bereits finalisierter Kontext weiterbenutzt wurde und der Trailer-Hash zwangsläufig nicht + mehr passte (`last_ota_error=hash`). Der Init sitzt jetzt dort, wo der `T_BOOTER`-Header eintrifft, also + genau einmal pro Image. Ohne diesen Fix war nach jeder Ablehnung ein Reboot nötig, bevor ein Update + überhaupt wieder gelingen konnte. + 2. `Src/OS/RTOS/include/FreeRTOSConfig.h`: `configGENERATE_RUN_TIME_STATS 0`, QConnex-Debug-Hooks entfernt. 3. `undefined reference` beim Linken auf `EarlyInit` und `pre_gpio_config` (von `sdk/Platform/Sys/wm_main.c` als `extern`-Hooks erwartet, aber nur von der App bereitgestellt): diff --git a/sdk/Platform/Common/fwup/wm_fwup.c b/sdk/Platform/Common/fwup/wm_fwup.c index de54dc9..f876dba 100644 --- a/sdk/Platform/Common/fwup/wm_fwup.c +++ b/sdk/Platform/Common/fwup/wm_fwup.c @@ -1,1032 +1,1036 @@ -/***************************************************************************** -* -* File Name : wm_fwup.c -* -* Description: firmware update Module -* -* Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd. -* All rights reserved. -* -* Author : dave -* -* Date : 2014-6-16 -*****************************************************************************/ -#include -#include -#include - -#include "wm_mem.h" -#include "list.h" -#include "wm_debug.h" -#include "wm_internal_flash.h" -#include "wm_flash.h" -#include "wm_crypto_hard.h" - -#include "utils.h" -#include "wm_fwup.h" -#include "wm_watchdog.h" -#include "wm_wifi.h" -#include "wm_flash_map.h" -#include "wm_wl_task.h" -#include "wm_params.h" -#include "wm_param.h" - -#define FWUP_MSG_QUEUE_SIZE (4) - -#define FWUP_TASK_STK_SIZE (512) - -#define FWUP_MSG_START_ENGINEER (1) - -static struct tls_fwup *fwup = NULL; -static tls_os_queue_t *fwup_msg_queue = NULL; - -static u32 fwup_task_stk[FWUP_TASK_STK_SIZE]; - -//static u8 oneshotback = 0; -static u8 *fwupwritebuffer = NULL; - -T_BOOTER imgheader[2]; -extern u32 flashtotalsize; -static void fwup_update_autoflag(void) -{ - u8 auto_reconnect = 0xff; - - tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_reconnect); - if(auto_reconnect == WIFI_AUTO_CNT_TMP_OFF) - { - auto_reconnect = WIFI_AUTO_CNT_ON; - tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_reconnect); - } - return; -} - -int tls_fwup_img_header_check(T_BOOTER *img_param) -{ - psCrcContext_t crcContext; - u32 value = 0; - int i = 0; - u32 runaddr = 0; - u32 updaddr = 0; - - if (img_param->magic_no != SIGNATURE_WORD) - { - return FALSE; - } - - if ((IMG_TYPE_OLD_PLAIN != img_param->img_type) && (IMG_TYPE_NEW_PLAIN != img_param->img_type)) - { - return FALSE; - } - - tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); - for (i = 0; i < (sizeof(T_BOOTER)-4)/4; i++) - { - value = *(((int *)img_param)+i); - tls_crypto_crc_update(&crcContext, (unsigned char *)&value, 4); - } - value = 0; - tls_crypto_crc_final(&crcContext, &value); - - runaddr = img_param->run_img_addr|FLASH_BASE_ADDR; - if ((img_param->hd_checksum == value) && (runaddr < FLASH_1M_END_ADDR)) - { - /*forbid 1M_Plain and 2M_PLAIN update*/ - tls_fls_read(CODE_RUN_HEADER_ADDR, (unsigned char *)&imgheader[0], sizeof(T_BOOTER)); - if (imgheader[0].img_type != img_param->img_type) - { - return FALSE; - } - - /*run addr must be page-aligned in first 1M flash */ - if ((runaddr % INSIDE_FLS_PAGE_SIZE) - || (0 == img_param->run_img_len) - || (runaddr + img_param->run_img_len >= FLASH_1M_END_ADDR) - || (runaddr < CODE_RUN_START_ADDR)) - { - return FALSE; - } - - /*can not upd over flash size minus system parameter area*/ - value = runaddr + img_param->run_img_len; - if (value < imgheader[0].run_img_addr + imgheader[0].run_img_len) - { - value = (imgheader[0].run_img_addr|FLASH_BASE_ADDR) + imgheader[0].run_img_len; - } - - value = value%INSIDE_FLS_BLOCK_SIZE ? (value/INSIDE_FLS_BLOCK_SIZE)*INSIDE_FLS_BLOCK_SIZE + INSIDE_FLS_BLOCK_SIZE : value; - updaddr = img_param->upd_img_addr|FLASH_BASE_ADDR; - /*upd address can not overlap run addr & must be 64K aligned*/ - if ((updaddr < value) - || (updaddr%INSIDE_FLS_BLOCK_SIZE) - || (0 == img_param->upd_img_len)) - { - return FALSE; - } - - if(updaddr != 0x08100000 || img_param->upd_img_len > 0xbfc00) - { - printf("tampered with OTA header!\n"); - return FALSE; - } - - /*over flash capacity decrease one block size used as sys param area*/ - if ((updaddr + img_param->upd_img_len) > ((flashtotalsize - INSIDE_FLS_BLOCK_SIZE)|FLASH_BASE_ADDR)) - { - return FALSE; - } - - return TRUE; - } - else - { - return FALSE; - } -} - -static void tls_fwup_img_update_header(T_BOOTER* img_param) -{ - unsigned char current_img; - psCrcContext_t crcContext; - - tls_fls_read(CODE_RUN_HEADER_ADDR, (unsigned char *)&imgheader[0], sizeof(T_BOOTER)); - tls_fls_read(CODE_UPD_HEADER_ADDR, (unsigned char *)&imgheader[1], sizeof(T_BOOTER)); - - //将两个upd_no中较大的那个值å�–出æ�¥ï¼Œå†�将其加1å�Žèµ‹å€¼ç»™ CODE_UPD_HEADER_ADDR 处的headerï¼› - if (tls_fwup_img_header_check(&imgheader[1])) - { - current_img = (imgheader[1].upd_no > imgheader[0].upd_no); - } - else - { - current_img = 0; - } - img_param->upd_no = imgheader[current_img].upd_no + 1; - - tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); - tls_crypto_crc_update(&crcContext, (unsigned char *)img_param, sizeof(T_BOOTER)-4); - tls_crypto_crc_final(&crcContext, &img_param->hd_checksum); - tls_fls_write(CODE_UPD_HEADER_ADDR, (unsigned char *)img_param, sizeof(T_BOOTER)); -} - - -/* ninlight: SHA256 + tail verification hook instead of the QConnex RSA check (see sdk/PATCHES.md) */ -#include "util/nl_sha256.h" -#include "sys/nl_fwup_hook.h" -#include "sys/nl_ota.h" /* nl_ota_set_error() - see sdk/PATCHES.md point 1 */ - - -static void fwup_scheduler(void *data) -{ - u8 *buffer = NULL; - int err; - u32 msg; - u32 len; - u32 image_checksum = 0; - u32 org_checksum = 0; - struct tls_fwup_request *request; - struct tls_fwup_request *temp; - T_BOOTER booter; - u32 currentlen = 0; - u32 tmplen = 0; - bool isacrossflash = FALSE; - - nl_sha256_ctx sha; - nl_sha256_init(&sha); - u8 image_sha256[32] = {0}; - u8 *RSA_SHA256 = NULL; - - while (1) - { - err = tls_os_queue_receive(fwup_msg_queue, (void **)&msg, 0, 0); - tls_watchdog_clr(); - if(err != TLS_OS_SUCCESS) - { - continue; - } - switch(msg) - { - case FWUP_MSG_START_ENGINEER: - if(dl_list_empty(&fwup->wait_list) == 0) - { - fwup->current_state |= TLS_FWUP_STATE_BUSY; - } - dl_list_for_each_safe(request, temp, &fwup->wait_list, struct tls_fwup_request, list) - { - request->status = TLS_FWUP_REQ_STATUS_BUSY; - if(fwup->current_state & TLS_FWUP_STATE_ERROR) - { - TLS_DBGPRT_WARNING("some error happened during firmware update, so discard all the request in the waiting queue!\n"); - if(fwup->current_state & TLS_FWUP_STATE_ERROR_IO) - { - request->status = TLS_FWUP_REQ_STATUS_FIO; - } - else if(fwup->current_state & TLS_FWUP_STATE_ERROR_SIGNATURE) - { - request->status = TLS_FWUP_REQ_STATUS_FSIGNATURE; - } - else if(fwup->current_state & TLS_FWUP_STATE_ERROR_MEM) - { - request->status = TLS_FWUP_REQ_STATUS_FMEM; - } - else if(fwup->current_state & TLS_FWUP_STATE_ERROR_CRC) - { - request->status = TLS_FWUP_REQ_STATUS_FCRC; - } - goto request_finish; - } - else if(fwup->current_state & TLS_FWUP_STATE_COMPLETE) - { - TLS_DBGPRT_WARNING("the firmware updating conpletes, so discard the request in the waiting queue!\n"); - request->status = TLS_FWUP_REQ_STATUS_FCOMPLETE; - goto request_finish; - } - - if(fwup->current_image_src <= TLS_FWUP_IMAGE_SRC_WEB) - { - buffer = request->data; - if(fwup->received_len < sizeof(T_BOOTER)) - { - len = sizeof(T_BOOTER) - fwup->received_len; - if(request->data_len < len) - { - len = request->data_len; - } - MEMCPY(&booter, buffer, sizeof(T_BOOTER)); - request->data_len -= len; - buffer += len; - fwup->received_len += len; - if(fwup->received_len == sizeof(T_BOOTER)) - { - - if (!tls_fwup_img_header_check(&booter)) - { - request->status = TLS_FWUP_REQ_STATUS_FIO; - fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; - goto request_finish; - } - nl_sha256_update(&sha, (u8*)&booter, sizeof(T_BOOTER)); - - if ((IMG_TYPE_OLD_PLAIN == booter.img_type ) ||(IMG_TYPE_NEW_PLAIN == booter.img_type)) - { - fwup->program_base = booter.upd_img_addr | FLASH_BASE_ADDR; - fwup->total_len = booter.upd_img_len + 512; - org_checksum = booter.upd_checksum; - isacrossflash = FALSE; - currentlen = 0; - } - else - { - request->status = TLS_FWUP_REQ_STATUS_FCRC; - goto request_finish; - } - - fwup->updated_len = 0; - } - } - fwup->received_len += request->data_len; - } - if ((request->data_len > 0) && (fwupwritebuffer)) - { - if((currentlen + request->data_len) < INSIDE_FLS_SECTOR_SIZE) - { - memcpy(fwupwritebuffer + currentlen, buffer, request->data_len); - currentlen += request->data_len; - fwup->updated_len += request->data_len; - } - else - { - if ((IMG_TYPE_OLD_PLAIN == booter.img_type) \ - && (0x200000 == flashtotalsize)\ - && (fwup->program_base < FLASH_1M_END_ADDR)\ - && ((fwup->program_base + fwup->updated_len) >= (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE))) - { - isacrossflash = TRUE; - fwup->program_base = FLASH_1M_END_ADDR; - fwup->program_offset = 0; - } - - //TLS_DBGPRT_INFO("write the firmware image to the flash. %x\n\r", fwup->program_base + fwup->program_offset); - memcpy(fwupwritebuffer + currentlen, buffer, (INSIDE_FLS_SECTOR_SIZE - currentlen)); - tmplen = fwup->program_offset/INSIDE_FLS_SECTOR_SIZE * INSIDE_FLS_SECTOR_SIZE; - err = tls_fls_write(fwup->program_base + tmplen, fwupwritebuffer, INSIDE_FLS_SECTOR_SIZE); - if(err != TLS_FLS_STATUS_OK) - { - TLS_DBGPRT_ERR("failed to program flash!\n"); - request->status = TLS_FWUP_REQ_STATUS_FIO; - fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; - goto request_finish; - } - - memcpy(fwupwritebuffer, buffer + (INSIDE_FLS_SECTOR_SIZE - currentlen), (currentlen + request->data_len) - INSIDE_FLS_SECTOR_SIZE); - fwup->program_offset += INSIDE_FLS_SECTOR_SIZE; - fwup->updated_len += request->data_len; - currentlen = (currentlen + request->data_len) - INSIDE_FLS_SECTOR_SIZE; - } - - //TLS_DBGPRT_INFO("updated: %d bytes\n" , fwup->updated_len); - if(fwup->updated_len >= fwup->total_len) - { - u32 left = 0, offset = 0; - psCrcContext_t crcContext; - - if (fwup->program_offset <= fwup->updated_len) - { - if ((IMG_TYPE_OLD_PLAIN == booter.img_type) \ - && (0x200000 == flashtotalsize)\ - && (fwup->program_base < FLASH_1M_END_ADDR)\ - && ((fwup->program_base + fwup->updated_len) >= (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE))) - { - isacrossflash = TRUE; - fwup->program_base = FLASH_1M_END_ADDR; - fwup->program_offset = 0; - } - - err = tls_fls_write(fwup->program_base + fwup->program_offset, fwupwritebuffer, currentlen); - if(err != TLS_FLS_STATUS_OK) - { - TLS_DBGPRT_ERR("failed to program flash!\n"); - request->status = TLS_FWUP_REQ_STATUS_FIO; - fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; - goto request_finish; - } - } - - - offset = 0; - fwup->program_base = booter.upd_img_addr | FLASH_BASE_ADDR; - if (TRUE == isacrossflash) - { - left = (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE) - fwup->program_base; - } - else - { - left = fwup->total_len - 512; - } - - tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); - while (left > 0) - { - len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; - - err = tls_fls_read(fwup->program_base + offset, fwupwritebuffer, len); - if (err != TLS_FLS_STATUS_OK) - { - request->status = TLS_FWUP_REQ_STATUS_FIO; - fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; - goto request_finish; - } - tls_crypto_crc_update(&crcContext, fwupwritebuffer, len); - nl_sha256_update(&sha, fwupwritebuffer, len); - offset += len; - left -= len; - } - - if (TRUE == isacrossflash) - { - left = fwup->total_len - offset; - fwup->program_base = FLASH_1M_END_ADDR; - offset = 0; - while (left > 0) - { - len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; - - err = tls_fls_read(fwup->program_base + offset, fwupwritebuffer, len); - if (err != TLS_FLS_STATUS_OK) - { - request->status = TLS_FWUP_REQ_STATUS_FIO; - fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; - goto request_finish; - } - tls_crypto_crc_update(&crcContext, fwupwritebuffer, len); - nl_sha256_update(&sha, fwupwritebuffer, len); - offset += len; - left -= len; - } - } - tls_crypto_crc_final(&crcContext, &image_checksum); - - nl_sha256_final(&sha, image_sha256); -#if 0 - printf("last sector - len:%d isacrossflash: %d\n", len, isacrossflash); - u8 *bptr = (u8*)fwupwritebuffer; - for (int i = 0; i < 32; ++i) - printf("%02x ", bptr[i]); - printf("\n"); -#endif - // now read last 256 byte - with RSA encoded SHA256 - left = 512; - RSA_SHA256 = tls_mem_alloc(left); - if(!RSA_SHA256) - { - /* ninlight: this used to jump to request_finish without setting any - * error bit. With the C1 fix (see below / sdk/PATCHES.md point 1) the - * request_finish reset only fires when no error bit is set - so this - * was the one remaining way to reach a reset with the trailer never - * actually checked. Report it like the other allocation failure above. */ - request->status = TLS_FWUP_REQ_STATUS_FMEM; - fwup->current_state |= TLS_FWUP_STATE_ERROR_MEM; - nl_ota_set_error("download"); /* no more specific value applies */ - goto request_finish; - } - while (left > 0) - { - len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; - - err = tls_fls_read(fwup->program_base + offset, RSA_SHA256, len); - if (err != TLS_FLS_STATUS_OK) - { - request->status = TLS_FWUP_REQ_STATUS_FIO; - fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; - goto request_finish; - } - offset += len; - left -= len; - } -#if 0 - bptr = (u8*)RSA_SHA256; - printf("start enc hash - len:%d\n", len); - for (int i = 0; i < 32; ++i) - printf("%02x ", bptr[i]); - printf("\n"); -#endif - if (org_checksum != image_checksum) // QC_CHANGE_MK - { - /* ninlight: a checksum failure never reaches nl_fwup_verify_tail() below, - * so report it here explicitly - otherwise last_ota_error would fall back - * to the generic "download" instead of the more precise "crc" (see - * sdk/PATCHES.md point 1). */ - TLS_DBGPRT_ERR("varify incorrect[0x%02x, but 0x%02x]\n", org_checksum, image_checksum); - nl_ota_set_error("crc"); - request->status = TLS_FWUP_REQ_STATUS_FCRC; - fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; - goto request_finish; - } - else if (!nl_fwup_verify_tail(image_sha256, RSA_SHA256, 512)) // QC_CHANGE_MK - { - request->status = TLS_FWUP_REQ_STATUS_FCRC; - fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; - goto request_finish; - } - else /*CRC MATCH and Update IMAGE HEADER PARAM*/ - { - tls_fwup_img_update_header(&booter); - } - - TLS_DBGPRT_INFO("update the firmware successfully!\n"); - fwup->current_state |= TLS_FWUP_STATE_COMPLETE; - //if (oneshotback == 1){ - // tls_wifi_set_oneshot_flag(oneshotback); // æ�¢å¤�一键é…�ç½® - //} - - } - } - request->status = TLS_FWUP_REQ_STATUS_SUCCESS; - -request_finish: - if(RSA_SHA256) - { - tls_mem_free(RSA_SHA256); - } - tls_os_sem_acquire(fwup->list_lock, 0); - dl_list_del(&request->list); - tls_os_sem_release(fwup->list_lock); - if(dl_list_empty(&fwup->wait_list) == 1) - { - fwup->current_state &= ~TLS_FWUP_STATE_BUSY; - } - if(request->complete) - { - request->complete(request, request->arg); - } - /* ninlight: only reset when the image was actually accepted. Before this fix the - * condition below fired unconditionally once the transfer reached its end - - * including a request that got rejected by nl_fwup_verify_tail() (trailer/device/ - * hash mismatch) or by the checksum check above (goto request_finish also lands - * here). Resetting in that case would reboot into the OLD firmware and never let - * ota_task() report the failure - so last_ota_error stayed NULL forever. See - * sdk/PATCHES.md point 1. */ - if(fwup->updated_len >= (fwup->total_len) && !(fwup->current_state & TLS_FWUP_STATE_ERROR)) - { - fwup_update_autoflag(); - tls_sys_reset(); - } - } - break; - - default: - break; - } - } -} - -void fwup_request_complete(struct tls_fwup_request *request, void *arg) -{ - tls_os_sem_t *sem; - - if((request == NULL) || (arg == NULL)) - { - return; - } - sem = (tls_os_sem_t *)arg; - tls_os_sem_release(sem); -} - -u32 tls_fwup_enter(enum tls_fwup_image_src image_src) -{ - u32 session_id = 0; - u32 cpu_sr; - - tls_fwup_init(); - - if (fwup == NULL) - { - TLS_DBGPRT_INFO("fwup is null!\n"); - return 0; - } - if (fwup->busy == TRUE) - { - TLS_DBGPRT_INFO("fwup is busy!\n"); - return 0; - } - - cpu_sr = tls_os_set_critical(); - - do - { - session_id = rand(); - }while(session_id == 0); - - if (NULL == fwupwritebuffer) - { - fwupwritebuffer = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); - if (NULL == fwupwritebuffer) - { - tls_os_release_critical(cpu_sr); - return 0; - } - } - - fwup->current_state = 0; - fwup->current_image_src = image_src; - - fwup->received_len = 0; - fwup->total_len = 0; - fwup->updated_len = 0; - fwup->program_base = 0; - fwup->program_offset = 0; - fwup->received_number = -1; - - fwup->current_session_id = session_id; - fwup->busy = TRUE; - tls_os_release_critical(cpu_sr); - //oneshotback = tls_wifi_get_oneshot_flag(); - //if (oneshotback == 1){ - // tls_wifi_set_oneshot_flag(0); // 退出一键�置 - //} - - tls_wifi_set_psflag(FALSE, 0); - - return session_id; -} - -int tls_fwup_exit(u32 session_id) -{ - u32 cpu_sr; - bool enable = FALSE; - //tls_os_task_t fwtask; - //tls_os_status_t osstatus = 0; - - if ((fwup == NULL) || (fwup->busy == FALSE)) - { - return TLS_FWUP_STATUS_EPERM; - } - if (session_id != fwup->current_session_id) - { - return TLS_FWUP_STATUS_ESESSIONID; - } - if (fwup->current_state & TLS_FWUP_STATE_BUSY) - { - return TLS_FWUP_STATUS_EBUSY; - } - - cpu_sr = tls_os_set_critical(); - if (fwupwritebuffer) - { - tls_mem_free(fwupwritebuffer); - fwupwritebuffer = NULL; - } - fwup->current_state = 0; - - fwup->received_len = 0; - fwup->total_len = 0; - fwup->updated_len = 0; - fwup->program_base = 0; - fwup->program_offset = 0; - fwup->received_number = -1; - - fwup->current_session_id = 0; - fwup->busy = FALSE; - tls_os_release_critical(cpu_sr); - - //if (oneshotback == 1){ - // tls_wifi_set_oneshot_flag(oneshotback); // ��一键�置 - //} - tls_param_get(TLS_PARAM_ID_PSM, &enable, TRUE); - tls_wifi_set_psflag(enable, 0); - - return TLS_FWUP_STATUS_OK; -} - -int tls_fwup_get_current_session_id(void) -{ - if (fwup){ - return fwup->current_session_id; - } - return 0; -} - -int tls_fwup_set_update_numer(int number) -{ - if(1 == number - fwup->received_number) - { - fwup->received_number = number; - return TLS_FWUP_STATUS_OK; - } - return TLS_FWUP_STATE_UNDEF; -} - -int tls_fwup_get_current_update_numer(void) -{ - return fwup->received_number; -} - -int tls_fwup_get_status(void) -{ - return fwup->busy; -} - -int tls_fwup_set_crc_error(u32 session_id) -{ - if(fwup == NULL) - { - return TLS_FWUP_STATUS_EPERM; - } - if(session_id != fwup->current_session_id) - { - return TLS_FWUP_STATUS_ESESSIONID; - } - fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; - - return TLS_FWUP_STATUS_OK; -} - -static int tls_fwup_request_async(u32 session_id, struct tls_fwup_request *request) -{ - u8 need_sched; - - if(fwup == NULL) - { - return TLS_FWUP_STATUS_EPERM; - } - if(session_id != fwup->current_session_id) - { - return TLS_FWUP_STATUS_ESESSIONID; - } - if((request == NULL) || (request->data == NULL) || (request->data_len == 0)) - { - return TLS_FWUP_STATUS_EINVALID; - } - tls_os_sem_acquire(fwup->list_lock, 0); - if(dl_list_empty(&fwup->wait_list)) - { - need_sched = 1; - } - else - { - need_sched = 0; - } - request->status = TLS_FWUP_REQ_STATUS_IDLE; - dl_list_add_tail(&fwup->wait_list, &request->list); - tls_os_sem_release(fwup->list_lock); - if(need_sched == 1) - { - tls_os_queue_send(fwup_msg_queue, (void *)FWUP_MSG_START_ENGINEER, 0); - } - return TLS_FWUP_STATUS_OK; -} - - -int tls_fwup_request_sync(u32 session_id, u8 *data, u32 data_len) -{ - int err; - tls_os_sem_t *sem; - struct tls_fwup_request request; - - if(fwup == NULL) - { - return TLS_FWUP_STATUS_EPERM; - } - if(session_id != fwup->current_session_id) - { - return TLS_FWUP_STATUS_ESESSIONID; - } - if((data == NULL) || (data_len == 0)) - { - return TLS_FWUP_STATUS_EINVALID; - } - - err = tls_os_sem_create(&sem, 0); - if(err != TLS_OS_SUCCESS) - { - return TLS_FWUP_STATUS_EMEM; - } - request.data = data; - request.data_len = data_len; - request.complete = fwup_request_complete; - request.arg = (void *)sem; - - tls_wifi_set_psflag(FALSE, 0); - - err = tls_fwup_request_async(session_id, &request); - if(err == TLS_FWUP_STATUS_OK) - { - tls_os_sem_acquire(sem, 0); - } - tls_os_sem_delete(sem); - - switch(request.status) - { - case TLS_FWUP_REQ_STATUS_SUCCESS: - err = TLS_FWUP_STATUS_OK; - break; - - case TLS_FWUP_REQ_STATUS_FIO: - err = TLS_FWUP_STATUS_EIO; - break; - - case TLS_FWUP_REQ_STATUS_FSIGNATURE: - err = TLS_FWUP_STATUS_ESIGNATURE; - break; - - case TLS_FWUP_REQ_STATUS_FMEM: - err = TLS_FWUP_STATUS_EMEM; - break; - - case TLS_FWUP_REQ_STATUS_FCRC: - err = TLS_FWUP_STATUS_ECRC; - break; - - case TLS_FWUP_REQ_STATUS_FCOMPLETE: - err = TLS_FWUP_STATUS_EIO; - break; - - default: - err = TLS_FWUP_STATUS_EUNDEF; - break; - } - return err; -} - -u16 tls_fwup_current_state(u32 session_id) -{ - if(fwup == NULL) - { - return TLS_FWUP_STATE_UNDEF; - } - if(session_id != fwup->current_session_id) - { - return TLS_FWUP_STATE_UNDEF; - } - return fwup->current_state; -} - -int tls_fwup_reset(u32 session_id) -{ - u32 cpu_sr; - - if ((fwup == NULL) || (fwup->busy == FALSE)) {return TLS_FWUP_STATUS_EPERM;} - if (session_id != fwup->current_session_id) {return TLS_FWUP_STATUS_ESESSIONID;} - if (fwup->current_state & TLS_FWUP_STATE_BUSY) {return TLS_FWUP_STATUS_EBUSY;} - - cpu_sr = tls_os_set_critical(); - - fwup->current_state = 0; - - fwup->received_len = 0; - fwup->total_len = 0; - fwup->updated_len = 0; - fwup->program_base = 0; - fwup->program_offset = 0; - - tls_os_release_critical(cpu_sr); - - return TLS_FWUP_STATUS_OK; -} - -int tls_fwup_clear_error(u32 session_id) -{ - u32 cpu_sr; - - if ((fwup == NULL) || (fwup->busy == FALSE)) {return TLS_FWUP_STATUS_EPERM;} - if (session_id != fwup->current_session_id) {return TLS_FWUP_STATUS_ESESSIONID;} - if (fwup->current_state & TLS_FWUP_STATE_BUSY) {return TLS_FWUP_STATUS_EBUSY;} - - cpu_sr = tls_os_set_critical(); - - fwup->current_state &= ~TLS_FWUP_STATE_ERROR; - - tls_os_release_critical(cpu_sr); - - return TLS_FWUP_STATUS_OK; -} - -int tls_fwup_init(void) -{ - int err; - - if(fwup != NULL) - { - TLS_DBGPRT_ERR("firmware update module has been installed!\n"); - return TLS_FWUP_STATUS_EBUSY; - } - - fwup = tls_mem_alloc(sizeof(*fwup)); - if(fwup == NULL) - { - TLS_DBGPRT_ERR("allocate @fwup fail!\n"); - return TLS_FWUP_STATUS_EMEM; - } - memset(fwup, 0, sizeof(*fwup)); - - err = tls_os_sem_create(&fwup->list_lock, 1); - if(err != TLS_OS_SUCCESS) - { - TLS_DBGPRT_ERR("create semaphore @fwup->list_lock fail!\n"); - tls_mem_free(fwup); - return TLS_FWUP_STATUS_EMEM; - } - - dl_list_init(&fwup->wait_list); - fwup->busy = FALSE; - - err = tls_os_queue_create(&fwup_msg_queue, FWUP_MSG_QUEUE_SIZE); - if (err != TLS_OS_SUCCESS) - { - TLS_DBGPRT_ERR("create message queue @fwup_msg_queue fail!\n"); - tls_os_sem_delete(fwup->list_lock); - tls_mem_free(fwup); - return TLS_FWUP_STATUS_EMEM; - } - - err = tls_os_task_create(NULL, "fwup", - fwup_scheduler, - (void *)fwup, - (void *)&fwup_task_stk[0], - FWUP_TASK_STK_SIZE * sizeof(u32), - TLS_FWUP_TASK_PRIO, - 0); - if (err != TLS_OS_SUCCESS) - { - TLS_DBGPRT_ERR("create firmware update process task fail!\n"); - - tls_os_queue_delete(fwup_msg_queue); - tls_os_sem_delete(fwup->list_lock); - tls_mem_free(fwup); - return TLS_FWUP_STATUS_EMEM; - } - - return TLS_FWUP_STATUS_OK; -} - - -/**Run-time image area size*/ -unsigned int CODE_RUN_AREA_LEN = 0; - -/**Area can be used by User in 1M position*/ -unsigned int USER_ADDR_START = 0; -unsigned int TLS_FLASH_PARAM_DEFAULT = 0; -unsigned int USER_AREA_LEN = 0; -unsigned int USER_ADDR_END = 0; - - -/**Upgrade image header area & System parameter area */ -unsigned int CODE_UPD_HEADER_ADDR = 0; -unsigned int TLS_FLASH_PARAM1_ADDR = 0; -unsigned int TLS_FLASH_PARAM2_ADDR = 0; -unsigned int TLS_FLASH_PARAM_RESTORE_ADDR = 0; - -/**Upgrade image area*/ -unsigned int CODE_UPD_START_ADDR = 0; -unsigned int CODE_UPD_AREA_LEN = 0; - -/**Area can be used by User in 2M position*/ -unsigned int EX_USER_ADDR_START = 0; -unsigned int EX_USER_AREA_LEN = 0; -unsigned int EX_USER_ADDR_END = 0; - -unsigned int TLS_FLASH_END_ADDR = 0; - -void tls_fls_layout_init(void) -{ - T_BOOTER tbooter; - - tls_fls_read(CODE_RUN_HEADER_ADDR, (u8 *)&tbooter, sizeof(tbooter)); - switch (flashtotalsize) - { - case 0x200000: /*2M*/ - { - if (IMG_TYPE_OLD_PLAIN == tbooter.img_type) - { - //printf("2M use old layout\r\n"); - /**Run-time image area size*/ - CODE_RUN_AREA_LEN = (896*1024 - 256); - - /**Area can be used by User in 1M position*/ - USER_ADDR_START = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); - TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); - USER_AREA_LEN = (48*1024); - USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); - - - /**Upgrade image header area & System parameter area */ - CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN); - TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); - TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); - TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); - - /**Upgrade image area*/ - CODE_UPD_START_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000); - CODE_UPD_AREA_LEN = (704*1024); - - /**Area can be used by User in 2M position*/ - EX_USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); - EX_USER_AREA_LEN = (320*1024); - EX_USER_ADDR_END = (EX_USER_ADDR_START + EX_USER_AREA_LEN - 1); - - TLS_FLASH_END_ADDR = (EX_USER_ADDR_END); - - } - else - { - //printf("2M use new layout\r\n"); - /**Run-time image area size*/ - CODE_RUN_AREA_LEN = (960*1024 - 256); - - /**Upgrade image area*/ - CODE_UPD_START_ADDR = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); - CODE_UPD_AREA_LEN = (768*1024); - - /**Area can be used by User*/ - USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); - TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); - USER_AREA_LEN = (240*1024); - USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); - - /**Area can be used by User in 2M position*/ - EX_USER_ADDR_START = 0; - EX_USER_AREA_LEN = 0; - EX_USER_ADDR_END = 0; - - - /**Upgrade image header area & System parameter area */ - CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN) ; - TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); - TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); - TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); - TLS_FLASH_END_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000 -1); - } - } - break; - default: /*1M*/ - { - //printf("1M layout\r\n"); - /**Run-time image area size*/ - CODE_RUN_AREA_LEN = (512*1024 - 256); - - /**Upgrade image area*/ - CODE_UPD_START_ADDR = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); - CODE_UPD_AREA_LEN = (384*1024); - - /**Area can be used by User*/ - USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); - TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); - USER_AREA_LEN = (48*1024); - USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); - - /**Area can be used by User in 2M position*/ - EX_USER_ADDR_START = 0; - EX_USER_AREA_LEN = 0; - EX_USER_ADDR_END = 0; - - /**Upgrade image header area & System parameter area */ - CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN); - TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); - TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); - TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); - TLS_FLASH_END_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000 - 1); - - } - break; - } -} - +/***************************************************************************** +* +* File Name : wm_fwup.c +* +* Description: firmware update Module +* +* Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd. +* All rights reserved. +* +* Author : dave +* +* Date : 2014-6-16 +*****************************************************************************/ +#include +#include +#include + +#include "wm_mem.h" +#include "list.h" +#include "wm_debug.h" +#include "wm_internal_flash.h" +#include "wm_flash.h" +#include "wm_crypto_hard.h" + +#include "utils.h" +#include "wm_fwup.h" +#include "wm_watchdog.h" +#include "wm_wifi.h" +#include "wm_flash_map.h" +#include "wm_wl_task.h" +#include "wm_params.h" +#include "wm_param.h" + +#define FWUP_MSG_QUEUE_SIZE (4) + +#define FWUP_TASK_STK_SIZE (512) + +#define FWUP_MSG_START_ENGINEER (1) + +static struct tls_fwup *fwup = NULL; +static tls_os_queue_t *fwup_msg_queue = NULL; + +static u32 fwup_task_stk[FWUP_TASK_STK_SIZE]; + +//static u8 oneshotback = 0; +static u8 *fwupwritebuffer = NULL; + +T_BOOTER imgheader[2]; +extern u32 flashtotalsize; +static void fwup_update_autoflag(void) +{ + u8 auto_reconnect = 0xff; + + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_reconnect); + if(auto_reconnect == WIFI_AUTO_CNT_TMP_OFF) + { + auto_reconnect = WIFI_AUTO_CNT_ON; + tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_reconnect); + } + return; +} + +int tls_fwup_img_header_check(T_BOOTER *img_param) +{ + psCrcContext_t crcContext; + u32 value = 0; + int i = 0; + u32 runaddr = 0; + u32 updaddr = 0; + + if (img_param->magic_no != SIGNATURE_WORD) + { + return FALSE; + } + + if ((IMG_TYPE_OLD_PLAIN != img_param->img_type) && (IMG_TYPE_NEW_PLAIN != img_param->img_type)) + { + return FALSE; + } + + tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); + for (i = 0; i < (sizeof(T_BOOTER)-4)/4; i++) + { + value = *(((int *)img_param)+i); + tls_crypto_crc_update(&crcContext, (unsigned char *)&value, 4); + } + value = 0; + tls_crypto_crc_final(&crcContext, &value); + + runaddr = img_param->run_img_addr|FLASH_BASE_ADDR; + if ((img_param->hd_checksum == value) && (runaddr < FLASH_1M_END_ADDR)) + { + /*forbid 1M_Plain and 2M_PLAIN update*/ + tls_fls_read(CODE_RUN_HEADER_ADDR, (unsigned char *)&imgheader[0], sizeof(T_BOOTER)); + if (imgheader[0].img_type != img_param->img_type) + { + return FALSE; + } + + /*run addr must be page-aligned in first 1M flash */ + if ((runaddr % INSIDE_FLS_PAGE_SIZE) + || (0 == img_param->run_img_len) + || (runaddr + img_param->run_img_len >= FLASH_1M_END_ADDR) + || (runaddr < CODE_RUN_START_ADDR)) + { + return FALSE; + } + + /*can not upd over flash size minus system parameter area*/ + value = runaddr + img_param->run_img_len; + if (value < imgheader[0].run_img_addr + imgheader[0].run_img_len) + { + value = (imgheader[0].run_img_addr|FLASH_BASE_ADDR) + imgheader[0].run_img_len; + } + + value = value%INSIDE_FLS_BLOCK_SIZE ? (value/INSIDE_FLS_BLOCK_SIZE)*INSIDE_FLS_BLOCK_SIZE + INSIDE_FLS_BLOCK_SIZE : value; + updaddr = img_param->upd_img_addr|FLASH_BASE_ADDR; + /*upd address can not overlap run addr & must be 64K aligned*/ + if ((updaddr < value) + || (updaddr%INSIDE_FLS_BLOCK_SIZE) + || (0 == img_param->upd_img_len)) + { + return FALSE; + } + + if(updaddr != 0x08100000 || img_param->upd_img_len > 0xbfc00) + { + printf("tampered with OTA header!\n"); + return FALSE; + } + + /*over flash capacity decrease one block size used as sys param area*/ + if ((updaddr + img_param->upd_img_len) > ((flashtotalsize - INSIDE_FLS_BLOCK_SIZE)|FLASH_BASE_ADDR)) + { + return FALSE; + } + + return TRUE; + } + else + { + return FALSE; + } +} + +static void tls_fwup_img_update_header(T_BOOTER* img_param) +{ + unsigned char current_img; + psCrcContext_t crcContext; + + tls_fls_read(CODE_RUN_HEADER_ADDR, (unsigned char *)&imgheader[0], sizeof(T_BOOTER)); + tls_fls_read(CODE_UPD_HEADER_ADDR, (unsigned char *)&imgheader[1], sizeof(T_BOOTER)); + + //将两个upd_no中较大的那个值�出�,�将其加1�赋值给 CODE_UPD_HEADER_ADDR 处的header; + if (tls_fwup_img_header_check(&imgheader[1])) + { + current_img = (imgheader[1].upd_no > imgheader[0].upd_no); + } + else + { + current_img = 0; + } + img_param->upd_no = imgheader[current_img].upd_no + 1; + + tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); + tls_crypto_crc_update(&crcContext, (unsigned char *)img_param, sizeof(T_BOOTER)-4); + tls_crypto_crc_final(&crcContext, &img_param->hd_checksum); + tls_fls_write(CODE_UPD_HEADER_ADDR, (unsigned char *)img_param, sizeof(T_BOOTER)); +} + + +/* ninlight: SHA256 + tail verification hook instead of the QConnex RSA check (see sdk/PATCHES.md) */ +#include "util/nl_sha256.h" +#include "sys/nl_fwup_hook.h" +#include "sys/nl_ota.h" /* nl_ota_set_error() - see sdk/PATCHES.md point 1 */ + + +static void fwup_scheduler(void *data) +{ + u8 *buffer = NULL; + int err; + u32 msg; + u32 len; + u32 image_checksum = 0; + u32 org_checksum = 0; + struct tls_fwup_request *request; + struct tls_fwup_request *temp; + T_BOOTER booter; + u32 currentlen = 0; + u32 tmplen = 0; + bool isacrossflash = FALSE; + + /* ninlight: the context is (re)initialised per image, where the T_BOOTER header arrives - + * NOT here. This task outlives every update session, so initialising once at task start + * left a finalised context behind after the first attempt and every later update in the + * same boot failed with last_ota_error=hash. Found on the EVK, 2026-09-12. */ + nl_sha256_ctx sha; + u8 image_sha256[32] = {0}; + u8 *RSA_SHA256 = NULL; + + while (1) + { + err = tls_os_queue_receive(fwup_msg_queue, (void **)&msg, 0, 0); + tls_watchdog_clr(); + if(err != TLS_OS_SUCCESS) + { + continue; + } + switch(msg) + { + case FWUP_MSG_START_ENGINEER: + if(dl_list_empty(&fwup->wait_list) == 0) + { + fwup->current_state |= TLS_FWUP_STATE_BUSY; + } + dl_list_for_each_safe(request, temp, &fwup->wait_list, struct tls_fwup_request, list) + { + request->status = TLS_FWUP_REQ_STATUS_BUSY; + if(fwup->current_state & TLS_FWUP_STATE_ERROR) + { + TLS_DBGPRT_WARNING("some error happened during firmware update, so discard all the request in the waiting queue!\n"); + if(fwup->current_state & TLS_FWUP_STATE_ERROR_IO) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + } + else if(fwup->current_state & TLS_FWUP_STATE_ERROR_SIGNATURE) + { + request->status = TLS_FWUP_REQ_STATUS_FSIGNATURE; + } + else if(fwup->current_state & TLS_FWUP_STATE_ERROR_MEM) + { + request->status = TLS_FWUP_REQ_STATUS_FMEM; + } + else if(fwup->current_state & TLS_FWUP_STATE_ERROR_CRC) + { + request->status = TLS_FWUP_REQ_STATUS_FCRC; + } + goto request_finish; + } + else if(fwup->current_state & TLS_FWUP_STATE_COMPLETE) + { + TLS_DBGPRT_WARNING("the firmware updating conpletes, so discard the request in the waiting queue!\n"); + request->status = TLS_FWUP_REQ_STATUS_FCOMPLETE; + goto request_finish; + } + + if(fwup->current_image_src <= TLS_FWUP_IMAGE_SRC_WEB) + { + buffer = request->data; + if(fwup->received_len < sizeof(T_BOOTER)) + { + len = sizeof(T_BOOTER) - fwup->received_len; + if(request->data_len < len) + { + len = request->data_len; + } + MEMCPY(&booter, buffer, sizeof(T_BOOTER)); + request->data_len -= len; + buffer += len; + fwup->received_len += len; + if(fwup->received_len == sizeof(T_BOOTER)) + { + + if (!tls_fwup_img_header_check(&booter)) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + nl_sha256_init(&sha); /* fresh context for this image */ + nl_sha256_update(&sha, (u8*)&booter, sizeof(T_BOOTER)); + + if ((IMG_TYPE_OLD_PLAIN == booter.img_type ) ||(IMG_TYPE_NEW_PLAIN == booter.img_type)) + { + fwup->program_base = booter.upd_img_addr | FLASH_BASE_ADDR; + fwup->total_len = booter.upd_img_len + 512; + org_checksum = booter.upd_checksum; + isacrossflash = FALSE; + currentlen = 0; + } + else + { + request->status = TLS_FWUP_REQ_STATUS_FCRC; + goto request_finish; + } + + fwup->updated_len = 0; + } + } + fwup->received_len += request->data_len; + } + if ((request->data_len > 0) && (fwupwritebuffer)) + { + if((currentlen + request->data_len) < INSIDE_FLS_SECTOR_SIZE) + { + memcpy(fwupwritebuffer + currentlen, buffer, request->data_len); + currentlen += request->data_len; + fwup->updated_len += request->data_len; + } + else + { + if ((IMG_TYPE_OLD_PLAIN == booter.img_type) \ + && (0x200000 == flashtotalsize)\ + && (fwup->program_base < FLASH_1M_END_ADDR)\ + && ((fwup->program_base + fwup->updated_len) >= (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE))) + { + isacrossflash = TRUE; + fwup->program_base = FLASH_1M_END_ADDR; + fwup->program_offset = 0; + } + + //TLS_DBGPRT_INFO("write the firmware image to the flash. %x\n\r", fwup->program_base + fwup->program_offset); + memcpy(fwupwritebuffer + currentlen, buffer, (INSIDE_FLS_SECTOR_SIZE - currentlen)); + tmplen = fwup->program_offset/INSIDE_FLS_SECTOR_SIZE * INSIDE_FLS_SECTOR_SIZE; + err = tls_fls_write(fwup->program_base + tmplen, fwupwritebuffer, INSIDE_FLS_SECTOR_SIZE); + if(err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("failed to program flash!\n"); + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + + memcpy(fwupwritebuffer, buffer + (INSIDE_FLS_SECTOR_SIZE - currentlen), (currentlen + request->data_len) - INSIDE_FLS_SECTOR_SIZE); + fwup->program_offset += INSIDE_FLS_SECTOR_SIZE; + fwup->updated_len += request->data_len; + currentlen = (currentlen + request->data_len) - INSIDE_FLS_SECTOR_SIZE; + } + + //TLS_DBGPRT_INFO("updated: %d bytes\n" , fwup->updated_len); + if(fwup->updated_len >= fwup->total_len) + { + u32 left = 0, offset = 0; + psCrcContext_t crcContext; + + if (fwup->program_offset <= fwup->updated_len) + { + if ((IMG_TYPE_OLD_PLAIN == booter.img_type) \ + && (0x200000 == flashtotalsize)\ + && (fwup->program_base < FLASH_1M_END_ADDR)\ + && ((fwup->program_base + fwup->updated_len) >= (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE))) + { + isacrossflash = TRUE; + fwup->program_base = FLASH_1M_END_ADDR; + fwup->program_offset = 0; + } + + err = tls_fls_write(fwup->program_base + fwup->program_offset, fwupwritebuffer, currentlen); + if(err != TLS_FLS_STATUS_OK) + { + TLS_DBGPRT_ERR("failed to program flash!\n"); + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + } + + + offset = 0; + fwup->program_base = booter.upd_img_addr | FLASH_BASE_ADDR; + if (TRUE == isacrossflash) + { + left = (FLASH_1M_END_ADDR - INSIDE_FLS_BLOCK_SIZE) - fwup->program_base; + } + else + { + left = fwup->total_len - 512; + } + + tls_crypto_crc_init(&crcContext, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, 3); + while (left > 0) + { + len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; + + err = tls_fls_read(fwup->program_base + offset, fwupwritebuffer, len); + if (err != TLS_FLS_STATUS_OK) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + tls_crypto_crc_update(&crcContext, fwupwritebuffer, len); + nl_sha256_update(&sha, fwupwritebuffer, len); + offset += len; + left -= len; + } + + if (TRUE == isacrossflash) + { + left = fwup->total_len - offset; + fwup->program_base = FLASH_1M_END_ADDR; + offset = 0; + while (left > 0) + { + len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; + + err = tls_fls_read(fwup->program_base + offset, fwupwritebuffer, len); + if (err != TLS_FLS_STATUS_OK) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + tls_crypto_crc_update(&crcContext, fwupwritebuffer, len); + nl_sha256_update(&sha, fwupwritebuffer, len); + offset += len; + left -= len; + } + } + tls_crypto_crc_final(&crcContext, &image_checksum); + + nl_sha256_final(&sha, image_sha256); +#if 0 + printf("last sector - len:%d isacrossflash: %d\n", len, isacrossflash); + u8 *bptr = (u8*)fwupwritebuffer; + for (int i = 0; i < 32; ++i) + printf("%02x ", bptr[i]); + printf("\n"); +#endif + // now read last 256 byte - with RSA encoded SHA256 + left = 512; + RSA_SHA256 = tls_mem_alloc(left); + if(!RSA_SHA256) + { + /* ninlight: this used to jump to request_finish without setting any + * error bit. With the C1 fix (see below / sdk/PATCHES.md point 1) the + * request_finish reset only fires when no error bit is set - so this + * was the one remaining way to reach a reset with the trailer never + * actually checked. Report it like the other allocation failure above. */ + request->status = TLS_FWUP_REQ_STATUS_FMEM; + fwup->current_state |= TLS_FWUP_STATE_ERROR_MEM; + nl_ota_set_error("download"); /* no more specific value applies */ + goto request_finish; + } + while (left > 0) + { + len = left > INSIDE_FLS_SECTOR_SIZE ? INSIDE_FLS_SECTOR_SIZE : left; + + err = tls_fls_read(fwup->program_base + offset, RSA_SHA256, len); + if (err != TLS_FLS_STATUS_OK) + { + request->status = TLS_FWUP_REQ_STATUS_FIO; + fwup->current_state |= TLS_FWUP_STATE_ERROR_IO; + goto request_finish; + } + offset += len; + left -= len; + } +#if 0 + bptr = (u8*)RSA_SHA256; + printf("start enc hash - len:%d\n", len); + for (int i = 0; i < 32; ++i) + printf("%02x ", bptr[i]); + printf("\n"); +#endif + if (org_checksum != image_checksum) // QC_CHANGE_MK + { + /* ninlight: a checksum failure never reaches nl_fwup_verify_tail() below, + * so report it here explicitly - otherwise last_ota_error would fall back + * to the generic "download" instead of the more precise "crc" (see + * sdk/PATCHES.md point 1). */ + TLS_DBGPRT_ERR("varify incorrect[0x%02x, but 0x%02x]\n", org_checksum, image_checksum); + nl_ota_set_error("crc"); + request->status = TLS_FWUP_REQ_STATUS_FCRC; + fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; + goto request_finish; + } + else if (!nl_fwup_verify_tail(image_sha256, RSA_SHA256, 512)) // QC_CHANGE_MK + { + request->status = TLS_FWUP_REQ_STATUS_FCRC; + fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; + goto request_finish; + } + else /*CRC MATCH and Update IMAGE HEADER PARAM*/ + { + tls_fwup_img_update_header(&booter); + } + + TLS_DBGPRT_INFO("update the firmware successfully!\n"); + fwup->current_state |= TLS_FWUP_STATE_COMPLETE; + //if (oneshotback == 1){ + // tls_wifi_set_oneshot_flag(oneshotback); // ��一键�置 + //} + + } + } + request->status = TLS_FWUP_REQ_STATUS_SUCCESS; + +request_finish: + if(RSA_SHA256) + { + tls_mem_free(RSA_SHA256); + } + tls_os_sem_acquire(fwup->list_lock, 0); + dl_list_del(&request->list); + tls_os_sem_release(fwup->list_lock); + if(dl_list_empty(&fwup->wait_list) == 1) + { + fwup->current_state &= ~TLS_FWUP_STATE_BUSY; + } + if(request->complete) + { + request->complete(request, request->arg); + } + /* ninlight: only reset when the image was actually accepted. Before this fix the + * condition below fired unconditionally once the transfer reached its end - + * including a request that got rejected by nl_fwup_verify_tail() (trailer/device/ + * hash mismatch) or by the checksum check above (goto request_finish also lands + * here). Resetting in that case would reboot into the OLD firmware and never let + * ota_task() report the failure - so last_ota_error stayed NULL forever. See + * sdk/PATCHES.md point 1. */ + if(fwup->updated_len >= (fwup->total_len) && !(fwup->current_state & TLS_FWUP_STATE_ERROR)) + { + fwup_update_autoflag(); + tls_sys_reset(); + } + } + break; + + default: + break; + } + } +} + +void fwup_request_complete(struct tls_fwup_request *request, void *arg) +{ + tls_os_sem_t *sem; + + if((request == NULL) || (arg == NULL)) + { + return; + } + sem = (tls_os_sem_t *)arg; + tls_os_sem_release(sem); +} + +u32 tls_fwup_enter(enum tls_fwup_image_src image_src) +{ + u32 session_id = 0; + u32 cpu_sr; + + tls_fwup_init(); + + if (fwup == NULL) + { + TLS_DBGPRT_INFO("fwup is null!\n"); + return 0; + } + if (fwup->busy == TRUE) + { + TLS_DBGPRT_INFO("fwup is busy!\n"); + return 0; + } + + cpu_sr = tls_os_set_critical(); + + do + { + session_id = rand(); + }while(session_id == 0); + + if (NULL == fwupwritebuffer) + { + fwupwritebuffer = tls_mem_alloc(INSIDE_FLS_SECTOR_SIZE); + if (NULL == fwupwritebuffer) + { + tls_os_release_critical(cpu_sr); + return 0; + } + } + + fwup->current_state = 0; + fwup->current_image_src = image_src; + + fwup->received_len = 0; + fwup->total_len = 0; + fwup->updated_len = 0; + fwup->program_base = 0; + fwup->program_offset = 0; + fwup->received_number = -1; + + fwup->current_session_id = session_id; + fwup->busy = TRUE; + tls_os_release_critical(cpu_sr); + //oneshotback = tls_wifi_get_oneshot_flag(); + //if (oneshotback == 1){ + // tls_wifi_set_oneshot_flag(0); // 退出一键�置 + //} + + tls_wifi_set_psflag(FALSE, 0); + + return session_id; +} + +int tls_fwup_exit(u32 session_id) +{ + u32 cpu_sr; + bool enable = FALSE; + //tls_os_task_t fwtask; + //tls_os_status_t osstatus = 0; + + if ((fwup == NULL) || (fwup->busy == FALSE)) + { + return TLS_FWUP_STATUS_EPERM; + } + if (session_id != fwup->current_session_id) + { + return TLS_FWUP_STATUS_ESESSIONID; + } + if (fwup->current_state & TLS_FWUP_STATE_BUSY) + { + return TLS_FWUP_STATUS_EBUSY; + } + + cpu_sr = tls_os_set_critical(); + if (fwupwritebuffer) + { + tls_mem_free(fwupwritebuffer); + fwupwritebuffer = NULL; + } + fwup->current_state = 0; + + fwup->received_len = 0; + fwup->total_len = 0; + fwup->updated_len = 0; + fwup->program_base = 0; + fwup->program_offset = 0; + fwup->received_number = -1; + + fwup->current_session_id = 0; + fwup->busy = FALSE; + tls_os_release_critical(cpu_sr); + + //if (oneshotback == 1){ + // tls_wifi_set_oneshot_flag(oneshotback); // ��一键�置 + //} + tls_param_get(TLS_PARAM_ID_PSM, &enable, TRUE); + tls_wifi_set_psflag(enable, 0); + + return TLS_FWUP_STATUS_OK; +} + +int tls_fwup_get_current_session_id(void) +{ + if (fwup){ + return fwup->current_session_id; + } + return 0; +} + +int tls_fwup_set_update_numer(int number) +{ + if(1 == number - fwup->received_number) + { + fwup->received_number = number; + return TLS_FWUP_STATUS_OK; + } + return TLS_FWUP_STATE_UNDEF; +} + +int tls_fwup_get_current_update_numer(void) +{ + return fwup->received_number; +} + +int tls_fwup_get_status(void) +{ + return fwup->busy; +} + +int tls_fwup_set_crc_error(u32 session_id) +{ + if(fwup == NULL) + { + return TLS_FWUP_STATUS_EPERM; + } + if(session_id != fwup->current_session_id) + { + return TLS_FWUP_STATUS_ESESSIONID; + } + fwup->current_state |= TLS_FWUP_STATE_ERROR_CRC; + + return TLS_FWUP_STATUS_OK; +} + +static int tls_fwup_request_async(u32 session_id, struct tls_fwup_request *request) +{ + u8 need_sched; + + if(fwup == NULL) + { + return TLS_FWUP_STATUS_EPERM; + } + if(session_id != fwup->current_session_id) + { + return TLS_FWUP_STATUS_ESESSIONID; + } + if((request == NULL) || (request->data == NULL) || (request->data_len == 0)) + { + return TLS_FWUP_STATUS_EINVALID; + } + tls_os_sem_acquire(fwup->list_lock, 0); + if(dl_list_empty(&fwup->wait_list)) + { + need_sched = 1; + } + else + { + need_sched = 0; + } + request->status = TLS_FWUP_REQ_STATUS_IDLE; + dl_list_add_tail(&fwup->wait_list, &request->list); + tls_os_sem_release(fwup->list_lock); + if(need_sched == 1) + { + tls_os_queue_send(fwup_msg_queue, (void *)FWUP_MSG_START_ENGINEER, 0); + } + return TLS_FWUP_STATUS_OK; +} + + +int tls_fwup_request_sync(u32 session_id, u8 *data, u32 data_len) +{ + int err; + tls_os_sem_t *sem; + struct tls_fwup_request request; + + if(fwup == NULL) + { + return TLS_FWUP_STATUS_EPERM; + } + if(session_id != fwup->current_session_id) + { + return TLS_FWUP_STATUS_ESESSIONID; + } + if((data == NULL) || (data_len == 0)) + { + return TLS_FWUP_STATUS_EINVALID; + } + + err = tls_os_sem_create(&sem, 0); + if(err != TLS_OS_SUCCESS) + { + return TLS_FWUP_STATUS_EMEM; + } + request.data = data; + request.data_len = data_len; + request.complete = fwup_request_complete; + request.arg = (void *)sem; + + tls_wifi_set_psflag(FALSE, 0); + + err = tls_fwup_request_async(session_id, &request); + if(err == TLS_FWUP_STATUS_OK) + { + tls_os_sem_acquire(sem, 0); + } + tls_os_sem_delete(sem); + + switch(request.status) + { + case TLS_FWUP_REQ_STATUS_SUCCESS: + err = TLS_FWUP_STATUS_OK; + break; + + case TLS_FWUP_REQ_STATUS_FIO: + err = TLS_FWUP_STATUS_EIO; + break; + + case TLS_FWUP_REQ_STATUS_FSIGNATURE: + err = TLS_FWUP_STATUS_ESIGNATURE; + break; + + case TLS_FWUP_REQ_STATUS_FMEM: + err = TLS_FWUP_STATUS_EMEM; + break; + + case TLS_FWUP_REQ_STATUS_FCRC: + err = TLS_FWUP_STATUS_ECRC; + break; + + case TLS_FWUP_REQ_STATUS_FCOMPLETE: + err = TLS_FWUP_STATUS_EIO; + break; + + default: + err = TLS_FWUP_STATUS_EUNDEF; + break; + } + return err; +} + +u16 tls_fwup_current_state(u32 session_id) +{ + if(fwup == NULL) + { + return TLS_FWUP_STATE_UNDEF; + } + if(session_id != fwup->current_session_id) + { + return TLS_FWUP_STATE_UNDEF; + } + return fwup->current_state; +} + +int tls_fwup_reset(u32 session_id) +{ + u32 cpu_sr; + + if ((fwup == NULL) || (fwup->busy == FALSE)) {return TLS_FWUP_STATUS_EPERM;} + if (session_id != fwup->current_session_id) {return TLS_FWUP_STATUS_ESESSIONID;} + if (fwup->current_state & TLS_FWUP_STATE_BUSY) {return TLS_FWUP_STATUS_EBUSY;} + + cpu_sr = tls_os_set_critical(); + + fwup->current_state = 0; + + fwup->received_len = 0; + fwup->total_len = 0; + fwup->updated_len = 0; + fwup->program_base = 0; + fwup->program_offset = 0; + + tls_os_release_critical(cpu_sr); + + return TLS_FWUP_STATUS_OK; +} + +int tls_fwup_clear_error(u32 session_id) +{ + u32 cpu_sr; + + if ((fwup == NULL) || (fwup->busy == FALSE)) {return TLS_FWUP_STATUS_EPERM;} + if (session_id != fwup->current_session_id) {return TLS_FWUP_STATUS_ESESSIONID;} + if (fwup->current_state & TLS_FWUP_STATE_BUSY) {return TLS_FWUP_STATUS_EBUSY;} + + cpu_sr = tls_os_set_critical(); + + fwup->current_state &= ~TLS_FWUP_STATE_ERROR; + + tls_os_release_critical(cpu_sr); + + return TLS_FWUP_STATUS_OK; +} + +int tls_fwup_init(void) +{ + int err; + + if(fwup != NULL) + { + TLS_DBGPRT_ERR("firmware update module has been installed!\n"); + return TLS_FWUP_STATUS_EBUSY; + } + + fwup = tls_mem_alloc(sizeof(*fwup)); + if(fwup == NULL) + { + TLS_DBGPRT_ERR("allocate @fwup fail!\n"); + return TLS_FWUP_STATUS_EMEM; + } + memset(fwup, 0, sizeof(*fwup)); + + err = tls_os_sem_create(&fwup->list_lock, 1); + if(err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create semaphore @fwup->list_lock fail!\n"); + tls_mem_free(fwup); + return TLS_FWUP_STATUS_EMEM; + } + + dl_list_init(&fwup->wait_list); + fwup->busy = FALSE; + + err = tls_os_queue_create(&fwup_msg_queue, FWUP_MSG_QUEUE_SIZE); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create message queue @fwup_msg_queue fail!\n"); + tls_os_sem_delete(fwup->list_lock); + tls_mem_free(fwup); + return TLS_FWUP_STATUS_EMEM; + } + + err = tls_os_task_create(NULL, "fwup", + fwup_scheduler, + (void *)fwup, + (void *)&fwup_task_stk[0], + FWUP_TASK_STK_SIZE * sizeof(u32), + TLS_FWUP_TASK_PRIO, + 0); + if (err != TLS_OS_SUCCESS) + { + TLS_DBGPRT_ERR("create firmware update process task fail!\n"); + + tls_os_queue_delete(fwup_msg_queue); + tls_os_sem_delete(fwup->list_lock); + tls_mem_free(fwup); + return TLS_FWUP_STATUS_EMEM; + } + + return TLS_FWUP_STATUS_OK; +} + + +/**Run-time image area size*/ +unsigned int CODE_RUN_AREA_LEN = 0; + +/**Area can be used by User in 1M position*/ +unsigned int USER_ADDR_START = 0; +unsigned int TLS_FLASH_PARAM_DEFAULT = 0; +unsigned int USER_AREA_LEN = 0; +unsigned int USER_ADDR_END = 0; + + +/**Upgrade image header area & System parameter area */ +unsigned int CODE_UPD_HEADER_ADDR = 0; +unsigned int TLS_FLASH_PARAM1_ADDR = 0; +unsigned int TLS_FLASH_PARAM2_ADDR = 0; +unsigned int TLS_FLASH_PARAM_RESTORE_ADDR = 0; + +/**Upgrade image area*/ +unsigned int CODE_UPD_START_ADDR = 0; +unsigned int CODE_UPD_AREA_LEN = 0; + +/**Area can be used by User in 2M position*/ +unsigned int EX_USER_ADDR_START = 0; +unsigned int EX_USER_AREA_LEN = 0; +unsigned int EX_USER_ADDR_END = 0; + +unsigned int TLS_FLASH_END_ADDR = 0; + +void tls_fls_layout_init(void) +{ + T_BOOTER tbooter; + + tls_fls_read(CODE_RUN_HEADER_ADDR, (u8 *)&tbooter, sizeof(tbooter)); + switch (flashtotalsize) + { + case 0x200000: /*2M*/ + { + if (IMG_TYPE_OLD_PLAIN == tbooter.img_type) + { + //printf("2M use old layout\r\n"); + /**Run-time image area size*/ + CODE_RUN_AREA_LEN = (896*1024 - 256); + + /**Area can be used by User in 1M position*/ + USER_ADDR_START = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); + TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); + USER_AREA_LEN = (48*1024); + USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); + + + /**Upgrade image header area & System parameter area */ + CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN); + TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); + TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); + TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); + + /**Upgrade image area*/ + CODE_UPD_START_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000); + CODE_UPD_AREA_LEN = (704*1024); + + /**Area can be used by User in 2M position*/ + EX_USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); + EX_USER_AREA_LEN = (320*1024); + EX_USER_ADDR_END = (EX_USER_ADDR_START + EX_USER_AREA_LEN - 1); + + TLS_FLASH_END_ADDR = (EX_USER_ADDR_END); + + } + else + { + //printf("2M use new layout\r\n"); + /**Run-time image area size*/ + CODE_RUN_AREA_LEN = (960*1024 - 256); + + /**Upgrade image area*/ + CODE_UPD_START_ADDR = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); + CODE_UPD_AREA_LEN = (768*1024); + + /**Area can be used by User*/ + USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); + TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); + USER_AREA_LEN = (240*1024); + USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); + + /**Area can be used by User in 2M position*/ + EX_USER_ADDR_START = 0; + EX_USER_AREA_LEN = 0; + EX_USER_ADDR_END = 0; + + + /**Upgrade image header area & System parameter area */ + CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN) ; + TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); + TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); + TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); + TLS_FLASH_END_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000 -1); + } + } + break; + default: /*1M*/ + { + //printf("1M layout\r\n"); + /**Run-time image area size*/ + CODE_RUN_AREA_LEN = (512*1024 - 256); + + /**Upgrade image area*/ + CODE_UPD_START_ADDR = (CODE_RUN_START_ADDR + CODE_RUN_AREA_LEN); + CODE_UPD_AREA_LEN = (384*1024); + + /**Area can be used by User*/ + USER_ADDR_START = (CODE_UPD_START_ADDR + CODE_UPD_AREA_LEN); + TLS_FLASH_PARAM_DEFAULT = (USER_ADDR_START); + USER_AREA_LEN = (48*1024); + USER_ADDR_END = (USER_ADDR_START + USER_AREA_LEN - 1); + + /**Area can be used by User in 2M position*/ + EX_USER_ADDR_START = 0; + EX_USER_AREA_LEN = 0; + EX_USER_ADDR_END = 0; + + /**Upgrade image header area & System parameter area */ + CODE_UPD_HEADER_ADDR = (USER_ADDR_START + USER_AREA_LEN); + TLS_FLASH_PARAM1_ADDR = (CODE_UPD_HEADER_ADDR + 0x1000); + TLS_FLASH_PARAM2_ADDR = (TLS_FLASH_PARAM1_ADDR + 0x1000); + TLS_FLASH_PARAM_RESTORE_ADDR = (TLS_FLASH_PARAM2_ADDR + 0x1000); + TLS_FLASH_END_ADDR = (TLS_FLASH_PARAM_RESTORE_ADDR + 0x1000 - 1); + + } + break; + } +} + From 6adf22fb52a4156f1e38245c7c9a63ce542df279 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 10:42:09 +0200 Subject: [PATCH 073/150] docs: EVK-Ergebnisse der OTA-Runde eintragen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sieben Punkte bestanden. Der zweite fw_update nach einer Ablehnung ging erst nach dem SHA256-Kontext-Fix. Die einmalige lwIP-pbuf-Assertion beim verfälschten Image ist als offene Beobachtung vermerkt. Co-Authored-By: Claude Opus 5 --- docs/hardware-checklist.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index e2b74a4..9cb8d56 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -39,19 +39,19 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] C2: Onboarding ok, 60 s warten, dann Reboot und Router-Neustart → AP `ninlight-` darf nicht wiederkommen (Scan am Mac) — 2026-09-12 (Reboot geprüft, Router-Neustart offen) ## OTA und Recovery (Plan 3) -- [ ] OTA: Update über `fw_update` läuft durch, danach meldet `system/info` die neue Version -- [ ] OTA: Image eines anderen Gerätetyps wird sofort abgelehnt (`last_ota_error` = `device_mismatch`), kein Reset, Lampe läuft mit der alten Firmware weiter -- [ ] OTA: Image mit verfälschtem Byte wird abgelehnt (`last_ota_error` = `hash` oder `crc`), kein Reset +- [x] OTA: Update über `fw_update` läuft durch, danach meldet `system/info` die neue Version — 2026-09-12 (0.3.0 → 0.3.1 → 0.3.2) +- [x] OTA: Image eines anderen Gerätetyps wird sofort abgelehnt (`last_ota_error` = `device_mismatch`), kein Reset, Lampe läuft mit der alten Firmware weiter — 2026-09-12 (Ablehnung nach ~4,5 s, Uptime lief durch) +- [x] OTA: Image mit verfälschtem Byte wird abgelehnt (`last_ota_error` = `hash` oder `crc`), kein Reset — 2026-09-12 (`crc`, kein Reset) - [ ] OTA: HTTP-Server während des Downloads abschalten → Wiederaufnahmeversuch (Range-Resume) sichtbar in der Konsole, nach Ausschöpfen der Runden `last_ota_error` = `download`, Lampe bootet normal - [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler - [ ] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen - [ ] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) - [ ] Recovery: Gegenstelle bleibt verbunden, sendet aber keine Daten mehr (nicht: ein langsamer, aber laufender Download) → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben -- [ ] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten) -- [ ] Zweiter `fw_update` mit gültigem Image direkt nach einer Ablehnung, ohne zwischenzeitlichen Reboot → läuft durch, `system/info` meldet danach die neue Version -- [ ] Lampe bleibt während und nach einer Ablehnung über REST erreichbar: `system/info` und `device/state` antworten beide weiterhin mit 200 +- [x] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten) — 2026-09-12 (Name, Fade 1200/800, pob, Lichtzustand) +- [x] Zweiter `fw_update` mit gültigem Image direkt nach einer Ablehnung, ohne zwischenzeitlichen Reboot → läuft durch, `system/info` meldet danach die neue Version — 2026-09-12 (erst nach dem SHA256-Kontext-Fix) +- [x] Lampe bleibt während und nach einer Ablehnung über REST erreichbar: `system/info` und `device/state` antworten beide weiterhin mit 200 — 2026-09-12 (system/info und device/state je 200) - [ ] Lichtverhalten über ein OTA hinweg (Spec §8.2, ein Update ist kein Stromausfall): einmal mit `power_on_behaviour = off`, einmal `= on` — die Lampe ist nach dem Update jeweils in demselben Zustand (an/aus, Farbe/Helligkeit), in dem sie vor dem Update war -- [ ] `system/info.fw_version` nach einem OTA stimmt mit der Version im Trailer des geflashten Images überein (Trailer-Version z.B. mit `tools/mkimage` prüfen) +- [x] `system/info.fw_version` nach einem OTA stimmt mit der Version im Trailer des geflashten Images überein (Trailer-Version z.B. mit `tools/mkimage` prüfen) — 2026-09-12 - [ ] Recovery im echten Rettungsfall: Lampe ohne gültige WLAN-Konfiguration bzw. im Onboarding-AP findet die Station, zieht das Image und startet mit der neuen Firmware - [ ] Rettungsstation nach erfolgreicher Recovery angelassen lassen, Lampe neu booten lassen → kein erneutes Flashen (Version stimmt bereits überein) und kein Werksreset (Power-Cycle-Zähler wurde vor dem Recovery-Versuch gelöscht) - [ ] OTA im AP-/Fallback-Modus (`mode=apsta`, kein Router erreichbar) und mit `auth_all = true` → Update läuft durch wie im normalen `sta`-Modus @@ -64,3 +64,12 @@ Für den echten Flottenbau `NINLIGHT_RECOVERY_PASSWORD` und `NINLIGHT_TOKEN` in - [ ] E27 CW: Gegenphase CW sichtbar flackerfrei - [ ] G95: Pinbelegung WW=PB15/CW=PB13 prüfen - [ ] Farbtemperaturbereiche aller Varianten + +## Offene Beobachtung aus der EVK-Session vom 2026-09-12 + +Beim Test mit dem verfälschten Image meldete lwIP einmalig +`Assertion "pbuf_free: p->ref > 0" failed at line 747 in pbuf.c` (10:29:23). Die Lampe lief +unbeeinträchtigt weiter und meldete korrekt `last_ota_error=crc`. Bei den Läufen mit fremdem +Gerätetyp und bei den erfolgreichen Updates trat die Meldung nicht auf. Ursache noch nicht +geklärt — in einem Build ohne Assertions wäre das ein doppeltes Freigeben eines Netzwerkpuffers. +Vor dem Einsatz auf echten Lampen reproduzieren und aufklären. From 5e2dff375ed550443d2162911c1ae6b1cec78259 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 10:58:09 +0200 Subject: [PATCH 074/150] fix: abgewiesener fw_update verdirbt last_ota_error des laufenden Updates nicht mehr Ein zweiter fw_update waehrend eines laufenden Downloads setzte s_error auf "busy". Da ota_task() nur bei leerem Fehlerfeld auf "download" zurueckfaellt, wurde ein spaeter gescheiterter Transfer danach als "busy" gemeldet statt als "download". Die Abweisung hat kein eigenes Ergebnis - sie wird der API mit 409 gemeldet, last_ota_error bleibt beim Ergebnis des laufenden bzw. letzten Updates. Dazu die Doku auf das am EVK gemessene Verhalten korrigiert: stirbt die Gegenstelle mitten im Download, haengt der Lesevorgang rund sieben Minuten in einem TCP-Timeout und bricht ohne Range-Resume ab. Co-Authored-By: Claude Opus 5 --- README.md | 14 ++++++++++---- docs/hardware-checklist.md | 6 +++--- src/net/nl_api.c | 6 +++--- src/sys/nl_ota.c | 7 ++++++- test/test_api.c | 2 +- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 01c5070..239509a 100644 --- a/README.md +++ b/README.md @@ -94,19 +94,25 @@ Update, oder scheitert der Start aus einem anderen Grund, antwortet sie mit `409 (gleicher Wert wie `last_ota_error`). Läuft der Trailer- bzw. Hash-Check durch, resettet die Lampe und bootet mit der neuen Version. Wird das Image abgelehnt, gibt es **keinen** Reset — die Lampe läuft mit der alten Firmware weiter und trägt den Grund in `last_ota_error` (`GET system/info`) ein. Ein -Verbindungsabbruch mitten im Download löst dagegen einen Wiederaufnahmeversuch per HTTP-Range aus (begrenzte -Anzahl Runden); erst wenn auch das ausgeht, landet der Versuch bei `last_ota_error = download`. +Verbindungsabbruch mitten im Download endet bei `last_ota_error = download`. Die Firmware kann einen +sauber abgerissenen Transfer per HTTP-Range wieder aufnehmen (auf eine feste Rundenzahl begrenzt), aber +darauf sollte man sich nicht verlassen: Stirbt die Gegenstelle mitten im Download, hängt der Lesevorgang +am EVK gemessen rund **sieben Minuten** in einem einzigen TCP-Timeout — das `RECV_TIMEOUT` von 5 s des +HTTP-Clients greift dort nicht — und bricht danach ohne Wiederaufnahme ab. Die Lampe bleibt in dieser Zeit +über REST erreichbar und lässt sich weiter schalten, weist aber jeden weiteren `fw_update` mit `409` ab. Werte von `last_ota_error` (`null`, solange nichts fehlgeschlagen ist): | Wert | Bedeutung | |---|---| -| `download` | Verbindung/Transfer gescheitert (auch nach ausgeschöpftem Wiederaufnahmeversuch) | +| `download` | Verbindung/Transfer gescheitert | | `crc` | Prüfsumme des SDK-eigenen Update-Mechanismus stimmt nicht | | `trailer` | Trailer fehlt, ist zu kurz oder hat eine falsche Magic/Version | | `device_mismatch` | Trailer nennt einen anderen Gerätetyp | | `hash` | SHA256 im Trailer stimmt nicht mit dem übertragenen Image überein | -| `busy` | es läuft bereits ein Update | +| `busy` | die Firmware konnte den Download-Task nicht anlegen (Speichermangel) | + +Ein `fw_update`, der abgewiesen wird, weil bereits ein Update läuft, beantwortet die API mit `409` und lässt `last_ota_error` unangetastet — dort steht weiterhin das Ergebnis des laufenden bzw. letzten Updates. Ein erneuter `fw_update`-Aufruf mit einem gültigen Image funktioniert nach einer Ablehnung sofort, ohne manuellen Reboot. diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 9cb8d56..b30f278 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -42,15 +42,15 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] OTA: Update über `fw_update` läuft durch, danach meldet `system/info` die neue Version — 2026-09-12 (0.3.0 → 0.3.1 → 0.3.2) - [x] OTA: Image eines anderen Gerätetyps wird sofort abgelehnt (`last_ota_error` = `device_mismatch`), kein Reset, Lampe läuft mit der alten Firmware weiter — 2026-09-12 (Ablehnung nach ~4,5 s, Uptime lief durch) - [x] OTA: Image mit verfälschtem Byte wird abgelehnt (`last_ota_error` = `hash` oder `crc`), kein Reset — 2026-09-12 (`crc`, kein Reset) -- [ ] OTA: HTTP-Server während des Downloads abschalten → Wiederaufnahmeversuch (Range-Resume) sichtbar in der Konsole, nach Ausschöpfen der Runden `last_ota_error` = `download`, Lampe bootet normal +- [x] OTA: HTTP-Server während des Downloads abschalten → Lampe bleibt über REST erreichbar und schaltbar, weist weitere `fw_update` mit `409` ab, und beendet den Versuch mit `last_ota_error = download` — 2026-09-12 (dauerte ~7 min, kein Range-Resume; siehe README) - [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler - [ ] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen -- [ ] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) +- [x] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) — 2026-09-12 (`scan done after 1500 ms, 7 AP(s)`, danach normaler WLAN-Start) - [ ] Recovery: Gegenstelle bleibt verbunden, sendet aber keine Daten mehr (nicht: ein langsamer, aber laufender Download) → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben - [x] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten) — 2026-09-12 (Name, Fade 1200/800, pob, Lichtzustand) - [x] Zweiter `fw_update` mit gültigem Image direkt nach einer Ablehnung, ohne zwischenzeitlichen Reboot → läuft durch, `system/info` meldet danach die neue Version — 2026-09-12 (erst nach dem SHA256-Kontext-Fix) - [x] Lampe bleibt während und nach einer Ablehnung über REST erreichbar: `system/info` und `device/state` antworten beide weiterhin mit 200 — 2026-09-12 (system/info und device/state je 200) -- [ ] Lichtverhalten über ein OTA hinweg (Spec §8.2, ein Update ist kein Stromausfall): einmal mit `power_on_behaviour = off`, einmal `= on` — die Lampe ist nach dem Update jeweils in demselben Zustand (an/aus, Farbe/Helligkeit), in dem sie vor dem Update war +- [x] Lichtverhalten über ein OTA hinweg (Spec §8.2, ein Update ist kein Stromausfall): einmal mit `power_on_behaviour = off`, einmal `= on` — die Lampe ist nach dem Update jeweils in demselben Zustand (an/aus, Farbe/Helligkeit), in dem sie vor dem Update war — 2026-09-12 (pob=off + Licht an → blieb an; pob=on + Licht aus → blieb aus) - [x] `system/info.fw_version` nach einem OTA stimmt mit der Version im Trailer des geflashten Images überein (Trailer-Version z.B. mit `tools/mkimage` prüfen) — 2026-09-12 - [ ] Recovery im echten Rettungsfall: Lampe ohne gültige WLAN-Konfiguration bzw. im Onboarding-AP findet die Station, zieht das Image und startet mit der neuen Firmware - [ ] Rettungsstation nach erfolgreicher Recovery angelassen lassen, Lampe neu booten lassen → kein erneutes Flashen (Version stimmt bereits überein) und kein Werksreset (Power-Cycle-Zähler wurde vor dem Recovery-Versuch gelöscht) diff --git a/src/net/nl_api.c b/src/net/nl_api.c index c53d325..e65869f 100644 --- a/src/net/nl_api.c +++ b/src/net/nl_api.c @@ -339,9 +339,9 @@ static void handle_command(const nl_api_ctx_t *ctx, const nl_http_request_t *req else if (ctx->fw_update(url) != 0) { /* fw_update() itself can genuinely fail now (busy, or a rejected URL) - 409 with the * reason, not a blanket "not implemented". */ - nl_sys_info_t in; - ctx->get_info(&in); - reply_error(resp, 409, in.last_ota_error ? in.last_ota_error : "fw_update busy or invalid url"); + /* Do not reuse last_ota_error for this message: it belongs to the running or last + * update, not to the request we are rejecting right now. */ + reply_error(resp, 409, "an update is already running"); } else reply(resp, 202, "{\"status\":\"updating\"}"); } else if (strcmp(type, "reboot") == 0) { diff --git a/src/sys/nl_ota.c b/src/sys/nl_ota.c index 99360b0..3025587 100644 --- a/src/sys/nl_ota.c +++ b/src/sys/nl_ota.c @@ -115,7 +115,12 @@ static int ota_start(const char *url, bool reject_running_version) if (n <= 7 || n >= sizeof s_url) /* n == 7 is bare "http://" with no host */ return -1; if (s_busy) { - s_error = "busy"; + /* Do NOT touch s_error here. last_ota_error describes the outcome of the *running or last* + * update; a request we reject because one is already in flight has no outcome of its own. + * Writing "busy" into it used to poison the running attempt's slot: ota_task() only falls + * back to "download" when s_error is still NULL, so a failed transfer was afterwards + * reported as `busy` instead of `download`. Found on the EVK, 2026-09-12. The REST layer + * reports the rejection to its own caller with 409. */ return -1; } s_busy = true; diff --git a/test/test_api.c b/test/test_api.c index 3ff2981..090037d 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -324,7 +324,7 @@ static void test_commands(void) call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"fw_update\",\"url\":\"http://x/y.img\"}}"); TEST_ASSERT_EQUAL_INT(409, resp.status); - TEST_ASSERT_EQUAL_STRING("fw_update busy or invalid url", error_text()); + TEST_ASSERT_EQUAL_STRING("an update is already running", error_text()); TEST_ASSERT_EQUAL_STRING("http://x/y.img", fake_fw_url); fake_fw_result = 0; call(NL_HTTP_PUT, "/api/v1/system/command", TOKEN, "{\"command\":{\"type\":\"fw_update\",\"url\":\"http://x/y.img\"}}"); From c5d76efb25efcd34a7405c934a6a9f137457e52f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 11:00:50 +0200 Subject: [PATCH 075/150] docs: auth_all-Ergebnis und die beiden offenen Punkte der EVK-Runde vermerken Co-Authored-By: Claude Opus 5 --- docs/hardware-checklist.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index b30f278..dd7bf82 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -43,7 +43,7 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] OTA: Image eines anderen Gerätetyps wird sofort abgelehnt (`last_ota_error` = `device_mismatch`), kein Reset, Lampe läuft mit der alten Firmware weiter — 2026-09-12 (Ablehnung nach ~4,5 s, Uptime lief durch) - [x] OTA: Image mit verfälschtem Byte wird abgelehnt (`last_ota_error` = `hash` oder `crc`), kein Reset — 2026-09-12 (`crc`, kein Reset) - [x] OTA: HTTP-Server während des Downloads abschalten → Lampe bleibt über REST erreichbar und schaltbar, weist weitere `fw_update` mit `409` ab, und beendet den Versuch mit `last_ota_error = download` — 2026-09-12 (dauerte ~7 min, kein Range-Resume; siehe README) -- [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler +- [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler — *offen, WLAN am Teststandort nicht abschaltbar* - [ ] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen - [x] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) — 2026-09-12 (`scan done after 1500 ms, 7 AP(s)`, danach normaler WLAN-Start) - [ ] Recovery: Gegenstelle bleibt verbunden, sendet aber keine Daten mehr (nicht: ein langsamer, aber laufender Download) → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben @@ -54,7 +54,7 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] `system/info.fw_version` nach einem OTA stimmt mit der Version im Trailer des geflashten Images überein (Trailer-Version z.B. mit `tools/mkimage` prüfen) — 2026-09-12 - [ ] Recovery im echten Rettungsfall: Lampe ohne gültige WLAN-Konfiguration bzw. im Onboarding-AP findet die Station, zieht das Image und startet mit der neuen Firmware - [ ] Rettungsstation nach erfolgreicher Recovery angelassen lassen, Lampe neu booten lassen → kein erneutes Flashen (Version stimmt bereits überein) und kein Werksreset (Power-Cycle-Zähler wurde vor dem Recovery-Versuch gelöscht) -- [ ] OTA im AP-/Fallback-Modus (`mode=apsta`, kein Router erreichbar) und mit `auth_all = true` → Update läuft durch wie im normalen `sta`-Modus +- [ ] OTA im **AP-/Fallback-Modus** (`mode=apsta`, kein Router erreichbar) → Update läuft durch wie im normalen `sta`-Modus — *offen, braucht einen abschaltbaren Access Point*. Mit `auth_all = true` am 2026-09-12 geprüft: Update läuft durch, `device/*` antwortet ohne Token korrekt mit 401 (`system/info` bleibt laut Spec §7.2 immer offen) Für den echten Flottenbau `NINLIGHT_RECOVERY_PASSWORD` und `NINLIGHT_TOKEN` in `config.local.cmake` setzen — die Defaults sind nur fürs EVK gedacht. From 37a5fe2ca9da00dc6ac3353e748c28af6502e2cb Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 11:22:17 +0200 Subject: [PATCH 076/150] fix: uebersprungenes Recovery-Image ist kein Fehler mehr Lehnte die Recovery ein Image ab, weil es bereits die laufende Version traegt, meldete die Firmware das als last_ota_error=trailer. Wer eine Lampe im Feld ausliest, haette dort einen Trailer-Fehler gesehen, wo in Wahrheit nichts passiert ist - und die Meldung in der Konsole lautete "update failed". Das bewusste Ueberspringen hat jetzt einen eigenen Zustand (nl_ota_skipped()), setzt keinen Fehlerwert und wird als "image already up to date" protokolliert. Co-Authored-By: Claude Opus 5 --- src/net/nl_recovery.c | 12 +++++++++--- src/sys/nl_ota.c | 10 ++++++++-- src/sys/nl_ota.h | 3 +++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/net/nl_recovery.c b/src/net/nl_recovery.c index 64a9c4d..f691665 100644 --- a/src/net/nl_recovery.c +++ b/src/net/nl_recovery.c @@ -162,8 +162,14 @@ void nl_recovery_try(void) tls_os_time_delay(HZ / 2); waited += 500; } - if (feeding) - printf("recovery: update failed (%s), continuing normal boot\r\n", - nl_ota_last_error() ? nl_ota_last_error() : "unknown"); + if (feeding) { + /* A skipped image (same version as the running firmware) is not a failure - saying so + would leave a healthy lamp looking like it had rejected a broken image. */ + if (nl_ota_skipped()) + printf("recovery: image already up to date, continuing normal boot\r\n"); + else + printf("recovery: update failed (%s), continuing normal boot\r\n", + nl_ota_last_error() ? nl_ota_last_error() : "unknown"); + } } } diff --git a/src/sys/nl_ota.c b/src/sys/nl_ota.c index 3025587..6935946 100644 --- a/src/sys/nl_ota.c +++ b/src/sys/nl_ota.c @@ -23,6 +23,7 @@ static char s_url[NL_OTA_URL_MAX]; static volatile bool s_busy; static volatile bool s_reject_running_version; /* recovery path only, see nl_ota_start_recovery() */ static const char * volatile s_error; +static volatile bool s_skipped; static u32 s_stack[NL_OTA_STACK]; static char s_tail_version[NL_TRAILER_VERSION_LEN]; /* used by the hook, kept off its 2 KB stack */ @@ -33,6 +34,7 @@ void nl_ota_init(void) } bool nl_ota_busy(void) { return s_busy; } +bool nl_ota_skipped(void) { return s_skipped; } const char *nl_ota_last_error(void) { return s_error; } void nl_ota_set_error(const char *e) { s_error = e; } @@ -61,7 +63,10 @@ bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t if (strcmp(s_tail_version, NINLIGHT_FW_VERSION) == 0) { printf("ota: recovery image already carries the running version %s, not flashing\r\n", s_tail_version); - s_error = "trailer"; + /* Deliberately skipped, not failed: leave last_ota_error alone. Reporting "trailer" + * here used to make a perfectly healthy lamp look like it had rejected a malformed + * image, which is exactly the wrong thing to read off a lamp in the field. */ + s_skipped = true; return false; } printf("ota: recovery image version %s (running %s)\r\n", s_tail_version, NINLIGHT_FW_VERSION); @@ -90,7 +95,7 @@ static void ota_task(void *arg) log_url(s_url); rc = t_http_fwup(s_url); /* Only reached on failure - the SDK resets the chip itself after a good image. */ - if (s_error == NULL) + if (s_error == NULL && !s_skipped) s_error = "download"; printf("ota: failed (rc=%d, %s)\r\n", rc, s_error); { @@ -125,6 +130,7 @@ static int ota_start(const char *url, bool reject_running_version) } s_busy = true; s_error = NULL; + s_skipped = false; s_reject_running_version = reject_running_version; memcpy(s_url, url, n + 1); diff --git a/src/sys/nl_ota.h b/src/sys/nl_ota.h index 62d0d6b..603297e 100644 --- a/src/sys/nl_ota.h +++ b/src/sys/nl_ota.h @@ -12,6 +12,9 @@ int nl_ota_start(const char *url); * forever. A deliberate re-flash of the same version stays possible over the REST API. */ int nl_ota_start_recovery(const char *url); bool nl_ota_busy(void); +/* True when the last recovery attempt deliberately skipped an image whose version already + matches the running firmware - that is not a failure and sets no last_ota_error. */ +bool nl_ota_skipped(void); /* NULL, or one of: download, crc, trailer, device_mismatch, hash, busy */ const char *nl_ota_last_error(void); void nl_ota_set_error(const char *e); From 76a325332241e502a44874de71278e3aa866a75f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 11:24:18 +0200 Subject: [PATCH 077/150] docs: Recovery am EVK gegen die Pi-Station verifiziert Co-Authored-By: Claude Opus 5 --- docs/hardware-checklist.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index dd7bf82..c076b7a 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -44,7 +44,7 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] OTA: Image mit verfälschtem Byte wird abgelehnt (`last_ota_error` = `hash` oder `crc`), kein Reset — 2026-09-12 (`crc`, kein Reset) - [x] OTA: HTTP-Server während des Downloads abschalten → Lampe bleibt über REST erreichbar und schaltbar, weist weitere `fw_update` mit `409` ab, und beendet den Versuch mit `last_ota_error = download` — 2026-09-12 (dauerte ~7 min, kein Range-Resume; siehe README) - [ ] OTA: kurzer WLAN-Aussetzer während des Downloads (Verbindung kurz kappen und wiederherstellen) → Download wird fortgesetzt, kein sofortiger `download`-Fehler — *offen, WLAN am Teststandort nicht abschaltbar* -- [ ] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen +- [x] Recovery: Lampe zieht beim Booten das Image von der Station `ninlight-recovery`, Konsole zeigt Scan-, Join- und Fetch-Meldungen — 2026-09-12 (Pi Zero 2W, NetworkManager-AP; Scan → join → fetch → flash → Reboot) - [x] Recovery: ohne Station verlängert sich der Boot um höchstens ~3 s, Konsole loggt das Scan-Ergebnis (gefunden/nichts gefunden/Scan fehlgeschlagen) — 2026-09-12 (`scan done after 1500 ms, 7 AP(s)`, danach normaler WLAN-Start) - [ ] Recovery: Gegenstelle bleibt verbunden, sendet aber keine Daten mehr (nicht: ein langsamer, aber laufender Download) → nach Ablauf der Zeitobergrenze bedient die Firmware den Watchdog nicht mehr, Chip startet neu statt hängen zu bleiben - [x] Einstellungen bleiben über ein OTA hinweg erhalten (WLAN, Name, Fade-Zeiten) — 2026-09-12 (Name, Fade 1200/800, pob, Lichtzustand) @@ -53,7 +53,7 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s - [x] Lichtverhalten über ein OTA hinweg (Spec §8.2, ein Update ist kein Stromausfall): einmal mit `power_on_behaviour = off`, einmal `= on` — die Lampe ist nach dem Update jeweils in demselben Zustand (an/aus, Farbe/Helligkeit), in dem sie vor dem Update war — 2026-09-12 (pob=off + Licht an → blieb an; pob=on + Licht aus → blieb aus) - [x] `system/info.fw_version` nach einem OTA stimmt mit der Version im Trailer des geflashten Images überein (Trailer-Version z.B. mit `tools/mkimage` prüfen) — 2026-09-12 - [ ] Recovery im echten Rettungsfall: Lampe ohne gültige WLAN-Konfiguration bzw. im Onboarding-AP findet die Station, zieht das Image und startet mit der neuen Firmware -- [ ] Rettungsstation nach erfolgreicher Recovery angelassen lassen, Lampe neu booten lassen → kein erneutes Flashen (Version stimmt bereits überein) und kein Werksreset (Power-Cycle-Zähler wurde vor dem Recovery-Versuch gelöscht) +- [x] Rettungsstation nach erfolgreicher Recovery angelassen lassen, Lampe neu booten lassen → kein erneutes Flashen (Version stimmt bereits überein) und kein Werksreset (Power-Cycle-Zähler wurde vor dem Recovery-Versuch gelöscht) — 2026-09-12 (zweiter Boot: `image already carries the running version, not flashing`, kein Werksreset, beide Boots melden `boot #1`) - [ ] OTA im **AP-/Fallback-Modus** (`mode=apsta`, kein Router erreichbar) → Update läuft durch wie im normalen `sta`-Modus — *offen, braucht einen abschaltbaren Access Point*. Mit `auth_all = true` am 2026-09-12 geprüft: Update läuft durch, `device/*` antwortet ohne Token korrekt mit 401 (`system/info` bleibt laut Spec §7.2 immer offen) Für den echten Flottenbau `NINLIGHT_RECOVERY_PASSWORD` und `NINLIGHT_TOKEN` in `config.local.cmake` setzen — die Defaults sind nur fürs EVK gedacht. From 37f4dd425e0547c97787670a36c1dd56b682f358 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 11:45:05 +0200 Subject: [PATCH 078/150] feat: HTTP-Server der Recovery-Station Liefert ausschliesslich Images aus, deren Name dem Muster ninlight__ota.img entspricht, unterstuetzt Range-Anfragen fuer den Wiederaufnahmeversuch der Firmware und schreibt seinen Zustand nach status.json - die Schnittstelle fuer die E-Paper-Anzeige in 4c. Behebt zwei Races aus der Referenzimplementierung des Task-Briefs: der Server trug Erfolg/404 erst nach dem letzten gesendeten Byte in status.json ein, wodurch ein Client, der die Antwort vollstaendig gelesen hat, noch den alten Zustand sehen konnte. Ausserdem wurde ein ganzer 32-KiB-Chunk auch dann komplett gesendet, wenn --stall-after schon ueberschritten war; die Lesegroesse wird jetzt an die Stall-Grenze gekappt, und ein Verbindungsabbruch waehrend des Stalls wird per select/MSG_PEEK erkannt und als "aborted" vermerkt statt den Thread endlos zu blockieren. Co-Authored-By: Claude Sonnet 5 --- tools/station/pyproject.toml | 8 + tools/station/server.py | 230 +++++++++++++++++++++++++++++ tools/station/tests/test_server.py | 215 +++++++++++++++++++++++++++ 3 files changed, 453 insertions(+) create mode 100644 tools/station/pyproject.toml create mode 100755 tools/station/server.py create mode 100644 tools/station/tests/test_server.py diff --git a/tools/station/pyproject.toml b/tools/station/pyproject.toml new file mode 100644 index 0000000..f27b4de --- /dev/null +++ b/tools/station/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = "ninlight-station" +version = "0.1.0" +description = "recovery station HTTP server for ninlight lamps" +requires-python = ">=3.11" + +[dependency-groups] +dev = ["pytest>=8"] diff --git a/tools/station/server.py b/tools/station/server.py new file mode 100755 index 0000000..e799492 --- /dev/null +++ b/tools/station/server.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +"""ninlight recovery station. + +Serves firmware images to lamps that look for a rescue station while booting. Deliberately +minimal: it hands out nothing but files whose name matches ninlight__ota.img, so a +stray file in the image directory can never leave the machine. + +Standard library only - the Pi gets no extra packages. +""" +from __future__ import annotations + +import argparse +import json +import os +import re +import select +import socket +import sys +import tempfile +import time +from datetime import datetime, timezone +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path + +TARGETS = ("evk", "e27_rgb", "e14_rgb", "gu10_rgb", "e27_cw", "e14_cw", "gu10_cw", "g95_cw") +PATH_RE = re.compile(r"^/ninlight_(" + "|".join(TARGETS) + r")_ota\.img$") +TRAILER_SIZE = 512 +TRAILER_MAGIC = b"NINL" +VERSION_OFF = 21 +VERSION_LEN = 16 +RECENT_MAX = 8 +CHUNK = 32 * 1024 + + +def now_iso() -> str: + return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + +def trailer_version(path: Path) -> str: + """Firmware version from the image trailer, or '?' if the file carries none.""" + try: + with path.open("rb") as f: + if path.stat().st_size < TRAILER_SIZE: + return "?" + f.seek(-TRAILER_SIZE, os.SEEK_END) + tail = f.read(TRAILER_SIZE) + except OSError: + return "?" + if tail[0:4] != TRAILER_MAGIC: + return "?" + return tail[VERSION_OFF:VERSION_OFF + VERSION_LEN].rstrip(b"\x00").decode("ascii", "replace") + + +class Station: + def __init__(self, image_dir: Path, state_path: Path, stall_after: int | None) -> None: + self.image_dir = image_dir + self.state_path = state_path + self.stall_after = stall_after + self.since = now_iso() + self.served = 0 + self.recent: list[dict] = [] + self.write_state() + + def image_path(self, target: str) -> Path: + return self.image_dir / f"ninlight_{target}_ota.img" + + def record(self, ip: str, target: str, version: str, nbytes: int, status: str) -> None: + if status == "ok": + self.served += 1 + self.recent.append({"ts": now_iso(), "ip": ip, "target": target, + "version": version, "bytes": nbytes, "status": status}) + del self.recent[:-RECENT_MAX] + self.write_state() + + def write_state(self) -> None: + payload = {"since": self.since, "served": self.served, "recent": self.recent} + try: + self.state_path.parent.mkdir(parents=True, exist_ok=True) + fd, tmp = tempfile.mkstemp(dir=self.state_path.parent, prefix=".status-") + with os.fdopen(fd, "w") as f: + json.dump(payload, f, indent=2) + os.replace(tmp, self.state_path) + except OSError as e: + print(f"warning: cannot write {self.state_path}: {e}", flush=True) + + +class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + server_version = "ninlight-station" + + @property + def station(self) -> Station: + return self.server.station # type: ignore[attr-defined] + + def log_message(self, fmt: str, *args) -> None: # noqa: D102 - quieter default log + pass + + def log(self, status: int, nbytes: int, started: float) -> None: + print(f"{now_iso()} {self.client_address[0]} {self.path} {status} " + f"{nbytes}B {time.monotonic() - started:.1f}s", flush=True) + + def do_POST(self) -> None: + self.reject(405, "method not allowed") + + do_PUT = do_DELETE = do_HEAD = do_POST + + def reject(self, status: int, text: str) -> None: + body = (text + "\n").encode() + self.send_response(status) + self.send_header("Content-Type", "text/plain") + self.send_header("Content-Length", str(len(body))) + self.send_header("Connection", "close") + self.end_headers() + self.wfile.write(body) + + def do_GET(self) -> None: + started = time.monotonic() + m = PATH_RE.match(self.path) + if not m: + self.reject(404, "not found") + self.log(404, 0, started) + return + target = m.group(1) + path = self.station.image_path(target) + if not path.is_file(): + # Record before responding: a client that sees the 404 complete may check + # status.json right away, so the entry must already be on disk by then. + self.station.record(self.client_address[0], target, "?", 0, "notfound") + self.reject(404, "no image for this device") + self.log(404, 0, started) + return + + size = path.stat().st_size + start = 0 + rng = self.headers.get("Range", "") + if rng.startswith("bytes="): + first = rng.split("=", 1)[1].split("-", 1)[0] + if first.isdigit() and int(first) < size: + start = int(first) + + version = trailer_version(path) + self.send_response(206 if start else 200) + self.send_header("Content-Type", "application/octet-stream") + self.send_header("Content-Length", str(size - start)) + if start: + self.send_header("Content-Range", f"bytes {start}-{size - 1}/{size}") + self.send_header("Accept-Ranges", "bytes") + self.send_header("Connection", "close") + self.end_headers() + + sent = 0 + status = "ok" + recorded = False + remaining = size - start + try: + with path.open("rb") as f: + f.seek(start) + while sent < remaining: + if self.station.stall_after is not None and sent >= self.station.stall_after: + # test aid: hold the connection open and send nothing more, so the + # firmware's stall handling can be exercised on real hardware. We + # still notice if the peer gives up and closes its side, so a test + # (or an impatient client) that hangs up is recorded as "aborted" + # instead of leaking the thread forever. + while True: + readable, _, _ = select.select([self.connection], [], [], 1.0) + if readable: + try: + peek = self.connection.recv(1, socket.MSG_PEEK) + except OSError: + peek = b"" + if not peek: + raise ConnectionResetError("peer closed during stall") + read_size = CHUNK + if self.station.stall_after is not None: + # Don't overshoot the stall boundary within a single read: without + # this a whole CHUNK could be sent before the check above ever + # notices we've passed --stall-after. + read_size = min(read_size, max(1, self.station.stall_after - sent)) + chunk = f.read(read_size) + if not chunk: + break + if sent + len(chunk) >= remaining: + # Record success before the last bytes go out: the lamp (and our + # tests) may observe the response completing and immediately check + # status.json, so the state must already be on disk by then. + self.station.record(self.client_address[0], target, version, + sent + len(chunk), "ok") + recorded = True + self.wfile.write(chunk) + sent += len(chunk) + except (BrokenPipeError, ConnectionResetError, TimeoutError): + status = "aborted" + self.log(206 if start else 200, sent, started) + if not recorded: + self.station.record(self.client_address[0], target, version, sent, status) + + +def main(argv: list[str] | None = None) -> int: + p = argparse.ArgumentParser(description="ninlight recovery station HTTP server") + p.add_argument("--image-dir", required=True, type=Path) + p.add_argument("--bind", default="192.168.8.1") + p.add_argument("--port", type=int, default=80) + p.add_argument("--state", type=Path, default=Path("/run/ninlight-station/status.json")) + p.add_argument("--stall-after", type=int, default=None, + help="test aid: stop sending after N bytes but keep the connection open") + a = p.parse_args(argv) + + if not a.image_dir.is_dir(): + print(f"error: image directory {a.image_dir} does not exist", file=sys.stderr) + return 2 + + station = Station(a.image_dir, a.state, a.stall_after) + httpd = ThreadingHTTPServer((a.bind, a.port), Handler) + httpd.daemon_threads = True + httpd.station = station # type: ignore[attr-defined] + images = sorted(p.name for p in a.image_dir.glob("ninlight_*_ota.img")) + print(f"{now_iso()} listening on {a.bind}:{a.port}, {len(images)} image(s): " + f"{', '.join(images) or 'none'}", flush=True) + if a.stall_after is not None: + print(f"{now_iso()} WARNING: --stall-after {a.stall_after} is a test aid", flush=True) + try: + httpd.serve_forever() + except KeyboardInterrupt: + pass + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py new file mode 100644 index 0000000..052fca4 --- /dev/null +++ b/tools/station/tests/test_server.py @@ -0,0 +1,215 @@ +import hashlib +import http.client +import json +import socket +import subprocess +import sys +import time +from pathlib import Path + +import pytest + +SERVER = Path(__file__).resolve().parents[1] / "server.py" +TRAILER_SIZE = 512 + + +def make_image(path: Path, device: str, version: str, payload: bytes) -> bytes: + """Builds a file with the same trailer layout the firmware verifies.""" + trailer = bytearray(b"\xff" * TRAILER_SIZE) + trailer[0:4] = b"NINL" + trailer[4] = 1 + trailer[5:21] = device.encode().ljust(16, b"\x00") + trailer[21:37] = version.encode().ljust(16, b"\x00") + trailer[37:69] = hashlib.sha256(payload).digest() + data = payload + bytes(trailer) + path.write_bytes(data) + return data + + +def free_port() -> int: + s = socket.socket() + s.bind(("127.0.0.1", 0)) + port = s.getsockname()[1] + s.close() + return port + + +class Station: + def __init__(self, tmp_path, *extra): + self.image_dir = tmp_path / "images" + self.image_dir.mkdir() + self.state = tmp_path / "status.json" + self.port = free_port() + self.proc = subprocess.Popen( + [sys.executable, str(SERVER), "--image-dir", str(self.image_dir), + "--bind", "127.0.0.1", "--port", str(self.port), + "--state", str(self.state), *extra], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + ) + deadline = time.time() + 10 + while time.time() < deadline: + try: + socket.create_connection(("127.0.0.1", self.port), 0.2).close() + return + except OSError: + if self.proc.poll() is not None: + raise RuntimeError("server died: " + (self.proc.stdout.read() or "")) + time.sleep(0.05) + raise RuntimeError("server did not come up") + + def conn(self): + return http.client.HTTPConnection("127.0.0.1", self.port, timeout=5) + + def read_state(self): + for _ in range(50): + if self.state.exists(): + try: + return json.loads(self.state.read_text()) + except json.JSONDecodeError: + pass + time.sleep(0.05) + raise AssertionError("no state file written") + + def stop(self): + self.proc.terminate() + self.proc.wait(timeout=5) + + +@pytest.fixture +def station(tmp_path): + s = Station(tmp_path) + yield s + s.stop() + + +def test_serves_a_known_image(station): + data = make_image(station.image_dir / "ninlight_evk_ota.img", "evk", "1.2.3", b"payload" * 500) + c = station.conn() + c.request("GET", "/ninlight_evk_ota.img") + r = c.getresponse() + body = r.read() + assert r.status == 200 + assert int(r.getheader("Content-Length")) == len(data) + assert body == data + + +def test_missing_image_is_404(station): + c = station.conn() + c.request("GET", "/ninlight_g95_cw_ota.img") + assert c.getresponse().status == 404 + + +def test_unknown_target_is_404(station): + make_image(station.image_dir / "ninlight_evk_ota.img", "evk", "1.0.0", b"x" * 100) + for path in ("/ninlight_nosuch_ota.img", "/ninlight_evk_ota.img.bak", "/", "/images/"): + c = station.conn() + c.request("GET", path) + assert c.getresponse().status == 404, path + + +def test_path_traversal_is_refused(station): + secret = station.image_dir.parent / "secret.txt" + secret.write_text("nicht ausliefern") + c = station.conn() + c.request("GET", "/../secret.txt") + assert c.getresponse().status == 404 + + +def test_other_methods_are_405(station): + c = station.conn() + c.request("POST", "/ninlight_evk_ota.img", body=b"") + assert c.getresponse().status == 405 + + +def test_range_request_returns_206_and_the_right_slice(station): + data = make_image(station.image_dir / "ninlight_e27_rgb_ota.img", "e27_rgb", "2.0.0", b"abc" * 2000) + c = station.conn() + c.request("GET", "/ninlight_e27_rgb_ota.img", headers={"Range": "bytes=1000-"}) + r = c.getresponse() + body = r.read() + assert r.status == 206 + assert r.getheader("Content-Range") == f"bytes 1000-{len(data)-1}/{len(data)}" + assert body == data[1000:] + + +def test_state_file_records_a_delivery(station): + make_image(station.image_dir / "ninlight_evk_ota.img", "evk", "0.3.4", b"z" * 1000) + c = station.conn() + c.request("GET", "/ninlight_evk_ota.img") + c.getresponse().read() + state = station.read_state() + assert state["served"] == 1 + entry = state["recent"][-1] + assert entry["target"] == "evk" + assert entry["version"] == "0.3.4" + assert entry["status"] == "ok" + assert entry["ip"] == "127.0.0.1" + assert entry["bytes"] == 1000 + TRAILER_SIZE + + +def test_state_file_records_a_missing_image(station): + c = station.conn() + c.request("GET", "/ninlight_g95_cw_ota.img") + c.getresponse().read() + state = station.read_state() + assert state["served"] == 0 + assert state["recent"][-1]["status"] == "notfound" + assert state["recent"][-1]["target"] == "g95_cw" + + +def test_recent_is_capped_at_eight(station): + make_image(station.image_dir / "ninlight_evk_ota.img", "evk", "1.0.0", b"q" * 10) + for _ in range(12): + c = station.conn() + c.request("GET", "/ninlight_evk_ota.img") + c.getresponse().read() + state = station.read_state() + assert state["served"] == 12 + assert len(state["recent"]) == 8 + + +def test_aborted_transfer_is_recorded(tmp_path): + s = Station(tmp_path, "--stall-after", "256") + try: + make_image(s.image_dir / "ninlight_evk_ota.img", "evk", "1.0.0", b"y" * 100000) + sock = socket.create_connection(("127.0.0.1", s.port), timeout=5) + sock.sendall(b"GET /ninlight_evk_ota.img HTTP/1.1\r\nHost: x\r\n\r\n") + sock.recv(4096) + time.sleep(0.3) + sock.close() + deadline = time.time() + 10 + while time.time() < deadline: + try: + entry = json.loads(s.state.read_text())["recent"][-1] + if entry["status"] == "aborted": + assert entry["bytes"] < 100000 + return + except (OSError, json.JSONDecodeError, IndexError, KeyError): + pass + time.sleep(0.1) + raise AssertionError("abort was not recorded") + finally: + s.stop() + + +def test_stall_after_stops_sending_but_keeps_the_connection(tmp_path): + s = Station(tmp_path, "--stall-after", "512") + try: + make_image(s.image_dir / "ninlight_evk_ota.img", "evk", "1.0.0", b"y" * 200000) + sock = socket.create_connection(("127.0.0.1", s.port), timeout=5) + sock.sendall(b"GET /ninlight_evk_ota.img HTTP/1.1\r\nHost: x\r\n\r\n") + got = b"" + sock.settimeout(2) + try: + while len(got) < 200000: + chunk = sock.recv(65536) + if not chunk: + break + got += chunk + except socket.timeout: + pass + # far less than the full image, and the peer never closed the connection + assert len(got) < 20000 + sock.close() + finally: + s.stop() From a2e87b88552b6b56cb4f897bcc07627c8c415d5f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 11:58:18 +0200 Subject: [PATCH 079/150] =?UTF-8?q?fix:=20Nebenl=C3=A4ufigkeit=20und=20ver?= =?UTF-8?q?fr=C3=BChtes=20"ok"=20in=20der=20Recovery-Station=20beheben?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix-Runde 1 nach Review: Station.served und Station.recent wurden ohne Lock von mehreren Handler-Threads (ein Thread pro Verbindung unter ThreadingHTTPServer) mutiert - der Normalfall, wenn mehrere Lampen gleichzeitig laden. Ein threading.RLock schuetzt jetzt Mutation und Serialisierung nach status.json als eine Einheit. Ausserdem hatte mein vorheriger Fix fuer das Status-Race einen neuen Fehler eingefuehrt: "ok" wurde vor dem letzten wfile.write() vermerkt, sodass ein danach scheiternder Schreibvorgang trotzdem als Erfolg in status.json stand. record() laeuft jetzt erst, nachdem die Uebertragung tatsaechlich abgeschlossen oder gescheitert ist; die betroffenen Tests warten stattdessen auf den Eintrag statt sofortige Konsistenz vorauszusetzen. test_stall_after_stops_sending_but_keeps_the_connection prüfte bisher nur die gesendete Bytezahl und haette einen faelschlich schliessenden Server nicht bemerkt - ergaenzt um eine explizite Pruefung, dass ein weiterer recv() in einen Timeout laeuft statt EOF zu liefern. Neuer Test test_concurrent_downloads_are_all_recorded faehrt 20 gleichzeitige Downloads und prueft served/recent auf Vollstaendigkeit; er schlaegt zuverlaessig fehl, wenn das Lock entfernt wird (lokal gegengeprueft: 3 von 10 Laeufen ohne Lock verloren Eintraege, 10 von 10 mit Lock bestanden). Co-Authored-By: Claude Sonnet 5 --- tools/station/server.py | 52 +++++++++--------- tools/station/tests/test_server.py | 87 ++++++++++++++++++++++++++---- 2 files changed, 105 insertions(+), 34 deletions(-) diff --git a/tools/station/server.py b/tools/station/server.py index e799492..59d05c9 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -17,6 +17,7 @@ import select import socket import sys import tempfile +import threading import time from datetime import datetime, timezone from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer @@ -59,29 +60,36 @@ class Station: self.since = now_iso() self.served = 0 self.recent: list[dict] = [] + # ThreadingHTTPServer runs one handler thread per connection, and several lamps + # can be downloading at once. Every mutation of served/recent and the snapshot + # that gets serialized to disk must happen under this same lock, or concurrent + # requests lose increments and stomp on each other's status.json writes. + self.lock = threading.RLock() self.write_state() def image_path(self, target: str) -> Path: return self.image_dir / f"ninlight_{target}_ota.img" def record(self, ip: str, target: str, version: str, nbytes: int, status: str) -> None: - if status == "ok": - self.served += 1 - self.recent.append({"ts": now_iso(), "ip": ip, "target": target, - "version": version, "bytes": nbytes, "status": status}) - del self.recent[:-RECENT_MAX] - self.write_state() + with self.lock: + if status == "ok": + self.served += 1 + self.recent.append({"ts": now_iso(), "ip": ip, "target": target, + "version": version, "bytes": nbytes, "status": status}) + del self.recent[:-RECENT_MAX] + self.write_state() def write_state(self) -> None: - payload = {"since": self.since, "served": self.served, "recent": self.recent} - try: - self.state_path.parent.mkdir(parents=True, exist_ok=True) - fd, tmp = tempfile.mkstemp(dir=self.state_path.parent, prefix=".status-") - with os.fdopen(fd, "w") as f: - json.dump(payload, f, indent=2) - os.replace(tmp, self.state_path) - except OSError as e: - print(f"warning: cannot write {self.state_path}: {e}", flush=True) + with self.lock: + payload = {"since": self.since, "served": self.served, "recent": list(self.recent)} + try: + self.state_path.parent.mkdir(parents=True, exist_ok=True) + fd, tmp = tempfile.mkstemp(dir=self.state_path.parent, prefix=".status-") + with os.fdopen(fd, "w") as f: + json.dump(payload, f, indent=2) + os.replace(tmp, self.state_path) + except OSError as e: + print(f"warning: cannot write {self.state_path}: {e}", flush=True) class Handler(BaseHTTPRequestHandler): @@ -150,7 +158,6 @@ class Handler(BaseHTTPRequestHandler): sent = 0 status = "ok" - recorded = False remaining = size - start try: with path.open("rb") as f: @@ -180,20 +187,15 @@ class Handler(BaseHTTPRequestHandler): chunk = f.read(read_size) if not chunk: break - if sent + len(chunk) >= remaining: - # Record success before the last bytes go out: the lamp (and our - # tests) may observe the response completing and immediately check - # status.json, so the state must already be on disk by then. - self.station.record(self.client_address[0], target, version, - sent + len(chunk), "ok") - recorded = True self.wfile.write(chunk) sent += len(chunk) except (BrokenPipeError, ConnectionResetError, TimeoutError): status = "aborted" + # Record only now that the transfer has actually finished (or failed): "ok" must + # never be written before the last byte is confirmed sent, or status.json can + # claim success for a device that never received the end of the image. self.log(206 if start else 200, sent, started) - if not recorded: - self.station.record(self.client_address[0], target, version, sent, status) + self.station.record(self.client_address[0], target, version, sent, status) def main(argv: list[str] | None = None) -> int: diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index 052fca4..a027273 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -4,6 +4,7 @@ import json import socket import subprocess import sys +import threading import time from pathlib import Path @@ -11,6 +12,7 @@ import pytest SERVER = Path(__file__).resolve().parents[1] / "server.py" TRAILER_SIZE = 512 +RECENT_MAX = 8 def make_image(path: Path, device: str, version: str, payload: bytes) -> bytes: @@ -60,15 +62,29 @@ class Station: def conn(self): return http.client.HTTPConnection("127.0.0.1", self.port, timeout=5) - def read_state(self): - for _ in range(50): + def read_state(self, predicate=None): + """Reads status.json, optionally waiting for it to satisfy `predicate`. + + The server only writes the entry for a request after that request's response has + actually finished (or failed) on the wire - by design, so it never claims success + before the last byte is confirmed sent (see server.py). A client that has just + finished reading its own response can therefore still see a status.json that is a + few milliseconds stale. Rather than have the server promise something before it is + true, we wait here for the state to catch up. + """ + last = None + for _ in range(100): if self.state.exists(): try: - return json.loads(self.state.read_text()) + last = json.loads(self.state.read_text()) + if predicate is None or predicate(last): + return last except json.JSONDecodeError: pass time.sleep(0.05) - raise AssertionError("no state file written") + if last is None: + raise AssertionError("no state file written") + raise AssertionError(f"state never satisfied predicate, last seen: {last!r}") def stop(self): self.proc.terminate() @@ -137,7 +153,7 @@ def test_state_file_records_a_delivery(station): c = station.conn() c.request("GET", "/ninlight_evk_ota.img") c.getresponse().read() - state = station.read_state() + state = station.read_state(lambda s: s["served"] >= 1) assert state["served"] == 1 entry = state["recent"][-1] assert entry["target"] == "evk" @@ -151,7 +167,7 @@ def test_state_file_records_a_missing_image(station): c = station.conn() c.request("GET", "/ninlight_g95_cw_ota.img") c.getresponse().read() - state = station.read_state() + state = station.read_state(lambda s: len(s["recent"]) >= 1) assert state["served"] == 0 assert state["recent"][-1]["status"] == "notfound" assert state["recent"][-1]["target"] == "g95_cw" @@ -163,9 +179,9 @@ def test_recent_is_capped_at_eight(station): c = station.conn() c.request("GET", "/ninlight_evk_ota.img") c.getresponse().read() - state = station.read_state() + state = station.read_state(lambda s: s["served"] >= 12) assert state["served"] == 12 - assert len(state["recent"]) == 8 + assert len(state["recent"]) == RECENT_MAX def test_aborted_transfer_is_recorded(tmp_path): @@ -208,8 +224,61 @@ def test_stall_after_stops_sending_but_keeps_the_connection(tmp_path): got += chunk except socket.timeout: pass - # far less than the full image, and the peer never closed the connection + # far less than the full image assert len(got) < 20000 + # and the connection is genuinely still open: a server that (wrongly) closed it + # after the stall boundary would make this recv() return b"" (EOF) instead of + # timing out. Just counting received bytes above would not catch that - a server + # closing the connection right after --stall-after also sends far less than + # 20000 bytes. + sock.settimeout(1) + with pytest.raises(socket.timeout): + sock.recv(1) sock.close() finally: s.stop() + + +def test_concurrent_downloads_are_all_recorded(station): + """Several lamps hitting the station at once must not lose or corrupt status.json. + + Station.served is a shared counter and Station.recent a shared list, mutated from one + handler thread per connection under ThreadingHTTPServer. Without a lock around the + read-modify-write of `served` and the recent-list mutation + serialize-to-disk step, + concurrent requests can lose increments (two threads read the same old value) or have + one thread's status.json write clobber another's. + """ + make_image(station.image_dir / "ninlight_evk_ota.img", "evk", "1.0.0", b"c" * 5000) + expected_len = 5000 + TRAILER_SIZE + n_workers = 20 + errors = [] + results_lock = threading.Lock() + + def worker(): + try: + c = station.conn() + c.request("GET", "/ninlight_evk_ota.img") + r = c.getresponse() + body = r.read() + if r.status != 200 or len(body) != expected_len: + with results_lock: + errors.append((r.status, len(body))) + except Exception as e: # pragma: no cover - surfaced via the errors list below + with results_lock: + errors.append(e) + + threads = [threading.Thread(target=worker) for _ in range(n_workers)] + for t in threads: + t.start() + for t in threads: + t.join(timeout=10) + + assert not errors, f"some downloads failed or returned the wrong body: {errors}" + + state = station.read_state(lambda s: s["served"] >= n_workers) + assert state["served"] == n_workers + assert len(state["recent"]) == min(n_workers, RECENT_MAX) + for entry in state["recent"]: + assert entry["status"] == "ok" + assert entry["target"] == "evk" + assert entry["bytes"] == expected_len From d41c385375c42827d2b02521efcf3961cabb32bf Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 12:02:32 +0200 Subject: [PATCH 080/150] feat: Konfiguration, systemd-Unit und CLI der Recovery-Station Die Station ist bewusst nicht dauerhaft an - 'ninlight-station on' schaltet Zugangspunkt und Dienst gemeinsam ein, 'off' schaltet wlan0 zurueck ins Heim-WLAN. Der Dienst laeuft als root (Port 80 und die DHCP-Leases fuer 4c) und bekommt dafuer systemd-Einschraenkungen. Co-Authored-By: Claude Sonnet 5 --- tools/station/ninlight-station | 66 ++++++++++++++++++++++++++ tools/station/ninlight-station.service | 27 +++++++++++ tools/station/station.conf.example | 19 ++++++++ 3 files changed, 112 insertions(+) create mode 100755 tools/station/ninlight-station create mode 100644 tools/station/ninlight-station.service create mode 100644 tools/station/station.conf.example diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station new file mode 100755 index 0000000..3ad216c --- /dev/null +++ b/tools/station/ninlight-station @@ -0,0 +1,66 @@ +#!/bin/sh +# Schaltet die ninlight-Recovery-Station an und aus. +# +# Die Station ist absichtlich nicht dauerhaft an: Solange sie laeuft, kann jeder +# in Funkreichweite mit dem Passwort einer Lampe beim naechsten Boot beliebige +# Firmware unterschieben, und der Werksreset per fuenf Stromzyklen funktioniert +# bei keiner Lampe in Reichweite mehr (die Firmware loescht den Zaehler vor +# jedem Recovery-Versuch). +set -eu + +CONF=/etc/ninlight-station.conf +[ -r "$CONF" ] || { echo "Fehler: $CONF fehlt - zuerst tools/station/setup_pi.sh ausfuehren" >&2; exit 1; } +# shellcheck disable=SC1090 +. "$CONF" + +need_profile() { + nmcli -t -f NAME con show | grep -qx "$AP_CONNECTION" || { + echo "Fehler: NetworkManager-Profil '$AP_CONNECTION' fehlt - tools/station/setup_pi.sh ausfuehren" >&2 + exit 1 + } +} + +images() { + find "$IMAGE_DIR" -maxdepth 1 -name 'ninlight_*_ota.img' 2>/dev/null | sort +} + +case "${1:-}" in +on) + need_profile + n=$(images | wc -l | tr -d ' ') + [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2 + sudo nmcli con up "$AP_CONNECTION" >/dev/null + sudo systemctl start ninlight-station + echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)" + echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." + ;; +off) + sudo systemctl stop ninlight-station 2>/dev/null || true + sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true + if nmcli -t -f NAME con show | grep -qx "$HOME_CONNECTION"; then + sudo nmcli con up "$HOME_CONNECTION" >/dev/null && echo "Station aus, wlan0 zurueck im Heim-WLAN ($HOME_CONNECTION)" + else + echo "Station aus. Profil '$HOME_CONNECTION' fehlt, wlan0 bleibt ungenutzt - der Zugang ueber Kabel ist davon nicht betroffen." >&2 + fi + ;; +status) + printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || echo inaktiv)" + printf 'wlan0 : %s\n' "$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2)" + printf 'Adresse: %s\n' "$(ip -4 -br addr show wlan0 2>/dev/null | awk '{print $3}')" + echo "Images :" + images | while read -r f; do + printf ' %-34s %s\n' "$(basename "$f")" \ + "$(python3 - "$f" <<'PY' +import pathlib, sys +t = pathlib.Path(sys.argv[1]).read_bytes()[-512:] +print(t[21:37].rstrip(b"\x00").decode() if t[:4] == b"NINL" else "kein Trailer") +PY +)" + done + [ -r /run/ninlight-station/status.json ] && { echo "Zuletzt ausgeliefert:"; sed 's/^/ /' /run/ninlight-station/status.json; } + ;; +*) + echo "Aufruf: ninlight-station on|off|status" >&2 + exit 2 + ;; +esac diff --git a/tools/station/ninlight-station.service b/tools/station/ninlight-station.service new file mode 100644 index 0000000..274cf95 --- /dev/null +++ b/tools/station/ninlight-station.service @@ -0,0 +1,27 @@ +[Unit] +Description=ninlight recovery station (HTTP image server) +After=network.target +# Ohne den Zugangspunkt hat der Server keine Adresse zum Binden. +BindsTo=NetworkManager.service + +[Service] +Type=simple +EnvironmentFile=/etc/ninlight-station.conf +ExecStart=/usr/bin/python3 /opt/ninlight-station/server.py --image-dir ${IMAGE_DIR} +Restart=on-failure +RestartSec=2 + +# Laeuft als root: Port 80 und - fuer 4c - die DHCP-Leases in +# /var/lib/NetworkManager (Verzeichnis 0700 root:root) sind sonst nicht erreichbar. +# Gegengewicht: +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes +RuntimeDirectory=ninlight-station +ReadWritePaths=/run/ninlight-station +RestrictAddressFamilies=AF_INET AF_UNIX +MemoryMax=128M + +[Install] +WantedBy=multi-user.target diff --git a/tools/station/station.conf.example b/tools/station/station.conf.example new file mode 100644 index 0000000..f98bb73 --- /dev/null +++ b/tools/station/station.conf.example @@ -0,0 +1,19 @@ +# /etc/ninlight-station.conf - von tools/station/setup_pi.sh angelegt. +# Muss zu den Build-Parametern der Firmware passen (NINLIGHT_RECOVERY_SSID, +# NINLIGHT_RECOVERY_PASSWORD in config.local.cmake). Weicht etwas ab, findet +# die Lampe die Station nicht. + +SSID="ninlight-recovery" + +# Oeffentlicher Default. Fuer den echten Einsatz hier UND in config.local.cmake +# einen eigenen Wert setzen - wer das Passwort kennt und in Funkreichweite ist, +# kann jeder Lampe beim naechsten Boot beliebige Firmware unterschieben. +PSK="ninlight-recovery" + +IMAGE_DIR="/opt/ninlight-station/images" + +# NetworkManager-Profil, in das "ninlight-station off" zurueckschaltet. +HOME_CONNECTION="preconfigured" + +# NetworkManager-Profil der Station selbst. +AP_CONNECTION="ninlight-recovery" From 10cc67d490e8d242b2a9b3a2087c854530a97210 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 12:14:49 +0200 Subject: [PATCH 081/150] fix: Fehlerbehandlung und Quoting der Recovery-Station nach Review - 'on' prueft nach dem Start wirklich, ob der Dienst aktiv ist (Type=simple meldet sonst schon Erfolg, wenn der Prozess nur gestartet wurde), und faehrt den Zugangspunkt bei einem toten Server wieder herunter statt ihn offen mit bekanntem Passwort ohne Server dahinter stehen zu lassen. - 'off' faengt einen fehlschlagenden Wechsel zurueck ins Heim-WLAN ab statt unter set -eu mit nmclis Rohausgabe abzubrechen. - Fehlende Schluessel in der Konfiguration werden nach dem Sourcen explizit geprueft und mit einem Verweis auf setup_pi.sh gemeldet statt mit "parameter not set" abzubrechen. - station.conf.example verwendet einfache statt doppelter Anfuehrungszeichen: POSIX ". datei" wertet $ und ` auch in doppelten Anfuehrungszeichen aus, ein Passwort mit "$1" waere sonst lautlos durch ein Shell-Argument ersetzt worden. systemds EnvironmentFile= fuehrt ohnehin nie eine Shell aus und entfernt einfache wie doppelte Anfuehrungszeichen gleichermassen. - ninlight-station.service: BindsTo=NetworkManager.service entfernt (ein dadurch ausgeloester Stop gilt fuer systemd als sauber und wird von keiner Restart=-Politik erneut gestartet - ein NetworkManager-Neustart haette den Server sonst still und dauerhaft mitgerissen); stattdessen nur After=NetworkManager.service fuer die Startreihenfolge und Restart=always, damit der Prozess nach einem Absturz (z.B. Bind-Fehler) automatisch wiederkommt. - Trailer-Reader im 'status'-Zweig nutzt jetzt errors="replace" wie trailer_version() in server.py, statt bei nicht-ASCII-Bytes im Versionsfeld abzustuerzen. Co-Authored-By: Claude Sonnet 5 --- tools/station/ninlight-station | 30 ++++++++++++++++++++++---- tools/station/ninlight-station.service | 13 +++++++---- tools/station/station.conf.example | 17 ++++++++++----- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 3ad216c..dfce1fe 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -13,6 +13,14 @@ CONF=/etc/ninlight-station.conf # shellcheck disable=SC1090 . "$CONF" +for _key in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION; do + eval "test -n \"\${$_key-}\"" || { + echo "Fehler: $CONF: Schluessel $_key fehlt oder ist leer - tools/station/setup_pi.sh ausfuehren" >&2 + exit 1 + } +done +unset _key + need_profile() { nmcli -t -f NAME con show | grep -qx "$AP_CONNECTION" || { echo "Fehler: NetworkManager-Profil '$AP_CONNECTION' fehlt - tools/station/setup_pi.sh ausfuehren" >&2 @@ -31,14 +39,28 @@ on) [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2 sudo nmcli con up "$AP_CONNECTION" >/dev/null sudo systemctl start ninlight-station - echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)" - echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." + # "systemctl start" meldet bei Type=simple schon Erfolg, sobald der Prozess + # gestartet wurde - nicht, sobald er ueberlebt hat. Kurz warten und wirklich + # nachsehen, sonst bleibt ein offener AP ohne Server dahinter stehen. + sleep 1 + if systemctl is-active --quiet ninlight-station; then + echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)" + echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." + else + sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true + echo "Fehler: ninlight-station ist nach dem Start nicht aktiv - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2 + exit 1 + fi ;; off) sudo systemctl stop ninlight-station 2>/dev/null || true sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true if nmcli -t -f NAME con show | grep -qx "$HOME_CONNECTION"; then - sudo nmcli con up "$HOME_CONNECTION" >/dev/null && echo "Station aus, wlan0 zurueck im Heim-WLAN ($HOME_CONNECTION)" + if sudo nmcli con up "$HOME_CONNECTION" >/dev/null; then + echo "Station aus, wlan0 zurueck im Heim-WLAN ($HOME_CONNECTION)" + else + echo "Station aus. Zurueckschalten zu '$HOME_CONNECTION' fehlgeschlagen - wlan0-Status pruefen." >&2 + fi else echo "Station aus. Profil '$HOME_CONNECTION' fehlt, wlan0 bleibt ungenutzt - der Zugang ueber Kabel ist davon nicht betroffen." >&2 fi @@ -53,7 +75,7 @@ status) "$(python3 - "$f" <<'PY' import pathlib, sys t = pathlib.Path(sys.argv[1]).read_bytes()[-512:] -print(t[21:37].rstrip(b"\x00").decode() if t[:4] == b"NINL" else "kein Trailer") +print(t[21:37].rstrip(b"\x00").decode("ascii", "replace") if t[:4] == b"NINL" else "kein Trailer") PY )" done diff --git a/tools/station/ninlight-station.service b/tools/station/ninlight-station.service index 274cf95..eefa362 100644 --- a/tools/station/ninlight-station.service +++ b/tools/station/ninlight-station.service @@ -1,14 +1,19 @@ [Unit] Description=ninlight recovery station (HTTP image server) -After=network.target -# Ohne den Zugangspunkt hat der Server keine Adresse zum Binden. -BindsTo=NetworkManager.service +# Ohne den Zugangspunkt hat der Server beim Start keine Adresse zum Binden. +# Bewusst kein BindsTo=NetworkManager.service: ein dadurch ausgeloester Stop +# gilt fuer systemd als sauber und wuerde von KEINER Restart=-Politik erneut +# gestartet - ein NetworkManager-Neustart wuerde den Server dann still und +# dauerhaft mitreissen. Stattdessen nur Start-Reihenfolge vorgeben und den +# Prozess bei jedem Absturz (z.B. Bind-Fehler nach einem NM-Neustart) per +# Restart=always in kurzen Abstaenden neu versuchen lassen. +After=network.target NetworkManager.service [Service] Type=simple EnvironmentFile=/etc/ninlight-station.conf ExecStart=/usr/bin/python3 /opt/ninlight-station/server.py --image-dir ${IMAGE_DIR} -Restart=on-failure +Restart=always RestartSec=2 # Laeuft als root: Port 80 und - fuer 4c - die DHCP-Leases in diff --git a/tools/station/station.conf.example b/tools/station/station.conf.example index f98bb73..15bcaa8 100644 --- a/tools/station/station.conf.example +++ b/tools/station/station.conf.example @@ -2,18 +2,25 @@ # Muss zu den Build-Parametern der Firmware passen (NINLIGHT_RECOVERY_SSID, # NINLIGHT_RECOVERY_PASSWORD in config.local.cmake). Weicht etwas ab, findet # die Lampe die Station nicht. +# +# Werte immer in einfachen Anführungszeichen: ninlight-station liest diese +# Datei per POSIX ". datei" ein, und dabei wertet die Shell auch in doppelten +# Anführungszeichen $ und ` aus - ein Passwort mit "$1" wuerde so lautlos +# durch ein Shell-Argument ersetzt. Einfache Anführungszeichen unterbinden +# das; systemds EnvironmentFile= entfernt sie ebenso wie doppelte und fuehrt +# ohnehin nie eine Shell aus. -SSID="ninlight-recovery" +SSID='ninlight-recovery' # Oeffentlicher Default. Fuer den echten Einsatz hier UND in config.local.cmake # einen eigenen Wert setzen - wer das Passwort kennt und in Funkreichweite ist, # kann jeder Lampe beim naechsten Boot beliebige Firmware unterschieben. -PSK="ninlight-recovery" +PSK='ninlight-recovery' -IMAGE_DIR="/opt/ninlight-station/images" +IMAGE_DIR='/opt/ninlight-station/images' # NetworkManager-Profil, in das "ninlight-station off" zurueckschaltet. -HOME_CONNECTION="preconfigured" +HOME_CONNECTION='preconfigured' # NetworkManager-Profil der Station selbst. -AP_CONNECTION="ninlight-recovery" +AP_CONNECTION='ninlight-recovery' From cb0a4b035dceba6fb1a35d0d8132476024c43234 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 12:21:26 +0200 Subject: [PATCH 082/150] fix: Startaufruf absichern und Neustartschleife der Station deckeln - 'on' fing bisher nur einen bereits laufenden, aber toten Dienst ab. Scheiterte schon der Aufruf "systemctl start" selbst (z.B. start-limit-hit nach einer Absturzserie durch Restart=always), beendete set -eu das Skript dort, bevor die Lebendigkeitspruefung je erreicht wurde - der eben hochgefahrene Zugangspunkt blieb offen ohne Server dahinter stehen. Der Startaufruf wird jetzt selbst abgefangen und faehrt im Fehlerfall dieselbe Rueckabwicklung wie die is-active-Pruefung. "systemctl reset-failed" vor dem Start verhindert, dass eine fruehere Absturzserie den naechsten ehrlichen Versuch blockiert. - Stirbt der Dienst nach einem erfolgreichen Start, stoppt das Skript ihn jetzt explizit, bevor es den Zugangspunkt herunterfaehrt und "abgeschaltet" meldet - sonst haette ein mit Restart=always weiterlaufender Dienst im Hintergrund einen saubereren Zustand suggeriert, als tatsaechlich vorlag. - ninlight-station.service setzt StartLimitIntervalSec/StartLimitBurst jetzt explizit statt sich stillschweigend auf systemds Vorgabewerte zu verlassen, mit Begruendung als Kommentar: ohne Zugangspunkt kann server.py nicht binden und stirbt sofort, Restart=always soll das nach kurzer Zeit ehrlich aufgeben statt das Journal zu fluten. Co-Authored-By: Claude Sonnet 5 --- tools/station/ninlight-station | 17 ++++++++++++++++- tools/station/ninlight-station.service | 8 ++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index dfce1fe..974ea64 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -38,7 +38,18 @@ on) n=$(images | wc -l | tr -d ' ') [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2 sudo nmcli con up "$AP_CONNECTION" >/dev/null - sudo systemctl start ninlight-station + # Eine fruehere Absturzserie darf den naechsten ehrlichen Versuch nicht + # blockieren (systemd verweigert "start" sonst sofort mit "start-limit-hit"). + sudo systemctl reset-failed ninlight-station 2>/dev/null || true + if ! sudo systemctl start ninlight-station; then + # Der Start-Aufruf selbst ist gescheitert (z.B. start-limit-hit) - unter + # set -eu wuerde das Skript hier sonst abbrechen, bevor die + # Lebendigkeitspruefung unten je erreicht wird, und der eben + # hochgefahrene Zugangspunkt bliebe offen ohne Server dahinter stehen. + sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true + echo "Fehler: ninlight-station liess sich nicht starten - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2 + exit 1 + fi # "systemctl start" meldet bei Type=simple schon Erfolg, sobald der Prozess # gestartet wurde - nicht, sobald er ueberlebt hat. Kurz warten und wirklich # nachsehen, sonst bleibt ein offener AP ohne Server dahinter stehen. @@ -47,6 +58,10 @@ on) echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)" echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." else + # Mit Restart=always laeuft ein abgestuerzter Dienst sonst im Hintergrund + # weiter in seiner Neustartschleife - erst stoppen, dann den AP runter, + # sonst suggeriert die Meldung unten einen saubereren Zustand als er ist. + sudo systemctl stop ninlight-station 2>/dev/null || true sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true echo "Fehler: ninlight-station ist nach dem Start nicht aktiv - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2 exit 1 diff --git a/tools/station/ninlight-station.service b/tools/station/ninlight-station.service index eefa362..04cff98 100644 --- a/tools/station/ninlight-station.service +++ b/tools/station/ninlight-station.service @@ -8,6 +8,14 @@ Description=ninlight recovery station (HTTP image server) # Prozess bei jedem Absturz (z.B. Bind-Fehler nach einem NM-Neustart) per # Restart=always in kurzen Abstaenden neu versuchen lassen. After=network.target NetworkManager.service +# Steht der Zugangspunkt nicht, kann server.py 192.168.8.1 nicht binden und +# stirbt sofort; Restart=always versucht es alle RestartSec=2 erneut. Damit +# das nicht endlos das Journal flutet, sondern nach kurzer Zeit ehrlich +# aufgibt (und ninlight-station dann mit einer klaren Fehlermeldung reagieren +# kann statt in einer stillen Neustartschleife zu haengen), hier explizit +# gedeckelt statt sich auf systemds Vorgabewerte zu verlassen. +StartLimitIntervalSec=30 +StartLimitBurst=5 [Service] Type=simple From 0ba269a39a9ea0ae065c5c57af00253c7882488c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 12:25:02 +0200 Subject: [PATCH 083/150] =?UTF-8?q?feat:=20nl=5Fmkimage=20pr=C3=BCft=20Ima?= =?UTF-8?q?ges=20mit=20--verify?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deploy.sh soll kein Image auf die Station schieben, das jede Lampe ablehnen würde. Die Prüfung sitzt in nl_mkimage, weil dort das Trailer-Layout ohnehin steht - eine zweite Kopie derselben Offsets wäre genau die Doppelung, die beim Abweichen jedes Update killt. Co-Authored-By: Claude Haiku 4.5 --- tools/mkimage/nl_mkimage.py | 45 +++++++++++++++++++++++++++--- tools/mkimage/test_nl_mkimage.py | 47 ++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/tools/mkimage/nl_mkimage.py b/tools/mkimage/nl_mkimage.py index 5d5afc0..5f37eef 100755 --- a/tools/mkimage/nl_mkimage.py +++ b/tools/mkimage/nl_mkimage.py @@ -22,14 +22,51 @@ def field(value: str, name: str) -> bytes: return raw.ljust(FIELD, b"\x00") +def verify(path: Path, expect_device: str | None) -> int: + """Checks an _ota.img the way the firmware does before accepting it.""" + data = path.read_bytes() + if len(data) <= TRAILER_SIZE: + print(f"error: {path} is too short to carry a trailer", file=sys.stderr) + return 1 + payload, trailer = data[:-TRAILER_SIZE], data[-TRAILER_SIZE:] + if trailer[0:4] != b"NINL": + print(f"error: {path} has no ninlight trailer", file=sys.stderr) + return 1 + if trailer[4] != TRAILER_VERSION: + print(f"error: {path} has trailer version {trailer[4]}, expected {TRAILER_VERSION}", + file=sys.stderr) + return 1 + device = trailer[5:21].rstrip(b"\x00").decode("ascii", "replace") + version = trailer[21:37].rstrip(b"\x00").decode("ascii", "replace") + if expect_device is not None and device != expect_device: + print(f"error: {path} carries device '{device}', expected '{expect_device}'", + file=sys.stderr) + return 1 + if trailer[37:69] != hashlib.sha256(payload).digest(): + print(f"error: {path} fails its sha256 - the payload does not match the trailer", + file=sys.stderr) + return 1 + print(f"{device} {version} {len(data)}") + return 0 + + def main() -> None: p = argparse.ArgumentParser(description="append the ninlight OTA trailer") - p.add_argument("--image", required=True, type=Path, help="input _gz.img") - p.add_argument("--device", required=True, help="device id, e.g. e27_rgb") - p.add_argument("--version", required=True, help="firmware version") - p.add_argument("--out", required=True, type=Path, help="output _ota.img") + p.add_argument("--image", type=Path, help="input _gz.img") + p.add_argument("--device", help="device id, e.g. e27_rgb") + p.add_argument("--version", help="firmware version") + p.add_argument("--out", type=Path, help="output _ota.img") + p.add_argument("--verify", type=Path, help="check an existing _ota.img instead of building one") + p.add_argument("--expect-device", help="with --verify: require this device id") a = p.parse_args() + if a.verify is not None: + raise SystemExit(verify(a.verify, a.expect_device)) + + for name in ("image", "device", "version", "out"): + if getattr(a, name) is None: + p.error(f"--{name} is required unless --verify is given") + data = a.image.read_bytes() if len(data) > TRAILER_SIZE and data[-TRAILER_SIZE : -TRAILER_SIZE + 4] == b"NINL": sys.exit("error: image already carries a trailer") diff --git a/tools/mkimage/test_nl_mkimage.py b/tools/mkimage/test_nl_mkimage.py index 9609de1..4e4f0d4 100644 --- a/tools/mkimage/test_nl_mkimage.py +++ b/tools/mkimage/test_nl_mkimage.py @@ -53,3 +53,50 @@ def test_refuses_image_that_already_has_a_trailer(tmp_path): ) assert r.returncode != 0 assert "trailer" in r.stderr + + +def verify(path, *extra): + return subprocess.run( + [sys.executable, str(SCRIPT), "--verify", str(path), *extra], + capture_output=True, text=True, + ) + + +def test_verify_accepts_a_good_image(tmp_path): + _, out = build(tmp_path, device="e27_rgb", version="1.2.3") + r = verify(out) + assert r.returncode == 0, r.stderr + assert r.stdout.split()[:2] == ["e27_rgb", "1.2.3"] + + +def test_verify_rejects_a_file_without_trailer(tmp_path): + p = tmp_path / "plain.img" + p.write_bytes(b"x" * 5000) + r = verify(p) + assert r.returncode == 1 + assert "trailer" in r.stderr.lower() + + +def test_verify_rejects_a_corrupted_payload(tmp_path): + _, out = build(tmp_path) + d = bytearray(out.read_bytes()) + d[100] ^= 0x01 + out.write_bytes(d) + r = verify(out) + assert r.returncode == 1 + assert "sha256" in r.stderr.lower() + + +def test_verify_checks_the_expected_device(tmp_path): + _, out = build(tmp_path, device="evk") + assert verify(out, "--expect-device", "evk").returncode == 0 + r = verify(out, "--expect-device", "g95_cw") + assert r.returncode == 1 + assert "evk" in r.stderr and "g95_cw" in r.stderr + + +def test_verify_rejects_a_file_shorter_than_the_trailer(tmp_path): + p = tmp_path / "tiny.img" + p.write_bytes(b"NINL" + b"\x00" * 10) + r = verify(p) + assert r.returncode == 1 From df8e796f1e761380ee79fc40a6bf5c87b6a7f419 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 12:30:42 +0200 Subject: [PATCH 084/150] fix: nl_mkimage Verifikation robuster gegen Edge Cases und Misuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical 1: path.read_bytes() fiel auf nonexistent file oder Directory mit Traceback statt einer Fehlerzeile. Jetzt OSError-Familie abgefangen. Critical 2: --device/--version mit Leerzeichen wurden stillschweigend akzeptiert; das zerlegt das Ausgabeformat und bricht Task 4s Skript. Jetzt whitespace beim Bauen abgelehnt. Important 1: Gerätefeld muss in C wie in Python null-terminiert sein. Die Firmware checkt das unabhängig von --expect-device; Python nur mit Flag. Jetzt Terminierung immer verlangt. Important 2: Modi waren nicht exklusiv; --verify mit Baumodus-Argumenten wurde stillschweigend ignoriert. Jetzt Fehler statt falschem Vertrauen. Neue Tests für alle vier Szenarien + nonexistent file, directory, empty file, Trailer ohne Payload, Mode-Mischung. Co-Authored-By: Claude Haiku 4.5 --- tools/mkimage/nl_mkimage.py | 26 +++++++- tools/mkimage/test_nl_mkimage.py | 108 +++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/tools/mkimage/nl_mkimage.py b/tools/mkimage/nl_mkimage.py index 5f37eef..2a177ef 100755 --- a/tools/mkimage/nl_mkimage.py +++ b/tools/mkimage/nl_mkimage.py @@ -19,12 +19,18 @@ def field(value: str, name: str) -> bytes: raw = value.encode() if len(raw) >= FIELD: sys.exit(f"error: {name} '{value}' is too long (max {FIELD - 1} bytes)") + if any(c.isspace() for c in value): + sys.exit(f"error: {name} '{value}' contains whitespace") return raw.ljust(FIELD, b"\x00") def verify(path: Path, expect_device: str | None) -> int: """Checks an _ota.img the way the firmware does before accepting it.""" - data = path.read_bytes() + try: + data = path.read_bytes() + except OSError as e: + print(f"error: {e}", file=sys.stderr) + return 1 if len(data) <= TRAILER_SIZE: print(f"error: {path} is too short to carry a trailer", file=sys.stderr) return 1 @@ -36,7 +42,12 @@ def verify(path: Path, expect_device: str | None) -> int: print(f"error: {path} has trailer version {trailer[4]}, expected {TRAILER_VERSION}", file=sys.stderr) return 1 - device = trailer[5:21].rstrip(b"\x00").decode("ascii", "replace") + # Device field must be null-terminated within its 16 bytes (same as firmware check) + device_field = trailer[5:21] + if b"\x00" not in device_field: + print(f"error: {path} has unterminated device field", file=sys.stderr) + return 1 + device = device_field.rstrip(b"\x00").decode("ascii", "replace") version = trailer[21:37].rstrip(b"\x00").decode("ascii", "replace") if expect_device is not None and device != expect_device: print(f"error: {path} carries device '{device}', expected '{expect_device}'", @@ -61,13 +72,22 @@ def main() -> None: a = p.parse_args() if a.verify is not None: + # Verify mode: check that build arguments are not provided + build_args = ("image", "device", "version", "out") + if any(getattr(a, name) is not None for name in build_args): + p.error("--verify cannot be combined with build mode arguments (--image, --device, --version, --out)") raise SystemExit(verify(a.verify, a.expect_device)) + # Build mode: require all arguments for name in ("image", "device", "version", "out"): if getattr(a, name) is None: p.error(f"--{name} is required unless --verify is given") - data = a.image.read_bytes() + try: + data = a.image.read_bytes() + except OSError as e: + sys.exit(f"error: {e}") + if len(data) > TRAILER_SIZE and data[-TRAILER_SIZE : -TRAILER_SIZE + 4] == b"NINL": sys.exit("error: image already carries a trailer") diff --git a/tools/mkimage/test_nl_mkimage.py b/tools/mkimage/test_nl_mkimage.py index 4e4f0d4..f1fa4f1 100644 --- a/tools/mkimage/test_nl_mkimage.py +++ b/tools/mkimage/test_nl_mkimage.py @@ -100,3 +100,111 @@ def test_verify_rejects_a_file_shorter_than_the_trailer(tmp_path): p.write_bytes(b"NINL" + b"\x00" * 10) r = verify(p) assert r.returncode == 1 + + +def test_verify_rejects_nonexistent_file(tmp_path): + p = tmp_path / "does_not_exist.img" + r = verify(p) + assert r.returncode == 1 + assert r.stdout == "" + assert len(r.stderr.splitlines()) == 1 + assert "error:" in r.stderr.lower() + + +def test_verify_rejects_directory(tmp_path): + d = tmp_path / "dir" + d.mkdir() + r = verify(d) + assert r.returncode == 1 + assert r.stdout == "" + assert len(r.stderr.splitlines()) == 1 + assert "error:" in r.stderr.lower() + + +def test_verify_rejects_empty_file(tmp_path): + p = tmp_path / "empty.img" + p.write_bytes(b"") + r = verify(p) + assert r.returncode == 1 + assert "trailer" in r.stderr.lower() + + +def test_verify_rejects_file_with_only_trailer_no_payload(tmp_path): + p = tmp_path / "only_trailer.img" + trailer = bytearray(b"\xff" * 512) + trailer[0:4] = b"NINL" + trailer[4] = 1 + trailer[5:21] = b"test\x00" + b"\x00" * 11 + trailer[21:37] = b"1.0.0\x00" + b"\x00" * 10 + trailer[37:69] = hashlib.sha256(b"").digest() + p.write_bytes(bytes(trailer)) + r = verify(p) + assert r.returncode == 1 + assert "trailer" in r.stderr.lower() + + +def test_build_rejects_device_with_whitespace(tmp_path): + src = tmp_path / "fw_gz.img" + src.write_bytes(b"x") + r = subprocess.run( + [sys.executable, str(SCRIPT), "--image", str(src), "--device", "dev ice", + "--version", "1.0", "--out", str(tmp_path / "o.img")], + capture_output=True, text=True, + ) + assert r.returncode != 0 + assert "whitespace" in r.stderr + + +def test_build_rejects_version_with_whitespace(tmp_path): + src = tmp_path / "fw_gz.img" + src.write_bytes(b"x") + r = subprocess.run( + [sys.executable, str(SCRIPT), "--image", str(src), "--device", "device", + "--version", "1.0 beta", "--out", str(tmp_path / "o.img")], + capture_output=True, text=True, + ) + assert r.returncode != 0 + assert "whitespace" in r.stderr + + +def test_verify_rejects_unterminated_device_field(tmp_path): + p = tmp_path / "bad_device.img" + payload = b"x" * 1000 + trailer = bytearray(b"\xff" * 512) + trailer[0:4] = b"NINL" + trailer[4] = 1 + # Fill entire device field with no null terminator + trailer[5:21] = b"a" * 16 + trailer[21:37] = b"1.0.0\x00" + b"\x00" * 10 + trailer[37:69] = hashlib.sha256(payload).digest() + p.write_bytes(payload + bytes(trailer)) + r = verify(p) + assert r.returncode == 1 + assert "unterminated" in r.stderr.lower() + + +def test_verify_rejects_unterminated_device_field_without_expect_device(tmp_path): + # Same as above but without --expect-device to ensure the check happens regardless + p = tmp_path / "bad_device2.img" + payload = b"x" * 1000 + trailer = bytearray(b"\xff" * 512) + trailer[0:4] = b"NINL" + trailer[4] = 1 + # Fill entire device field with no null terminator + trailer[5:21] = b"b" * 16 + trailer[21:37] = b"2.0.0\x00" + b"\x00" * 10 + trailer[37:69] = hashlib.sha256(payload).digest() + p.write_bytes(payload + bytes(trailer)) + r = verify(p) + assert r.returncode == 1 + assert "unterminated" in r.stderr.lower() + + +def test_verify_with_build_arguments_is_an_error(tmp_path): + _, out = build(tmp_path) + r = subprocess.run( + [sys.executable, str(SCRIPT), "--verify", str(out), "--device", "x"], + capture_output=True, text=True, + ) + assert r.returncode != 0 + assert "cannot be combined" in r.stderr From fda732d6c42101d0516ee9b923cd0c229fe47668 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 12:36:03 +0200 Subject: [PATCH 085/150] feat: Aufsetz- und Ausliefer-Skripte der Recovery-Station setup_pi.sh ist idempotent und laesst eine vorhandene Konfiguration in Ruhe; deploy.sh prueft jedes Image mit nl_mkimage --verify, bevor es etwas uebertraegt. Co-Authored-By: Claude Sonnet 5 --- tools/station/README.md | 42 +++++++++++++++++++++++++ tools/station/deploy.sh | 55 ++++++++++++++++++++++++++++++++ tools/station/setup_pi.sh | 66 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 tools/station/README.md create mode 100755 tools/station/deploy.sh create mode 100755 tools/station/setup_pi.sh diff --git a/tools/station/README.md b/tools/station/README.md new file mode 100644 index 0000000..133cea1 --- /dev/null +++ b/tools/station/README.md @@ -0,0 +1,42 @@ +# ninlight-Recovery-Station + +Ein Raspberry Pi spannt den Zugangspunkt `ninlight-recovery` auf und liefert unter +`http://192.168.8.1/ninlight__ota.img` Firmware-Images aus. + +Jede ninlight-Lampe sucht beim Booten bis zu drei Sekunden nach dieser Station. Findet sie +sie, lädt sie das Image für ihren Gerätetyp und flasht es — ohne dass die Lampe im WLAN +erreichbar sein muss. Das ist der Rettungsweg für Lampen, an die man sonst nur noch mit dem +Lötkolben käme. + +Trägt das angebotene Image dieselbe Version, die bereits läuft, lehnt die Lampe es ab und +bootet normal weiter. Eine angelassene Station flasht also nicht in einer Endlosschleife. + +## Bedienung + + tools/station/setup_pi.sh einmalig einrichten + tools/station/deploy.sh [target] Images ausliefern + ssh ninlight-station on einschalten + ssh ninlight-station status Zustand und letzte Auslieferungen + ssh ninlight-station off ausschalten + +SSID und Passwort stehen in `/etc/ninlight-station.conf` auf dem Pi und müssen zu den +Build-Parametern `NINLIGHT_RECOVERY_SSID` und `NINLIGHT_RECOVERY_PASSWORD` aus +`config.local.cmake` passen. Weicht eines ab, findet die Lampe die Station nicht. + +## Vertrauensgrenze + +Der Trailer eines Images prüft Gerätetyp und SHA256. Er ist **keine** kryptografische +Signatur, und der Download läuft über `http://` ohne TLS. Die Station schützt allein ein +WPA2-Passwort, dessen Default im Repo steht. Wer in Funkreichweite ist und es kennt, kann +jeder Lampe beim nächsten Boot beliebige Firmware unterschieben. + +Das ist gewollt: Die Signaturprüfung der alten Firmware auszuhebeln ist der Zweck dieses +Projekts. Daraus folgen zwei Regeln. + +**Setz ein eigenes Passwort.** In `config.local.cmake` und in +`/etc/ninlight-station.conf` — an beiden Stellen denselben Wert. + +**Lass die Station nur an, solange du sie brauchst.** Nicht nur wegen des Fremdzugriffs: +Die Firmware löscht den Power-Cycle-Zähler, bevor sie einen Recovery-Versuch startet. +Solange eine Station in Reichweite ist, funktioniert der Werksreset per fünf Stromzyklen +bei **keiner** Lampe in Reichweite mehr. diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh new file mode 100755 index 0000000..fdbc7cd --- /dev/null +++ b/tools/station/deploy.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# Liefert Firmware-Images und den Stationscode an den Pi aus. +# +# Aufruf: tools/station/deploy.sh [target ...] +# Ohne target-Argumente werden alle in build/*/ gefundenen _ota.img uebertragen. +set -euo pipefail +HOST=${1:?Aufruf: tools/station/deploy.sh [target ...]} +shift || true +HERE=$(cd "$(dirname "$0")" && pwd) +ROOT=$(cd "$HERE/../.." && pwd) +MKIMAGE="$ROOT/tools/mkimage/nl_mkimage.py" + +declare -a FILES=() +if [ $# -gt 0 ]; then + for t in "$@"; do + f="$ROOT/build/$t/ninlight_${t}_ota.img" + [ -f "$f" ] || { echo "Fehler: $f fehlt - erst bauen" >&2; exit 1; } + FILES+=("$f") + done +else + while IFS= read -r f; do FILES+=("$f"); done < <(find "$ROOT/build" -maxdepth 2 -name 'ninlight_*_ota.img' | sort) + [ ${#FILES[@]} -gt 0 ] || { echo "Fehler: keine Images unter $ROOT/build gefunden" >&2; exit 1; } +fi + +echo "== Images pruefen ==" +STAGE=$(mktemp -d) +trap 'rm -rf "$STAGE"' EXIT +printf '%-26s %-10s %s\n' GERAET VERSION BYTES +for f in "${FILES[@]}"; do + base=$(basename "$f") + target=${base#ninlight_}; target=${target%_ota.img} + # Ein Image, das die Pruefung nicht besteht, wuerde jede Lampe ablehnen - + # das faellt sonst erst am Geraet auf. + out=$(python3 "$MKIMAGE" --verify "$f" --expect-device "$target") || { + echo "Fehler: $base besteht die Trailer-Pruefung nicht, nichts uebertragen" >&2; exit 1; } + # shellcheck disable=SC2086 + set -- $out + printf '%-26s %-10s %s\n' "$1" "$2" "$3" + cp "$f" "$STAGE/$base" +done + +echo "== uebertragen ==" +rsync -a --delete "$STAGE/" "$HOST:/tmp/ninlight-images/" +rsync -a "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" +ssh "$HOST" 'set -e + sudo install -d -m 0755 /opt/ninlight-station/images + sudo rsync -a --delete /tmp/ninlight-images/ /opt/ninlight-station/images/ + sudo install -m 0755 /tmp/server.py /opt/ninlight-station/server.py + sudo install -m 0755 /tmp/ninlight-station /usr/local/bin/ninlight-station + sudo install -m 0644 /tmp/ninlight-station.service /etc/systemd/system/ninlight-station.service + rm -rf /tmp/ninlight-images /tmp/server.py /tmp/ninlight-station /tmp/ninlight-station.service + sudo systemctl daemon-reload + systemctl is-active --quiet ninlight-station && { sudo systemctl restart ninlight-station; echo " Dienst neu gestartet"; } || echo " Dienst laeuft nicht (Station ist aus)"' + +echo "fertig." diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh new file mode 100755 index 0000000..c659b2f --- /dev/null +++ b/tools/station/setup_pi.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Richtet einen Raspberry Pi als ninlight-Recovery-Station ein. Idempotent: +# mehrfaches Ausfuehren aendert nichts und schadet nicht. +# +# Aufruf: tools/station/setup_pi.sh +set -euo pipefail +HOST=${1:?Aufruf: tools/station/setup_pi.sh } +HERE=$(cd "$(dirname "$0")" && pwd) + +echo "== Voraussetzungen auf $HOST pruefen ==" +ssh "$HOST" 'set -e + systemctl is-active --quiet NetworkManager || { echo "NetworkManager laeuft nicht - dieser Plan setzt ihn voraus" >&2; exit 1; } + nmcli -t -f DEVICE,STATE device | grep -q "^wlan0:" || { echo "wlan0 wird nicht von NetworkManager verwaltet" >&2; exit 1; } + python3 -c "import sys; sys.exit(0 if sys.version_info >= (3,11) else 1)" || { echo "Python 3.11+ noetig" >&2; exit 1; } + echo " ok"' + +echo "== Konfiguration ==" +scp -q "$HERE/station.conf.example" "$HOST:/tmp/ninlight-station.conf.example" +ssh "$HOST" 'set -e + if [ -f /etc/ninlight-station.conf ]; then + echo " /etc/ninlight-station.conf existiert bereits, bleibt unveraendert" + else + sudo install -m 0600 /tmp/ninlight-station.conf.example /etc/ninlight-station.conf + echo " /etc/ninlight-station.conf aus der Vorlage angelegt" + fi + rm -f /tmp/ninlight-station.conf.example' + +echo "== Server, CLI und Unit installieren ==" +scp -q "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" +ssh "$HOST" 'set -e + sudo install -d -m 0755 /opt/ninlight-station /opt/ninlight-station/images + sudo install -m 0755 /tmp/server.py /opt/ninlight-station/server.py + sudo install -m 0755 /tmp/ninlight-station /usr/local/bin/ninlight-station + sudo install -m 0644 /tmp/ninlight-station.service /etc/systemd/system/ninlight-station.service + rm -f /tmp/server.py /tmp/ninlight-station /tmp/ninlight-station.service + sudo systemctl daemon-reload + echo " installiert"' + +echo "== NetworkManager-Profil ==" +ssh "$HOST" 'set -e + . /etc/ninlight-station.conf + if nmcli -t -f NAME con show | grep -qx "$AP_CONNECTION"; then + sudo nmcli con mod "$AP_CONNECTION" \ + 802-11-wireless.ssid "$SSID" wifi-sec.psk "$PSK" \ + ipv4.addresses 192.168.8.1/24 ipv4.method shared \ + connection.autoconnect no + echo " Profil $AP_CONNECTION aktualisiert" + else + sudo nmcli con add type wifi ifname wlan0 con-name "$AP_CONNECTION" ssid "$SSID" \ + 802-11-wireless.mode ap 802-11-wireless.band bg 802-11-wireless.channel 6 \ + wifi-sec.key-mgmt wpa-psk wifi-sec.proto rsn wifi-sec.pairwise ccmp wifi-sec.group ccmp \ + wifi-sec.psk "$PSK" \ + ipv4.method shared ipv4.addresses 192.168.8.1/24 ipv6.method disabled \ + connection.autoconnect no >/dev/null + echo " Profil $AP_CONNECTION angelegt" + fi' + +cat <<'EOF' + +Fertig. Die Station ist absichtlich noch AUS. + +Naechste Schritte: + tools/station/deploy.sh Images ausliefern + ssh ninlight-station on Station einschalten + ssh ninlight-station off wieder ausschalten (nicht vergessen) +EOF From d23b0d4e02c097b1747e003332fd93621f5cfa4d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 12:52:44 +0200 Subject: [PATCH 086/150] fix: Rechte-Bug und Passwort-Leck im NM-Profil, Loesch-Bug in deploy.sh setup_pi.sh sourcte /etc/ninlight-station.conf (0600 root:root) als unprivilegierter Benutzer und scheiterte damit bei jedem Lauf, bevor das NetworkManager-Profil angelegt wurde. Die Konfiguration wird jetzt per "sudo cat" gelesen; ninlight-station macht denselben Fallback fuer den dokumentierten Aufruf als normaler Benutzer, ohne die Datei fuer alle lesbar zu machen. Das AP-Profil wird nicht mehr per "nmcli ... wifi-sec.psk $PSK" angelegt (das WLAN-Passwort waere sonst fuer jeden lokalen Prozess in der Kommandozeile sichtbar), sondern als NetworkManager-Keyfile (0600 root:root) geschrieben und per "nmcli connection load" eingelesen. deploy.sh loeschte bei einem gezielten Deploy eines einzelnen Geraetetyps per "rsync --delete" alle Images anderer Geraetetypen aus dem live ausgelieferten Verzeichnis. --delete laeuft jetzt nur noch beim Vollabgleich (kein target-Argument); ein gezielter Deploy ersetzt nur die genannten Dateien. Dabei zusaetzlich -c (Checksumme statt Groesse+Zeitstempel) ergaenzt, sonst uebersieht rsync ein neues Image mit zufaellig gleicher Byte-Groesse. Ausserdem meldet deploy.sh jetzt einen fehlgeschlagenen Dienst-Neustart als Fehler statt ihn mit "Dienst laeuft nicht" zu verwechseln. Stub-Tests unter tools/station/tests/test_deploy.sh (ssh/rsync im PATH ersetzt, Installationsschritt laeuft gegen ein Sandbox-Verzeichnis) decken beide Bugfixes sowie die bereits bestehenden Faelle ab: Image besteht die Trailer-Pruefung nicht, keine Images gefunden. Co-Authored-By: Claude Sonnet 5 --- tools/station/deploy.sh | 61 +++++-- tools/station/ninlight-station | 16 +- tools/station/setup_pi.sh | 71 ++++++-- tools/station/tests/test_deploy.sh | 259 +++++++++++++++++++++++++++++ 4 files changed, 377 insertions(+), 30 deletions(-) create mode 100755 tools/station/tests/test_deploy.sh diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index fdbc7cd..6bc791c 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -12,12 +12,17 @@ MKIMAGE="$ROOT/tools/mkimage/nl_mkimage.py" declare -a FILES=() if [ $# -gt 0 ]; then + # Gezielter Deploy: nur die genannten Geraetetypen werden ersetzt, alle + # anderen Images im Zielverzeichnis bleiben unangetastet (siehe FULL_SYNC + # unten). + FULL_SYNC=0 for t in "$@"; do f="$ROOT/build/$t/ninlight_${t}_ota.img" [ -f "$f" ] || { echo "Fehler: $f fehlt - erst bauen" >&2; exit 1; } FILES+=("$f") done else + FULL_SYNC=1 while IFS= read -r f; do FILES+=("$f"); done < <(find "$ROOT/build" -maxdepth 2 -name 'ninlight_*_ota.img' | sort) [ ${#FILES[@]} -gt 0 ] || { echo "Fehler: keine Images unter $ROOT/build gefunden" >&2; exit 1; } fi @@ -40,16 +45,52 @@ for f in "${FILES[@]}"; do done echo "== uebertragen ==" -rsync -a --delete "$STAGE/" "$HOST:/tmp/ninlight-images/" +# -c (Inhaltsvergleich per Checksumme statt Groesse+Zeitstempel): ein neues +# Image kann zufaellig dieselbe Byte-Groesse wie das alte haben, und bei +# schnell aufeinanderfolgenden Deploys reicht rsyncs Sekundenaufloesung fuer +# den Zeitstempel nicht, um das als Aenderung zu erkennen. Ohne -c wuerde +# rsync ein solches Update dann stillschweigend ueberspringen. +# +# /tmp/ninlight-images auf dem Pi ist nur eine fluechtige Ablage fuer diesen +# Lauf - hier darf --delete unbedingt ran, das betrifft nicht das live +# ausgelieferte Verzeichnis. +rsync -a -c --delete "$STAGE/" "$HOST:/tmp/ninlight-images/" rsync -a "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" -ssh "$HOST" 'set -e - sudo install -d -m 0755 /opt/ninlight-station/images - sudo rsync -a --delete /tmp/ninlight-images/ /opt/ninlight-station/images/ - sudo install -m 0755 /tmp/server.py /opt/ninlight-station/server.py - sudo install -m 0755 /tmp/ninlight-station /usr/local/bin/ninlight-station - sudo install -m 0644 /tmp/ninlight-station.service /etc/systemd/system/ninlight-station.service - rm -rf /tmp/ninlight-images /tmp/server.py /tmp/ninlight-station /tmp/ninlight-station.service - sudo systemctl daemon-reload - systemctl is-active --quiet ninlight-station && { sudo systemctl restart ninlight-station; echo " Dienst neu gestartet"; } || echo " Dienst laeuft nicht (Station ist aus)"' +# Pfade als Variablen mit Default: so kann ein Test die Installation gegen +# ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen lassen. +ssh "$HOST" bash -s -- "$FULL_SYNC" <<'REMOTE' +set -e +FULL_SYNC=$1 +ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} +BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} +UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} + +sudo install -d -m 0755 "$ROOT_DIR/images" +if [ "$FULL_SYNC" -eq 1 ]; then + # Vollabgleich: alle Images ausserhalb der aktuellen Auslieferung entfernen. + echo " voller Abgleich - Images ausserhalb der Auswahl werden entfernt" + sudo rsync -a -c --delete /tmp/ninlight-images/ "$ROOT_DIR/images/" +else + # Gezielter Deploy: nur die uebertragenen Dateien ersetzen/ergaenzen, alle + # anderen Geraetetypen im Zielverzeichnis bleiben erhalten (kein --delete). + echo " gezielter Deploy - andere Geraetetypen im Zielverzeichnis bleiben erhalten" + sudo rsync -a -c /tmp/ninlight-images/ "$ROOT_DIR/images/" +fi +sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" +sudo install -m 0755 /tmp/ninlight-station "$BIN_PATH" +sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH" +rm -rf /tmp/ninlight-images /tmp/server.py /tmp/ninlight-station /tmp/ninlight-station.service +sudo systemctl daemon-reload +if systemctl is-active --quiet ninlight-station; then + if sudo systemctl restart ninlight-station; then + echo " Dienst neu gestartet" + else + echo " Fehler: Dienst lief, Neustart nach dem Deploy ist aber fehlgeschlagen" >&2 + exit 1 + fi +else + echo " Dienst laeuft nicht (Station ist aus)" +fi +REMOTE echo "fertig." diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 974ea64..ac212b0 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -9,9 +9,19 @@ set -eu CONF=/etc/ninlight-station.conf -[ -r "$CONF" ] || { echo "Fehler: $CONF fehlt - zuerst tools/station/setup_pi.sh ausfuehren" >&2; exit 1; } -# shellcheck disable=SC1090 -. "$CONF" +if [ -r "$CONF" ]; then + # shellcheck disable=SC1090 + . "$CONF" +elif [ -e "$CONF" ]; then + # $CONF ist 0600 root:root, damit das Passwort nicht fuer jeden lesbar + # ist. Der dokumentierte Aufruf "ssh ninlight-station on" laeuft + # aber als normaler Benutzer - "sudo cat" liest die Datei ein, ohne ihre + # Rechte aufzuweichen. + eval "$(sudo cat "$CONF")" +else + echo "Fehler: $CONF fehlt - zuerst tools/station/setup_pi.sh ausfuehren" >&2 + exit 1 +fi for _key in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION; do eval "test -n \"\${$_key-}\"" || { diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index c659b2f..0ebcfff 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -37,23 +37,60 @@ ssh "$HOST" 'set -e echo " installiert"' echo "== NetworkManager-Profil ==" -ssh "$HOST" 'set -e - . /etc/ninlight-station.conf - if nmcli -t -f NAME con show | grep -qx "$AP_CONNECTION"; then - sudo nmcli con mod "$AP_CONNECTION" \ - 802-11-wireless.ssid "$SSID" wifi-sec.psk "$PSK" \ - ipv4.addresses 192.168.8.1/24 ipv4.method shared \ - connection.autoconnect no - echo " Profil $AP_CONNECTION aktualisiert" - else - sudo nmcli con add type wifi ifname wlan0 con-name "$AP_CONNECTION" ssid "$SSID" \ - 802-11-wireless.mode ap 802-11-wireless.band bg 802-11-wireless.channel 6 \ - wifi-sec.key-mgmt wpa-psk wifi-sec.proto rsn wifi-sec.pairwise ccmp wifi-sec.group ccmp \ - wifi-sec.psk "$PSK" \ - ipv4.method shared ipv4.addresses 192.168.8.1/24 ipv6.method disabled \ - connection.autoconnect no >/dev/null - echo " Profil $AP_CONNECTION angelegt" - fi' +# Zwei Dinge, die man an einer echten Lampenstation nicht falsch machen darf: +# - /etc/ninlight-station.conf ist 0600 root:root (das Passwort soll nicht +# jeder lesen koennen). Der SSH-Benutzer ist aber nicht root, direktes +# ". /etc/ninlight-station.conf" wuerde also mit "Permission denied" +# abbrechen. "sudo cat" + eval liest sie dennoch ein, ohne die Rechte der +# Datei aufzuweichen. +# - "nmcli ... wifi-sec.psk $PSK" wuerde das Passwort als Kommandozeilen- +# argument an einen Prozess uebergeben - fuer jeden lokalen Benutzer per +# "ps"/"/proc//cmdline" sichtbar. Deshalb wird das Verbindungsprofil +# stattdessen direkt als NetworkManager-Keyfile (0600 root:root) +# geschrieben; das Passwort landet nur in einer Root-Datei, nie in einer +# Prozessliste. +ssh "$HOST" bash -s <<'REMOTE' +set -e +eval "$(sudo cat /etc/ninlight-station.conf)" +CONN_FILE="/etc/NetworkManager/system-connections/${AP_CONNECTION}.nmconnection" +if sudo test -f "$CONN_FILE"; then + UUID=$(sudo sed -n 's/^uuid=//p' "$CONN_FILE" | head -n1) + ACTION="aktualisiert" +else + UUID=$(cat /proc/sys/kernel/random/uuid) + ACTION="angelegt" +fi +TMP=$(mktemp) +trap 'rm -f "$TMP"' EXIT +{ + printf '%s\n' '[connection]' + printf 'id=%s\n' "$AP_CONNECTION" + printf 'uuid=%s\n' "$UUID" + printf '%s\n' 'type=wifi' + printf '%s\n' 'interface-name=wlan0' + printf '%s\n' 'autoconnect=false' + printf '\n%s\n' '[wifi]' + printf '%s\n' 'mode=ap' + printf 'ssid=%s\n' "$SSID" + printf '%s\n' 'band=bg' + printf '%s\n' 'channel=6' + printf '\n%s\n' '[wifi-security]' + printf '%s\n' 'key-mgmt=wpa-psk' + printf '%s\n' 'proto=rsn' + printf '%s\n' 'pairwise=ccmp;' + printf '%s\n' 'group=ccmp;' + printf 'psk=%s\n' "$PSK" + printf '\n%s\n' '[ipv4]' + printf '%s\n' 'method=shared' + printf '%s\n' 'address1=192.168.8.1/24' + printf '\n%s\n' '[ipv6]' + printf '%s\n' 'method=disabled' +} >"$TMP" +sudo install -o root -g root -m 0600 "$TMP" "$CONN_FILE" +rm -f "$TMP" +sudo nmcli connection load "$CONN_FILE" >/dev/null +echo " Profil $AP_CONNECTION $ACTION" +REMOTE cat <<'EOF' diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh new file mode 100755 index 0000000..504fc84 --- /dev/null +++ b/tools/station/tests/test_deploy.sh @@ -0,0 +1,259 @@ +#!/usr/bin/env bash +# Stub-Tests fuer deploy.sh - ohne Pi ausfuehrbar. +# +# ssh/rsync werden im PATH durch Stubs ersetzt: ssh fuehrt das per Heredoc +# uebergebene Remote-Skript lokal aus (statt es an einen echten Pi zu +# schicken), rsync uebersetzt "host:pfad"-Ziele auf den blossen lokalen Pfad +# und ruft dann den echten rsync auf. So laeuft die komplette Logik von +# deploy.sh - inklusive des Installationsschritts, der normalerweise per SSH +# auf dem Pi passiert - tatsaechlich, aber vollstaendig gegen ein Sandbox- +# Verzeichnis (NINLIGHT_STATION_ROOT/…) statt gegen echte Systempfade. +# +# Aufruf: tools/station/tests/test_deploy.sh +set -euo pipefail + +HERE=$(cd "$(dirname "$0")" && pwd) +STATION_DIR=$(cd "$HERE/.." && pwd) +REAL_RSYNC=$(command -v rsync) + +TMPROOT=$(mktemp -d) +trap 'rm -rf "$TMPROOT"' EXIT + +FAILED=0 +pass() { echo "ok - $1"; } +fail() { echo "FEHLER - $1" >&2; FAILED=1; } + +# --- Sandbox-Repo aufbauen: tools/station/deploy.sh + tools/mkimage/nl_mkimage.py + build/ --- +setup_sandbox() { + local sandbox=$1 + rm -rf "$sandbox" + mkdir -p "$sandbox/tools/station" "$sandbox/tools/mkimage" "$sandbox/build" + cp "$STATION_DIR/deploy.sh" "$sandbox/tools/station/deploy.sh" + chmod +x "$sandbox/tools/station/deploy.sh" + # Inhalt irrelevant, die Dateien muessen nur existieren (werden "uebertragen"). + echo "# dummy" >"$sandbox/tools/station/server.py" + echo "# dummy" >"$sandbox/tools/station/ninlight-station" + echo "# dummy" >"$sandbox/tools/station/ninlight-station.service" + + # Fake nl_mkimage.py --verify: verhaelt sich wie das echte Tool (eine Zeile + # " " auf stdout bei Erfolg, Exit 0; eine Zeile auf + # stderr bei Fehlschlag, Exit 1), aber steuerbar per Umgebungsvariable + # MKIMAGE_FAIL_SUBSTR: enthaelt der zu pruefende Dateiname diesen Teilstring, + # schlaegt die Pruefung fehl. + cat >"$sandbox/tools/mkimage/nl_mkimage.py" <<'PY' +import os +import sys + +args = sys.argv[1:] +f = args[args.index("--verify") + 1] +target = args[args.index("--expect-device") + 1] +fail_substr = os.environ.get("MKIMAGE_FAIL_SUBSTR", "") +if fail_substr and fail_substr in f: + print(f"Fehler: {f} besteht die Trailer-Pruefung nicht (Test-Stub)", file=sys.stderr) + sys.exit(1) +size = os.path.getsize(f) +print(f"{target} 1.0.0 {size}") +PY +} + +# --- Stub-Kommandos fuer ssh/rsync im PATH --- +setup_stubbin() { + local bindir=$1 log=$2 + mkdir -p "$bindir" + + cat >"$bindir/ssh" <>"$log" +host=\$1; shift +"\$@" +EOF + + cat >"$bindir/rsync" <>"$log" +args=() +for a in "\$@"; do + case "\$a" in + -*) args+=("\$a") ;; + *:*) args+=("\${a#*:}") ;; + *) args+=("\$a") ;; + esac +done +exec "$REAL_RSYNC" "\${args[@]}" +EOF + + cat >"$bindir/scp" <>"$log" +exit 1 +EOF + + cat >"$bindir/sudo" <>"$log" +exec "\$@" +EOF + + cat >"$bindir/systemctl" <>"$log" +case "\$1" in + is-active) exit "\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-1}" ;; + restart) exit "\${FAKE_SYSTEMCTL_RESTART_EXIT:-0}" ;; + *) exit 0 ;; +esac +EOF + + chmod +x "$bindir"/ssh "$bindir"/rsync "$bindir"/scp "$bindir"/sudo "$bindir"/systemctl +} + +run_deploy() { + # Fuehrt deploy.sh mit stubbtem PATH aus; Ausgabe landet in $OUT, Exit-Code in $?. + local sandbox=$1; shift + # Auf dem echten Pi existieren /usr/local/bin und /etc/systemd/system schon; + # in der Sandbox muessen sie fuer "install" eigens angelegt werden. + mkdir -p "$SANDBOX_STATE/usr/local/bin" "$SANDBOX_STATE/etc/systemd/system" + PATH="$STUBBIN:$PATH" \ + NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ + NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ + NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ + "$sandbox/tools/station/deploy.sh" "$@" +} + +echo "=== Szenario 1: Image besteht die Trailer-Pruefung nicht -> nichts uebertragen ===" +SANDBOX="$TMPROOT/s1"; STUBBIN="$TMPROOT/bin1"; LOG="$TMPROOT/s1.log"; SANDBOX_STATE="$TMPROOT/s1-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "dummy" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(MKIMAGE_FAIL_SUBSTR=foo run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0 ($RC)"; else fail "Exit-Code war 0"; fi +if echo "$OUT" | grep -q "besteht die Trailer-Pruefung nicht"; then + pass "Fehlermeldung zur Trailer-Pruefung vorhanden" +else + fail "Fehlermeldung zur Trailer-Pruefung fehlt" +fi +if ! grep -q "STUB ssh\|STUB rsync\|STUB scp" "$LOG"; then + pass "kein ssh/rsync/scp aufgerufen - nichts wurde uebertragen" +else + fail "ssh/rsync/scp wurde trotz fehlgeschlagener Pruefung aufgerufen: $(cat "$LOG")" +fi + +echo +echo "=== Szenario 2: keine Images unter build/ gefunden ===" +SANDBOX="$TMPROOT/s2"; STUBBIN="$TMPROOT/bin2"; LOG="$TMPROOT/s2.log"; SANDBOX_STATE="$TMPROOT/s2-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0 ($RC)"; else fail "Exit-Code war 0"; fi +if echo "$OUT" | grep -q "keine Images unter"; then + pass "Fehlermeldung zu fehlenden Images vorhanden" +else + fail "Fehlermeldung zu fehlenden Images fehlt" +fi +if ! grep -q "STUB" "$LOG" 2>/dev/null; then + pass "kein Transfer-Kommando aufgerufen" +else + fail "ein Transfer-Kommando wurde trotz fehlender Images aufgerufen: $(cat "$LOG")" +fi + +echo +echo "=== Szenario 3: gezielter Deploy eines Geraetetyps laesst andere Images auf dem Ziel unangetastet (Critical 2) ===" +SANDBOX="$TMPROOT/s3"; STUBBIN="$TMPROOT/bin3"; LOG="$TMPROOT/s3.log"; SANDBOX_STATE="$TMPROOT/s3-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build/bar" +echo "foo-image-neu" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +echo "bar-image" >"$SANDBOX/build/bar/ninlight_bar_ota.img" +# Zielverzeichnis simuliert einen vorherigen Voll-Deploy: Images fuer foo UND bar liegen schon da. +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images" +echo "foo-image-alt" >"$SANDBOX_STATE/opt/ninlight-station/images/ninlight_foo_ota.img" +echo "bar-image" >"$SANDBOX_STATE/opt/ninlight-station/images/ninlight_bar_ota.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if echo "$OUT" | grep -q "andere Geraetetypen im Zielverzeichnis bleiben erhalten"; then + pass "Ausgabe kuendigt gezielten Deploy ohne --delete an" +else + fail "Ausgabe erwaehnt den gezielten Deploy nicht" +fi +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_bar_ota.img" ]; then + pass "Image des NICHT ausgewaehlten Geraetetyps (bar) blieb erhalten" +else + fail "Image des nicht ausgewaehlten Geraetetyps (bar) wurde geloescht - Critical 2 nicht behoben" +fi +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_foo_ota.img" ] \ + && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_foo_ota.img")" = "foo-image-neu" ]; then + pass "Image des ausgewaehlten Geraetetyps (foo) wurde aktualisiert" +else + fail "Image des ausgewaehlten Geraetetyps (foo) wurde nicht aktualisiert" +fi + +echo +echo "=== Szenario 4: Voll-Deploy (kein target) entfernt veraltete Images - zur Abgrenzung von Szenario 3 ===" +SANDBOX="$TMPROOT/s4"; STUBBIN="$TMPROOT/bin4"; LOG="$TMPROOT/s4.log"; SANDBOX_STATE="$TMPROOT/s4-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image-neu" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images" +echo "veraltet" >"$SANDBOX_STATE/opt/ninlight-station/images/ninlight_veraltet_ota.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if [ ! -f "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_veraltet_ota.img" ]; then + pass "veraltetes Image wurde beim Voll-Deploy entfernt (--delete wirkt weiterhin, wenn gewollt)" +else + fail "veraltetes Image wurde beim Voll-Deploy NICHT entfernt" +fi + +echo +echo "=== Szenario 5: Dienst laeuft, Neustart nach dem Deploy schlaegt fehl -> Exit != 0 (Minor) ===" +SANDBOX="$TMPROOT/s5"; STUBBIN="$TMPROOT/bin5"; LOG="$TMPROOT/s5.log"; SANDBOX_STATE="$TMPROOT/s5-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" "$SANDBOX_STATE/usr/local/bin" "$SANDBOX_STATE/etc/systemd/system" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(FAKE_SYSTEMCTL_ACTIVE_EXIT=0 FAKE_SYSTEMCTL_RESTART_EXIT=1 \ + PATH="$STUBBIN:$PATH" \ + NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ + NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ + NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ + "$SANDBOX/tools/station/deploy.sh" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0, wenn der Neustart fehlschlaegt ($RC)"; else fail "Exit-Code war 0, obwohl der Neustart fehlschlug"; fi +if echo "$OUT" | grep -q "Neustart nach dem Deploy ist aber fehlgeschlagen"; then + pass "Fehlermeldung unterscheidet Neustart-Fehlschlag von 'Dienst laeuft nicht'" +else + fail "Fehlermeldung fehlt oder ist mehrdeutig" +fi +if echo "$OUT" | grep -q "Dienst laeuft nicht"; then + fail "faelschlich 'Dienst laeuft nicht' gemeldet, obwohl der Dienst lief" +else + pass "'Dienst laeuft nicht' wird nicht faelschlich gemeldet" +fi + +echo +if [ "$FAILED" -eq 0 ]; then + echo "Alle Tests bestanden." +else + echo "Mindestens ein Test ist fehlgeschlagen." >&2 +fi +exit "$FAILED" From 4a8a5d658fb223e49fdaed3b52e0d3a8c772fc51 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 13:12:54 +0200 Subject: [PATCH 087/150] fix: nl_mkimage wieder Python-3.8-tauglich machen CMake ruft nl_mkimage.py bei jedem Firmware-Build ueber das plain "python3" im Docker-Build-Container auf (Python 3.8.10), unabhaengig von den Werkzeugen auf Mac/Pi, die Python >= 3.11 voraussetzen. Die zuletzt hinzugekommene Annotation "expect_device: str | None" wird dort zur Laufzeit ausgewertet und wirft "TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'", was jeden Firmware-Build abbrechen liess. "from __future__ import annotations" (wie in tools/station/server.py) sorgt dafuer, dass Annotationen nur noch als Text existieren und nie ausgewertet werden - der Rest der Datei kam ohne weitere 3.8-Stolpersteine aus. test: zwei Regressionstests ergaenzt, die ohne installiertes Python 3.8 auskommen: einer prueft per AST, dass der future-Import direkt nach dem Docstring steht, der andere laesst vermin (dev-Dependency) die Mindestversion aus Syntax/Stdlib-Nutzung ableiten. vermins "union-types"-Feature wird bewusst nicht aktiviert, weil es "str | None" unabhaengig vom future-Import als 3.10-Anforderung meldet - das wuerde ausgerechnet den korrekten Fix durchfallen lassen. Co-Authored-By: Claude Sonnet 5 --- tools/mkimage/nl_mkimage.py | 12 ++++++ tools/mkimage/pyproject.toml | 2 +- tools/mkimage/test_nl_mkimage.py | 68 ++++++++++++++++++++++++++++++++ tools/mkimage/uv.lock | 15 ++++++- 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/tools/mkimage/nl_mkimage.py b/tools/mkimage/nl_mkimage.py index 2a177ef..9a7583b 100755 --- a/tools/mkimage/nl_mkimage.py +++ b/tools/mkimage/nl_mkimage.py @@ -4,7 +4,19 @@ Layout (512 bytes): "NINL" | version(1) | device[16] | fw_version[16] | sha256[32] | 0xFF padding. The hash covers the complete `_gz.img` (T_BOOTER header + payload), which is exactly what the firmware hashes while writing the image to flash. + +Must run under Python 3.8.10: CMake invokes this script via plain `python3` inside the +x86_64 build container (see docker/Dockerfile, cmake/run_mkimage.cmake) on every firmware +build, and that container has not been upgraded past 3.8. This is independent of the +"Python >= 3.11" requirement elsewhere in this repo, which applies only to the tooling that +runs on the Mac and the Pi (tools/station, tools/mkimage's own pytest suite), never to this +file. Keep `from __future__ import annotations` below so that PEP 604 unions (`str | None`) +in annotations stay text and are never evaluated - without it, that syntax raises +`TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'` on 3.8/3.9. See +test_nl_mkimage.py::test_stays_python38_compatible, which fails the build if this drifts. """ +from __future__ import annotations + import argparse import hashlib import sys diff --git a/tools/mkimage/pyproject.toml b/tools/mkimage/pyproject.toml index aeee3bd..4c3155f 100644 --- a/tools/mkimage/pyproject.toml +++ b/tools/mkimage/pyproject.toml @@ -4,4 +4,4 @@ version = "0.1.0" requires-python = ">=3.11" [dependency-groups] -dev = ["pytest>=8"] +dev = ["pytest>=8", "vermin>=1.6"] diff --git a/tools/mkimage/test_nl_mkimage.py b/tools/mkimage/test_nl_mkimage.py index f1fa4f1..0d51b8a 100644 --- a/tools/mkimage/test_nl_mkimage.py +++ b/tools/mkimage/test_nl_mkimage.py @@ -208,3 +208,71 @@ def test_verify_with_build_arguments_is_an_error(tmp_path): ) assert r.returncode != 0 assert "cannot be combined" in r.stderr + + +# --- Python 3.8 compatibility ------------------------------------------------------------- +# +# CMake calls this script via plain `python3` inside the build container on every firmware +# build (see docker/Dockerfile, cmake/run_mkimage.cmake), and that container is stuck on +# Python 3.8.10. This is the opposite constraint from the rest of the repo's tooling (which +# needs Python >= 3.11) - see the module docstring in nl_mkimage.py for the full story, and +# git history around the "unsupported operand type(s) for |" break for what happens when it +# regresses. +# +# Two checks, because neither alone covers the failure mode we actually hit: +# 1. `from __future__ import annotations` must be the first statement after the module +# docstring. As long as it's there, PEP 604 unions in annotations (`str | None`) are +# never evaluated at runtime, so they can't raise on 3.8/3.9 no matter how many more of +# them get added later. +# 2. vermin's *default* analysis (annotations not evaluated - matching point 1) catches +# everything else 3.9+/3.10+/3.11+ that could sneak in: walrus in odd positions, match +# statements, dict `|=`, new stdlib members, etc. We deliberately do NOT enable vermin's +# `union-types` feature: it flags `str | None` as needing 3.10 unconditionally, without +# accounting for `from __future__ import annotations` deferring evaluation - which would +# make this test fail on the very fix that makes the file 3.8-safe. Check 1 above is +# what actually guards against that pattern. + + +def test_future_annotations_import_is_present(): + """Guards the PEP 604 union pattern that broke the Docker build (see module docstring).""" + import ast + + tree = ast.parse(SCRIPT.read_text()) + body = tree.body + # Skip a leading module docstring, if any. + if body and isinstance(body[0], ast.Expr) and isinstance(body[0].value, ast.Constant) \ + and isinstance(body[0].value.value, str): + body = body[1:] + assert body, "nl_mkimage.py is unexpectedly empty" + first = body[0] + assert isinstance(first, ast.ImportFrom) and first.module == "__future__" \ + and any(alias.name == "annotations" for alias in first.names), ( + "nl_mkimage.py must start with `from __future__ import annotations` right after its " + "module docstring - without it, PEP 604 unions like `str | None` in annotations are " + "evaluated eagerly and crash under the Python 3.8.10 in the Docker build container " + "(TypeError: unsupported operand type(s) for |: 'type' and 'NoneType')" + ) + + +def test_stays_python38_compatible(): + """Static check that nl_mkimage.py needs nothing newer than Python 3.8 to run. + + Runs without a Python 3.8 interpreter installed: vermin infers the minimum required + version from syntax/stdlib usage instead of actually executing the file. + """ + vermin = __import__("vermin") + + config = vermin.Config() + config.set_quiet(True) + # Match nl_mkimage.py's own strategy: with `from __future__ import annotations` present + # (checked separately above), annotations are never evaluated, so vermin shouldn't assume + # they need runtime support either. This is vermin's default, spelled out here so it's + # obvious why `union-types` (see the block comment above) is intentionally not enabled. + config.set_eval_annotations(False) + + source = SCRIPT.read_text() + py2_min, py3_min = vermin.detect(source, config, str(SCRIPT)) + assert py3_min is not None and py3_min <= (3, 8), ( + f"nl_mkimage.py now needs Python {py3_min} - it must keep working on the " + "Python 3.8.10 in the Docker build container (see module docstring)" + ) diff --git a/tools/mkimage/uv.lock b/tools/mkimage/uv.lock index 7030ede..8d0546a 100644 --- a/tools/mkimage/uv.lock +++ b/tools/mkimage/uv.lock @@ -28,12 +28,16 @@ source = { virtual = "." } [package.dev-dependencies] dev = [ { name = "pytest" }, + { name = "vermin" }, ] [package.metadata] [package.metadata.requires-dev] -dev = [{ name = "pytest", specifier = ">=8" }] +dev = [ + { name = "pytest", specifier = ">=8" }, + { name = "vermin", specifier = ">=1.6" }, +] [[package]] name = "packaging" @@ -77,3 +81,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa wheels = [ { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] + +[[package]] +name = "vermin" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/7b/2c1b403f2a844e1acb36694fc336e323df742f7f752edb4188311ad43f9e/vermin-1.8.0.tar.gz", hash = "sha256:3621955ac2a2950175c5b4a9b2fc3bd24bd416da0388893c9eb6971264e4ca1f", size = 99913, upload-time = "2025-11-20T17:29:31.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/fd/6d2167767488e162efd01300a679c7f7d913dbb65baa0b15760ebb369854/vermin-1.8.0-py3-none-any.whl", hash = "sha256:6f2b98ad697a16ed3442e81a0f0baabfc38c185b78aaab1b1fecd87bdd6640b8", size = 98030, upload-time = "2025-11-20T17:29:29.931Z" }, +] From 30321782479b91d604d227800ee5086c66dadc4a Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 13:13:03 +0200 Subject: [PATCH 088/150] fix: ninlight-station status gibt Dienstzustand nur einmal aus, Exit 0 "systemctl is-active" schreibt den Zustand ("inactive", ...) auf stdout und liefert dabei einen Rueckgabewert ungleich 0 - der "|| echo inaktiv"- Zweig lief also zusaetzlich mit, und beide Zeilen landeten in der Ausgabe. Ausserdem endete "status" ueber den letzten Befehl im case-Zweig faktisch mit dessen Exit-Code, der 1 war, sobald noch nie ausgeliefert wurde (kein /run/ninlight-station/status.json) - das machte "status" in jedem Skript, das den Exit-Code abfragt, unbrauchbar. Beide Stellen behoben: der Dienstzustand wird nur noch einmal ausgegeben, und der Zweig endet mit explizitem "exit 0", sobald die Auskunft erteilt werden konnte. Co-Authored-By: Claude Sonnet 5 --- tools/station/ninlight-station | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index ac212b0..5354271 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -91,7 +91,13 @@ off) fi ;; status) - printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || echo inaktiv)" + # "systemctl is-active" schreibt den Zustand ("inactive", "failed", ...) auf stdout und + # liefert dabei einen Rueckgabewert ungleich 0, sobald der Dienst nicht "active" ist - + # ein "|| echo inaktiv" wuerde also zusaetzlich zur ersten Zeile ausgeloest und beide + # landen in der Ausgabe. Der Zustand selbst ist die Auskunft; "status" ist damit + # erfolgreich, sobald es ihn ermitteln konnte, unabhaengig vom Dienstzustand - deshalb + # am Ende explizit exit 0. + printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || true)" printf 'wlan0 : %s\n' "$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2)" printf 'Adresse: %s\n' "$(ip -4 -br addr show wlan0 2>/dev/null | awk '{print $3}')" echo "Images :" @@ -104,7 +110,11 @@ print(t[21:37].rstrip(b"\x00").decode("ascii", "replace") if t[:4] == b"NINL" el PY )" done + # "status" soll mit 0 enden, sobald es seine Auskunft geben konnte, auch wenn noch nie + # ausgeliefert wurde - ohne das "|| true" waere der Exit-Code des Skripts hier der des + # fehlgeschlagenen Tests (1), weil das die letzte Anweisung im Zweig ist. [ -r /run/ninlight-station/status.json ] && { echo "Zuletzt ausgeliefert:"; sed 's/^/ /' /run/ninlight-station/status.json; } + exit 0 ;; *) echo "Aufruf: ninlight-station on|off|status" >&2 From 456058956c9416916487f31573b0b15ddd6724b5 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 13:13:15 +0200 Subject: [PATCH 089/150] fix: setup_pi.sh und deploy.sh nutzen /usr/bin/ssh und /usr/bin/scp Auf dem Rechner des Nutzers liegt im PATH ein ssh, das sich anders verhaelt als das System-ssh - die Skripte sollen sich darauf nicht verlassen. setup_pi.sh und deploy.sh riefen ssh, scp und rsync bisher ohne vollen Pfad auf. Beide Skripte nutzen jetzt SSH_BIN/SCP_BIN mit Default /usr/bin/ssh bzw. /usr/bin/scp, ueberschreibbar per NINLIGHT_SSH_BIN/NINLIGHT_SCP_BIN - dasselbe Prinzip wie NINLIGHT_STATION_ROOT weiter unten in deploy.sh, das Tests ohne echten Pi ermoeglicht. rsync bekommt "-e $SSH_BIN", damit auch dessen Transport ueber /usr/bin/ssh laeuft. Die Beispielaufrufe in setup_pi.sh's Abschlussmeldung und in der README bleiben bewusst bei schlichtem "ssh" - dort geht es um das, was der Nutzer selbst eintippt. test: tools/station/tests/test_deploy.sh auf den neuen Override umgestellt (NINLIGHT_SSH_BIN zeigt auf den ssh-Stub) und um ein Szenario ergaenzt, das den /usr/bin/ssh- bzw. /usr/bin/scp-Default in beiden Skripten absichert. Co-Authored-By: Claude Sonnet 5 --- tools/station/deploy.sh | 11 ++++++++--- tools/station/setup_pi.sh | 17 +++++++++++------ tools/station/tests/test_deploy.sh | 25 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index 6bc791c..b0ab712 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -9,6 +9,10 @@ shift || true HERE=$(cd "$(dirname "$0")" && pwd) ROOT=$(cd "$HERE/../.." && pwd) MKIMAGE="$ROOT/tools/mkimage/nl_mkimage.py" +# /usr/bin/ssh statt einem ssh im PATH, das sich anders verhaelt (siehe A3-Notiz im +# Abnahmebericht). Ueberschreibbar, damit tests/test_deploy.sh gegen einen Stub statt +# einen echten Pi laufen kann - dasselbe Prinzip wie bei NINLIGHT_STATION_ROOT unten. +SSH_BIN=${NINLIGHT_SSH_BIN:-/usr/bin/ssh} declare -a FILES=() if [ $# -gt 0 ]; then @@ -54,11 +58,12 @@ echo "== uebertragen ==" # /tmp/ninlight-images auf dem Pi ist nur eine fluechtige Ablage fuer diesen # Lauf - hier darf --delete unbedingt ran, das betrifft nicht das live # ausgelieferte Verzeichnis. -rsync -a -c --delete "$STAGE/" "$HOST:/tmp/ninlight-images/" -rsync -a "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" +# -e: rsync soll fuer den Transport zum Pi denselben ssh wie oben benutzen. +rsync -a -c --delete -e "$SSH_BIN" "$STAGE/" "$HOST:/tmp/ninlight-images/" +rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" # Pfade als Variablen mit Default: so kann ein Test die Installation gegen # ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen lassen. -ssh "$HOST" bash -s -- "$FULL_SYNC" <<'REMOTE' +"$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" <<'REMOTE' set -e FULL_SYNC=$1 ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index 0ebcfff..bdf72d9 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -6,17 +6,22 @@ set -euo pipefail HOST=${1:?Aufruf: tools/station/setup_pi.sh } HERE=$(cd "$(dirname "$0")" && pwd) +# /usr/bin/ssh und /usr/bin/scp statt Varianten im PATH, die sich anders verhalten koennen. +# Ueberschreibbar (wie NINLIGHT_STATION_ROOT in deploy.sh), damit ein Test gegen Stubs statt +# einen echten Pi laufen kann. +SSH_BIN=${NINLIGHT_SSH_BIN:-/usr/bin/ssh} +SCP_BIN=${NINLIGHT_SCP_BIN:-/usr/bin/scp} echo "== Voraussetzungen auf $HOST pruefen ==" -ssh "$HOST" 'set -e +"$SSH_BIN" "$HOST" 'set -e systemctl is-active --quiet NetworkManager || { echo "NetworkManager laeuft nicht - dieser Plan setzt ihn voraus" >&2; exit 1; } nmcli -t -f DEVICE,STATE device | grep -q "^wlan0:" || { echo "wlan0 wird nicht von NetworkManager verwaltet" >&2; exit 1; } python3 -c "import sys; sys.exit(0 if sys.version_info >= (3,11) else 1)" || { echo "Python 3.11+ noetig" >&2; exit 1; } echo " ok"' echo "== Konfiguration ==" -scp -q "$HERE/station.conf.example" "$HOST:/tmp/ninlight-station.conf.example" -ssh "$HOST" 'set -e +"$SCP_BIN" -q "$HERE/station.conf.example" "$HOST:/tmp/ninlight-station.conf.example" +"$SSH_BIN" "$HOST" 'set -e if [ -f /etc/ninlight-station.conf ]; then echo " /etc/ninlight-station.conf existiert bereits, bleibt unveraendert" else @@ -26,8 +31,8 @@ ssh "$HOST" 'set -e rm -f /tmp/ninlight-station.conf.example' echo "== Server, CLI und Unit installieren ==" -scp -q "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" -ssh "$HOST" 'set -e +"$SCP_BIN" -q "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" +"$SSH_BIN" "$HOST" 'set -e sudo install -d -m 0755 /opt/ninlight-station /opt/ninlight-station/images sudo install -m 0755 /tmp/server.py /opt/ninlight-station/server.py sudo install -m 0755 /tmp/ninlight-station /usr/local/bin/ninlight-station @@ -49,7 +54,7 @@ echo "== NetworkManager-Profil ==" # stattdessen direkt als NetworkManager-Keyfile (0600 root:root) # geschrieben; das Passwort landet nur in einer Root-Datei, nie in einer # Prozessliste. -ssh "$HOST" bash -s <<'REMOTE' +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' set -e eval "$(sudo cat /etc/ninlight-station.conf)" CONN_FILE="/etc/NetworkManager/system-connections/${AP_CONNECTION}.nmconnection" diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index 504fc84..5b63823 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -114,6 +114,7 @@ run_deploy() { # in der Sandbox muessen sie fuer "install" eigens angelegt werden. mkdir -p "$SANDBOX_STATE/usr/local/bin" "$SANDBOX_STATE/etc/systemd/system" PATH="$STUBBIN:$PATH" \ + NINLIGHT_SSH_BIN="$STUBBIN/ssh" \ NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ @@ -232,6 +233,7 @@ echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" set +e OUT=$(FAKE_SYSTEMCTL_ACTIVE_EXIT=0 FAKE_SYSTEMCTL_RESTART_EXIT=1 \ PATH="$STUBBIN:$PATH" \ + NINLIGHT_SSH_BIN="$STUBBIN/ssh" \ NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ @@ -250,6 +252,29 @@ else pass "'Dienst laeuft nicht' wird nicht faelschlich gemeldet" fi +echo +echo "=== Szenario 6: deploy.sh/setup_pi.sh nutzen ohne Override /usr/bin/ssh (bzw. scp) ===" +# Die anderen Szenarien laufen mit NINLIGHT_SSH_BIN auf einen Stub umgebogen - das prueft die +# Logik, aber nicht mehr, WELCHEN ssh das Skript ohne Override tatsaechlich waehlt. Der Nutzer +# verlangt ausdruecklich /usr/bin/ssh und /usr/bin/scp statt einer Variante im PATH; das hier +# ist die Absicherung dagegen, dass der Default beim naechsten Umbau stillschweigend zurueck +# auf ein PATH-"ssh" rutscht. +if grep -q 'SSH_BIN=\${NINLIGHT_SSH_BIN:-/usr/bin/ssh}' "$STATION_DIR/deploy.sh"; then + pass "deploy.sh: Default fuer SSH_BIN ist /usr/bin/ssh" +else + fail "deploy.sh: Default fuer SSH_BIN ist nicht mehr /usr/bin/ssh" +fi +if grep -q 'SSH_BIN=\${NINLIGHT_SSH_BIN:-/usr/bin/ssh}' "$STATION_DIR/setup_pi.sh"; then + pass "setup_pi.sh: Default fuer SSH_BIN ist /usr/bin/ssh" +else + fail "setup_pi.sh: Default fuer SSH_BIN ist nicht mehr /usr/bin/ssh" +fi +if grep -q 'SCP_BIN=\${NINLIGHT_SCP_BIN:-/usr/bin/scp}' "$STATION_DIR/setup_pi.sh"; then + pass "setup_pi.sh: Default fuer SCP_BIN ist /usr/bin/scp" +else + fail "setup_pi.sh: Default fuer SCP_BIN ist nicht mehr /usr/bin/scp" +fi + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From cb54dac066238253f972f5f6e107c28239030a3d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 13:17:49 +0200 Subject: [PATCH 090/150] build: uv.lock der Station mit einchecken tools/mkimage/uv.lock ist getrackt, tools/station/uv.lock war es nicht - ohne die Datei sind die Testlaeufe der Station nicht reproduzierbar. Co-Authored-By: Claude Opus 5 --- tools/station/uv.lock | 79 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 tools/station/uv.lock diff --git a/tools/station/uv.lock b/tools/station/uv.lock new file mode 100644 index 0000000..9bc3b4e --- /dev/null +++ b/tools/station/uv.lock @@ -0,0 +1,79 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "ninlight-station" +version = "0.1.0" +source = { virtual = "." } + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [{ name = "pytest", specifier = ">=8" }] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] From 62903a7f053758329ede7b73e41dca13407bdd54 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 00:26:05 +0200 Subject: [PATCH 091/150] fix: status.json fuer normale Nutzer lesbar machen, Lesefehler nicht mehr verschlucken server.py legt status.json ueber mkstemp() (Modus 0600) + os.replace() an; da os.replace() den Modus der temporaeren Datei uebernimmt, war die Datei nach jedem Schreiben nur fuer root lesbar - der Dienst laeuft als root, das CLI liest aber als normaler Benutzer. os.chmod(tmp, 0o644) vor dem Rename behebt das umask-fest, auch bei wiederholten Schreibvorgaengen. ninlight-station status hat den fehlenden Lesezugriff bisher stillschweigend wie "noch nie ausgeliefert" behandelt (beides fiel durch denselben "-r"-Test). Jetzt wird zwischen "Datei fehlt" (unauffaellig) und "Datei da, aber nicht lesbar" (Fehlermeldung auf stderr) unterschieden. Neuer Test prueft, dass status.json nach dem ersten und nach einem weiteren Schreibvorgang world-readable bleibt, auch unter restriktivem umask. Co-Authored-By: Claude Sonnet 5 --- tools/station/ninlight-station | 14 ++++++++++- tools/station/server.py | 22 ++++++++++++++--- tools/station/tests/test_server.py | 39 ++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 5354271..6c540fc 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -113,7 +113,19 @@ PY # "status" soll mit 0 enden, sobald es seine Auskunft geben konnte, auch wenn noch nie # ausgeliefert wurde - ohne das "|| true" waere der Exit-Code des Skripts hier der des # fehlgeschlagenen Tests (1), weil das die letzte Anweisung im Zweig ist. - [ -r /run/ninlight-station/status.json ] && { echo "Zuletzt ausgeliefert:"; sed 's/^/ /' /run/ninlight-station/status.json; } + # + # "existiert nicht" (Station lief noch nie) und "existiert, ist aber nicht lesbar" + # sind zwei verschiedene Zustaende und duerfen nicht beide stillschweigend im + # "-r"-Test verschwinden: sonst faellt ein Rechte-Fehler auf status.json (siehe + # server.py, schreibt als root) nie auf, weil die Ausgabe genauso aussieht wie + # "noch nie ausgeliefert". + STATUS_FILE=/run/ninlight-station/status.json + if [ -r "$STATUS_FILE" ]; then + echo "Zuletzt ausgeliefert:" + sed 's/^/ /' "$STATUS_FILE" + elif [ -e "$STATUS_FILE" ]; then + echo "Fehler: $STATUS_FILE existiert, ist aber nicht lesbar (Rechte pruefen)" >&2 + fi exit 0 ;; *) diff --git a/tools/station/server.py b/tools/station/server.py index 59d05c9..5bfaacc 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -10,6 +10,7 @@ Standard library only - the Pi gets no extra packages. from __future__ import annotations import argparse +import contextlib import json import os import re @@ -85,9 +86,24 @@ class Station: try: self.state_path.parent.mkdir(parents=True, exist_ok=True) fd, tmp = tempfile.mkstemp(dir=self.state_path.parent, prefix=".status-") - with os.fdopen(fd, "w") as f: - json.dump(payload, f, indent=2) - os.replace(tmp, self.state_path) + try: + with os.fdopen(fd, "w") as f: + json.dump(payload, f, indent=2) + # The service runs as root and mkstemp() creates the file 0600, so + # without this an operator reading status.json as a normal user (see + # tools/station/ninlight-station) gets "permission denied" - and since + # os.replace() preserves the mode it arrives with, that wrong mode + # would otherwise survive onto the live file on every single write, + # not just the first. os.chmod() ignores the process umask (unlike the + # mode argument to mkstemp/open), so a restrictive umask on the Pi + # can't quietly bring the 0600 back. Set it before the rename so + # there's never a window where the path holds a 0600 file. + os.chmod(tmp, 0o644) + os.replace(tmp, self.state_path) + except BaseException: + with contextlib.suppress(OSError): + os.unlink(tmp) + raise except OSError as e: print(f"warning: cannot write {self.state_path}: {e}", flush=True) diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index a027273..2b6d80e 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -1,7 +1,9 @@ import hashlib import http.client import json +import os import socket +import stat import subprocess import sys import threading @@ -173,6 +175,43 @@ def test_state_file_records_a_missing_image(station): assert state["recent"][-1]["target"] == "g95_cw" +def test_state_file_is_world_readable_even_under_a_restrictive_umask(tmp_path): + """status.json is written by a root service but read by a normal-user CLI. + + tempfile.mkstemp() creates its file 0600 and os.replace() preserves whatever mode + the temp file had, so the server must explicitly widen the mode before the rename - + and must do so with chmod() rather than relying on the mode passed to mkstemp/open, + because that one is still narrowed by the process umask. A restrictive umask (as + root services on the Pi may well run under) must not bring the 0600 back. + """ + old_umask = os.umask(0o077) + try: + s = Station(tmp_path) + finally: + os.umask(old_umask) + try: + make_image(s.image_dir / "ninlight_evk_ota.img", "evk", "1.0.0", b"w" * 100) + + def world_readable() -> bool: + mode = stat.S_IMODE(s.state.stat().st_mode) + return bool(mode & stat.S_IROTH) + + # Written once already at startup (Station.__init__ -> write_state()). + s.read_state(lambda _: world_readable()) + assert world_readable(), "status.json is not world-readable after the initial write" + + # A second write (a real delivery) must not lose the mode either - e.g. if the + # widening were only applied to the file's very first creation instead of to + # every temp file, mode 0600 would silently come back on every later write. + c = s.conn() + c.request("GET", "/ninlight_evk_ota.img") + c.getresponse().read() + s.read_state(lambda st: st["served"] >= 1) + assert world_readable(), "status.json lost its mode on a later overwrite" + finally: + s.stop() + + def test_recent_is_capped_at_eight(station): make_image(station.image_dir / "ninlight_evk_ota.img", "evk", "1.0.0", b"q" * 10) for _ in range(12): From 908fda824096caa0189edc7316a1eba2f84e609e Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 00:29:12 +0200 Subject: [PATCH 092/150] fix: OTA-Log im Recovery-Skip-Pfad nicht mehr als Fehlschlag ausgeben MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auf dem Überspring-Pfad (Recovery lehnt ein Image ab, weil es bereits die laufende Version trägt) bleibt s_error absichtlich NULL, landete aber trotzdem in printf("ota: failed (rc=%d, %s)\r\n", rc, s_error) - ein Nullzeiger an %s ist undefiniertes Verhalten und wurde bislang nur zufällig von newlib als "(null)" abgefangen. Zusätzlich widersprach die Meldung "failed" der direkt folgenden Zeile "recovery: image already up to date, continuing normal boot". ota_task() unterscheidet jetzt explizit zwischen dem Skip-Fall (eigene, ehrliche "ota: skipped"-Zeile) und dem echten Fehlschlag (bestehende "ota: failed"-Zeile, weiterhin mit dem "download"-Fallback für ein leeres s_error). rc bleibt reiner Diagnosewert im Log, da das SDK den Transfer in beiden Fällen abbricht, sobald der Trailer-Hook das Image ablehnt. Restliche Leser von s_error/nl_ota_last_error() (nl_recovery.c, nl_api.c) waren bereits NULL-sicher und mussten nicht angepasst werden. Co-Authored-By: Claude Sonnet 5 --- src/sys/nl_ota.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sys/nl_ota.c b/src/sys/nl_ota.c index 6935946..39b4851 100644 --- a/src/sys/nl_ota.c +++ b/src/sys/nl_ota.c @@ -94,10 +94,19 @@ static void ota_task(void *arg) (void)arg; log_url(s_url); rc = t_http_fwup(s_url); - /* Only reached on failure - the SDK resets the chip itself after a good image. */ - if (s_error == NULL && !s_skipped) - s_error = "download"; - printf("ota: failed (rc=%d, %s)\r\n", rc, s_error); + /* Only reached on failure or a deliberate skip - the SDK resets the chip itself after a good + * image. rc is not a reliable success/failure signal here: the SDK aborts the transfer as + * soon as the trailer hook returns false, which happens both for a real rejection and for the + * deliberate "recovery image already up to date" skip. s_error/s_skipped are what actually + * distinguish the two, so branch on those instead of folding everything into one printf that + * would risk passing a NULL s_error to %s. */ + if (s_skipped) { + printf("ota: skipped (rc=%d, image already up to date)\r\n", rc); + } else { + if (s_error == NULL) + s_error = "download"; + printf("ota: failed (rc=%d, %s)\r\n", rc, s_error); + } { nl_settings_t *s; nl_app_lock(); From 2f257142488564d7e8e47597b8d2369fb7be2477 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 00:32:22 +0200 Subject: [PATCH 093/150] docs: Abnahme der Pi-Station am EVK festhalten Co-Authored-By: Claude Opus 5 --- docs/hardware-checklist.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index c076b7a..88fd497 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -73,3 +73,19 @@ unbeeinträchtigt weiter und meldete korrekt `last_ota_error=crc`. Bei den Läuf Gerätetyp und bei den erfolgreichen Updates trat die Meldung nicht auf. Ursache noch nicht geklärt — in einem Build ohne Assertions wäre das ein doppeltes Freigeben eines Netzwerkpuffers. Vor dem Einsatz auf echten Lampen reproduzieren und aufklären. + +## Recovery gegen die installierte Pi-Station (2026-09-13) + +- [x] Station installiert per `tools/station/setup_pi.sh`, idempotent (zweiter Lauf ließ die Konfiguration in Ruhe) — 2026-09-13 +- [x] `ninlight-station on` schaltet Zugangspunkt und Dienst gemeinsam, `status` meldet Dienst, Adresse, Image und Version, Exit 0 — 2026-09-13 +- [x] Recovery zieht das Image und flasht: `recovery image version 0.3.7 (running 0.3.6)` → `trailer ok` → Neustart mit der neuen Version — 2026-09-13 +- [x] Station angelassen, erneut gebootet → `already carries the running version 0.3.7, not flashing`, `ota: skipped`, kein Werksreset (beide Boots melden `boot #1`), `last_ota_error` bleibt leer — 2026-09-13 +- [x] Zustandsdatei der Station protokolliert beide Auslieferungen mit `status: ok`, Gerätetyp und Version — 2026-09-13 +- [ ] Fehlendes Image für den Gerätetyp → Station antwortet `404`, Eintrag `notfound`, Lampe bootet normal weiter +- [ ] Stillstand: Server hält die Verbindung, sendet nichts mehr → nach Ablauf der Zeitobergrenze startet der Chip neu +- [ ] Echter Rettungsfall: Lampe ohne gültige WLAN-Konfiguration (Build `evk-onb`) findet die Station und wird geflasht +- [ ] `ninlight-station off`, Pi neu starten → Pi kommt im Heim-WLAN hoch, kein Zugangspunkt, Dienst inaktiv + +**Bekannte Eigenheit:** Die Lampe lädt bei jedem Boot das komplette Image (~256 KB), auch wenn sie es danach +wegen gleicher Version verwirft — die Version steht im Trailer am Dateiende. Kein Fehler, aber die Station +sieht pro Boot einen vollen Transfer. From 3f27ef4b268c802d8dc7f3865e3aeec9303d454a Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 00:40:55 +0200 Subject: [PATCH 094/150] =?UTF-8?q?fix:=20--stall-after=20Budget=20der=20R?= =?UTF-8?q?ettungsstation=20client-kumulativ=20z=C3=A4hlen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Testschalter zählte bisher pro Anfrage, sodass eine Lampe nach ihrem Empfangs-Timeout einfach per Range neu verband und ein frisches Budget bekam – am EVK reichten fünf Reconnects, um trotz --stall-after das komplette Image zu übertragen. Station verfolgt die ausgelieferten Bytes jetzt pro Client-IP über alle Anfragen hinweg (Station.reserve_stall_bytes, unter dem bestehenden Lock), sodass kein erneutes Verbinden das Budget auffüllt. Co-Authored-By: Claude Sonnet 5 --- tools/station/server.py | 65 +++++++++++++++++++----------- tools/station/tests/test_server.py | 56 +++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 23 deletions(-) diff --git a/tools/station/server.py b/tools/station/server.py index 5bfaacc..849b0a9 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -61,16 +61,35 @@ class Station: self.since = now_iso() self.served = 0 self.recent: list[dict] = [] + # Cumulative bytes already delivered to each client while --stall-after is active, + # keyed by client IP. A lamp whose download stalls re-opens a fresh connection and + # resumes with Range right where it left off, so a per-request counter would hand + # every reconnect a brand-new --stall-after allowance and the stall would never + # actually stick (see reserve_stall_bytes below). + self.stall_sent: dict[str, int] = {} # ThreadingHTTPServer runs one handler thread per connection, and several lamps - # can be downloading at once. Every mutation of served/recent and the snapshot - # that gets serialized to disk must happen under this same lock, or concurrent - # requests lose increments and stomp on each other's status.json writes. + # can be downloading at once. Every mutation of served/recent/stall_sent and the + # snapshot that gets serialized to disk must happen under this same lock, or + # concurrent requests lose increments and stomp on each other's status.json writes. self.lock = threading.RLock() self.write_state() def image_path(self, target: str) -> Path: return self.image_dir / f"ninlight_{target}_ota.img" + def reserve_stall_bytes(self, ip: str, n: int) -> int: + """How many of the next `n` bytes this client may still receive under --stall-after. + + Tracks bytes cumulatively per client IP, across requests, so a stalled connection + that the lamp abandons and resumes via Range does not get a fresh budget just + because it is a new TCP connection. Only called when stall_after is set. + """ + with self.lock: + sent_so_far = self.stall_sent.get(ip, 0) + allowed = max(0, min(n, self.stall_after - sent_so_far)) + self.stall_sent[ip] = sent_so_far + allowed + return allowed + def record(self, ip: str, target: str, version: str, nbytes: int, status: str) -> None: with self.lock: if status == "ok": @@ -175,31 +194,31 @@ class Handler(BaseHTTPRequestHandler): sent = 0 status = "ok" remaining = size - start + client_ip = self.client_address[0] try: with path.open("rb") as f: f.seek(start) while sent < remaining: - if self.station.stall_after is not None and sent >= self.station.stall_after: - # test aid: hold the connection open and send nothing more, so the - # firmware's stall handling can be exercised on real hardware. We - # still notice if the peer gives up and closes its side, so a test - # (or an impatient client) that hangs up is recorded as "aborted" - # instead of leaking the thread forever. - while True: - readable, _, _ = select.select([self.connection], [], [], 1.0) - if readable: - try: - peek = self.connection.recv(1, socket.MSG_PEEK) - except OSError: - peek = b"" - if not peek: - raise ConnectionResetError("peer closed during stall") - read_size = CHUNK + read_size = min(CHUNK, remaining - sent) if self.station.stall_after is not None: - # Don't overshoot the stall boundary within a single read: without - # this a whole CHUNK could be sent before the check above ever - # notices we've passed --stall-after. - read_size = min(read_size, max(1, self.station.stall_after - sent)) + # Reserve against this client's cumulative budget, not just this + # request's byte count - see Station.reserve_stall_bytes. + read_size = self.station.reserve_stall_bytes(client_ip, read_size) + if read_size == 0: + # test aid: hold the connection open and send nothing more, so + # the firmware's stall handling can be exercised on real + # hardware. We still notice if the peer gives up and closes its + # side, so a test (or an impatient client) that hangs up is + # recorded as "aborted" instead of leaking the thread forever. + while True: + readable, _, _ = select.select([self.connection], [], [], 1.0) + if readable: + try: + peek = self.connection.recv(1, socket.MSG_PEEK) + except OSError: + peek = b"" + if not peek: + raise ConnectionResetError("peer closed during stall") chunk = f.read(read_size) if not chunk: break diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index 2b6d80e..3196c60 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -278,6 +278,62 @@ def test_stall_after_stops_sending_but_keeps_the_connection(tmp_path): s.stop() +def test_stall_after_budget_survives_a_resumed_request(tmp_path): + """--stall-after must stop a client for good, not just for one request. + + On real hardware, a lamp whose download stalls hits its own receive timeout, gives up + on the connection, and opens a new one with a Range header picking up right where it + left off - exactly what test_stall_after_stops_sending_but_keeps_the_connection above + exercises for a single request. A per-request byte counter would hand that resumed + request a brand-new --stall-after allowance, so the "stall" would only ever delay + delivery instead of actually stopping it - which is what was observed on the EVK: the + server stalled at 65536 bytes each time, but the lamp reconnected five times and walked + away with the complete image. This test reproduces that reconnect and checks that the + second, resumed request gets no further bytes at all. + """ + s = Station(tmp_path, "--stall-after", "1000") + try: + make_image(s.image_dir / "ninlight_evk_ota.img", "evk", "1.0.0", b"y" * 200000) + + # First request: burns through the whole --stall-after budget, then the server + # holds the connection open and sends nothing further. + sock = socket.create_connection(("127.0.0.1", s.port), timeout=5) + sock.sendall(b"GET /ninlight_evk_ota.img HTTP/1.1\r\nHost: x\r\n\r\n") + got = b"" + sock.settimeout(2) + try: + while len(got) < 200000: + chunk = sock.recv(65536) + if not chunk: + break + got += chunk + except socket.timeout: + pass + assert len(got) < 20000 # far less than the full image, same bound as above + sock.close() # the lamp's watchdog gives up and reconnects, like on real hardware + + # Second request, resuming with Range exactly like a real lamp would. This + # client's cumulative budget is already spent, so it must get zero more bytes of + # body - not a fresh --stall-after allowance just because it's a new connection. + sock2 = socket.create_connection(("127.0.0.1", s.port), timeout=5) + sock2.sendall(b"GET /ninlight_evk_ota.img HTTP/1.1\r\nHost: x\r\nRange: bytes=1000-\r\n\r\n") + sock2.settimeout(2) + header = b"" + while b"\r\n\r\n" not in header: + header += sock2.recv(4096) + assert b" 206 " in header.split(b"\r\n", 1)[0] + body_after_header = header.split(b"\r\n\r\n", 1)[1] + assert body_after_header == b"", "resumed request must not receive any body bytes" + try: + more = sock2.recv(1) + except socket.timeout: + more = None + assert more in (None, b""), "server sent data after the client's stall budget was spent" + sock2.close() + finally: + s.stop() + + def test_concurrent_downloads_are_all_recorded(station): """Several lamps hitting the station at once must not lose or corrupt status.json. From 3d69412f5a30b2f9f2d62406c4197ea88e01a574 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 00:40:55 +0200 Subject: [PATCH 095/150] docs: Wiederaufnahme-Verhalten bei Firmware-Updates differenzieren MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Abschnitt beschrieb pauschal ein rund siebenminütiges TCP-Timeout ohne Wiederaufnahme. Heute gemessen gilt das nur, wenn die Gegenstelle komplett stirbt (Prozess weg). Bleibt sie erreichbar und liefert nur nichts mehr, greift das 5-s-RECV_TIMEOUT der Firmware, und die Lampe setzt per Range sauber fort – am EVK fünfmal hintereinander bis das Image vollständig war. Co-Authored-By: Claude Sonnet 5 --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 239509a..a89d0e7 100644 --- a/README.md +++ b/README.md @@ -95,11 +95,19 @@ Update, oder scheitert der Start aus einem anderen Grund, antwortet sie mit `409 Lampe und bootet mit der neuen Version. Wird das Image abgelehnt, gibt es **keinen** Reset — die Lampe läuft mit der alten Firmware weiter und trägt den Grund in `last_ota_error` (`GET system/info`) ein. Ein Verbindungsabbruch mitten im Download endet bei `last_ota_error = download`. Die Firmware kann einen -sauber abgerissenen Transfer per HTTP-Range wieder aufnehmen (auf eine feste Rundenzahl begrenzt), aber -darauf sollte man sich nicht verlassen: Stirbt die Gegenstelle mitten im Download, hängt der Lesevorgang -am EVK gemessen rund **sieben Minuten** in einem einzigen TCP-Timeout — das `RECV_TIMEOUT` von 5 s des -HTTP-Clients greift dort nicht — und bricht danach ohne Wiederaufnahme ab. Die Lampe bleibt in dieser Zeit -über REST erreichbar und lässt sich weiter schalten, weist aber jeden weiteren `fw_update` mit `409` ab. +abgerissenen Transfer per HTTP-Range wieder aufnehmen (auf eine feste Rundenzahl begrenzt) — was dabei +passiert, hängt aber davon ab, wie die Gegenstelle abbricht: + +- **Gegenstelle bleibt erreichbar, liefert aber nichts mehr** (Station hängt, Netzwerkpfad lebt): Das + `RECV_TIMEOUT` von 5 s des HTTP-Clients greift wie vorgesehen, die Lampe gibt die Verbindung auf, baut + eine neue auf und setzt per `Range` genau dort fort, wo sie aufgehört hat. Am EVK gemessen funktionierte + das über **fünf** aufeinanderfolgende Reconnects hinweg, bis das Image vollständig übertragen war — auf + diesen Fall darf man sich also verlassen. +- **Gegenstelle stirbt komplett** (Prozess weg, nicht mehr erreichbar): Der Lesevorgang hängt am EVK + gemessen rund **sieben Minuten** in einem einzigen TCP-Timeout, den das 5-s-`RECV_TIMEOUT` der Firmware + nicht abfängt, und bricht danach **ohne** Wiederaufnahme ab. Das bleibt der unangenehme Fall: Die Lampe + ist während der sieben Minuten über REST erreichbar und lässt sich weiter schalten, weist aber jeden + weiteren `fw_update` mit `409` ab, bis der Download endgültig als gescheitert gilt. Werte von `last_ota_error` (`null`, solange nichts fehlgeschlagen ist): From b169fae89d73c63c75fe42bfa609449b290bfba6 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 00:54:15 +0200 Subject: [PATCH 096/150] docs: Stillstandstest und Wiederaufnahme am EVK belegt Co-Authored-By: Claude Opus 5 --- docs/hardware-checklist.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 88fd497..4c41d1c 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -81,8 +81,9 @@ Vor dem Einsatz auf echten Lampen reproduzieren und aufklären. - [x] Recovery zieht das Image und flasht: `recovery image version 0.3.7 (running 0.3.6)` → `trailer ok` → Neustart mit der neuen Version — 2026-09-13 - [x] Station angelassen, erneut gebootet → `already carries the running version 0.3.7, not flashing`, `ota: skipped`, kein Werksreset (beide Boots melden `boot #1`), `last_ota_error` bleibt leer — 2026-09-13 - [x] Zustandsdatei der Station protokolliert beide Auslieferungen mit `status: ok`, Gerätetyp und Version — 2026-09-13 -- [ ] Fehlendes Image für den Gerätetyp → Station antwortet `404`, Eintrag `notfound`, Lampe bootet normal weiter -- [ ] Stillstand: Server hält die Verbindung, sendet nichts mehr → nach Ablauf der Zeitobergrenze startet der Chip neu +- [x] Fehlendes Image für den Gerätetyp → Station antwortet `404`, Eintrag `notfound`, Lampe bootet normal weiter — 2026-09-13 (Lampe meldete `update failed (download)`) +- [x] Stillstand: Server hält die Verbindung, sendet nichts mehr → nach Ablauf der Zeitobergrenze startet der Chip neu — 2026-09-13 (Download 00:42:17, ~120 Wiederaufnahmeversuche im 5-s-Takt mit je null Bytes, Obergrenze exakt bei 600000 ms um 00:52:17, Reset um 00:52:56 nach dem 20-s-Watchdog, danach `boot #1` ohne Werksreset) +- [x] Wiederaufnahme nach Abriss: Gegenstelle erreichbar, sendet aber nichts mehr → Lampe setzt per `Range` fort — 2026-09-13 (fünf Fortsetzungen 263700 → 198164 → 132628 → 67092 → 1556, Image vollständig; gilt **nicht** für eine tote Gegenstelle, siehe README) - [ ] Echter Rettungsfall: Lampe ohne gültige WLAN-Konfiguration (Build `evk-onb`) findet die Station und wird geflasht - [ ] `ninlight-station off`, Pi neu starten → Pi kommt im Heim-WLAN hoch, kein Zugangspunkt, Dienst inaktiv From 6daf36540cbe01684cd4f1a45ea82c7f22ea5eb8 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 01:05:11 +0200 Subject: [PATCH 097/150] docs: Rettungsfall und Abschlusspruefung der Station belegt Co-Authored-By: Claude Opus 5 --- docs/hardware-checklist.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 4c41d1c..303b860 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -84,8 +84,8 @@ Vor dem Einsatz auf echten Lampen reproduzieren und aufklären. - [x] Fehlendes Image für den Gerätetyp → Station antwortet `404`, Eintrag `notfound`, Lampe bootet normal weiter — 2026-09-13 (Lampe meldete `update failed (download)`) - [x] Stillstand: Server hält die Verbindung, sendet nichts mehr → nach Ablauf der Zeitobergrenze startet der Chip neu — 2026-09-13 (Download 00:42:17, ~120 Wiederaufnahmeversuche im 5-s-Takt mit je null Bytes, Obergrenze exakt bei 600000 ms um 00:52:17, Reset um 00:52:56 nach dem 20-s-Watchdog, danach `boot #1` ohne Werksreset) - [x] Wiederaufnahme nach Abriss: Gegenstelle erreichbar, sendet aber nichts mehr → Lampe setzt per `Range` fort — 2026-09-13 (fünf Fortsetzungen 263700 → 198164 → 132628 → 67092 → 1556, Image vollständig; gilt **nicht** für eine tote Gegenstelle, siehe README) -- [ ] Echter Rettungsfall: Lampe ohne gültige WLAN-Konfiguration (Build `evk-onb`) findet die Station und wird geflasht -- [ ] `ninlight-station off`, Pi neu starten → Pi kommt im Heim-WLAN hoch, kein Zugangspunkt, Dienst inaktiv +- [x] Echter Rettungsfall: Lampe ohne gültige WLAN-Konfiguration findet die Station und wird geflasht — 2026-09-13, zweistufig belegt: (1) Station aus, Build ohne Zugangsdaten seriell aufgespielt → `ninlight 0.3.7-4-gb169fa`, Scan ohne Treffer, Lampe geht in den Onboarding-AP, über Netz nicht erreichbar. (2) Station an, ein Stromzyklus → `station found` → `recovery image version 0.3.7 (running 0.3.7-4-gb169fa)` → `trailer ok` → Neustart → `connecting to ninIOT`. 23 Sekunden vom Einschalten bis zur geretteten Lampe. +- [x] `ninlight-station off`, Pi neu starten → Pi kommt im Heim-WLAN hoch, kein Zugangspunkt, Dienst inaktiv — 2026-09-13 **Bekannte Eigenheit:** Die Lampe lädt bei jedem Boot das komplette Image (~256 KB), auch wenn sie es danach wegen gleicher Version verwirft — die Version steht im Trailer am Dateiende. Kein Fehler, aber die Station From d464bb167ed8e5a88334cc06f031ead8a192a845 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 01:29:25 +0200 Subject: [PATCH 098/150] fix: Voll-Deploy der Rettungsstation gegen doppelte Images und stillen Loeschverlust absichern Zwei Build-Verzeichnisse mit demselben Image-Dateinamen (z.B. build/evk und build/evk-onb, letzteres ohne WLAN-Zugangsdaten) landeten flach im selben Staging-Namen - die Sortierreihenfolge entschied zufaellig, welches gewinnt, und die Trailer-Pruefung merkt nichts, weil in beiden derselbe Geraetetyp steht. deploy.sh bricht jetzt ab und nennt beide Fundorte, statt zu raten. Der Voll-Deploy (deploy.sh ohne target - der in README.md gezeigte Normalfall) loeschte per rsync --delete zudem die Images aller gerade nicht gebauten Geraetetypen, ohne je zu sagen, welche. Vor dem Loeschen wird jetzt per Namensvergleich ermittelt und ausgegeben, welche Dateien verschwinden - sichtbar statt still, ohne den Ablauf fuer Skripte zu blockieren. Neue Testszenarien 7-9 in tools/station/tests/test_deploy.sh decken beide Faelle ab. Co-Authored-By: Claude Sonnet 5 --- tools/station/deploy.sh | 35 ++++++++++++- tools/station/tests/test_deploy.sh | 81 ++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index b0ab712..b98e345 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -27,7 +27,23 @@ if [ $# -gt 0 ]; then done else FULL_SYNC=1 - while IFS= read -r f; do FILES+=("$f"); done < <(find "$ROOT/build" -maxdepth 2 -name 'ninlight_*_ota.img' | sort) + # Zwei Build-Verzeichnisse koennen denselben Image-Dateinamen liefern (z.B. + # build/evk/ und ein Nebenschauplatz wie build/evk-onb/, das ohne WLAN- + # Zugangsdaten gebaut wurde). Landen beide flach im selben STAGE/$base, + # entscheidet allein die Sortierreihenfolge, welches gewinnt - und die + # Trailer-Pruefung merkt nichts, weil in beiden derselbe Geraetetyp steht. + # Ein zweiter Fundort fuer denselben Dateinamen ist deshalb ein Abbruch, + # kein Zufall. + declare -A SEEN_AT=() + while IFS= read -r f; do + base=$(basename "$f") + if [ -n "${SEEN_AT[$base]:-}" ]; then + echo "Fehler: $base mehrfach gefunden - ${SEEN_AT[$base]} und $f. Voller Deploy abgebrochen, welches Bild echt ist, ist nicht entscheidbar. Nicht benoetigtes Build-Verzeichnis loeschen oder gezielt deployen." >&2 + exit 1 + fi + SEEN_AT[$base]=$f + FILES+=("$f") + done < <(find "$ROOT/build" -maxdepth 2 -name 'ninlight_*_ota.img' | sort) [ ${#FILES[@]} -gt 0 ] || { echo "Fehler: keine Images unter $ROOT/build gefunden" >&2; exit 1; } fi @@ -73,7 +89,22 @@ UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} sudo install -d -m 0755 "$ROOT_DIR/images" if [ "$FULL_SYNC" -eq 1 ]; then # Vollabgleich: alle Images ausserhalb der aktuellen Auslieferung entfernen. - echo " voller Abgleich - Images ausserhalb der Auswahl werden entfernt" + # Vorher zeigen, WELCHE das sind - sonst faellt ein geloeschtes Image erst + # auf, wenn jemand mit einer toten Lampe vor der Station steht. Ermittelt + # per einfachem Namensvergleich (portabel, kein Verlass auf rsyncs + # "deleting"-Ausgabe, die ohne -v gar nicht erscheint). + OLD_LIST=$(find "$ROOT_DIR/images" -maxdepth 1 -name 'ninlight_*_ota.img' -exec basename {} \; 2>/dev/null | sort) + NEW_LIST=$(find /tmp/ninlight-images -maxdepth 1 -name 'ninlight_*_ota.img' -exec basename {} \; | sort) + REMOVED="" + if [ -n "$OLD_LIST" ]; then + REMOVED=$(comm -23 <(printf '%s\n' "$OLD_LIST") <(printf '%s\n' "$NEW_LIST")) + fi + if [ -n "$REMOVED" ]; then + echo " voller Abgleich - folgende Images werden entfernt:" + echo "$REMOVED" | sed 's/^/ /' + else + echo " voller Abgleich - keine Images werden entfernt" + fi sudo rsync -a -c --delete /tmp/ninlight-images/ "$ROOT_DIR/images/" else # Gezielter Deploy: nur die uebertragenen Dateien ersetzen/ergaenzen, alle diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index 5b63823..347b973 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -275,6 +275,87 @@ else fail "setup_pi.sh: Default fuer SCP_BIN ist nicht mehr /usr/bin/scp" fi +echo +echo "=== Szenario 7: Voll-Deploy mit zwei Build-Verzeichnissen fuer denselben Dateinamen -> Abbruch (B1) ===" +SANDBOX="$TMPROOT/s7"; STUBBIN="$TMPROOT/bin7"; LOG="$TMPROOT/s7.log"; SANDBOX_STATE="$TMPROOT/s7-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/evk" "$SANDBOX/build/evk-onb" +echo "evk-mit-wlan" >"$SANDBOX/build/evk/ninlight_evk_ota.img" +echo "evk-ohne-wlan" >"$SANDBOX/build/evk-onb/ninlight_evk_ota.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0 ($RC)"; else fail "Exit-Code war 0, obwohl zwei Fundorte denselben Dateinamen liefern"; fi +if echo "$OUT" | grep -q "ninlight_evk_ota.img mehrfach gefunden"; then + pass "Fehlermeldung nennt den doppelten Dateinamen" +else + fail "Fehlermeldung zum doppelten Dateinamen fehlt: $OUT" +fi +if echo "$OUT" | grep -q "build/evk/ninlight_evk_ota.img" && echo "$OUT" | grep -q "build/evk-onb/ninlight_evk_ota.img"; then + pass "Fehlermeldung nennt beide Fundorte" +else + fail "Fehlermeldung nennt nicht beide Fundorte: $OUT" +fi +if ! grep -q "STUB" "$LOG" 2>/dev/null; then + pass "kein Transfer-Kommando aufgerufen - nichts wurde stillschweigend uebertragen" +else + fail "ein Transfer-Kommando wurde trotz doppeltem Dateinamen aufgerufen: $(cat "$LOG")" +fi + +echo +echo "=== Szenario 8: Voll-Deploy zeigt vor dem Loeschen, welche Images verschwinden (B2) ===" +SANDBOX="$TMPROOT/s8"; STUBBIN="$TMPROOT/bin8"; LOG="$TMPROOT/s8.log"; SANDBOX_STATE="$TMPROOT/s8-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image-neu" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images" +echo "veraltet" >"$SANDBOX_STATE/opt/ninlight-station/images/ninlight_veraltet_ota.img" +echo "auch-veraltet" >"$SANDBOX_STATE/opt/ninlight-station/images/ninlight_e14_cw_ota.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if echo "$OUT" | grep -q "folgende Images werden entfernt"; then + pass "Ausgabe kuendigt die zu entfernenden Images an, bevor geloescht wird" +else + fail "Ausgabe nennt die zu entfernenden Images nicht: $OUT" +fi +if echo "$OUT" | grep -q "ninlight_veraltet_ota.img" && echo "$OUT" | grep -q "ninlight_e14_cw_ota.img"; then + pass "beide zu entfernenden Dateien werden namentlich genannt" +else + fail "nicht alle zu entfernenden Dateien werden genannt: $OUT" +fi +if [ ! -f "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_veraltet_ota.img" ]; then + pass "veraltetes Image wurde nach der Ankuendigung tatsaechlich entfernt" +else + fail "veraltetes Image wurde trotz Ankuendigung nicht entfernt" +fi + +echo +echo "=== Szenario 9: Voll-Deploy ohne verwaiste Images meldet das statt zu schweigen (B2) ===" +SANDBOX="$TMPROOT/s9"; STUBBIN="$TMPROOT/bin9"; LOG="$TMPROOT/s9.log"; SANDBOX_STATE="$TMPROOT/s9-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if echo "$OUT" | grep -q "keine Images werden entfernt"; then + pass "Ausgabe meldet explizit, dass nichts entfernt wird" +else + fail "Ausgabe meldet nicht, dass nichts entfernt wird: $OUT" +fi + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From 2dfb1fd1c2cdcc53368f2fb5f77a083878a53fe9 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 01:29:34 +0200 Subject: [PATCH 099/150] feat: GET system/settings zeigt Herkunft der WLAN-Zugangsdaten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Antwort gab bisher nur die gespeicherte SSID aus. Wurde eine Lampe nie per REST onboardet, ist die leer, und die Lampe laeuft in Wirklichkeit auf den einkompilierten NINLIGHT_WIFI_SSID/-PASSWORD (siehe app_main.c) - GET system/settings suggerierte damit "kein WLAN konfiguriert", obwohl die Lampe zuverlaessig verbunden ist, und widersprach scheinbar system/info. Additiv, ohne die Bedeutung von "ssid" zu aendern: wifi.source ist jetzt "stored" oder "compiled". Spec (§7.2) und bruno/ninlight.yml dokumentieren das Feld, ein neuer Hosttest deckt den compiled-Fall ab. Co-Authored-By: Claude Sonnet 5 --- bruno/ninlight.yml | 5 +++++ src/net/nl_api.c | 7 +++++++ test/test_api.c | 15 +++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/bruno/ninlight.yml b/bruno/ninlight.yml index 8412270..2d95d8e 100644 --- a/bruno/ninlight.yml +++ b/bruno/ninlight.yml @@ -225,6 +225,11 @@ items: timeout: 0 followRedirects: true maxRedirects: 5 + docs: |- + `wifi.source` ist `"stored"`, solange ein per PUT onboardetes WLAN gespeichert ist, + sonst `"compiled"` - dann laeuft die Lampe auf den einkompilierten + NINLIGHT_WIFI_SSID/-PASSWORD, `wifi.ssid` ist leer, und `GET system/info` kann trotzdem + eine verbundene SSID zeigen. - info: name: Set WiFi type: http diff --git a/src/net/nl_api.c b/src/net/nl_api.c index e65869f..888f8f6 100644 --- a/src/net/nl_api.c +++ b/src/net/nl_api.c @@ -262,6 +262,13 @@ static void handle_get_settings(const nl_api_ctx_t *ctx, nl_api_response_t *resp cJSON_AddStringToObject(o, "name", s.name); w = cJSON_AddObjectToObject(o, "wifi"); cJSON_AddStringToObject(w, "ssid", s.wifi_ssid); + /* "ssid" alone can't be told apart from "kein WLAN konfiguriert": ein leerer + * gespeicherter Datensatz laesst app_main.c beim Boot auf die einkompilierten + * NINLIGHT_WIFI_SSID/-PASSWORD zurueckfallen (siehe dort), und die Lampe kommt + * trotzdem zuverlaessig ins WLAN. Wer nur "ssid" sieht, kann das nicht + * unterscheiden - "source" macht additiv sichtbar, woher die Zugangsdaten + * kommen, ohne die Bedeutung von "ssid" zu aendern. */ + cJSON_AddStringToObject(w, "source", s.wifi_ssid[0] ? "stored" : "compiled"); cJSON_AddBoolToObject(o, "auth_all", s.auth_all != 0); reply_json(resp, 200, o); } diff --git a/test/test_api.c b/test/test_api.c index 090037d..65014fb 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -233,6 +233,7 @@ static void test_settings_auth(void) TEST_ASSERT_EQUAL_INT(200, resp.status); TEST_ASSERT_EQUAL_STRING("Wohnzimmer", cJSON_GetStringValue(field("name"))); TEST_ASSERT_EQUAL_STRING("ninHome", cJSON_GetStringValue(cJSON_GetObjectItem(field("wifi"), "ssid"))); + TEST_ASSERT_EQUAL_STRING("stored", cJSON_GetStringValue(cJSON_GetObjectItem(field("wifi"), "source"))); TEST_ASSERT_TRUE(cJSON_IsFalse(field("auth_all"))); TEST_ASSERT_NULL(cJSON_GetObjectItem(field("wifi"), "password")); /* never exposed */ fake_open = true; @@ -240,6 +241,19 @@ static void test_settings_auth(void) TEST_ASSERT_EQUAL_INT(200, resp.status); } +/* F1: nie onboardet -> gespeicherte ssid ist leer, die Lampe laeuft auf den + * einkompilierten Zugangsdaten (siehe app_main.c). "source" muss das additiv + * sichtbar machen, ohne "ssid" umzudeuten - sonst widerspricht die Antwort + * scheinbar system/info, das die tatsaechlich verbundene SSID zeigt. */ +static void test_settings_wifi_source_reflects_compiled_fallback(void) +{ + fake_settings.wifi_ssid[0] = '\0'; + call(NL_HTTP_GET, "/api/v1/system/settings", TOKEN, NULL); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_STRING("", cJSON_GetStringValue(cJSON_GetObjectItem(field("wifi"), "ssid"))); + TEST_ASSERT_EQUAL_STRING("compiled", cJSON_GetStringValue(cJSON_GetObjectItem(field("wifi"), "source"))); +} + static void test_settings_wifi(void) { call(NL_HTTP_PUT, "/api/v1/system/settings", TOKEN, "{\"type\":\"wifi\",\"ssid\":\"ninIOT\",\"password\":\"secret12\"}"); @@ -356,6 +370,7 @@ int main(void) RUN_TEST(test_info_is_open); RUN_TEST(test_info_with_sta_ip_and_error); RUN_TEST(test_settings_auth); + RUN_TEST(test_settings_wifi_source_reflects_compiled_fallback); RUN_TEST(test_settings_wifi); RUN_TEST(test_settings_name_token_auth); RUN_TEST(test_commands); From 435884039b788e2eb77643c25949dc18dc257f9a Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 01:29:43 +0200 Subject: [PATCH 100/150] feat: ninlight-station warnt beim Einschalten vor dem oeffentlichen Default-Passwort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Spec (§9) verlangt beide Betriebsregeln in der Ausgabe von "on": die Erinnerung ans Ausschalten stand schon da, die zum eigenen Passwort fehlte. Bleibt PSK auf dem oeffentlichen Default 'ninlight-recovery' stehen, kann jeder in Funkreichweite, der ihn kennt, jeder Lampe beliebige Firmware unterschieben - das wird jetzt bei jedem Einschalten sichtbar gemacht. Co-Authored-By: Claude Sonnet 5 --- tools/station/ninlight-station | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 6c540fc..6a3386c 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -66,6 +66,13 @@ on) sleep 1 if systemctl is-active --quiet ninlight-station; then echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)" + # Mit dem oeffentlichen Default-Passwort (im Repo dokumentiert) kann jeder in + # Funkreichweite, der es kennt, jeder Lampe beliebige Firmware unterschieben - das ist + # die eine Haelfte der Betriebsregeln aus README.md/§9, die andere (Station wieder + # ausschalten) stand hier schon. + if [ "$PSK" = "ninlight-recovery" ]; then + echo "Warnung: PSK ist noch der oeffentliche Default 'ninlight-recovery' - eigenes Passwort in /etc/ninlight-station.conf setzen." >&2 + fi echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." else # Mit Restart=always laeuft ein abgestuerzter Dienst sonst im Hintergrund From 1d7d087cffad0fa502840c86631e5999faee4575 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 01:29:56 +0200 Subject: [PATCH 101/150] fix: Rettungsstation loggt auch abgelehnte Methoden und Protokollfehler do_POST (und PUT/DELETE/HEAD) riefen reject() auf, ohne eine Logzeile zu schreiben, und das ueberschriebene log_message() legte damit unbeabsichtigt auch log_error() lahm (BaseHTTPRequestHandler ruft es standardmaessig auf, z.B. bei einer kaputten Request-Zeile oder einem Timeout, bevor ueberhaupt ein Handler wie do_GET erreicht wird). Fuer das einzige Diagnosewerkzeug im Rettungsfall darf keine Anfrage stumm bleiben. do_POST loggt jetzt wie do_GET explizit, und log_error() ist separat ueberschrieben statt implizit mit log_message() stillgelegt zu werden. Die neuen Tests fuer den 405-Fall lesen den rohen Datei-Deskriptor des Server-Prozesses statt ueber TextIOWrapper.readline() zu gehen - Letzteres kann mehr Zeilen aus der Pipe in den eigenen Puffer lesen, als es zurueckgibt, was select() auf dem naechsten Datei-Deskriptor faelschlich "nichts da" melden liess. Der Test fuer die kaputte Request-Zeile ruft log_error() direkt auf: ein echter kaputter Rohsocket wird in dieser Sandbox von einer Netzwerkschicht abgefangen, bevor er den Server erreicht (sichtbar an einem untergeschobenen "Server: aiohttp"-Header). Co-Authored-By: Claude Sonnet 5 --- tools/station/server.py | 14 ++++++ tools/station/tests/test_server.py | 76 ++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/tools/station/server.py b/tools/station/server.py index 849b0a9..e941fd3 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -138,12 +138,26 @@ class Handler(BaseHTTPRequestHandler): def log_message(self, fmt: str, *args) -> None: # noqa: D102 - quieter default log pass + def log_error(self, fmt: str, *args) -> None: + # BaseHTTPRequestHandler.log_error() calls log_message() by default, which we + # silence above in favour of our own log() calls - but that also silences it for + # requests that never reach do_GET/do_POST (a malformed request line, a client + # timeout mid-request). The station is the only diagnostic tool available during a + # rescue, so those cases must still produce a line instead of nothing. + try: + client = self.client_address[0] + except (AttributeError, IndexError): + client = "?" + print(f"{now_iso()} {client} ERROR {fmt % args}", flush=True) + def log(self, status: int, nbytes: int, started: float) -> None: print(f"{now_iso()} {self.client_address[0]} {self.path} {status} " f"{nbytes}B {time.monotonic() - started:.1f}s", flush=True) def do_POST(self) -> None: + started = time.monotonic() self.reject(405, "method not allowed") + self.log(405, 0, started) do_PUT = do_DELETE = do_HEAD = do_POST diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index 3196c60..dbda832 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -2,6 +2,7 @@ import hashlib import http.client import json import os +import select import socket import stat import subprocess @@ -92,6 +93,44 @@ class Station: self.proc.terminate() self.proc.wait(timeout=5) + def wait_for_log_line(self, contains, timeout=3.0): + """Reads the server's merged stdout/stderr (see __init__) until a line containing + `contains` shows up, skipping unrelated lines (e.g. the startup banner) - used to + check that a request actually produced a log line (F3: every request must, + including rejected ones and errors the base handler catches before routing). + + Reads the raw fd directly with os.read() instead of self.proc.stdout.readline(): + readline() goes through TextIOWrapper's internal BufferedReader, whose underlying + read() can slurp everything currently sitting in the pipe (banner *and* the log + line we're waiting for) into its own buffer on the very first call, while only + handing back the first line. select() then reports the fd as not-readable on the + next iteration - the data isn't gone, it is just buffered somewhere select() can't + see - and a fixed-size test timeout would flake on exactly the line it's checking + for. + """ + fd = self.proc.stdout.fileno() + buf = getattr(self, "_log_buf", "") + deadline = time.time() + timeout + while True: + while "\n" in buf: + line, buf = buf.split("\n", 1) + self._log_buf = buf + if contains in line: + return line + "\n" + remaining = deadline - time.time() + if remaining <= 0: + self._log_buf = buf + return None + r, _, _ = select.select([fd], [], [], remaining) + if not r: + self._log_buf = buf + return None + chunk = os.read(fd, 65536) + if not chunk: + self._log_buf = buf + return None + buf += chunk.decode("utf-8", "replace") + @pytest.fixture def station(tmp_path): @@ -139,6 +178,43 @@ def test_other_methods_are_405(station): assert c.getresponse().status == 405 +def test_rejected_method_produces_a_log_line(station): + """F3: do_POST (and PUT/DELETE/HEAD) used to call reject() without logging anything - + the station is the only diagnostic tool during a rescue, and the spec requires a line + per request, rejections included.""" + c = station.conn() + c.request("POST", "/ninlight_evk_ota.img", body=b"") + assert c.getresponse().status == 405 + line = station.wait_for_log_line(" 405 ") + assert line is not None, "kein Log-Eintrag fuer die abgelehnte POST-Anfrage" + assert "/ninlight_evk_ota.img" in line + + +def test_log_error_is_not_silenced_by_log_message(capsys): + """F3: BaseHTTPRequestHandler.log_error() calls log_message() by default (used e.g. for + a malformed request line or a client timeout, cases that never reach do_GET/do_POST). + server.py silences log_message() in favour of its own per-request log() calls - which + used to silence log_error() right along with it, since it wasn't overridden separately. + This exercises Handler.log_error() directly rather than over a raw socket: this + sandbox's network layer intercepts/rewrites malformed non-HTTP payloads before they + reach the server (observed via a spoofed 'Server: aiohttp' response), which makes a + genuine malformed-request-line test unreliable here.""" + import importlib.util + + spec = importlib.util.spec_from_file_location("ninlight_station_server", SERVER) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + class _Stub: + client_address = ("203.0.113.5", 54321) + + module.Handler.log_error(_Stub(), "code %d, message %s", 400, "Bad request version") + out = capsys.readouterr().out + assert "ERROR" in out + assert "203.0.113.5" in out + assert "code 400, message Bad request version" in out + + def test_range_request_returns_206_and_the_right_slice(station): data = make_image(station.image_dir / "ninlight_e27_rgb_ota.img", "e27_rgb", "2.0.0", b"abc" * 2000) c = station.conn() From 9768b25d10176da6e4a0dc55b3f7101ed69c9f26 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 01:30:03 +0200 Subject: [PATCH 102/150] docs: passwortloses sudo als Voraussetzung der Rettungsstation dokumentieren CLI und beide Skripte (setup_pi.sh, deploy.sh) rufen sudo nicht-interaktiv auf; ohne passwortloses sudo fuer den SSH-Benutzer bricht der gesamte Ablauf mit einem Passwort-Prompt ab, den nichts davon behandeln kann. Das war bisher eine unausgesprochene Voraussetzung. Co-Authored-By: Claude Sonnet 5 --- tools/station/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/station/README.md b/tools/station/README.md index 133cea1..fb6394f 100644 --- a/tools/station/README.md +++ b/tools/station/README.md @@ -11,6 +11,12 @@ Lötkolben käme. Trägt das angebotene Image dieselbe Version, die bereits läuft, lehnt die Lampe es ab und bootet normal weiter. Eine angelassene Station flasht also nicht in einer Endlosschleife. +## Voraussetzung + +Der SSH-Benutzer auf dem Pi braucht passwortloses `sudo` — beide Skripte und die CLI +(`ninlight-station on|off|status`) rufen `sudo` nicht-interaktiv auf; ein Passwort-Prompt +würde dort nur mit einer Fehlermeldung enden. + ## Bedienung tools/station/setup_pi.sh einmalig einrichten From f32216d17f33a5cdcff36398dbcc10a69cb9cb63 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 02:32:31 +0200 Subject: [PATCH 103/150] fix: SM2135EH beim Ausschalten in den Standby legen Bei allen Kanaelen auf null schrieb der Treiber nur Nullen ins Kanal-Register, liess den Chip aber aktiv - er laesst dann genug Reststrom durch, dass die Lampe schwach weiterglimmt. An einer migrierten GU10 RGB gemessen. Jetzt wird Bit 5 (Standby) gesetzt. Die alte QConnex-Firmware hatte diese Logik, aber auskommentiert (qcl_lamp.c) - das Verhalten ist von dort geerbt. Lampen an direkten PWM-Pins (G95, CW-Varianten) sind nicht betroffen. Co-Authored-By: Claude Opus 5 --- src/drivers/nl_sm2135eh.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/drivers/nl_sm2135eh.c b/src/drivers/nl_sm2135eh.c index da0f95e..6730251 100644 --- a/src/drivers/nl_sm2135eh.c +++ b/src/drivers/nl_sm2135eh.c @@ -7,6 +7,7 @@ #define SM_ADDR_CURRENT 0xC0u #define SM_ADDR_WY 0xC5u #define SM_CHANNEL_WY 0x80u /* channel select bit 7: 0 = RGB, 1 = WY */ +#define SM_CHANNEL_STBY 0x20u /* standby bit 5: powers the outputs down */ extern void delay_us(unsigned int us); /* provided by libwlan.a */ @@ -76,7 +77,15 @@ void nl_sm2135eh_set(const uint8_t ch[NL_CH_COUNT]) s_have_last = true; if (!white) { - uint8_t d[6] = {SM_ADDR_CURRENT, s_current, 0x00, ch[NL_CH_B], ch[NL_CH_G], ch[NL_CH_R]}; + /* All channels dark: put the driver into standby instead of just writing zero duty. + Without bit 5 the chip stays active and leaks enough current to keep the lamp + faintly glowing - measured on a migrated GU10 RGB, 2026-09-13. The old QConnex + firmware had this logic but left it commented out (qcl_lamp.c), which is why the + behaviour was inherited. Lamps driven straight from PWM pins (G95, the CW variants) + are unaffected. Turning back on writes 0x00 or SM_CHANNEL_WY here and clears it. */ + bool dark = !ch[NL_CH_R] && !ch[NL_CH_G] && !ch[NL_CH_B]; + uint8_t chan = dark ? SM_CHANNEL_STBY : 0x00u; + uint8_t d[6] = {SM_ADDR_CURRENT, s_current, chan, ch[NL_CH_B], ch[NL_CH_G], ch[NL_CH_R]}; sm_send(d, 6); } else { uint8_t h[3] = {SM_ADDR_CURRENT, s_current, SM_CHANNEL_WY}; From 62a2b43175a2528c123507d48540c48fc2d981ab Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 02:37:08 +0200 Subject: [PATCH 104/150] docs: Migration zweier echter Lampen und den SM2135EH-Standby-Fund festhalten Co-Authored-By: Claude Opus 5 --- docs/hardware-checklist.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 303b860..6feae5d 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -59,11 +59,11 @@ Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.s Für den echten Flottenbau `NINLIGHT_RECOVERY_PASSWORD` und `NINLIGHT_TOKEN` in `config.local.cmake` setzen — die Defaults sind nur fürs EVK gedacht. ## Später (Lampen) -- [ ] SM2135EH: Standby-Bit bei allen Kanälen 0 prüfen (Spec §4; alte Firmware setzte es praktisch nie) -- [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) +- [x] SM2135EH: Standby-Bit bei allen Kanälen 0 — 2026-09-13 an migrierter GU10 RGB geprüft: **war ein echter Fehler**, die Lampe glomm im Aus weiter. Bit 5 im Kanal-Register wird jetzt gesetzt (Commit zu v0.3.8); danach beide Ausschaltwege (aus Weiß und aus Farbe) vollständig dunkel +- [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) — *derselbe Treiber ist am 2026-09-13 an einer GU10 RGB geprüft (rot/grün/blau sauber getrennt, warm- und kaltweiß deutlich unterschieden); für die E27 selbst steht die Prüfung noch aus* - [ ] E27 CW: Gegenphase CW sichtbar flackerfrei -- [ ] G95: Pinbelegung WW=PB15/CW=PB13 prüfen -- [ ] Farbtemperaturbereiche aller Varianten +- [x] G95: Pinbelegung WW=PB15/CW=PB13 — 2026-09-13 an einer migrierten G95 geprüft: 2500 K deutlich warm, 5000 K deutlich kühler, Dimmen auf 10 % flackerfrei, weiches Ausblenden +- [ ] Farbtemperaturbereiche aller Varianten — *g95_cw (2500–5000 K) und gu10_rgb (2700–6500 K) am 2026-09-13 an echten Lampen bestätigt; die übrigen sechs Varianten stehen aus* ## Offene Beobachtung aus der EVK-Session vom 2026-09-12 @@ -90,3 +90,9 @@ Vor dem Einsatz auf echten Lampen reproduzieren und aufklären. **Bekannte Eigenheit:** Die Lampe lädt bei jedem Boot das komplette Image (~256 KB), auch wenn sie es danach wegen gleicher Version verwirft — die Version steht im Trailer am Dateiende. Kein Fehler, aber die Station sieht pro Boot einen vollen Transfer. + +## Migration echter Lampen (Plan 4b, 2026-09-13) + +- [x] G95 (Dev-Build, Product-ID `-klyqa.lighting.cw-ww.g95-dev`) mit dem **Entwicklungs**schlüssel signiert → angenommen, geflasht, läuft ninlight als `g95_cw` +- [x] GU10 (Produktionsbuild, Product-ID `-klyqa.lighting.rgb-cw-ww.gu10`) mit dem **Produktions**schlüssel signiert → angenommen, geflasht, läuft ninlight als `gu10_rgb` +- [x] Migrierte Lampe lässt sich anschließend über das normale ninlight-OTA aktualisieren (GU10 auf 0.3.8) From 00d5a4c53280c324d25cc0f466fdc961df5254b2 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 02:56:56 +0200 Subject: [PATCH 105/150] feat: Default-Transition, Weckschalter und farbfreier Ausschaltfade Zwei neue Einstellungen, beide wie fade_in/fade_out ueber POST /api/v1/device/state les- und schreibbar und sofort wirksam: - transition_default (Voreinstellung 300 ms) fuellt die Transition fuer Farb-, Weiss- und Helligkeitswechsel bei eingeschalteter Lampe, wenn der Aufruf kein eigenes "transition" mitbringt. Beim Ein- und Ausschalten behalten fade_in/fade_out weiterhin Vorrang. - wake_on_change (Voreinstellung aus) schaltet die Lampe bei einer Farb-, Weiss- oder Helligkeitsaenderung ein, wenn sie aus war. Ein ausdrueckliches "on": false im selben Aufruf gewinnt immer. Beide Felder haengen am Ende von nl_settings_t, damit bestehende Lampen ueber die Laengentoleranz von read_rec() ihre echten Voreinstellungen bekommen statt Nullen. nl_ctrl_set_fades() heisst jetzt nl_ctrl_set_options(). Dazu ein Treiberfehler, der erst an der migrierten GU10 RGB auffiel: der WY-Zweig von nl_sm2135eh_set() schrieb nur Header und WY-Daten, nie die RGB-Datenregister. Der Chip behielt also die zuletzt gesetzte Farbe, und bei einem direkten Wechsel Farbe -> Weiss (ohne Zwischenschritt ueber dunkel) schaelte sie sich beim Ausblenden wieder frei. Der Header sendet jetzt die - in Weiss immer nullwertigen - RGB-Daten mit. Die Bruno-Collection zeigt Transition jetzt zusammen mit einem Farb- und einem Weisswechsel im selben Aufruf und hat einen Request fuer die beiden neuen Einstellungen. Co-Authored-By: Claude Opus 5 --- bruno/ninlight.yml | 29 ++++++++-- src/app_main.c | 3 +- src/config/nl_settings.c | 2 + src/config/nl_settings.h | 9 +++ src/drivers/nl_sm2135eh.c | 10 +++- src/light/nl_control.c | 21 +++++-- src/light/nl_control.h | 14 ++++- src/net/nl_api.c | 16 ++++++ src/sys/nl_app_api.c | 2 +- src/sys/nl_console.c | 2 +- test/test_api.c | 33 +++++++++++ test/test_control.c | 118 +++++++++++++++++++++++++++++++++++++- test/test_settings.c | 16 ++++++ 13 files changed, 256 insertions(+), 19 deletions(-) diff --git a/bruno/ninlight.yml b/bruno/ninlight.yml index 2d95d8e..16e7e79 100644 --- a/bruno/ninlight.yml +++ b/bruno/ninlight.yml @@ -96,7 +96,7 @@ items: followRedirects: true maxRedirects: 5 - info: - name: Color Red + name: Color Red + Transition type: http seq: 5 http: @@ -110,7 +110,8 @@ items: "r": 255, "g": 0, "b": 0 - } + }, + "transition": 1500 } settings: encodeUrl: true @@ -118,7 +119,7 @@ items: followRedirects: true maxRedirects: 5 - info: - name: Color Temp 2700 + name: Color Temp 2700 + Transition type: http seq: 6 http: @@ -128,7 +129,8 @@ items: type: json data: |- { - "color_temp": 2700 + "color_temp": 2700, + "transition": 1500 } settings: encodeUrl: true @@ -192,6 +194,25 @@ items: timeout: 0 followRedirects: true maxRedirects: 5 + - info: + name: Default Transition + Wake Switch + type: http + seq: 10 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "transition_default": 300, + "wake_on_change": false + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 - info: name: System type: folder diff --git a/src/app_main.c b/src/app_main.c index d3125bd..7b2f96f 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -202,7 +202,8 @@ static void app_task(void *arg) after_ota); nl_app_lock(); nl_engine_init(&cfg); - nl_ctrl_init(board, s_settings.fade_in_ms, s_settings.fade_out_ms); + nl_ctrl_init(board, s_settings.fade_in_ms, s_settings.fade_out_ms, s_settings.transition_ms, + s_settings.wake_on_change != 0); nl_ctrl_boot(&initial); nl_app_unlock(); diff --git a/src/config/nl_settings.c b/src/config/nl_settings.c index 5376a16..ec97167 100644 --- a/src/config/nl_settings.c +++ b/src/config/nl_settings.c @@ -47,6 +47,8 @@ void nl_settings_defaults(nl_settings_t *s) s->power_on_behaviour = NL_POB_LAST; s->fade_in_ms = 500; s->fade_out_ms = 500; + s->transition_ms = 300; + s->wake_on_change = 0; } void nl_settings_init(uint32_t addr_a, uint32_t addr_b) diff --git a/src/config/nl_settings.h b/src/config/nl_settings.h index 5b5f5ba..1941be8 100644 --- a/src/config/nl_settings.h +++ b/src/config/nl_settings.h @@ -19,6 +19,15 @@ typedef struct { uint16_t fade_in_ms; uint16_t fade_out_ms; uint8_t ota_pending; /* set before an OTA; the next boot restores the last state */ + /* Appended fields: read_rec() in nl_settings.c pre-fills the struct with + * nl_settings_defaults() and only overwrites the bytes actually stored, so records + * written by an older firmware pick up the real default here, not zero. Keep adding new + * fields at the end for that to keep working. */ + uint16_t transition_ms; /* default transition (ms) for a color/brightness/CT change + * while the lamp stays on; fade_in/fade_out still win when + * switching on or off. */ + uint8_t wake_on_change; /* if set, such a change also switches the lamp on when it was + * off, unless the same call says "on": false. */ } nl_settings_t; void nl_settings_defaults(nl_settings_t *s); diff --git a/src/drivers/nl_sm2135eh.c b/src/drivers/nl_sm2135eh.c index 6730251..5a14e07 100644 --- a/src/drivers/nl_sm2135eh.c +++ b/src/drivers/nl_sm2135eh.c @@ -88,9 +88,15 @@ void nl_sm2135eh_set(const uint8_t ch[NL_CH_COUNT]) uint8_t d[6] = {SM_ADDR_CURRENT, s_current, chan, ch[NL_CH_B], ch[NL_CH_G], ch[NL_CH_R]}; sm_send(d, 6); } else { - uint8_t h[3] = {SM_ADDR_CURRENT, s_current, SM_CHANNEL_WY}; + /* The RGB data registers are written along with the channel select, even though they + are always zero here: the chip keeps whatever colour was last written, and a direct + colour -> white change never passes through the all-dark frame above. The stale + colour then bleeds through as soon as the white dims - visible as a coloured + fade-out on a migrated GU10 RGB, 2026-09-13. */ + uint8_t h[6] = {SM_ADDR_CURRENT, s_current, SM_CHANNEL_WY, + ch[NL_CH_B], ch[NL_CH_G], ch[NL_CH_R]}; uint8_t d[3] = {SM_ADDR_WY, ch[NL_CH_WW], ch[NL_CH_CW]}; - sm_send(h, 3); + sm_send(h, 6); sm_send(d, 3); } } diff --git a/src/light/nl_control.c b/src/light/nl_control.c index 0ed78df..e88dc08 100644 --- a/src/light/nl_control.c +++ b/src/light/nl_control.c @@ -7,23 +7,29 @@ static const nl_board_t *s_board; static nl_light_state_t s_state; static uint16_t s_fade_in; static uint16_t s_fade_out; +static uint16_t s_transition; +static bool s_wake_on_change; static bool s_dirty; static uint32_t s_changed_at; -void nl_ctrl_init(const nl_board_t *board, uint16_t fade_in_ms, uint16_t fade_out_ms) +void nl_ctrl_init(const nl_board_t *board, uint16_t fade_in_ms, uint16_t fade_out_ms, + uint16_t transition_ms, bool wake_on_change) { s_board = board; s_state = nl_boot_default_state(board); s_state.on = false; s_dirty = false; s_changed_at = 0; - nl_ctrl_set_fades(fade_in_ms, fade_out_ms); + nl_ctrl_set_options(fade_in_ms, fade_out_ms, transition_ms, wake_on_change); } -void nl_ctrl_set_fades(uint16_t fade_in_ms, uint16_t fade_out_ms) +void nl_ctrl_set_options(uint16_t fade_in_ms, uint16_t fade_out_ms, uint16_t transition_ms, + bool wake_on_change) { s_fade_in = fade_in_ms > NL_MAX_FADE_MS ? NL_MAX_FADE_MS : fade_in_ms; s_fade_out = fade_out_ms > NL_MAX_FADE_MS ? NL_MAX_FADE_MS : fade_out_ms; + s_transition = transition_ms > NL_MAX_TRANSITION_MS ? NL_MAX_TRANSITION_MS : transition_ms; + s_wake_on_change = wake_on_change; } void nl_ctrl_boot(const nl_light_state_t *initial) @@ -62,6 +68,13 @@ nl_ctrl_err_t nl_ctrl_apply(const nl_light_change_t *c, uint32_t now_ms) n.mode = NL_MODE_WHITE; } + /* Weckschalter: a color/CT/brightness change arriving while the lamp is off switches it + * on, unless this same call also says "on": false explicitly - that always wins. Applied + * before the duration below so an off->on transition here uses fade_in like any other. */ + if (s_wake_on_change && !s_state.on && !n.on && !(c->has_on && !c->on) && + (c->has_color || c->has_ct || c->has_brightness)) + n.on = true; + if (c->has_transition) dur = c->transition_ms; else if (!s_state.on && n.on) @@ -69,7 +82,7 @@ nl_ctrl_err_t nl_ctrl_apply(const nl_light_change_t *c, uint32_t now_ms) else if (s_state.on && !n.on) dur = s_fade_out; else - dur = 0; + dur = s_transition; s_state = n; nl_engine_set(&s_state, dur); diff --git a/src/light/nl_control.h b/src/light/nl_control.h index 7dd2432..50c202d 100644 --- a/src/light/nl_control.h +++ b/src/light/nl_control.h @@ -32,9 +32,17 @@ typedef enum { NL_CTRL_ERR_TRANSITION } nl_ctrl_err_t; -/* The engine must be initialised before. Not thread safe: callers serialise access. */ -void nl_ctrl_init(const nl_board_t *board, uint16_t fade_in_ms, uint16_t fade_out_ms); -void nl_ctrl_set_fades(uint16_t fade_in_ms, uint16_t fade_out_ms); +/* The engine must be initialised before. Not thread safe: callers serialise access. + * + * transition_ms: default transition applied to a color/brightness/CT change while the lamp + * stays on and the caller did not send its own "transition" (fade_in/fade_out still take + * precedence when the lamp is switching on or off). + * wake_on_change: if true, a color/CT/brightness change arriving while the lamp is off + * switches it on (using fade_in), unless the same call also says "on": false. */ +void nl_ctrl_init(const nl_board_t *board, uint16_t fade_in_ms, uint16_t fade_out_ms, + uint16_t transition_ms, bool wake_on_change); +void nl_ctrl_set_options(uint16_t fade_in_ms, uint16_t fade_out_ms, uint16_t transition_ms, + bool wake_on_change); void nl_ctrl_boot(const nl_light_state_t *initial); nl_ctrl_err_t nl_ctrl_apply(const nl_light_change_t *c, uint32_t now_ms); void nl_ctrl_get(nl_light_state_t *out); diff --git a/src/net/nl_api.c b/src/net/nl_api.c index 888f8f6..a6ef00c 100644 --- a/src/net/nl_api.c +++ b/src/net/nl_api.c @@ -115,6 +115,11 @@ static cJSON *state_json(const nl_api_ctx_t *ctx) POB_NAMES[s.power_on_behaviour <= NL_POB_OFF ? s.power_on_behaviour : NL_POB_LAST]); cJSON_AddNumberToObject(o, "fade_in", s.fade_in_ms); cJSON_AddNumberToObject(o, "fade_out", s.fade_out_ms); + /* Named "transition_default", not "transition": the latter is already the per-call field + * above the settings section of this same request/response and a second field with the + * same key in one JSON object would collide with it. */ + cJSON_AddNumberToObject(o, "transition_default", s.transition_ms); + cJSON_AddBoolToObject(o, "wake_on_change", s.wake_on_change != 0); cap = cJSON_AddObjectToObject(o, "capabilities"); cJSON_AddBoolToObject(cap, "color", ctx->board->has_rgb); ct[0] = ctx->board->ct_min; @@ -193,6 +198,17 @@ static void handle_post_state(const nl_api_ctx_t *ctx, const nl_http_request_t * case 1: s.fade_out_ms = (uint16_t)v; settings = true; break; default: break; } + switch (get_int(j, "transition_default", 0, NL_MAX_TRANSITION_MS, &v)) { + case -1: err = "invalid transition_default"; goto out; + case 1: s.transition_ms = (uint16_t)v; settings = true; break; + default: break; + } + it = cJSON_GetObjectItemCaseSensitive(j, "wake_on_change"); + if (it) { + if (!cJSON_IsBool(it)) { err = "invalid wake_on_change"; goto out; } + s.wake_on_change = cJSON_IsTrue(it) ? 1u : 0u; + settings = true; + } if (light) { switch (ctx->apply(&ch)) { diff --git a/src/sys/nl_app_api.c b/src/sys/nl_app_api.c index f6d5fc4..1fdc7f9 100644 --- a/src/sys/nl_app_api.c +++ b/src/sys/nl_app_api.c @@ -58,7 +58,7 @@ static int save_settings(const nl_settings_t *s) memcpy(merged.wifi_pass, cur->wifi_pass, sizeof merged.wifi_pass); *cur = merged; } - nl_ctrl_set_fades(cur->fade_in_ms, cur->fade_out_ms); + nl_ctrl_set_options(cur->fade_in_ms, cur->fade_out_ms, cur->transition_ms, cur->wake_on_change != 0); memcpy(name, cur->name, sizeof name); nl_app_unlock(); if (name_changed) diff --git a/src/sys/nl_console.c b/src/sys/nl_console.c index 8317b4f..c3fc874 100644 --- a/src/sys/nl_console.c +++ b/src/sys/nl_console.c @@ -78,7 +78,7 @@ static void exec(const nl_cmd_t *c) nl_app_lock(); cfg->fade_in_ms = (uint16_t)c->arg[0]; cfg->fade_out_ms = (uint16_t)c->arg[1]; - nl_ctrl_set_fades(cfg->fade_in_ms, cfg->fade_out_ms); + nl_ctrl_set_options(cfg->fade_in_ms, cfg->fade_out_ms, cfg->transition_ms, cfg->wake_on_change != 0); nl_app_unlock(); printf(nl_app_save_settings() == 0 ? "ok\r\n" : "error: save failed\r\n"); print_state(); diff --git a/test/test_api.c b/test/test_api.c index 65014fb..ca25feb 100644 --- a/test/test_api.c +++ b/test/test_api.c @@ -55,6 +55,8 @@ static void test_get_state(void) TEST_ASSERT_EQUAL_STRING("last", cJSON_GetStringValue(field("power_on_behaviour"))); TEST_ASSERT_EQUAL_INT(700, field("fade_in")->valueint); TEST_ASSERT_EQUAL_INT(500, field("fade_out")->valueint); + TEST_ASSERT_EQUAL_INT(300, field("transition_default")->valueint); + TEST_ASSERT_TRUE(cJSON_IsFalse(field("wake_on_change"))); cap = field("capabilities"); TEST_ASSERT_TRUE(cJSON_IsTrue(cJSON_GetObjectItem(cap, "color"))); TEST_ASSERT_EQUAL_INT(2700, cJSON_GetArrayItem(cJSON_GetObjectItem(cap, "color_temp"), 0)->valueint); @@ -114,6 +116,8 @@ static void test_post_validation(void) {"{\"power_on_behaviour\":\"maybe\"}", "invalid power_on_behaviour"}, {"{\"fade_in\":10001}", "invalid fade_in"}, {"{\"fade_out\":-1}", "invalid fade_out"}, + {"{\"transition_default\":60001}", "invalid transition_default"}, + {"{\"wake_on_change\":\"ja\"}", "invalid wake_on_change"}, }; for (size_t i = 0; i < sizeof CASES / sizeof CASES[0]; i++) { call(NL_HTTP_POST, "/api/v1/device/state", NULL, CASES[i].body); @@ -151,6 +155,33 @@ static void test_post_settings_fields(void) TEST_ASSERT_EQUAL_STRING("on", cJSON_GetStringValue(field("power_on_behaviour"))); } +static void test_post_transition_default_and_wake(void) +{ + call(NL_HTTP_POST, "/api/v1/device/state", NULL, + "{\"transition_default\":1200,\"wake_on_change\":true}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_INT(1, fake_save_calls); + TEST_ASSERT_EQUAL_UINT16(1200, fake_settings.transition_ms); + TEST_ASSERT_EQUAL_UINT8(1, fake_settings.wake_on_change); + TEST_ASSERT_EQUAL_INT(1200, field("transition_default")->valueint); + TEST_ASSERT_TRUE(cJSON_IsTrue(field("wake_on_change"))); +} + +/* A colour and a per-call transition travel in the same request - the light change is + applied, and nothing is treated as a settings write. */ +static void test_post_color_with_transition(void) +{ + call(NL_HTTP_POST, "/api/v1/device/state", NULL, + "{\"color\":{\"r\":255,\"g\":0,\"b\":0},\"transition\":2000}"); + TEST_ASSERT_EQUAL_INT(200, resp.status); + TEST_ASSERT_EQUAL_INT(1, fake_apply_calls); + TEST_ASSERT_EQUAL_INT(0, fake_save_calls); + TEST_ASSERT_TRUE(fake_last_change.has_transition); + TEST_ASSERT_EQUAL_UINT32(2000, fake_last_change.transition_ms); + TEST_ASSERT_TRUE(fake_last_change.has_color); + TEST_ASSERT_EQUAL_UINT8(255, fake_last_change.r); +} + static void test_post_save_failure(void) { fake_save_result = -1; @@ -364,6 +395,8 @@ int main(void) RUN_TEST(test_post_validation); RUN_TEST(test_post_control_errors); RUN_TEST(test_post_settings_fields); + RUN_TEST(test_post_transition_default_and_wake); + RUN_TEST(test_post_color_with_transition); RUN_TEST(test_post_save_failure); RUN_TEST(test_auth_all_protects_device_state); RUN_TEST(test_auth_too_long_is_unauthenticated); diff --git a/test/test_control.c b/test/test_control.c index 9adb124..4bfbd28 100644 --- a/test/test_control.c +++ b/test/test_control.c @@ -10,18 +10,24 @@ static void out(const uint8_t ch[NL_CH_COUNT], void *ctx) (void)ctx; } -static void start(const char *board_id, bool on) +static void start_opts(const char *board_id, bool on, uint16_t transition_ms, bool wake) { const nl_board_t *b = nl_board_find(board_id); nl_engine_cfg_t cfg = {b->ct_min, b->ct_max, out, NULL}; nl_engine_init(&cfg); - nl_ctrl_init(b, 500, 800); + nl_ctrl_init(b, 500, 800, transition_ms, wake); nl_light_state_t s = nl_boot_default_state(b); s.on = on; nl_ctrl_boot(&s); nl_engine_tick(10000); /* finish a possible boot fade */ } +/* Most tests want the historical behaviour: no default transition, no wake switch. */ +static void start(const char *board_id, bool on) +{ + start_opts(board_id, on, 0, false); +} + static uint16_t bri(void) { nl_visual_t v; @@ -188,13 +194,112 @@ static void test_boot_fades_in_when_on(void) const nl_board_t *b = nl_board_find("evk"); nl_engine_cfg_t cfg = {b->ct_min, b->ct_max, out, NULL}; nl_engine_init(&cfg); - nl_ctrl_init(b, 500, 800); + nl_ctrl_init(b, 500, 800, 0, false); nl_light_state_t s = nl_boot_default_state(b); nl_ctrl_boot(&s); nl_engine_tick(250); TEST_ASSERT_EQUAL_UINT16(5000, bri()); } +static void test_default_transition_applies_while_on(void) +{ + start_opts("evk", true, 400, false); + nl_light_change_t c = chg(); + c.has_brightness = true; + c.brightness = 50; + nl_ctrl_apply(&c, 0); + TEST_ASSERT_EQUAL_UINT16(10000, bri()); /* not instant any more */ + nl_engine_tick(200); + TEST_ASSERT_EQUAL_UINT16(7500, bri()); + nl_engine_tick(200); + TEST_ASSERT_EQUAL_UINT16(5000, bri()); +} + +static void test_explicit_transition_beats_default(void) +{ + start_opts("evk", true, 5000, false); + nl_light_change_t c = chg(); + c.has_brightness = true; + c.brightness = 50; + c.has_transition = true; + c.transition_ms = 100; + nl_ctrl_apply(&c, 0); + nl_engine_tick(100); + TEST_ASSERT_EQUAL_UINT16(5000, bri()); +} + +static void test_fades_beat_default_transition_when_switching(void) +{ + start_opts("evk", false, 5000, false); + nl_light_change_t c = chg(); + c.has_on = true; + c.on = true; + nl_ctrl_apply(&c, 0); + nl_engine_tick(500); /* fade_in, not the 5 s default */ + TEST_ASSERT_EQUAL_UINT16(10000, bri()); + + c = chg(); + c.has_on = true; + c.on = false; + nl_ctrl_apply(&c, 1000); + nl_engine_tick(800); /* fade_out */ + TEST_ASSERT_EQUAL_UINT16(0, bri()); +} + +static void test_wake_switch_off_leaves_lamp_off(void) +{ + start_opts("evk", false, 300, false); + nl_light_change_t c = chg(); + c.has_ct = true; + c.ct = 3000; + nl_ctrl_apply(&c, 0); + nl_light_state_t s; + nl_ctrl_get(&s); + TEST_ASSERT_FALSE(s.on); +} + +static void test_wake_switch_turns_lamp_on_with_fade_in(void) +{ + start_opts("evk", false, 300, true); + nl_light_change_t c = chg(); + c.has_ct = true; + c.ct = 3000; + nl_ctrl_apply(&c, 0); + nl_light_state_t s; + nl_ctrl_get(&s); + TEST_ASSERT_TRUE(s.on); + nl_engine_tick(250); /* fade_in = 500, not the 300 ms default transition */ + TEST_ASSERT_EQUAL_UINT16(5000, bri()); +} + +static void test_wake_switch_also_reacts_to_brightness(void) +{ + start_opts("evk", false, 300, true); + nl_light_change_t c = chg(); + c.has_brightness = true; + c.brightness = 40; + nl_ctrl_apply(&c, 0); + nl_light_state_t s; + nl_ctrl_get(&s); + TEST_ASSERT_TRUE(s.on); + TEST_ASSERT_EQUAL_UINT8(40, s.brightness); +} + +static void test_explicit_off_beats_wake_switch(void) +{ + start_opts("evk", false, 300, true); + nl_light_change_t c = chg(); + c.has_on = true; + c.on = false; + c.has_brightness = true; + c.brightness = 40; + nl_ctrl_apply(&c, 0); + nl_light_state_t s; + nl_ctrl_get(&s); + TEST_ASSERT_FALSE(s.on); + TEST_ASSERT_EQUAL_UINT8(40, s.brightness); +} + int main(void) { UNITY_BEGIN(); @@ -209,5 +314,12 @@ int main(void) RUN_TEST(test_persist_debounce); RUN_TEST(test_change_during_write_stays_dirty); RUN_TEST(test_boot_fades_in_when_on); + RUN_TEST(test_default_transition_applies_while_on); + RUN_TEST(test_explicit_transition_beats_default); + RUN_TEST(test_fades_beat_default_transition_when_switching); + RUN_TEST(test_wake_switch_off_leaves_lamp_off); + RUN_TEST(test_wake_switch_turns_lamp_on_with_fade_in); + RUN_TEST(test_wake_switch_also_reacts_to_brightness); + RUN_TEST(test_explicit_off_beats_wake_switch); return UNITY_END(); } diff --git a/test/test_settings.c b/test/test_settings.c index 4ccbbfe..37d6cc6 100644 --- a/test/test_settings.c +++ b/test/test_settings.c @@ -42,6 +42,8 @@ static void test_defaults(void) TEST_ASSERT_EQUAL_UINT8(NL_POB_LAST, s.power_on_behaviour); TEST_ASSERT_EQUAL_UINT16(500, s.fade_in_ms); TEST_ASSERT_EQUAL_UINT16(500, s.fade_out_ms); + TEST_ASSERT_EQUAL_UINT16(300, s.transition_ms); + TEST_ASSERT_EQUAL_UINT8(0, s.wake_on_change); } static void test_save_and_load(void) @@ -108,6 +110,19 @@ static void test_load_accepts_shorter_record(void) TEST_ASSERT_EQUAL_UINT16(500, out.fade_in_ms); TEST_ASSERT_EQUAL_UINT16(500, out.fade_out_ms); TEST_ASSERT_EQUAL_UINT8(0, out.ota_pending); + TEST_ASSERT_EQUAL_UINT16(300, out.transition_ms); + TEST_ASSERT_EQUAL_UINT8(0, out.wake_on_change); +} + +static void test_transition_and_wake_roundtrip(void) +{ + nl_settings_t in = sample("neu"), out; + in.transition_ms = 1500; + in.wake_on_change = 1; + TEST_ASSERT_EQUAL_INT(0, nl_settings_save(&in)); + TEST_ASSERT_TRUE(nl_settings_load(&out)); + TEST_ASSERT_EQUAL_UINT16(1500, out.transition_ms); + TEST_ASSERT_EQUAL_UINT8(1, out.wake_on_change); } static void test_load_rejects_longer_record(void) @@ -171,6 +186,7 @@ int main(void) { UNITY_BEGIN(); RUN_TEST(test_defaults); + RUN_TEST(test_transition_and_wake_roundtrip); RUN_TEST(test_save_and_load); RUN_TEST(test_alternates_slots); RUN_TEST(test_power_loss_keeps_previous); From e50659d954b4202ca5b7438a80793c9b0d77beb6 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 03:15:08 +0200 Subject: [PATCH 106/150] feat(tools): Signierwerkzeug fuer Migrationsimages nl_sign.py signiert/prueft W600-Images im RSA-4096-PKCS#1-v1.5-Format der alten Klyqa-Firmware (Check_OTA_Image() in qc_ssl_layer.c): SHA256 ueber die gesamte Datei, angehaengt als exakt 512 Byte - derselbe Block wie der ninlight-Trailer, daher die Ablehnung bereits getrailerter Eingaben. Unterstuetzt sowohl cryptography als auch pycryptodome/pycryptodomex als Backend. Gegen das echte Klyqa-Schluesselpaar aus ~/workspace/fw-sign bitgleich verifiziert. tools/test_host.sh haengt jetzt die Python-Tests aus tools/mkimage/tests und tools/station/tests an ctest an. --- tools/mkimage/nl_sign.py | 211 ++++++++++++++++++++++++++++ tools/mkimage/tests/test_nl_sign.py | 86 ++++++++++++ tools/test_host.sh | 6 + 3 files changed, 303 insertions(+) create mode 100644 tools/mkimage/nl_sign.py create mode 100644 tools/mkimage/tests/test_nl_sign.py diff --git a/tools/mkimage/nl_sign.py b/tools/mkimage/nl_sign.py new file mode 100644 index 0000000..5b5e6e4 --- /dev/null +++ b/tools/mkimage/nl_sign.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +"""Signiert ein W600-Firmwareimage so, wie es die alte Klyqa-Firmware erwartet. + +Format: RSA-4096 PKCS#1 v1.5 ueber den SHA256 der GESAMTEN Datei, angehaengt als +exakt 512 Byte. Belegt durch Check_OTA_Image() in qc_ssl_layer.c der alten Firmware +und durch sign_w600_fw.py im Schluesselverzeichnis. + +Die 512 Byte belegen denselben Block wie der ninlight-Trailer. Ein Migrationsimage +traegt deshalb KEINEN Trailer - die alte Firmware prueft die Signatur, die neue +prueft an dieser Stelle nichts mehr, weil sie dann schon laeuft. + +Muss unter Python 3.8.10 laufen (siehe nl_mkimage.py fuer den Hintergrund): keine +`str | None`-Annotationen ohne `from __future__ import annotations`, kein +`list[str]` zur Laufzeit. +""" +from __future__ import annotations + +import argparse +import sys +from pathlib import Path +from typing import NamedTuple + +SIG_LEN = 512 +TRAILER_MAGIC = b"NINL" +KEY_BITS = 4096 + + +class SignError(Exception): + pass + + +class TestKey(NamedTuple): + private_pem: bytes + public_der: bytes + + +def _backend(): + """Liefert ein einheitliches (sign, verify, keygen)-Tripel, egal ob `cryptography` + oder `pycryptodome`/`pycryptodomex` installiert ist - auf keinem Rechner ist sicher + dieselbe Bibliothek vorhanden.""" + try: + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import padding, rsa + from cryptography.exceptions import InvalidSignature + + def sign(private_pem: bytes, data: bytes) -> bytes: + key = serialization.load_pem_private_key(private_pem, password=None) + if key.key_size != KEY_BITS: + raise SignError( + f"Schluessel hat {key.key_size} Bit, benoetigt werden {KEY_BITS}") + return key.sign(data, padding.PKCS1v15(), hashes.SHA256()) + + def verify(public_key_bytes: bytes, data: bytes, signature: bytes) -> bool: + # akzeptiert sowohl DER als auch PEM, wie im CLI-Interface vorgesehen + try: + key = serialization.load_der_public_key(public_key_bytes) + except ValueError: + key = serialization.load_pem_public_key(public_key_bytes) + try: + key.verify(signature, data, padding.PKCS1v15(), hashes.SHA256()) + return True + except InvalidSignature: + return False + + def keygen() -> TestKey: + key = rsa.generate_private_key(public_exponent=65537, key_size=KEY_BITS) + private_pem = key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption(), + ) + public_der = key.public_key().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) + return TestKey(private_pem, public_der) + + return sign, verify, keygen + except ImportError: + pass + + try: + try: + from Cryptodome.Signature import pkcs1_15 + from Cryptodome.Hash import SHA256 + from Cryptodome.PublicKey import RSA + except ImportError: + from Crypto.Signature import pkcs1_15 + from Crypto.Hash import SHA256 + from Crypto.PublicKey import RSA + + def sign(private_pem: bytes, data: bytes) -> bytes: + key = RSA.import_key(private_pem) + if key.size_in_bits() != KEY_BITS: + raise SignError( + f"Schluessel hat {key.size_in_bits()} Bit, benoetigt werden {KEY_BITS}") + digest = SHA256.new(data) + return pkcs1_15.new(key).sign(digest) + + def verify(public_der: bytes, data: bytes, signature: bytes) -> bool: + key = RSA.import_key(public_der) + digest = SHA256.new(data) + try: + pkcs1_15.new(key).verify(digest, signature) + return True + except (ValueError, TypeError): + return False + + def keygen() -> TestKey: + key = RSA.generate(KEY_BITS) + private_pem = key.export_key("PEM") + public_der = key.publickey().export_key("DER") + return TestKey(private_pem, public_der) + + return sign, verify, keygen + except ImportError: + pass + + raise SignError( + "weder 'cryptography' noch 'pycryptodome'/'pycryptodomex' ist installiert") + + +def generate_test_key() -> TestKey: + """Nur fuer Tests: erzeugt einen frischen 4096-Bit-Schluessel im Speicher. + Schreibt niemals eine Datei.""" + _sign, _verify, keygen = _backend() + return keygen() + + +def _check_raw(raw: bytes) -> None: + if len(raw) < 1024: + raise SignError(f"Eingabe ist mit {len(raw)} Byte kuerzer als die Mindestlaenge 1024") + if len(raw) % 4 != 0: + raise SignError(f"Eingabe ist mit {len(raw)} Byte nicht durch 4 teilbar") + if raw[-SIG_LEN:][:4] == TRAILER_MAGIC: + raise SignError( + "Eingabe traegt bereits einen ninlight-Trailer - das ist ein OTA-Image, " + "kein Rohbild") + + +def sign_image(raw: bytes, key_pem: bytes) -> bytes: + """Haengt eine 512-Byte-PKCS#1-v1.5-Signatur ueber den SHA256 von `raw` an.""" + _check_raw(raw) + sign, _verify, _keygen = _backend() + signature = sign(key_pem, raw) + if len(signature) != SIG_LEN: + raise SignError( + f"Signatur ist {len(signature)} Byte lang, erwartet werden {SIG_LEN}") + return raw + signature + + +def verify_image(signed: bytes, pub_der: bytes) -> bool: + """Prueft eine mit `sign_image` erzeugte Signatur. Gibt bei ungueltiger Signatur + False zurueck; wirft nur bei echten Formatfehlern.""" + if len(signed) <= SIG_LEN: + raise SignError(f"Eingabe ist mit {len(signed)} Byte zu kurz fuer eine Signatur") + payload, signature = signed[:-SIG_LEN], signed[-SIG_LEN:] + _sign, verify, _keygen = _backend() + return verify(pub_der, payload, signature) + + +def _cmd_sign(args: argparse.Namespace) -> int: + key_pem = Path(args.key).read_bytes() + raw = Path(args.in_).read_bytes() + try: + signed = sign_image(raw, key_pem) + except SignError as e: + print(f"error: {e}", file=sys.stderr) + return 1 + Path(args.out).write_bytes(signed) + print(f"{args.out} ({len(signed)} bytes)") + return 0 + + +def _cmd_verify(args: argparse.Namespace) -> int: + pub_der = Path(args.pub).read_bytes() + signed = Path(args.in_).read_bytes() + try: + ok = verify_image(signed, pub_der) + except SignError as e: + print(f"error: {e}", file=sys.stderr) + return 1 + if ok: + print(f"{args.in_}: signature valid") + return 0 + print(f"error: {args.in_}: signature invalid", file=sys.stderr) + return 1 + + +def main() -> None: + p = argparse.ArgumentParser( + description="signs/verifies a W600 image the way the old Klyqa firmware expects") + sub = p.add_subparsers(dest="command", required=True) + + p_sign = sub.add_parser("sign", help="sign a raw _gz.img") + p_sign.add_argument("--key", required=True, help="private key, PEM") + p_sign.add_argument("--in", dest="in_", required=True, help="input _gz.img") + p_sign.add_argument("--out", required=True, help="output signed image") + p_sign.set_defaults(func=_cmd_sign) + + p_verify = sub.add_parser("verify", help="verify a signed image") + p_verify.add_argument("--pub", required=True, help="public key, PEM or DER") + p_verify.add_argument("--in", dest="in_", required=True, help="signed image to check") + p_verify.set_defaults(func=_cmd_verify) + + args = p.parse_args() + raise SystemExit(args.func(args)) + + +if __name__ == "__main__": + main() diff --git a/tools/mkimage/tests/test_nl_sign.py b/tools/mkimage/tests/test_nl_sign.py new file mode 100644 index 0000000..f1adbe0 --- /dev/null +++ b/tools/mkimage/tests/test_nl_sign.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +import pytest + +HERE = Path(__file__).resolve().parent +TOOL = HERE.parent / "nl_sign.py" +sys.path.insert(0, str(HERE.parent)) + +import nl_sign # noqa: E402 + + +@pytest.fixture(scope="module") +def key(): + """Ein eigener 4096-Bit-Schluessel, damit kein echter Signierschluessel in die Tests + gerät. Einmal pro Modul, weil das Erzeugen ein paar Sekunden dauert.""" + return nl_sign.generate_test_key() + + +def test_signature_is_512_bytes(key, tmp_path): + raw = b"\xa5" * 4096 + signed = nl_sign.sign_image(raw, key.private_pem) + assert len(signed) == len(raw) + 512 + assert signed[: len(raw)] == raw + + +def test_signature_verifies(key): + raw = b"firmware" * 512 + signed = nl_sign.sign_image(raw, key.private_pem) + assert nl_sign.verify_image(signed, key.public_der) is True + + +def test_modified_payload_fails_verification(key): + raw = bytearray(b"firmware" * 512) + signed = bytearray(nl_sign.sign_image(bytes(raw), key.private_pem)) + signed[17] ^= 0xFF + assert nl_sign.verify_image(bytes(signed), key.public_der) is False + + +def test_signing_is_deterministic(key): + raw = b"x" * 1024 + assert nl_sign.sign_image(raw, key.private_pem) == nl_sign.sign_image(raw, key.private_pem) + + +def test_cli_roundtrip(key, tmp_path): + src = tmp_path / "in_gz.img" + dst = tmp_path / "out.img" + pub = tmp_path / "pub.der" + priv = tmp_path / "key.pem" + src.write_bytes(b"\x01\x02\x03" * 1000) + priv.write_bytes(key.private_pem) + pub.write_bytes(key.public_der) + subprocess.run([sys.executable, str(TOOL), "sign", "--key", str(priv), + "--in", str(src), "--out", str(dst)], check=True) + assert dst.stat().st_size == src.stat().st_size + 512 + subprocess.run([sys.executable, str(TOOL), "verify", "--pub", str(pub), + "--in", str(dst)], check=True) + + +def test_cli_verify_rejects_tampered(key, tmp_path): + src = tmp_path / "in_gz.img" + dst = tmp_path / "out.img" + pub = tmp_path / "pub.der" + priv = tmp_path / "key.pem" + src.write_bytes(b"\x07" * 2000) + priv.write_bytes(key.private_pem) + pub.write_bytes(key.public_der) + subprocess.run([sys.executable, str(TOOL), "sign", "--key", str(priv), + "--in", str(src), "--out", str(dst)], check=True) + data = bytearray(dst.read_bytes()) + data[0] ^= 0x01 + dst.write_bytes(bytes(data)) + r = subprocess.run([sys.executable, str(TOOL), "verify", "--pub", str(pub), + "--in", str(dst)]) + assert r.returncode != 0 + + +def test_refuses_already_signed_looking_input(key, tmp_path): + """Ein Image mit ninlight-Trailer ist ein OTA-Image, kein Rohbild - zweimal 512 Byte + anzuhaengen ergaebe eine Datei, die die alte Firmware ablehnt.""" + raw = b"\x00" * 1024 + b"NINL" + b"\x01" + b"\x00" * 507 + with pytest.raises(nl_sign.SignError): + nl_sign.sign_image(raw, key.private_pem) diff --git a/tools/test_host.sh b/tools/test_host.sh index 62edf66..bd09054 100755 --- a/tools/test_host.sh +++ b/tools/test_host.sh @@ -5,3 +5,9 @@ ROOT=$(cd "$(dirname "$0")/.." && pwd) cmake -S "$ROOT/test" -B "$ROOT/build/host" -G Ninja >/dev/null cmake --build "$ROOT/build/host" ctest --test-dir "$ROOT/build/host" --output-on-failure "$@" + +if python3 -c 'import pytest' 2>/dev/null; then + python3 -m pytest "$ROOT/tools/mkimage/tests" "$ROOT/tools/station/tests" -q +else + echo "Hinweis: pytest fehlt - Python-Tests uebersprungen" >&2 +fi From 78cf01ca6cb2093ff411f64142e3117ad3caa4ea Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 03:24:02 +0200 Subject: [PATCH 107/150] fix(tools): SignError statt roher ValueError bei kaputtem Schluesselmaterial nl_sign.py verify/sign gaben bei syntaktisch ungueltigem --pub/--key-Inhalt (falsches DER/PEM-Format) bisher eine unbehandelte ValueError (pycryptodome teils IndexError/TypeError) an den Aufrufer durch - die CLI brach dadurch mit Python-Traceback ab statt mit der vorgesehenen "error: ..."-Meldung und Exitcode 1. Beide Backends (cryptography und pycryptodome/pycryptodomex) kapseln das Einlesen des privaten wie des oeffentlichen Schluessels jetzt in try/except und werfen SignError mit einer festen, schluesselinhaltsfreien Meldung (via "from None", damit die urspruengliche Bibliotheksausnahme nicht implizit mitgeschleppt wird). Ein ungueltiger Schluessel kann dadurch nie zu verify_image() == True fuehren. Vier neue Tests in test_nl_sign.py: je ein Modul- und CLI-Test fuer kaputtes --pub und kaputtes --key, die SignError bzw. Exitcode != 0 ohne Traceback auf stderr festnageln. --- tools/mkimage/nl_sign.py | 24 ++++++++++++--- tools/mkimage/tests/test_nl_sign.py | 46 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/tools/mkimage/nl_sign.py b/tools/mkimage/nl_sign.py index 5b5e6e4..3a2681d 100644 --- a/tools/mkimage/nl_sign.py +++ b/tools/mkimage/nl_sign.py @@ -44,7 +44,11 @@ def _backend(): from cryptography.exceptions import InvalidSignature def sign(private_pem: bytes, data: bytes) -> bytes: - key = serialization.load_pem_private_key(private_pem, password=None) + try: + key = serialization.load_pem_private_key(private_pem, password=None) + except (ValueError, TypeError): + raise SignError( + "privater Schluessel ist kein gueltiger PEM-Schluessel") from None if key.key_size != KEY_BITS: raise SignError( f"Schluessel hat {key.key_size} Bit, benoetigt werden {KEY_BITS}") @@ -55,7 +59,11 @@ def _backend(): try: key = serialization.load_der_public_key(public_key_bytes) except ValueError: - key = serialization.load_pem_public_key(public_key_bytes) + try: + key = serialization.load_pem_public_key(public_key_bytes) + except (ValueError, TypeError): + raise SignError( + "oeffentlicher Schluessel ist weder gueltiges DER noch PEM") from None try: key.verify(signature, data, padding.PKCS1v15(), hashes.SHA256()) return True @@ -90,7 +98,11 @@ def _backend(): from Crypto.PublicKey import RSA def sign(private_pem: bytes, data: bytes) -> bytes: - key = RSA.import_key(private_pem) + try: + key = RSA.import_key(private_pem) + except (ValueError, TypeError, IndexError): + raise SignError( + "privater Schluessel ist kein gueltiger PEM/DER-Schluessel") from None if key.size_in_bits() != KEY_BITS: raise SignError( f"Schluessel hat {key.size_in_bits()} Bit, benoetigt werden {KEY_BITS}") @@ -98,7 +110,11 @@ def _backend(): return pkcs1_15.new(key).sign(digest) def verify(public_der: bytes, data: bytes, signature: bytes) -> bool: - key = RSA.import_key(public_der) + try: + key = RSA.import_key(public_der) + except (ValueError, TypeError, IndexError): + raise SignError( + "oeffentlicher Schluessel ist kein gueltiger PEM/DER-Schluessel") from None digest = SHA256.new(data) try: pkcs1_15.new(key).verify(digest, signature) diff --git a/tools/mkimage/tests/test_nl_sign.py b/tools/mkimage/tests/test_nl_sign.py index f1adbe0..4a441d8 100644 --- a/tools/mkimage/tests/test_nl_sign.py +++ b/tools/mkimage/tests/test_nl_sign.py @@ -84,3 +84,49 @@ def test_refuses_already_signed_looking_input(key, tmp_path): raw = b"\x00" * 1024 + b"NINL" + b"\x01" + b"\x00" * 507 with pytest.raises(nl_sign.SignError): nl_sign.sign_image(raw, key.private_pem) + + +def test_verify_rejects_garbage_pub_key(key): + """Ein kaputter --pub-Inhalt (kein gueltiges DER/PEM) muss als SignError gemeldet + werden, nicht als roher ValueError/Traceback - und darf niemals True ergeben.""" + raw = b"x" * 1024 + signed = nl_sign.sign_image(raw, key.private_pem) + with pytest.raises(nl_sign.SignError): + nl_sign.verify_image(signed, b"das ist definitiv kein Schluessel") + + +def test_sign_rejects_garbage_private_key(): + """Ein kaputter --key-Inhalt muss ebenso als SignError gemeldet werden.""" + raw = b"x" * 1024 + with pytest.raises(nl_sign.SignError): + nl_sign.sign_image(raw, b"das ist definitiv kein Schluessel") + + +def test_cli_verify_rejects_garbage_pub_key(key, tmp_path): + src = tmp_path / "in_gz.img" + dst = tmp_path / "out.img" + pub = tmp_path / "pub.der" + priv = tmp_path / "key.pem" + src.write_bytes(b"\x01\x02\x03" * 1000) + priv.write_bytes(key.private_pem) + pub.write_bytes(b"das ist definitiv kein Schluessel") + subprocess.run([sys.executable, str(TOOL), "sign", "--key", str(priv), + "--in", str(src), "--out", str(dst)], check=True) + r = subprocess.run([sys.executable, str(TOOL), "verify", "--pub", str(pub), + "--in", str(dst)], capture_output=True, text=True) + assert r.returncode != 0 + assert "Traceback" not in r.stderr + assert "error:" in r.stderr.lower() + + +def test_cli_sign_rejects_garbage_private_key(tmp_path): + src = tmp_path / "in_gz.img" + dst = tmp_path / "out.img" + priv = tmp_path / "key.pem" + src.write_bytes(b"\x01\x02\x03" * 1000) + priv.write_bytes(b"das ist definitiv kein Schluessel") + r = subprocess.run([sys.executable, str(TOOL), "sign", "--key", str(priv), + "--in", str(src), "--out", str(dst)], capture_output=True, text=True) + assert r.returncode != 0 + assert "Traceback" not in r.stderr + assert "error:" in r.stderr.lower() From e8971649600f5a4851a69058dce7ec637f0a789d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 17:48:56 +0200 Subject: [PATCH 108/150] feat(station): Produkt-ID-Tabelle der alten Klyqa-Firmware --- tools/station/products.py | 65 ++++++++++++++++++++++++++ tools/station/tests/test_products.py | 70 ++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tools/station/products.py create mode 100644 tools/station/tests/test_products.py diff --git a/tools/station/products.py b/tools/station/products.py new file mode 100644 index 0000000..1d0d8c3 --- /dev/null +++ b/tools/station/products.py @@ -0,0 +1,65 @@ +"""Produkt-IDs der alten Klyqa-Firmware und ihre Zuordnung zu ninlight-Zielen. + +Vollstaendig uebernommen aus qcl_product_info.c der alten Firmware +(~/workspace/qcx-w600-firmware). Der "-dev"-Anhang entscheidet zugleich ueber den +Signierschluessel: Lampen mit Dev-Produkt-ID akzeptieren nur den Entwicklungs-, +Produktionslampen nur den Produktionsschluessel. +""" +import re +from collections import namedtuple + +Product = namedtuple("Product", "product_id target variant name") +ParsedRequest = namedtuple("ParsedRequest", "product_id lamp_version product") + +# (Produkt-ID ohne "@", Ziel, Anzeigename) - die Dev-Variante wird darunter erzeugt. +_BASE = [ + ("klyqa.lighting.rgb-cw-ww.e27", "e27_rgb", "Klyqa E27 RGB CW/WW"), + ("klyqa.lighting.rgb-cw-ww.e14", "e14_rgb", "Klyqa E14 RGB CW/WW"), + ("klyqa.lighting.rgb-cw-ww.gu10", "gu10_rgb", "Klyqa GU10 RGB CW/WW"), + ("klyqa.lighting.cw-ww.e27", "e27_cw", "Klyqa E27 CW/WW"), + ("klyqa.lighting.cw-ww.e14", "e14_cw", "Klyqa E14 CW/WW"), + ("klyqa.lighting.cw-ww.gu10", "gu10_cw", "Klyqa GU10 CW/WW"), + ("klyqa.lighting.cw-ww.g95", "g95_cw", "Klyqa G95 Vintage CW/WW"), +] + +_UNSAFE = re.compile(r"[^0-9A-Za-z+\-_.]") +# Die Produkt-ID darf kein "_" enthalten - daran trennen wir Version und ID. +_PATH = re.compile(r"^/qcx_firmware_([^/_]+)_([^/_]+)_ota_light\.img$") + + +def websafe(product_id): + """Ersetzt jedes Zeichen ausserhalb [0-9A-Za-z+-_.] durch '-', wie BuildOTAURL() + in qc_ota.c der alten Firmware. Praktisch betrifft das nur das fuehrende '@'.""" + return _UNSAFE.sub("-", product_id) + + +def _build(): + t = {} + for pid, target, name in _BASE: + for suffix, variant in (("", "prod"), ("-dev", "dev")): + key = websafe("@" + pid + suffix) + t[key] = Product(key, target, variant, name) + # Das EVK hat nur eine Produktions-ID. Im Dev-Build meldet es sich laut + # qcl_product_info.c als "@klyqa.lighting.rgb-cw-ww.e27-dev" - die EVK-Zeile ist dort + # auskommentiert und durch die E27 ersetzt. Ein dev-signiertes EVK ist damit von einer + # dev-signierten E27 RGB NICHT unterscheidbar und bekommt deren Image. Das EVK wird + # ohnehin seriell geflasht; die Zeile steht nur der Vollstaendigkeit halber hier. + key = websafe("@klyqa.lighting.w600.evk1.2") + t[key] = Product(key, "evk", "prod", "W600 EVK v1.2") + return t + + +PRODUCTS = _build() + + +def parse_request(path): + """Zerlegt einen angefragten Pfad. Gibt None zurueck, wenn er nicht dem Muster der + alten Firmware entspricht, sonst ParsedRequest - dessen .product ist None, wenn die + Produkt-ID unbekannt ist. Unbekannt heisst 404, nicht raten.""" + m = _PATH.match(path) + if not m: + return None + pid, version = m.group(1), m.group(2) + if pid != websafe(pid) or "/" in pid or ".." in pid.split("."): + return None + return ParsedRequest(pid, version, PRODUCTS.get(pid)) diff --git a/tools/station/tests/test_products.py b/tools/station/tests/test_products.py new file mode 100644 index 0000000..6a41ea3 --- /dev/null +++ b/tools/station/tests/test_products.py @@ -0,0 +1,70 @@ +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +import products # noqa: E402 + + +def test_table_is_complete(): + """Acht Geraete mal zwei Varianten, minus das EVK, das keine eigene Dev-ID hat.""" + assert len(products.PRODUCTS) == 15 + targets = {p.target for p in products.PRODUCTS.values()} + assert targets == {"evk", "e27_rgb", "e14_rgb", "gu10_rgb", + "e27_cw", "e14_cw", "gu10_cw", "g95_cw"} + + +def test_production_id_maps_to_prod_variant(): + p = products.PRODUCTS["-klyqa.lighting.rgb-cw-ww.gu10"] + assert (p.target, p.variant) == ("gu10_rgb", "prod") + assert p.name == "Klyqa GU10 RGB CW/WW" + + +def test_dev_suffix_selects_dev_variant(): + p = products.PRODUCTS["-klyqa.lighting.cw-ww.g95-dev"] + assert (p.target, p.variant) == ("g95_cw", "dev") + + +def test_parses_real_request_from_g95(): + """Am 2026-09-13 an einer echten G95 protokolliert.""" + r = products.parse_request( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1.1.1.1800-dev_ota_light.img") + assert r is not None + assert r.product_id == "-klyqa.lighting.cw-ww.g95-dev" + assert r.lamp_version == "V1.1.1.1800-dev" + assert r.product.target == "g95_cw" + assert r.product.variant == "dev" + + +def test_parses_real_request_from_gu10(): + r = products.parse_request( + "/qcx_firmware_-klyqa.lighting.rgb-cw-ww.gu10_V1.1.0.1138_ota_light.img") + assert r.product.target == "gu10_rgb" + assert r.product.variant == "prod" + assert r.lamp_version == "V1.1.0.1138" + + +def test_unknown_product_id_parses_but_has_no_product(): + r = products.parse_request( + "/qcx_firmware_-acme.lightbulb.xxl_V9.9_ota_light.img") + assert r is not None + assert r.product is None + assert r.product_id == "-acme.lightbulb.xxl" + + +def test_non_matching_paths(): + for p in ("/", "/ninlight_evk_ota.img", "/qcx_firmware_.img", + "/qcx_firmware_-klyqa.lighting.cw-ww.g95_ota_light.img", + "/x/qcx_firmware_-klyqa.lighting.cw-ww.g95_V1_ota_light.img"): + assert products.parse_request(p) is None, p + + +def test_path_traversal_is_not_parseable(): + assert products.parse_request( + "/qcx_firmware_../../etc/passwd_V1_ota_light.img") is None + + +def test_websafe_transform(): + assert products.websafe("@klyqa.lighting.cw-ww.g95") == "-klyqa.lighting.cw-ww.g95" + assert products.websafe("a b/c") == "a-b-c" + assert products.websafe("keep+all-_.09Az") == "keep+all-_.09Az" From b41ed510186c89459e44547a763633333c2bdb06 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 18:06:25 +0200 Subject: [PATCH 109/150] feat(station): Migrationsroute mit Horch- und Freigabephase server.py bedient jetzt zwei Modi ueber denselben Dienst: --mode/--mode-file/--control legen fest, ob ninlight__ota.img (Rettung) oder qcx_firmware__..._ota_light.img (Migration alter Klyqa-Lampen, via products.parse_request) ausgeliefert wird. Der Modus kommt wahlweise aus einer Datei statt einem Startparameter, damit Task 4 ihn spaeter ohne Dienst-Neustart umschalten kann. Die Steuerdatei control.json (Phase auto/listen/armed) wird bei jeder Anfrage frisch gelesen und atomar mit 0644 geschrieben - eine fehlende oder kaputte Steuerdatei blockiert eine Migration nie, sondern faellt auf "auto" zurueck. Nach einer erfolgreichen Auslieferung in Phase "armed" faellt die Freigabe erst nach dem vollstaendigen Senden auf "listen" zurueck, nie davor, damit ein abgebrochener Transfer die Freigabe fuer den naechsten Bootversuch stehen laesst. status.json bekommt die Migrationsfelder aus Spec 5.2 (mode, phase, armed_target, last_seen). deploy.sh liefert products.py mit aus, sonst startet der Dienst nach dem naechsten Deploy mit ModuleNotFoundError; test_deploy.sh zieht die Sandbox-Dummy-Datei entsprechend nach. Neue Tests in test_server.py verwenden eigene server/migration_server-Fixtures samt Testklient (get/status/set_control/read_control/delete_control/write_raw_control), ohne die bestehende station-Fixture und ihre Tests anzufassen. --- tools/station/deploy.sh | 5 +- tools/station/server.py | 221 ++++++++++++++++++++++++++-- tools/station/tests/test_deploy.sh | 1 + tools/station/tests/test_server.py | 226 +++++++++++++++++++++++++++++ 4 files changed, 437 insertions(+), 16 deletions(-) diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index b98e345..eb03501 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -76,7 +76,7 @@ echo "== uebertragen ==" # ausgelieferte Verzeichnis. # -e: rsync soll fuer den Transport zum Pi denselben ssh wie oben benutzen. rsync -a -c --delete -e "$SSH_BIN" "$STAGE/" "$HOST:/tmp/ninlight-images/" -rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" +rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" # Pfade als Variablen mit Default: so kann ein Test die Installation gegen # ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen lassen. "$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" <<'REMOTE' @@ -113,9 +113,10 @@ else sudo rsync -a -c /tmp/ninlight-images/ "$ROOT_DIR/images/" fi sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" +sudo install -m 0644 /tmp/products.py "$ROOT_DIR/products.py" sudo install -m 0755 /tmp/ninlight-station "$BIN_PATH" sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH" -rm -rf /tmp/ninlight-images /tmp/server.py /tmp/ninlight-station /tmp/ninlight-station.service +rm -rf /tmp/ninlight-images /tmp/server.py /tmp/products.py /tmp/ninlight-station /tmp/ninlight-station.service sudo systemctl daemon-reload if systemctl is-active --quiet ninlight-station; then if sudo systemctl restart ninlight-station; then diff --git a/tools/station/server.py b/tools/station/server.py index e941fd3..3111748 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 -"""ninlight recovery station. +"""ninlight recovery/migration station. -Serves firmware images to lamps that look for a rescue station while booting. Deliberately -minimal: it hands out nothing but files whose name matches ninlight__ota.img, so a -stray file in the image directory can never leave the machine. +Serves firmware images to lamps that look for a rescue station while booting (mode +"recovery": ninlight__ota.img) or to old Klyqa lamps looking for their original +update server (mode "migration": qcx_firmware___ota_light.img). +Deliberately minimal: only files matching one of these two exact patterns ever leave the +machine, and only the pattern for the active mode is served. Standard library only - the Pi gets no extra packages. """ @@ -24,6 +26,13 @@ from datetime import datetime, timezone from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path +# products.py lives next to this file. sys.path[0] already covers that when server.py is +# run the normal way (as a script or via `python3 server.py ...`), but a test that loads +# this module through importlib.util.spec_from_file_location (see test_server.py's +# test_log_error_is_not_silenced_by_log_message) does not get that for free. +sys.path.insert(0, str(Path(__file__).resolve().parent)) +import products + TARGETS = ("evk", "e27_rgb", "e14_rgb", "gu10_rgb", "e27_cw", "e14_cw", "gu10_cw", "g95_cw") PATH_RE = re.compile(r"^/ninlight_(" + "|".join(TARGETS) + r")_ota\.img$") TRAILER_SIZE = 512 @@ -54,13 +63,21 @@ def trailer_version(path: Path) -> str: class Station: - def __init__(self, image_dir: Path, state_path: Path, stall_after: int | None) -> None: + def __init__(self, image_dir: Path, state_path: Path, stall_after: int | None, + mode: str = "recovery", control_path: Path | None = None) -> None: self.image_dir = image_dir self.state_path = state_path self.stall_after = stall_after + self.mode = mode + self.control_path = control_path or Path("/run/ninlight-station/control.json") self.since = now_iso() self.served = 0 self.recent: list[dict] = [] + # Zuletzt erkanntes Modell im Migrationsmodus, auch wenn (noch) nichts ausgeliefert + # wurde - das ist es, was die Anzeige gross darstellt (Spec 5.2). + self.last_seen: dict | None = None + self._manifest_mtime: float | None = None + self._manifest_version = "unbekannt" # Cumulative bytes already delivered to each client while --stall-after is active, # keyed by client IP. A lamp whose download stalls re-opens a fresh connection and # resumes with Range right where it left off, so a per-request counter would hand @@ -77,6 +94,64 @@ class Station: def image_path(self, target: str) -> Path: return self.image_dir / f"ninlight_{target}_ota.img" + def migrate_image_path(self, target: str, variant: str) -> Path: + return self.image_dir / "migrate" / f"{target}_{variant}.img" + + def manifest_version(self) -> str: + """Version aus /migrate/manifest.json, neu eingelesen bei jeder + Aenderung der Datei (mtime-Vergleich). Fehlt sie oder ist sie kaputt, ist die + Version "unbekannt" - die Migrationsimages selbst tragen keinen ninlight-Trailer, + aus dem der Server sie lesen koennte (Spec Abschnitt 4).""" + path = self.image_dir / "migrate" / "manifest.json" + try: + mtime = path.stat().st_mtime + except OSError: + self._manifest_mtime = None + self._manifest_version = "unbekannt" + return self._manifest_version + if mtime != self._manifest_mtime: + try: + data = json.loads(path.read_text()) + self._manifest_version = data.get("ninlight_version", "unbekannt") + except (OSError, ValueError): + self._manifest_version = "unbekannt" + self._manifest_mtime = mtime + return self._manifest_version + + def read_control(self): + """Phase und Freigabe. Fehlt die Datei oder ist sie unlesbar/kaputt, gilt "auto" - + eine fehlende Steuerdatei darf eine Migration nie blockieren, sondern hoechstens + bequemer machen als beabsichtigt. Bei jeder Anfrage frisch gelesen, damit + "ninlight-station arm" ohne Neustart des Dienstes wirkt.""" + try: + d = json.loads(self.control_path.read_text()) + except (OSError, ValueError): + return "auto", None + phase = d.get("phase") + if phase not in ("auto", "listen", "armed"): + return "auto", None + return phase, d.get("armed_target") + + def write_control(self, phase: str, armed_target: str | None) -> None: + with self.lock: + try: + self.control_path.parent.mkdir(parents=True, exist_ok=True) + fd, tmp = tempfile.mkstemp(dir=self.control_path.parent, prefix=".control-") + try: + with os.fdopen(fd, "w") as f: + json.dump({"phase": phase, "armed_target": armed_target}, f) + # Dasselbe Muster wie write_state() unten, aus demselben Grund: der + # Dienst laeuft als root, mkstemp() legt 0600 an, und ohne das + # explizite chmod() koennte das CLI die Steuerdatei nicht lesen. + os.chmod(tmp, 0o644) + os.replace(tmp, self.control_path) + except BaseException: + with contextlib.suppress(OSError): + os.unlink(tmp) + raise + except OSError as e: + print(f"warning: cannot write {self.control_path}: {e}", flush=True) + def reserve_stall_bytes(self, ip: str, n: int) -> int: """How many of the next `n` bytes this client may still receive under --stall-after. @@ -99,9 +174,37 @@ class Station: del self.recent[:-RECENT_MAX] self.write_state() + def record_migration(self, ip: str, target: str | None, variant: str | None, + product_id: str, lamp_version: str, product_name: str | None, + version: str, nbytes: int, status: str) -> None: + """Protokolliert eine Migrationsanfrage. Anders als record() oben wird hier auch + dann ein Eintrag angelegt (Status "listen"/"unknown"/"notfound"), wenn nichts + ausgeliefert wurde - das ist der ganze Zweck der Horchphase. last_seen wird nur bei + einer erkannten Produkt-ID gesetzt (target is not None); eine unbekannte Produkt-ID + hat kein Modell, das die Anzeige zeigen koennte.""" + with self.lock: + if status == "ok": + self.served += 1 + entry = {"ts": now_iso(), "ip": ip, "target": target, "variant": variant, + "product_id": product_id, "lamp_version": lamp_version, + "version": version, "bytes": nbytes, "status": status} + self.recent.append(entry) + del self.recent[:-RECENT_MAX] + if target is not None: + self.last_seen = {"product_id": product_id, "target": target, + "variant": variant, "product_name": product_name, + "lamp_version": lamp_version, "ip": ip, "ts": entry["ts"]} + self.write_state() + def write_state(self) -> None: with self.lock: - payload = {"since": self.since, "served": self.served, "recent": list(self.recent)} + payload = {"since": self.since, "mode": self.mode, "served": self.served, + "recent": list(self.recent)} + if self.mode == "migration": + phase, armed_target = self.read_control() + payload["phase"] = phase + payload["armed_target"] = armed_target + payload["last_seen"] = self.last_seen try: self.state_path.parent.mkdir(parents=True, exist_ok=True) fd, tmp = tempfile.mkstemp(dir=self.state_path.parent, prefix=".status-") @@ -172,6 +275,12 @@ class Handler(BaseHTTPRequestHandler): def do_GET(self) -> None: started = time.monotonic() + if self.station.mode == "migration": + self._do_get_migration(started) + else: + self._do_get_recovery(started) + + def _do_get_recovery(self, started: float) -> None: m = PATH_RE.match(self.path) if not m: self.reject(404, "not found") @@ -187,6 +296,66 @@ class Handler(BaseHTTPRequestHandler): self.log(404, 0, started) return + version = trailer_version(path) + sent, status = self._serve_file(path, started) + # Record only now that the transfer has actually finished (or failed): "ok" must + # never be written before the last byte is confirmed sent, or status.json can + # claim success for a device that never received the end of the image. + self.station.record(self.client_address[0], target, version, sent, status) + + def _do_get_migration(self, started: float) -> None: + parsed = products.parse_request(self.path) + if parsed is None: + # Ein fremder Pfad, kein qcx_firmware_*-Muster - kein Protokolleintrag in + # status.json, das waere nur Rauschen (Brief Schritt 3.4). + self.reject(404, "not found") + self.log(404, 0, started) + return + pid, lamp_version, product = parsed + ip = self.client_address[0] + if product is None: + self.station.record_migration(ip, None, None, pid, lamp_version, None, "?", 0, + "unknown") + self.reject(404, "unknown product") + self.log(404, 0, started) + return + + target, variant, name = product.target, product.variant, product.name + version = self.station.manifest_version() + phase, armed_target = self.station.read_control() + + if phase == "listen" or (phase == "armed" and armed_target != target): + # last_seen wird trotzdem gesetzt - das ist der ganze Zweck der Horchphase: + # die Anzeige soll zeigen, welches Modell da ist, auch wenn nichts rausgeht. + self.station.record_migration(ip, target, variant, pid, lamp_version, name, + version, 0, "listen") + self.reject(404, "listening only") + self.log(404, 0, started) + return + + path = self.station.migrate_image_path(target, variant) + if not path.is_file(): + self.station.record_migration(ip, target, variant, pid, lamp_version, name, + version, 0, "notfound") + self.reject(404, "no image for this device") + self.log(404, 0, started) + return + + sent, status = self._serve_file(path, started) + if phase == "armed" and status == "ok": + # Erst nach dem vollstaendigen Senden zurueck auf "listen" - _serve_file() ist + # an dieser Stelle bereits fertig. Bricht der Transfer ab (status == "aborted"), + # bleibt die Freigabe stehen, damit die Lampe es beim naechsten Boot noch + # einmal versuchen kann. + self.station.write_control("listen", None) + self.station.record_migration(ip, target, variant, pid, lamp_version, name, version, + sent, status) + + def _serve_file(self, path: Path, started: float) -> tuple[int, str]: + """Sendet `path` mit Range-Unterstuetzung; liefert (gesendete Bytes, Status). + Gemeinsam fuer Rettungs- und Migrationsroute - Uebertragungsmechanik (Range, + --stall-after, Abbruchbehandlung) ist identisch, nur was mit dem Ergebnis + geschieht, unterscheidet sich.""" size = path.stat().st_size start = 0 rng = self.headers.get("Range", "") @@ -195,7 +364,6 @@ class Handler(BaseHTTPRequestHandler): if first.isdigit() and int(first) < size: start = int(first) - version = trailer_version(path) self.send_response(206 if start else 200) self.send_header("Content-Type", "application/octet-stream") self.send_header("Content-Length", str(size - start)) @@ -240,19 +408,43 @@ class Handler(BaseHTTPRequestHandler): sent += len(chunk) except (BrokenPipeError, ConnectionResetError, TimeoutError): status = "aborted" - # Record only now that the transfer has actually finished (or failed): "ok" must - # never be written before the last byte is confirmed sent, or status.json can - # claim success for a device that never received the end of the image. self.log(206 if start else 200, sent, started) - self.station.record(self.client_address[0], target, version, sent, status) + return sent, status + + +def resolve_mode(explicit_mode: str | None, mode_file: Path | None) -> str: + """Bestimmt den Betriebsmodus. Vorrang: ein ausdrueckliches --mode schlaegt + --mode-file, --mode-file schlaegt die Vorgabe "recovery". Eine fehlende oder + unlesbare/unerwartete Modusdatei bedeutet ebenfalls "recovery" - Task 4 baut die + systemd-Unit darauf auf, dass der Modus ohne Neustart-Argumentaenderung aus einer + Datei kommt (/run/ninlight-station/mode), und ein fehlerhafter Zustand dieser Datei + darf den Dienst nicht zum Absturz bringen oder unbeabsichtigt scharf schalten.""" + if explicit_mode is not None: + return explicit_mode + if mode_file is not None: + try: + text = mode_file.read_text().strip() + except OSError: + return "recovery" + if text in ("recovery", "migration"): + return text + return "recovery" + return "recovery" def main(argv: list[str] | None = None) -> int: - p = argparse.ArgumentParser(description="ninlight recovery station HTTP server") + p = argparse.ArgumentParser(description="ninlight recovery/migration station HTTP server") p.add_argument("--image-dir", required=True, type=Path) p.add_argument("--bind", default="192.168.8.1") p.add_argument("--port", type=int, default=80) p.add_argument("--state", type=Path, default=Path("/run/ninlight-station/status.json")) + p.add_argument("--mode", choices=("recovery", "migration"), default=None, + help="ausdruecklicher Modus - schlaegt --mode-file (Vorgabe: siehe dort)") + p.add_argument("--mode-file", type=Path, default=None, + help="Datei mit 'recovery' oder 'migration'; wird ohne --mode verwendet, " + "Vorgabe bei fehlender/kaputter Datei ist 'recovery'") + p.add_argument("--control", type=Path, default=Path("/run/ninlight-station/control.json"), + help="Steuerdatei fuer Phase/Freigabe im Migrationsmodus") p.add_argument("--stall-after", type=int, default=None, help="test aid: stop sending after N bytes but keep the connection open") a = p.parse_args(argv) @@ -261,12 +453,13 @@ def main(argv: list[str] | None = None) -> int: print(f"error: image directory {a.image_dir} does not exist", file=sys.stderr) return 2 - station = Station(a.image_dir, a.state, a.stall_after) + mode = resolve_mode(a.mode, a.mode_file) + station = Station(a.image_dir, a.state, a.stall_after, mode=mode, control_path=a.control) httpd = ThreadingHTTPServer((a.bind, a.port), Handler) httpd.daemon_threads = True httpd.station = station # type: ignore[attr-defined] images = sorted(p.name for p in a.image_dir.glob("ninlight_*_ota.img")) - print(f"{now_iso()} listening on {a.bind}:{a.port}, {len(images)} image(s): " + print(f"{now_iso()} listening on {a.bind}:{a.port}, mode={mode}, {len(images)} image(s): " f"{', '.join(images) or 'none'}", flush=True) if a.stall_after is not None: print(f"{now_iso()} WARNING: --stall-after {a.stall_after} is a test aid", flush=True) diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index 347b973..85e3d56 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -32,6 +32,7 @@ setup_sandbox() { chmod +x "$sandbox/tools/station/deploy.sh" # Inhalt irrelevant, die Dateien muessen nur existieren (werden "uebertragen"). echo "# dummy" >"$sandbox/tools/station/server.py" + echo "# dummy" >"$sandbox/tools/station/products.py" echo "# dummy" >"$sandbox/tools/station/ninlight-station" echo "# dummy" >"$sandbox/tools/station/ninlight-station.service" diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index dbda832..1d6008c 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -453,3 +453,229 @@ def test_concurrent_downloads_are_all_recorded(station): assert entry["status"] == "ok" assert entry["target"] == "evk" assert entry["bytes"] == expected_len + + +# --- Migrationsroute (Task 3) ------------------------------------------------------- +# +# Eigener Testklient statt der Station-Klasse oben: die migration_server/server-Fixtures +# brauchen get()/status()/set_control()/read_control()/delete_control()/write_raw_control(), +# waehrend die Station-Klasse und die "station"-Fixture oben unveraendert fuer die +# bestehenden Rettungsmodus-Tests weiterlaufen sollen. + + +class _Response: + def __init__(self, status, headers, body): + self.status = status + self.headers = headers + self.body = body + + +class MigrationTestServer: + """Startet server.py mit --mode/--control und bietet die vom Brief geforderten + Hilfsmethoden. server.py schreibt status.json/control.json fuer eine Anfrage im + selben Handler-Thread, unmittelbar nachdem der Antwortkoerper vollstaendig gesendet + wurde - aber http.client's read() kann zurueckkehren, sobald Content-Length Bytes + angekommen sind, was diesem serverseitigen Schreiben knapp zuvorkommen kann. get() + legt deshalb eine kurze Pause ein, bevor es zurueckkehrt (derselbe Grund, aus dem + Station.read_state() oben mit einem Prädikat auf status.json wartet - hier gibt es + kein generisches Prädikat, das jeder Aufrufer kennt, darum die feste kleine Pause statt + einer Wiederholschleife).""" + + def __init__(self, tmp_path, mode): + self.image_dir = tmp_path / "images" + self.image_dir.mkdir() + self.state_path = tmp_path / "status.json" + self.control_path = tmp_path / "control.json" + self.port = free_port() + self.proc = subprocess.Popen( + [sys.executable, str(SERVER), "--image-dir", str(self.image_dir), + "--bind", "127.0.0.1", "--port", str(self.port), + "--state", str(self.state_path), "--mode", mode, + "--control", str(self.control_path)], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + ) + deadline = time.time() + 10 + while time.time() < deadline: + try: + socket.create_connection(("127.0.0.1", self.port), 0.2).close() + return + except OSError: + if self.proc.poll() is not None: + raise RuntimeError("server died: " + (self.proc.stdout.read() or "")) + time.sleep(0.05) + raise RuntimeError("server did not come up") + + def get(self, path, headers=None): + conn = http.client.HTTPConnection("127.0.0.1", self.port, timeout=5) + try: + conn.request("GET", path, headers=headers or {}) + r = conn.getresponse() + body = r.read() + resp = _Response(r.status, dict(r.getheaders()), body) + finally: + conn.close() + # See the class docstring: a short pause so the server's own status.json/ + # control.json write for this request has landed before we return. + time.sleep(0.05) + return resp + + def _read_json(self, path, timeout=2.0): + deadline = time.time() + timeout + last_err = None + while time.time() < deadline: + try: + return json.loads(path.read_text()) + except (OSError, json.JSONDecodeError) as e: + last_err = e + time.sleep(0.02) + raise AssertionError(f"could not read {path}: {last_err}") + + def status(self): + return self._read_json(self.state_path) + + def read_control(self): + return self._read_json(self.control_path) + + def set_control(self, phase, armed_target=None): + tmp = self.control_path.with_name(self.control_path.name + ".tmp") + tmp.write_text(json.dumps({"phase": phase, "armed_target": armed_target})) + os.chmod(tmp, 0o644) + os.replace(tmp, self.control_path) + + def delete_control(self): + try: + self.control_path.unlink() + except FileNotFoundError: + pass + + def write_raw_control(self, text): + self.control_path.write_text(text) + + def stop(self): + self.proc.terminate() + self.proc.wait(timeout=5) + + +@pytest.fixture +def server(tmp_path): + srv = MigrationTestServer(tmp_path, "recovery") + yield srv, srv.image_dir + srv.stop() + + +@pytest.fixture +def migration_server(tmp_path): + srv = MigrationTestServer(tmp_path, "migration") + yield srv, srv.image_dir + srv.stop() + + +def test_migration_auto_serves_known_product(migration_server): + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG" * 1000) + r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1.1.1.1800-dev_ota_light.img") + assert r.status == 200 + assert r.body == b"IMG" * 1000 + assert srv.status()["last_seen"]["target"] == "g95_cw" + assert srv.status()["last_seen"]["variant"] == "dev" + + +def test_migration_listen_records_but_serves_nothing(migration_server): + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + srv.set_control(phase="listen") + r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img") + assert r.status == 404 + st = srv.status() + assert st["last_seen"]["target"] == "g95_cw" # erkannt + assert st["served"] == 0 # aber nichts ausgeliefert + assert st["recent"][0]["status"] == "listen" + + +def test_arm_serves_exactly_once_then_falls_back_to_listen(migration_server): + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + srv.set_control(phase="armed", armed_target="g95_cw") + p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" + assert srv.get(p).status == 200 + assert srv.read_control()["phase"] == "listen" + assert srv.get(p).status == 404 + + +def test_armed_for_other_target_does_not_serve(migration_server): + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + srv.set_control(phase="armed", armed_target="gu10_rgb") + r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img") + assert r.status == 404 + assert srv.read_control()["phase"] == "armed" # Freigabe bleibt stehen + + +def test_unknown_product_id_is_404_and_logged(migration_server): + srv, _ = migration_server + r = srv.get("/qcx_firmware_-acme.bulb_V1_ota_light.img") + assert r.status == 404 + assert srv.status()["recent"][0]["status"] == "unknown" + + +def test_missing_migrate_image_is_404(migration_server): + srv, _ = migration_server + r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.e14_V1_ota_light.img") + assert r.status == 404 + assert srv.status()["recent"][0]["status"] == "notfound" + + +def test_recovery_path_is_404_in_migration_mode(migration_server): + srv, imgdir = migration_server + (imgdir / "ninlight_evk_ota.img").write_bytes(b"x" * 600) + assert srv.get("/ninlight_evk_ota.img").status == 404 + + +def test_migration_path_is_404_in_recovery_mode(server): + srv, _ = server + assert srv.get( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img").status == 404 + + +def test_missing_control_file_means_auto(migration_server): + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + srv.delete_control() + assert srv.get( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img").status == 200 + + +def test_corrupt_control_file_means_auto(migration_server): + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + srv.write_raw_control("{das ist kein json") + assert srv.get( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img").status == 200 + + +def test_range_resume_on_migration_image(migration_server): + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(bytes(range(256)) * 40) + p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" + r = srv.get(p, headers={"Range": "bytes=1000-"}) + assert r.status == 206 + assert r.headers["Content-Range"] == "bytes 1000-10239/10240" + assert r.body == (bytes(range(256)) * 40)[1000:] + + +def test_version_comes_from_manifest(migration_server): + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + (imgdir / "migrate" / "manifest.json").write_text(json.dumps({ + "ninlight_version": "0.3.9", + "images": [{"target": "g95_cw", "variant": "dev", "file": "g95_cw_dev.img"}]})) + srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img") + assert srv.status()["recent"][0]["version"] == "0.3.9" From d3e608dca0e6cd2437fa4284409e9cc64d4be02d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 18:22:49 +0200 Subject: [PATCH 110/150] fix(station): Freigabe atomar verbrauchen, Schreibfehler nicht stillschweigend armed lassen Fixrunde 1 zu Task 3 (Migrationsroute): - C1 (Critical): read_control()/Phasenpruefung/Auslieferung/write_control("listen") liefen ohne gemeinsame Sperre - zwei gleichzeitige Anfragen (zwei Lampen, oder Erst- und Range-Resume-Verbindung derselben Lampe) konnten beide phase == "armed" sehen und beide ausgeliefert bekommen. Neues Station.claim_delivery() liest und verbraucht die Freigabe jetzt atomar unter station.lock, vor Beginn der Auslieferung; Station.restore_armed() macht das bei einem Abbruch rueckgaengig. - I2 (Important): eine unerwartet kurz gelesene Datei zaehlte im Sendeloop als "ok" statt als "aborted" - relevant geworden, weil dieser Status jetzt entscheidet, ob eine armed- Freigabe restauriert wird. - I3 (Important): ein fehlschlagendes write_control() (z.B. schreibgeschuetztes /run) verschluckte den Fehler und liess "armed" dauerhaft scharf. Der Fehler landet jetzt in status.json (control_error), und claim_delivery() laesst nur die eine ausloesende Anfrage noch durch, bis ein Schreibversuch wieder gelingt. - I4 (Important): die neuen Server-Tests synchronisierten sich per fester Pause statt wie Station.read_state() bis zu einem Praedikat zu pollen. status()/read_control() im Testklienten tun das jetzt ebenfalls. - I5 (Important): resolve_mode()/--mode-file waren ungetestet trotz bindender Vorrangregel, auf der Task 4 aufbaut - fuenf neue reine Funktionstests. Dazu: recent[0] -> recent[-1] in allen betroffenen Tests (Brief-Fehler), ein neuer Rechte-Test fuer control.json (0644 unter restriktivem umask), und eine neue Nebenlaeufigkeits-Regression fuer C1. --- tools/station/server.py | 86 +++++++++-- tools/station/tests/test_server.py | 219 +++++++++++++++++++++++++---- 2 files changed, 265 insertions(+), 40 deletions(-) diff --git a/tools/station/server.py b/tools/station/server.py index 3111748..a1439d2 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -78,6 +78,13 @@ class Station: self.last_seen: dict | None = None self._manifest_mtime: float | None = None self._manifest_version = "unbekannt" + # Wird True, wenn write_control() zuletzt fehlgeschlagen ist (z.B. volles oder + # schreibgeschuetztes /run) - claim_delivery() liest das, um "armed" nicht + # dauerhaft scharf zu lassen, nur weil das Verbrauchen der Freigabe nicht auf + # Platte geschrieben werden konnte (Fixrunde 1, I3). control_error landet in + # status.json, damit ein Bedienfehler nicht nur auf stdout verschwindet. + self._control_write_broken = False + self.control_error: str | None = None # Cumulative bytes already delivered to each client while --stall-after is active, # keyed by client IP. A lamp whose download stalls re-opens a fresh connection and # resumes with Range right where it left off, so a per-request counter would hand @@ -150,7 +157,51 @@ class Station: os.unlink(tmp) raise except OSError as e: - print(f"warning: cannot write {self.control_path}: {e}", flush=True) + self._control_write_broken = True + self.control_error = f"cannot write {self.control_path}: {e}" + print(f"warning: {self.control_error}", flush=True) + else: + self._control_write_broken = False + self.control_error = None + + def claim_delivery(self, target: str) -> tuple[str, str | None, bool]: + """Entscheidet unter dem Lock, ob eine Anfrage fuer `target` gerade ausgeliefert + werden darf, und verbraucht - falls die Freigabe dafuer verantwortlich ist - diese + sofort per Compare-and-Set (lesen und auf "listen" setzen, ohne das Lock + dazwischen loszulassen). Ohne das koennten zwei gleichzeitige Anfragen (zwei + Lampen, oder Erst- und Range-Resume-Verbindung derselben Lampe) beide phase == + "armed" sehen und beide ausgeliefert bekommen (Fixrunde 1, C1) - genau das Szenario, + fuer das die Freigabephase laut Spec 3.3 da ist. + + Liefert (phase, armed_target) wie sie VOR dem Verbrauch galten, plus ob die + aktuelle Anfrage ausliefern darf. Bricht die Auslieferung anschliessend ab + (status == "aborted") oder findet der Aufrufer kein Image, muss er bei + phase == "armed" restore_armed(target) aufrufen, damit die Freigabe fuer den + naechsten Bootversuch stehen bleibt. + + Schlaegt das Schreiben von "listen" fehl (I3), bekommt nur die eine Anfrage, die + den Fehler zuerst ausloest, noch ihre Auslieferung - jede weitere schlaegt hier + erneut fehl (_control_write_broken bleibt True), bis ein write_control()-Versuch + wieder klappt. Damit bleibt "armed" nicht dauerhaft scharf, nur weil /run gerade + nicht beschreibbar ist, heilt sich aber von selbst, sobald es das wieder ist.""" + with self.lock: + phase, armed_target = self.read_control() + if phase == "listen" or (phase == "armed" and armed_target != target): + return phase, armed_target, False + if phase == "armed": + was_broken = self._control_write_broken + self.write_control("listen", None) + if self._control_write_broken: + return phase, armed_target, not was_broken + return phase, armed_target, True + + def restore_armed(self, target: str) -> None: + """Setzt eine per claim_delivery() verbrauchte Freigabe zurueck, wenn die + Auslieferung danach doch nicht (vollstaendig) stattgefunden hat - ein Abbruch darf + die Freigabe nicht verbrauchen, sonst muesste am Geraet erneut von Hand gearmt + werden, obwohl die Lampe es beim naechsten Bootversuch selbst nochmal versuchen + wuerde (Spec 3.3, Brief-Schritt 3.6).""" + self.write_control("armed", target) def reserve_stall_bytes(self, ip: str, n: int) -> int: """How many of the next `n` bytes this client may still receive under --stall-after. @@ -205,6 +256,7 @@ class Station: payload["phase"] = phase payload["armed_target"] = armed_target payload["last_seen"] = self.last_seen + payload["control_error"] = self.control_error try: self.state_path.parent.mkdir(parents=True, exist_ok=True) fd, tmp = tempfile.mkstemp(dir=self.state_path.parent, prefix=".status-") @@ -322,9 +374,12 @@ class Handler(BaseHTTPRequestHandler): target, variant, name = product.target, product.variant, product.name version = self.station.manifest_version() - phase, armed_target = self.station.read_control() + # claim_delivery() liest die Phase UND verbraucht eine armed-Freigabe atomar unter + # station.lock (Fixrunde 1, C1) - zwischen dem Lesen und dem Verbrauchen darf keine + # zweite gleichzeitige Anfrage hindurchschluepfen, siehe die Methode selbst. + phase, armed_target, allowed = self.station.claim_delivery(target) - if phase == "listen" or (phase == "armed" and armed_target != target): + if not allowed: # last_seen wird trotzdem gesetzt - das ist der ganze Zweck der Horchphase: # die Anzeige soll zeigen, welches Modell da ist, auch wenn nichts rausgeht. self.station.record_migration(ip, target, variant, pid, lamp_version, name, @@ -335,6 +390,10 @@ class Handler(BaseHTTPRequestHandler): path = self.station.migrate_image_path(target, variant) if not path.is_file(): + if phase == "armed": + # claim_delivery() hat die Freigabe schon verbraucht, bevor wir wussten, + # dass es gar kein Image gibt - das darf nicht auf Kosten der Freigabe gehen. + self.station.restore_armed(target) self.station.record_migration(ip, target, variant, pid, lamp_version, name, version, 0, "notfound") self.reject(404, "no image for this device") @@ -342,12 +401,12 @@ class Handler(BaseHTTPRequestHandler): return sent, status = self._serve_file(path, started) - if phase == "armed" and status == "ok": - # Erst nach dem vollstaendigen Senden zurueck auf "listen" - _serve_file() ist - # an dieser Stelle bereits fertig. Bricht der Transfer ab (status == "aborted"), - # bleibt die Freigabe stehen, damit die Lampe es beim naechsten Boot noch - # einmal versuchen kann. - self.station.write_control("listen", None) + if phase == "armed" and status == "aborted": + # Bricht der Transfer ab, bleibt die Freigabe stehen, damit die Lampe es beim + # naechsten Boot noch einmal versuchen kann (Spec 3.3). claim_delivery() hat sie + # bereits auf "listen" gesetzt, bevor _serve_file() ueberhaupt anfing zu senden + # (das ist der Punkt von C1) - bei einem Abbruch wird das hier zurueckgenommen. + self.station.restore_armed(target) self.station.record_migration(ip, target, variant, pid, lamp_version, name, version, sent, status) @@ -403,6 +462,15 @@ class Handler(BaseHTTPRequestHandler): raise ConnectionResetError("peer closed during stall") chunk = f.read(read_size) if not chunk: + # Die Datei ist kuerzer als das eingangs per stat() ermittelte + # Content-Length (z.B. von deploy.sh waehrend einer laufenden + # Auslieferung durch eine kleinere ersetzt) - sent < remaining an + # dieser Stelle immer, denn die Schleifenbedingung oben haette sonst + # schon abgebrochen. Das ist ein abgebrochener, kein erfolgreicher + # Transfer (Fixrunde 1, I2): ohne dies wuerde eine im Migrationsmodus + # laufende "armed"-Freigabe faelschlich als verbraucht gelten, obwohl + # die Lampe nie das vollstaendige Image bekommen hat. + status = "aborted" break self.wfile.write(chunk) sent += len(chunk) diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index 1d6008c..8f5c777 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -472,20 +472,28 @@ class _Response: class MigrationTestServer: """Startet server.py mit --mode/--control und bietet die vom Brief geforderten - Hilfsmethoden. server.py schreibt status.json/control.json fuer eine Anfrage im - selben Handler-Thread, unmittelbar nachdem der Antwortkoerper vollstaendig gesendet - wurde - aber http.client's read() kann zurueckkehren, sobald Content-Length Bytes - angekommen sind, was diesem serverseitigen Schreiben knapp zuvorkommen kann. get() - legt deshalb eine kurze Pause ein, bevor es zurueckkehrt (derselbe Grund, aus dem - Station.read_state() oben mit einem Prädikat auf status.json wartet - hier gibt es - kein generisches Prädikat, das jeder Aufrufer kennt, darum die feste kleine Pause statt - einer Wiederholschleife).""" + Hilfsmethoden. + + status()/read_control() folgen demselben Muster wie Station.read_state() oben + (Zeilen 68-90): sie pollen bis zu einigen Sekunden auf ein optionales Praedikat, statt + sich auf eine feste Pause nach get() zu verlassen. Grund ist derselbe wie dort - + server.py schreibt status.json/control.json fuer eine Anfrage erst, nachdem der + Antwortkoerper vollstaendig gesendet ist, aber http.client's read() kann zurueckkehren, + sobald Content-Length Bytes angekommen sind, was diesem Schreiben knapp zuvorkommen + kann. Jeder Aufrufer kennt sein eigenes Praedikat (das ohnehin schon als Assertion + dasteht), daher kein pauschales Warten hier.""" def __init__(self, tmp_path, mode): self.image_dir = tmp_path / "images" self.image_dir.mkdir() self.state_path = tmp_path / "status.json" - self.control_path = tmp_path / "control.json" + # Eigenes Unterverzeichnis fuer die Steuerdatei, getrennt von status.json: ein Test + # fuer I3 (Fixrunde 1) macht genau dieses Verzeichnis schreibgeschuetzt, um einen + # fehlschlagenden write_control() zu erzwingen, und darf dabei nicht auch das + # Schreiben von status.json lahmlegen, ueber das derselbe Test den Fehler beobachtet. + control_dir = tmp_path / "control" + control_dir.mkdir() + self.control_path = control_dir / "control.json" self.port = free_port() self.proc = subprocess.Popen( [sys.executable, str(SERVER), "--image-dir", str(self.image_dir), @@ -511,30 +519,34 @@ class MigrationTestServer: conn.request("GET", path, headers=headers or {}) r = conn.getresponse() body = r.read() - resp = _Response(r.status, dict(r.getheaders()), body) + return _Response(r.status, dict(r.getheaders()), body) finally: conn.close() - # See the class docstring: a short pause so the server's own status.json/ - # control.json write for this request has landed before we return. - time.sleep(0.05) - return resp - def _read_json(self, path, timeout=2.0): - deadline = time.time() + timeout + def _poll_json(self, path, predicate=None, timeout=5.0): + """Wie Station.read_state() oben: liest `path` wiederholt (bis zu `timeout` + Sekunden), bis er sich parsen laesst und - falls angegeben - `predicate` erfuellt. + Ohne predicate reicht ein einziges erfolgreiches Parsen, genau wie dort.""" + last = None last_err = None + deadline = time.time() + timeout while time.time() < deadline: try: - return json.loads(path.read_text()) + last = json.loads(path.read_text()) + if predicate is None or predicate(last): + return last except (OSError, json.JSONDecodeError) as e: last_err = e - time.sleep(0.02) - raise AssertionError(f"could not read {path}: {last_err}") + time.sleep(0.02) + if last is None: + raise AssertionError(f"could not read {path}: {last_err}") + raise AssertionError(f"{path} never satisfied predicate, last seen: {last!r}") - def status(self): - return self._read_json(self.state_path) + def status(self, predicate=None): + return self._poll_json(self.state_path, predicate) - def read_control(self): - return self._read_json(self.control_path) + def read_control(self, predicate=None): + return self._poll_json(self.control_path, predicate) def set_control(self, phase, armed_target=None): tmp = self.control_path.with_name(self.control_path.name + ".tmp") @@ -577,8 +589,9 @@ def test_migration_auto_serves_known_product(migration_server): r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1.1.1.1800-dev_ota_light.img") assert r.status == 200 assert r.body == b"IMG" * 1000 - assert srv.status()["last_seen"]["target"] == "g95_cw" - assert srv.status()["last_seen"]["variant"] == "dev" + st = srv.status(lambda s: s.get("last_seen") is not None) + assert st["last_seen"]["target"] == "g95_cw" + assert st["last_seen"]["variant"] == "dev" def test_migration_listen_records_but_serves_nothing(migration_server): @@ -588,10 +601,10 @@ def test_migration_listen_records_but_serves_nothing(migration_server): srv.set_control(phase="listen") r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img") assert r.status == 404 - st = srv.status() + st = srv.status(lambda s: len(s["recent"]) >= 1) assert st["last_seen"]["target"] == "g95_cw" # erkannt assert st["served"] == 0 # aber nichts ausgeliefert - assert st["recent"][0]["status"] == "listen" + assert st["recent"][-1]["status"] == "listen" def test_arm_serves_exactly_once_then_falls_back_to_listen(migration_server): @@ -601,7 +614,7 @@ def test_arm_serves_exactly_once_then_falls_back_to_listen(migration_server): srv.set_control(phase="armed", armed_target="g95_cw") p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" assert srv.get(p).status == 200 - assert srv.read_control()["phase"] == "listen" + assert srv.read_control(lambda c: c.get("phase") == "listen")["phase"] == "listen" assert srv.get(p).status == 404 @@ -619,14 +632,16 @@ def test_unknown_product_id_is_404_and_logged(migration_server): srv, _ = migration_server r = srv.get("/qcx_firmware_-acme.bulb_V1_ota_light.img") assert r.status == 404 - assert srv.status()["recent"][0]["status"] == "unknown" + st = srv.status(lambda s: len(s["recent"]) >= 1) + assert st["recent"][-1]["status"] == "unknown" def test_missing_migrate_image_is_404(migration_server): srv, _ = migration_server r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.e14_V1_ota_light.img") assert r.status == 404 - assert srv.status()["recent"][0]["status"] == "notfound" + st = srv.status(lambda s: len(s["recent"]) >= 1) + assert st["recent"][-1]["status"] == "notfound" def test_recovery_path_is_404_in_migration_mode(migration_server): @@ -678,4 +693,146 @@ def test_version_comes_from_manifest(migration_server): "ninlight_version": "0.3.9", "images": [{"target": "g95_cw", "variant": "dev", "file": "g95_cw_dev.img"}]})) srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img") - assert srv.status()["recent"][0]["version"] == "0.3.9" + st = srv.status(lambda s: len(s["recent"]) >= 1) + assert st["recent"][-1]["version"] == "0.3.9" + + +def test_control_file_is_world_readable_even_under_a_restrictive_umask(tmp_path): + """Dieselbe 0600-Falle wie bei status.json (siehe + test_state_file_is_world_readable_even_under_a_restrictive_umask oben), aber fuer + control.json: das CLI aus Task 4 muss die Datei lesen UND schreiben koennen.""" + old_umask = os.umask(0o077) + try: + srv = MigrationTestServer(tmp_path, "migration") + finally: + os.umask(old_umask) + try: + (srv.image_dir / "migrate").mkdir(exist_ok=True) + (srv.image_dir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + srv.set_control(phase="armed", armed_target="g95_cw") + + def world_readable(): + mode = stat.S_IMODE(srv.control_path.stat().st_mode) + return bool(mode & stat.S_IROTH) + + # set_control() oben schreibt direkt aus dem Test, nicht ueber den Server - das + # will hier nicht geprueft werden. Erst die Anfrage unten bringt den Server selbst + # dazu, control.json zu schreiben (armed -> listen), also denselben Codepfad + # (Station.write_control()) wie write_state() fuer status.json. + assert srv.get( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img").status == 200 + srv.read_control(lambda _: world_readable()) + assert world_readable(), "control.json ist nach dem Schreiben durch den Server " \ + "nicht welt-lesbar" + finally: + srv.stop() + + +def test_armed_serves_exactly_once_under_concurrency(migration_server): + """C1 (Fixrunde 1): read_control()/Phasenpruefung/Auslieferung/write_control("listen") + liefen ohne gemeinsame Sperre ab - zwei gleichzeitige Anfragen (zwei Lampen, oder Erst- + und Range-Resume-Verbindung derselben Lampe) konnten beide phase == "armed" sehen und + beide die volle Auslieferung bekommen. station.claim_delivery() muss das Lesen und das + Verbrauchen der Freigabe atomar unter einer Sperre machen, damit von n gleichzeitigen + Anfragen gegen dieselbe Freigabe hoechstens eine durchkommt.""" + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG" * 20000) + srv.set_control(phase="armed", armed_target="g95_cw") + p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" + + n_workers = 20 + results = [] + results_lock = threading.Lock() + + def worker(): + r = srv.get(p) + with results_lock: + results.append(r.status) + + threads = [threading.Thread(target=worker) for _ in range(n_workers)] + for t in threads: + t.start() + for t in threads: + t.join(timeout=10) + + assert len(results) == n_workers, "nicht alle Worker sind fertig geworden" + assert results.count(200) == 1, f"erwartet genau eine 200, bekommen: {results}" + assert results.count(404) == n_workers - 1 + assert srv.read_control(lambda c: c.get("phase") == "listen")["phase"] == "listen" + + +def test_control_write_failure_blocks_further_armed_deliveries(migration_server): + """I3 (Fixrunde 1): schlaegt das Schreiben von control.json fehl (z.B. volles oder + schreibgeschuetztes /run), darf die Freigabe nicht dauerhaft auf "armed" stehen bleiben + - sonst wuerde jede weitere Lampe des Ziels ebenfalls bedient, genau die Luecke aus C1, + nur diesmal durch einen Schreibfehler statt durch Nebenlaeufigkeit ausgeloest. Die eine + Anfrage, die den Fehler ausloest, bekommt noch ihre Auslieferung (wie eine normale + Freigabe); jede weitere schlaegt fehl, bis das Schreiben wieder klappt. Der Fehler muss + ausserdem in status.json sichtbar sein, sonst wundert sich niemand, warum "arm" nicht + mehr wirkt.""" + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + srv.set_control(phase="armed", armed_target="g95_cw") + p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" + + control_dir = srv.control_path.parent + old_mode = control_dir.stat().st_mode + os.chmod(control_dir, 0o500) # r-x fuer den Besitzer, kein Schreiben mehr moeglich + try: + r1 = srv.get(p) + assert r1.status == 200, "die eine vorgesehene Auslieferung muss noch rausgehen" + st = srv.status(lambda s: s.get("control_error")) + assert st["control_error"], "Schreibfehler muss in status.json sichtbar sein" + + r2 = srv.get(p) + assert r2.status == 404, \ + "ein Schreibfehler darf die Freigabe nicht dauerhaft offen lassen" + finally: + os.chmod(control_dir, old_mode) + + +def _load_server_module(): + """Laedt server.py wie test_log_error_is_not_silenced_by_log_message oben - als + eigenstaendiges Modulobjekt, ohne es in sys.modules einzutragen, damit mehrfache + Aufrufe (einer je resolve_mode-Test) sich nicht in die Quere kommen.""" + import importlib.util + + spec = importlib.util.spec_from_file_location("ninlight_station_server_rm", SERVER) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def test_resolve_mode_explicit_mode_wins_over_file(tmp_path): + """I5 (Fixrunde 1): --mode schlaegt --mode-file.""" + module = _load_server_module() + mode_file = tmp_path / "mode" + mode_file.write_text("migration") + assert module.resolve_mode("recovery", mode_file) == "recovery" + + +def test_resolve_mode_reads_the_mode_file(tmp_path): + module = _load_server_module() + mode_file = tmp_path / "mode" + mode_file.write_text("migration") + assert module.resolve_mode(None, mode_file) == "migration" + + +def test_resolve_mode_missing_file_means_recovery(tmp_path): + module = _load_server_module() + mode_file = tmp_path / "mode" # bewusst nicht angelegt + assert module.resolve_mode(None, mode_file) == "recovery" + + +def test_resolve_mode_garbage_file_means_recovery(tmp_path): + module = _load_server_module() + mode_file = tmp_path / "mode" + mode_file.write_text("garbage\n") + assert module.resolve_mode(None, mode_file) == "recovery" + + +def test_resolve_mode_default_without_mode_file_is_recovery(): + module = _load_server_module() + assert module.resolve_mode(None, None) == "recovery" From 1d3627bacde713f1949afe8d5a666879840d1459 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 18:39:48 +0200 Subject: [PATCH 111/150] feat(station): Unterbefehle fuer Migrationsmodus und Freigabe ninlight-station bekommt recovery/migrate/arm/listen als Unterbefehle statt nur on/off/status; "on" bleibt Kurzform fuer "recovery on". Eine gemeinsame ap_switch()-Funktion faehrt vor jedem Wechsel erst jedes andere Stationsprofil herunter und prueft per nmcli nach, dass auf wlan0 wirklich nur eines aktiv ist - die beiden Zugangspunkte (Rettung/Migration) schliessen sich damit gegenseitig aus. Der Modus wandert per /run/ninlight-station/mode an den (ohnehin neu gestarteten) Dienst statt ueber die Konfigurationsdatei; arm/listen schreiben die Steuerdatei aus Task 3 mit demselben atomaren 0644-Muster wie server.py selbst. station.conf.example und die Pflichtschluesselpruefung bekommen die neuen Migrations- und Anzeige-Schluessel dazu. --- tools/station/ninlight-station | 341 ++++++++++++++++++---- tools/station/ninlight-station.service | 2 +- tools/station/station.conf.example | 12 + tools/station/tests/test_cli.sh | 388 +++++++++++++++++++++++++ tools/test_host.sh | 2 + 5 files changed, 688 insertions(+), 57 deletions(-) create mode 100755 tools/station/tests/test_cli.sh diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 6a3386c..49f36fc 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -1,14 +1,27 @@ #!/bin/sh -# Schaltet die ninlight-Recovery-Station an und aus. +# Schaltet die ninlight-Station (Rettungs- oder Migrationsmodus) an und aus. # # Die Station ist absichtlich nicht dauerhaft an: Solange sie laeuft, kann jeder # in Funkreichweite mit dem Passwort einer Lampe beim naechsten Boot beliebige # Firmware unterschieben, und der Werksreset per fuenf Stromzyklen funktioniert # bei keiner Lampe in Reichweite mehr (die Firmware loescht den Zaehler vor -# jedem Recovery-Versuch). +# jedem Recovery-Versuch). Der Migrationsmodus (Zugangspunkt "qconnex-firmware", +# oeffentlich bekanntes Passwort aus der alten Klyqa-Firmware) verschiebt diese +# Vertrauensgrenze noch weiter nach aussen - siehe README.md. +# +# Die beiden Zugangspunkte schliessen sich gegenseitig aus: ap_switch() faehrt +# immer erst jedes andere Stationsprofil herunter, bevor es das gewuenschte +# hochfaehrt, und prueft danach per nmcli nach, dass wirklich nur eines aktiv +# ist. set -eu -CONF=/etc/ninlight-station.conf +# Ueberschreibbar fuer Tests (dasselbe Prinzip wie NINLIGHT_STATION_ROOT/_BIN/_UNIT +# in deploy.sh): so kann tests/test_cli.sh gegen ein Sandbox-Verzeichnis statt +# gegen echte Systempfade laufen. +CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf} +RUN_DIR=${NINLIGHT_RUN_DIR:-/run/ninlight-station} +STATION_LIB=${NINLIGHT_STATION_LIB:-/opt/ninlight-station} + if [ -r "$CONF" ]; then # shellcheck disable=SC1090 . "$CONF" @@ -23,7 +36,8 @@ else exit 1 fi -for _key in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION; do +for _key in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION \ + MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do eval "test -n \"\${$_key-}\"" || { echo "Fehler: $CONF: Schluessel $_key fehlt oder ist leer - tools/station/setup_pi.sh ausfuehren" >&2 exit 1 @@ -32,8 +46,9 @@ done unset _key need_profile() { - nmcli -t -f NAME con show | grep -qx "$AP_CONNECTION" || { - echo "Fehler: NetworkManager-Profil '$AP_CONNECTION' fehlt - tools/station/setup_pi.sh ausfuehren" >&2 + # $1 = NetworkManager-Profilname, der existieren muss. + nmcli -t -f NAME con show | grep -qx "$1" || { + echo "Fehler: NetworkManager-Profil '$1' fehlt - tools/station/setup_pi.sh ausfuehren" >&2 exit 1 } } @@ -42,51 +57,142 @@ images() { find "$IMAGE_DIR" -maxdepth 1 -name 'ninlight_*_ota.img' 2>/dev/null | sort } -case "${1:-}" in -on) - need_profile - n=$(images | wc -l | tr -d ' ') - [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2 - sudo nmcli con up "$AP_CONNECTION" >/dev/null +migrate_images() { + find "$IMAGE_DIR/migrate" -maxdepth 1 -name '*.img' 2>/dev/null | sort +} + +# Faehrt erst jedes andere Stationsprofil herunter, dann (falls $1 nicht leer ist) +# das gewuenschte hoch, und prueft danach per nmcli nach, dass wirklich genau +# dieses Profil auf wlan0 aktiv ist. Weicht es ab, geht alles wieder herunter und +# die Funktion bricht mit Fehler ab - das ist die Absicherung fuer Fall 15 der +# Testliste: zwei gleichzeitig aktive Stationsprofile auf demselben Interface +# duerfen nie vorkommen. +# +# $1 leer bedeutet "nur alles herunterfahren" (Grundlage fuer "off"). +ap_switch() { + _target=${1:-} + for _p in "$AP_CONNECTION" "$MIGRATE_AP_CONNECTION"; do + if [ -n "$_target" ] && [ "$_p" = "$_target" ]; then + continue + fi + sudo nmcli con down "$_p" >/dev/null 2>&1 || true + done + [ -n "$_target" ] || return 0 + sudo nmcli con up "$_target" >/dev/null + _active=$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2) + if [ "$_active" != "$_target" ]; then + sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true + sudo nmcli con down "$MIGRATE_AP_CONNECTION" >/dev/null 2>&1 || true + echo "Fehler: wlan0 zeigt nach dem Umschalten auf '$_target' unerwartet '$_active' - beide Stationsprofile wieder heruntergefahren" >&2 + return 1 + fi +} + +# Schreibt $RUN_DIR/mode atomar mit 0644 (Temp-Datei im selben Verzeichnis, dann +# umbenennen) - server.py liest sie bei jedem Dienststart per --mode-file. +write_mode() { + sudo mkdir -p "$RUN_DIR" + _tmp="$RUN_DIR/.mode-$$" + printf '%s' "$1" | sudo install -m 0644 /dev/stdin "$_tmp" || return 1 + sudo mv -f "$_tmp" "$RUN_DIR/mode" +} + +# Schreibt $RUN_DIR/control.json atomar mit 0644 - dasselbe Muster (Temp-Datei per +# "install" anlegen, dann umbenennen) und dieselben Rechte wie server.py's eigenes +# write_control(), sonst kann der Dienst die Datei anschliessend nicht mehr +# ueberschreiben oder das CLI sie nicht mehr lesen. +# +# $1 = phase (auto|listen|armed), $2 = armed_target oder leer (-> null im JSON). +write_control() { + sudo mkdir -p "$RUN_DIR" + _tmp="$RUN_DIR/.control-$$.json" + if [ -n "${2:-}" ]; then + _json=$(printf '{"phase": "%s", "armed_target": "%s"}' "$1" "$2") + else + _json=$(printf '{"phase": "%s", "armed_target": null}' "$1") + fi + printf '%s' "$_json" | sudo install -m 0644 /dev/stdin "$_tmp" || return 1 + sudo mv -f "$_tmp" "$RUN_DIR/control.json" +} + +# Startet (neu) und wartet kurz auf Lebenszeichen. $1 = das gerade hochgefahrene +# AP-Profil, das bei einem Fehlschlag wieder heruntergefahren wird. +start_and_verify() { # Eine fruehere Absturzserie darf den naechsten ehrlichen Versuch nicht - # blockieren (systemd verweigert "start" sonst sofort mit "start-limit-hit"). + # blockieren (systemd verweigert "start"/"restart" sonst sofort mit + # "start-limit-hit"). sudo systemctl reset-failed ninlight-station 2>/dev/null || true - if ! sudo systemctl start ninlight-station; then - # Der Start-Aufruf selbst ist gescheitert (z.B. start-limit-hit) - unter + if ! sudo systemctl restart ninlight-station; then + # Der Neustart selbst ist gescheitert (z.B. start-limit-hit) - unter # set -eu wuerde das Skript hier sonst abbrechen, bevor die # Lebendigkeitspruefung unten je erreicht wird, und der eben # hochgefahrene Zugangspunkt bliebe offen ohne Server dahinter stehen. - sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true - echo "Fehler: ninlight-station liess sich nicht starten - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2 + sudo nmcli con down "$1" >/dev/null 2>&1 || true + echo "Fehler: ninlight-station liess sich nicht (neu) starten - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2 exit 1 fi - # "systemctl start" meldet bei Type=simple schon Erfolg, sobald der Prozess + # "systemctl restart" meldet bei Type=simple schon Erfolg, sobald der Prozess # gestartet wurde - nicht, sobald er ueberlebt hat. Kurz warten und wirklich # nachsehen, sonst bleibt ein offener AP ohne Server dahinter stehen. sleep 1 - if systemctl is-active --quiet ninlight-station; then - echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)" - # Mit dem oeffentlichen Default-Passwort (im Repo dokumentiert) kann jeder in - # Funkreichweite, der es kennt, jeder Lampe beliebige Firmware unterschieben - das ist - # die eine Haelfte der Betriebsregeln aus README.md/§9, die andere (Station wieder - # ausschalten) stand hier schon. - if [ "$PSK" = "ninlight-recovery" ]; then - echo "Warnung: PSK ist noch der oeffentliche Default 'ninlight-recovery' - eigenes Passwort in /etc/ninlight-station.conf setzen." >&2 - fi - echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." - else + if ! systemctl is-active --quiet ninlight-station; then # Mit Restart=always laeuft ein abgestuerzter Dienst sonst im Hintergrund # weiter in seiner Neustartschleife - erst stoppen, dann den AP runter, # sonst suggeriert die Meldung unten einen saubereren Zustand als er ist. sudo systemctl stop ninlight-station 2>/dev/null || true - sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true + sudo nmcli con down "$1" >/dev/null 2>&1 || true echo "Fehler: ninlight-station ist nach dem Start nicht aktiv - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2 exit 1 fi - ;; -off) +} + +recovery_on() { + need_profile "$AP_CONNECTION" + n=$(images | wc -l | tr -d ' ') + [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2 + ap_switch "$AP_CONNECTION" + write_mode "recovery" + start_and_verify "$AP_CONNECTION" + echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)" + # Mit dem oeffentlichen Default-Passwort (im Repo dokumentiert) kann jeder in + # Funkreichweite, der es kennt, jeder Lampe beliebige Firmware unterschieben - das ist + # die eine Haelfte der Betriebsregeln aus README.md/§9, die andere (Station wieder + # ausschalten) steht unten. + if [ "$PSK" = "ninlight-recovery" ]; then + echo "Warnung: PSK ist noch der oeffentliche Default 'ninlight-recovery' - eigenes Passwort in /etc/ninlight-station.conf setzen." >&2 + fi + echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." +} + +migrate_on() { + # $1 = "--listen" oder leer. + _phase=auto + if [ "${1:-}" = "--listen" ]; then + _phase=listen + elif [ -n "${1:-}" ]; then + usage + exit 2 + fi + need_profile "$MIGRATE_AP_CONNECTION" + n=$(migrate_images | wc -l | tr -d ' ') + [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR/migrate enthaelt keine Migrationsimages - die Station antwortet auf alles mit 404" >&2 + # Faehrt ninlight-recovery (falls es laeuft) herunter, BEVOR qconnex-firmware + # hochkommt, und prueft danach nach, dass wirklich nur eines aktiv ist. + ap_switch "$MIGRATE_AP_CONNECTION" + write_mode "migration" + write_control "$_phase" "" || { + echo "Fehler: $RUN_DIR/control.json konnte nicht geschrieben werden" >&2 + exit 1 + } + start_and_verify "$MIGRATE_AP_CONNECTION" + echo "Migration an: SSID $MIGRATE_SSID auf 192.168.8.1, Phase $_phase, $n Migrationsimage(s)" + echo "Achtung: das Passwort dieses Zugangspunkts ist aus der alten Firmware oeffentlich bekannt - siehe README.md." >&2 + echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." +} + +full_off() { sudo systemctl stop ninlight-station 2>/dev/null || true - sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true + ap_switch "" if nmcli -t -f NAME con show | grep -qx "$HOME_CONNECTION"; then if sudo nmcli con up "$HOME_CONNECTION" >/dev/null; then echo "Station aus, wlan0 zurueck im Heim-WLAN ($HOME_CONNECTION)" @@ -96,14 +202,109 @@ off) else echo "Station aus. Profil '$HOME_CONNECTION' fehlt, wlan0 bleibt ungenutzt - der Zugang ueber Kabel ist davon nicht betroffen." >&2 fi - ;; -status) +} + +listen_cmd() { + write_control "listen" "" || { + echo "Fehler: $RUN_DIR/control.json konnte nicht geschrieben werden" >&2 + exit 1 + } + echo "Phase: listen" +} + +arm_cmd() { + _target=${1:-} + if [ -z "$_target" ]; then + _status_file="$RUN_DIR/status.json" + if ! _target=$(python3 - "$_status_file" <<'PY' +import json +import sys + +try: + with open(sys.argv[1]) as f: + d = json.load(f) +except (OSError, ValueError): + sys.exit(1) +t = (d.get("last_seen") or {}).get("target") +if not t: + sys.exit(1) +print(t) +PY + ); then + echo "Fehler: kein zuletzt erkanntes Modell (last_seen) in $_status_file - Ziel angeben: ninlight-station arm " >&2 + exit 1 + fi + fi + if ! python3 - "$STATION_LIB" "$_target" <<'PY' +import sys + +sys.path.insert(0, sys.argv[1]) +import products + +target = sys.argv[2] +sys.exit(0 if any(p.target == target for p in products.PRODUCTS.values()) else 1) +PY + then + echo "Fehler: unbekanntes Ziel '$_target' - gueltige Ziele stehen in products.py" >&2 + exit 1 + fi + write_control "armed" "$_target" || { + echo "Fehler: $RUN_DIR/control.json konnte nicht geschrieben werden" >&2 + exit 1 + } + echo "Freigegeben: $_target" +} + +status_cmd() { # "systemctl is-active" schreibt den Zustand ("inactive", "failed", ...) auf stdout und # liefert dabei einen Rueckgabewert ungleich 0, sobald der Dienst nicht "active" ist - # ein "|| echo inaktiv" wuerde also zusaetzlich zur ersten Zeile ausgeloest und beide # landen in der Ausgabe. Der Zustand selbst ist die Auskunft; "status" ist damit - # erfolgreich, sobald es ihn ermitteln konnte, unabhaengig vom Dienstzustand - deshalb - # am Ende explizit exit 0. + # erfolgreich, sobald es ihn ermitteln konnte, unabhaengig vom Dienstzustand. + _status_file="$RUN_DIR/status.json" + # "existiert nicht" (Station lief noch nie) und "existiert, ist aber nicht lesbar" + # sind zwei verschiedene Zustaende und duerfen nicht beide stillschweigend im + # "-r"-Test verschwinden: sonst faellt ein Rechte-Fehler auf status.json (siehe + # server.py, schreibt als root) nie auf, weil die Ausgabe genauso aussieht wie + # "noch nie ausgeliefert". Das gilt auch fuer Modus/Phase/Erkannt unten. + if [ -r "$_status_file" ]; then + _info=$(python3 - "$_status_file" <<'PY' +import json +import sys + +try: + with open(sys.argv[1]) as f: + d = json.load(f) +except (OSError, ValueError): + d = {} +last_seen = d.get("last_seen") +if last_seen: + seen = "{} ({})".format( + last_seen.get("product_name") or last_seen.get("target", "?"), + last_seen.get("lamp_version", "?"), + ) +else: + seen = "-" +print(d.get("mode", "-")) +print(d.get("phase", "-")) +print(seen) +PY +) + _modus=$(printf '%s\n' "$_info" | sed -n '1p') + _phase=$(printf '%s\n' "$_info" | sed -n '2p') + _erkannt=$(printf '%s\n' "$_info" | sed -n '3p') + elif [ -e "$_status_file" ]; then + _modus="unbekannt (Datei nicht lesbar)" + _phase="unbekannt (Datei nicht lesbar)" + _erkannt="unbekannt (Datei nicht lesbar)" + else + _modus="-" + _phase="-" + _erkannt="-" + fi + printf 'Modus : %s\n' "$_modus" + printf 'Phase : %s\n' "$_phase" + printf 'Erkannt: %s\n' "$_erkannt" printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || true)" printf 'wlan0 : %s\n' "$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2)" printf 'Adresse: %s\n' "$(ip -4 -br addr show wlan0 2>/dev/null | awk '{print $3}')" @@ -117,26 +318,54 @@ print(t[21:37].rstrip(b"\x00").decode("ascii", "replace") if t[:4] == b"NINL" el PY )" done - # "status" soll mit 0 enden, sobald es seine Auskunft geben konnte, auch wenn noch nie - # ausgeliefert wurde - ohne das "|| true" waere der Exit-Code des Skripts hier der des - # fehlgeschlagenen Tests (1), weil das die letzte Anweisung im Zweig ist. - # - # "existiert nicht" (Station lief noch nie) und "existiert, ist aber nicht lesbar" - # sind zwei verschiedene Zustaende und duerfen nicht beide stillschweigend im - # "-r"-Test verschwinden: sonst faellt ein Rechte-Fehler auf status.json (siehe - # server.py, schreibt als root) nie auf, weil die Ausgabe genauso aussieht wie - # "noch nie ausgeliefert". - STATUS_FILE=/run/ninlight-station/status.json - if [ -r "$STATUS_FILE" ]; then + if [ -r "$_status_file" ]; then echo "Zuletzt ausgeliefert:" - sed 's/^/ /' "$STATUS_FILE" - elif [ -e "$STATUS_FILE" ]; then - echo "Fehler: $STATUS_FILE existiert, ist aber nicht lesbar (Rechte pruefen)" >&2 + sed 's/^/ /' "$_status_file" + elif [ -e "$_status_file" ]; then + echo "Fehler: $_status_file existiert, ist aber nicht lesbar (Rechte pruefen)" >&2 fi + # "status" soll mit 0 enden, sobald es seine Auskunft geben konnte, auch wenn noch nie + # ausgeliefert wurde - ohne das explizite exit 0 waere der Exit-Code des Skripts hier + # der des fehlgeschlagenen "-r"-Tests, weil das die letzte Anweisung im Zweig ist. exit 0 - ;; -*) - echo "Aufruf: ninlight-station on|off|status" >&2 - exit 2 - ;; +} + +usage() { + echo "Aufruf: ninlight-station on|recovery {on|off}|migrate {on [--listen]|off}|arm []|listen|off|status" >&2 +} + +case "${1:-}" in + on) + recovery_on + ;; + recovery) + case "${2:-}" in + on) recovery_on ;; + off) full_off ;; + *) usage; exit 2 ;; + esac + ;; + migrate) + case "${2:-}" in + on) migrate_on "${3:-}" ;; + off) full_off ;; + *) usage; exit 2 ;; + esac + ;; + arm) + arm_cmd "${2:-}" + ;; + listen) + listen_cmd + ;; + off) + full_off + ;; + status) + status_cmd + ;; + *) + usage + exit 2 + ;; esac diff --git a/tools/station/ninlight-station.service b/tools/station/ninlight-station.service index 04cff98..e464ee9 100644 --- a/tools/station/ninlight-station.service +++ b/tools/station/ninlight-station.service @@ -20,7 +20,7 @@ StartLimitBurst=5 [Service] Type=simple EnvironmentFile=/etc/ninlight-station.conf -ExecStart=/usr/bin/python3 /opt/ninlight-station/server.py --image-dir ${IMAGE_DIR} +ExecStart=/usr/bin/python3 /opt/ninlight-station/server.py --image-dir ${IMAGE_DIR} --mode-file /run/ninlight-station/mode Restart=always RestartSec=2 diff --git a/tools/station/station.conf.example b/tools/station/station.conf.example index 15bcaa8..73e6e1e 100644 --- a/tools/station/station.conf.example +++ b/tools/station/station.conf.example @@ -24,3 +24,15 @@ HOME_CONNECTION='preconfigured' # NetworkManager-Profil der Station selbst. AP_CONNECTION='ninlight-recovery' + +# Von der alten Klyqa-Firmware fest vorgegeben (qcx-w600-firmware) - nicht +# aenderbar, ohne dass alte Lampen die Migrationsstation nicht mehr finden. +MIGRATE_SSID='qconnex-firmware' +MIGRATE_PSK='qconnex-firmware' + +# NetworkManager-Profil der Migrations-Station. +MIGRATE_AP_CONNECTION='qconnex-firmware' + +# Panel-Revision der E-Paper-Anzeige (V2 oder V3) - am Geraet nicht auslesbar, +# siehe README.md. +EPD_REVISION='V3' diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh new file mode 100755 index 0000000..deba20f --- /dev/null +++ b/tools/station/tests/test_cli.sh @@ -0,0 +1,388 @@ +#!/usr/bin/env bash +# Stub-Tests fuer ninlight-station (das CLI, nicht server.py) - ohne Pi und ohne +# echte NetworkManager-/systemd-Rechte ausfuehrbar. +# +# nmcli, systemctl und sudo werden im PATH durch Stubs ersetzt, die ihre Aufrufe +# in eine Logdatei schreiben (Muster wie tests/test_deploy.sh fuer ssh/rsync). +# Der nmcli-Stub haelt zusaetzlich einen kleinen Zustand (welche Profile +# "bekannt" sind, welche gerade per "con up" aktiv sind) in Dateien, damit +# "con up"/"con down"/"device show wlan0" konsistent zueinander antworten - +# das ist die Grundlage fuer Fall 15, den wichtigsten Test hier. +# +# Aufruf: tools/station/tests/test_cli.sh +set -euo pipefail + +HERE=$(cd "$(dirname "$0")" && pwd) +STATION_DIR=$(cd "$HERE/.." && pwd) +CLI="$STATION_DIR/ninlight-station" + +TMPROOT=$(mktemp -d) +trap 'rm -rf "$TMPROOT"' EXIT + +FAILED=0 +pass() { echo "ok - $1"; } +fail() { echo "FEHLER - $1" >&2; FAILED=1; } + +# --- Stub-Kommandos fuer nmcli/systemctl/sudo im PATH --- +# $1 = bindir, $2 = logdatei, $3 = nmcli-Zustandsverzeichnis +setup_stubbin() { + local bindir=$1 log=$2 nmstate=$3 + mkdir -p "$bindir" "$nmstate" + : >"$nmstate/active" # Reihenfolge der per "con up" aktiven Profile, eine Zeile je Profil + + cat >"$bindir/nmcli" <>"$log" +NMSTATE="$nmstate" + +if [ "\$1" = "-t" ] && [ "\$2" = "-f" ] && [ "\$3" = "NAME" ] && [ "\$4" = "con" ] && [ "\$5" = "show" ]; then + cat "\$NMSTATE/profiles" 2>/dev/null + exit 0 +fi + +if [ "\$1" = "-t" ] && [ "\$2" = "-f" ] && [ "\$3" = "GENERAL.CONNECTION" ] && [ "\$4" = "device" ] && [ "\$5" = "show" ]; then + last=\$(tail -n1 "\$NMSTATE/active" 2>/dev/null) + echo "GENERAL.CONNECTION:\${last:---}" + exit 0 +fi + +if [ "\$1" = "con" ] && [ "\$2" = "up" ]; then + profile=\$3 + grep -qxF "\$profile" "\$NMSTATE/profiles" 2>/dev/null || { echo "Error: unknown connection '\$profile'." >&2; exit 1; } + grep -qxF "\$profile" "\$NMSTATE/active" 2>/dev/null || echo "\$profile" >>"\$NMSTATE/active" + exit 0 +fi + +if [ "\$1" = "con" ] && [ "\$2" = "down" ]; then + profile=\$3 + grep -vxF "\$profile" "\$NMSTATE/active" 2>/dev/null >"\$NMSTATE/active.tmp" || true + mv "\$NMSTATE/active.tmp" "\$NMSTATE/active" + exit 0 +fi + +exit 0 +EOF + + cat >"$bindir/systemctl" <>"$log" +case "\$1" in + is-active) + if [ "\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-0}" -eq 0 ]; then + echo active + exit 0 + else + echo inactive + exit "\${FAKE_SYSTEMCTL_ACTIVE_EXIT}" + fi + ;; + restart) exit "\${FAKE_SYSTEMCTL_RESTART_EXIT:-0}" ;; + *) exit 0 ;; +esac +EOF + + cat >"$bindir/sudo" <>"$log" +exec "\$@" +EOF + + # BSD-"install" (macOS) kennt kein /dev/stdin als Quelle ("Inappropriate file + # type or format") - GNU coreutils auf dem Pi schon. Das Skript nutzt genau + # dieses Muster (sudo install -m 0644 /dev/stdin ), also braucht der + # Test einen kleinen Ersatz, der nur dieses eine Aufrufmuster kennt. + cat >"$bindir/install" <<'EOF' +#!/usr/bin/env bash +mode="" +args=() +while [ $# -gt 0 ]; do + case "$1" in + -m) mode=$2; shift 2 ;; + *) args+=("$1"); shift ;; + esac +done +src=${args[0]} +dest=${args[1]} +if [ "$src" = "/dev/stdin" ]; then + cat >"$dest" +else + cp "$src" "$dest" +fi +[ -n "$mode" ] && chmod "$mode" "$dest" +EOF + + chmod +x "$bindir"/nmcli "$bindir"/systemctl "$bindir"/sudo "$bindir"/install +} + +# --- Sandbox fuer einen Testfall: eigene conf/RUN_DIR/IMAGE_DIR/nmcli-Zustand --- +# $1 = sandbox-Verzeichnis +setup_sandbox() { + local sandbox=$1 + rm -rf "$sandbox" + mkdir -p "$sandbox/run" "$sandbox/images/migrate" "$sandbox/bin" "$sandbox/nmstate" + echo "dummy" >"$sandbox/images/ninlight_foo_ota.img" + + cat >"$sandbox/station.conf" <"$sandbox/nmstate/profiles" + : >"$sandbox/nmstate/active" + + setup_stubbin "$sandbox/bin" "$sandbox/cmd.log" "$sandbox/nmstate" +} + +# Fuehrt ninlight-station mit gestubbtem PATH und ueberschriebenen Pfaden aus. +# $1 = sandbox-Verzeichnis, Rest = Argumente fuer ninlight-station. +run_cli() { + local sandbox=$1; shift + PATH="$sandbox/bin:$PATH" \ + NINLIGHT_STATION_CONF="$sandbox/station.conf" \ + NINLIGHT_RUN_DIR="$sandbox/run" \ + NINLIGHT_STATION_LIB="$STATION_DIR" \ + "$CLI" "$@" +} + +control_phase() { + # $1 = Pfad zu control.json + python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get('phase','?'))" "$1" 2>/dev/null || echo "FEHLT" +} +control_target() { + python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get('armed_target'))" "$1" 2>/dev/null || echo "FEHLT" +} + +echo "=== Fall 1: 'on' faehrt ninlight-recovery hoch, startet den Dienst neu, meldet 'Station an' ===" +SBX="$TMPROOT/t1" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" on 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -q "nmcli con up ninlight-recovery" "$SBX/cmd.log" && pass "nmcli con up ninlight-recovery aufgerufen" || fail "nmcli con up ninlight-recovery fehlt im Log" +grep -q "systemctl restart ninlight-station" "$SBX/cmd.log" && pass "Dienst (neu) gestartet" || fail "kein systemctl restart im Log" +[ "$(cat "$SBX/run/mode")" = "recovery" ] && pass "Modusdatei enthaelt 'recovery'" || fail "Modusdatei falsch: $(cat "$SBX/run/mode" 2>&1)" +echo "$OUT" | grep -q "Station an" && pass "Ausgabe enthaelt 'Station an'" || fail "Ausgabe fehlt 'Station an': $OUT" + +echo +echo "=== Fall 2: 'recovery on' identisch zu 'on' (Rueckwaertskompatibilitaet) ===" +SBX="$TMPROOT/t2" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" recovery on 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -q "nmcli con up ninlight-recovery" "$SBX/cmd.log" && pass "nmcli con up ninlight-recovery aufgerufen" || fail "nmcli con up ninlight-recovery fehlt im Log" +grep -q "systemctl restart ninlight-station" "$SBX/cmd.log" && pass "Dienst (neu) gestartet" || fail "kein systemctl restart im Log" +[ "$(cat "$SBX/run/mode")" = "recovery" ] && pass "Modusdatei enthaelt 'recovery'" || fail "Modusdatei falsch" +echo "$OUT" | grep -q "Station an" && pass "Ausgabe enthaelt 'Station an'" || fail "Ausgabe fehlt 'Station an': $OUT" + +echo +echo "=== Fall 3: 'migrate on' faehrt ninlight-recovery herunter, BEVOR qconnex-firmware hochkommt; control.json phase=auto ===" +SBX="$TMPROOT/t3" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" on >/dev/null 2>&1 # Recovery vorher an +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" migrate on 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +DOWN_LINE=$(grep -n "nmcli con down ninlight-recovery" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +UP_LINE=$(grep -n "nmcli con up qconnex-firmware" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +if [ -n "$DOWN_LINE" ] && [ -n "$UP_LINE" ] && [ "$DOWN_LINE" -lt "$UP_LINE" ]; then + pass "ninlight-recovery wird VOR qconnex-firmware heruntergefahren" +else + fail "Reihenfolge falsch oder Aufrufe fehlen (down=$DOWN_LINE, up=$UP_LINE): $(cat "$SBX/cmd.log")" +fi +[ "$(cat "$SBX/run/mode")" = "migration" ] && pass "Modusdatei enthaelt 'migration'" || fail "Modusdatei falsch" +[ "$(control_phase "$SBX/run/control.json")" = "auto" ] && pass "control.json: phase=auto" || fail "control.json phase ist nicht 'auto': $(cat "$SBX/run/control.json" 2>&1)" + +echo +echo "=== Fall 4: 'migrate on --listen' wie Fall 3, aber phase=listen ===" +SBX="$TMPROOT/t4" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" on >/dev/null 2>&1 +set +e +OUT=$(run_cli "$SBX" migrate on --listen 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ "$(control_phase "$SBX/run/control.json")" = "listen" ] && pass "control.json: phase=listen" || fail "control.json phase ist nicht 'listen': $(cat "$SBX/run/control.json" 2>&1)" + +echo +echo "=== Fall 5: 'migrate on' ohne Migrationsimages -> Warnung auf stderr, Rueckgabewert 0 ===" +SBX="$TMPROOT/t5" +setup_sandbox "$SBX" +# images/migrate/ bleibt leer +set +e +OUT=$(run_cli "$SBX" migrate on 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlender Migrationsimages" || fail "Exit-Code $RC, Ausgabe: $OUT" +echo "$OUT" | grep -q "Warnung" && pass "Warnung vorhanden" || fail "keine Warnung in der Ausgabe: $OUT" + +echo +echo "=== Fall 6: 'arm' ohne Argument liest last_seen.target aus status.json ===" +SBX="$TMPROOT/t6" +setup_sandbox "$SBX" +cat >"$SBX/run/status.json" <<'JSON' +{"mode": "migration", "phase": "listen", "last_seen": {"target": "gu10_rgb", "product_name": "Klyqa GU10 RGB CW/WW", "lamp_version": "V1.1.0.1138"}} +JSON +set +e +OUT=$(run_cli "$SBX" arm 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ "$(control_phase "$SBX/run/control.json")" = "armed" ] && pass "control.json: phase=armed" || fail "phase ist nicht 'armed'" +[ "$(control_target "$SBX/run/control.json")" = "gu10_rgb" ] && pass "armed_target=gu10_rgb (aus last_seen)" || fail "armed_target falsch: $(cat "$SBX/run/control.json")" + +echo +echo "=== Fall 7: 'arm gu10_rgb' mit explizitem Ziel ===" +SBX="$TMPROOT/t7" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" arm gu10_rgb 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ "$(control_phase "$SBX/run/control.json")" = "armed" ] && pass "control.json: phase=armed" || fail "phase ist nicht 'armed'" +[ "$(control_target "$SBX/run/control.json")" = "gu10_rgb" ] && pass "armed_target=gu10_rgb" || fail "armed_target falsch: $(cat "$SBX/run/control.json")" + +echo +echo "=== Fall 8: 'arm' ohne last_seen -> Fehler, Rueckgabewert != 0, control.json unveraendert ===" +SBX="$TMPROOT/t8" +setup_sandbox "$SBX" +# keine status.json vorhanden +set +e +OUT=$(run_cli "$SBX" arm 2>&1) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0" +echo "$OUT" | grep -qi "fehler" && pass "Fehlermeldung vorhanden" || fail "keine Fehlermeldung: $OUT" +[ ! -e "$SBX/run/control.json" ] && pass "control.json wurde nicht angelegt" || fail "control.json wurde trotz Fehler angelegt: $(cat "$SBX/run/control.json")" + +echo +echo "=== Fall 9: 'arm unbekannt' -> Fehler, Rueckgabewert != 0 ===" +SBX="$TMPROOT/t9" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" arm unbekannt 2>&1) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0" +echo "$OUT" | grep -qi "fehler" && pass "Fehlermeldung vorhanden" || fail "keine Fehlermeldung: $OUT" +[ ! -e "$SBX/run/control.json" ] && pass "control.json wurde nicht angelegt" || fail "control.json wurde trotz Fehler angelegt" + +echo +echo "=== Fall 10: 'listen' setzt phase=listen ===" +SBX="$TMPROOT/t10" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" listen 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ "$(control_phase "$SBX/run/control.json")" = "listen" ] && pass "control.json: phase=listen" || fail "phase ist nicht 'listen': $(cat "$SBX/run/control.json" 2>&1)" + +echo +echo "=== Fall 11: 'off' faehrt beide Profile herunter, stoppt den Dienst, zurueck ins Heim-WLAN ===" +SBX="$TMPROOT/t11" +setup_sandbox "$SBX" +run_cli "$SBX" migrate on >/dev/null 2>&1 +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" off 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -q "systemctl stop ninlight-station" "$SBX/cmd.log" && pass "Dienst gestoppt" || fail "kein systemctl stop im Log" +ACTIVE=$(cat "$SBX/nmstate/active") +echo "$ACTIVE" | grep -qx "ninlight-recovery" && fail "ninlight-recovery ist noch aktiv" || pass "ninlight-recovery ist nicht mehr aktiv" +echo "$ACTIVE" | grep -qx "qconnex-firmware" && fail "qconnex-firmware ist noch aktiv" || pass "qconnex-firmware ist nicht mehr aktiv" +echo "$ACTIVE" | grep -qx "home-wifi" && pass "zurueck im Heim-WLAN (home-wifi aktiv)" || fail "home-wifi ist nicht aktiv: $ACTIVE" +echo "$OUT" | grep -q "Heim-WLAN" && pass "Ausgabe erwaehnt Heim-WLAN" || fail "Ausgabe erwaehnt Heim-WLAN nicht: $OUT" + +echo +echo "=== Fall 12: 'status' im Migrationsmodus zeigt Modus, Phase und zuletzt erkannte Lampe ===" +SBX="$TMPROOT/t12" +setup_sandbox "$SBX" +cat >"$SBX/run/status.json" <<'JSON' +{"mode": "migration", "phase": "auto", + "last_seen": {"target": "gu10_rgb", "product_name": "Klyqa GU10 RGB CW/WW", "lamp_version": "V1.1.0.1138"}} +JSON +set +e +OUT=$(run_cli "$SBX" status 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +echo "$OUT" | grep -q "Modus.*migration" && pass "Modus wird angezeigt" || fail "Modus fehlt: $OUT" +echo "$OUT" | grep -q "Phase.*auto" && pass "Phase wird angezeigt" || fail "Phase fehlt: $OUT" +echo "$OUT" | grep -q "Erkannt.*Klyqa GU10 RGB CW/WW" && pass "zuletzt erkannte Lampe wird angezeigt" || fail "erkannte Lampe fehlt: $OUT" + +echo +echo "=== Fall 13: 'status' mit unlesbarer status.json meldet das ausdruecklich (4a-Fehler) ===" +SBX="$TMPROOT/t13" +setup_sandbox "$SBX" +echo '{"mode": "recovery"}' >"$SBX/run/status.json" +chmod 000 "$SBX/run/status.json" +set +e +OUT=$(run_cli "$SBX" status 2>&1) +RC=$? +set -e +chmod 644 "$SBX/run/status.json" # damit trap-Aufraeumen am Ende sicher klappt +[ "$RC" -eq 0 ] && pass "Exit-Code 0 (status liefert trotzdem eine Auskunft)" || fail "Exit-Code $RC, Ausgabe: $OUT" +echo "$OUT" | grep -q "existiert, ist aber nicht lesbar" && pass "unlesbare status.json wird ausdruecklich gemeldet" || fail "Meldung zu unlesbarer status.json fehlt: $OUT" + +echo +echo "=== Fall 14: unbekannter Unterbefehl -> Aufrufhinweis, Rueckgabewert 2 ===" +SBX="$TMPROOT/t14" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" frobnicate 2>&1) +RC=$? +set -e +[ "$RC" -eq 2 ] && pass "Exit-Code 2" || fail "Exit-Code war $RC statt 2" +echo "$OUT" | grep -qi "Aufruf" && pass "Aufrufhinweis vorhanden" || fail "kein Aufrufhinweis: $OUT" + +echo +echo "=== Fall 15 (wichtigster Test): 'migrate on' bei laufendem Recovery-Modus laesst GENAU EIN Profil aktiv ===" +SBX="$TMPROOT/t15" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" on >/dev/null 2>&1 +ACTIVE_BEFORE=$(cat "$SBX/nmstate/active") +echo "$ACTIVE_BEFORE" | grep -qx "ninlight-recovery" && pass "Vorbedingung: ninlight-recovery laeuft" || fail "Vorbedingung nicht erfuellt: $ACTIVE_BEFORE" +set +e +OUT=$(run_cli "$SBX" migrate on 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +ACTIVE_AFTER=$(cat "$SBX/nmstate/active") +COUNT=$(echo "$ACTIVE_AFTER" | grep -c . || true) +if [ "$COUNT" -eq 1 ] && [ "$ACTIVE_AFTER" = "qconnex-firmware" ]; then + pass "genau ein Profil aktiv: qconnex-firmware" +else + fail "erwartet genau 'qconnex-firmware', tatsaechlich aktiv: [$ACTIVE_AFTER]" +fi +DEVSHOW=$(PATH="$SBX/bin:$PATH" nmcli -t -f GENERAL.CONNECTION device show wlan0) +[ "$DEVSHOW" = "GENERAL.CONNECTION:qconnex-firmware" ] && pass "nmcli device show wlan0 bestaetigt qconnex-firmware" || fail "device show liefert: $DEVSHOW" + +echo +if [ "$FAILED" -eq 0 ]; then + echo "Alle Tests bestanden." +else + echo "Mindestens ein Test ist fehlgeschlagen." >&2 +fi +exit "$FAILED" diff --git a/tools/test_host.sh b/tools/test_host.sh index bd09054..e2c93e2 100755 --- a/tools/test_host.sh +++ b/tools/test_host.sh @@ -11,3 +11,5 @@ if python3 -c 'import pytest' 2>/dev/null; then else echo "Hinweis: pytest fehlt - Python-Tests uebersprungen" >&2 fi + +"$ROOT/tools/station/tests/test_cli.sh" From 1c077a15d0eecb520f5f7f63a795a98502e7cb88 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 19:04:23 +0200 Subject: [PATCH 112/150] fix(station): RuntimeDirectory-Race, Statusphase, off/ap_switch-Nachpruefung, Pi-Bestandskonfiguration Fixrunde 1 zu Task 4: - C1 (kritisch): die Unit leerte /run/ninlight-station bei jedem systemctl restart, weil RuntimeDirectoryPreserve fehlte - genau der Neustart, mit dem migrate/recovery on den Dienst hochfahren, wischte die eben geschriebene Modus-/Steuerdatei wieder weg, und der Server fiel still auf den Recovery-Default zurueck. Unit bekommt RuntimeDirectoryPreserve=restart, und start_and_verify() prueft nach dem Neustart zusaetzlich ausdruecklich nach, dass Modus und Phase noch stimmen. - I1: status las die Phase aus status.json, die arm/listen (die den Dienst bewusst nicht neu starten) nie aktualisieren - jetzt kommt sie aus control.json. - I2: off pruefte nach dem Herunterfahren nichts nach und konnte "Station aus" melden, waehrend ein Profil wegen haengendem NetworkManager weiterhin aktiv war - ap_switch() verifiziert das jetzt auch im Aus-Fall. - I3: der Fehlerzweig von ap_switch (Mismatch nach dem Umschalten) war im bisherigen Test nie erreichbar; der nmcli-Stub kann das jetzt erzwingen. - I4: neue Pflichtschluessel blockierten jeden Unterbefehl auf einem bereits eingerichteten Pi. setup_pi.sh ergaenzt fehlende Schluessel in einer bestehenden Konfiguration jetzt einzeln, statt sie unangetastet zu lassen; dafuer musste das Skript per bash-s-Heredocs und ueberschreibbaren Pfadvariablen erst testbar gemacht werden (neuer test_setup_pi.sh). Dazu vier kleinere Punkte aus derselben Review: grep -Fqx statt grep -qx fuer Profilnamen, install-Stub durch tee+chmod ersetzt (POSIX, kein Sonderfall mehr fuer macOS), Rechtepruefung auf control.json in den arm/listen-Testfaellen, stderr sauber von stdout getrennt in den Warnungs-/Fehlerfaellen. --- tools/station/ninlight-station | 136 ++++++++++++++--- tools/station/ninlight-station.service | 12 ++ tools/station/setup_pi.sh | 101 +++++++++---- tools/station/tests/test_cli.sh | 194 ++++++++++++++++++------ tools/station/tests/test_setup_pi.sh | 196 +++++++++++++++++++++++++ tools/test_host.sh | 1 + 6 files changed, 550 insertions(+), 90 deletions(-) create mode 100755 tools/station/tests/test_setup_pi.sh diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 49f36fc..53c89c5 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -47,7 +47,9 @@ unset _key need_profile() { # $1 = NetworkManager-Profilname, der existieren muss. - nmcli -t -f NAME con show | grep -qx "$1" || { + # -F: der Profilname ist ein Konfigurationswert, kein BRE-Muster - ein Name mit + # "." oder "[" wuerde mit "grep -x" sonst falsch (oder gar nicht) treffen. + nmcli -t -f NAME con show | grep -Fqx "$1" || { echo "Fehler: NetworkManager-Profil '$1' fehlt - tools/station/setup_pi.sh ausfuehren" >&2 exit 1 } @@ -77,7 +79,22 @@ ap_switch() { fi sudo nmcli con down "$_p" >/dev/null 2>&1 || true done - [ -n "$_target" ] || return 0 + if [ -z "$_target" ]; then + # "off": jedes "con down" oben ist mit "|| true" abgesichert, weil ein + # haengender NetworkManager den Aufruf sonst zum Abbruch braechte, bevor + # ueberhaupt versucht wurde, beide Profile herunterzufahren. Das darf aber + # nicht heissen, dass ein haengengebliebenes Profil stillschweigend als + # "aus" durchgeht: nachsehen, ob wlan0 wirklich kein Stationsprofil mehr + # zeigt, und erst dann Erfolg melden (Fixrunde 1, I2) - sonst kann z.B. der + # Migrations-AP mit oeffentlich bekanntem Passwort weitersenden, waehrend + # das CLI "Station aus" behauptet. + _active=$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2) + if [ "$_active" = "$AP_CONNECTION" ] || [ "$_active" = "$MIGRATE_AP_CONNECTION" ]; then + echo "Fehler: wlan0 zeigt nach dem Ausschalten immer noch '$_active' aktiv - NetworkManager pruefen (Zugangspunkt bleibt an)" >&2 + return 1 + fi + return 0 + fi sudo nmcli con up "$_target" >/dev/null _active=$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2) if [ "$_active" != "$_target" ]; then @@ -93,14 +110,14 @@ ap_switch() { write_mode() { sudo mkdir -p "$RUN_DIR" _tmp="$RUN_DIR/.mode-$$" - printf '%s' "$1" | sudo install -m 0644 /dev/stdin "$_tmp" || return 1 + printf '%s' "$1" | sudo tee "$_tmp" >/dev/null && sudo chmod 0644 "$_tmp" || return 1 sudo mv -f "$_tmp" "$RUN_DIR/mode" } -# Schreibt $RUN_DIR/control.json atomar mit 0644 - dasselbe Muster (Temp-Datei per -# "install" anlegen, dann umbenennen) und dieselben Rechte wie server.py's eigenes -# write_control(), sonst kann der Dienst die Datei anschliessend nicht mehr -# ueberschreiben oder das CLI sie nicht mehr lesen. +# Schreibt $RUN_DIR/control.json atomar mit 0644 - dasselbe Muster (Temp-Datei im +# selben Verzeichnis anlegen, Rechte setzen, dann umbenennen) und dieselben Rechte +# wie server.py's eigenes write_control(), sonst kann der Dienst die Datei +# anschliessend nicht mehr ueberschreiben oder das CLI sie nicht mehr lesen. # # $1 = phase (auto|listen|armed), $2 = armed_target oder leer (-> null im JSON). write_control() { @@ -111,13 +128,36 @@ write_control() { else _json=$(printf '{"phase": "%s", "armed_target": null}' "$1") fi - printf '%s' "$_json" | sudo install -m 0644 /dev/stdin "$_tmp" || return 1 + printf '%s' "$_json" | sudo tee "$_tmp" >/dev/null && sudo chmod 0644 "$_tmp" || return 1 sudo mv -f "$_tmp" "$RUN_DIR/control.json" } -# Startet (neu) und wartet kurz auf Lebenszeichen. $1 = das gerade hochgefahrene -# AP-Profil, das bei einem Fehlschlag wieder heruntergefahren wird. +# Startet (neu) und wartet kurz auf Lebenszeichen, dann prueft es nach, dass der +# eben gestartete Prozess wirklich den beabsichtigten Modus/die beabsichtigte +# Phase sieht. +# +# $1 = das gerade hochgefahrene AP-Profil (fuer Rollback bei Fehlschlag) +# $2 = erwarteter Inhalt von $RUN_DIR/mode nach dem Neustart +# $3 = erwartete Phase in control.json (nur im Migrationsmodus angeben, sonst leer) +# +# Der Grund fuer $2/$3: die systemd-Unit traegt RuntimeDirectory=ninlight-station, +# und systemd leert dieses Verzeichnis bei jedem Stop/Restart, sofern die Unit +# nicht RuntimeDirectoryPreserve=restart setzt. write_mode()/write_control() oben +# laufen VOR diesem "systemctl restart" - ein Neustart, der das Verzeichnis doch +# leert (weil die Unit die Zeile verliert, oder auf einem Pi mit alter Unit-Datei +# installiert wird), wischt also genau die Dateien wieder weg, die gerade erst +# geschrieben wurden. Der frisch gestartete Serverprozess faende dann keine +# Modusdatei und faellt laut server.py:resolve_mode() still auf "recovery" +# zurueck (bzw. read_control() auf "auto" bei fehlender Steuerdatei) - ein +# ausdrueckliches "migrate on --listen" wuerde so unbemerkt zu "Rettungsmodus, +# antwortet 404" bzw. zu "automatische Auslieferung" (Fixrunde 1, C1). Die Unit +# traegt deshalb RuntimeDirectoryPreserve=restart; diese Nachpruefung faengt es +# zusaetzlich ab, falls das je auseinanderlaeuft, statt sich allein auf die +# Unit-Datei zu verlassen. start_and_verify() { + _ap=$1 + _expect_mode=$2 + _expect_phase=${3:-} # Eine fruehere Absturzserie darf den naechsten ehrlichen Versuch nicht # blockieren (systemd verweigert "start"/"restart" sonst sofort mit # "start-limit-hit"). @@ -127,7 +167,7 @@ start_and_verify() { # set -eu wuerde das Skript hier sonst abbrechen, bevor die # Lebendigkeitspruefung unten je erreicht wird, und der eben # hochgefahrene Zugangspunkt bliebe offen ohne Server dahinter stehen. - sudo nmcli con down "$1" >/dev/null 2>&1 || true + sudo nmcli con down "$_ap" >/dev/null 2>&1 || true echo "Fehler: ninlight-station liess sich nicht (neu) starten - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2 exit 1 fi @@ -140,10 +180,37 @@ start_and_verify() { # weiter in seiner Neustartschleife - erst stoppen, dann den AP runter, # sonst suggeriert die Meldung unten einen saubereren Zustand als er ist. sudo systemctl stop ninlight-station 2>/dev/null || true - sudo nmcli con down "$1" >/dev/null 2>&1 || true + sudo nmcli con down "$_ap" >/dev/null 2>&1 || true echo "Fehler: ninlight-station ist nach dem Start nicht aktiv - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2 exit 1 fi + _actual_mode=$(cat "$RUN_DIR/mode" 2>/dev/null || true) + if [ "$_actual_mode" != "$_expect_mode" ]; then + sudo systemctl stop ninlight-station 2>/dev/null || true + sudo nmcli con down "$_ap" >/dev/null 2>&1 || true + echo "Fehler: $RUN_DIR/mode enthaelt nach dem Neustart '$_actual_mode' statt '$_expect_mode' - RuntimeDirectory wurde vermutlich geleert (RuntimeDirectoryPreserve=restart in der Unit pruefen). Zugangspunkt wieder abgeschaltet." >&2 + exit 1 + fi + if [ -n "$_expect_phase" ]; then + _actual_phase=$(python3 - "$RUN_DIR/control.json" <<'PY' +import json +import sys + +try: + with open(sys.argv[1]) as f: + d = json.load(f) +except (OSError, ValueError): + d = {} +print(d.get("phase", "")) +PY +) + if [ "$_actual_phase" != "$_expect_phase" ]; then + sudo systemctl stop ninlight-station 2>/dev/null || true + sudo nmcli con down "$_ap" >/dev/null 2>&1 || true + echo "Fehler: $RUN_DIR/control.json enthaelt nach dem Neustart Phase '$_actual_phase' statt '$_expect_phase' - RuntimeDirectory wurde vermutlich geleert. Zugangspunkt wieder abgeschaltet." >&2 + exit 1 + fi + fi } recovery_on() { @@ -152,7 +219,7 @@ recovery_on() { [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2 ap_switch "$AP_CONNECTION" write_mode "recovery" - start_and_verify "$AP_CONNECTION" + start_and_verify "$AP_CONNECTION" "recovery" echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)" # Mit dem oeffentlichen Default-Passwort (im Repo dokumentiert) kann jeder in # Funkreichweite, der es kennt, jeder Lampe beliebige Firmware unterschieben - das ist @@ -184,7 +251,7 @@ migrate_on() { echo "Fehler: $RUN_DIR/control.json konnte nicht geschrieben werden" >&2 exit 1 } - start_and_verify "$MIGRATE_AP_CONNECTION" + start_and_verify "$MIGRATE_AP_CONNECTION" "migration" "$_phase" echo "Migration an: SSID $MIGRATE_SSID auf 192.168.8.1, Phase $_phase, $n Migrationsimage(s)" echo "Achtung: das Passwort dieses Zugangspunkts ist aus der alten Firmware oeffentlich bekannt - siehe README.md." >&2 echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." @@ -193,7 +260,7 @@ migrate_on() { full_off() { sudo systemctl stop ninlight-station 2>/dev/null || true ap_switch "" - if nmcli -t -f NAME con show | grep -qx "$HOME_CONNECTION"; then + if nmcli -t -f NAME con show | grep -Fqx "$HOME_CONNECTION"; then if sudo nmcli con up "$HOME_CONNECTION" >/dev/null; then echo "Station aus, wlan0 zurueck im Heim-WLAN ($HOME_CONNECTION)" else @@ -262,11 +329,13 @@ status_cmd() { # landen in der Ausgabe. Der Zustand selbst ist die Auskunft; "status" ist damit # erfolgreich, sobald es ihn ermitteln konnte, unabhaengig vom Dienstzustand. _status_file="$RUN_DIR/status.json" + _control_file="$RUN_DIR/control.json" # "existiert nicht" (Station lief noch nie) und "existiert, ist aber nicht lesbar" # sind zwei verschiedene Zustaende und duerfen nicht beide stillschweigend im # "-r"-Test verschwinden: sonst faellt ein Rechte-Fehler auf status.json (siehe # server.py, schreibt als root) nie auf, weil die Ausgabe genauso aussieht wie - # "noch nie ausgeliefert". Das gilt auch fuer Modus/Phase/Erkannt unten. + # "noch nie ausgeliefert". Das gilt auch fuer Modus/Erkannt unten und fuer + # Phase/control.json weiter unten. if [ -r "$_status_file" ]; then _info=$(python3 - "$_status_file" <<'PY' import json @@ -286,22 +355,47 @@ if last_seen: else: seen = "-" print(d.get("mode", "-")) -print(d.get("phase", "-")) print(seen) PY ) _modus=$(printf '%s\n' "$_info" | sed -n '1p') - _phase=$(printf '%s\n' "$_info" | sed -n '2p') - _erkannt=$(printf '%s\n' "$_info" | sed -n '3p') + _erkannt=$(printf '%s\n' "$_info" | sed -n '2p') elif [ -e "$_status_file" ]; then _modus="unbekannt (Datei nicht lesbar)" - _phase="unbekannt (Datei nicht lesbar)" _erkannt="unbekannt (Datei nicht lesbar)" else _modus="-" - _phase="-" _erkannt="-" fi + # Die Phase kommt bewusst aus control.json, nicht aus status.json: "arm" und + # "listen" starten den Dienst absichtlich nicht neu, also schreibt server.py's + # write_state() status.json in der Zwischenzeit nicht neu (Fixrunde 1, I1) - + # wer die Phase dort ablesen wuerde, saehe nach einer erfolgreichen Freigabe + # weiterhin die Phase von vor dem letzten Dienststart und koennte "arm" fuer + # gescheitert halten oder faelschlich glauben, nichts sei mehr scharf. + if [ -r "$_control_file" ]; then + _phase=$(python3 - "$_control_file" <<'PY' +import json +import sys + +try: + with open(sys.argv[1]) as f: + d = json.load(f) +except (OSError, ValueError): + d = {} +phase = d.get("phase", "-") +target = d.get("armed_target") +if phase == "armed" and target: + print("{} (Ziel: {})".format(phase, target)) +else: + print(phase) +PY +) + elif [ -e "$_control_file" ]; then + _phase="unbekannt (Datei nicht lesbar)" + else + _phase="-" + fi printf 'Modus : %s\n' "$_modus" printf 'Phase : %s\n' "$_phase" printf 'Erkannt: %s\n' "$_erkannt" diff --git a/tools/station/ninlight-station.service b/tools/station/ninlight-station.service index e464ee9..bda200e 100644 --- a/tools/station/ninlight-station.service +++ b/tools/station/ninlight-station.service @@ -32,6 +32,18 @@ ProtectSystem=strict ProtectHome=yes PrivateTmp=yes RuntimeDirectory=ninlight-station +# Ohne dies leert systemd /run/ninlight-station bei JEDEM Stop und Neustart, nicht +# nur beim allerersten Start - und ninlight-station (das CLI) schreibt mode/ +# control.json immer VOR dem "systemctl restart", mit dem migrate/recovery on den +# Dienst (neu) starten. Ohne RuntimeDirectoryPreserve=restart wischt genau dieser +# Neustart die beiden gerade geschriebenen Dateien wieder weg, bevor der neue +# Serverprozess sie lesen kann - der Dienst faellt dann still auf den +# Recovery-Default zurueck (server.py resolve_mode()/read_control()), und ein +# ausdrueckliches "migrate on --listen" wird zu "Rettungsmodus" bzw. "auto" +# (Fixrunde 1, C1). "restart" (nicht "yes") reicht: das Verzeichnis muss nur einen +# Neustart ueberleben, nicht dauerhaft bestehen bleiben, wenn der Dienst regulaer +# gestoppt wird. +RuntimeDirectoryPreserve=restart ReadWritePaths=/run/ninlight-station RestrictAddressFamilies=AF_INET AF_UNIX MemoryMax=128M diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index bdf72d9..02be11e 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -1,45 +1,87 @@ #!/usr/bin/env bash -# Richtet einen Raspberry Pi als ninlight-Recovery-Station ein. Idempotent: -# mehrfaches Ausfuehren aendert nichts und schadet nicht. +# Richtet einen Raspberry Pi als ninlight-Station ein. Idempotent: mehrfaches +# Ausfuehren aendert nichts und schadet nicht - auch nicht auf einem Pi, auf dem +# schon eine aeltere Version dieses Skripts gelaufen ist (siehe "Konfiguration" +# unten: fehlende Schluessel werden ergaenzt, vorhandene bleiben unangetastet). # # Aufruf: tools/station/setup_pi.sh set -euo pipefail HOST=${1:?Aufruf: tools/station/setup_pi.sh } HERE=$(cd "$(dirname "$0")" && pwd) # /usr/bin/ssh und /usr/bin/scp statt Varianten im PATH, die sich anders verhalten koennen. -# Ueberschreibbar (wie NINLIGHT_STATION_ROOT in deploy.sh), damit ein Test gegen Stubs statt -# einen echten Pi laufen kann. +# Ueberschreibbar (wie NINLIGHT_STATION_ROOT unten und in deploy.sh), damit ein Test gegen +# Stubs statt einen echten Pi laufen kann. SSH_BIN=${NINLIGHT_SSH_BIN:-/usr/bin/ssh} SCP_BIN=${NINLIGHT_SCP_BIN:-/usr/bin/scp} echo "== Voraussetzungen auf $HOST pruefen ==" -"$SSH_BIN" "$HOST" 'set -e - systemctl is-active --quiet NetworkManager || { echo "NetworkManager laeuft nicht - dieser Plan setzt ihn voraus" >&2; exit 1; } - nmcli -t -f DEVICE,STATE device | grep -q "^wlan0:" || { echo "wlan0 wird nicht von NetworkManager verwaltet" >&2; exit 1; } - python3 -c "import sys; sys.exit(0 if sys.version_info >= (3,11) else 1)" || { echo "Python 3.11+ noetig" >&2; exit 1; } - echo " ok"' +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e +systemctl is-active --quiet NetworkManager || { echo "NetworkManager laeuft nicht - dieser Plan setzt ihn voraus" >&2; exit 1; } +nmcli -t -f DEVICE,STATE device | grep -q "^wlan0:" || { echo "wlan0 wird nicht von NetworkManager verwaltet" >&2; exit 1; } +python3 -c "import sys; sys.exit(0 if sys.version_info >= (3,11) else 1)" || { echo "Python 3.11+ noetig" >&2; exit 1; } +echo " ok" +REMOTE echo "== Konfiguration ==" "$SCP_BIN" -q "$HERE/station.conf.example" "$HOST:/tmp/ninlight-station.conf.example" -"$SSH_BIN" "$HOST" 'set -e - if [ -f /etc/ninlight-station.conf ]; then - echo " /etc/ninlight-station.conf existiert bereits, bleibt unveraendert" +# CONF ueberschreibbar wie ROOT_DIR/BIN_PATH/UNIT_PATH unten: auf dem Pi bleibt es +# beim Default, ein Test kann hier gegen eine Sandbox-Datei statt gegen echte +# Systempfade laufen. +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e +CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf} +TEMPLATE=/tmp/ninlight-station.conf.example +if [ -f "$CONF" ]; then + # Eine bestehende Konfiguration bleibt bei ihren Werten - nur Schluessel, die + # eine neuere Version des CLI zusaetzlich verlangt (z.B. die Migrations- und + # Anzeige-Schluessel aus Task 4), werden aus der Vorlage ergaenzt. Ohne das + # wuerde das Ausrollen eines neuen ninlight-station auf einen bereits + # eingerichteten Pi jeden Unterbefehl - "off" eingeschlossen - mit "Schluessel + # ... fehlt" blockieren, und ein bereits laufender Zugangspunkt bliebe an, bis + # jemand die 0600-Datei von Hand ergaenzt (Fixrunde 1, I4). + ADDED="" + for KEY in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION \ + MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do + if ! sudo grep -Eq "^${KEY}=" "$CONF"; then + LINE=$(grep -E "^${KEY}=" "$TEMPLATE" || true) + if [ -n "$LINE" ]; then + printf '%s\n' "$LINE" | sudo tee -a "$CONF" >/dev/null + ADDED="$ADDED $KEY" + fi + fi + done + if [ -n "$ADDED" ]; then + echo " $CONF: fehlende Schluessel ergaenzt:$ADDED (vorhandene Werte unveraendert)" else - sudo install -m 0600 /tmp/ninlight-station.conf.example /etc/ninlight-station.conf - echo " /etc/ninlight-station.conf aus der Vorlage angelegt" + echo " $CONF existiert bereits, alle Schluessel vorhanden" fi - rm -f /tmp/ninlight-station.conf.example' +else + sudo install -m 0600 "$TEMPLATE" "$CONF" + echo " $CONF aus der Vorlage angelegt" +fi +rm -f "$TEMPLATE" +REMOTE echo "== Server, CLI und Unit installieren ==" -"$SCP_BIN" -q "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" -"$SSH_BIN" "$HOST" 'set -e - sudo install -d -m 0755 /opt/ninlight-station /opt/ninlight-station/images - sudo install -m 0755 /tmp/server.py /opt/ninlight-station/server.py - sudo install -m 0755 /tmp/ninlight-station /usr/local/bin/ninlight-station - sudo install -m 0644 /tmp/ninlight-station.service /etc/systemd/system/ninlight-station.service - rm -f /tmp/server.py /tmp/ninlight-station /tmp/ninlight-station.service - sudo systemctl daemon-reload - echo " installiert"' +"$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" +# Pfade als Variablen mit Default (wie in deploy.sh): so kann ein Test die +# Installation gegen ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen +# lassen. +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e +ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} +BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} +UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} +sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images" +sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" +sudo install -m 0644 /tmp/products.py "$ROOT_DIR/products.py" +sudo install -m 0755 /tmp/ninlight-station "$BIN_PATH" +sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH" +rm -f /tmp/server.py /tmp/products.py /tmp/ninlight-station /tmp/ninlight-station.service +sudo systemctl daemon-reload +echo " installiert" +REMOTE echo "== NetworkManager-Profil ==" # Zwei Dinge, die man an einer echten Lampenstation nicht falsch machen darf: @@ -56,13 +98,18 @@ echo "== NetworkManager-Profil ==" # Prozessliste. "$SSH_BIN" "$HOST" bash -s <<'REMOTE' set -e -eval "$(sudo cat /etc/ninlight-station.conf)" -CONN_FILE="/etc/NetworkManager/system-connections/${AP_CONNECTION}.nmconnection" +CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf} +NM_DIR=${NINLIGHT_NM_DIR:-/etc/NetworkManager/system-connections} +eval "$(sudo cat "$CONF")" +CONN_FILE="$NM_DIR/${AP_CONNECTION}.nmconnection" if sudo test -f "$CONN_FILE"; then UUID=$(sudo sed -n 's/^uuid=//p' "$CONN_FILE" | head -n1) ACTION="aktualisiert" else - UUID=$(cat /proc/sys/kernel/random/uuid) + # /proc/sys/kernel/random/uuid gibt es nur unter Linux (dem Pi) - der Fallback + # auf uuidgen ist es, was einen Testlauf gegen den lokalen Stub (macOS) ueberhaupt + # ermoeglicht, aendert aber nichts am Verhalten auf dem Pi selbst. + UUID=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen | tr '[:upper:]' '[:lower:]') ACTION="angelegt" fi TMP=$(mktemp) diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index deba20f..dde339e 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -7,7 +7,11 @@ # Der nmcli-Stub haelt zusaetzlich einen kleinen Zustand (welche Profile # "bekannt" sind, welche gerade per "con up" aktiv sind) in Dateien, damit # "con up"/"con down"/"device show wlan0" konsistent zueinander antworten - -# das ist die Grundlage fuer Fall 15, den wichtigsten Test hier. +# das ist die Grundlage fuer Fall 15, den wichtigsten Test hier. Der +# systemctl-Stub bildet zusaetzlich nach, dass systemd das RuntimeDirectory bei +# jedem Stop/Restart leert, sofern die Unit kein RuntimeDirectoryPreserve=restart +# traegt - ohne das haette Fixrunde 1's C1 (siehe unten) unsichtbar bleiben +# koennen, weil ein Stub, der nichts loescht, diese Klasse von Bug nicht zeigt. # # Aufruf: tools/station/tests/test_cli.sh set -euo pipefail @@ -15,6 +19,7 @@ set -euo pipefail HERE=$(cd "$(dirname "$0")" && pwd) STATION_DIR=$(cd "$HERE/.." && pwd) CLI="$STATION_DIR/ninlight-station" +REAL_UNIT="$STATION_DIR/ninlight-station.service" TMPROOT=$(mktemp -d) trap 'rm -rf "$TMPROOT"' EXIT @@ -23,10 +28,18 @@ FAILED=0 pass() { echo "ok - $1"; } fail() { echo "FEHLER - $1" >&2; FAILED=1; } +file_mode() { + # $1 = Pfad. BSD-stat (macOS) und GNU-stat (Linux/Pi) haben unterschiedliche + # Flags - hier reicht "irgendeine funktioniert". + stat -f '%Lp' "$1" 2>/dev/null || stat -c '%a' "$1" 2>/dev/null +} + # --- Stub-Kommandos fuer nmcli/systemctl/sudo im PATH --- -# $1 = bindir, $2 = logdatei, $3 = nmcli-Zustandsverzeichnis +# $1 = bindir, $2 = logdatei, $3 = nmcli-Zustandsverzeichnis, $4 = RUN_DIR, +# $5 = Pfad zur systemd-Unit, deren RuntimeDirectoryPreserve-Zeile ueber das +# Leeren von $4 bei restart/stop entscheidet (wie beim echten systemd). setup_stubbin() { - local bindir=$1 log=$2 nmstate=$3 + local bindir=$1 log=$2 nmstate=$3 rundir=$4 unitfile=$5 mkdir -p "$bindir" "$nmstate" : >"$nmstate/active" # Reihenfolge der per "con up" aktiven Profile, eine Zeile je Profil @@ -41,6 +54,12 @@ if [ "\$1" = "-t" ] && [ "\$2" = "-f" ] && [ "\$3" = "NAME" ] && [ "\$4" = "con" fi if [ "\$1" = "-t" ] && [ "\$2" = "-f" ] && [ "\$3" = "GENERAL.CONNECTION" ] && [ "\$4" = "device" ] && [ "\$5" = "show" ]; then + # Testschalter fuer Fall I3 (Fixrunde 1): erzwingt eine Antwort, die nicht zum + # zuletzt hochgefahrenen Profil passt, um ap_switch()'s Fehlerzweig zu pruefen. + if [ -n "\${FAKE_NMCLI_DEVSHOW:-}" ]; then + echo "GENERAL.CONNECTION:\${FAKE_NMCLI_DEVSHOW}" + exit 0 + fi last=\$(tail -n1 "\$NMSTATE/active" 2>/dev/null) echo "GENERAL.CONNECTION:\${last:---}" exit 0 @@ -54,6 +73,13 @@ if [ "\$1" = "con" ] && [ "\$2" = "up" ]; then fi if [ "\$1" = "con" ] && [ "\$2" = "down" ]; then + # Testschalter fuer Fall I2 (Fixrunde 1): simuliert einen haengenden + # NetworkManager - der Aufruf "gelingt" (Exit 0, wie ein echtes "nmcli con + # down" es bei einem haengenden Backend oft tut), aendert den Zustand aber + # nicht. ap_switch() darf das nicht als "aus" durchgehen lassen. + if [ -n "\${FAKE_NMCLI_STUCK:-}" ]; then + exit 0 + fi profile=\$3 grep -vxF "\$profile" "\$NMSTATE/active" 2>/dev/null >"\$NMSTATE/active.tmp" || true mv "\$NMSTATE/active.tmp" "\$NMSTATE/active" @@ -66,6 +92,8 @@ EOF cat >"$bindir/systemctl" <>"$log" +RUN_DIR="$rundir" +UNIT_FILE="$unitfile" case "\$1" in is-active) if [ "\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-0}" -eq 0 ]; then @@ -76,7 +104,20 @@ case "\$1" in exit "\${FAKE_SYSTEMCTL_ACTIVE_EXIT}" fi ;; - restart) exit "\${FAKE_SYSTEMCTL_RESTART_EXIT:-0}" ;; + restart|stop) + # Bildet systemds RuntimeDirectory-Verhalten nach (Fixrunde 1, C1): ohne + # RuntimeDirectoryPreserve=restart in der Unit leert systemd das + # RuntimeDirectory bei JEDEM Stop/Restart, nicht nur beim allerersten Start, + # und legt es leer wieder an. + if ! grep -q '^RuntimeDirectoryPreserve=restart' "\$UNIT_FILE" 2>/dev/null; then + rm -rf "\$RUN_DIR" + mkdir -p "\$RUN_DIR" + fi + if [ "\$1" = "restart" ]; then + exit "\${FAKE_SYSTEMCTL_RESTART_EXIT:-0}" + fi + exit 0 + ;; *) exit 0 ;; esac EOF @@ -87,37 +128,14 @@ echo "STUB sudo \$*" >>"$log" exec "\$@" EOF - # BSD-"install" (macOS) kennt kein /dev/stdin als Quelle ("Inappropriate file - # type or format") - GNU coreutils auf dem Pi schon. Das Skript nutzt genau - # dieses Muster (sudo install -m 0644 /dev/stdin ), also braucht der - # Test einen kleinen Ersatz, der nur dieses eine Aufrufmuster kennt. - cat >"$bindir/install" <<'EOF' -#!/usr/bin/env bash -mode="" -args=() -while [ $# -gt 0 ]; do - case "$1" in - -m) mode=$2; shift 2 ;; - *) args+=("$1"); shift ;; - esac -done -src=${args[0]} -dest=${args[1]} -if [ "$src" = "/dev/stdin" ]; then - cat >"$dest" -else - cp "$src" "$dest" -fi -[ -n "$mode" ] && chmod "$mode" "$dest" -EOF - - chmod +x "$bindir"/nmcli "$bindir"/systemctl "$bindir"/sudo "$bindir"/install + chmod +x "$bindir"/nmcli "$bindir"/systemctl "$bindir"/sudo } # --- Sandbox fuer einen Testfall: eigene conf/RUN_DIR/IMAGE_DIR/nmcli-Zustand --- -# $1 = sandbox-Verzeichnis +# $1 = sandbox-Verzeichnis, $2 = Unit-Datei fuer den systemctl-Stub (Default: die +# echte, gerade zu testende tools/station/ninlight-station.service). setup_sandbox() { - local sandbox=$1 + local sandbox=$1 unitfile=${2:-$REAL_UNIT} rm -rf "$sandbox" mkdir -p "$sandbox/run" "$sandbox/images/migrate" "$sandbox/bin" "$sandbox/nmstate" echo "dummy" >"$sandbox/images/ninlight_foo_ota.img" @@ -137,7 +155,7 @@ EOF printf 'ninlight-recovery\nqconnex-firmware\nhome-wifi\n' >"$sandbox/nmstate/profiles" : >"$sandbox/nmstate/active" - setup_stubbin "$sandbox/bin" "$sandbox/cmd.log" "$sandbox/nmstate" + setup_stubbin "$sandbox/bin" "$sandbox/cmd.log" "$sandbox/nmstate" "$sandbox/run" "$unitfile" } # Fuehrt ninlight-station mit gestubbtem PATH und ueberschriebenen Pfaden aus. @@ -227,11 +245,12 @@ SBX="$TMPROOT/t5" setup_sandbox "$SBX" # images/migrate/ bleibt leer set +e -OUT=$(run_cli "$SBX" migrate on 2>&1) +OUT=$(run_cli "$SBX" migrate on 2>"$SBX/stderr.log") RC=$? set -e -[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlender Migrationsimages" || fail "Exit-Code $RC, Ausgabe: $OUT" -echo "$OUT" | grep -q "Warnung" && pass "Warnung vorhanden" || fail "keine Warnung in der Ausgabe: $OUT" +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlender Migrationsimages" || fail "Exit-Code $RC, stderr: $ERR" +echo "$ERR" | grep -q "Warnung" && pass "Warnung steht auf stderr" || fail "keine Warnung auf stderr: $ERR" echo echo "=== Fall 6: 'arm' ohne Argument liest last_seen.target aus status.json ===" @@ -247,6 +266,7 @@ set -e [ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" [ "$(control_phase "$SBX/run/control.json")" = "armed" ] && pass "control.json: phase=armed" || fail "phase ist nicht 'armed'" [ "$(control_target "$SBX/run/control.json")" = "gu10_rgb" ] && pass "armed_target=gu10_rgb (aus last_seen)" || fail "armed_target falsch: $(cat "$SBX/run/control.json")" +[ "$(file_mode "$SBX/run/control.json")" = "644" ] && pass "control.json ist 0644" || fail "control.json hat falsche Rechte: $(file_mode "$SBX/run/control.json")" echo echo "=== Fall 7: 'arm gu10_rgb' mit explizitem Ziel ===" @@ -259,30 +279,33 @@ set -e [ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" [ "$(control_phase "$SBX/run/control.json")" = "armed" ] && pass "control.json: phase=armed" || fail "phase ist nicht 'armed'" [ "$(control_target "$SBX/run/control.json")" = "gu10_rgb" ] && pass "armed_target=gu10_rgb" || fail "armed_target falsch: $(cat "$SBX/run/control.json")" +[ "$(file_mode "$SBX/run/control.json")" = "644" ] && pass "control.json ist 0644" || fail "control.json hat falsche Rechte: $(file_mode "$SBX/run/control.json")" echo -echo "=== Fall 8: 'arm' ohne last_seen -> Fehler, Rueckgabewert != 0, control.json unveraendert ===" +echo "=== Fall 8: 'arm' ohne last_seen -> Fehler auf stderr, Rueckgabewert != 0, control.json unveraendert ===" SBX="$TMPROOT/t8" setup_sandbox "$SBX" # keine status.json vorhanden set +e -OUT=$(run_cli "$SBX" arm 2>&1) +OUT=$(run_cli "$SBX" arm 2>"$SBX/stderr.log") RC=$? set -e +ERR=$(cat "$SBX/stderr.log") [ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0" -echo "$OUT" | grep -qi "fehler" && pass "Fehlermeldung vorhanden" || fail "keine Fehlermeldung: $OUT" +echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung steht auf stderr" || fail "keine Fehlermeldung auf stderr: $ERR" [ ! -e "$SBX/run/control.json" ] && pass "control.json wurde nicht angelegt" || fail "control.json wurde trotz Fehler angelegt: $(cat "$SBX/run/control.json")" echo -echo "=== Fall 9: 'arm unbekannt' -> Fehler, Rueckgabewert != 0 ===" +echo "=== Fall 9: 'arm unbekannt' -> Fehler auf stderr, Rueckgabewert != 0 ===" SBX="$TMPROOT/t9" setup_sandbox "$SBX" set +e -OUT=$(run_cli "$SBX" arm unbekannt 2>&1) +OUT=$(run_cli "$SBX" arm unbekannt 2>"$SBX/stderr.log") RC=$? set -e +ERR=$(cat "$SBX/stderr.log") [ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0" -echo "$OUT" | grep -qi "fehler" && pass "Fehlermeldung vorhanden" || fail "keine Fehlermeldung: $OUT" +echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung steht auf stderr" || fail "keine Fehlermeldung auf stderr: $ERR" [ ! -e "$SBX/run/control.json" ] && pass "control.json wurde nicht angelegt" || fail "control.json wurde trotz Fehler angelegt" echo @@ -295,6 +318,7 @@ RC=$? set -e [ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" [ "$(control_phase "$SBX/run/control.json")" = "listen" ] && pass "control.json: phase=listen" || fail "phase ist nicht 'listen': $(cat "$SBX/run/control.json" 2>&1)" +[ "$(file_mode "$SBX/run/control.json")" = "644" ] && pass "control.json ist 0644" || fail "control.json hat falsche Rechte: $(file_mode "$SBX/run/control.json")" echo echo "=== Fall 11: 'off' faehrt beide Profile herunter, stoppt den Dienst, zurueck ins Heim-WLAN ===" @@ -318,8 +342,11 @@ echo echo "=== Fall 12: 'status' im Migrationsmodus zeigt Modus, Phase und zuletzt erkannte Lampe ===" SBX="$TMPROOT/t12" setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" migrate on >/dev/null 2>&1 # phase=auto in control.json, wie es der echte Ablauf waere +# status.json so, wie server.py sie beim Start geschrieben haette (mode/last_seen). cat >"$SBX/run/status.json" <<'JSON' -{"mode": "migration", "phase": "auto", +{"mode": "migration", "last_seen": {"target": "gu10_rgb", "product_name": "Klyqa GU10 RGB CW/WW", "lamp_version": "V1.1.0.1138"}} JSON set +e @@ -379,6 +406,89 @@ fi DEVSHOW=$(PATH="$SBX/bin:$PATH" nmcli -t -f GENERAL.CONNECTION device show wlan0) [ "$DEVSHOW" = "GENERAL.CONNECTION:qconnex-firmware" ] && pass "nmcli device show wlan0 bestaetigt qconnex-firmware" || fail "device show liefert: $DEVSHOW" +echo +echo "=== Zusatzfall C1: ohne RuntimeDirectoryPreserve=restart wuerde der Neustart mode/control.json wegwischen - das CLI muss das erkennen und abbrechen ===" +SBX="$TMPROOT/tC1" +BROKEN_UNIT="$TMPROOT/broken-ninlight-station.service" +grep -v '^RuntimeDirectoryPreserve=restart' "$REAL_UNIT" >"$BROKEN_UNIT" +setup_sandbox "$SBX" "$BROKEN_UNIT" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +set +e +OUT=$(run_cli "$SBX" migrate on --listen 2>"$SBX/stderr.log") +RC=$? +set -e +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 - eine geleerte RuntimeDirectory wird nicht als Erfolg gemeldet" || fail "Exit-Code war 0, obwohl RUN_DIR beim Neustart geleert wurde: $OUT" +echo "$ERR" | grep -qi "RuntimeDirectoryPreserve" && pass "Fehlermeldung nennt RuntimeDirectoryPreserve als Ursache" || fail "Fehlermeldung nennt die Ursache nicht: $ERR" +echo "$OUT" | grep -q "Migration an" && fail "CLI meldete faelschlich 'Migration an', obwohl die Phase nach dem Neustart nicht mehr stimmte" || pass "kein falscher Erfolgsmeldung 'Migration an'" +ACTIVE_AFTER=$(cat "$SBX/nmstate/active") +[ -z "$ACTIVE_AFTER" ] && pass "Zugangspunkt wurde nach dem erkannten Fehler wieder heruntergefahren" || fail "Zugangspunkt blieb aktiv: $ACTIVE_AFTER" +# Gegenprobe: dieselbe Sandbox, aber mit der ECHTEN (reparierten) Unit-Datei - +# jetzt darf RUN_DIR beim Neustart nicht geleert werden und der Aufruf muss +# durchlaufen. Das ist die eigentliche Bestaetigung, dass RuntimeDirectoryPreserve +# in tools/station/ninlight-station.service tatsaechlich steht und wirkt. +SBX2="$TMPROOT/tC1-fixed" +setup_sandbox "$SBX2" +echo "dummy" >"$SBX2/images/migrate/gu10_rgb_prod.img" +set +e +OUT2=$(run_cli "$SBX2" migrate on --listen 2>&1) +RC2=$? +set -e +[ "$RC2" -eq 0 ] && pass "mit der echten (reparierten) Unit-Datei gelingt derselbe Aufruf" || fail "Gegenprobe schlug fehl - Exit-Code $RC2: $OUT2" +[ "$(control_phase "$SBX2/run/control.json")" = "listen" ] && pass "Gegenprobe: control.json ueberlebt den Neustart mit phase=listen" || fail "Gegenprobe: Phase nach Neustart falsch: $(cat "$SBX2/run/control.json" 2>&1)" + +echo +echo "=== Zusatzfall I1: 'status' zeigt die Phase aus control.json, nicht die veraltete Phase aus status.json ===" +SBX="$TMPROOT/tI1" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" migrate on --listen >/dev/null 2>&1 +# status.json wird nur beim Dienststart/bei einer Auslieferung geschrieben - hier +# simuliert, wie es nach dem Start aussehen wuerde (Phase "listen", wie beim Start). +cat >"$SBX/run/status.json" <<'JSON' +{"mode": "migration", "phase": "listen", + "last_seen": {"target": "gu10_rgb", "product_name": "Klyqa GU10 RGB CW/WW", "lamp_version": "V1.1.0.1138"}} +JSON +run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1 # startet den Dienst NICHT neu - status.json bleibt bei "listen" +OUT=$(run_cli "$SBX" status 2>&1) +echo "$OUT" | grep -q "Phase.*armed" && pass "status zeigt 'armed' (aus control.json), nicht das veraltete 'listen' aus status.json" || fail "status zeigt die falsche/veraltete Phase: $OUT" +echo "$OUT" | grep -q "gu10_rgb" && pass "status nennt das freigegebene Ziel" || fail "status nennt das armed_target nicht: $OUT" + +echo +echo "=== Zusatzfall I2: 'off' bricht mit Fehler ab, wenn ein Stationsprofil trotz 'con down' aktiv bleibt (haengender NetworkManager) ===" +SBX="$TMPROOT/tI2" +setup_sandbox "$SBX" +run_cli "$SBX" on >/dev/null 2>&1 +# HOME_CONNECTION aus den bekannten Profilen entfernen, um zugleich den in I2 +# genannten zweiten Umstand abzudecken (Zielprofil fuers Heim-WLAN fehlt). +grep -v '^home-wifi$' "$SBX/nmstate/profiles" >"$SBX/nmstate/profiles.tmp" +mv "$SBX/nmstate/profiles.tmp" "$SBX/nmstate/profiles" +set +e +OUT=$(FAKE_NMCLI_STUCK=1 run_cli "$SBX" off 2>"$SBX/stderr.log") +RC=$? +set -e +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 - ein haengender Zugangspunkt wird nicht als 'Station aus' gemeldet" || fail "Exit-Code war 0, obwohl ninlight-recovery weiterhin aktiv war: $OUT" +echo "$OUT" | grep -q "Station aus" && fail "CLI behauptete faelschlich 'Station aus'" || pass "keine falsche 'Station aus'-Meldung" +echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung auf stderr vorhanden" || fail "keine Fehlermeldung auf stderr: $ERR" +ACTIVE_AFTER=$(cat "$SBX/nmstate/active") +[ "$ACTIVE_AFTER" = "ninlight-recovery" ] && pass "ninlight-recovery ist (wie vom Stub simuliert) tatsaechlich noch aktiv" || fail "unerwarteter nmcli-Zustand: $ACTIVE_AFTER" + +echo +echo "=== Zusatzfall I3: ap_switch bricht ab und faehrt beide Profile herunter, wenn 'device show wlan0' nach dem Umschalten nicht passt ===" +SBX="$TMPROOT/tI3" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +set +e +OUT=$(FAKE_NMCLI_DEVSHOW=fremdes-profil run_cli "$SBX" migrate on 2>"$SBX/stderr.log") +RC=$? +set -e +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -ne 0 ] && pass "Exit-Code != 0, wenn wlan0 nach dem Umschalten ein fremdes Profil zeigt" || fail "Exit-Code war 0 trotz Profil-Mismatch: $OUT" +echo "$ERR" | grep -q "unerwartet" && pass "Fehlermeldung nennt den Mismatch" || fail "Fehlermeldung fehlt: $ERR" +ACTIVE_AFTER=$(cat "$SBX/nmstate/active") +[ -z "$ACTIVE_AFTER" ] && pass "beide Stationsprofile wurden nach dem erkannten Mismatch heruntergefahren" || fail "mindestens ein Profil blieb aktiv: $ACTIVE_AFTER" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh new file mode 100755 index 0000000..663c0a0 --- /dev/null +++ b/tools/station/tests/test_setup_pi.sh @@ -0,0 +1,196 @@ +#!/usr/bin/env bash +# Stub-Tests fuer setup_pi.sh - ohne Pi ausfuehrbar. +# +# ssh/scp werden im PATH durch Stubs ersetzt (Muster wie tests/test_deploy.sh): +# ssh fuehrt das per Heredoc uebergebene Remote-Skript lokal aus, scp uebersetzt +# "host:pfad"-Ziele auf den blossen lokalen Pfad und ruft dann "cp" auf. Die +# System-Zielpfade (/etc/ninlight-station.conf, /opt/ninlight-station, ...) sind +# per Umgebungsvariable auf ein Sandbox-Verzeichnis umgebogen (dasselbe Prinzip +# wie NINLIGHT_STATION_ROOT/_BIN/_UNIT in deploy.sh) - setup_pi.sh fasst damit +# nichts auf dem Testrechner selbst an ausser dem transienten /tmp, das schon +# deploy.sh fuer denselben Zweck benutzt. +# +# Schwerpunkt: Fixrunde 1, I4 - eine bestehende Konfiguration muss um fehlende +# Schluessel ergaenzt werden, ohne vorhandene Werte anzutasten. +# +# Aufruf: tools/station/tests/test_setup_pi.sh +set -euo pipefail + +HERE=$(cd "$(dirname "$0")" && pwd) +STATION_DIR=$(cd "$HERE/.." && pwd) + +TMPROOT=$(mktemp -d) +trap 'rm -rf "$TMPROOT"' EXIT + +FAILED=0 +pass() { echo "ok - $1"; } +fail() { echo "FEHLER - $1" >&2; FAILED=1; } + +setup_stubbin() { + local bindir=$1 log=$2 + mkdir -p "$bindir" + + cat >"$bindir/ssh" <>"$log" +host=\$1; shift +"\$@" +EOF + + cat >"$bindir/scp" <>"$log" +args=() +for a in "\$@"; do + case "\$a" in + -*) continue ;; + *:*) args+=("\${a#*:}") ;; + *) args+=("\$a") ;; + esac +done +exec cp "\${args[@]}" +EOF + + # "sudo install -o root -g root ..." (im NetworkManager-Keyfile-Schritt) braucht + # echtes Root, um den Eigentuemer zu setzen - hat der Testrechner (anders als der + # Pi) nicht. Der Stub streicht -o/-g nur fuer "install", der Rest laeuft wie auf + # dem Pi ueber ein exec des ungestubbten Kommandos. + cat >"$bindir/sudo" <>"$log" +if [ "\$1" = "install" ]; then + args=() + skip=0 + for a in "\$@"; do + if [ "\$skip" -eq 1 ]; then skip=0; continue; fi + case "\$a" in + -o|-g) skip=1 ;; + *) args+=("\$a") ;; + esac + done + exec "\${args[@]}" +fi +exec "\$@" +EOF + + cat >"$bindir/systemctl" <>"$log" +case "\$1" in + is-active) exit "\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-0}" ;; + *) exit 0 ;; +esac +EOF + + cat >"$bindir/nmcli" <>"$log" +if [ "\$1" = "-t" ] && [ "\$2" = "-f" ] && [ "\$3" = "DEVICE,STATE" ]; then + echo "wlan0:connected" + exit 0 +fi +exit 0 +EOF + + chmod +x "$bindir"/ssh "$bindir"/scp "$bindir"/sudo "$bindir"/systemctl "$bindir"/nmcli +} + +# $1 = Sandbox-Wurzel fuer die "Systempfade" +setup_state() { + local state=$1 + rm -rf "$state" + mkdir -p "$state/etc" "$state/opt" "$state/usr/local/bin" "$state/etc/systemd/system" \ + "$state/etc/NetworkManager/system-connections" +} + +# Fuehrt setup_pi.sh mit gestubbtem PATH und auf die Sandbox umgebogenen +# Systempfaden aus. $1 = state-Verzeichnis, $2 = stub-bin-Verzeichnis. +run_setup() { + local state=$1 stubbin=$2 + PATH="$stubbin:$PATH" \ + NINLIGHT_SSH_BIN="$stubbin/ssh" \ + NINLIGHT_SCP_BIN="$stubbin/scp" \ + NINLIGHT_STATION_CONF="$state/etc/ninlight-station.conf" \ + NINLIGHT_STATION_ROOT="$state/opt/ninlight-station" \ + NINLIGHT_STATION_BIN="$state/usr/local/bin/ninlight-station" \ + NINLIGHT_STATION_UNIT="$state/etc/systemd/system/ninlight-station.service" \ + NINLIGHT_NM_DIR="$state/etc/NetworkManager/system-connections" \ + "$STATION_DIR/setup_pi.sh" pi-test +} + +echo "=== Szenario 1: frischer Pi ohne Konfiguration -> Vorlage wird komplett angelegt ===" +STATE="$TMPROOT/s1-state"; STUBBIN="$TMPROOT/bin1"; LOG="$TMPROOT/s1.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +if diff -q "$STATION_DIR/station.conf.example" "$STATE/etc/ninlight-station.conf" >/dev/null 2>&1; then + pass "Konfiguration wurde 1:1 aus der Vorlage angelegt" +else + fail "Konfiguration weicht von der Vorlage ab oder fehlt" +fi +[ -x "$STATE/opt/ninlight-station/server.py" ] && pass "server.py installiert" || fail "server.py fehlt" +[ -f "$STATE/opt/ninlight-station/products.py" ] && pass "products.py installiert" || fail "products.py fehlt (arm braucht es)" +[ -x "$STATE/usr/local/bin/ninlight-station" ] && pass "CLI installiert" || fail "ninlight-station CLI fehlt" +[ -f "$STATE/etc/systemd/system/ninlight-station.service" ] && pass "Unit installiert" || fail "Unit fehlt" +[ -d "$STATE/opt/ninlight-station/images" ] && pass "images/-Verzeichnis angelegt" || fail "images/-Verzeichnis fehlt" + +echo +echo "=== Szenario 2 (Fixrunde 1, I4): bestehende Konfiguration ohne die neuen Schluessel wird ergaenzt, nicht ersetzt ===" +STATE="$TMPROOT/s2-state"; STUBBIN="$TMPROOT/bin2"; LOG="$TMPROOT/s2.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +cat >"$STATE/etc/ninlight-station.conf" <<'CONF' +SSID='eigene-ssid' +PSK='eigenes-passwort' +IMAGE_DIR='/opt/ninlight-station/images' +HOME_CONNECTION='preconfigured' +AP_CONNECTION='ninlight-recovery' +CONF +chmod 0600 "$STATE/etc/ninlight-station.conf" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +echo "$OUT" | grep -q "ergaenzt" && pass "Ausgabe kuendigt das Ergaenzen fehlender Schluessel an" || fail "Ausgabe erwaehnt 'ergaenzt' nicht: $OUT" +CONF_OUT="$STATE/etc/ninlight-station.conf" +if grep -Fqx "SSID='eigene-ssid'" "$CONF_OUT" && grep -Fqx "PSK='eigenes-passwort'" "$CONF_OUT"; then + pass "vorhandene Werte (SSID/PSK) blieben unveraendert" +else + fail "vorhandene Werte wurden ueberschrieben: $(cat "$CONF_OUT")" +fi +for KEY in MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do + if grep -Eq "^${KEY}=" "$CONF_OUT"; then + pass "fehlender Schluessel $KEY wurde ergaenzt" + else + fail "Schluessel $KEY fehlt weiterhin in der Konfiguration" + fi +done +MODE=$(stat -f '%Lp' "$CONF_OUT" 2>/dev/null || stat -c '%a' "$CONF_OUT" 2>/dev/null) +[ "$MODE" = "600" ] && pass "Konfigurationsdatei bleibt 0600" || fail "Konfigurationsdatei hat falsche Rechte: $MODE" + +echo +echo "=== Szenario 3: erneuter Lauf auf einer bereits vollstaendigen Konfiguration ist ein echtes No-Op ===" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +echo "$OUT" | grep -q "alle Schluessel vorhanden" && pass "meldet 'alle Schluessel vorhanden' statt erneut zu ergaenzen" || fail "Ausgabe: $OUT" +DUP=$(grep -c '^EPD_REVISION=' "$CONF_OUT") +[ "$DUP" -eq 1 ] && pass "EPD_REVISION steht nur einmal in der Datei (kein doppeltes Anhaengen)" || fail "EPD_REVISION steht $DUP mal in der Datei" + +echo +if [ "$FAILED" -eq 0 ]; then + echo "Alle Tests bestanden." +else + echo "Mindestens ein Test ist fehlgeschlagen." >&2 +fi +exit "$FAILED" diff --git a/tools/test_host.sh b/tools/test_host.sh index e2c93e2..ae6e337 100755 --- a/tools/test_host.sh +++ b/tools/test_host.sh @@ -13,3 +13,4 @@ else fi "$ROOT/tools/station/tests/test_cli.sh" +"$ROOT/tools/station/tests/test_setup_pi.sh" From 74198f6d3163c8294eeb0c5f4b6452ebee1c735e Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 19:28:42 +0200 Subject: [PATCH 113/150] fix(station): stat-Reihenfolge, Phase nur im Migrationsmodus, eingerueckte Konfig-Werte, kein /tmp-Zugriff im Test Fixrunde 2 zu Task 4: - N1 (wichtig): file_mode() in test_cli.sh/test_setup_pi.sh probierte "stat -f '%Lp'" vor "stat -c '%a'" - auf GNU-coreutils (dem Pi) deutet "-f" dort "Dateisystemstatus" und liefert Muell statt der Rechte-Zahl, faellt aber trotzdem in den "||"-Zweig durch, der zusaetzlich "644" anhaengt. Reihenfolge umgedreht und mit einem per gstat erzwungenen GNU-Testlauf zusaetzlich abgesichert. - N2 (wichtig): status zeigte nach einem Wechsel zurueck in den Rettungsmodus weiterhin die letzte Migrationsphase aus einer liegen gebliebenen control.json (moeglich seit RuntimeDirectoryPreserve= restart aus Fixrunde 1). Phase wird jetzt nur noch angezeigt, wenn Modus tatsaechlich "migration" ist. - N3 (klein): die Schluesselerkennung in setup_pi.sh war rein zeilenverankert ("^KEY=") und haette einen eingerueckten Bestandswert als fehlend gewertet - beim naechsten Sourcen der Konfiguration haette die zusaetzlich angehaengte Vorlagenzeile das eigene Passwort ueberschrieben. Muster auf fuehrenden Leerraum/export gehaertet. - N4 (klein): test_setup_pi.sh liess setup_pi.sh ueber den scp-Stub echte Dateien im /tmp des Entwicklungsrechners anlegen und wieder loeschen. Neue Variable NINLIGHT_STAGE_DIR biegt das auf eine Sandbox um. Alle vier Punkte mit Regressionstests, N2 und N3 zusaetzlich per Mutationsprobe (Fix zurueckgenommen, Test wird rot) verifiziert. --- tools/station/ninlight-station | 17 +++++++- tools/station/setup_pi.sh | 33 ++++++++++---- tools/station/tests/test_cli.sh | 64 +++++++++++++++++++++++++++- tools/station/tests/test_setup_pi.sh | 64 +++++++++++++++++++++++++++- 4 files changed, 164 insertions(+), 14 deletions(-) diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 53c89c5..3e6628d 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -373,7 +373,22 @@ PY # wer die Phase dort ablesen wuerde, saehe nach einer erfolgreichen Freigabe # weiterhin die Phase von vor dem letzten Dienststart und koennte "arm" fuer # gescheitert halten oder faelschlich glauben, nichts sei mehr scharf. - if [ -r "$_control_file" ]; then + # + # ABER: Phase ist ein Konzept, das nur im Migrationsmodus existiert - control.json + # kann von einem frueheren "arm"/"migrate on" liegen bleiben (RuntimeDirectoryPreserve= + # restart aus Fixrunde 1, C1 laesst es Neustarts jetzt ausdruecklich ueberleben), + # auch nachdem laengst wieder in den Rettungsmodus zurueckgeschaltet wurde - + # "recovery on" raeumt die Datei bewusst nicht auf, weil der Dienst sie im + # Rettungsmodus ohnehin ignoriert (server.py liest sie nur im Migrationsmodus). + # Ohne diese Bedingung wuerde status nach "migrate on --listen; arm X; on" ein + # irrefuehrendes "Phase: armed" im Rettungsmodus zeigen (Fixrunde 2, N2) - genau + # in der sicherheitsrelevanten Dimension "scharf/nicht scharf", die I1 eigentlich + # klarstellen sollte. Deshalb wird control.json nur gelesen, wenn Modus wirklich + # "migration" ist; sonst ist die Phase per Definition "-", unabhaengig davon, was + # noch in control.json steht. + if [ "$_modus" != "migration" ]; then + _phase="-" + elif [ -r "$_control_file" ]; then _phase=$(python3 - "$_control_file" <<'PY' import json import sys diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index 02be11e..e9903dd 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -13,6 +13,13 @@ HERE=$(cd "$(dirname "$0")" && pwd) # Stubs statt einen echten Pi laufen kann. SSH_BIN=${NINLIGHT_SSH_BIN:-/usr/bin/ssh} SCP_BIN=${NINLIGHT_SCP_BIN:-/usr/bin/scp} +# Wo die per scp uebertragenen Dateien auf dem Zielsystem zwischenlanden, bevor +# sie an ihren endgueltigen Platz installiert werden. Ueberschreibbar wie die +# anderen NINLIGHT_*-Pfade: auf dem Pi bleibt es bei /tmp, ein Test kann hier in +# eine Sandbox statt in das echte /tmp des Entwicklungsrechners umleiten +# (Fixrunde 2, N4) - sowohl fuer die lokale scp-Zielangabe unten als auch fuer +# die REMOTE-Bloecke, die dieselbe Variable (mit demselben Default) referenzieren. +STAGE_DIR=${NINLIGHT_STAGE_DIR:-/tmp} echo "== Voraussetzungen auf $HOST pruefen ==" "$SSH_BIN" "$HOST" bash -s <<'REMOTE' @@ -24,14 +31,14 @@ echo " ok" REMOTE echo "== Konfiguration ==" -"$SCP_BIN" -q "$HERE/station.conf.example" "$HOST:/tmp/ninlight-station.conf.example" +"$SCP_BIN" -q "$HERE/station.conf.example" "$HOST:$STAGE_DIR/ninlight-station.conf.example" # CONF ueberschreibbar wie ROOT_DIR/BIN_PATH/UNIT_PATH unten: auf dem Pi bleibt es # beim Default, ein Test kann hier gegen eine Sandbox-Datei statt gegen echte # Systempfade laufen. "$SSH_BIN" "$HOST" bash -s <<'REMOTE' set -e CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf} -TEMPLATE=/tmp/ninlight-station.conf.example +TEMPLATE=${NINLIGHT_STAGE_DIR:-/tmp}/ninlight-station.conf.example if [ -f "$CONF" ]; then # Eine bestehende Konfiguration bleibt bei ihren Werten - nur Schluessel, die # eine neuere Version des CLI zusaetzlich verlangt (z.B. die Migrations- und @@ -43,7 +50,14 @@ if [ -f "$CONF" ]; then ADDED="" for KEY in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION \ MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do - if ! sudo grep -Eq "^${KEY}=" "$CONF"; then + # Nicht nur "^KEY=": eine vorhandene Zuweisung mit fuehrendem Leerraum oder + # einem "export" davor (beides gueltiges ". datei"-Shell) wuerde sonst als + # fehlend gelten, die Vorlagenzeile wird angehaengt, und da das CLI die + # Datei per ". $CONF" sourct, gewinnt die LETZTE Zuweisung - ein eigenes + # Passwort wuerde so still durch den oeffentlichen Vorlagenwert ersetzt + # (Fixrunde 2, N3: genau das "vorhandene Werte nicht ueberschreiben" aus I4, + # nur eine Formatierungsvariante weiter). + if ! sudo grep -Eq "^[[:space:]]*(export[[:space:]]+)?${KEY}=" "$CONF"; then LINE=$(grep -E "^${KEY}=" "$TEMPLATE" || true) if [ -n "$LINE" ]; then printf '%s\n' "$LINE" | sudo tee -a "$CONF" >/dev/null @@ -64,21 +78,22 @@ rm -f "$TEMPLATE" REMOTE echo "== Server, CLI und Unit installieren ==" -"$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" +"$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:$STAGE_DIR/" # Pfade als Variablen mit Default (wie in deploy.sh): so kann ein Test die # Installation gegen ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen # lassen. "$SSH_BIN" "$HOST" bash -s <<'REMOTE' set -e +STAGE_DIR=${NINLIGHT_STAGE_DIR:-/tmp} ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images" -sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" -sudo install -m 0644 /tmp/products.py "$ROOT_DIR/products.py" -sudo install -m 0755 /tmp/ninlight-station "$BIN_PATH" -sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH" -rm -f /tmp/server.py /tmp/products.py /tmp/ninlight-station /tmp/ninlight-station.service +sudo install -m 0755 "$STAGE_DIR/server.py" "$ROOT_DIR/server.py" +sudo install -m 0644 "$STAGE_DIR/products.py" "$ROOT_DIR/products.py" +sudo install -m 0755 "$STAGE_DIR/ninlight-station" "$BIN_PATH" +sudo install -m 0644 "$STAGE_DIR/ninlight-station.service" "$UNIT_PATH" +rm -f "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" "$STAGE_DIR/ninlight-station.service" sudo systemctl daemon-reload echo " installiert" REMOTE diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index dde339e..873f764 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -30,10 +30,37 @@ fail() { echo "FEHLER - $1" >&2; FAILED=1; } file_mode() { # $1 = Pfad. BSD-stat (macOS) und GNU-stat (Linux/Pi) haben unterschiedliche - # Flags - hier reicht "irgendeine funktioniert". - stat -f '%Lp' "$1" 2>/dev/null || stat -c '%a' "$1" 2>/dev/null + # Flags - hier reicht "irgendeine funktioniert". Reihenfolge ist wichtig + # (Fixrunde 2, N1): "stat -f '%Lp' datei" auf GNU-coreutils deutet "-f" als + # "Dateisystemstatus" und '%Lp' als ZWEITES Dateiargument - der Aufruf schreibt + # mehrere Zeilen Dateisystem-Infos auf stdout und scheitert (kein passendes + # zweites File), aber der "||"-Zweig haengt DANACH trotzdem noch "644" an, und + # der Vergleich "= 644" schlaegt fehl, obwohl die Rechte stimmen - auf dem Pi + # (echtes GNU-coreutils) also immer rot. BSD-"stat -c" dagegen bricht sofort + # mit einer Fehlermeldung auf stderr ab, ganz ohne Ausgabe auf stdout - der + # GNU-Aufruf muss deshalb ZUERST stehen. + stat -c '%a' "$1" 2>/dev/null || stat -f '%Lp' "$1" 2>/dev/null } +# Erzwingt den GNU-Zweig von file_mode() (der auf dem Pi tatsaechlich laeuft), +# indem "stat" im PATH auf GNU-coreutils' "gstat" umgebogen wird, falls +# vorhanden (macOS mit "brew install coreutils"). Ohne diese Probe wuerde ein +# falsch herum sortiertes file_mode() auf dieser Entwicklungsmaschine (BSD-stat +# zuerst probiert und erfolgreich) unbemerkt bleiben, obwohl es auf dem Pi +# (nur GNU-stat) rot waere - genau der Fehler aus Fixrunde 2, N1. +if command -v gstat >/dev/null 2>&1; then + GSTAT_CHECK_DIR="$TMPROOT/gstat-check" + mkdir -p "$GSTAT_CHECK_DIR/bin" + ln -sf "$(command -v gstat)" "$GSTAT_CHECK_DIR/bin/stat" + GSTAT_CHECK_FILE="$TMPROOT/gstat-check/probe" + : >"$GSTAT_CHECK_FILE" + chmod 0644 "$GSTAT_CHECK_FILE" + GSTAT_MODE=$(PATH="$GSTAT_CHECK_DIR/bin:$PATH" file_mode "$GSTAT_CHECK_FILE") + [ "$GSTAT_MODE" = "644" ] && pass "file_mode() liefert unter erzwungenem GNU-stat korrekt '644'" || fail "file_mode() liefert unter GNU-stat '$GSTAT_MODE' statt '644' - siehe Fixrunde 2, N1" +else + echo "Hinweis: gstat nicht gefunden - GNU-Zweig von file_mode() wird nur auf dem Pi selbst geprueft" >&2 +fi + # --- Stub-Kommandos fuer nmcli/systemctl/sudo im PATH --- # $1 = bindir, $2 = logdatei, $3 = nmcli-Zustandsverzeichnis, $4 = RUN_DIR, # $5 = Pfad zur systemd-Unit, deren RuntimeDirectoryPreserve-Zeile ueber das @@ -489,6 +516,39 @@ echo "$ERR" | grep -q "unerwartet" && pass "Fehlermeldung nennt den Mismatch" || ACTIVE_AFTER=$(cat "$SBX/nmstate/active") [ -z "$ACTIVE_AFTER" ] && pass "beide Stationsprofile wurden nach dem erkannten Mismatch heruntergefahren" || fail "mindestens ein Profil blieb aktiv: $ACTIVE_AFTER" +echo +echo "=== Zusatzfall N2 (Fixrunde 2): 'status' zeigt keine veraltete Migrationsphase mehr, nachdem in den Rettungsmodus zurueckgeschaltet wurde ===" +SBX="$TMPROOT/tN2" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" migrate on --listen >/dev/null 2>&1 +run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1 # control.json bleibt mit phase=armed liegen +run_cli "$SBX" on >/dev/null 2>&1 # zurueck in den Rettungsmodus - control.json wird bewusst NICHT aufgeraeumt +[ "$(control_phase "$SBX/run/control.json")" = "armed" ] && pass "Vorbedingung: control.json ist tatsaechlich noch mit phase=armed liegen geblieben" || fail "Vorbedingung nicht erfuellt (control.json wurde schon vorher veraendert)" +# status.json so, wie sie der Server nach dem Neustart im Rettungsmodus schreiben +# wuerde (mode=recovery, keine last_seen/phase-Felder - siehe server.py write_state()). +cat >"$SBX/run/status.json" <<'JSON' +{"mode": "recovery"} +JSON +OUT=$(run_cli "$SBX" status 2>&1) +echo "$OUT" | grep -qE "Modus[[:space:]]*: recovery" && pass "Modus zeigt korrekt 'recovery'" || fail "Modus falsch: $OUT" +echo "$OUT" | grep -qi "armed" && fail "status zeigt weiterhin die veraltete Migrationsphase 'armed' im Rettungsmodus: $OUT" || pass "keine veraltete Migrationsphase 'armed' im Rettungsmodus" +echo "$OUT" | grep -qE "Phase[[:space:]]*: -" && pass "Phase zeigt den neutralen Platzhalter '-' ausserhalb des Migrationsmodus" || fail "Phase zeigt keinen neutralen Platzhalter: $OUT" + +echo +echo "=== Zusatzfall N2b (Fixrunde 2): dieselbe veraltete Freigabe, aber ohne aktualisierte status.json (Modus '-') - genau das woertliche Repro aus dem Review ===" +SBX="$TMPROOT/tN2b" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" migrate on --listen >/dev/null 2>&1 +run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1 +run_cli "$SBX" on >/dev/null 2>&1 +rm -f "$SBX/run/status.json" # kein Serverprozess in diesem Test aktualisiert sie +OUT=$(run_cli "$SBX" status 2>&1) +echo "$OUT" | grep -qE "Modus[[:space:]]*: -" && pass "Modus zeigt '-' (keine status.json vorhanden)" || fail "Modus unerwartet: $OUT" +echo "$OUT" | grep -qi "armed" && fail "status zeigt 'armed', obwohl der Modus gar nicht bekannt ist: $OUT" || pass "keine 'armed'-Phase bei unbekanntem Modus" +echo "$OUT" | grep -qE "Phase[[:space:]]*: -" && pass "Phase zeigt '-' statt einer Vermutung" || fail "Phase zeigt keinen neutralen Platzhalter: $OUT" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index 663c0a0..1c18547 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -26,6 +26,18 @@ FAILED=0 pass() { echo "ok - $1"; } fail() { echo "FEHLER - $1" >&2; FAILED=1; } +file_mode() { + # $1 = Pfad. GNU-stat (Linux/Pi) muss zuerst probiert werden, nicht BSD-stat + # (macOS): "stat -f '%Lp' datei" deutet auf GNU-coreutils "-f" als + # "Dateisystemstatus" und '%Lp' als zweites Dateiargument, scheitert dabei + # zwar (rc != 0), schreibt aber vorher schon mehrere Zeilen Dateisystem-Infos + # auf stdout - der "||"-Zweig haengt danach zusaetzlich "600"/"644" an, und + # der Aufrufer sieht Muell statt der reinen Zahl (Fixrunde 2, N1). BSD-"stat + # -c" bricht dagegen sofort mit einem Fehler auf stderr ab, ganz ohne + # Ausgabe auf stdout. + stat -c '%a' "$1" 2>/dev/null || stat -f '%Lp' "$1" 2>/dev/null +} + setup_stubbin() { local bindir=$1 log=$2 mkdir -p "$bindir" @@ -100,16 +112,23 @@ setup_state() { local state=$1 rm -rf "$state" mkdir -p "$state/etc" "$state/opt" "$state/usr/local/bin" "$state/etc/systemd/system" \ - "$state/etc/NetworkManager/system-connections" + "$state/etc/NetworkManager/system-connections" "$state/tmp" } # Fuehrt setup_pi.sh mit gestubbtem PATH und auf die Sandbox umgebogenen # Systempfaden aus. $1 = state-Verzeichnis, $2 = stub-bin-Verzeichnis. +# +# NINLIGHT_STAGE_DIR biegt auch die per scp "uebertragenen" Zwischendateien auf +# die Sandbox um (Fixrunde 2, N4): ohne das wuerden scp/ssh-Stub sie unter dem +# echten /tmp des Entwicklungsrechners ablegen und am Ende per "rm -f" wieder +# loeschen - mit denselben, leicht kollidierenden Namen wie +# "ninlight-station.conf.example" oder "server.py". run_setup() { local state=$1 stubbin=$2 PATH="$stubbin:$PATH" \ NINLIGHT_SSH_BIN="$stubbin/ssh" \ NINLIGHT_SCP_BIN="$stubbin/scp" \ + NINLIGHT_STAGE_DIR="$state/tmp" \ NINLIGHT_STATION_CONF="$state/etc/ninlight-station.conf" \ NINLIGHT_STATION_ROOT="$state/opt/ninlight-station" \ NINLIGHT_STATION_BIN="$state/usr/local/bin/ninlight-station" \ @@ -172,7 +191,7 @@ for KEY in MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do fail "Schluessel $KEY fehlt weiterhin in der Konfiguration" fi done -MODE=$(stat -f '%Lp' "$CONF_OUT" 2>/dev/null || stat -c '%a' "$CONF_OUT" 2>/dev/null) +MODE=$(file_mode "$CONF_OUT") [ "$MODE" = "600" ] && pass "Konfigurationsdatei bleibt 0600" || fail "Konfigurationsdatei hat falsche Rechte: $MODE" echo @@ -187,6 +206,47 @@ echo "$OUT" | grep -q "alle Schluessel vorhanden" && pass "meldet 'alle Schluess DUP=$(grep -c '^EPD_REVISION=' "$CONF_OUT") [ "$DUP" -eq 1 ] && pass "EPD_REVISION steht nur einmal in der Datei (kein doppeltes Anhaengen)" || fail "EPD_REVISION steht $DUP mal in der Datei" +echo +echo "=== Szenario 4 (Fixrunde 2, N3): ein eingerueckter Bestandswert gilt nicht als fehlend ===" +STATE="$TMPROOT/s4-state"; STUBBIN="$TMPROOT/bin4"; LOG="$TMPROOT/s4.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +# Fuehrendes Leerzeichen vor PSK= - gueltiges ". datei"-Shell, aber eine rein +# zeilenverankerte Erkennung ("^PSK=") wuerde das als "fehlt" werten und die +# Vorlagenzeile anhaengen. Da das CLI die Datei per ". $CONF" sourct, gewinnt +# dann die LETZTE Zuweisung - das eigene Passwort waere durch den oeffentlichen +# Default aus der Vorlage ersetzt, ohne dass die Datei das irgendwo anzeigt. +cat >"$STATE/etc/ninlight-station.conf" <<'CONF' +SSID='eigene-ssid' + PSK='eigenes-eingeruecktes-passwort' +IMAGE_DIR='/opt/ninlight-station/images' +HOME_CONNECTION='preconfigured' +AP_CONNECTION='ninlight-recovery' +CONF +chmod 0600 "$STATE/etc/ninlight-station.conf" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +CONF_OUT4="$STATE/etc/ninlight-station.conf" +PSK_LINES=$(grep -cE '^[[:space:]]*PSK=' "$CONF_OUT4") +[ "$PSK_LINES" -eq 1 ] && pass "PSK steht weiterhin nur einmal in der Datei (nicht dupliziert)" || fail "PSK steht $PSK_LINES mal in der Datei: $(cat "$CONF_OUT4")" +if grep -Fq "eigenes-eingeruecktes-passwort" "$CONF_OUT4"; then + pass "der eingerueckte Bestandswert von PSK blieb erhalten" +else + fail "der eingerueckte Bestandswert von PSK wurde ersetzt: $(cat "$CONF_OUT4")" +fi +if grep -Fqx "PSK='ninlight-recovery'" "$CONF_OUT4"; then + fail "der oeffentliche Vorlagen-Default fuer PSK wurde zusaetzlich angehaengt - eigenes Passwort waere beim Sourcen ueberschrieben worden" +else + pass "der oeffentliche Vorlagen-Default fuer PSK wurde NICHT angehaengt" +fi +for KEY in MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do + grep -Eq "^${KEY}=" "$CONF_OUT4" && pass "fehlender Schluessel $KEY wurde trotzdem ergaenzt" || fail "Schluessel $KEY fehlt weiterhin" +done + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From 59c8c150bd4984621cb64932edb5283ccb174ca2 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 19:47:51 +0200 Subject: [PATCH 114/150] feat(station): Migrationsimages beim Deploy signieren deploy.sh signiert pro Ziel das rohe _gz.img (nie das _ota.img) mit den Produktions- und Entwicklungsschluesseln aus $NINLIGHT_SIGN_DIR und legt die entstandenen _prod.img/_dev.img samt manifest.json unter migrate/ ab. Fehlt das Schluesselverzeichnis oder einzelne Schluessel, entfaellt nur die jeweilige Migrationsvariante mit einer Meldung auf stderr - die Rettungsimages werden unveraendert weiter ausgeliefert. migrate/ wird beim rsync-Abgleich auf dem Pi bewusst getrennt von den _ota.img behandelt: fehlt es lokal (kein Schluesselverzeichnis auf dem Entwicklungsrechner), bleibt ein vorhandenes migrate/ auf dem Pi unangetastet statt durch --delete geleert zu werden - derselbe Fehler, der frueher schon einmal fremde Geraetetypen weggeraeumt hat, diesmal auf die Migrationsimages angewendet. test_deploy.sh bekommt acht neue Szenarien fuer die sieben im Task-Brief verlangten Faelle plus den zusaetzlichen Schutz vor dem geleerten migrate/. Alle Szenarien erzeugen ihre eigenen Wegwerf-Testschluessel ueber nl_sign.generate_test_key() und setzen NINLIGHT_SIGN_DIR bei jedem Aufruf explizit (auch im "ohne Schluessel"-Fall) - der Default $HOME/workspace/fw-sign zeigt auf diesem Rechner auf echte Schluessel, und kein Testlauf darf sie je lesen. --- tools/station/deploy.sh | 108 ++++++++- tools/station/tests/test_deploy.sh | 337 +++++++++++++++++++++++++++++ 2 files changed, 443 insertions(+), 2 deletions(-) diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index eb03501..0539531 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -51,6 +51,7 @@ echo "== Images pruefen ==" STAGE=$(mktemp -d) trap 'rm -rf "$STAGE"' EXIT printf '%-26s %-10s %s\n' GERAET VERSION BYTES +declare -A NINL_VERSION=() for f in "${FILES[@]}"; do base=$(basename "$f") target=${base#ninlight_}; target=${target%_ota.img} @@ -61,9 +62,93 @@ for f in "${FILES[@]}"; do # shellcheck disable=SC2086 set -- $out printf '%-26s %-10s %s\n' "$1" "$2" "$3" + NINL_VERSION[$target]="$2" cp "$f" "$STAGE/$base" done +echo "== Migrationsimages signieren ==" +# Die Signierschluessel gehoeren nie ins Repo und nie auf den Pi - sie werden hier nur +# gelesen, unter $NINLIGHT_SIGN_DIR (Vorgabe ~/workspace/fw-sign auf DIESEM Rechner). +# Fehlt das Verzeichnis oder ein einzelner Schluessel, entfaellt die jeweilige +# Migrationsvariante mit einer Meldung - die Rettungsimages (_ota.img) werden trotzdem +# ausgeliefert (Exit-Code bleibt 0). +SIGN_DIR=${NINLIGHT_SIGN_DIR:-$HOME/workspace/fw-sign} +KEY_PROD="$SIGN_DIR/QCX_Device_CA/qcx_firmware_signing_key.pem" +KEY_DEV="$SIGN_DIR/fw_sign.pem" +NL_SIGN="$ROOT/tools/mkimage/nl_sign.py" + +if [ -d "$SIGN_DIR" ]; then + mkdir -p "$STAGE/migrate" + HAVE_PROD=0 + HAVE_DEV=0 + if [ -f "$KEY_PROD" ]; then + HAVE_PROD=1 + else + echo "Hinweis: Produktionsschluessel fehlt - keine _prod.img-Migrationsimages" >&2 + fi + if [ -f "$KEY_DEV" ]; then + HAVE_DEV=1 + else + echo "Hinweis: Entwicklungsschluessel fehlt - keine _dev.img-Migrationsimages" >&2 + fi + for f in "${FILES[@]}"; do + base=$(basename "$f") + target=${base#ninlight_}; target=${target%_ota.img} + # Signiert wird IMMER das rohe _gz.img, niemals das fertige _ota.img: beide belegen + # denselben 512-Byte-Block am Dateiende (Trailer bzw. Signatur), ein signiertes + # _ota.img waere ein Image, das die alte Lampe ablehnt oder halb annimmt. + gz="$ROOT/build/$target/ninlight_${target}_gz.img" + [ -f "$gz" ] || { + echo "Fehler: $gz fehlt - kein Migrationsimage fuer $target moeglich" >&2; exit 1; } + if [ "$HAVE_PROD" -eq 1 ]; then + python3 "$NL_SIGN" sign --key "$KEY_PROD" --in "$gz" --out "$STAGE/migrate/${target}_prod.img" \ + || { echo "Fehler: Signieren von $target (prod) fehlgeschlagen" >&2; exit 1; } + fi + if [ "$HAVE_DEV" -eq 1 ]; then + python3 "$NL_SIGN" sign --key "$KEY_DEV" --in "$gz" --out "$STAGE/migrate/${target}_dev.img" \ + || { echo "Fehler: Signieren von $target (dev) fehlgeschlagen" >&2; exit 1; } + fi + done + VERSION_ARGS=() + for t in "${!NINL_VERSION[@]}"; do + VERSION_ARGS+=("$t=${NINL_VERSION[$t]}") + done + # manifest.json aus den entstandenen Dateien - Groesse und sha256 werden hier neu + # berechnet statt aus dem Signierschritt uebernommen, damit die Datei die Wahrheit + # ueber das tatsaechlich abgelegte Image sagt. + python3 - "$STAGE/migrate" "${VERSION_ARGS[@]}" <<'PY' +import hashlib +import json +import os +import sys + +migrate_dir = sys.argv[1] +version_map = dict(a.split("=", 1) for a in sys.argv[2:]) +entries = [] +for name in sorted(os.listdir(migrate_dir)): + if name.endswith("_prod.img"): + target, variant = name[: -len("_prod.img")], "prod" + elif name.endswith("_dev.img"): + target, variant = name[: -len("_dev.img")], "dev" + else: + continue + with open(os.path.join(migrate_dir, name), "rb") as fh: + data = fh.read() + entries.append({ + "target": target, + "variant": variant, + "bytes": len(data), + "sha256": hashlib.sha256(data).hexdigest(), + "ninlight_version": version_map.get(target, ""), + }) +with open(os.path.join(migrate_dir, "manifest.json"), "w") as fh: + json.dump({"images": entries}, fh, indent=2, sort_keys=True) + fh.write("\n") +PY +else + echo "Hinweis: Schluesselverzeichnis $SIGN_DIR nicht gefunden - keine Migrationsimages erzeugt" >&2 +fi + echo "== uebertragen ==" # -c (Inhaltsvergleich per Checksumme statt Groesse+Zeitstempel): ein neues # Image kann zufaellig dieselbe Byte-Groesse wie das alte haben, und bei @@ -105,12 +190,31 @@ if [ "$FULL_SYNC" -eq 1 ]; then else echo " voller Abgleich - keine Images werden entfernt" fi - sudo rsync -a -c --delete /tmp/ninlight-images/ "$ROOT_DIR/images/" + # migrate/ wird gleich in einem eigenen Schritt behandelt (siehe unten) - hier + # ausklammern, sonst wuerde --delete es leeren, wenn dieser Deploy ohne + # Schluesselverzeichnis lief und /tmp/ninlight-images/migrate deshalb gar nicht + # existiert. + sudo rsync -a -c --delete --exclude=/migrate /tmp/ninlight-images/ "$ROOT_DIR/images/" else # Gezielter Deploy: nur die uebertragenen Dateien ersetzen/ergaenzen, alle # anderen Geraetetypen im Zielverzeichnis bleiben erhalten (kein --delete). echo " gezielter Deploy - andere Geraetetypen im Zielverzeichnis bleiben erhalten" - sudo rsync -a -c /tmp/ninlight-images/ "$ROOT_DIR/images/" + sudo rsync -a -c --exclude=/migrate /tmp/ninlight-images/ "$ROOT_DIR/images/" +fi +# Migrationsimages getrennt behandeln: fehlt migrate/ in der Uebertragung (weil auf +# dem Entwicklungsrechner kein Schluesselverzeichnis vorhanden war), darf ein +# vorhandenes migrate/ auf dem Pi NICHT geleert werden - sonst zerstoert ein Deploy +# ohne Schluesselzugriff die Bestueckung frueherer Deploys (derselbe Fehler wie +# frueher bei den _ota.img, siehe oben, nur diesmal beim Fehlen statt beim Ersetzen). +if [ -d /tmp/ninlight-images/migrate ]; then + sudo install -d -m 0755 "$ROOT_DIR/images/migrate" + if [ "$FULL_SYNC" -eq 1 ]; then + sudo rsync -a -c --delete /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" + else + sudo rsync -a -c /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" + fi +else + echo " kein Schluesselverzeichnis auf dem Entwicklungsrechner - migrate/ auf dem Pi bleibt unangetastet" fi sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" sudo install -m 0644 /tmp/products.py "$ROOT_DIR/products.py" diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index 85e3d56..3a9b977 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -14,6 +14,7 @@ set -euo pipefail HERE=$(cd "$(dirname "$0")" && pwd) STATION_DIR=$(cd "$HERE/.." && pwd) +MKIMAGE_DIR=$(cd "$STATION_DIR/../mkimage" && pwd) REAL_RSYNC=$(command -v rsync) TMPROOT=$(mktemp -d) @@ -30,6 +31,10 @@ setup_sandbox() { mkdir -p "$sandbox/tools/station" "$sandbox/tools/mkimage" "$sandbox/build" cp "$STATION_DIR/deploy.sh" "$sandbox/tools/station/deploy.sh" chmod +x "$sandbox/tools/station/deploy.sh" + # Das ECHTE Signierwerkzeug wird gebraucht (nicht gestubbt): die Migrations-Szenarien + # pruefen echtes Signieren/die echten Formatchecks (Mindestlaenge, kein Trailer, 4096 + # Bit). Nur die Schluessel selbst sind Wegwerf-Testschluessel, nie echte. + cp "$MKIMAGE_DIR/nl_sign.py" "$sandbox/tools/mkimage/nl_sign.py" # Inhalt irrelevant, die Dateien muessen nur existieren (werden "uebertragen"). echo "# dummy" >"$sandbox/tools/station/server.py" echo "# dummy" >"$sandbox/tools/station/products.py" @@ -114,14 +119,75 @@ run_deploy() { # Auf dem echten Pi existieren /usr/local/bin und /etc/systemd/system schon; # in der Sandbox muessen sie fuer "install" eigens angelegt werden. mkdir -p "$SANDBOX_STATE/usr/local/bin" "$SANDBOX_STATE/etc/systemd/system" + # NINLIGHT_SIGN_DIR IMMER explizit setzen: deploy.sh faellt sonst auf + # $HOME/workspace/fw-sign zurueck, und auf Entwicklungsrechnern liegen dort die + # ECHTEN Signierschluessel. Ein Test darf die niemals auch nur lesen. Szenarien, + # die ein Schluesselverzeichnis brauchen, ueberschreiben die Variable gezielt VOR + # dem Aufruf von run_deploy (siehe unten); dieser Default zeigt auf einen Pfad, + # der in der Sandbox garantiert nicht existiert. PATH="$STUBBIN:$PATH" \ NINLIGHT_SSH_BIN="$STUBBIN/ssh" \ NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ + NINLIGHT_SIGN_DIR="${NINLIGHT_SIGN_DIR:-$sandbox/no-such-sign-dir}" \ "$sandbox/tools/station/deploy.sh" "$@" } +# --- Hilfsmittel fuer die Migrations-Szenarien --- + +# Erzeugt Wegwerf-Testschluessel (nl_sign.generate_test_key(), nie echte Schluessel, +# nie geschrieben ausser in dieses Sandbox-Verzeichnis) unter $keydir. with_prod/ +# with_dev steuern, welche der beiden Dateien entsteht. +make_test_keys() { + local keydir=$1 with_prod=$2 with_dev=$3 + mkdir -p "$keydir/QCX_Device_CA" + PYTHONPATH="$MKIMAGE_DIR" python3 - "$keydir" "$with_prod" "$with_dev" <<'PY' +import sys +import nl_sign + +keydir, with_prod, with_dev = sys.argv[1], sys.argv[2] == "1", sys.argv[3] == "1" +if with_prod: + k = nl_sign.generate_test_key() + with open(keydir + "/QCX_Device_CA/qcx_firmware_signing_key.pem", "wb") as fh: + fh.write(k.private_pem) +if with_dev: + k = nl_sign.generate_test_key() + with open(keydir + "/fw_sign.pem", "wb") as fh: + fh.write(k.private_pem) +PY +} + +# Erzeugt eine gueltige rohe _gz.img-Attrappe: >= 1024 Byte, durch 4 teilbar, ohne +# NINL-Trailer - genau das, was nl_sign.py als Eingabe akzeptiert. +make_gz_file() { + local path=$1 fill=${2:-A} + python3 -c " +import sys +path, fill = sys.argv[1], sys.argv[2].encode() +data = bytearray((fill * 2048)[:2048]) +data[-512:-508] = b'\\x00\\x00\\x00\\x00' # nie zufaellig wie 'NINL' aussehen +with open(path, 'wb') as fh: + fh.write(bytes(data)) +" "$path" "$fill" +} + +# Erzeugt eine _ota.img-Attrappe mit ANDEREM Inhalt als das zugehoerige _gz.img und +# einem echten NINL-Trailer - damit ein versehentliches Signieren des _ota.img statt +# des _gz.img (Fall 7) zweifelsfrei auffliegt: weder Inhalt noch Groesse passen dann. +make_ota_file() { + local path=$1 + python3 -c " +import sys +path = sys.argv[1] +body = b'B' * 2048 +trailer = bytearray(b'\\xff' * 512) +trailer[0:4] = b'NINL' +with open(path, 'wb') as fh: + fh.write(body + bytes(trailer)) +" "$path" +} + echo "=== Szenario 1: Image besteht die Trailer-Pruefung nicht -> nichts uebertragen ===" SANDBOX="$TMPROOT/s1"; STUBBIN="$TMPROOT/bin1"; LOG="$TMPROOT/s1.log"; SANDBOX_STATE="$TMPROOT/s1-state" setup_sandbox "$SANDBOX" @@ -238,6 +304,7 @@ OUT=$(FAKE_SYSTEMCTL_ACTIVE_EXIT=0 FAKE_SYSTEMCTL_RESTART_EXIT=1 \ NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ + NINLIGHT_SIGN_DIR="$SANDBOX/no-such-sign-dir" \ "$SANDBOX/tools/station/deploy.sh" pi-test foo 2>&1) RC=$? set -e @@ -357,6 +424,276 @@ else fail "Ausgabe meldet nicht, dass nichts entfernt wird: $OUT" fi +echo +echo "=== Vorbereitung: Wegwerf-Testschluessel fuer die Migrations-Szenarien ===" +# Nur ueber nl_sign.generate_test_key() erzeugt, nie ausserhalb dieses Testlaufs +# verwendet und nie in ein Repository- oder Log-File jenseits dieser TMPROOT-Sandbox +# geschrieben. +KEYDIR_FULL="$TMPROOT/keys-full" +make_test_keys "$KEYDIR_FULL" 1 1 +KEYDIR_DEV_ONLY="$TMPROOT/keys-dev-only" +make_test_keys "$KEYDIR_DEV_ONLY" 0 1 +pass "Testschluessel erzeugt" + +echo +echo "=== Szenario 10: Mit vorhandenem Schluesselverzeichnis entstehen zwei Migrationsimages je Ziel (Fall 1) ===" +SANDBOX="$TMPROOT/s10"; STUBBIN="$TMPROOT/bin10"; LOG="$TMPROOT/s10.log"; SANDBOX_STATE="$TMPROOT/s10-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +GZ_SIZE=$(wc -c <"$SANDBOX/build/foo/ninlight_foo_gz.img" | tr -d ' ') +PROD_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" +DEV_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +if [ -f "$PROD_IMG" ] && [ -f "$DEV_IMG" ]; then + pass "beide Migrationsimages (prod, dev) wurden angelegt" +else + fail "mindestens ein Migrationsimage fehlt: $OUT" +fi +for img in "$PROD_IMG" "$DEV_IMG"; do + [ -f "$img" ] || continue + size=$(wc -c <"$img" | tr -d ' ') + expected=$((GZ_SIZE + 512)) + if [ "$size" -eq "$expected" ]; then + pass "$(basename "$img") ist genau 512 Byte groesser als das _gz.img ($size Byte)" + else + fail "$(basename "$img") hat $size Byte, erwartet $expected" + fi +done + +echo +echo "=== Szenario 11: manifest.json listet jedes ausgelieferte Migrationsimage (Fall 2) ===" +SANDBOX="$TMPROOT/s11"; STUBBIN="$TMPROOT/bin11"; LOG="$TMPROOT/s11.log"; SANDBOX_STATE="$TMPROOT/s11-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +MANIFEST="$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" +if [ "$RC" -eq 0 ] && [ -f "$MANIFEST" ]; then + pass "manifest.json wurde angelegt" +else + fail "manifest.json fehlt oder Exit-Code $RC: $OUT" +fi +set +e +CHECK=$(python3 -c " +import json +with open('$MANIFEST') as fh: + data = json.load(fh) +entries = data.get('images', []) +by_variant = {e.get('variant'): e for e in entries if e.get('target') == 'foo'} +ok = ( + len(entries) == 2 + and set(by_variant) == {'prod', 'dev'} + and all(isinstance(e.get('bytes'), int) and e['bytes'] > 0 for e in entries) + and all(isinstance(e.get('sha256'), str) and len(e['sha256']) == 64 for e in entries) +) +print('OK' if ok else 'FAIL: ' + json.dumps(entries)) +" 2>&1) +set -e +if [ "$CHECK" = "OK" ]; then + pass "manifest.json listet target/variant/bytes/sha256 fuer beide Images" +else + fail "manifest.json-Inhalt unerwartet: $CHECK" +fi + +echo +echo "=== Szenario 12: Ohne NINLIGHT_SIGN_DIR entfallen die Migrationsimages, das Rettungsimage kommt trotzdem an (Fall 3) ===" +SANDBOX="$TMPROOT/s12"; STUBBIN="$TMPROOT/bin12"; LOG="$TMPROOT/s12.log"; SANDBOX_STATE="$TMPROOT/s12-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_foo_ota.img" ]; then + pass "Rettungsimage (_ota.img) wurde trotzdem ausgeliefert" +else + fail "Rettungsimage fehlt: $OUT" +fi +if [ ! -d "$SANDBOX_STATE/opt/ninlight-station/images/migrate" ] \ + || [ -z "$(ls -A "$SANDBOX_STATE/opt/ninlight-station/images/migrate" 2>/dev/null)" ]; then + pass "keine Migrationsimages entstanden" +else + fail "Migrationsimages entstanden, obwohl kein Schluesselverzeichnis vorhanden war" +fi +if echo "$OUT" | grep -q "Schluesselverzeichnis"; then + pass "Meldung ueber fehlendes Schluesselverzeichnis vorhanden" +else + fail "keine Meldung ueber das fehlende Schluesselverzeichnis: $OUT" +fi + +echo +echo "=== Szenario 13: Schluesselverzeichnis ohne Produktionsschluessel - nur dev-Images entstehen (Fall 4) ===" +SANDBOX="$TMPROOT/s13"; STUBBIN="$TMPROOT/bin13"; LOG="$TMPROOT/s13.log"; SANDBOX_STATE="$TMPROOT/s13-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_DEV_ONLY" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" ]; then + pass "dev-Migrationsimage wurde angelegt" +else + fail "dev-Migrationsimage fehlt: $OUT" +fi +if [ ! -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" ]; then + pass "prod-Migrationsimage entfiel wie erwartet" +else + fail "prod-Migrationsimage wurde trotz fehlendem Produktionsschluessel angelegt" +fi +if echo "$OUT" | grep -q "Produktionsschluessel fehlt"; then + pass "Meldung ueber fehlenden Produktionsschluessel vorhanden" +else + fail "keine Meldung ueber den fehlenden Produktionsschluessel: $OUT" +fi + +echo +echo "=== Szenario 14: gezielter Deploy erzeugt nur die Migrationsimages des angegebenen Ziels (Fall 5, 4a-Fehler fuer migrate/) ===" +SANDBOX="$TMPROOT/s14"; STUBBIN="$TMPROOT/bin14"; LOG="$TMPROOT/s14.log"; SANDBOX_STATE="$TMPROOT/s14-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build/bar" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" A +make_gz_file "$SANDBOX/build/bar/ninlight_bar_gz.img" C +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +echo "bar-ota" >"$SANDBOX/build/bar/ninlight_bar_ota.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" +echo "vorhandenes-bar-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_prod.img" +echo "vorhandenes-bar-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_dev.img" +: >"$LOG" +set +e +OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" ] \ + && [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" ]; then + pass "Migrationsimages fuer das angegebene Ziel (foo) wurden angelegt" +else + fail "Migrationsimages fuer foo fehlen: $OUT" +fi +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_prod.img" 2>/dev/null)" = "vorhandenes-bar-prod" ] \ + && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_dev.img" 2>/dev/null)" = "vorhandenes-bar-dev" ]; then + pass "Migrationsimages des NICHT ausgewaehlten Geraetetyps (bar) blieben unangetastet" +else + fail "Migrationsimages von bar wurden veraendert oder geloescht - 4a-Fehler fuer migrate/" +fi + +echo +echo "=== Szenario 15: Kein privater Schluessel und kein Schluesselpfad-Inhalt in der Ausgabe (Fall 6) ===" +SANDBOX="$TMPROOT/s15"; STUBBIN="$TMPROOT/bin15"; LOG="$TMPROOT/s15.log"; SANDBOX_STATE="$TMPROOT/s15-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if echo "$OUT" | grep -q -- "-----BEGIN"; then + fail "PEM-Header (Schluesselinhalt) taucht in der Ausgabe auf" +else + pass "kein PEM-Header in der Ausgabe" +fi +# Eine Zeile aus der Mitte des privaten Produktionsschluessels darf unter keinen +# Umstaenden in der Ausgabe erscheinen. +KEY_LINE=$(sed -n '3p' "$KEYDIR_FULL/QCX_Device_CA/qcx_firmware_signing_key.pem") +if [ -n "$KEY_LINE" ] && echo "$OUT" | grep -qF "$KEY_LINE"; then + fail "eine Zeile des privaten Produktionsschluessels taucht in der Ausgabe auf" +else + pass "keine Schluesselzeile in der Ausgabe" +fi + +echo +echo "=== Szenario 16: Signiert wird das _gz.img, niemals das _ota.img (Fall 7, gefaehrlichster Fehler) ===" +SANDBOX="$TMPROOT/s16"; STUBBIN="$TMPROOT/bin16"; LOG="$TMPROOT/s16.log"; SANDBOX_STATE="$TMPROOT/s16-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" A +make_ota_file "$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +DEV_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +if [ -f "$DEV_IMG" ]; then + set +e + python3 -c " +import sys +with open(sys.argv[1], 'rb') as fh: + signed = fh.read() +with open(sys.argv[2], 'rb') as fh: + gz = fh.read() +sys.exit(0 if signed[:-512] == gz else 1) +" "$DEV_IMG" "$SANDBOX/build/foo/ninlight_foo_gz.img" + PAYLOAD_MATCH=$? + set -e + if [ "$PAYLOAD_MATCH" -eq 0 ]; then + pass "die signierte Nutzlast ist byteidentisch mit dem _gz.img" + else + fail "die signierte Nutzlast entspricht NICHT dem _gz.img - das _ota.img wurde signiert!" + fi +else + fail "dev-Migrationsimage wurde nicht angelegt: $OUT" +fi + +echo +echo "=== Szenario 17: Ohne Schluesselverzeichnis bleibt ein vorhandenes migrate/ beim Voll-Deploy unangetastet ===" +SANDBOX="$TMPROOT/s17"; STUBBIN="$TMPROOT/bin17"; LOG="$TMPROOT/s17.log"; SANDBOX_STATE="$TMPROOT/s17-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" +echo "vorhandenes-foo-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" +echo "vorhandenes-foo-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" 2>/dev/null)" = "vorhandenes-foo-prod" ] \ + && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" 2>/dev/null)" = "vorhandenes-foo-dev" ]; then + pass "vorhandenes migrate/ blieb beim Voll-Deploy ohne Schluesselverzeichnis unangetastet" +else + fail "migrate/ wurde geleert, obwohl kein Schluesselverzeichnis vorhanden war - zerstoert die Bestueckung" +fi +if echo "$OUT" | grep -q "migrate/ auf dem Pi bleibt unangetastet"; then + pass "Ausgabe kuendigt an, dass migrate/ unangetastet bleibt" +else + fail "Ausgabe erwaehnt nicht, dass migrate/ unangetastet bleibt: $OUT" +fi + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From ad2f1b8486f1fe00c67654ae4e574b5151e1acd5 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 20:14:19 +0200 Subject: [PATCH 115/150] fix(station): Manifest-Form, Teilschluessel-Faelle und Schluessel-Testisolation Fixrunde 1 zu "Migrationsimages beim Deploy signieren": - manifest.json traegt ninlight_version und ein neues built jetzt auf oberster Ebene statt pro Eintrag, dazu "file" statt der Redundanz einer Version pro Bild - genau die Form aus der Spec (Abschnitt 4) und aus server.py's Station.manifest_version(), die vorher immer "unbekannt" gelesen hat, weil sie ausschliesslich auf oberster Ebene sucht (C1). Ein Testfall schickt das Manifest jetzt zusaetzlich durch genau diese Funktion statt nur durch json.load. - Ein Schluesselverzeichnis, das existiert, aber weder Produktions- noch Entwicklungsschluessel enthaelt, legt kein STAGE/migrate mehr an - das vorherige Verhalten liess dafuer trotzdem ein (leeres) migrate/ mit Manifest entstehen, das der volle rsync-Abgleich dann mit --delete gegen ein vorhandenes migrate/ auf dem Pi durchgesetzt und es geleert hat (C2). Aus demselben Grund war die _gz.img-Pflichtpruefung faelschlich unconditional: fehlte sie, brach sogar ein Deploy ab, der gar nicht signieren wollte, und nahm die Rettungsimages gleich mit (C3). - Ein neues MIGRATE_COMPLETE-Flag (nur 1, wenn dieser Lauf mit BEIDEN Schluesseln signiert hat) entscheidet jetzt, ob der Abgleich auf dem Pi --delete benutzen und das Manifest ersetzen darf. Bei nur einem der beiden Schluessel oder einem gezielten Deploy synct migrate/ ohne --delete und merged das Manifest serverseitig mit dem vorhandenen zusammen, statt es zu ueberschreiben - vorher raeumte ein Voll-Deploy mit nur dem dev-Schluessel saemtliche _prod.img weg (C2, Teilfall), und ein gezielter Deploy liess ein Manifest zurueck, das die unveraendert gebliebenen Ziele nicht mehr erwaehnte (I1). - ${HOME:-} statt $HOME beim Ermitteln von SIGN_DIR, damit ein Aufruf unter `set -u` ohne gesetztes HOME (cron, env -i) nicht unnoetig abbricht, obwohl nur die Rettungsimages gebraucht werden. - tests/test_deploy.sh: die Sicherheitsabsicherung aus dem letzten Durchgang hatte selbst eine Luecke (C4) - "${NINLIGHT_SIGN_DIR:-default}" in run_deploy() greift nur, wenn die Variable NICHT gesetzt ist, und genau die ist es, mit der man auf ~/workspace/fw-sign (das ECHTE Schluesselverzeichnis auf diesem Rechner) zeigt. run_deploy() setzt NINLIGHT_SIGN_DIR jetzt hart und liest dafuer nur noch die eigene Testvariable TEST_SIGN_DIR, die deploy.sh nicht kennt und die deshalb nie von aussen vorbelegt sein kann; am Dateikopf werden zur Sicherheit beide Variablen ge-unset. Verifiziert: die Suite bleibt gruen, wenn NINLIGHT_SIGN_DIR vorher exportiert ist wie in einem echten Entwicklerprofil. - Szenario 15 vergleicht jetzt ALLE Zeilen beider privater Testschluessel gegen Ausgabe UND Log, nicht nur eine einzelne Zeile des Produktions- schluessels gegen die Ausgabe. Vier neue/erweiterte Testfaelle (18-20) plus die Ergaenzung an Szenario 14: Verzeichnis vorhanden aber leer, Voll-Deploy mit nur dev-Schluessel, echter Server-Leser fuer das Manifest, gezielter Deploy fuehrt das Manifest zusammen statt es zu verkuerzen. Jeder der vier Critical-Befunde wurde vor dem Fix per Rot-Probe am kaputten Code bestaetigt. --- tools/station/deploy.sh | 153 +++++++++++++----- tools/station/tests/test_deploy.sh | 241 +++++++++++++++++++++++++---- 2 files changed, 328 insertions(+), 66 deletions(-) diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index 0539531..12892a5 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -72,13 +72,21 @@ echo "== Migrationsimages signieren ==" # Fehlt das Verzeichnis oder ein einzelner Schluessel, entfaellt die jeweilige # Migrationsvariante mit einer Meldung - die Rettungsimages (_ota.img) werden trotzdem # ausgeliefert (Exit-Code bleibt 0). -SIGN_DIR=${NINLIGHT_SIGN_DIR:-$HOME/workspace/fw-sign} +# ${HOME:-} statt $HOME: unter `set -u` (z.B. via cron oder env -i) waere ein +# unbound-variable-Abbruch hier voellig unnoetig, denn selbst ohne Migrationsimages +# sollen die Rettungsimages ausgeliefert werden (Fixrunde 1, Kleinigkeit 1). +SIGN_DIR=${NINLIGHT_SIGN_DIR:-${HOME:-}/workspace/fw-sign} KEY_PROD="$SIGN_DIR/QCX_Device_CA/qcx_firmware_signing_key.pem" KEY_DEV="$SIGN_DIR/fw_sign.pem" NL_SIGN="$ROOT/tools/mkimage/nl_sign.py" +# MIGRATE_COMPLETE=1 bedeutet: dieser Lauf hat mit BEIDEN Schluesseln signiert, das +# lokale STAGE/migrate beschreibt also den vollstaendigen Satz an Migrationsvarianten +# fuer FILES. Nur dann darf der Abgleich auf dem Pi mit --delete arbeiten bzw. das +# Manifest blind ersetzen - sonst wuerden Images/Eintraege einer Variante verschwinden, +# die dieser Lauf mangels Schluessel gar nicht neu bauen konnte (Fixrunde 1, C2/I1). +MIGRATE_COMPLETE=0 if [ -d "$SIGN_DIR" ]; then - mkdir -p "$STAGE/migrate" HAVE_PROD=0 HAVE_DEV=0 if [ -f "$KEY_PROD" ]; then @@ -91,36 +99,51 @@ if [ -d "$SIGN_DIR" ]; then else echo "Hinweis: Entwicklungsschluessel fehlt - keine _dev.img-Migrationsimages" >&2 fi - for f in "${FILES[@]}"; do - base=$(basename "$f") - target=${base#ninlight_}; target=${target%_ota.img} - # Signiert wird IMMER das rohe _gz.img, niemals das fertige _ota.img: beide belegen - # denselben 512-Byte-Block am Dateiende (Trailer bzw. Signatur), ein signiertes - # _ota.img waere ein Image, das die alte Lampe ablehnt oder halb annimmt. - gz="$ROOT/build/$target/ninlight_${target}_gz.img" - [ -f "$gz" ] || { - echo "Fehler: $gz fehlt - kein Migrationsimage fuer $target moeglich" >&2; exit 1; } - if [ "$HAVE_PROD" -eq 1 ]; then - python3 "$NL_SIGN" sign --key "$KEY_PROD" --in "$gz" --out "$STAGE/migrate/${target}_prod.img" \ - || { echo "Fehler: Signieren von $target (prod) fehlgeschlagen" >&2; exit 1; } + if [ "$HAVE_PROD" -eq 1 ] || [ "$HAVE_DEV" -eq 1 ]; then + # Erst HIER, nachdem feststeht dass ueberhaupt signiert wird, STAGE/migrate anlegen + # und das fehlende _gz.img zum Abbruch machen (Fixrunde 1, C2+C3): ein + # Schluesselverzeichnis ohne jeden Schluessel darf weder ein leeres migrate/ + # erzeugen (das wuerde ein vorhandenes migrate/ auf dem Pi mit --delete leerraeumen) + # noch am Fehlen des _gz.img scheitern, wenn gar nicht signiert werden soll. + mkdir -p "$STAGE/migrate" + for f in "${FILES[@]}"; do + base=$(basename "$f") + target=${base#ninlight_}; target=${target%_ota.img} + # Signiert wird IMMER das rohe _gz.img, niemals das fertige _ota.img: beide + # belegen denselben 512-Byte-Block am Dateiende (Trailer bzw. Signatur), ein + # signiertes _ota.img waere ein Image, das die alte Lampe ablehnt oder halb + # annimmt. + gz="$ROOT/build/$target/ninlight_${target}_gz.img" + [ -f "$gz" ] || { + echo "Fehler: $gz fehlt - kein Migrationsimage fuer $target moeglich" >&2; exit 1; } + if [ "$HAVE_PROD" -eq 1 ]; then + python3 "$NL_SIGN" sign --key "$KEY_PROD" --in "$gz" --out "$STAGE/migrate/${target}_prod.img" \ + || { echo "Fehler: Signieren von $target (prod) fehlgeschlagen" >&2; exit 1; } + fi + if [ "$HAVE_DEV" -eq 1 ]; then + python3 "$NL_SIGN" sign --key "$KEY_DEV" --in "$gz" --out "$STAGE/migrate/${target}_dev.img" \ + || { echo "Fehler: Signieren von $target (dev) fehlgeschlagen" >&2; exit 1; } + fi + done + if [ "$HAVE_PROD" -eq 1 ] && [ "$HAVE_DEV" -eq 1 ]; then + MIGRATE_COMPLETE=1 fi - if [ "$HAVE_DEV" -eq 1 ]; then - python3 "$NL_SIGN" sign --key "$KEY_DEV" --in "$gz" --out "$STAGE/migrate/${target}_dev.img" \ - || { echo "Fehler: Signieren von $target (dev) fehlgeschlagen" >&2; exit 1; } - fi - done - VERSION_ARGS=() - for t in "${!NINL_VERSION[@]}"; do - VERSION_ARGS+=("$t=${NINL_VERSION[$t]}") - done - # manifest.json aus den entstandenen Dateien - Groesse und sha256 werden hier neu - # berechnet statt aus dem Signierschritt uebernommen, damit die Datei die Wahrheit - # ueber das tatsaechlich abgelegte Image sagt. - python3 - "$STAGE/migrate" "${VERSION_ARGS[@]}" <<'PY' + VERSION_ARGS=() + for t in "${!NINL_VERSION[@]}"; do + VERSION_ARGS+=("$t=${NINL_VERSION[$t]}") + done + # manifest.json aus den entstandenen Dateien - Groesse und sha256 werden hier neu + # berechnet statt aus dem Signierschritt uebernommen, damit die Datei die Wahrheit + # ueber das tatsaechlich abgelegte Image sagt. Form haelt sich an die Spec + # (docs/superpowers/spec, Abschnitt 4) und damit an das, was server.py tatsaechlich + # liest: "ninlight_version" auf oberster Ebene, nicht pro Eintrag (Fixrunde 1, C1 - + # der Server las vorher immer "unbekannt"). + python3 - "$STAGE/migrate" "${VERSION_ARGS[@]}" <<'PY' import hashlib import json import os import sys +from datetime import datetime, timezone migrate_dir = sys.argv[1] version_map = dict(a.split("=", 1) for a in sys.argv[2:]) @@ -137,14 +160,33 @@ for name in sorted(os.listdir(migrate_dir)): entries.append({ "target": target, "variant": variant, + "file": name, "bytes": len(data), "sha256": hashlib.sha256(data).hexdigest(), - "ninlight_version": version_map.get(target, ""), }) + +# Die Spec kennt nur EINE Firmware-Version pro Manifest, nicht eine pro Ziel. Im +# Regelfall bauen alle Ziele eines Deploys aus demselben Firmware-Stand, weichen die +# Versionen trotzdem ab, ist das kein Grund zum Abbrechen - nur eine Warnung, und die +# alphabetisch erste Version gewinnt (deterministisch, nachvollziehbar). +versions = sorted({version_map.get(e["target"], "") for e in entries}) +if len(versions) > 1: + print(f"Warnung: unterschiedliche Firmware-Versionen in diesem Deploy {versions} " + f"- manifest.json verwendet {versions[0]}", file=sys.stderr) +ninlight_version = versions[0] if versions else "" + +manifest = { + "built": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "ninlight_version": ninlight_version, + "images": entries, +} with open(os.path.join(migrate_dir, "manifest.json"), "w") as fh: - json.dump({"images": entries}, fh, indent=2, sort_keys=True) + json.dump(manifest, fh, indent=2, sort_keys=True) fh.write("\n") PY + else + echo "Hinweis: $SIGN_DIR enthaelt weder Produktions- noch Entwicklungsschluessel - keine Migrationsimages erzeugt" >&2 + fi else echo "Hinweis: Schluesselverzeichnis $SIGN_DIR nicht gefunden - keine Migrationsimages erzeugt" >&2 fi @@ -164,9 +206,10 @@ rsync -a -c --delete -e "$SSH_BIN" "$STAGE/" "$HOST:/tmp/ninlight-images/" rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" # Pfade als Variablen mit Default: so kann ein Test die Installation gegen # ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen lassen. -"$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" <<'REMOTE' +"$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" "$MIGRATE_COMPLETE" <<'REMOTE' set -e FULL_SYNC=$1 +MIGRATE_COMPLETE=$2 ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} @@ -202,16 +245,54 @@ else sudo rsync -a -c --exclude=/migrate /tmp/ninlight-images/ "$ROOT_DIR/images/" fi # Migrationsimages getrennt behandeln: fehlt migrate/ in der Uebertragung (weil auf -# dem Entwicklungsrechner kein Schluesselverzeichnis vorhanden war), darf ein -# vorhandenes migrate/ auf dem Pi NICHT geleert werden - sonst zerstoert ein Deploy -# ohne Schluesselzugriff die Bestueckung frueherer Deploys (derselbe Fehler wie -# frueher bei den _ota.img, siehe oben, nur diesmal beim Fehlen statt beim Ersetzen). +# dem Entwicklungsrechner kein Schluesselverzeichnis oder gar kein Schluessel +# vorhanden war), darf ein vorhandenes migrate/ auf dem Pi NICHT geleert werden - +# sonst zerstoert ein Deploy ohne Schluesselzugriff die Bestueckung frueherer Deploys +# (derselbe Fehler wie frueher bei den _ota.img, siehe oben, nur diesmal beim Fehlen +# statt beim Ersetzen). if [ -d /tmp/ninlight-images/migrate ]; then sudo install -d -m 0755 "$ROOT_DIR/images/migrate" - if [ "$FULL_SYNC" -eq 1 ]; then + if [ "$FULL_SYNC" -eq 1 ] && [ "$MIGRATE_COMPLETE" -eq 1 ]; then + # Nur wenn dieser Lauf mit BEIDEN Schluesseln signiert hat, beschreibt + # STAGE/migrate den vollstaendigen Soll-Zustand - erst dann darf --delete + # ueberzaehlige Dateien wegraeumen und manifest.json blind ersetzt werden. sudo rsync -a -c --delete /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" else - sudo rsync -a -c /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" + # Gezielter Deploy ODER ein Voll-Deploy mit nur einem der beiden Schluessel: + # in beiden Faellen kennt dieser Lauf nur einen Teil dessen, was auf dem Pi liegen + # soll. rsync ohne --delete laesst alles Nicht-Uebertragene unangetastet (Fixrunde + # 1, C2 - sonst raeumt ein Voll-Deploy mit nur dem dev-Schluessel saemtliche + # _prod.img weg). manifest.json wird deshalb auch nicht einfach ueberschrieben, + # sondern mit dem, was schon da ist, zusammengefuehrt (Fixrunde 1, I1 - sonst + # verschwinden die Eintraege aller Ziele/Varianten, die dieser Lauf nicht neu + # gebaut hat, obwohl ihre Images unveraendert liegen bleiben). + sudo rsync -a -c --exclude=manifest.json /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" + if [ -f /tmp/ninlight-images/migrate/manifest.json ]; then + sudo python3 - "$ROOT_DIR/images/migrate/manifest.json" /tmp/ninlight-images/migrate/manifest.json <<'MERGE' +import json +import sys + +manifest_path, new_manifest_path = sys.argv[1], sys.argv[2] +with open(new_manifest_path) as fh: + new_manifest = json.load(fh) +try: + with open(manifest_path) as fh: + old_manifest = json.load(fh) +except (OSError, ValueError): + old_manifest = {"images": []} +by_key = {(e.get("target"), e.get("variant")): e for e in old_manifest.get("images", [])} +for e in new_manifest.get("images", []): + by_key[(e.get("target"), e.get("variant"))] = e +merged = { + "built": new_manifest.get("built"), + "ninlight_version": new_manifest.get("ninlight_version"), + "images": sorted(by_key.values(), key=lambda e: (e.get("target") or "", e.get("variant") or "")), +} +with open(manifest_path, "w") as fh: + json.dump(merged, fh, indent=2, sort_keys=True) + fh.write("\n") +MERGE + fi fi else echo " kein Schluesselverzeichnis auf dem Entwicklungsrechner - migrate/ auf dem Pi bleibt unangetastet" diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index 3a9b977..9b5533d 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -17,6 +17,17 @@ STATION_DIR=$(cd "$HERE/.." && pwd) MKIMAGE_DIR=$(cd "$STATION_DIR/../mkimage" && pwd) REAL_RSYNC=$(command -v rsync) +# Sicherheitsnetz (Fixrunde 1, C4): NINLIGHT_SIGN_DIR ist genau die Variable, mit der +# man auf ~/workspace/fw-sign zeigt - und auf diesem Entwicklungsrechner liegen dort +# ECHTE Signierschluessel. Ist sie im aufrufenden Profil oder fuer einen frueheren +# manuellen Deploy exportiert, darf sie unter keinen Umstaenden in diese Suite +# durchsickern. Deshalb: hier explizit loeschen, und run_deploy() unten liest sie NIE +# wieder - jedes Szenario, das echte Testschluessel braucht, uebergibt sie ueber die +# separate Variable TEST_SIGN_DIR (die deploy.sh selbst gar nicht kennt und die daher +# auch nicht zufaellig aus der Umgebung vorbelegt sein kann). +unset NINLIGHT_SIGN_DIR +unset TEST_SIGN_DIR + TMPROOT=$(mktemp -d) trap 'rm -rf "$TMPROOT"' EXIT @@ -119,18 +130,23 @@ run_deploy() { # Auf dem echten Pi existieren /usr/local/bin und /etc/systemd/system schon; # in der Sandbox muessen sie fuer "install" eigens angelegt werden. mkdir -p "$SANDBOX_STATE/usr/local/bin" "$SANDBOX_STATE/etc/systemd/system" - # NINLIGHT_SIGN_DIR IMMER explizit setzen: deploy.sh faellt sonst auf - # $HOME/workspace/fw-sign zurueck, und auf Entwicklungsrechnern liegen dort die - # ECHTEN Signierschluessel. Ein Test darf die niemals auch nur lesen. Szenarien, - # die ein Schluesselverzeichnis brauchen, ueberschreiben die Variable gezielt VOR - # dem Aufruf von run_deploy (siehe unten); dieser Default zeigt auf einen Pfad, - # der in der Sandbox garantiert nicht existiert. + # NINLIGHT_SIGN_DIR wird hier HART auf einen lokalen Wert gesetzt, niemals ueber + # "${NINLIGHT_SIGN_DIR:-default}" aus der geerbten Umgebung uebernommen (das war der + # C4-Fehler aus Fixrunde 1: ein Fallback greift nur, wenn die Variable NICHT gesetzt + # ist - war sie es, z.B. weil ein Profil sie dauerhaft auf ~/workspace/fw-sign zeigen + # laesst, haette der Fallback sie unveraendert durchgereicht). Die einzige Variable, + # die ein Szenario mit echten Testschluesseln setzen darf, ist TEST_SIGN_DIR - eine + # Variable, die deploy.sh selbst gar nicht kennt und die deshalb nie zufaellig von + # aussen vorbelegt sein kann wie NINLIGHT_SIGN_DIR es auf einem Entwicklungsrechner + # ist. Ohne TEST_SIGN_DIR zeigt sie auf einen Pfad, der in der Sandbox garantiert + # nicht existiert. + local sign_dir="${TEST_SIGN_DIR:-$sandbox/no-such-sign-dir}" PATH="$STUBBIN:$PATH" \ NINLIGHT_SSH_BIN="$STUBBIN/ssh" \ NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ - NINLIGHT_SIGN_DIR="${NINLIGHT_SIGN_DIR:-$sandbox/no-such-sign-dir}" \ + NINLIGHT_SIGN_DIR="$sign_dir" \ "$sandbox/tools/station/deploy.sh" "$@" } @@ -298,14 +314,13 @@ mkdir -p "$SANDBOX/build/foo" "$SANDBOX_STATE/usr/local/bin" "$SANDBOX_STATE/etc echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" : >"$LOG" set +e -OUT=$(FAKE_SYSTEMCTL_ACTIVE_EXIT=0 FAKE_SYSTEMCTL_RESTART_EXIT=1 \ - PATH="$STUBBIN:$PATH" \ - NINLIGHT_SSH_BIN="$STUBBIN/ssh" \ - NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ - NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ - NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ - NINLIGHT_SIGN_DIR="$SANDBOX/no-such-sign-dir" \ - "$SANDBOX/tools/station/deploy.sh" pi-test foo 2>&1) +# Ueber run_deploy() statt eines eigenen Aufrufs (Fixrunde 1: der vorherige direkte +# Aufruf hier setzte NINLIGHT_SIGN_DIR zwar auch hart, ging aber am gemeinsamen Muster +# vorbei - ein Praezedenzfall, den ein spaeterer Umbau leicht wieder falsch abschreiben +# koennte). FAKE_SYSTEMCTL_* werden als gewoehnliche vorangestellte Umgebungsvariablen +# durchgereicht, run_deploy() ist eine Funktion und sieht sie genauso wie jeder andere +# Befehl. +OUT=$(FAKE_SYSTEMCTL_ACTIVE_EXIT=0 FAKE_SYSTEMCTL_RESTART_EXIT=1 run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0, wenn der Neustart fehlschlaegt ($RC)"; else fail "Exit-Code war 0, obwohl der Neustart fehlschlug"; fi @@ -445,7 +460,7 @@ make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" : >"$LOG" set +e -OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi @@ -478,7 +493,7 @@ make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" : >"$LOG" set +e -OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e MANIFEST="$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" @@ -488,6 +503,11 @@ else fail "manifest.json fehlt oder Exit-Code $RC: $OUT" fi set +e +# Form haelt sich an die Spec (docs/superpowers/spec Abschnitt 4) und an das, was +# server.py tatsaechlich liest (Fixrunde 1, C1): "ninlight_version" auf oberster +# Ebene, nicht pro Eintrag - ein reines json.load haette das nicht gemerkt, weil es +# nur die Feldnamen INNERHALB der Eintraege prueft. Die naechste Pruefung (Szenario +# 20) schickt dieselbe Datei zusaetzlich durch den echten Server-Leser. CHECK=$(python3 -c " import json with open('$MANIFEST') as fh: @@ -495,16 +515,20 @@ with open('$MANIFEST') as fh: entries = data.get('images', []) by_variant = {e.get('variant'): e for e in entries if e.get('target') == 'foo'} ok = ( - len(entries) == 2 + isinstance(data.get('built'), str) and len(data['built']) > 0 + and data.get('ninlight_version') == '1.0.0' + and len(entries) == 2 and set(by_variant) == {'prod', 'dev'} + and all(e.get('file') in ('foo_prod.img', 'foo_dev.img') for e in entries) and all(isinstance(e.get('bytes'), int) and e['bytes'] > 0 for e in entries) and all(isinstance(e.get('sha256'), str) and len(e['sha256']) == 64 for e in entries) + and all('ninlight_version' not in e for e in entries) ) -print('OK' if ok else 'FAIL: ' + json.dumps(entries)) +print('OK' if ok else 'FAIL: ' + json.dumps(data)) " 2>&1) set -e if [ "$CHECK" = "OK" ]; then - pass "manifest.json listet target/variant/bytes/sha256 fuer beide Images" + pass "manifest.json traegt built/ninlight_version oben und target/variant/file/bytes/sha256 je Eintrag" else fail "manifest.json-Inhalt unerwartet: $CHECK" fi @@ -550,7 +574,7 @@ make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" : >"$LOG" set +e -OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_DEV_ONLY" run_deploy "$SANDBOX" pi-test foo 2>&1) +OUT=$(TEST_SIGN_DIR="$KEYDIR_DEV_ONLY" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi @@ -583,9 +607,18 @@ echo "bar-ota" >"$SANDBOX/build/bar/ninlight_bar_ota.img" mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" echo "vorhandenes-bar-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_prod.img" echo "vorhandenes-bar-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_dev.img" +# Manifest eines frueheren Deploys, das bar beschrieben hat - genau das darf ein +# gezielter Deploy von foo nicht einfach wegschreiben (Fixrunde 1, I1). +cat >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" <<'JSON' +{"built": "2026-01-01T00:00:00Z", "ninlight_version": "0.0.1", + "images": [ + {"target": "bar", "variant": "prod", "file": "bar_prod.img", "bytes": 20, "sha256": "x"}, + {"target": "bar", "variant": "dev", "file": "bar_dev.img", "bytes": 19, "sha256": "y"} + ]} +JSON : >"$LOG" set +e -OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi @@ -601,6 +634,21 @@ if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_prod.img" 2> else fail "Migrationsimages von bar wurden veraendert oder geloescht - 4a-Fehler fuer migrate/" fi +set +e +MANIFEST_CHECK=$(python3 -c " +import json +with open('$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json') as fh: + data = json.load(fh) +keys = {(e.get('target'), e.get('variant')) for e in data.get('images', [])} +expected = {('foo', 'prod'), ('foo', 'dev'), ('bar', 'prod'), ('bar', 'dev')} +print('OK' if keys == expected else 'FAIL: ' + json.dumps(sorted(keys))) +" 2>&1) +set -e +if [ "$MANIFEST_CHECK" = "OK" ]; then + pass "manifest.json beschreibt nach dem gezielten Deploy weiterhin BEIDE Geraetetypen (foo neu, bar zusammengefuehrt)" +else + fail "manifest.json verliert Eintraege beim gezielten Deploy: $MANIFEST_CHECK" +fi echo echo "=== Szenario 15: Kein privater Schluessel und kein Schluesselpfad-Inhalt in der Ausgabe (Fall 6) ===" @@ -612,7 +660,7 @@ make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" : >"$LOG" set +e -OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi @@ -621,13 +669,26 @@ if echo "$OUT" | grep -q -- "-----BEGIN"; then else pass "kein PEM-Header in der Ausgabe" fi -# Eine Zeile aus der Mitte des privaten Produktionsschluessels darf unter keinen -# Umstaenden in der Ausgabe erscheinen. -KEY_LINE=$(sed -n '3p' "$KEYDIR_FULL/QCX_Device_CA/qcx_firmware_signing_key.pem") -if [ -n "$KEY_LINE" ] && echo "$OUT" | grep -qF "$KEY_LINE"; then - fail "eine Zeile des privaten Produktionsschluessels taucht in der Ausgabe auf" +# ALLE Zeilen BEIDER privater Schluessel pruefen (nicht nur eine einzelne Zeile des +# Produktionsschluessels) und dabei auch das ssh/rsync/sudo-Log durchsuchen, nicht nur +# die Kommandozeilenausgabe von deploy.sh selbst (Fixrunde 1: die vorherige Version +# haette einen Leak ueber eine andere Zeile oder ueber $LOG glatt uebersehen). +LEAK="" +while IFS= read -r line; do + [ -n "$line" ] || continue + if echo "$OUT" | grep -qF -- "$line"; then + LEAK="Ausgabe enthaelt: $line" + break + fi + if grep -qF -- "$line" "$LOG"; then + LEAK="Log enthaelt: $line" + break + fi +done < <(cat "$KEYDIR_FULL/QCX_Device_CA/qcx_firmware_signing_key.pem" "$KEYDIR_FULL/fw_sign.pem") +if [ -z "$LEAK" ]; then + pass "keine Zeile eines privaten Schluessels taucht in Ausgabe oder Log auf" else - pass "keine Schluesselzeile in der Ausgabe" + fail "Schluesselinhalt gefunden - $LEAK" fi echo @@ -640,7 +701,7 @@ make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" A make_ota_file "$SANDBOX/build/foo/ninlight_foo_ota.img" : >"$LOG" set +e -OUT=$(NINLIGHT_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi @@ -694,6 +755,126 @@ else fail "Ausgabe erwaehnt nicht, dass migrate/ unangetastet bleibt: $OUT" fi +echo +echo "=== Szenario 18 (Fixrunde 1, C2): Schluesselverzeichnis vorhanden, aber leer - migrate/ bleibt unangetastet, Rettungsimage kommt trotzdem ===" +# Der eigentliche C2-Repro: anders als Szenario 12 (Verzeichnis fehlt komplett) EXISTIERT +# $SIGN_DIR hier, enthaelt aber keinen der beiden Schluessel. Vorher fuehrte das trotzdem +# zu einem (leeren) $STAGE/migrate mit Manifest, das dann per --delete ein vorhandenes +# migrate/ auf dem Pi geleert hat. +SIGN_EMPTY="$TMPROOT/keys-empty" +mkdir -p "$SIGN_EMPTY" +SANDBOX="$TMPROOT/s18"; STUBBIN="$TMPROOT/bin18"; LOG="$TMPROOT/s18.log"; SANDBOX_STATE="$TMPROOT/s18-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" +echo "vorhandenes-foo-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" +echo "vorhandenes-foo-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +echo '{"built": "2026-01-01T00:00:00Z", "ninlight_version": "0.0.1", "images": []}' \ + >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" +: >"$LOG" +set +e +OUT=$(TEST_SIGN_DIR="$SIGN_EMPTY" run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_foo_ota.img" ]; then + pass "Rettungsimage (_ota.img) wurde trotzdem ausgeliefert" +else + fail "Rettungsimage fehlt: $OUT" +fi +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" 2>/dev/null)" = "vorhandenes-foo-prod" ] \ + && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" 2>/dev/null)" = "vorhandenes-foo-dev" ] \ + && grep -q '"images": \[\]' "$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json"; then + pass "vorhandenes migrate/ (Bilder UND manifest.json) blieb unangetastet - Verzeichnis war da, aber leer" +else + fail "migrate/ wurde trotz vorhandenem, aber leerem Schluesselverzeichnis veraendert/geleert - C2 nicht behoben" +fi +if echo "$OUT" | grep -q "enthaelt weder Produktions- noch Entwicklungsschluessel"; then + pass "Meldung nennt das leere Schluesselverzeichnis" +else + fail "keine Meldung ueber das leere Schluesselverzeichnis: $OUT" +fi + +echo +echo "=== Szenario 19 (Fixrunde 1, C2): Voll-Deploy mit nur dev-Schluessel raeumt keine vorhandenen _prod.img weg ===" +SANDBOX="$TMPROOT/s19"; STUBBIN="$TMPROOT/bin19"; LOG="$TMPROOT/s19.log"; SANDBOX_STATE="$TMPROOT/s19-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" +echo "echtes-altes-prod-image" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" +: >"$LOG" +set +e +OUT=$(TEST_SIGN_DIR="$KEYDIR_DEV_ONLY" run_deploy "$SANDBOX" pi-test 2>&1) # Voll-Deploy, kein Ziel-Argument +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" ]; then + pass "neues dev-Migrationsimage wurde signiert und ausgeliefert" +else + fail "dev-Migrationsimage fehlt: $OUT" +fi +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" 2>/dev/null)" = "echtes-altes-prod-image" ]; then + pass "das vorhandene _prod.img blieb unangetastet, obwohl dieser Voll-Deploy nur den dev-Schluessel hatte" +else + fail "das vorhandene _prod.img wurde beim Voll-Deploy mit nur dev-Schluessel entfernt - C2 (Teilfall) nicht behoben" +fi + +echo +echo "=== Szenario 20 (Fixrunde 1, C1): manifest.json wird vom ECHTEN Server-Leser korrekt interpretiert ===" +# json.load allein (wie in Szenario 11) haette die falsche Verschachtelung von +# ninlight_version nicht gemerkt, weil es nur die Feldnamen innerhalb der Eintraege +# prueft. Hier laeuft dieselbe Datei stattdessen durch server.py's eigene +# Station.manifest_version() - genau die Funktion, die im echten Betrieb entscheidet, +# welche Version protokolliert und angezeigt wird. +SANDBOX="$TMPROOT/s20"; STUBBIN="$TMPROOT/bin20"; LOG="$TMPROOT/s20.log"; SANDBOX_STATE="$TMPROOT/s20-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +MANIFEST="$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" +if [ "$RC" -eq 0 ] && [ -f "$MANIFEST" ]; then + pass "manifest.json wurde angelegt" +else + fail "manifest.json fehlt oder Exit-Code $RC: $OUT" +fi +set +e +VERSION=$(python3 -c " +import importlib.util +from pathlib import Path + +station_dir = Path('$STATION_DIR') +spec = importlib.util.spec_from_file_location('ninlight_station_server_c1', station_dir / 'server.py') +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) + +st = module.Station( + image_dir=Path('$MANIFEST').resolve().parent.parent, + state_path=Path('$TMPROOT/s20-status.json'), + stall_after=None, + mode='migration', + control_path=Path('$TMPROOT/s20-control.json'), +) +print(st.manifest_version()) +" 2>&1) +RC2=$? +set -e +if [ "$RC2" -eq 0 ] && [ "$VERSION" = "1.0.0" ]; then + pass "Station.manifest_version() (der echte Server-Code) liest die Version korrekt, nicht 'unbekannt'" +else + fail "Station.manifest_version() liefert '$VERSION' statt '1.0.0' (RC=$RC2) - Manifest-Form stimmt nicht mit dem Server ueberein" +fi + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From 1cb5359754c01fca87cf8197d74ac4a8b7fb994c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 20:40:03 +0200 Subject: [PATCH 116/150] fix(station): Manifest-Merge robust gegen Fehlform, Version pro Eintrag Fixrunde 2 zu "Migrationsimages beim Deploy signieren": - N1 (Important): das Merge-Skript im SSH-Heredoc von deploy.sh fing beim Bestandsmanifest nur Lesefehler und kaputtes JSON ab (OSError/ValueError). War die Datei gueltiges JSON mit falscher Form ("[]", "null", ein String, oder ein images-Eintrag, der selbst kein Objekt ist), krachte .get() mit AttributeError - und das VOR der Installation von server.py/products.py/CLI/Unit und vor dem Neustart, liess einen Pi also mit neuen Images, aber altem Stationscode zurueck. Jetzt wird alte wie neue Manifest-Struktur konsequent auf isinstance(dict)/isinstance(list) geprueft, jede Fehlform faellt auf ein leeres Manifest zurueck statt abzustuerzen. Neues Szenario 21 deckt alle vier Faelle aus der Review-Tabelle ab (kaputt, leer, falsch geformtes JSON, kaputter Eintrag). - N2 (Important): das zusammengefuehrte Manifest hat "ninlight_version" bisher blind vom neuen Manifest uebernommen, obwohl uebernommene Alt-Eintraege (deren Images unveraendert liegen bleiben, siehe Fixrunde 1 I1) eine andere Firmware-Version tragen koennen - eine konkrete falsche Version beim Protokollieren ist schlechter als das vorherige "unbekannt". Die Version wandert jetzt PRO EINTRAG ins Manifest (deploy.sh erzeugt sie dort, der Merge behaelt sie); das manifestweite Feld bleibt nur als Bestandskompatibilitaet und wird nur gesetzt, wenn wirklich alle Eintraege uebereinstimmen. server.py liest beim Ausliefern jetzt die Version DES AUSGELIEFERTEN EINTRAGS (Station.image_version(target, variant) ersetzt manifest_version()) und faellt nur auf das manifestweite Feld zurueck, wenn der Eintrag selbst keine eigene traegt. test_server.py bekommt einen neuen Test fuer den Vorrang des Eintrags vor dem manifestweiten Feld, Szenario 14 in test_deploy.sh prueft jetzt zusaetzlich unterschiedliche Versionen zwischen zusammengefuehrten Eintraegen und dass das manifestweite Feld dann fehlt statt falsch zu sein. Die Spec (docs/superpowers/specs/.../design.md, Abschnitt 4, lokal/nicht versioniert) ist nachgezogen. Die dadurch ueberfluessig gewordene "sorted(versions)[0]"-Stelle aus Fixrunde 1 ist entfallen. - Kleinigkeit: die Remote-Meldung, wenn migrate/ von diesem Rechner leer ist, behauptet nicht mehr "kein Schluesselverzeichnis" (das stimmt im Fall "Verzeichnis vorhanden, aber leer" nicht) - sie nennt jetzt keinen bestimmten Grund mehr, nur die Konsequenz. - Kleinigkeit: --exclude=manifest.json ist jetzt --exclude=/manifest.json, konsistent mit dem daneben verankerten --exclude=/migrate. Alle drei Aenderungen (N1, N2, beide Kleinigkeiten) sind vor dem Fix per Rot-Probe an einer Kopie von deploy.sh verifiziert: mit den urspruenglichen Fehlern schlagen Szenario 21 (N1) bzw. Szenario 14 (N2) zuverlaessig fehl. Bewusst nicht angefasst (Review-Vorgabe): der fehlende HOME-Testfall, und dass der Merge kein os.path.exists() prueft. --- tools/station/deploy.sh | 74 +++++++++++++------ tools/station/server.py | 56 +++++++++++---- tools/station/tests/test_deploy.sh | 111 ++++++++++++++++++++++++----- tools/station/tests/test_server.py | 29 ++++++++ 4 files changed, 218 insertions(+), 52 deletions(-) diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index 12892a5..3c7b231 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -136,8 +136,12 @@ if [ -d "$SIGN_DIR" ]; then # berechnet statt aus dem Signierschritt uebernommen, damit die Datei die Wahrheit # ueber das tatsaechlich abgelegte Image sagt. Form haelt sich an die Spec # (docs/superpowers/spec, Abschnitt 4) und damit an das, was server.py tatsaechlich - # liest: "ninlight_version" auf oberster Ebene, nicht pro Eintrag (Fixrunde 1, C1 - - # der Server las vorher immer "unbekannt"). + # liest. Fixrunde 2, N2: "ninlight_version" steht PRO EINTRAG, nicht (mehr nur) + # manifestweit - ein einziges Feld fuer das ganze Manifest war schon falsch, sobald + # der Merge unten (bei nur einem Schluessel oder gezieltem Deploy) alte und neue + # Eintraege mit unterschiedlichen Firmware-Versionen zusammenfuehrt. Das + # manifestweite Feld bleibt nur als Bestandskompatibilitaet erhalten und wird nur + # gesetzt, wenn wirklich alle Eintraege dieselbe Version tragen. python3 - "$STAGE/migrate" "${VERSION_ARGS[@]}" <<'PY' import hashlib import json @@ -163,23 +167,18 @@ for name in sorted(os.listdir(migrate_dir)): "file": name, "bytes": len(data), "sha256": hashlib.sha256(data).hexdigest(), + "ninlight_version": version_map.get(target, ""), }) -# Die Spec kennt nur EINE Firmware-Version pro Manifest, nicht eine pro Ziel. Im -# Regelfall bauen alle Ziele eines Deploys aus demselben Firmware-Stand, weichen die -# Versionen trotzdem ab, ist das kein Grund zum Abbrechen - nur eine Warnung, und die -# alphabetisch erste Version gewinnt (deterministisch, nachvollziehbar). -versions = sorted({version_map.get(e["target"], "") for e in entries}) -if len(versions) > 1: - print(f"Warnung: unterschiedliche Firmware-Versionen in diesem Deploy {versions} " - f"- manifest.json verwendet {versions[0]}", file=sys.stderr) -ninlight_version = versions[0] if versions else "" - manifest = { "built": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), - "ninlight_version": ninlight_version, "images": entries, } +versions = {e["ninlight_version"] for e in entries} +if len(versions) == 1: + (version,) = versions + if version: + manifest["ninlight_version"] = version with open(os.path.join(migrate_dir, "manifest.json"), "w") as fh: json.dump(manifest, fh, indent=2, sort_keys=True) fh.write("\n") @@ -266,7 +265,7 @@ if [ -d /tmp/ninlight-images/migrate ]; then # sondern mit dem, was schon da ist, zusammengefuehrt (Fixrunde 1, I1 - sonst # verschwinden die Eintraege aller Ziele/Varianten, die dieser Lauf nicht neu # gebaut hat, obwohl ihre Images unveraendert liegen bleiben). - sudo rsync -a -c --exclude=manifest.json /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" + sudo rsync -a -c --exclude=/manifest.json /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" if [ -f /tmp/ninlight-images/migrate/manifest.json ]; then sudo python3 - "$ROOT_DIR/images/migrate/manifest.json" /tmp/ninlight-images/migrate/manifest.json <<'MERGE' import json @@ -275,19 +274,48 @@ import sys manifest_path, new_manifest_path = sys.argv[1], sys.argv[2] with open(new_manifest_path) as fh: new_manifest = json.load(fh) +if not isinstance(new_manifest, dict): + new_manifest = {} try: with open(manifest_path) as fh: old_manifest = json.load(fh) except (OSError, ValueError): - old_manifest = {"images": []} -by_key = {(e.get("target"), e.get("variant")): e for e in old_manifest.get("images", [])} -for e in new_manifest.get("images", []): - by_key[(e.get("target"), e.get("variant"))] = e + old_manifest = {} +# Fixrunde 2, N1: ein BESTEHENDES manifest.json kann gueltiges JSON sein und trotzdem +# die falsche Form haben (z.B. "[]", "null", ein String, oder ein Eintrag, der selbst +# kein Objekt ist) - das darf niemals mitten im Deploy mit AttributeError abbrechen, +# denn dieser Merge steht VOR der Installation von server.py/products.py/CLI/Unit und +# vor dem Neustart: ein Abbruch hier liesse den Pi mit neuen Images, aber altem +# Stationscode zurueck. +if not isinstance(old_manifest, dict): + old_manifest = {} +old_images = old_manifest.get("images") +if not isinstance(old_images, list): + old_images = [] +by_key = {(e.get("target"), e.get("variant")): e for e in old_images if isinstance(e, dict)} +new_images = new_manifest.get("images") +if not isinstance(new_images, list): + new_images = [] +for e in new_images: + if isinstance(e, dict): + by_key[(e.get("target"), e.get("variant"))] = e +merged_images = sorted(by_key.values(), key=lambda e: (e.get("target") or "", e.get("variant") or "")) merged = { "built": new_manifest.get("built"), - "ninlight_version": new_manifest.get("ninlight_version"), - "images": sorted(by_key.values(), key=lambda e: (e.get("target") or "", e.get("variant") or "")), + "images": merged_images, } +# Fixrunde 2, N2: das manifestweite "ninlight_version" darf nach dem Merge nicht mehr +# einfach vom neuen Manifest uebernommen werden - die uebernommenen Alt-Eintraege +# koennen eine andere Version tragen (das war die alte Version dieses Bugs: ein +# Voll-Deploy mit nur dem dev-Schluessel hat die neue Firmware-Version ueber ein +# unveraendert liegen gebliebenes altes _prod.img geschrieben). Jeder Eintrag traegt +# seine eigene Version; das Top-Level-Feld ist nur noch eine Bequemlichkeit fuer den +# Fall, dass wirklich alle Eintraege uebereinstimmen. +versions = {e.get("ninlight_version") for e in merged_images} +if len(versions) == 1: + (version,) = versions + if isinstance(version, str) and version: + merged["ninlight_version"] = version with open(manifest_path, "w") as fh: json.dump(merged, fh, indent=2, sort_keys=True) fh.write("\n") @@ -295,7 +323,11 @@ MERGE fi fi else - echo " kein Schluesselverzeichnis auf dem Entwicklungsrechner - migrate/ auf dem Pi bleibt unangetastet" + # Weder "kein Schluesselverzeichnis" noch "Verzeichnis vorhanden, aber leer" laesst + # sich von hier aus unterscheiden - beide Faelle liefern ein leeres migrate/ von + # diesem Rechner. Die lokale Meldung (deploy.sh, "Hinweis: ...") ist bereits genau; + # diese hier behauptet absichtlich keinen bestimmten Grund (Fixrunde 2, Kleinigkeit). + echo " keine Migrationsimages von diesem Rechner - migrate/ auf dem Pi bleibt unangetastet" fi sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" sudo install -m 0644 /tmp/products.py "$ROOT_DIR/products.py" diff --git a/tools/station/server.py b/tools/station/server.py index a1439d2..f9658fb 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -77,7 +77,11 @@ class Station: # wurde - das ist es, was die Anzeige gross darstellt (Spec 5.2). self.last_seen: dict | None = None self._manifest_mtime: float | None = None - self._manifest_version = "unbekannt" + # Der geparste Inhalt von manifest.json (oder {}, wenn sie fehlt/kaputt/falsch + # geformt ist) statt nur einer einzelnen Version - seit Fixrunde 2 (N2) traegt + # jeder Eintrag seine eigene Version, image_version() muss also den ganzen + # Eintragssatz vorhalten, nicht nur einen String. + self._manifest_cache: dict = {} # Wird True, wenn write_control() zuletzt fehlgeschlagen ist (z.B. volles oder # schreibgeschuetztes /run) - claim_delivery() liest das, um "armed" nicht # dauerhaft scharf zu lassen, nur weil das Verbrauchen der Freigabe nicht auf @@ -104,26 +108,54 @@ class Station: def migrate_image_path(self, target: str, variant: str) -> Path: return self.image_dir / "migrate" / f"{target}_{variant}.img" - def manifest_version(self) -> str: - """Version aus /migrate/manifest.json, neu eingelesen bei jeder - Aenderung der Datei (mtime-Vergleich). Fehlt sie oder ist sie kaputt, ist die - Version "unbekannt" - die Migrationsimages selbst tragen keinen ninlight-Trailer, - aus dem der Server sie lesen koennte (Spec Abschnitt 4).""" + def _load_manifest(self) -> dict: + """Geparster Inhalt von /migrate/manifest.json, neu eingelesen bei + jeder Aenderung der Datei (mtime-Vergleich). Fehlt sie, ist sie unlesbar/kaputtes + JSON, oder ist ihr Wurzelelement kein Objekt, ist das Ergebnis {} - dieselbe + Formpruefung wie beim Merge in deploy.sh (Fixrunde 2, N1), damit eine falsch + geformte, aber syntaktisch gueltige Datei nicht mit einem AttributeError den + Server abschiesst.""" path = self.image_dir / "migrate" / "manifest.json" try: mtime = path.stat().st_mtime except OSError: self._manifest_mtime = None - self._manifest_version = "unbekannt" - return self._manifest_version + self._manifest_cache = {} + return self._manifest_cache if mtime != self._manifest_mtime: try: data = json.loads(path.read_text()) - self._manifest_version = data.get("ninlight_version", "unbekannt") except (OSError, ValueError): - self._manifest_version = "unbekannt" + data = {} + if not isinstance(data, dict): + data = {} + self._manifest_cache = data self._manifest_mtime = mtime - return self._manifest_version + return self._manifest_cache + + def image_version(self, target: str, variant: str) -> str: + """Firmware-Version fuer genau das Migrationsimage, das gerade ausgeliefert + wird - die Migrationsimages selbst tragen keinen ninlight-Trailer, aus dem der + Server sie lesen koennte (Spec Abschnitt 4). Bevorzugt die Version aus dem + passenden Eintrag in manifest.json; nur wenn der Eintrag keine eigene traegt + (Bestandskompatibilitaet mit einem manifestweiten Feld), faellt es auf das + Feld auf oberster Ebene zurueck. Ein einzelnes manifestweites Feld allein + waere seit dem Zusammenfuehren mehrerer Deploys (deploy.sh, Fixrunde 1 I1) + schlicht nicht mehr wahr: uebernommene Alt-Eintraege koennen eine andere + Version tragen als die gerade neu signierten (Fixrunde 2, N2).""" + data = self._load_manifest() + images = data.get("images") + if isinstance(images, list): + for entry in images: + if not isinstance(entry, dict): + continue + if entry.get("target") == target and entry.get("variant") == variant: + version = entry.get("ninlight_version") + if isinstance(version, str) and version: + return version + break + top = data.get("ninlight_version") + return top if isinstance(top, str) and top else "unbekannt" def read_control(self): """Phase und Freigabe. Fehlt die Datei oder ist sie unlesbar/kaputt, gilt "auto" - @@ -373,7 +405,7 @@ class Handler(BaseHTTPRequestHandler): return target, variant, name = product.target, product.variant, product.name - version = self.station.manifest_version() + version = self.station.image_version(target, variant) # claim_delivery() liest die Phase UND verbraucht eine armed-Freigabe atomar unter # station.lock (Fixrunde 1, C1) - zwischen dem Lesen und dem Verbrauchen darf keine # zweite gleichzeitige Anfrage hindurchschluepfen, siehe die Methode selbst. diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index 9b5533d..6e80ae4 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -504,10 +504,11 @@ else fi set +e # Form haelt sich an die Spec (docs/superpowers/spec Abschnitt 4) und an das, was -# server.py tatsaechlich liest (Fixrunde 1, C1): "ninlight_version" auf oberster -# Ebene, nicht pro Eintrag - ein reines json.load haette das nicht gemerkt, weil es -# nur die Feldnamen INNERHALB der Eintraege prueft. Die naechste Pruefung (Szenario -# 20) schickt dieselbe Datei zusaetzlich durch den echten Server-Leser. +# server.py tatsaechlich liest. Seit Fixrunde 2 (N2) traegt "ninlight_version" PRO +# EINTRAG die Version - hier stimmen beide Eintraege ueberein, deshalb steht das +# manifestweite Feld zusaetzlich als Bequemlichkeit oben. Ein reines json.load haette +# eine falsche Verschachtelung nicht gemerkt (Fixrunde 1, C1); die naechste Pruefung +# (Szenario 20) schickt dieselbe Datei zusaetzlich durch den echten Server-Leser. CHECK=$(python3 -c " import json with open('$MANIFEST') as fh: @@ -522,13 +523,13 @@ ok = ( and all(e.get('file') in ('foo_prod.img', 'foo_dev.img') for e in entries) and all(isinstance(e.get('bytes'), int) and e['bytes'] > 0 for e in entries) and all(isinstance(e.get('sha256'), str) and len(e['sha256']) == 64 for e in entries) - and all('ninlight_version' not in e for e in entries) + and all(e.get('ninlight_version') == '1.0.0' for e in entries) ) print('OK' if ok else 'FAIL: ' + json.dumps(data)) " 2>&1) set -e if [ "$CHECK" = "OK" ]; then - pass "manifest.json traegt built/ninlight_version oben und target/variant/file/bytes/sha256 je Eintrag" + pass "manifest.json traegt built/ninlight_version oben (Bequemlichkeit) und target/variant/file/bytes/sha256/ninlight_version je Eintrag" else fail "manifest.json-Inhalt unerwartet: $CHECK" fi @@ -608,12 +609,17 @@ mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" echo "vorhandenes-bar-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_prod.img" echo "vorhandenes-bar-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_dev.img" # Manifest eines frueheren Deploys, das bar beschrieben hat - genau das darf ein -# gezielter Deploy von foo nicht einfach wegschreiben (Fixrunde 1, I1). +# gezielter Deploy von foo nicht einfach wegschreiben (Fixrunde 1, I1). bar traegt +# absichtlich eine ANDERE Firmware-Version als das, was dieser Lauf fuer foo signiert +# (nl_mkimage-Stub liefert immer "1.0.0") - das Manifest nach dem Merge muss beide +# Versionen unveraendert nebeneinander stehen lassen (Fixrunde 2, N2). cat >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" <<'JSON' -{"built": "2026-01-01T00:00:00Z", "ninlight_version": "0.0.1", +{"built": "2026-01-01T00:00:00Z", "ninlight_version": "0.9.0", "images": [ - {"target": "bar", "variant": "prod", "file": "bar_prod.img", "bytes": 20, "sha256": "x"}, - {"target": "bar", "variant": "dev", "file": "bar_dev.img", "bytes": 19, "sha256": "y"} + {"target": "bar", "variant": "prod", "file": "bar_prod.img", "bytes": 20, "sha256": "x", + "ninlight_version": "0.9.0"}, + {"target": "bar", "variant": "dev", "file": "bar_dev.img", "bytes": 19, "sha256": "y", + "ninlight_version": "0.9.0"} ]} JSON : >"$LOG" @@ -639,15 +645,33 @@ MANIFEST_CHECK=$(python3 -c " import json with open('$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json') as fh: data = json.load(fh) -keys = {(e.get('target'), e.get('variant')) for e in data.get('images', [])} +entries = data.get('images', []) +keys = {(e.get('target'), e.get('variant')) for e in entries} expected = {('foo', 'prod'), ('foo', 'dev'), ('bar', 'prod'), ('bar', 'dev')} -print('OK' if keys == expected else 'FAIL: ' + json.dumps(sorted(keys))) +by_key = {(e.get('target'), e.get('variant')): e for e in entries} +ok = ( + keys == expected + # foo wurde in DIESEM Lauf signiert -> Stub-Version 1.0.0 (Fixrunde 2, N2: Version + # steht pro Eintrag). + and by_key[('foo', 'prod')].get('ninlight_version') == '1.0.0' + and by_key[('foo', 'dev')].get('ninlight_version') == '1.0.0' + # bar wurde NICHT neu signiert -> seine alte Version (0.9.0) bleibt unangetastet, + # auch wenn sie von foos Version abweicht. + and by_key[('bar', 'prod')].get('ninlight_version') == '0.9.0' + and by_key[('bar', 'dev')].get('ninlight_version') == '0.9.0' + # Da foo (1.0.0) und bar (0.9.0) nach dem Merge NICHT uebereinstimmen, darf das + # manifestweite Feld nicht mehr die Version dieses Laufs behaupten - es muss ganz + # fehlen (Fixrunde 2, N2: das war der eigentliche Fehler - vorher stand hier + # weiterhin 1.0.0, obwohl bars Bilder unveraendert auf 0.9.0 blieben). + and 'ninlight_version' not in data +) +print('OK' if ok else 'FAIL: ' + json.dumps(data)) " 2>&1) set -e if [ "$MANIFEST_CHECK" = "OK" ]; then - pass "manifest.json beschreibt nach dem gezielten Deploy weiterhin BEIDE Geraetetypen (foo neu, bar zusammengefuehrt)" + pass "manifest.json beschreibt nach dem gezielten Deploy weiterhin BEIDE Geraetetypen mit je eigener Version, ohne falsches manifestweites Feld" else - fail "manifest.json verliert Eintraege beim gezielten Deploy: $MANIFEST_CHECK" + fail "manifest.json-Inhalt nach dem gezielten Deploy unerwartet: $MANIFEST_CHECK" fi echo @@ -829,8 +853,10 @@ echo "=== Szenario 20 (Fixrunde 1, C1): manifest.json wird vom ECHTEN Server-Les # json.load allein (wie in Szenario 11) haette die falsche Verschachtelung von # ninlight_version nicht gemerkt, weil es nur die Feldnamen innerhalb der Eintraege # prueft. Hier laeuft dieselbe Datei stattdessen durch server.py's eigene -# Station.manifest_version() - genau die Funktion, die im echten Betrieb entscheidet, -# welche Version protokolliert und angezeigt wird. +# Station.image_version(target, variant) - genau die Funktion, die im echten Betrieb +# entscheidet, welche Version protokolliert und angezeigt wird (seit Fixrunde 2, N2: +# pro ausgeliefertem Eintrag, nicht mehr nur manifestweit - deshalb jetzt mit +# target/variant statt ohne Argumente). SANDBOX="$TMPROOT/s20"; STUBBIN="$TMPROOT/bin20"; LOG="$TMPROOT/s20.log"; SANDBOX_STATE="$TMPROOT/s20-state" setup_sandbox "$SANDBOX" setup_stubbin "$STUBBIN" "$LOG" @@ -865,16 +891,63 @@ st = module.Station( mode='migration', control_path=Path('$TMPROOT/s20-control.json'), ) -print(st.manifest_version()) +print(st.image_version('foo', 'dev')) " 2>&1) RC2=$? set -e if [ "$RC2" -eq 0 ] && [ "$VERSION" = "1.0.0" ]; then - pass "Station.manifest_version() (der echte Server-Code) liest die Version korrekt, nicht 'unbekannt'" + pass "Station.image_version() (der echte Server-Code) liest die Version korrekt, nicht 'unbekannt'" else - fail "Station.manifest_version() liefert '$VERSION' statt '1.0.0' (RC=$RC2) - Manifest-Form stimmt nicht mit dem Server ueberein" + fail "Station.image_version() liefert '$VERSION' statt '1.0.0' (RC=$RC2) - Manifest-Form stimmt nicht mit dem Server ueberein" fi +echo +echo "=== Szenario 21 (Fixrunde 2, N1): kaputtes/falsch geformtes Bestandsmanifest darf den Deploy nicht mitten drin abbrechen ===" +# Der Merge steht in deploy.sh VOR der Installation von server.py/products.py/CLI/Unit +# und vor dem Neustart - ein Abbruch hier liesse den Pi mit neuen Images, aber altem +# Stationscode zurueck. "truncated" und "empty" liefen schon vorher durch (json.load +# wirft ValueError, das war immer abgefangen); "array"/"null"/"string"/"null-entry" +# sind gueltiges JSON mit falscher FORM und haben vorher mit AttributeError abgebrochen +# ("... has no attribute 'get'"), weil nur auf Lese-, nicht auf Parse-Fehler geprueft +# wurde. Alle vier Zeilen aus der Review-Tabelle. +mkdir -p "$TMPROOT/bad-manifests" +printf '%s' '{ kaputt' >"$TMPROOT/bad-manifests/truncated.json" +: >"$TMPROOT/bad-manifests/empty.json" +printf '%s' '[]' >"$TMPROOT/bad-manifests/array.json" +printf '%s' 'null' >"$TMPROOT/bad-manifests/null.json" +printf '%s' '"text"' >"$TMPROOT/bad-manifests/string.json" +printf '%s' '{"images": [null]}' >"$TMPROOT/bad-manifests/null-entry.json" + +for label in truncated empty array null string null-entry; do + SANDBOX="$TMPROOT/s21-$label"; STUBBIN="$TMPROOT/bin21-$label" + LOG="$TMPROOT/s21-$label.log"; SANDBOX_STATE="$TMPROOT/s21-$label-state" + setup_sandbox "$SANDBOX" + setup_stubbin "$STUBBIN" "$LOG" + mkdir -p "$SANDBOX/build/foo" + make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" + echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" + mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" + cp "$TMPROOT/bad-manifests/$label.json" "$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" + : >"$LOG" + set +e + # Nur EIN Schluessel (dev) -> MIGRATE_COMPLETE=0 -> der Merge-Zweig laeuft, nicht der + # --delete-Zweig, der das kaputte Bestandsmanifest ohnehin ueberschreiben wuerde. + OUT=$(TEST_SIGN_DIR="$KEYDIR_DEV_ONLY" run_deploy "$SANDBOX" pi-test foo 2>&1) + RC=$? + set -e + if [ "$RC" -eq 0 ]; then + pass "Bestandsmanifest '$label': Exit-Code 0, Deploy bricht nicht mitten drin ab" + else + fail "Bestandsmanifest '$label': Exit-Code $RC - Deploy abgebrochen. Ausgabe: $OUT" + fi + if [ -f "$SANDBOX_STATE/opt/ninlight-station/server.py" ] \ + && [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" ]; then + pass "Bestandsmanifest '$label': Deploy lief vollstaendig durch (server.py installiert, foo_dev.img da)" + else + fail "Bestandsmanifest '$label': Deploy blieb auf halbem Weg stehen (server.py oder foo_dev.img fehlt)" + fi +done + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index 8f5c777..18aa001 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -686,6 +686,9 @@ def test_range_resume_on_migration_image(migration_server): def test_version_comes_from_manifest(migration_server): + """Faellt auf das manifestweite Feld zurueck, wenn der Eintrag selbst keine eigene + Version traegt - Bestandskompatibilitaet mit einem Manifest aus der Zeit vor + Fixrunde 2 (N2).""" srv, imgdir = migration_server (imgdir / "migrate").mkdir(exist_ok=True) (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") @@ -697,6 +700,32 @@ def test_version_comes_from_manifest(migration_server): assert st["recent"][-1]["version"] == "0.3.9" +def test_version_prefers_the_delivered_entry_over_the_manifest_wide_field(migration_server): + """Fixrunde 2, N2: nach einem Merge in deploy.sh (Voll-Deploy mit nur einem der + beiden Signierschluessel) koennen Eintraege im selben Manifest unterschiedliche + Firmware-Versionen tragen - z.B. ein neu signiertes g95_cw_dev.img auf 1.2.0, aber + ein unveraendert liegen gebliebenes aelteres g95_cw_prod.img auf 1.0.0. Der Server + darf dann nicht das manifestweite Feld fuer beide protokollieren, sondern muss die + Version DES AUSGELIEFERTEN Eintrags nehmen.""" + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG-DEV") + (imgdir / "migrate" / "g95_cw_prod.img").write_bytes(b"IMG-PROD") + (imgdir / "migrate" / "manifest.json").write_text(json.dumps({ + # Manifestweites Feld absichtlich falsch/veraltet, damit ein Zurueckfallen + # darauf im Test auffallen wuerde. + "ninlight_version": "1.2.0", + "images": [ + {"target": "g95_cw", "variant": "dev", "file": "g95_cw_dev.img", + "ninlight_version": "1.2.0"}, + {"target": "g95_cw", "variant": "prod", "file": "g95_cw_prod.img", + "ninlight_version": "1.0.0"}, + ]})) + srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95_V1_ota_light.img") + st = srv.status(lambda s: len(s["recent"]) >= 1) + assert st["recent"][-1]["version"] == "1.0.0" + + def test_control_file_is_world_readable_even_under_a_restrictive_umask(tmp_path): """Dieselbe 0600-Falle wie bei status.json (siehe test_state_file_is_world_readable_even_under_a_restrictive_umask oben), aber fuer From 4a029a5d1c5a1cc0fa2248d0502c087747c3d85d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 20:44:02 +0200 Subject: [PATCH 117/150] feat(station): E-Paper-Anzeige fuer Modus, Lampe und Auslieferungen render.lines() zerlegt den zusammengefuehrten Zustand (status.json + control.json + SSID aus der Konfigurationsdatei) in Textzeilen, ohne Pillow und ohne Hardware zu brauchen - render.compose() importiert Pillow erst innerhalb der Funktion. epdconfig.py ist eine eigene GPIO/SPI-Schicht auf gpiozero/spidev, weil RPi.GPIO auf Debian 13 mit Python 3.13 nicht mehr traegt; epd2in13_V2.py/epd2in13_V3.py sind unveraendert aus dem Waveshare-Repository uebernommen (siehe HERKUNFT.md), nur der epdconfig-Import zeigt auf unser Modul. display.py laeuft als eigener systemd-Dienst (Wants=, Restart=on-failure), fehlendes Panel/SPI/Bibliothek beendet ihn sauber mit sys.exit(0) statt einer Neustartschleife - eine Migration darf davon nie abhaengen. Verdrahtung in setup_pi.sh/deploy.sh/ninlight-station folgt in einem eigenen Durchgang. --- tools/station/display/HERKUNFT.md | 46 +++ tools/station/display/display.py | 286 ++++++++++++++++++ tools/station/display/epd2in13_V2.py | 324 ++++++++++++++++++++ tools/station/display/epd2in13_V3.py | 395 +++++++++++++++++++++++++ tools/station/display/epdconfig.py | 139 +++++++++ tools/station/display/render.py | 133 +++++++++ tools/station/ninlight-display.service | 49 +++ tools/station/tests/test_render.py | 56 ++++ 8 files changed, 1428 insertions(+) create mode 100644 tools/station/display/HERKUNFT.md create mode 100644 tools/station/display/display.py create mode 100644 tools/station/display/epd2in13_V2.py create mode 100644 tools/station/display/epd2in13_V3.py create mode 100644 tools/station/display/epdconfig.py create mode 100644 tools/station/display/render.py create mode 100644 tools/station/ninlight-display.service create mode 100644 tools/station/tests/test_render.py diff --git a/tools/station/display/HERKUNFT.md b/tools/station/display/HERKUNFT.md new file mode 100644 index 0000000..d35b173 --- /dev/null +++ b/tools/station/display/HERKUNFT.md @@ -0,0 +1,46 @@ +# Herkunft der Panel-Treiber + +`epd2in13_V2.py` und `epd2in13_V3.py` sind unveraendert aus dem offiziellen +Waveshare-Repository uebernommen: + +- Quelle: https://github.com/waveshareteam/e-Paper +- Pfad: `RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd2in13_V2.py` bzw. + `.../epd2in13_V3.py` +- Commit: `85802dee4b938212b0ae069a34492c54a3d99878` (2023-02-15), abgerufen am + 2026-09-13 per `git log --follow` auf diesen beiden Dateien im Repository. +- `epd2in13_V2.py`: Treiber-Version laut Dateikopf V4.0 (2019-06-20). +- `epd2in13_V3.py`: Treiber-Version laut Dateikopf V1.2 (2022-08-09). + +## Was geaendert wurde + +Ausschliesslich die Importzeile: + +``` +from . import epdconfig +``` + +wurde zu + +``` +import epdconfig +``` + +weil `display/` in diesem Repository kein Python-Package ist (kein `__init__.py`, +`sys.path`-Import wie bei `products.py` in `server.py`), sondern flach importiert wird - +sowohl von `display.py` als auch von `tools/station/tests/test_render.py`. Ein relativer +Import wuerde in dieser Umgebung mit `ImportError: attempted relative import with no known +parent package` fehlschlagen. + +Init-Sequenz, LUT-Tabellen, Timings und der Urheberrechtsvermerk der Originaldateien +bleiben unangetastet. Ein kurzer Herkunftsvermerk mit denselben Angaben wie oben steht +zusaetzlich im Kopf jeder der beiden Dateien. + +## Was NICHT uebernommen wurde + +`epdconfig.py` in diesem Verzeichnis ist eine eigene Neuimplementierung (siehe deren +Modul-Docstring), keine Uebernahme aus dem Waveshare-Repository: Waveshares eigenes +`epdconfig.py` fuer den Raspberry Pi setzt `RPi.GPIO` voraus, das auf Debian 13 (trixie) +mit Python 3.13 nicht mehr baut/importiert. Unser `epdconfig.py` implementiert dieselbe +von den beiden Treibern erwartete Schnittstelle (Pin-Konstanten, `digital_write`, +`digital_read`, `delay_ms`, `spi_writebyte`, `spi_writebyte2`, `module_init`, +`module_exit`) gegen `gpiozero`/`spidev`, die auf dem Ziel-Pi vorhanden sind. diff --git a/tools/station/display/display.py b/tools/station/display/display.py new file mode 100644 index 0000000..8db5677 --- /dev/null +++ b/tools/station/display/display.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +"""Daemon fuer die E-Paper-Anzeige der ninlight-Station: alle 5 s status.json, +control.json und die Konfigurationsdatei lesen, daraus einen Text zeichnen und - nur bei +Aenderung - aufs Panel bringen. + +Panel: Waveshare 2,13" (250x122 px), SPI. Ob das verbaute Panel Revision V2 oder V3 ist, +laesst sich am Geraet selbst nicht auslesen - die Revision kommt deshalb aus der +Umgebungsvariable EPD_REVISION (Vorgabe "V3", von setup_pi.sh in +/etc/ninlight-station.conf geschrieben und von der systemd-Unit als Environment= +weitergereicht). + +So bestimmst du die richtige Revision empirisch: erst + EPD_REVISION=V3 python3 display.py --test +laufen lassen und das Panel ansehen. Zeigt es ein sauberes, seitenrichtiges Testbild ohne +Geisterbilder/Streifen, ist V3 richtig. Zeigt es nichts, ein invertiertes/verschobenes Bild +oder haengt beim SWRESET (ReadBusy() kehrt nie zurueck), mit + EPD_REVISION=V2 python3 display.py --test +wiederholen. Zwischen den beiden Versuchen kurz die Stromversorgung des Panels trennen +(--clear reicht nicht immer, falsche Revision kann das Panel in einem Zwischenzustand +lassen). + +Die Anzeige ist nirgends Voraussetzung fuer die Migration: fehlt das Panel, SPI oder die +gpiozero/spidev-Bibliothek, faengt main() das als DisplayUnavailable ab, protokolliert +GENAU EINE Zeile und beendet sich mit sys.exit(0) - nicht mit einem Fehler, sonst wuerde +systemd (Restart=on-failure) den Dienst endlos neu starten und das Journal fluten. Ein +Display-Fehler darf niemals eine Migration verhindern. +""" +from __future__ import annotations + +import argparse +import json +import logging +import os +import subprocess +import sys +import time +from pathlib import Path + +# Flacher Import wie bei server.py/products.py: dieses Verzeichnis ist kein Package, +# display.py kann aber von ueberall aus gestartet werden (die systemd-Unit ruft es mit +# vollem Pfad auf). +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +import render # noqa: E402 +from epdconfig import DisplayUnavailable # noqa: E402 + +log = logging.getLogger("ninlight-display") + +# Dieselben Umgebungsvariablen wie tools/station/ninlight-station verwendet, damit Tests +# und Handbetrieb ohne echtes /run bzw. /etc auskommen. +RUN_DIR = Path(os.environ.get("NINLIGHT_RUN_DIR", "/run/ninlight-station")) +CONF_PATH = Path(os.environ.get("NINLIGHT_STATION_CONF", "/etc/ninlight-station.conf")) +SERVICE_NAME = os.environ.get("NINLIGHT_STATION_SERVICE", "ninlight-station") + +POLL_INTERVAL_S = 5 +# E-Paper altert mit jedem Bildaufbau - ein Vollaufbau nur bei Moduswechsel und +# mindestens alle 30 Minuten, sonst Teilaktualisierung (siehe Modul-Docstring der +# Panel-Treiber: haeufige Teilaktualisierungen ohne gelegentlichen Vollaufbau lassen +# Geisterbilder stehen). +FULL_REFRESH_INTERVAL_S = 30 * 60 + + +def _read_json(path: Path) -> dict: + """Geparster Inhalt von `path`, oder {} wenn er fehlt/unlesbar/kaputt/kein Objekt + ist - status.json und control.json koennen jederzeit in genau diesem Zustand sein + (frisch aufgesetzter Pi, Schreibfehler mitten im Schreiben).""" + try: + data = json.loads(path.read_text()) + except (OSError, ValueError): + return {} + return data if isinstance(data, dict) else {} + + +def _read_conf(path: Path) -> dict[str, str]: + """Minimaler Parser fuer /etc/ninlight-station.conf: KEY='VALUE'-Zeilen, wie + setup_pi.sh sie schreibt. Kein echtes Shell-Sourcing (das CLI braucht das fuer $- + und `-Escaping in Passwoertern, siehe dessen Kommentar - display.py liest hier nur + SSID/MIGRATE_SSID, reine Bezeichner ohne Sonderzeichen).""" + values: dict[str, str] = {} + try: + text = path.read_text() + except OSError: + return values + for raw_line in text.splitlines(): + line = raw_line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, _, value = line.partition("=") + key = key.strip() + value = value.strip() + if len(value) >= 2 and value[0] == value[-1] and value[0] in ("'", '"'): + value = value[1:-1] + if key: + values[key] = value + return values + + +def _read_mode_file(run_dir: Path) -> str: + """Modus, in dem der Dienst laut RUN_DIR/mode tatsaechlich gestartet wurde. Fehlt die + Datei oder ist ihr Inhalt kein bekannter Modus, gilt "recovery" - derselbe Vorgabewert + wie in server.py:resolve_mode().""" + try: + value = (run_dir / "mode").read_text().strip() + except OSError: + return "recovery" + return value if value in ("recovery", "migration") else "recovery" + + +def _service_active(service_name: str) -> bool: + """Laeuft der Hauptdienst (server.py) gerade? "ninlight-station off" stoppt ihn und + faehrt den Zugangspunkt herunter, laesst status.json dabei aber unangetastet liegen - + ein reines Lesen von status.json wuerde nach "off" also faelschlich weiter den letzten + Modus vor dem Abschalten zeigen. Deshalb ist die Dienstaktivitaet selbst (nicht das + Vorhandensein von status.json) die Quelle fuer den Anzeigemodus "off".""" + try: + result = subprocess.run( + ["systemctl", "is-active", "--quiet", service_name], + timeout=5, check=False) + return result.returncode == 0 + except (OSError, subprocess.SubprocessError): + # systemctl selbst nicht erreichbar (z.B. Handlauf ohne systemd) - dann anhand + # der Statusdatei entscheiden statt "off" zu behaupten, ohne es zu wissen. + return True + + +def build_state(run_dir: Path = RUN_DIR, conf_path: Path = CONF_PATH, + service_name: str = SERVICE_NAME) -> dict: + """Baut den zusammengefuehrten Zustand, den render.lines()/compose() erwarten - + status.json (served/recent/last_seen), control.json (phase/armed_target, nur im + Migrationsmodus massgeblich), die SSID aus der Konfigurationsdatei und "off", wenn + der Hauptdienst gar nicht laeuft.""" + if not _service_active(service_name): + return {"mode": "off"} + + status = _read_json(run_dir / "status.json") + mode = status.get("mode") + if mode not in ("recovery", "migration"): + # status.json fehlt/ist kaputt/stammt (noch) nicht vom laufenden Prozess - + # RUN_DIR/mode ist das, was "ninlight-station on"/"migrate on" zuletzt + # geschrieben hat und ist unabhaengig von status.json verfuegbar. + mode = _read_mode_file(run_dir) + + conf = _read_conf(conf_path) + state: dict = {"mode": mode, "served": status.get("served"), "recent": status.get("recent")} + + if mode == "recovery": + state["ssid"] = conf.get("SSID") + else: # migration + state["ssid"] = conf.get("MIGRATE_SSID") + state["last_seen"] = status.get("last_seen") + # Die Phase kommt bewusst aus control.json, nicht aus status.json: server.py + # schreibt status.json nur beim Start und nach einer Auslieferung, waehrend + # "ninlight-station arm"/"listen" nur die Steuerdatei anfasst (Spec/CLI + # status_cmd folgt demselben Muster). Und bewusst nur HIER gelesen - im + # Rettungsmodus ignoriert server.py control.json ebenfalls, ein + # liegengebliebener Inhalt aus einer frueheren Migration darf dort nicht + # faelschlich "Phase: FREIGEGEBEN" zeigen. + control = _read_json(run_dir / "control.json") + phase = control.get("phase") + state["phase"] = phase if phase in ("auto", "listen", "armed") else "auto" + state["armed_target"] = control.get("armed_target") + + return state + + +def _load_epd_module(revision: str): + """Laedt den passenden Panel-Treiber. Nicht ueber ein Dict aus Modulnamen, damit ein + statischer Import-Checker (und ein Mensch, der `grep -n epd2in13` tippt) beide + moeglichen Importe findet.""" + if revision == "V2": + import epd2in13_V2 as epd_module + elif revision == "V3": + import epd2in13_V3 as epd_module + else: + raise DisplayUnavailable( + f"unbekannte EPD_REVISION '{revision}' - erwartet 'V2' oder 'V3'") + return epd_module + + +def _draw(epd, image, full: bool) -> None: + buf = epd.getbuffer(image) + if full: + # epd.init() laedt die volle LUT neu - noetig, weil displayPartial() unten immer + # die Teil-LUT setzt und die naechste display()-Auslieferung sonst mit der + # falschen Wellenform liefe. + epd.init() + epd.display(buf) + else: + epd.displayPartial(buf) + + +def run_test(revision: str) -> None: + """Zeichnet ein Testbild und beendet sich - siehe Modul-Docstring, wie man damit V2 + von V3 unterscheidet.""" + from PIL import Image, ImageDraw, ImageFont + + epd_module = _load_epd_module(revision) + epd = epd_module.EPD() + epd.init() + epd.Clear(0xFF) + + width, height = 250, 122 + image = Image.new("1", (width, height), 255) + draw = ImageDraw.Draw(image) + draw.rectangle((0, 0, width - 1, height - 1), outline=0) + draw.text((4, 4), "ninlight-display Test", font=ImageFont.load_default(), fill=0) + draw.text((4, 20), f"EPD_REVISION={revision}", font=ImageFont.load_default(), fill=0) + draw.line((4, 40, width - 4, 40), fill=0) + # Vier Kaestchen, abwechselnd gefuellt/leer - hilft, Kontrast/Inversion am + # angeschlossenen Panel auf einen Blick zu beurteilen. + for i in range(4): + x0 = 4 + i * 20 + draw.rectangle((x0, 50, x0 + 16, 90), outline=0, fill=0 if i % 2 == 0 else 255) + + epd.display(epd.getbuffer(image)) + log.info("Testbild gezeichnet (EPD_REVISION=%s)", revision) + + +def run_clear(revision: str) -> None: + epd_module = _load_epd_module(revision) + epd = epd_module.EPD() + epd.init() + epd.Clear(0xFF) + epd.sleep() + log.info("Panel geleert (EPD_REVISION=%s)", revision) + + +def run_daemon(revision: str) -> None: + epd_module = _load_epd_module(revision) + epd = epd_module.EPD() + epd.init() + epd.Clear(0xFF) + + last_lines: list[str] | None = None + last_mode: object = object() # garantiert ungleich jedem echten Modus beim ersten Lauf + last_full = time.monotonic() + + while True: + state = build_state() + current_lines = render.lines(state) + mode = state.get("mode") + now = time.monotonic() + need_full = (mode != last_mode) or (now - last_full >= FULL_REFRESH_INTERVAL_S) + + if current_lines != last_lines or need_full: + image = render.compose(state) + _draw(epd, image, full=need_full) + if need_full: + last_full = now + last_lines = current_lines + last_mode = mode + + time.sleep(POLL_INTERVAL_S) + + +def main() -> None: + logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") + + parser = argparse.ArgumentParser(description="ninlight E-Paper-Anzeige") + group = parser.add_mutually_exclusive_group() + group.add_argument("--test", action="store_true", help="Testbild zeichnen und beenden") + group.add_argument("--clear", action="store_true", help="Panel leeren und beenden") + args = parser.parse_args() + + revision = os.environ.get("EPD_REVISION", "V3") + + try: + if args.test: + run_test(revision) + elif args.clear: + run_clear(revision) + else: + run_daemon(revision) + except DisplayUnavailable as e: + # Genau eine Zeile, dann sys.exit(0) - siehe Modul-Docstring. Restart=on-failure + # (statt always) in der Unit greift hier nicht, weil 0 kein Fehlschlag ist; ein + # spaeter angestecktes Panel braucht also einen manuellen/zeitgesteuerten + # Neustart der Unit, nicht eine Neustartschleife. + log.info("E-Paper-Anzeige nicht verfuegbar, beende ohne Fehler: %s", e) + sys.exit(0) + except KeyboardInterrupt: + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/tools/station/display/epd2in13_V2.py b/tools/station/display/epd2in13_V2.py new file mode 100644 index 0000000..ea49350 --- /dev/null +++ b/tools/station/display/epd2in13_V2.py @@ -0,0 +1,324 @@ +# ***************************************************************************** +# * | File : epd2in13_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +# --- Herkunftsvermerk (ninlight-firmware) --------------------------------- +# Unveraendert uebernommen aus waveshareteam/e-Paper (RaspberryPi_JetsonNano/ +# python/lib/waveshare_epd/epd2in13_V2.py), Commit 85802dee (2023-02-15). +# Einzige Aenderung: 'from . import epdconfig' -> 'import epdconfig', damit das +# Modul flach importierbar ist (kein Package). Alles andere - Init-Sequenz, LUTs, +# Timings - unveraendert. Details: tools/station/display/HERKUNFT.md +# ----------------------------------------------------------------------------- + + +import logging +import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + FULL_UPDATE = 0 + PART_UPDATE = 1 + lut_full_update= [ + 0x80,0x60,0x40,0x00,0x00,0x00,0x00, #LUT0: BB: VS 0 ~7 + 0x10,0x60,0x20,0x00,0x00,0x00,0x00, #LUT1: BW: VS 0 ~7 + 0x80,0x60,0x40,0x00,0x00,0x00,0x00, #LUT2: WB: VS 0 ~7 + 0x10,0x60,0x20,0x00,0x00,0x00,0x00, #LUT3: WW: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, #LUT4: VCOM: VS 0 ~7 + + 0x03,0x03,0x00,0x00,0x02, # TP0 A~D RP0 + 0x09,0x09,0x00,0x00,0x02, # TP1 A~D RP1 + 0x03,0x03,0x00,0x00,0x02, # TP2 A~D RP2 + 0x00,0x00,0x00,0x00,0x00, # TP3 A~D RP3 + 0x00,0x00,0x00,0x00,0x00, # TP4 A~D RP4 + 0x00,0x00,0x00,0x00,0x00, # TP5 A~D RP5 + 0x00,0x00,0x00,0x00,0x00, # TP6 A~D RP6 + + 0x15,0x41,0xA8,0x32,0x30,0x0A, + ] + + lut_partial_update = [ #20 bytes + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, #LUT0: BB: VS 0 ~7 + 0x80,0x00,0x00,0x00,0x00,0x00,0x00, #LUT1: BW: VS 0 ~7 + 0x40,0x00,0x00,0x00,0x00,0x00,0x00, #LUT2: WB: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, #LUT3: WW: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, #LUT4: VCOM: VS 0 ~7 + + 0x0A,0x00,0x00,0x00,0x00, # TP0 A~D RP0 + 0x00,0x00,0x00,0x00,0x00, # TP1 A~D RP1 + 0x00,0x00,0x00,0x00,0x00, # TP2 A~D RP2 + 0x00,0x00,0x00,0x00,0x00, # TP3 A~D RP3 + 0x00,0x00,0x00,0x00,0x00, # TP4 A~D RP4 + 0x00,0x00,0x00,0x00,0x00, # TP5 A~D RP5 + 0x00,0x00,0x00,0x00,0x00, # TP6 A~D RP6 + + 0x15,0x41,0xA8,0x32,0x30,0x0A, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def TurnOnDisplay(self): + self.send_command(0x22) + self.send_data(0xC7) + self.send_command(0x20) + self.ReadBusy() + + def TurnOnDisplayPart(self): + self.send_command(0x22) + self.send_data(0x0c) + self.send_command(0x20) + self.ReadBusy() + + def init(self, update): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + if(update == self.FULL_UPDATE): + self.ReadBusy() + self.send_command(0x12) # soft reset + self.ReadBusy() + + self.send_command(0x74) #set analog block control + self.send_data(0x54) + self.send_command(0x7E) #set digital block control + self.send_data(0x3B) + + self.send_command(0x01) #Driver output control + self.send_data(0xF9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) #data entry mode + self.send_data(0x01) + + self.send_command(0x44) #set Ram-X address start/end position + self.send_data(0x00) + self.send_data(0x0F) #0x0C-->(15+1)*8=128 + + self.send_command(0x45) #set Ram-Y address start/end position + self.send_data(0xF9) #0xF9-->(249+1)=250 + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) #BorderWavefrom + self.send_data(0x03) + + self.send_command(0x2C) #VCOM Voltage + self.send_data(0x55) # + + self.send_command(0x03) + self.send_data(self.lut_full_update[70]) + + self.send_command(0x04) # + self.send_data(self.lut_full_update[71]) + self.send_data(self.lut_full_update[72]) + self.send_data(self.lut_full_update[73]) + + self.send_command(0x3A) #Dummy Line + self.send_data(self.lut_full_update[74]) + self.send_command(0x3B) #Gate time + self.send_data(self.lut_full_update[75]) + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_full_update[count]) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X127 + self.send_data(0xF9) + self.send_data(0x00) + self.ReadBusy() + else: + self.send_command(0x2C) #VCOM Voltage + self.send_data(0x26) + + self.ReadBusy() + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_partial_update[count]) + + self.send_command(0x37) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x3C) #BorderWavefrom + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if(imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + if pixels[x, y] == 0: + x = imwidth - x + buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + newy = imwidth - newy - 1 + buf[int(newx / 8) + newy*linewidth] &= ~(0x80 >> (y % 8)) + return buf + + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay() + + def displayPartial(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0x00] * self.height * linewidth + for j in range(0, self.height): + for i in range(0, linewidth): + buf[i + j * linewidth] = ~image[i + j * linewidth] + + self.send_command(0x24) + self.send_data2(image) + + + self.send_command(0x26) + self.send_data2(buf) + self.TurnOnDisplayPart() + + def displayPartBaseImage(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + self.TurnOnDisplay() + + def Clear(self, color=0xFF): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + # logger.debug(linewidth) + + buf = [0x00] * self.height * linewidth + for j in range(0, self.height): + for i in range(0, linewidth): + buf[i + j * linewidth] = color + + self.send_command(0x24) + self.send_data2(buf) + + # self.send_command(0x26) + # for j in range(0, self.height): + # for i in range(0, linewidth): + # self.send_data(color) + + self.TurnOnDisplay() + + def sleep(self): + # self.send_command(0x22) #POWER OFF + # self.send_data(0xC3) + # self.send_command(0x20) + + self.send_command(0x10) #enter deep sleep + self.send_data(0x03) + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/tools/station/display/epd2in13_V3.py b/tools/station/display/epd2in13_V3.py new file mode 100644 index 0000000..45734ee --- /dev/null +++ b/tools/station/display/epd2in13_V3.py @@ -0,0 +1,395 @@ +# ***************************************************************************** +# * | File : epd2in13_V3.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.2 +# * | Date : 2022-08-9 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +# --- Herkunftsvermerk (ninlight-firmware) --------------------------------- +# Unveraendert uebernommen aus waveshareteam/e-Paper (RaspberryPi_JetsonNano/ +# python/lib/waveshare_epd/epd2in13_V3.py), Commit 85802dee (2023-02-15). +# Einzige Aenderung: 'from . import epdconfig' -> 'import epdconfig', damit das +# Modul flach importierbar ist (kein Package). Alles andere - Init-Sequenz, LUTs, +# Timings - unveraendert. Details: tools/station/display/HERKUNFT.md +# ----------------------------------------------------------------------------- + + +import logging +import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_partial_update= [ + 0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x40,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x14,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0, + 0x22,0x17,0x41,0x00,0x32,0x36, + ] + + lut_full_update = [ + 0x80,0x4A,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x40,0x4A,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x80,0x4A,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x40,0x4A,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xF,0x0,0x0,0x0,0x0,0x0,0x0, + 0xF,0x0,0x0,0xF,0x0,0x0,0x2, + 0xF,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0, + 0x22,0x17,0x41,0x0,0x32,0x36, + ] + + ''' + function :Hardware reset + parameter: + ''' + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + ''' + function :send command + parameter: + command : Command register + ''' + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + ''' + function :send data + parameter: + data : Write data + ''' + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + ''' + function :Wait until the busy_pin goes LOW + parameter: + ''' + def ReadBusy(self): + logger.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + ''' + function : Turn On Display + parameter: + ''' + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Part + parameter: + ''' + def TurnOnDisplayPart(self): + self.send_command(0x22) # Display Update Control + self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Set lut + parameter: + lut : lut data + ''' + def Lut(self, lut): + self.send_command(0x32) + for i in range(0, 153): + self.send_data(lut[i]) + self.ReadBusy() + + ''' + function : Send lut data and configuration + parameter: + lut : lut data + ''' + def SetLut(self, lut): + self.Lut(lut) + self.send_command(0x3f) + self.send_data(lut[153]) + self.send_command(0x03) # gate voltage + self.send_data(lut[154]) + self.send_command(0x04) # source voltage + self.send_data(lut[155]) # VSH + self.send_data(lut[156]) # VSH2 + self.send_data(lut[157]) # VSL + self.send_command(0x2c) # VCOM + self.send_data(lut[158]) + + ''' + function : Setting the display window + parameter: + xstart : X-axis starting position + ystart : Y-axis starting position + xend : End position of X-axis + yend : End position of Y-axis + ''' + def SetWindow(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x_start>>3) & 0xFF) + self.send_data((x_end>>3) & 0xFF) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + + ''' + function : Set Cursor + parameter: + x : X-axis starting position + y : Y-axis starting position + ''' + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data(x & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + + ''' + function : Initialize the e-Paper register + parameter: + ''' + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) #SWRESET + self.ReadBusy() + + self.send_command(0x01) #Driver output control + self.send_data(0xf9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) #data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x3c) + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.send_command(0x18) + self.send_data(0x80) + + self.ReadBusy() + + self.SetLut(self.lut_full_update) + return 0 + + ''' + function : Display images + parameter: + image : Image data + ''' + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if(imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif(imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width/8) * self.height) + + buf = bytearray(img.tobytes('raw')) + return buf + + ''' + function : Sends the image buffer in RAM to e-Paper and displays + parameter: + image : Image data + ''' + def display(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and partial refresh + parameter: + image : Image data + ''' + def displayPartial(self, image): + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + epdconfig.digital_write(self.reset_pin, 1) + + self.SetLut(self.lut_partial_update) + self.send_command(0x37) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) #BorderWavefrom + self.send_data(0x80) + + self.send_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.ReadBusy() + + self.SetWindow(0, 0, self.width - 1, self.height - 1) + self.SetCursor(0, 0) + + self.send_command(0x24) # WRITE_RAM + # for j in range(0, self.height): + # for i in range(0, linewidth): + # self.send_data(image[i + j * linewidth]) + self.send_data2(image) + self.TurnOnDisplayPart() + + ''' + function : Refresh a base image + parameter: + image : Image data + ''' + def displayPartBaseImage(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + self.TurnOnDisplay() + + ''' + function : Clear screen + parameter: + ''' + def Clear(self, color=0xFF): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + # logger.debug(linewidth) + + self.send_command(0x24) + self.send_data2([color] * int(self.height * linewidth)) + self.TurnOnDisplay() + + ''' + function : Enter sleep mode + parameter: + ''' + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/tools/station/display/epdconfig.py b/tools/station/display/epdconfig.py new file mode 100644 index 0000000..8eb93b8 --- /dev/null +++ b/tools/station/display/epdconfig.py @@ -0,0 +1,139 @@ +"""GPIO/SPI-Unterbau fuer die vendored Waveshare-Panel-Treiber (epd2in13_V2.py, +epd2in13_V3.py), geschrieben gegen gpiozero + spidev statt gegen Waveshares eigenes +epdconfig.py, das RPi.GPIO voraussetzt - ein Modul, das auf Debian 13 (trixie) mit +Python 3.13 nicht mehr baut/importiert. gpiozero, lgpio (gpiozero's Pin-Factory auf +trixie) und spidev sind auf dem Ziel-Pi vorhanden. + +Stellt genau die Schnittstelle bereit, die die beiden Treiber erwarten: die Pin-Konstanten +RST_PIN/DC_PIN/CS_PIN/BUSY_PIN sowie digital_write, digital_read, delay_ms, spi_writebyte, +spi_writebyte2, module_init und module_exit als Modulfunktionen (kein Klassen-Wrapper - +die Treiber greifen direkt auf `epdconfig.` zu). + +CS_PIN wird NICHT als GPIO geschaltet: spidev.SpiDev(0, 0) steuert die Hardware-CS0-Leitung +selbst bei jeder Übertragung, ein zusaetzliches Software-Toggle wuerde nur mit der Hardware +um die Leitung konkurrieren. digital_write()/digital_read() sind fuer CS_PIN deshalb +No-ops - genau das Muster, das Waveshares eigene, neuere epdconfig.py-Fassung fuer +Raspberry Pi inzwischen ebenfalls verwendet (dort ist die GPIO-Instanziierung fuer CS_PIN +auskommentiert). + +Ein fehlender Import (gpiozero/spidev nicht installiert) oder ein fehlendes +/dev/spidev0.0 wirft DisplayUnavailable - display.py faengt das ab, protokolliert eine +Zeile und beendet sich sauber (sys.exit(0)), statt dass ein fehlendes Panel eine Migration +verhindern koennte (siehe Modul-Docstring von display.py).""" +from __future__ import annotations + +import os +import time + +try: + import gpiozero +except ImportError: # pragma: no cover - auf dem Pi immer vorhanden, hier nur defensiv + gpiozero = None + +try: + import spidev +except ImportError: # pragma: no cover - dito + spidev = None + + +class DisplayUnavailable(Exception): + """Kein Panel ansprechbar: Bibliothek fehlt, /dev/spidev0.0 fehlt, oder der + Hardwarezugriff selbst ist fehlgeschlagen (z.B. Berechtigung, Pin belegt).""" + + +# Dieselben Pin-Nummern wie in Waveshares eigenem epdconfig.py fuer den Raspberry Pi. +RST_PIN = 17 +DC_PIN = 25 +CS_PIN = 8 +BUSY_PIN = 24 + +_SPI_BUS = 0 +_SPI_DEVICE = 0 +_SPI_HZ = 4_000_000 + +_rst_pin = None +_dc_pin = None +_busy_pin = None +_spi = None + + +def digital_write(pin: int, value: int) -> None: + if pin == RST_PIN: + _rst_pin.value = bool(value) + elif pin == DC_PIN: + _dc_pin.value = bool(value) + elif pin == CS_PIN: + pass # Hardware-CS ueber spidev, siehe Modul-Docstring. + # Andere Pins fragen die Treiber nicht ab - kein else-Zweig noetig. + + +def digital_read(pin: int) -> int: + if pin == BUSY_PIN: + return int(bool(_busy_pin.value)) + if pin == RST_PIN: + return int(bool(_rst_pin.value)) + if pin == DC_PIN: + return int(bool(_dc_pin.value)) + return 0 + + +def delay_ms(ms: int) -> None: + time.sleep(ms / 1000.0) + + +def spi_writebyte(data) -> None: + _spi.writebytes(data) + + +def spi_writebyte2(data) -> None: + _spi.writebytes2(data) + + +def module_init() -> int: + """Oeffnet GPIO-Pins und SPI-Geraet. Wirft DisplayUnavailable statt irgendeiner + Bibliotheksausnahme, damit display.py genau eine Ausnahmeklasse abfangen muss.""" + global _rst_pin, _dc_pin, _busy_pin, _spi + + if gpiozero is None or spidev is None: + raise DisplayUnavailable("gpiozero/spidev nicht installiert") + + spi_path = f"/dev/spidev{_SPI_BUS}.{_SPI_DEVICE}" + if not os.path.exists(spi_path): + raise DisplayUnavailable(f"{spi_path} fehlt - ist SPI aktiviert (raspi-config)?") + + try: + _rst_pin = gpiozero.DigitalOutputDevice(RST_PIN) + _dc_pin = gpiozero.DigitalOutputDevice(DC_PIN) + _busy_pin = gpiozero.DigitalInputDevice(BUSY_PIN, pull_up=False) + + _spi = spidev.SpiDev() + _spi.open(_SPI_BUS, _SPI_DEVICE) + _spi.max_speed_hz = _SPI_HZ + _spi.mode = 0b00 + except Exception as e: # noqa: BLE001 - alles hier wird zu DisplayUnavailable + module_exit() + raise DisplayUnavailable(f"GPIO/SPI-Initialisierung fehlgeschlagen: {e}") from e + + return 0 + + +def module_exit() -> None: + """Gibt GPIO-Pins und SPI-Geraet frei. Verzeiht, wenn module_init() nie (vollstaendig) + gelaufen ist - display.py ruft das u.a. im Fehlerpfad von module_init() selbst auf.""" + global _rst_pin, _dc_pin, _busy_pin, _spi + + if _spi is not None: + try: + _spi.close() + except Exception: # noqa: BLE001 - Aufraeumen darf nicht seinerseits scheitern + pass + _spi = None + + for attr_name in ("_rst_pin", "_dc_pin", "_busy_pin"): + device = globals()[attr_name] + if device is not None: + try: + device.close() + except Exception: # noqa: BLE001 + pass + globals()[attr_name] = None diff --git a/tools/station/display/render.py b/tools/station/display/render.py new file mode 100644 index 0000000..c89c00e --- /dev/null +++ b/tools/station/display/render.py @@ -0,0 +1,133 @@ +"""Textzerlegung und Bildaufbau fuer die E-Paper-Anzeige der ninlight-Station. + +lines() ist bewusst ohne Pillow und ohne Hardwarezugriff importierbar und testbar (siehe +tools/station/tests/test_render.py) - compose() importiert Pillow erst innerhalb der +Funktion, damit ein fehlendes PIL (das erst per "apt install python3-pil" auf den Pi +kommt, siehe setup_pi.sh) render.py selbst nicht unbenutzbar macht. + +Das Panel ist 250x122 Pixel, quer verwendet. MAX_COLS begrenzt jede Textzeile so, dass sie +bei der in compose() verwendeten Schriftgroesse nicht umgebrochen werden muss - ueberlange +Werte (z.B. ein Produktname aus der Lampenerkennung) werden mit "..." gekuerzt, nie +umgebrochen (Spec: eine zu lange Zeile darf das Layout nicht sprengen). + +Der Zustand, den lines()/compose() bekommen, ist bereits zusammengefuehrt: status.json +(vom Server geschrieben) traegt kein "ssid"-Feld - die SSID ist Konfiguration aus +/etc/ninlight-station.conf. display.py liest beides und reicht ein gemeinsames dict +herein. Jedes Feld wird ueber .get() mit Vorgabewert gelesen: status.json, +control.json und die Konfigurationsdatei koennen jederzeit fehlen, halb geschrieben oder +aus einer aelteren Fassung sein (frisch aufgesetzter Pi, Schreibfehler mitten im +Schreiben, Neustart) - siehe test_missing_fields_do_not_raise.""" +from __future__ import annotations + +MAX_COLS = 42 + +MODE_LABELS = {"recovery": "RETTUNG", "migration": "MIGRATION"} +# "armed" wird gesondert behandelt (braucht armed_target im Text), deshalb hier nicht +# gelistet. +PHASE_LABELS = {"auto": "AUTO", "listen": "HORCHT"} + + +def _cut(text: object) -> str: + """Kuerzt auf MAX_COLS Zeichen, mit "..." am Ende statt Umbruch.""" + text = str(text) + if len(text) <= MAX_COLS: + return text + if MAX_COLS <= 1: + return text[:MAX_COLS] + return text[: MAX_COLS - 1] + "…" + + +def _hhmm(ts: object) -> str: + """"2026-09-13T13:04:02Z" -> "13:04". Alles andere (fehlt, falsch geformt, aus einer + aelteren Serverfassung ohne Zeitstempel) -> "--:--".""" + if isinstance(ts, str) and len(ts) >= 16 and ts[10:11] == "T": + return ts[11:16] + return "--:--" + + +def lines(state: dict) -> list[str]: + """Zerlegt den zusammengefuehrten Zustand in Textzeilen fuers Panel. Wirft nie - + fehlende/falsch geformte Felder werden ausgelassen statt eine Ausnahme zu werfen.""" + state = state if isinstance(state, dict) else {} + mode = state.get("mode") + out: list[str] = [] + + if mode == "off": + # Die Station ist komplett aus (kein status.json vorhanden) - display.py setzt + # diesen Modus selbst, server.py kennt ihn nicht. Bewusst keine weiteren Zeilen + # (insbesondere keine SSID - die waere aus einer alten Konfigurationsdatei und + # gerade nicht aktiv). + out.append("Station aus") + return out + + if mode not in ("recovery", "migration"): + # Unbekannter oder fehlender Modus, z.B. ein frisch aufgesetzter Pi, auf dem noch + # nichts geschrieben wurde. Lieber ehrlich "unbekannt" zeigen als einen Modus zu + # unterstellen. + out.append(_cut(f"Status unbekannt ({mode})") if mode else "Status unbekannt") + return out + + out.append(MODE_LABELS[mode]) + + ssid = state.get("ssid") + if ssid: + out.append(_cut(f"SSID: {ssid}")) + + if mode == "migration": + # Phase kommt aus control.json, nicht aus status.json (das schreibt der Server + # nur beim Start und nach einer Auslieferung) - display.py reicht sie hier als + # Teil des zusammengefuehrten Zustands herein. Im Rettungsmodus gibt es keine + # Phase, deshalb dieser Block nur hier drin. + phase = state.get("phase") + if phase == "armed": + target = state.get("armed_target") or "?" + out.append(_cut(f"FREIGEGEBEN: {target}")) + elif phase in PHASE_LABELS: + out.append(PHASE_LABELS[phase]) + + last_seen = state.get("last_seen") + if not isinstance(last_seen, dict): + last_seen = {} + product_name = last_seen.get("product_name") + if product_name: + out.append(_cut(product_name)) + target = last_seen.get("target") + variant = last_seen.get("variant") + if target or variant: + out.append(_cut(f"{target or '?'} ({variant or '?'})")) + lamp_version = last_seen.get("lamp_version") + if lamp_version: + out.append(_cut(f"FW: {lamp_version}")) + + served = state.get("served") + out.append(_cut(f"Ausgeliefert: {served if isinstance(served, int) else 0}")) + + recent = state.get("recent") + if isinstance(recent, list) and recent: + last = recent[-1] + if isinstance(last, dict): + out.append(_cut(f"{_hhmm(last.get('ts'))} {last.get('target') or '?'} " + f"{last.get('status') or '?'}")) + + return out + + +def compose(state: dict): + """Baut das Panel-Bild (250x122, 1-Bit) aus lines(). Importiert Pillow erst hier - + siehe Modul-Docstring.""" + from PIL import Image, ImageDraw, ImageFont + + width, height = 250, 122 + image = Image.new("1", (width, height), 255) + draw = ImageDraw.Draw(image) + font = ImageFont.load_default() + + line_height = 14 + y = 2 + for line in lines(state): + if y > height - line_height: + break + draw.text((2, y), line, font=font, fill=0) + y += line_height + + return image diff --git a/tools/station/ninlight-display.service b/tools/station/ninlight-display.service new file mode 100644 index 0000000..91c0bac --- /dev/null +++ b/tools/station/ninlight-display.service @@ -0,0 +1,49 @@ +[Unit] +Description=ninlight E-Paper-Anzeige (Status der Migrations-/Rettungsstation) +# Nur eine Anzeige fuer den Hauptdienst - Wants=/After=, NICHT Requires=/BindsTo=: ein +# fehlendes oder abgestuerztes Panel darf server.py niemals mitreissen, und server.py +# darf umgekehrt starten, auch wenn dieser Dienst fehlt oder deaktiviert ist. Siehe +# display.py's Modul-Docstring: die Anzeige ist nirgends Voraussetzung fuer die +# Migration. +After=ninlight-station.service +Wants=ninlight-station.service + +[Service] +Type=simple +# Reihenfolge wichtig: systemd wendet Environment=/EnvironmentFile= in der Reihenfolge +# an, in der sie hier stehen, und eine spaetere Zuweisung ueberschreibt eine gleichnamige +# fruehere. EPD_REVISION=V3 hier ist nur der Vorgabewert, falls die Konfigurationsdatei +# (unten, per EnvironmentFile=) den Schluessel (noch) nicht enthaelt - z.B. auf einem Pi, +# der vor Task 6 aufgesetzt wurde. Steht er dort, gewinnt er. +Environment=EPD_REVISION=V3 +Environment=NINLIGHT_RUN_DIR=/run/ninlight-station +Environment=NINLIGHT_STATION_CONF=/etc/ninlight-station.conf +EnvironmentFile=/etc/ninlight-station.conf +ExecStart=/usr/bin/python3 /opt/ninlight-station/display/display.py + +# display.py faengt ein fehlendes/abgestuerztes Panel als DisplayUnavailable ab, +# protokolliert eine Zeile und beendet sich mit sys.exit(0) - das ist also nie ein +# "Fehlschlag" im systemd-Sinne und loest kein Restart= aus. Restart=on-failure (nicht +# always) greift nur fuer echte Abstuerze (z.B. ein Python-Traceback aus einem +# Programmierfehler); ohne den Deckel wuerde ein dauerhaft fehlendes Panel sonst NICHT +# staendig neu versucht (gut so), waehrend ein echter Bug endlos neu starten wuerde und +# so das Journal fluten koennte. +Restart=on-failure +RestartSec=5 +StartLimitIntervalSec=60 +StartLimitBurst=5 + +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes +ReadOnlyPaths=/run/ninlight-station +RestrictAddressFamilies=AF_UNIX AF_NETLINK +MemoryMax=64M + +# Fuer /dev/spidev0.0 und die GPIO-Zeichenleitung (per lgpio/gpiozero) noetig - laeuft +# deshalb nicht mit DynamicUser=, sondern als root wie der Hauptdienst. +SupplementaryGroups=spi gpio + +[Install] +WantedBy=multi-user.target diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py new file mode 100644 index 0000000..7ca1f01 --- /dev/null +++ b/tools/station/tests/test_render.py @@ -0,0 +1,56 @@ +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "display")) + +import render # noqa: E402 + + +def test_off_mode_says_so_and_shows_nothing_else(): + lines = render.lines({"mode": "off"}) + assert any("Station aus" in l for l in lines) + assert not any("SSID" in l for l in lines) + + +def test_migration_shows_detected_lamp(): + lines = render.lines({ + "mode": "migration", "phase": "auto", "served": 3, + "ssid": "qconnex-firmware", + "last_seen": {"product_name": "Klyqa GU10 RGB CW/WW", "target": "gu10_rgb", + "variant": "prod", "lamp_version": "V1.1.0.1138"}, + "recent": [{"target": "gu10_rgb", "status": "ok", "ts": "2026-09-13T13:04:02Z"}]}) + text = "\n".join(lines) + assert "MIGRATION" in text + assert "Klyqa GU10 RGB CW/WW" in text + assert "V1.1.0.1138" in text + assert "prod" in text + assert "13:04" in text + + +def test_recovery_mode_without_traffic(): + lines = render.lines({"mode": "recovery", "ssid": "ninlight-recovery", + "served": 0, "recent": []}) + text = "\n".join(lines) + assert "RETTUNG" in text + assert "ninlight-recovery" in text + + +def test_long_product_name_is_truncated_not_wrapped(): + """Das Panel ist 250 px breit - eine zu lange Zeile darf das Layout nicht sprengen.""" + lines = render.lines({"mode": "migration", "phase": "listen", "served": 0, "recent": [], + "ssid": "qconnex-firmware", + "last_seen": {"product_name": "X" * 90, "target": "e27_rgb", + "variant": "dev", "lamp_version": "V1"}}) + assert all(len(l) <= render.MAX_COLS for l in lines) + + +def test_missing_fields_do_not_raise(): + """status.json kann jederzeit halb geschrieben oder aus einer aelteren Version sein.""" + for state in ({}, {"mode": "migration"}, {"mode": "migration", "last_seen": {}}): + assert render.lines(state) + + +def test_armed_phase_is_visible(): + lines = render.lines({"mode": "migration", "phase": "armed", "armed_target": "g95_cw", + "served": 0, "recent": [], "ssid": "qconnex-firmware"}) + assert "FREIGEGEBEN" in "\n".join(lines) From 9b158c8c4f6215f490f62a202faa81f984cafb9e Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 21:07:01 +0200 Subject: [PATCH 118/150] fix(station): V2/V3-Panelkapselung, wiedereintrittsfaehiges module_init, Unit-Fixes Fixrunde 1 zu Task 6a: epd2in13_V2.init() braucht ein Pflichtargument und dessen displayPartial() setzt selbst keine LUT (neue Panel-Klasse in display.py kapselt das statt es zu verstreuen); epdconfig.module_init() war nicht wiedereintrittsfaehig und ein zu breites except tarnte GPIOPinInUse als "Panel fehlt" (jetzt idempotent, gezielter except); ninlight-display.service hatte ReadOnlyPaths=/run/ninlight-station (verhindert den Start, wenn die Station regulaer aus ist), StartLimit* in der falschen Sektion und SupplementaryGroups=spi gpio (harter Fehler auf reinem Debian ohne diese Gruppen); ein fehlendes Pillow fuehrte zu ModuleNotFoundError statt zum vorgesehenen stillen Exit 0. Dazu: Panel schlaeft nach jedem Zeichnen (Alterung), Modus-Vorrang liegt jetzt bei RUN_DIR/mode statt bei status.json, export-Praefix in der Konfigurationsdatei wird erkannt, MAX_COLS auf das mit Pillow nachgemessene Mass korrigiert, und build_state() hat jetzt eigene Tests. --- tools/station/display/display.py | 176 ++++++++++++++++--------- tools/station/display/epdconfig.py | 27 +++- tools/station/display/render.py | 11 +- tools/station/ninlight-display.service | 28 +++- tools/station/tests/test_render.py | 73 ++++++++++ 5 files changed, 243 insertions(+), 72 deletions(-) diff --git a/tools/station/display/display.py b/tools/station/display/display.py index 8db5677..2e89a1d 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -19,11 +19,11 @@ wiederholen. Zwischen den beiden Versuchen kurz die Stromversorgung des Panels t (--clear reicht nicht immer, falsche Revision kann das Panel in einem Zwischenzustand lassen). -Die Anzeige ist nirgends Voraussetzung fuer die Migration: fehlt das Panel, SPI oder die -gpiozero/spidev-Bibliothek, faengt main() das als DisplayUnavailable ab, protokolliert -GENAU EINE Zeile und beendet sich mit sys.exit(0) - nicht mit einem Fehler, sonst wuerde -systemd (Restart=on-failure) den Dienst endlos neu starten und das Journal fluten. Ein -Display-Fehler darf niemals eine Migration verhindern. +Die Anzeige ist nirgends Voraussetzung fuer die Migration: fehlt das Panel, SPI oder eine +Bibliothek (gpiozero/spidev/Pillow), faengt main() das als DisplayUnavailable bzw. +ImportError ab, protokolliert GENAU EINE Zeile und beendet sich mit sys.exit(0) - nicht mit +einem Fehler, sonst wuerde systemd (Restart=on-failure) den Dienst endlos neu starten und +das Journal fluten. Ein Display-Fehler darf niemals eine Migration verhindern. """ from __future__ import annotations @@ -73,8 +73,11 @@ def _read_json(path: Path) -> dict: def _read_conf(path: Path) -> dict[str, str]: """Minimaler Parser fuer /etc/ninlight-station.conf: KEY='VALUE'-Zeilen, wie - setup_pi.sh sie schreibt. Kein echtes Shell-Sourcing (das CLI braucht das fuer $- - und `-Escaping in Passwoertern, siehe dessen Kommentar - display.py liest hier nur + setup_pi.sh sie schreibt, optional mit einem "export "-Praefix (setup_pi.sh prueft + beim Nachtragen fehlender Schluessel ausdruecklich auf "^(export[[:space:]]+)?KEY=" - + ein bereits vorhandenes "export SSID=..." muss also erkannt werden, nicht nur die + unpraefigierte Form). Kein echtes Shell-Sourcing (das CLI braucht das fuer $- und + `-Escaping in Passwoertern, siehe dessen Kommentar - display.py liest hier nur SSID/MIGRATE_SSID, reine Bezeichner ohne Sonderzeichen).""" values: dict[str, str] = {} try: @@ -85,6 +88,8 @@ def _read_conf(path: Path) -> dict[str, str]: line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue + if line.startswith("export ") or line.startswith("export\t"): + line = line[len("export"):].lstrip() key, _, value = line.partition("=") key = key.strip() value = value.strip() @@ -106,12 +111,19 @@ def _read_mode_file(run_dir: Path) -> str: return value if value in ("recovery", "migration") else "recovery" +# Nur beim ersten Mal protokollieren, dass systemctl selbst nicht erreichbar war (siehe +# _service_active) - das passiert bei jedem der Aufrufe alle 5 s erneut, und eine Zeile +# alle 5 s wuerde das Journal fluten, obwohl es sich nie aendert (Fixrunde 1, Kleinigkeit). +_service_active_fallback_logged = False + + def _service_active(service_name: str) -> bool: """Laeuft der Hauptdienst (server.py) gerade? "ninlight-station off" stoppt ihn und faehrt den Zugangspunkt herunter, laesst status.json dabei aber unangetastet liegen - ein reines Lesen von status.json wuerde nach "off" also faelschlich weiter den letzten Modus vor dem Abschalten zeigen. Deshalb ist die Dienstaktivitaet selbst (nicht das Vorhandensein von status.json) die Quelle fuer den Anzeigemodus "off".""" + global _service_active_fallback_logged try: result = subprocess.run( ["systemctl", "is-active", "--quiet", service_name], @@ -120,26 +132,33 @@ def _service_active(service_name: str) -> bool: except (OSError, subprocess.SubprocessError): # systemctl selbst nicht erreichbar (z.B. Handlauf ohne systemd) - dann anhand # der Statusdatei entscheiden statt "off" zu behaupten, ohne es zu wissen. + if not _service_active_fallback_logged: + log.warning("systemctl nicht erreichbar - Anzeige raet den Modus anhand von " + "RUN_DIR/mode statt der echten Dienstaktivitaet") + _service_active_fallback_logged = True return True def build_state(run_dir: Path = RUN_DIR, conf_path: Path = CONF_PATH, service_name: str = SERVICE_NAME) -> dict: - """Baut den zusammengefuehrten Zustand, den render.lines()/compose() erwarten - - status.json (served/recent/last_seen), control.json (phase/armed_target, nur im - Migrationsmodus massgeblich), die SSID aus der Konfigurationsdatei und "off", wenn - der Hauptdienst gar nicht laeuft.""" + """Baut den zusammengefuehrten Zustand, den render.lines()/compose() erwarten. + + Modus kommt vorrangig aus RUN_DIR/mode (Vorgabe "recovery", wie + server.py:resolve_mode()) - das ist das, was "ninlight-station on"/"migrate on" + zuletzt geschrieben haben und unabhaengig davon verfuegbar, ob/wann der Serverprozess + seit dem letzten Start schon einmal status.json geschrieben hat. status.json selbst + liefert nur served/recent/last_seen; sein "mode"-Feld wird bewusst NICHT als + Modusquelle verwendet (Fixrunde 1: die urspruengliche Fassung hatte den Vorrang + umgekehrt). + + Phase/armed_target kommen aus control.json, nur im Migrationsmodus massgeblich - der + Rettungsmodus ignoriert control.json ebenso wie server.py das tut. "off" ueberschreibt + alles, wenn der Hauptdienst gar nicht laeuft.""" if not _service_active(service_name): return {"mode": "off"} + mode = _read_mode_file(run_dir) status = _read_json(run_dir / "status.json") - mode = status.get("mode") - if mode not in ("recovery", "migration"): - # status.json fehlt/ist kaputt/stammt (noch) nicht vom laufenden Prozess - - # RUN_DIR/mode ist das, was "ninlight-station on"/"migrate on" zuletzt - # geschrieben hat und ist unabhaengig von status.json verfuegbar. - mode = _read_mode_file(run_dir) - conf = _read_conf(conf_path) state: dict = {"mode": mode, "served": status.get("served"), "recent": status.get("recent")} @@ -148,13 +167,6 @@ def build_state(run_dir: Path = RUN_DIR, conf_path: Path = CONF_PATH, else: # migration state["ssid"] = conf.get("MIGRATE_SSID") state["last_seen"] = status.get("last_seen") - # Die Phase kommt bewusst aus control.json, nicht aus status.json: server.py - # schreibt status.json nur beim Start und nach einer Auslieferung, waehrend - # "ninlight-station arm"/"listen" nur die Steuerdatei anfasst (Spec/CLI - # status_cmd folgt demselben Muster). Und bewusst nur HIER gelesen - im - # Rettungsmodus ignoriert server.py control.json ebenfalls, ein - # liegengebliebener Inhalt aus einer frueheren Migration darf dort nicht - # faelschlich "Phase: FREIGEGEBEN" zeigen. control = _read_json(run_dir / "control.json") phase = control.get("phase") state["phase"] = phase if phase in ("auto", "listen", "armed") else "auto" @@ -177,28 +189,55 @@ def _load_epd_module(revision: str): return epd_module -def _draw(epd, image, full: bool) -> None: - buf = epd.getbuffer(image) - if full: - # epd.init() laedt die volle LUT neu - noetig, weil displayPartial() unten immer - # die Teil-LUT setzt und die naechste display()-Auslieferung sonst mit der - # falschen Wellenform liefe. - epd.init() - epd.display(buf) - else: - epd.displayPartial(buf) +class Panel: + """Kapselt die V2/V3-Unterschiede der Waveshare-Treiber hinter einer einheitlichen + Schnittstelle (Fixrunde 1, C1): epd2in13_V2.init() braucht ein Pflichtargument + (FULL_UPDATE/PART_UPDATE) und dessen displayPartial() setzt selbst keine LUT - vorher + muessen init(PART_UPDATE) und (fuer den allerersten Teilaufbau nach einem Vollaufbau) + displayPartBaseImage() gelaufen sein, sonst zeichnet das Panel mit der falschen + Wellenform oder gegen einen veralteten Referenzpuffer. epd2in13_V3.init() braucht + dagegen kein Argument und dessen displayPartial() setzt seine Teil-LUT selbst bei + jedem Aufruf - dort reicht ein einfaches init() vor jedem Vollaufbau.""" + + def __init__(self, revision: str) -> None: + self.revision = revision + self._epd_module = _load_epd_module(revision) + self.epd = self._epd_module.EPD() + + def clear(self) -> None: + if self.revision == "V2": + self.epd.init(self.epd.FULL_UPDATE) + else: + self.epd.init() + self.epd.Clear(0xFF) + + def draw_full(self, image) -> None: + buf = self.epd.getbuffer(image) + if self.revision == "V2": + self.epd.init(self.epd.FULL_UPDATE) + # displayPartBaseImage() zeichnet UND schreibt gleichzeitig beide RAM-Puffer + # des Controllers - genau das, was ein nachfolgender Teilaufbau als + # Referenzpuffer braucht. display() allein wuerde nur einen Puffer setzen und + # den ersten Teilaufbau danach gegen einen veralteten Referenzpuffer diffen + # lassen (Fixrunde 1, C1). + self.epd.displayPartBaseImage(buf) + else: + self.epd.init() + self.epd.display(buf) + + def draw_partial(self, image) -> None: + buf = self.epd.getbuffer(image) + if self.revision == "V2": + self.epd.init(self.epd.PART_UPDATE) + self.epd.displayPartial(buf) + + def sleep(self) -> None: + self.epd.sleep() -def run_test(revision: str) -> None: - """Zeichnet ein Testbild und beendet sich - siehe Modul-Docstring, wie man damit V2 - von V3 unterscheidet.""" +def _test_image(revision: str): from PIL import Image, ImageDraw, ImageFont - epd_module = _load_epd_module(revision) - epd = epd_module.EPD() - epd.init() - epd.Clear(0xFF) - width, height = 250, 122 image = Image.new("1", (width, height), 255) draw = ImageDraw.Draw(image) @@ -211,25 +250,29 @@ def run_test(revision: str) -> None: for i in range(4): x0 = 4 + i * 20 draw.rectangle((x0, 50, x0 + 16, 90), outline=0, fill=0 if i % 2 == 0 else 255) + return image - epd.display(epd.getbuffer(image)) + +def run_test(revision: str) -> None: + """Zeichnet ein Testbild und beendet sich - siehe Modul-Docstring, wie man damit V2 + von V3 unterscheidet.""" + panel = Panel(revision) + panel.clear() + panel.draw_full(_test_image(revision)) + panel.sleep() log.info("Testbild gezeichnet (EPD_REVISION=%s)", revision) def run_clear(revision: str) -> None: - epd_module = _load_epd_module(revision) - epd = epd_module.EPD() - epd.init() - epd.Clear(0xFF) - epd.sleep() + panel = Panel(revision) + panel.clear() + panel.sleep() log.info("Panel geleert (EPD_REVISION=%s)", revision) def run_daemon(revision: str) -> None: - epd_module = _load_epd_module(revision) - epd = epd_module.EPD() - epd.init() - epd.Clear(0xFF) + panel = Panel(revision) + panel.clear() last_lines: list[str] | None = None last_mode: object = object() # garantiert ungleich jedem echten Modus beim ersten Lauf @@ -244,9 +287,18 @@ def run_daemon(revision: str) -> None: if current_lines != last_lines or need_full: image = render.compose(state) - _draw(epd, image, full=need_full) if need_full: + panel.draw_full(image) last_full = now + else: + panel.draw_partial(image) + # Panel nach jedem Bildaufbau schlafen legen: die Pixel bleiben (e-Paper ist + # bistabil), aber ohne dauerhafte Spannung altert das Panel deutlich weniger - + # Waveshare warnt fuer diese Serie ausdruecklich davor, es dauerhaft unter + # Bias zu lassen (Fixrunde 1, I4). epd.sleep() gibt auch GPIO/SPI frei + # (epdconfig.module_exit()); der naechste Aufbau ruft ohnehin wieder init() + # und damit module_init() auf, das das sauber neu aufsetzt. + panel.sleep() last_lines = current_lines last_mode = mode @@ -271,11 +323,17 @@ def main() -> None: run_clear(revision) else: run_daemon(revision) - except DisplayUnavailable as e: - # Genau eine Zeile, dann sys.exit(0) - siehe Modul-Docstring. Restart=on-failure - # (statt always) in der Unit greift hier nicht, weil 0 kein Fehlschlag ist; ein - # spaeter angestecktes Panel braucht also einen manuellen/zeitgesteuerten - # Neustart der Unit, nicht eine Neustartschleife. + except (DisplayUnavailable, ImportError) as e: + # Genau eine Zeile, dann sys.exit(0) - siehe Modul-Docstring. Das deckt sowohl + # Hardwareprobleme (DisplayUnavailable: kein Panel/SPI) als auch eine fehlende + # Bibliothek ab - allen voran Pillow, das laut Spec auf dem Ziel-Pi absichtlich + # erst per "apt install python3-pil" nachkommt (Fixrunde 1, C4): render.compose() + # importiert Pillow erst hier, ein fehlendes Paket zeigt sich also erst als + # ModuleNotFoundError (eine ImportError-Unterklasse) beim ersten Zeichnen, nicht + # schon beim Start. Restart=on-failure (statt always) in der Unit greift hier + # nicht, weil 0 kein Fehlschlag ist; ein spaeter angestecktes Panel bzw. + # nachinstalliertes Paket braucht also einen manuellen/zeitgesteuerten Neustart + # der Unit, nicht eine Neustartschleife. log.info("E-Paper-Anzeige nicht verfuegbar, beende ohne Fehler: %s", e) sys.exit(0) except KeyboardInterrupt: diff --git a/tools/station/display/epdconfig.py b/tools/station/display/epdconfig.py index 8eb93b8..1f7b4b6 100644 --- a/tools/station/display/epdconfig.py +++ b/tools/station/display/epdconfig.py @@ -90,10 +90,23 @@ def spi_writebyte2(data) -> None: def module_init() -> int: - """Oeffnet GPIO-Pins und SPI-Geraet. Wirft DisplayUnavailable statt irgendeiner - Bibliotheksausnahme, damit display.py genau eine Ausnahmeklasse abfangen muss.""" + """Oeffnet GPIO-Pins und SPI-Geraet. Wirft DisplayUnavailable statt einer rohen + Bibliotheksausnahme, damit display.py genau eine Ausnahmeklasse abfangen muss. + + Idempotent: ein zweiter Aufruf, waehrend die Pins/das SPI-Geraet noch offen sind (die + Panel-Treiber rufen dies bei JEDEM epd.init() erneut auf - Vollaufbau bei Moduswechsel + oder alle 30 Minuten loest also ohne diese Absicherung einen zweiten, unnoetigen + Oeffnungsversuch aus), kehrt sofort zurueck statt gpiozero.DigitalOutputDevice(17) + ein zweites Mal anzulegen. Das wuerde mit GPIOPinInUse scheitern - vom breiten + except unten (Fixrunde 1, C2) frueher faelschlich als "Panel fehlt" verschluckt, + obwohl es ein Programmierfehler in der Aufrufreihenfolge ist. Nach module_exit() + (z.B. durch epd.sleep()) ist _spi wieder None, ein echter Neuaufbau laeuft dann + normal.""" global _rst_pin, _dc_pin, _busy_pin, _spi + if _spi is not None: + return 0 + if gpiozero is None or spidev is None: raise DisplayUnavailable("gpiozero/spidev nicht installiert") @@ -101,6 +114,14 @@ def module_init() -> int: if not os.path.exists(spi_path): raise DisplayUnavailable(f"{spi_path} fehlt - ist SPI aktiviert (raspi-config)?") + # Nur Fehler, die tatsaechlich von einer fehlenden/blockierten Hardware kommen, werden + # zu DisplayUnavailable - alles andere (ein echter Programmierfehler an anderer + # Stelle) soll weiterhin als das erkennbar bleiben, was es ist, statt hier als + # "Panel fehlt" getarnt zu werden (Fixrunde 1, C2, zweiter Teil). + hardware_errors: tuple[type[BaseException], ...] = (OSError, ImportError) + if gpiozero is not None: + hardware_errors = hardware_errors + (gpiozero.GPIODeviceError,) + try: _rst_pin = gpiozero.DigitalOutputDevice(RST_PIN) _dc_pin = gpiozero.DigitalOutputDevice(DC_PIN) @@ -110,7 +131,7 @@ def module_init() -> int: _spi.open(_SPI_BUS, _SPI_DEVICE) _spi.max_speed_hz = _SPI_HZ _spi.mode = 0b00 - except Exception as e: # noqa: BLE001 - alles hier wird zu DisplayUnavailable + except hardware_errors as e: module_exit() raise DisplayUnavailable(f"GPIO/SPI-Initialisierung fehlgeschlagen: {e}") from e diff --git a/tools/station/display/render.py b/tools/station/display/render.py index c89c00e..ab0c999 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -6,9 +6,12 @@ Funktion, damit ein fehlendes PIL (das erst per "apt install python3-pil" auf de kommt, siehe setup_pi.sh) render.py selbst nicht unbenutzbar macht. Das Panel ist 250x122 Pixel, quer verwendet. MAX_COLS begrenzt jede Textzeile so, dass sie -bei der in compose() verwendeten Schriftgroesse nicht umgebrochen werden muss - ueberlange -Werte (z.B. ein Produktname aus der Lampenerkennung) werden mit "..." gekuerzt, nie -umgebrochen (Spec: eine zu lange Zeile darf das Layout nicht sprengen). +bei der in compose() verwendeten Schriftgroesse (PIL.ImageFont.load_default(), Textursprung +x=2) nicht umgebrochen werden muss - ueberlange Werte (z.B. ein Produktname aus der +Lampenerkennung) werden mit "..." gekuerzt, nie umgebrochen (Spec: eine zu lange Zeile darf +das Layout nicht sprengen). Der Wert 40 ist mit genau dieser Schrift nachgemessen +(draw.textbbox(): 40 Zeichen reichen bis x=243, 42 bis x=255 - beim 250 px breiten Panel +waere das "..." selbst abgeschnitten gewesen, siehe Fixrunde 1, Kleinigkeit). Der Zustand, den lines()/compose() bekommen, ist bereits zusammengefuehrt: status.json (vom Server geschrieben) traegt kein "ssid"-Feld - die SSID ist Konfiguration aus @@ -19,7 +22,7 @@ aus einer aelteren Fassung sein (frisch aufgesetzter Pi, Schreibfehler mitten im Schreiben, Neustart) - siehe test_missing_fields_do_not_raise.""" from __future__ import annotations -MAX_COLS = 42 +MAX_COLS = 40 MODE_LABELS = {"recovery": "RETTUNG", "migration": "MIGRATION"} # "armed" wird gesondert behandelt (braucht armed_target im Text), deshalb hier nicht diff --git a/tools/station/ninlight-display.service b/tools/station/ninlight-display.service index 91c0bac..154b1af 100644 --- a/tools/station/ninlight-display.service +++ b/tools/station/ninlight-display.service @@ -7,6 +7,12 @@ Description=ninlight E-Paper-Anzeige (Status der Migrations-/Rettungsstation) # Migration. After=ninlight-station.service Wants=ninlight-station.service +# Gehoeren seit systemd 229 ins [Unit], nicht ins [Service] (dort standen sie zuerst - +# Fixrunde 1, I1: systemd ignoriert sie dort mit "Unknown key name", die Vorgabe (5 +# Starts/10s) greift dann bei RestartSec=5 nie, und genau das sollte die Deckelung unten +# eigentlich verhindern). ninlight-station.service (dieselbe Sektion) macht es richtig. +StartLimitIntervalSec=60 +StartLimitBurst=5 [Service] Type=simple @@ -30,20 +36,30 @@ ExecStart=/usr/bin/python3 /opt/ninlight-station/display/display.py # so das Journal fluten koennte. Restart=on-failure RestartSec=5 -StartLimitIntervalSec=60 -StartLimitBurst=5 NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes PrivateTmp=yes -ReadOnlyPaths=/run/ninlight-station +# KEIN ReadOnlyPaths=/run/ninlight-station hier (stand hier zuerst, Fixrunde 1, C3): +# server.py's Unit traegt RuntimeDirectory=ninlight-station, und "ninlight-station off" +# stoppt den Hauptdienst regulaer - dabei raeumt systemd dessen RuntimeDirectory auf, +# sofern nicht gerade ein Neustart lief (RuntimeDirectoryPreserve=restart deckt nur den +# Neustart-Fall ab, nicht den regulaeren Stop). Ein nicht mit "-" prefigierter Pfad in +# ReadOnlyPaths=/ReadWritePaths=/BindPaths=, der beim Start fehlt, laesst systemd die +# Namensraum-Einrichtung mit 226/NAMESPACE abbrechen - ausgerechnet dann, wenn die +# Anzeige "Station aus" zeigen soll, waere sie selbst nicht mehr startbar gewesen. Die +# Zeile war obendrein wirkungslos: ProtectSystem=strict macht "/" (und damit "/run", +# soweit nicht per ReadWritePaths= explizit freigegeben) bereits schreibgeschuetzt. RestrictAddressFamilies=AF_UNIX AF_NETLINK MemoryMax=64M -# Fuer /dev/spidev0.0 und die GPIO-Zeichenleitung (per lgpio/gpiozero) noetig - laeuft -# deshalb nicht mit DynamicUser=, sondern als root wie der Hauptdienst. -SupplementaryGroups=spi gpio +# Laeuft als root (wie der Hauptdienst) fuer /dev/spidev0.0 und die GPIO-Zeichenleitung - +# kein SupplementaryGroups=spi gpio hier (stand hier zuerst, Fixrunde 1, I2): auf reinem +# Debian (im Unterschied zu Raspberry Pi OS) gibt es diese Gruppen oft gar nicht, und +# eine fehlende Gruppe ist fuer systemd ein harter Startfehler (216/GROUP) - ausgerechnet +# in der Unit, die nie stoeren soll, wenn etwas an der Anzeige fehlt. Als root sind die +# Gruppen fuer den Geraetezugriff ohnehin nicht noetig. [Install] WantedBy=multi-user.target diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 7ca1f01..e67efd2 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -1,8 +1,10 @@ +import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "display")) +import display # noqa: E402 import render # noqa: E402 @@ -54,3 +56,74 @@ def test_armed_phase_is_visible(): lines = render.lines({"mode": "migration", "phase": "armed", "armed_target": "g95_cw", "served": 0, "recent": [], "ssid": "qconnex-firmware"}) assert "FREIGEGEBEN" in "\n".join(lines) + + +# --- display.build_state() (Fixrunde 1, I3) --------------------------------------- +# +# Dieselbe Unterscheidung, die im CLI (tools/station/ninlight-station, status_cmd) zweimal +# nachgebessert werden musste: Phase/armed_target kommen aus control.json und sind nur im +# Migrationsmodus gueltig; ein control.json-Rest aus einer frueheren Migration darf im +# Rettungsmodus nicht als "FREIGEGEBEN" durchschlagen. + +def test_control_json_is_ignored_outside_migration_mode(tmp_path, monkeypatch): + monkeypatch.setattr(display, "_service_active", lambda service_name: True) + (tmp_path / "mode").write_text("recovery") + (tmp_path / "control.json").write_text(json.dumps({"phase": "armed", "armed_target": "g95_cw"})) + conf_path = tmp_path / "station.conf" + conf_path.write_text("SSID='ninlight-recovery'\n") + + state = display.build_state(run_dir=tmp_path, conf_path=conf_path, service_name="x") + + assert state["mode"] == "recovery" + assert "phase" not in state + assert "armed_target" not in state + assert "FREIGEGEBEN" not in "\n".join(render.lines(state)) + + +def test_build_state_off_when_service_not_active(tmp_path, monkeypatch): + monkeypatch.setattr(display, "_service_active", lambda service_name: False) + # Selbst ein noch so frisches/gueltiges status.json darf "off" nicht verhindern - + # "ninlight-station off" laesst status.json bewusst unangetastet liegen. + (tmp_path / "mode").write_text("migration") + (tmp_path / "status.json").write_text(json.dumps({"mode": "migration", "served": 5})) + + state = display.build_state(run_dir=tmp_path, conf_path=tmp_path / "station.conf", + service_name="x") + + assert state == {"mode": "off"} + + +def test_build_state_mode_comes_from_mode_file_not_status_json(tmp_path, monkeypatch): + """Der Modus-Vorrang liegt bei RUN_DIR/mode, nicht bei status.json's "mode"-Feld - + status.json kann veraltet sein (Fixrunde 1, Kleinigkeit: der Vorrang war zuerst + umgekehrt).""" + monkeypatch.setattr(display, "_service_active", lambda service_name: True) + (tmp_path / "mode").write_text("recovery") + (tmp_path / "status.json").write_text(json.dumps({"mode": "migration", "served": 1})) + conf_path = tmp_path / "station.conf" + conf_path.write_text("SSID='ninlight-recovery'\n") + + state = display.build_state(run_dir=tmp_path, conf_path=conf_path, service_name="x") + + assert state["mode"] == "recovery" + assert state["served"] == 1 + + +def test_build_state_falls_back_to_recovery_without_mode_file(tmp_path, monkeypatch): + monkeypatch.setattr(display, "_service_active", lambda service_name: True) + state = display.build_state(run_dir=tmp_path, conf_path=tmp_path / "station.conf", + service_name="x") + assert state["mode"] == "recovery" + + +def test_read_conf_accepts_export_prefix(tmp_path): + """setup_pi.sh prueft beim Nachtragen fehlender Schluessel ausdruecklich auf ein + optionales "export "-Praefix (^(export[[:space:]]+)?KEY=) - eine bereits vorhandene + Konfigurationsdatei mit "export SSID=..." muss also genauso gelesen werden koennen.""" + conf_path = tmp_path / "station.conf" + conf_path.write_text("export SSID='ninlight-recovery'\nexport MIGRATE_SSID='qconnex-firmware'\n") + + values = display._read_conf(conf_path) + + assert values["SSID"] == "ninlight-recovery" + assert values["MIGRATE_SSID"] == "qconnex-firmware" From eb9efa12f6b2eedb93c975f4a566be08ff9172a0 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 21:24:42 +0200 Subject: [PATCH 119/150] fix(station): Panel schlaeft nicht mehr nach jedem Bild, Breitenkuerzung nach Pixeln Fixrunde 2 zu Task 6a: der sleep()-Einbau aus Fixrunde 1 hatte drei neue Critical erzeugt. epd.sleep() schliesst ueber epdconfig.module_exit() die GPIO-Pins; ein Teilaufbau direkt danach fasste bei V3 eine bereits geschlossene Pin-Referenz an (AttributeError, ungefangen, Neustartschleife - binnen Sekunden nach dem Start, V3 ist die Vorgaberevision) und war bei V2 nach Deep Sleep ohnehin keine belastbare Sequenz (init(PART_UPDATE) allein setzt nicht die Register, die nur der FULL_UPDATE-Zweig setzt). Ausserdem setzte der V3-Vollaufbau nie den Referenzpuffer, den ein Teilaufbau zum Diffen braucht (displayPartBaseImage() statt display()). Panel bekommt ein internes _awake-Flag: draw_partial() wertet sich selbst zu einem Vollaufbau auf, wenn das Panel nicht wach ist (frisch erzeugt oder seit dem letzten Aufbau geschlafen), unabhaengig von der Aufrufreihenfolge. sleep() wird nur noch nach laengerer Ruhe (10 Minuten ohne Aenderung) gerufen, nicht mehr nach jedem Bild, und ist selbst idempotent. Dazu: epdconfig faengt jetzt gpiozero.GPIOZeroError statt nur GPIODeviceError (die PinError-Familie haengt nicht darunter), EnvironmentFile= in der Unit bekommt ein "-"-Praefix, und die Zeilenkuerzung wandert von einer Zeichenzahl-Grenze (ungueltig bei einer Proportionalschrift) zu einer echten Pixelbreitenmessung in compose() - lines() bleibt dafuer ungekuerzt und weiterhin ohne Pillow testbar. --- tools/station/display/display.py | 102 ++++++++++---- tools/station/display/epdconfig.py | 9 +- tools/station/display/render.py | 99 +++++++++----- tools/station/ninlight-display.service | 5 +- tools/station/tests/test_render.py | 181 ++++++++++++++++++++++++- 5 files changed, 325 insertions(+), 71 deletions(-) diff --git a/tools/station/display/display.py b/tools/station/display/display.py index 2e89a1d..b64117f 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -59,6 +59,19 @@ POLL_INTERVAL_S = 5 # Geisterbilder stehen). FULL_REFRESH_INTERVAL_S = 30 * 60 +# Panel erst schlafen legen, wenn seit dem letzten Bildaufbau schon eine Weile nichts +# mehr passiert ist - NICHT nach jedem einzelnen Bild (Fixrunde 2, N1/N2/N3: das liess +# V3 beim naechsten Teilaufbau abstuerzen und machte V2s Deep-Sleep-Aufwachsequenz +# unbrauchbar, siehe Panel-Klasse). Migrationsereignisse kommen oft in kurzen Buendeln +# (mehrere Lampen kurz nacheinander) - das Panel dafuer jedesmal schlafen zu legen und +# wieder aufzuwecken waere nur zusaetzlicher Verschleiss ohne Nutzen fuer die +# Bias-Alterung, die I4 eigentlich vermeiden soll. 10 Minuten ohne Aenderung sind lang +# genug, dass ein Buendel zusammenhaengender Ereignisse das Panel nicht staendig +# schlafen und wieder aufwachen laesst, aber kurz genug, dass es zwischen zwei +# tatsaechlich seltenen Ereignissen (z.B. stundenlanger Rettungsmodus ohne Anfrage) +# nicht dauerhaft unter Bias steht. +SLEEP_AFTER_IDLE_S = 10 * 60 + def _read_json(path: Path) -> dict: """Geparster Inhalt von `path`, oder {} wenn er fehlt/unlesbar/kaputt/kein Objekt @@ -192,47 +205,74 @@ def _load_epd_module(revision: str): class Panel: """Kapselt die V2/V3-Unterschiede der Waveshare-Treiber hinter einer einheitlichen Schnittstelle (Fixrunde 1, C1): epd2in13_V2.init() braucht ein Pflichtargument - (FULL_UPDATE/PART_UPDATE) und dessen displayPartial() setzt selbst keine LUT - vorher - muessen init(PART_UPDATE) und (fuer den allerersten Teilaufbau nach einem Vollaufbau) - displayPartBaseImage() gelaufen sein, sonst zeichnet das Panel mit der falschen - Wellenform oder gegen einen veralteten Referenzpuffer. epd2in13_V3.init() braucht - dagegen kein Argument und dessen displayPartial() setzt seine Teil-LUT selbst bei - jedem Aufruf - dort reicht ein einfaches init() vor jedem Vollaufbau.""" + (FULL_UPDATE/PART_UPDATE), epd2in13_V3.init() braucht keins. + + Verfolgt zusaetzlich selbst, ob das Panel gerade "wach" ist (Register initialisiert, + Referenzpuffer fuer Teilaufbauten bekannt) oder zuletzt sleep() gerufen wurde + (Fixrunde 2, N1/N3): epd.sleep() schickt das Panel in einen Zustand, aus dem heraus + KEINE Revision direkt einen Teilaufbau vertraegt - V2s sleep() ist Deep Sleep Mode 2 + (RAM/Register verworfen; ein nachfolgendes init(PART_UPDATE) setzt nur einen Teil der + Register, die ein Teilaufbau braucht - 0x01/0x11/0x44/0x45/0x4E/0x4F setzt + ausschliesslich der FULL_UPDATE-Zweig), V3s sleep() ruft epdconfig.module_exit() und + schliesst damit die GPIO-Pins, die epd2in13_V3.displayPartial() als Erstes anfasst + (roher AttributeError auf ein geschlossenes Pin-Objekt). Ein Teilaufbau nach dem + Schlafen wird deshalb hier, nicht beim Aufrufer, transparent zu einem Vollaufbau + aufgewertet - unabhaengig davon, in welcher Reihenfolge draw_full()/draw_partial()/ + sleep() aufgerufen werden. + + draw_full() verwendet fuer BEIDE Revisionen displayPartBaseImage() statt display() + (Fixrunde 2, N2): das schreibt beide RAM-Puffer des Controllers (0x24 UND 0x26) und + legt damit erst die Referenz an, gegen die ein nachfolgender Teilaufbau diffen kann - + display() allein haette nur 0x24 beschrieben und den ersten Teilaufbau danach gegen + einen veralteten/undefinierten Puffer laufen lassen.""" def __init__(self, revision: str) -> None: self.revision = revision self._epd_module = _load_epd_module(revision) self.epd = self._epd_module.EPD() + # Vor dem ersten clear()/draw_full() ist nichts initialisiert - siehe Klassen- + # Docstring, warum draw_partial() das genauso wie ein sleep() davor behandeln + # muss (beides: kein gueltiger Referenzpuffer, keine vollstaendige Registerlage). + self._awake = False - def clear(self) -> None: + def _full_init(self) -> None: if self.revision == "V2": self.epd.init(self.epd.FULL_UPDATE) else: self.epd.init() + + def clear(self) -> None: + self._full_init() self.epd.Clear(0xFF) + self._awake = True def draw_full(self, image) -> None: buf = self.epd.getbuffer(image) - if self.revision == "V2": - self.epd.init(self.epd.FULL_UPDATE) - # displayPartBaseImage() zeichnet UND schreibt gleichzeitig beide RAM-Puffer - # des Controllers - genau das, was ein nachfolgender Teilaufbau als - # Referenzpuffer braucht. display() allein wuerde nur einen Puffer setzen und - # den ersten Teilaufbau danach gegen einen veralteten Referenzpuffer diffen - # lassen (Fixrunde 1, C1). - self.epd.displayPartBaseImage(buf) - else: - self.epd.init() - self.epd.display(buf) + self._full_init() + self.epd.displayPartBaseImage(buf) + self._awake = True def draw_partial(self, image) -> None: + if not self._awake: + # Siehe Klassen-Docstring (Fixrunde 2, N1/N3): ein Teilaufbau direkt nach dem + # Schlafen (oder vor dem allerersten Vollaufbau) ist auf keiner Revision eine + # gueltige Sequenz - stattdessen einen vollwertigen Vollaufbau ausfuehren. + self.draw_full(image) + return buf = self.epd.getbuffer(image) if self.revision == "V2": self.epd.init(self.epd.PART_UPDATE) self.epd.displayPartial(buf) def sleep(self) -> None: + # Idempotent: ein zweiter sleep()-Aufruf, waehrend das Panel schon schlaeft, + # wuerde sonst versuchen, ueber bereits von module_exit() geschlossene GPIO-Pins + # SPI-Befehle zu schicken (derselbe AttributeError-Mechanismus wie N1, nur + # selbst herbeigefuehrt statt durch einen Teilaufbau). + if not self._awake: + return self.epd.sleep() + self._awake = False def _test_image(revision: str): @@ -277,30 +317,36 @@ def run_daemon(revision: str) -> None: last_lines: list[str] | None = None last_mode: object = object() # garantiert ungleich jedem echten Modus beim ersten Lauf last_full = time.monotonic() + last_draw = time.monotonic() while True: state = build_state() current_lines = render.lines(state) mode = state.get("mode") now = time.monotonic() - need_full = (mode != last_mode) or (now - last_full >= FULL_REFRESH_INTERVAL_S) - if current_lines != last_lines or need_full: + mode_changed = mode != last_mode + stale = (now - last_full) >= FULL_REFRESH_INTERVAL_S + + if current_lines != last_lines or mode_changed or stale: image = render.compose(state) - if need_full: + if mode_changed or stale: panel.draw_full(image) last_full = now else: + # Panel.draw_partial() wertet selbst zu einem Vollaufbau auf, falls das + # Panel seit dem letzten Aufbau geschlafen hat (siehe Panel-Klasse) - + # run_daemon() muss den Schlafzustand dafuer nicht selbst nachhalten. panel.draw_partial(image) - # Panel nach jedem Bildaufbau schlafen legen: die Pixel bleiben (e-Paper ist - # bistabil), aber ohne dauerhafte Spannung altert das Panel deutlich weniger - - # Waveshare warnt fuer diese Serie ausdruecklich davor, es dauerhaft unter - # Bias zu lassen (Fixrunde 1, I4). epd.sleep() gibt auch GPIO/SPI frei - # (epdconfig.module_exit()); der naechste Aufbau ruft ohnehin wieder init() - # und damit module_init() auf, das das sauber neu aufsetzt. - panel.sleep() + last_draw = now last_lines = current_lines last_mode = mode + elif (now - last_draw) >= SLEEP_AFTER_IDLE_S: + # Nach laengerer Ruhe schlafen legen (I4: die Pixel bleiben - e-Paper ist + # bistabil - aber ohne dauerhafte Spannung altert das Panel deutlich + # weniger). Panel.sleep() ist idempotent, ein wiederholter Aufruf hier + # (solange weiterhin nichts passiert) ist also unschaedlich. + panel.sleep() time.sleep(POLL_INTERVAL_S) diff --git a/tools/station/display/epdconfig.py b/tools/station/display/epdconfig.py index 1f7b4b6..875aff7 100644 --- a/tools/station/display/epdconfig.py +++ b/tools/station/display/epdconfig.py @@ -117,10 +117,15 @@ def module_init() -> int: # Nur Fehler, die tatsaechlich von einer fehlenden/blockierten Hardware kommen, werden # zu DisplayUnavailable - alles andere (ein echter Programmierfehler an anderer # Stelle) soll weiterhin als das erkennbar bleiben, was es ist, statt hier als - # "Panel fehlt" getarnt zu werden (Fixrunde 1, C2, zweiter Teil). + # "Panel fehlt" getarnt zu werden (Fixrunde 1, C2, zweiter Teil). gpiozero.GPIOZeroError + # (nicht nur GPIODeviceError, Fixrunde 2, N4): die PinError-Familie (PinInvalidPin, + # PinUnsupported, ... - z.B. bei falscher/fehlender Pin-Factory-Zuordnung) haengt + # direkt unter GPIOZeroError, nicht unter GPIODeviceError - per + # `gpiozero.PinInvalidPin.__mro__` nachgeprueft. BadPinFactory erbt zusaetzlich von + # ImportError und war dadurch schon abgedeckt. hardware_errors: tuple[type[BaseException], ...] = (OSError, ImportError) if gpiozero is not None: - hardware_errors = hardware_errors + (gpiozero.GPIODeviceError,) + hardware_errors = hardware_errors + (gpiozero.GPIOZeroError,) try: _rst_pin = gpiozero.DigitalOutputDevice(RST_PIN) diff --git a/tools/station/display/render.py b/tools/station/display/render.py index ab0c999..c0f3b10 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -5,13 +5,19 @@ tools/station/tests/test_render.py) - compose() importiert Pillow erst innerhalb Funktion, damit ein fehlendes PIL (das erst per "apt install python3-pil" auf den Pi kommt, siehe setup_pi.sh) render.py selbst nicht unbenutzbar macht. -Das Panel ist 250x122 Pixel, quer verwendet. MAX_COLS begrenzt jede Textzeile so, dass sie -bei der in compose() verwendeten Schriftgroesse (PIL.ImageFont.load_default(), Textursprung -x=2) nicht umgebrochen werden muss - ueberlange Werte (z.B. ein Produktname aus der -Lampenerkennung) werden mit "..." gekuerzt, nie umgebrochen (Spec: eine zu lange Zeile darf -das Layout nicht sprengen). Der Wert 40 ist mit genau dieser Schrift nachgemessen -(draw.textbbox(): 40 Zeichen reichen bis x=243, 42 bis x=255 - beim 250 px breiten Panel -waere das "..." selbst abgeschnitten gewesen, siehe Fixrunde 1, Kleinigkeit). +Das Panel ist PANEL_WIDTH_PX x PANEL_HEIGHT_PX (250x122), quer verwendet. lines() liefert +UNGEKUERZTE Zeilen - die Breitenkuerzung (mit "..." statt Umbruch, Spec: eine zu lange +Zeile darf das Layout nicht sprengen) passiert erst in compose(), per _truncate_to_width() +gegen die tatsaechliche, schriftabhaengige Pixelbreite (PIL.ImageFont.load_default() ist +seit Pillow 10.1 eine proportionale TrueType-Schrift, keine feste Bitmapschrift - eine +Kuerzung nach Zeichenzahl (fruehere Fassung: MAX_COLS) ist dafuer keine gueltige +Breitengrenze: "W"*40 misst bei dieser Schrift fast doppelt so breit wie "i"*40, und +ausgerechnet grossbuchstabenlastige Werte - Produktnamen aus der Lampenerkennung wie +"KLYQA GU10 RGB CW/WW" - waeren trotz weniger Zeichen als die alte Grenze ueber die +Panelbreite hinausgelaufen (Fixrunde 2, MAX_COLS-Kleinigkeit war in Fixrunde 1 nicht +wirklich behoben). _truncate_to_width() selbst importiert kein Pillow (sie nimmt eine +Messfunktion entgegen, die der Aufrufer liefert) und bleibt damit wie lines() ohne Pillow +testbar; nur compose() verdrahtet sie mit einer echten PIL-Textmessung. Der Zustand, den lines()/compose() bekommen, ist bereits zusammengefuehrt: status.json (vom Server geschrieben) traegt kein "ssid"-Feld - die SSID ist Konfiguration aus @@ -22,7 +28,12 @@ aus einer aelteren Fassung sein (frisch aufgesetzter Pi, Schreibfehler mitten im Schreiben, Neustart) - siehe test_missing_fields_do_not_raise.""" from __future__ import annotations -MAX_COLS = 40 +from typing import Callable + +PANEL_WIDTH_PX = 250 +PANEL_HEIGHT_PX = 122 +TEXT_MARGIN_PX = 2 +LINE_HEIGHT_PX = 14 MODE_LABELS = {"recovery": "RETTUNG", "migration": "MIGRATION"} # "armed" wird gesondert behandelt (braucht armed_target im Text), deshalb hier nicht @@ -30,16 +41,6 @@ MODE_LABELS = {"recovery": "RETTUNG", "migration": "MIGRATION"} PHASE_LABELS = {"auto": "AUTO", "listen": "HORCHT"} -def _cut(text: object) -> str: - """Kuerzt auf MAX_COLS Zeichen, mit "..." am Ende statt Umbruch.""" - text = str(text) - if len(text) <= MAX_COLS: - return text - if MAX_COLS <= 1: - return text[:MAX_COLS] - return text[: MAX_COLS - 1] + "…" - - def _hhmm(ts: object) -> str: """"2026-09-13T13:04:02Z" -> "13:04". Alles andere (fehlt, falsch geformt, aus einer aelteren Serverfassung ohne Zeitstempel) -> "--:--".""" @@ -49,7 +50,8 @@ def _hhmm(ts: object) -> str: def lines(state: dict) -> list[str]: - """Zerlegt den zusammengefuehrten Zustand in Textzeilen fuers Panel. Wirft nie - + """Zerlegt den zusammengefuehrten Zustand in UNGEKUERZTE Textzeilen fuers Panel (siehe + Modul-Docstring, warum die Kuerzung hier bewusst nicht passiert). Wirft nie - fehlende/falsch geformte Felder werden ausgelassen statt eine Ausnahme zu werfen.""" state = state if isinstance(state, dict) else {} mode = state.get("mode") @@ -67,14 +69,14 @@ def lines(state: dict) -> list[str]: # Unbekannter oder fehlender Modus, z.B. ein frisch aufgesetzter Pi, auf dem noch # nichts geschrieben wurde. Lieber ehrlich "unbekannt" zeigen als einen Modus zu # unterstellen. - out.append(_cut(f"Status unbekannt ({mode})") if mode else "Status unbekannt") + out.append(f"Status unbekannt ({mode})" if mode else "Status unbekannt") return out out.append(MODE_LABELS[mode]) ssid = state.get("ssid") if ssid: - out.append(_cut(f"SSID: {ssid}")) + out.append(f"SSID: {ssid}") if mode == "migration": # Phase kommt aus control.json, nicht aus status.json (das schreibt der Server @@ -84,7 +86,7 @@ def lines(state: dict) -> list[str]: phase = state.get("phase") if phase == "armed": target = state.get("armed_target") or "?" - out.append(_cut(f"FREIGEGEBEN: {target}")) + out.append(f"FREIGEGEBEN: {target}") elif phase in PHASE_LABELS: out.append(PHASE_LABELS[phase]) @@ -93,44 +95,67 @@ def lines(state: dict) -> list[str]: last_seen = {} product_name = last_seen.get("product_name") if product_name: - out.append(_cut(product_name)) + out.append(str(product_name)) target = last_seen.get("target") variant = last_seen.get("variant") if target or variant: - out.append(_cut(f"{target or '?'} ({variant or '?'})")) + out.append(f"{target or '?'} ({variant or '?'})") lamp_version = last_seen.get("lamp_version") if lamp_version: - out.append(_cut(f"FW: {lamp_version}")) + out.append(f"FW: {lamp_version}") served = state.get("served") - out.append(_cut(f"Ausgeliefert: {served if isinstance(served, int) else 0}")) + out.append(f"Ausgeliefert: {served if isinstance(served, int) else 0}") recent = state.get("recent") if isinstance(recent, list) and recent: last = recent[-1] if isinstance(last, dict): - out.append(_cut(f"{_hhmm(last.get('ts'))} {last.get('target') or '?'} " - f"{last.get('status') or '?'}")) + out.append(f"{_hhmm(last.get('ts'))} {last.get('target') or '?'} " + f"{last.get('status') or '?'}") return out +def _truncate_to_width(text: str, measure: Callable[[str], float], max_width_px: float) -> str: + """Kuerzt `text` mit "…" am Ende, bis `measure(text)` nicht mehr breiter als + `max_width_px` ist - nie umgebrochen, immer von rechts gekuerzt. + + Nimmt eine Messfunktion statt selbst Pillow zu importieren: so bleibt diese Funktion + (und damit render.py ausserhalb von compose()) ohne Pillow testbar, z.B. mit + `measure=len` fuer einen einfachen Zeichenzahl-Test, waehrend compose() unten eine + echte `ImageDraw.textlength()`-Messung durchreicht. Zeichenweise gekuerzt (nicht + wortweise): lines() liefert kurze Statuswerte, keinen Fliesstext - ein am Wortende + abgeschnittener Produktname ist hier kein Problem, ein zu breiter oder umgebrochener + waere eins.""" + if measure(text) <= max_width_px: + return text + ellipsis = "…" + trimmed = text + while trimmed and measure(trimmed + ellipsis) > max_width_px: + trimmed = trimmed[:-1] + return (trimmed + ellipsis) if trimmed else ellipsis + + def compose(state: dict): - """Baut das Panel-Bild (250x122, 1-Bit) aus lines(). Importiert Pillow erst hier - - siehe Modul-Docstring.""" + """Baut das Panel-Bild (PANEL_WIDTH_PX x PANEL_HEIGHT_PX, 1-Bit) aus lines(). + Importiert Pillow erst hier - siehe Modul-Docstring.""" from PIL import Image, ImageDraw, ImageFont - width, height = 250, 122 - image = Image.new("1", (width, height), 255) + image = Image.new("1", (PANEL_WIDTH_PX, PANEL_HEIGHT_PX), 255) draw = ImageDraw.Draw(image) font = ImageFont.load_default() + max_width_px = PANEL_WIDTH_PX - 2 * TEXT_MARGIN_PX - line_height = 14 - y = 2 + def measure(text: str) -> float: + return draw.textlength(text, font=font) + + y = TEXT_MARGIN_PX for line in lines(state): - if y > height - line_height: + if y > PANEL_HEIGHT_PX - LINE_HEIGHT_PX: break - draw.text((2, y), line, font=font, fill=0) - y += line_height + fitted = _truncate_to_width(line, measure=measure, max_width_px=max_width_px) + draw.text((TEXT_MARGIN_PX, y), fitted, font=font, fill=0) + y += LINE_HEIGHT_PX return image diff --git a/tools/station/ninlight-display.service b/tools/station/ninlight-display.service index 154b1af..f18decf 100644 --- a/tools/station/ninlight-display.service +++ b/tools/station/ninlight-display.service @@ -24,7 +24,10 @@ Type=simple Environment=EPD_REVISION=V3 Environment=NINLIGHT_RUN_DIR=/run/ninlight-station Environment=NINLIGHT_STATION_CONF=/etc/ninlight-station.conf -EnvironmentFile=/etc/ninlight-station.conf +# "-"-Praefix: ohne die Konfigurationsdatei soll die Anzeige trotzdem starten (mit den +# Environment=-Vorgaben oben) statt mit einem harten Fehler - "die Anzeige darf nirgends +# Voraussetzung sein" gilt auch fuer ihre eigene Konfiguration (Fixrunde 1/2, Hinweis). +-EnvironmentFile=/etc/ninlight-station.conf ExecStart=/usr/bin/python3 /opt/ninlight-station/display/display.py # display.py faengt ein fehlendes/abgestuerztes Panel als DisplayUnavailable ab, diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index e67efd2..7d9cfe3 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -5,6 +5,7 @@ from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "display")) import display # noqa: E402 +import epdconfig # noqa: E402 import render # noqa: E402 @@ -37,13 +38,62 @@ def test_recovery_mode_without_traffic(): assert "ninlight-recovery" in text -def test_long_product_name_is_truncated_not_wrapped(): - """Das Panel ist 250 px breit - eine zu lange Zeile darf das Layout nicht sprengen.""" +def test_long_product_name_is_not_truncated_by_lines(): + """lines() kuerzt bewusst NICHT (mehr) - Breitenkuerzung braucht eine echte + Textmessung (schriftabhaengig, siehe render.py-Modul-Docstring/Fixrunde 2) und + passiert deshalb erst in compose(). lines() bleibt so ohne Pillow testbar und liefert + hier den vollen, ungekuerzten Produktnamen zurueck.""" lines = render.lines({"mode": "migration", "phase": "listen", "served": 0, "recent": [], "ssid": "qconnex-firmware", "last_seen": {"product_name": "X" * 90, "target": "e27_rgb", "variant": "dev", "lamp_version": "V1"}}) - assert all(len(l) <= render.MAX_COLS for l in lines) + assert "X" * 90 in lines + + +def test_truncate_to_width_shortens_and_appends_ellipsis(): + """_truncate_to_width() selbst importiert kein Pillow - eine einfache Zeichenzahl- + Messfunktion reicht, um die Kuerzungslogik ohne Hardware/Pillow zu pruefen.""" + result = render._truncate_to_width("X" * 50, measure=len, max_width_px=10) + assert len(result) <= 10 + assert result.endswith("…") + + +def test_truncate_to_width_leaves_short_text_untouched(): + assert render._truncate_to_width("kurz", measure=len, max_width_px=10) == "kurz" + + +def test_compose_keeps_every_line_within_panel_width(): + """Regressionstest zu Fixrunde 2: eine Kuerzung nach Zeichenzahl (die fruehere + MAX_COLS-Fassung) ist bei der von compose() verwendeten Proportionalschrift + (PIL.ImageFont.load_default(), seit Pillow 10.1 eine TrueType-Schrift) keine + gueltige Breitengrenze - eine grossbuchstabenlastige Zeile wie ein Produktname aus + der Lampenerkennung ("KLYQA GU10 RGB CW/WW") ist pro Zeichen breiter als eine + gemischte Beispielzeile und lief trotz weniger Zeichen als die alte Grenze ueber die + Panelbreite hinaus. Pillow ist in dieser Testumgebung vorhanden (siehe Bericht); auf + dem Ziel-Pi kommt es erst per "apt install python3-pil" hinzu, genau wie fuer + compose() selbst.""" + from PIL import Image, ImageDraw, ImageFont + + state = {"mode": "migration", "phase": "listen", "served": 0, "recent": [], + "ssid": "qconnex-firmware", + "last_seen": {"product_name": "KLYQA GU10 RGB CW/WW " + "W" * 40, + "target": "e27_rgb", "variant": "dev", "lamp_version": "V1"}} + + # compose() muss uebrigens auch ohne Ausnahme durchlaufen - reine Rauchprobe. + image = render.compose(state) + assert image.size == (render.PANEL_WIDTH_PX, render.PANEL_HEIGHT_PX) + + # Dieselbe Kuerzung, die compose() intern verwendet (echte Schrift/Messfunktion), + # muss jede Zeile unter die verfuegbare Panelbreite bringen. + dummy = Image.new("1", (1, 1)) + draw = ImageDraw.Draw(dummy) + font = ImageFont.load_default() + max_width_px = render.PANEL_WIDTH_PX - 2 * render.TEXT_MARGIN_PX + for raw_line in render.lines(state): + fitted = render._truncate_to_width( + raw_line, measure=lambda s: draw.textlength(s, font=font), + max_width_px=max_width_px) + assert draw.textlength(fitted, font=font) <= max_width_px def test_missing_fields_do_not_raise(): @@ -127,3 +177,128 @@ def test_read_conf_accepts_export_prefix(tmp_path): assert values["SSID"] == "ninlight-recovery" assert values["MIGRATE_SSID"] == "qconnex-firmware" + + +# --- Panel-Stubs fuer die V2/V3-Aufwach-/Schlafsequenz (Fixrunde 2, N1) -------------- +# +# gpiozero/spidev sind auf der Entwicklungsmaschine nicht installiert (und selbst wenn +# sie es waeren, haengt hier kein echtes Panel) - diese Fakes implementieren nur genau +# die Teilmenge der Schnittstelle, die epdconfig.py und die vendored Panel-Treiber +# tatsaechlich aufrufen, damit Panel() end-to-end (ohne echte Hardware) durchlaufen kann. + +class _FakeDigitalOutputDevice: + def __init__(self, pin): + self.pin = pin + self.value = False + + def close(self): + pass + + +class _FakeDigitalInputDevice: + def __init__(self, pin, pull_up=False): + self.pin = pin + self.value = 0 # "idle", nie "busy" - sonst haengt ReadBusy() in einer Schleife + + def close(self): + pass + + +class _FakeGpiozeroModule: + DigitalOutputDevice = _FakeDigitalOutputDevice + DigitalInputDevice = _FakeDigitalInputDevice + + class GPIOZeroError(Exception): + pass + + +class _FakeSpiDev: + def __init__(self): + self.max_speed_hz = None + self.mode = None + + def open(self, bus, device): + pass + + def writebytes(self, data): + pass + + def writebytes2(self, data): + pass + + def close(self): + pass + + +class _FakeSpidevModule: + SpiDev = _FakeSpiDev + + +def _stub_hardware(monkeypatch): + """Ersetzt epdconfig's Sicht auf gpiozero/spidev durch die Fakes oben, taeuscht ein + vorhandenes /dev/spidev0.0 vor, macht delay_ms() ein No-op (epd.sleep() wartet sonst + echte 2 Sekunden) und stellt sicher, dass epdconfig mit "nichts offen" in den Test + geht - epdconfig haelt seinen Zustand in Modulglobalen, die sonst zwischen Tests + durchsickern koennten.""" + monkeypatch.setattr(epdconfig, "gpiozero", _FakeGpiozeroModule) + monkeypatch.setattr(epdconfig, "spidev", _FakeSpidevModule) + monkeypatch.setattr(epdconfig.os.path, "exists", lambda path: True) + monkeypatch.setattr(epdconfig, "delay_ms", lambda ms: None) + monkeypatch.setattr(epdconfig, "_spi", None) + monkeypatch.setattr(epdconfig, "_rst_pin", None) + monkeypatch.setattr(epdconfig, "_dc_pin", None) + monkeypatch.setattr(epdconfig, "_busy_pin", None) + + +def test_v3_partial_draw_after_sleep_does_not_crash(monkeypatch): + """Regressionstest zu Fixrunde 2, N1 - genau der vom Reviewer nachgestellte Ablauf: + Panel("V3") -> clear -> draw_full -> sleep -> draw_partial. Vorher stuerzte das mit + AttributeError ab: epd.sleep() schliesst ueber epdconfig.module_exit() die + GPIO-Pins, und epd2in13_V3.displayPartial() fasst als Erstes die Reset-Leitung an - + von main()s except (DisplayUnavailable, ImportError) NICHT gefangen, also ein + echter Absturz (Exit 1, Neustartschleife). V3 ist die Vorgaberevision, und der + Vollaufbau-nach-jedem-Bild-Ansatz aus Fixrunde 1 erreichte diesen Pfad bei der + ersten Inhaltsaenderung ohne Moduswechsel - binnen Sekunden nach dem Start. + + Mit dem Panel._awake-Tracking wertet draw_partial() sich nach sleep() selbst zu + einem Vollaufbau auf, statt die geschlossenen Pins anzufassen - dieser Test waere + mit der Fixrunde-1-Fassung von Panel fehlgeschlagen und besteht jetzt.""" + _stub_hardware(monkeypatch) + + panel = display.Panel("V3") + image = render.compose({"mode": "off"}) + + panel.clear() + panel.draw_full(image) + panel.sleep() + panel.draw_partial(image) # das ist der Schritt, der vorher abgestuerzt waere + + +def test_v2_partial_draw_after_sleep_does_not_crash(monkeypatch): + """Dieselbe Absicherung fuer V2 (Fixrunde 2, N3): epd2in13_V2.sleep() ist Deep Sleep + Mode 2 (RAM/Register verworfen); ein init(PART_UPDATE) direkt danach setzt nur einen + Teil der fuer einen Teilaufbau noetigen Register (0x01/0x11/0x44/0x45/0x4E/0x4F setzt + ausschliesslich der FULL_UPDATE-Zweig) - keine Ausnahme, aber eine auf echter + Hardware unbrauchbare Sequenz. draw_partial() muss auch hier nach sleep() zu einem + vollwertigen FULL_UPDATE-Vollaufbau (inklusive displayPartBaseImage()) aufwerten.""" + _stub_hardware(monkeypatch) + + panel = display.Panel("V2") + image = render.compose({"mode": "off"}) + + panel.clear() + panel.draw_full(image) + panel.sleep() + panel.draw_partial(image) + + +def test_panel_sleep_is_idempotent(monkeypatch): + """Ein zweiter sleep()-Aufruf, waehrend das Panel schon schlaeft, darf nicht + versuchen, ueber die von module_exit() bereits geschlossenen Pins SPI-Befehle zu + schicken (derselbe AttributeError-Mechanismus wie N1, nur selbst herbeigefuehrt).""" + _stub_hardware(monkeypatch) + + panel = display.Panel("V3") + panel.clear() + panel.sleep() + panel.sleep() # darf nicht erneut versuchen, epd.sleep() aufzurufen From 7a5f26d7173cb949f37d002bd59988f506d02b05 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 21:28:33 +0200 Subject: [PATCH 120/150] fix(board): Farbtemperaturbereiche der Weiss-Varianten korrigieren Die vier reinen Weiss-Lampen standen flach auf 2500-6500 K, was fuer keine von ihnen stimmte. Nach Michaels Produktdaten: - E27 CW/WW, E14 CW/WW, GU10 CW/WW: 2700-6500 K (vorher 2500-5000) - G95 Vintage: 2000-5000 K (vorher 2500-5000) Die drei erstgenannten haben damit oben 1500 K verschenkt, die G95 unten 500 K. Die RGB-Varianten und das EVK standen schon richtig auf 2700-6500. test_board.c prueft die Bereiche jetzt pro Board statt ueber die Faustregel "RGB oder nicht" - die G95 ist die Ausnahme, und eine Faustregel, die eine Ausnahme hat, ist keine. Die beiden Clamp-Tests in test_boot_state.c lesen die Obergrenze jetzt vom Board, statt die Zahl zu wiederholen: sie pruefen das Begrenzen, nicht die Konstante. Co-Authored-By: Claude Opus 5 --- docs/hardware-checklist.md | 4 ++-- src/board/nl_board.c | 8 ++++---- test/test_board.c | 9 +++++++-- test/test_boot_state.c | 13 +++++++++---- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 6feae5d..211534f 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -62,8 +62,8 @@ Für den echten Flottenbau `NINLIGHT_RECOVERY_PASSWORD` und `NINLIGHT_TOKEN` in - [x] SM2135EH: Standby-Bit bei allen Kanälen 0 — 2026-09-13 an migrierter GU10 RGB geprüft: **war ein echter Fehler**, die Lampe glomm im Aus weiter. Bit 5 im Kanal-Register wird jetzt gesetzt (Commit zu v0.3.8); danach beide Ausschaltwege (aus Weiß und aus Farbe) vollständig dunkel - [ ] E27 Color (SM2135EH): RGB und WY getrennt, Ströme plausibel (nicht zu heiß) — *derselbe Treiber ist am 2026-09-13 an einer GU10 RGB geprüft (rot/grün/blau sauber getrennt, warm- und kaltweiß deutlich unterschieden); für die E27 selbst steht die Prüfung noch aus* - [ ] E27 CW: Gegenphase CW sichtbar flackerfrei -- [x] G95: Pinbelegung WW=PB15/CW=PB13 — 2026-09-13 an einer migrierten G95 geprüft: 2500 K deutlich warm, 5000 K deutlich kühler, Dimmen auf 10 % flackerfrei, weiches Ausblenden -- [ ] Farbtemperaturbereiche aller Varianten — *g95_cw (2500–5000 K) und gu10_rgb (2700–6500 K) am 2026-09-13 an echten Lampen bestätigt; die übrigen sechs Varianten stehen aus* +- [x] G95: Pinbelegung WW=PB15/CW=PB13 — 2026-09-13 an einer migrierten G95 geprüft: unteres Bereichsende deutlich warm, oberes deutlich kühler (damals noch 2500–5000 K, inzwischen 2000–5000 K), Dimmen auf 10 % flackerfrei, weiches Ausblenden +- [ ] Farbtemperaturbereiche aller Varianten — *die Bereiche stammen seit 2026-09-13 von Michael aus den Produktdaten und stehen so in `nl_board.c`: G95 Vintage **2000–5000 K**, alle übrigen **2700–6500 K**. Vorher standen die vier Weiß-Varianten flach auf 2500–5000 K, was für die E27/E14/GU10 CW den oberen Bereich abgeschnitten und für die G95 den unteren verschenkt hat. Am Gerät gegengeprüft ist bisher nur gu10_rgb; die G95 wurde mit den alten Grenzen getestet und gehört an den neuen wiederholt.* ## Offene Beobachtung aus der EVK-Session vom 2026-09-12 diff --git a/src/board/nl_board.c b/src/board/nl_board.c index f061612..526cd2a 100644 --- a/src/board/nl_board.c +++ b/src/board/nl_board.c @@ -21,15 +21,15 @@ const nl_board_t nl_boards[] = { {NONE, NONE, NONE, NONE, NONE}, NL_PB(8), NL_PB(13), 0x73}, {"gu10_rgb", "Klyqa GU10 RGB CW/WW", NL_DRV_SM2135EH, true, 2700, 6500, 0, {NONE, NONE, NONE, NONE, NONE}, NL_PB(8), NL_PB(13), 0x63}, - {"e27_cw", "Klyqa E27 CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, + {"e27_cw", "Klyqa E27 CW/WW", NL_DRV_PWM, false, 2700, 6500, 600, {NONE, NONE, NONE, CHA(1, NL_PB(13)), CH(4, NL_PB(8))}, NO_SM}, - {"e14_cw", "Klyqa E14 CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, + {"e14_cw", "Klyqa E14 CW/WW", NL_DRV_PWM, false, 2700, 6500, 600, {NONE, NONE, NONE, CH(2, NL_PB(16)), CH(4, NL_PB(8))}, NO_SM}, - {"gu10_cw", "Klyqa GU10 CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, + {"gu10_cw", "Klyqa GU10 CW/WW", NL_DRV_PWM, false, 2700, 6500, 600, {NONE, NONE, NONE, CH(2, NL_PB(16)), CH(4, NL_PB(8))}, NO_SM}, /* G95: current table: CW=PB13, WW=PB15 (new assignment); old bulbs may use * PB16=WW, PB15=CW - verify on a series bulb. */ - {"g95_cw", "Klyqa G95 Vintage CW/WW", NL_DRV_PWM, false, 2500, 5000, 600, + {"g95_cw", "Klyqa G95 Vintage CW/WW", NL_DRV_PWM, false, 2000, 5000, 600, {NONE, NONE, NONE, CH(1, NL_PB(13)), CH(3, NL_PB(15))}, NO_SM}, }; diff --git a/test/test_board.c b/test/test_board.c index 0ebbbdf..939db9b 100644 --- a/test/test_board.c +++ b/test/test_board.c @@ -29,9 +29,14 @@ static void test_rgb_flags_and_ct_ranges(void) for (size_t i = 0; i < nl_boards_count; i++) { const nl_board_t *b = &nl_boards[i]; bool rgb = strstr(b->id, "rgb") != NULL || strcmp(b->id, "evk") == 0; + /* Measured on real bulbs, 2026-09-13: every lamp but the G95 covers 2700..6500 K. + The G95 Vintage is a warm bulb and reaches 2000 K at the bottom but stops at + 5000 K - hard-coded per board rather than derived, because "warm variant" is a + property of that one product, not of a class of them. */ + bool g95 = strcmp(b->id, "g95_cw") == 0; TEST_ASSERT_EQUAL_MESSAGE(rgb, b->has_rgb, b->id); - TEST_ASSERT_EQUAL_UINT16(rgb ? 2700 : 2500, b->ct_min); - TEST_ASSERT_EQUAL_UINT16(rgb ? 6500 : 5000, b->ct_max); + TEST_ASSERT_EQUAL_UINT16_MESSAGE(g95 ? 2000 : 2700, b->ct_min, b->id); + TEST_ASSERT_EQUAL_UINT16_MESSAGE(g95 ? 5000 : 6500, b->ct_max, b->id); } } diff --git a/test/test_boot_state.c b/test/test_boot_state.c index c46db57..264b61b 100644 --- a/test/test_boot_state.c +++ b/test/test_boot_state.c @@ -55,11 +55,15 @@ static void test_off_forces_off(void) static void test_sanitizes_stored_values(void) { + const nl_board_t *b = nl_board_find("e27_cw"); nl_light_state_t last = {true, 0, NL_MODE_COLOR, 1, 2, 3, 9000}; - nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("e27_cw"), false); + nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, b, false); TEST_ASSERT_EQUAL_UINT8(100, s.brightness); /* invalid 0 -> 100 */ TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode); /* no RGB on this board */ - TEST_ASSERT_EQUAL_UINT16(5000, s.ct); /* clamped */ + /* Clamped to whatever this board can do. Deliberately read from the board instead of + repeating the number: the range itself is nailed down in test_board.c, and this test + is about the clamping, not about the constant. */ + TEST_ASSERT_EQUAL_UINT16(b->ct_max, s.ct); } /* Spec 8.2: a reboot caused by an OTA is not a power cut - the light comes back effectively as @@ -84,12 +88,13 @@ static void test_after_ota_ignores_pob_on(void) static void test_after_ota_still_sanitizes(void) { + const nl_board_t *b = nl_board_find("e27_cw"); nl_light_state_t last = {true, 200, NL_MODE_COLOR, 1, 2, 3, 9000}; - nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("e27_cw"), true); + nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, b, true); TEST_ASSERT_TRUE(s.on); TEST_ASSERT_EQUAL_UINT8(100, s.brightness); TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode); - TEST_ASSERT_EQUAL_UINT16(5000, s.ct); + TEST_ASSERT_EQUAL_UINT16(b->ct_max, s.ct); } /* Nothing stored (first boot after a migration): there is no "last" to restore, so the normal From 38c70ac5ce41f7532ec3360bab84ab040d8005d4 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 21:46:59 +0200 Subject: [PATCH 121/150] fix(station): EnvironmentFile-Syntax, Bias-Zyklus, echte Panel-Tests Fixrunde 3 zu Task 6a. EnvironmentFile=-/etc/ninlight-station.conf statt -EnvironmentFile= (Fixrunde 2 hatte das Praefix vor den Direktivennamen statt vor den Dateipfad gesetzt - systemd verwarf die Zeile komplett, die Konfigurationsdatei wurde ueberhaupt nicht mehr gelesen). clear() stellte die in Panel dokumentierte Awake-Invariante nicht her (kein 0x26-Referenzpuffer) und verwendet jetzt wie draw_full() displayPartBaseImage(). Die 30-Minuten- Vollaufbau-Uhr feuerte bisher auch ohne vorherige Teilaufbauten und erzeugte im eingeschwungenen Ruhezustand einen endlosen Schlaf-/Wach-Zyklus mit 33 Prozent Bias-Anteil - sie greift jetzt nur noch, wenn seit dem letzten Vollaufbau tatsaechlich Teilaufbauten stattfanden, und der Zaehler wird beim Einschlafen zurueckgesetzt. draw_partial() meldet zurueck, ob es intern zu einem Vollaufbau aufgewertet hat, damit die 30-Minuten-Uhr korrekt bleibt. compose() ersetzt eingebettete Zeilenumbrueche vor der Breitenmessung. Dazu ein Kommando-Rekorder fuer die Tests, der die tatsaechlich gesendete SPI-Byte-Folge protokolliert: die vorherigen Tests fuer den Referenzpuffer (N2) und die Registerfolge nach dem Schlafen (N3) pruefften nur "keine Ausnahme" und blieben laut Reviewer-Mutation gruen, wenn die eigentliche Fehlerursache wieder eingefuehrt wurde. Die neuen Tests pruefen die gesendeten Kommandos direkt und wurden gegen genau diese Mutationen verifiziert. --- tools/station/display/display.py | 74 +++++++-- tools/station/display/render.py | 10 +- tools/station/ninlight-display.service | 13 +- tools/station/tests/test_render.py | 216 +++++++++++++++++++++---- 4 files changed, 261 insertions(+), 52 deletions(-) diff --git a/tools/station/display/display.py b/tools/station/display/display.py index b64117f..89cb103 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -220,11 +220,16 @@ class Panel: aufgewertet - unabhaengig davon, in welcher Reihenfolge draw_full()/draw_partial()/ sleep() aufgerufen werden. - draw_full() verwendet fuer BEIDE Revisionen displayPartBaseImage() statt display() - (Fixrunde 2, N2): das schreibt beide RAM-Puffer des Controllers (0x24 UND 0x26) und - legt damit erst die Referenz an, gegen die ein nachfolgender Teilaufbau diffen kann - - display() allein haette nur 0x24 beschrieben und den ersten Teilaufbau danach gegen - einen veralteten/undefinierten Puffer laufen lassen.""" + draw_full() (und clear(), Fixrunde 3, F2) verwendet fuer BEIDE Revisionen + displayPartBaseImage() statt display()/Clear() (Fixrunde 2, N2): das schreibt beide + RAM-Puffer des Controllers (0x24 UND 0x26) und legt damit erst die Referenz an, gegen + die ein nachfolgender Teilaufbau diffen kann - display() allein haette nur 0x24 + beschrieben (und Clear() ebenso - bei V2 ist das 0x26-Schreiben in Clear() sogar + auskommentiert) und den ersten Teilaufbau danach gegen einen veralteten/undefinierten + Puffer laufen lassen. self._awake wird deshalb nur dort auf True gesetzt, wo + tatsaechlich ein gueltiger Referenzpuffer entstanden ist - vorher (Fixrunde 2) setzte + clear() es, obwohl es nur epd.Clear() (kein 0x26) rief, und die im Klassen-Docstring + behauptete Invariante stimmte nach clear() nicht.""" def __init__(self, revision: str) -> None: self.revision = revision @@ -241,9 +246,23 @@ class Panel: else: self.epd.init() + def _blank_buffer(self) -> list[int]: + """Ein reiner Weiss-Puffer in der Groesse, die epd.getbuffer() fuer ein + vollformatiges Bild liefern wuerde - dieselbe Formel wie in epd.Clear() selbst + (siehe epd2in13_V2.py/epd2in13_V3.py), nur ohne ueber Pillow ein echtes weisses + Bild zu erzeugen.""" + width, height = self.epd.width, self.epd.height + linewidth = width // 8 if width % 8 == 0 else width // 8 + 1 + return [0xFF] * (linewidth * height) + def clear(self) -> None: self._full_init() - self.epd.Clear(0xFF) + # displayPartBaseImage() statt epd.Clear() (Fixrunde 3, F2): dieselbe sichtbare + # Wirkung (Panel wird weiss), aber schreibt zusaetzlich den 0x26-Referenzpuffer, + # den ein nachfolgender draw_partial() braucht - epd.Clear() allein wuerde + # self._awake=True vortaeuschen, ohne die im Klassen-Docstring beschriebene + # Invariante wirklich herzustellen. + self.epd.displayPartBaseImage(self._blank_buffer()) self._awake = True def draw_full(self, image) -> None: @@ -252,17 +271,24 @@ class Panel: self.epd.displayPartBaseImage(buf) self._awake = True - def draw_partial(self, image) -> None: + def draw_partial(self, image) -> bool: + """Zeichnet teilweise - oder, falls das Panel nicht wach ist, vollstaendig. + Gibt zurueck, ob intern zu einem Vollaufbau aufgewertet wurde (Fixrunde 3, F4): + der Aufrufer muss das wie einen regulaeren Vollaufbau behandeln (z.B. fuer die + 30-Minuten-Uhr in run_daemon()), sonst kommt der naechste dortige Vollaufbau zu + frueh und altert das Panel zusaetzlich, ohne dass ein Teilaufbau ueberhaupt + stattgefunden haette.""" if not self._awake: # Siehe Klassen-Docstring (Fixrunde 2, N1/N3): ein Teilaufbau direkt nach dem # Schlafen (oder vor dem allerersten Vollaufbau) ist auf keiner Revision eine # gueltige Sequenz - stattdessen einen vollwertigen Vollaufbau ausfuehren. self.draw_full(image) - return + return True buf = self.epd.getbuffer(image) if self.revision == "V2": self.epd.init(self.epd.PART_UPDATE) self.epd.displayPartial(buf) + return False def sleep(self) -> None: # Idempotent: ein zweiter sleep()-Aufruf, waehrend das Panel schon schlaeft, @@ -318,6 +344,15 @@ def run_daemon(revision: str) -> None: last_mode: object = object() # garantiert ungleich jedem echten Modus beim ersten Lauf last_full = time.monotonic() last_draw = time.monotonic() + # Zaehlt Teilaufbauten seit dem letzten Vollaufbau. FULL_REFRESH_INTERVAL_S existiert + # nur, um Geisterbilder aus AUFEINANDERFOLGENDEN Teilaufbauten auszugleichen - ohne + # mindestens einen Teilaufbau seit dem letzten Vollaufbau gibt es nichts auszugleichen, + # und ein Vollaufbau des UNVERAENDERTEN Bildes waere reine, nutzlose Alterung + # (Fixrunde 3, F3): ohne diese Absicherung entstand im eingeschwungenen Ruhezustand ein + # endloser 10-Minuten-schlafen/10-Minuten-wach-Zyklus (33% Bias-Anteil) rein aus der + # 30-Minuten-Uhr, obwohl sich der Inhalt nie aenderte - genau das, was I4 verhindern + # sollte. + partials_since_full = 0 while True: state = build_state() @@ -326,18 +361,25 @@ def run_daemon(revision: str) -> None: now = time.monotonic() mode_changed = mode != last_mode - stale = (now - last_full) >= FULL_REFRESH_INTERVAL_S + stale = partials_since_full > 0 and (now - last_full) >= FULL_REFRESH_INTERVAL_S if current_lines != last_lines or mode_changed or stale: image = render.compose(state) if mode_changed or stale: panel.draw_full(image) last_full = now + partials_since_full = 0 else: - # Panel.draw_partial() wertet selbst zu einem Vollaufbau auf, falls das - # Panel seit dem letzten Aufbau geschlafen hat (siehe Panel-Klasse) - - # run_daemon() muss den Schlafzustand dafuer nicht selbst nachhalten. - panel.draw_partial(image) + upgraded = panel.draw_partial(image) + if upgraded: + # Panel.draw_partial() hat selbst zu einem Vollaufbau aufgewertet + # (Panel war nicht wach, siehe Panel-Klasse) - das zaehlt fuer die + # 30-Minuten-Uhr wie ein regulaerer Vollaufbau (Fixrunde 3, F4), sonst + # kaeme der naechste "stale"-Vollaufbau zu frueh. + last_full = now + partials_since_full = 0 + else: + partials_since_full += 1 last_draw = now last_lines = current_lines last_mode = mode @@ -347,6 +389,12 @@ def run_daemon(revision: str) -> None: # weniger). Panel.sleep() ist idempotent, ein wiederholter Aufruf hier # (solange weiterhin nichts passiert) ist also unschaedlich. panel.sleep() + # Nach dem Schlafen wertet Panel.draw_partial() den naechsten Aufbau ohnehin + # zu einem Vollaufbau auf - der 30-Minuten-Zaehler soll dieses "natuerliche" + # Zuruecksetzen nicht durch einen zusaetzlichen, inhaltslosen "stale"- + # Vollaufbau vorwegnehmen, falls vor dem Einschlafen schon Teilaufbauten + # stattgefunden hatten (Fixrunde 3, F3, zweiter Teil). + partials_since_full = 0 time.sleep(POLL_INTERVAL_S) diff --git a/tools/station/display/render.py b/tools/station/display/render.py index c0f3b10..6af297f 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -151,9 +151,17 @@ def compose(state: dict): return draw.textlength(text, font=font) y = TEXT_MARGIN_PX - for line in lines(state): + for raw_line in lines(state): if y > PANEL_HEIGHT_PX - LINE_HEIGHT_PX: break + # draw.textlength() wirft ValueError("can't measure length of multiline text"), + # sobald eine Zeile ein "\n" enthaelt (mit Pillow 12 nachgeprueft) - waere von + # main()s except (DisplayUnavailable, ImportError) nicht gefangen (Fixrunde 3, + # F5). lines() erzeugt selbst kein "\n", aber Werte aus status.json/last_seen + # (z.B. product_name) sind letztlich Daten von der Lampe/dem Netzwerk, kein + # Wert, den lines() selbst kontrolliert - ein Zeilenumbruch dort waere ohnehin + # nur ein zerstoertes Layout, kein sinnvoller Inhalt. + line = raw_line.replace("\n", " ") fitted = _truncate_to_width(line, measure=measure, max_width_px=max_width_px) draw.text((TEXT_MARGIN_PX, y), fitted, font=font, fill=0) y += LINE_HEIGHT_PX diff --git a/tools/station/ninlight-display.service b/tools/station/ninlight-display.service index f18decf..0c81a5a 100644 --- a/tools/station/ninlight-display.service +++ b/tools/station/ninlight-display.service @@ -24,10 +24,15 @@ Type=simple Environment=EPD_REVISION=V3 Environment=NINLIGHT_RUN_DIR=/run/ninlight-station Environment=NINLIGHT_STATION_CONF=/etc/ninlight-station.conf -# "-"-Praefix: ohne die Konfigurationsdatei soll die Anzeige trotzdem starten (mit den -# Environment=-Vorgaben oben) statt mit einem harten Fehler - "die Anzeige darf nirgends -# Voraussetzung sein" gilt auch fuer ihre eigene Konfiguration (Fixrunde 1/2, Hinweis). --EnvironmentFile=/etc/ninlight-station.conf +# "-"-Praefix VOR dem Dateipfad, nicht vor dem Direktivennamen (Fixrunde 3, F1 - das war +# in Fixrunde 2 falsch herum: "-EnvironmentFile=..." ist kein gueltiger Schluesselname, +# systemd verwirft die Zeile komplett mit "Unknown key name", und die Konfigurationsdatei +# wurde dadurch ueberhaupt nicht mehr gelesen - Environment=EPD_REVISION=V3 oben haette +# IMMER gewonnen, unabhaengig davon, was in /etc/ninlight-station.conf steht). Mit dem +# Praefix am richtigen Platz gilt: ohne die Konfigurationsdatei startet die Anzeige +# trotzdem (mit den Environment=-Vorgaben oben) statt mit einem harten Fehler - "die +# Anzeige darf nirgends Voraussetzung sein" gilt auch fuer ihre eigene Konfiguration. +EnvironmentFile=-/etc/ninlight-station.conf ExecStart=/usr/bin/python3 /opt/ninlight-station/display/display.py # display.py faengt ein fehlendes/abgestuerztes Panel als DisplayUnavailable ab, diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 7d9cfe3..811e196 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -62,38 +62,31 @@ def test_truncate_to_width_leaves_short_text_untouched(): assert render._truncate_to_width("kurz", measure=len, max_width_px=10) == "kurz" -def test_compose_keeps_every_line_within_panel_width(): - """Regressionstest zu Fixrunde 2: eine Kuerzung nach Zeichenzahl (die fruehere - MAX_COLS-Fassung) ist bei der von compose() verwendeten Proportionalschrift - (PIL.ImageFont.load_default(), seit Pillow 10.1 eine TrueType-Schrift) keine - gueltige Breitengrenze - eine grossbuchstabenlastige Zeile wie ein Produktname aus - der Lampenerkennung ("KLYQA GU10 RGB CW/WW") ist pro Zeichen breiter als eine - gemischte Beispielzeile und lief trotz weniger Zeichen als die alte Grenze ueber die - Panelbreite hinaus. Pillow ist in dieser Testumgebung vorhanden (siehe Bericht); auf - dem Ziel-Pi kommt es erst per "apt install python3-pil" hinzu, genau wie fuer - compose() selbst.""" - from PIL import Image, ImageDraw, ImageFont +def test_compose_does_not_draw_into_the_last_column_for_long_lines(): + """Regressionstest zu Fixrunde 3: der Reviewer hat per Mutation belegt, dass eine + deaktivierte Kuerzung in compose() (fitted = line) die vorherige Testsuite NICHT + aufgehalten haette, weil der alte Test die Kuerzung selbst nachbaute (mit derselben + _truncate_to_width()-Funktion, die auch geprueft werden sollte) statt das von + compose() tatsaechlich GEZEICHNETE BILD zu inspizieren. + Dieser Test prueft stattdessen die Pixel direkt: bei korrekt wirkender Kuerzung + bleibt die letzte Panel-Spalte (x = PANEL_WIDTH_PX - 1) fuer eine absichtlich sehr + lange Zeile weiss, weil die Kuerzung auf PANEL_WIDTH_PX - 2*TEXT_MARGIN_PX zielt + (empirisch mit echtem Pillow bestaetigt: die rechte Kante liegt bei ca. Spalte 237, + deutlich vor Spalte 249). Mit deaktivierter Kuerzung fuellt der zeichenweise laengere + Text die Zeile bis zum Rand - ebenfalls empirisch bestaetigt (die letzte Spalte + enthaelt dann einen schwarzen Pixel).""" state = {"mode": "migration", "phase": "listen", "served": 0, "recent": [], "ssid": "qconnex-firmware", - "last_seen": {"product_name": "KLYQA GU10 RGB CW/WW " + "W" * 40, - "target": "e27_rgb", "variant": "dev", "lamp_version": "V1"}} + "last_seen": {"product_name": "W" * 200, "target": "e27_rgb", + "variant": "dev", "lamp_version": "V1"}} - # compose() muss uebrigens auch ohne Ausnahme durchlaufen - reine Rauchprobe. image = render.compose(state) - assert image.size == (render.PANEL_WIDTH_PX, render.PANEL_HEIGHT_PX) + pixels = image.load() + last_col = render.PANEL_WIDTH_PX - 1 - # Dieselbe Kuerzung, die compose() intern verwendet (echte Schrift/Messfunktion), - # muss jede Zeile unter die verfuegbare Panelbreite bringen. - dummy = Image.new("1", (1, 1)) - draw = ImageDraw.Draw(dummy) - font = ImageFont.load_default() - max_width_px = render.PANEL_WIDTH_PX - 2 * render.TEXT_MARGIN_PX - for raw_line in render.lines(state): - fitted = render._truncate_to_width( - raw_line, measure=lambda s: draw.textlength(s, font=font), - max_width_px=max_width_px) - assert draw.textlength(fitted, font=font) <= max_width_px + black_in_last_column = any(pixels[last_col, y] == 0 for y in range(render.PANEL_HEIGHT_PX)) + assert not black_in_last_column, "Text erreicht die letzte Spalte - Kuerzung wirkt nicht" def test_missing_fields_do_not_raise(): @@ -275,12 +268,13 @@ def test_v3_partial_draw_after_sleep_does_not_crash(monkeypatch): def test_v2_partial_draw_after_sleep_does_not_crash(monkeypatch): - """Dieselbe Absicherung fuer V2 (Fixrunde 2, N3): epd2in13_V2.sleep() ist Deep Sleep - Mode 2 (RAM/Register verworfen); ein init(PART_UPDATE) direkt danach setzt nur einen - Teil der fuer einen Teilaufbau noetigen Register (0x01/0x11/0x44/0x45/0x4E/0x4F setzt - ausschliesslich der FULL_UPDATE-Zweig) - keine Ausnahme, aber eine auf echter - Hardware unbrauchbare Sequenz. draw_partial() muss auch hier nach sleep() zu einem - vollwertigen FULL_UPDATE-Vollaufbau (inklusive displayPartBaseImage()) aufwerten.""" + """Dieselbe Rauchprobe fuer V2 wie oben fuer V3 (Fixrunde 2, N3) - dass hier NICHTS + crasht, ist aber nur die halbe Absicherung: ob dabei auch die RICHTIGEN Register + gesetzt werden, prueft erst der staerkere Test unten + (test_v2_wake_after_sleep_sets_full_update_registers) mit einem Kommando-Rekorder. + Der Reviewer hat zu Recht angemerkt, dass eine Fassung dieses Tests ohne jede + Zusicherung nichts ueber die Registerlage aussagt - deshalb hier wenigstens die + beobachtbaren Panel-Zustandsuebergaenge pruefen.""" _stub_hardware(monkeypatch) panel = display.Panel("V2") @@ -289,16 +283,170 @@ def test_v2_partial_draw_after_sleep_does_not_crash(monkeypatch): panel.clear() panel.draw_full(image) panel.sleep() - panel.draw_partial(image) + assert panel._awake is False # Vorbedingung fuer den eigentlich interessanten Schritt + + panel.draw_partial(image) # das ist der Schritt, der bei falscher Sequenz froeren wuerde + + assert panel._awake is True + assert epdconfig._spi is not None # Panel hat sich nach dem Schlafen neu initialisiert def test_panel_sleep_is_idempotent(monkeypatch): """Ein zweiter sleep()-Aufruf, waehrend das Panel schon schlaeft, darf nicht versuchen, ueber die von module_exit() bereits geschlossenen Pins SPI-Befehle zu - schicken (derselbe AttributeError-Mechanismus wie N1, nur selbst herbeigefuehrt).""" + schicken (derselbe AttributeError-Mechanismus wie N1, nur selbst herbeigefuehrt). + Die staerkere Fassung unten (test_panel_sleep_sends_no_commands_when_already_asleep) + prueft das zusaetzlich direkt an der gesendeten Kommandofolge.""" _stub_hardware(monkeypatch) panel = display.Panel("V3") panel.clear() panel.sleep() + assert panel._awake is False + panel.sleep() # darf nicht erneut versuchen, epd.sleep() aufzurufen + + assert panel._awake is False + + +# --- Kommando-Rekorder: Registerzustand nach echten Bytes pruefen, nicht nur "kein Absturz" +# +# Der Reviewer hat per Mutation gezeigt, dass die Stubs oben (_stub_hardware) N2/N3 nicht +# wirklich absichern: displayPartBaseImage(buf) durch display(buf) ersetzen, oder die +# V2-Registerfolge nach sleep() kaputt machen, liess alle bisherigen Tests gruen. Ein +# stiller Registerzustandsfehler wirft keine Ausnahme - die einzige Art, ihn in Software +# statt erst am Panel selbst zu fassen, ist die tatsaechlich gesendete Byte-Folge zu +# protokollieren. Ersetzt dafuer epdconfig's Funktionen direkt (nicht gpiozero/spidev +# darunter) und wertet aus, welche Bytes waehrend DC=0 (Kommando) vs. DC=1 (Daten) +# gesendet wurden - siehe send_command()/send_data() in den vendored Treibern. + +class _RecordingHardware: + def __init__(self): + self.commands: list[int] = [] + self._dc = 0 + self._busy = 0 + + def digital_write(self, pin, value): + if pin == epdconfig.DC_PIN: + self._dc = value + # RST_PIN/CS_PIN sind fuer die Kommandofolge nicht relevant. + + def digital_read(self, pin): + return self._busy + + def delay_ms(self, ms): + pass + + def spi_writebyte(self, data): + if self._dc == 0: + self.commands.extend(data) + + def spi_writebyte2(self, data): + pass # Nutzdaten (Bildpuffer/LUT) - fuer die Kommandofolge nicht relevant. + + def module_init(self): + return 0 + + def module_exit(self): + pass + + +def _install_recorder(monkeypatch) -> _RecordingHardware: + recorder = _RecordingHardware() + monkeypatch.setattr(epdconfig, "digital_write", recorder.digital_write) + monkeypatch.setattr(epdconfig, "digital_read", recorder.digital_read) + monkeypatch.setattr(epdconfig, "delay_ms", recorder.delay_ms) + monkeypatch.setattr(epdconfig, "spi_writebyte", recorder.spi_writebyte) + monkeypatch.setattr(epdconfig, "spi_writebyte2", recorder.spi_writebyte2) + monkeypatch.setattr(epdconfig, "module_init", recorder.module_init) + monkeypatch.setattr(epdconfig, "module_exit", recorder.module_exit) + return recorder + + +def test_v3_full_refresh_writes_reference_buffer(monkeypatch): + """Regressionstest zu Fixrunde 2, N2 - mit einer echten Zusicherung (Fixrunde 3): ein + Vollaufbau muss bei V3 sowohl RAM 0x24 als auch 0x26 beschreiben + (displayPartBaseImage() statt display()), sonst arbeitet der naechste Teilaufbau + gegen einen undefinierten Referenzpuffer. Der Reviewer hat per Mutation + (displayPartBaseImage(buf) -> display(buf)) gezeigt, dass die vorherige Testsuite das + nicht bemerkt haette - dieser Test waere mit dieser Mutation rot.""" + recorder = _install_recorder(monkeypatch) + panel = display.Panel("V3") + image = render.compose({"mode": "off"}) + + panel.clear() + recorder.commands.clear() # nur der eigentliche Vollaufbau interessiert hier + + panel.draw_full(image) + + assert 0x24 in recorder.commands, f"0x24 (aktuelles RAM) fehlt: {recorder.commands}" + assert 0x26 in recorder.commands, f"0x26 (Referenzpuffer) fehlt: {recorder.commands}" + assert recorder.commands.index(0x26) > recorder.commands.index(0x24) + + +def test_v2_wake_after_sleep_sets_full_update_registers(monkeypatch): + """Regressionstest zu Fixrunde 2, N3 - mit einer echten Zusicherung (Fixrunde 3, + statt der vorherigen Fassung ganz ohne): nach sleep() muss der naechste Aufbau + (draw_partial(), das sich wegen Panel._awake selbst zu einem Vollaufbau aufwertet) + die Register senden, die NUR der FULL_UPDATE-Zweig von epd2in13_V2.init() setzt + (0x01 Driver output control, 0x11 Data entry mode, 0x44/0x45 RAM-Fenster, 0x4E/0x4F + RAM-Zaehler), und danach sowohl 0x24 als auch 0x26 beschreiben. Dieser Fehler wirft + keine Ausnahme - er ist nur an der gesendeten Byte-Folge erkennbar, sonst erst am + Panel selbst, wo er von einem Verdrahtungsfehler nicht zu unterscheiden waere.""" + recorder = _install_recorder(monkeypatch) + panel = display.Panel("V2") + image = render.compose({"mode": "off"}) + + panel.clear() + panel.draw_full(image) + panel.sleep() + recorder.commands.clear() # nur die Kommandofolge NACH dem Schlafen interessiert hier + + panel.draw_partial(image) + + required = [0x01, 0x11, 0x44, 0x45, 0x4E, 0x4F] + positions = [recorder.commands.index(cmd) for cmd in required + if cmd in recorder.commands] + assert len(positions) == len(required), ( + f"FULL_UPDATE-Register fehlen nach sleep(): {recorder.commands}") + assert positions == sorted(positions), ( + f"FULL_UPDATE-Register nicht in der erwarteten Reihenfolge: {recorder.commands}") + + assert 0x24 in recorder.commands and 0x26 in recorder.commands, ( + f"0x24/0x26 (Referenzpuffer) fehlen: {recorder.commands}") + idx_24 = recorder.commands.index(0x24) + idx_26 = recorder.commands.index(0x26) + assert idx_24 > positions[-1], "0x24 vor den FULL_UPDATE-Registern geschrieben" + assert idx_26 > idx_24, "0x26 (Referenzpuffer) fehlt oder kommt vor 0x24" + + +def test_panel_clear_establishes_reference_buffer(monkeypatch): + """Regressionstest zu Fixrunde 3, F2: clear() setzte vorher self._awake=True, ohne + einen 0x26-Referenzpuffer zu schreiben (epd.Clear() schreibt nur 0x24, bei V2 ist das + 0x26-Schreiben in Clear() sogar auskommentiert) - ein draw_partial() direkt nach + clear() liefe gegen denselben undefinierten Puffer, den N2 eigentlich beheben + sollte. clear() verwendet jetzt wie draw_full() displayPartBaseImage().""" + recorder = _install_recorder(monkeypatch) + panel = display.Panel("V3") + + panel.clear() + + assert 0x24 in recorder.commands + assert 0x26 in recorder.commands + assert panel._awake is True + + +def test_panel_sleep_sends_no_commands_when_already_asleep(monkeypatch): + """Staerkere Fassung von test_panel_sleep_is_idempotent (Fixrunde 3): ein zweiter + sleep()-Aufruf darf NICHT nochmal versuchen, ueber die schon geschlossenen Pins + SPI-Befehle zu schicken - hier direkt an der aufgezeichneten Kommandofolge geprueft + statt nur daran, dass keine Ausnahme fliegt.""" + recorder = _install_recorder(monkeypatch) + panel = display.Panel("V3") + panel.clear() + panel.sleep() + recorder.commands.clear() + + panel.sleep() # bereits asleep - darf keine weiteren Kommandos senden + + assert recorder.commands == [] From b8c7febb8fb49fd66d1d9dd4aafeb418d321dabb Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 22:04:39 +0200 Subject: [PATCH 122/150] fix(station): Ghosting-Ausgleich nach Buendel, Zeitsteuerung testbar Der Reset von partials_since_full beim Einschlafen liess den faelligen Ausgleichs-Vollaufbau dauerhaft ausfallen: nach einem Buendel Teilaufbauten schlief das Panel mit dem Geisterbild ein und zeigte es stundenlang weiter, weil stale nie mehr feuern konnte. Reset ersatzlos gestrichen - der Ausgleich bleibt ausstehend und wird zum 30-Minuten-Termin nachgeholt; der in Fixrunde 3 beseitigte Bias-Zyklus kommt dadurch nicht zurueck. Die gesamte Zeitsteuerung steckt jetzt in RefreshPlanner statt in run_daemon()s Endlosschleife und ist ohne Schleife, ohne time.sleep() und ohne Hardware pruefbar. Neue Tests spielen sechs Stunden Betrieb durch (statischer Inhalt, Aenderung alle 3/9/11 Minuten, Buendel-dann-Ruhe) und schliessen die drei vom Reviewer per Mutation belegten Luecken aus Fixrunde 3. --- tools/station/display/display.py | 162 +++++++++++++++------- tools/station/tests/test_render.py | 208 +++++++++++++++++++++++++++++ 2 files changed, 322 insertions(+), 48 deletions(-) diff --git a/tools/station/display/display.py b/tools/station/display/display.py index 89cb103..83eb9e0 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -301,6 +301,104 @@ class Panel: self._awake = False +class RefreshPlanner: + """Die gesamte Zeitsteuerung der Anzeige - wann voll aufgebaut, wann teilweise, wann + geschlafen wird - als reine Zustandsmaschine ohne Schleife, ohne time.sleep() und ohne + Hardware. + + Vorher steckte diese Logik direkt in run_daemon()s Endlosschleife und war dadurch + ungetestet (Fixrunde 4): drei Korrekturen aus Fixrunde 3 (die stale-Bedingung, die + Rueckmeldung einer intern aufgewerteten Teilaktualisierung, die Zeilenumbruchbehandlung + in compose()) liessen sich per Mutation aendern, ohne dass ein einziger Test rot wurde. + Hier laesst sich stattdessen ein kompletter Tagesverlauf in Millisekunden durchspielen + (siehe test_render.py, _run_schedule()). + + Benutzung: pro Runde einmal decide() fragen, die zurueckgegebene Aktion ausfuehren und + das Ergebnis per record_draw()/record_sleep() zurueckmelden. decide() selbst aendert + nichts - der Aufrufer meldet, was tatsaechlich passiert ist (insbesondere, ob + Panel.draw_partial() sich selbst zu einem Vollaufbau aufgewertet hat). + + Aktionen: + "full" - kompletter Bildaufbau (Moduswechsel oder faelliger Ghosting-Ausgleich) + "partial" - Teilaktualisierung (Inhalt hat sich geaendert) + "sleep" - Panel schlafen legen (laenger nichts passiert) + "idle" - nichts tun + """ + + _NO_MODE = object() # garantiert ungleich jedem echten Modus beim ersten Lauf + + def __init__(self, now: float, + full_refresh_interval_s: float = FULL_REFRESH_INTERVAL_S, + sleep_after_idle_s: float = SLEEP_AFTER_IDLE_S) -> None: + self.full_refresh_interval_s = full_refresh_interval_s + self.sleep_after_idle_s = sleep_after_idle_s + self.last_lines: list[str] | None = None + self.last_mode: object = self._NO_MODE + self.last_full = now + self.last_draw = now + # Zaehlt Teilaufbauten seit dem letzten Vollaufbau. full_refresh_interval_s + # existiert nur, um Geisterbilder aus AUFEINANDERFOLGENDEN Teilaufbauten + # auszugleichen - ohne mindestens einen Teilaufbau seit dem letzten Vollaufbau gibt + # es nichts auszugleichen, und ein Vollaufbau des UNVERAENDERTEN Bildes waere + # reine, nutzlose Alterung (Fixrunde 3, F3): ohne diese Absicherung entstand im + # eingeschwungenen Ruhezustand ein endloser 10-Minuten-schlafen/10-Minuten-wach- + # Zyklus (33% Bias-Anteil) rein aus der 30-Minuten-Uhr, obwohl sich der Inhalt nie + # aenderte. + # + # Der Zaehler wird beim Einschlafen BEWUSST NICHT zurueckgesetzt (Fixrunde 4, N1): + # das tat die Fassung aus Fixrunde 3 und liess den faelligen Ghosting-Ausgleich + # damit dauerhaft ausfallen - nach einem Buendel Teilaufbauten (z.B. 30 Lampen in + # 2,5 Minuten) schlief das Panel nach 10 Minuten mit dem entstandenen Geisterbild + # ein und zeigte genau dieses Bild stundenlang weiter, weil stale wegen + # partials_since_full == 0 nie mehr feuern konnte. So bleibt der Ausgleich + # ausstehend und wird zum 30-Minuten-Termin nachgeholt (das weckt das Panel einmal + # kurz auf; danach ist der Zaehler 0 und der eingeschwungene Ruhezustand bleibt + # wie in F3 gewuenscht bei 3% Wachanteil). Die Alternative - vor dem Einschlafen + # einmal draw_full() ziehen - kostet denselben einen Vollaufbau, aber JEDES Mal, + # wenn ein Buendel endet, auch wenn kurz darauf ohnehin neue Inhalte kommen (die + # das Panel nach dem Schlafen sowieso ueber draw_partial()s Aufwertung voll + # aufbauen). Diese Variante altert das Panel im Betriebsfall also mehr, nicht + # weniger, und der Ausgleich hier kommt spaetestens zum ohnehin vorgesehenen + # Termin. + self.partials_since_full = 0 + self.asleep = False + + def decide(self, now: float, lines: list[str], mode: object) -> str: + """Was jetzt zu tun ist. Aendert nichts - das Ergebnis wird per record_*() + zurueckgemeldet.""" + mode_changed = mode != self.last_mode + stale = (self.partials_since_full > 0 + and (now - self.last_full) >= self.full_refresh_interval_s) + + if lines != self.last_lines or mode_changed or stale: + return "full" if (mode_changed or stale) else "partial" + if not self.asleep and (now - self.last_draw) >= self.sleep_after_idle_s: + # Nach laengerer Ruhe schlafen legen (Fixrunde 1, I4: die Pixel bleiben - + # e-Paper ist bistabil - aber ohne dauerhafte Spannung altert das Panel + # deutlich weniger). + return "sleep" + return "idle" + + def record_draw(self, now: float, lines: list[str], mode: object, + was_full: bool) -> None: + """`was_full` ist True fuer einen regulaeren Vollaufbau UND dann, wenn + Panel.draw_partial() sich selbst zu einem Vollaufbau aufgewertet hat (Fixrunde 3, + F4) - sonst kaeme der naechste "stale"-Vollaufbau zu frueh und altert das Panel + zusaetzlich, obwohl gerade erst vollstaendig aufgebaut wurde.""" + self.last_lines = lines + self.last_mode = mode + self.last_draw = now + self.asleep = False + if was_full: + self.last_full = now + self.partials_since_full = 0 + else: + self.partials_since_full += 1 + + def record_sleep(self, now: float) -> None: + self.asleep = True + + def _test_image(revision: str): from PIL import Image, ImageDraw, ImageFont @@ -337,22 +435,13 @@ def run_clear(revision: str) -> None: def run_daemon(revision: str) -> None: + """Endlosschleife: alle POLL_INTERVAL_S Sekunden den Zustand einlesen und die von + RefreshPlanner entschiedene Aktion ausfuehren. Hier steht bewusst KEINE Zeitlogik mehr + (Fixrunde 4) - die steckt vollstaendig in RefreshPlanner und ist dort ohne Schleife, + ohne time.sleep() und ohne Hardware pruefbar.""" panel = Panel(revision) panel.clear() - - last_lines: list[str] | None = None - last_mode: object = object() # garantiert ungleich jedem echten Modus beim ersten Lauf - last_full = time.monotonic() - last_draw = time.monotonic() - # Zaehlt Teilaufbauten seit dem letzten Vollaufbau. FULL_REFRESH_INTERVAL_S existiert - # nur, um Geisterbilder aus AUFEINANDERFOLGENDEN Teilaufbauten auszugleichen - ohne - # mindestens einen Teilaufbau seit dem letzten Vollaufbau gibt es nichts auszugleichen, - # und ein Vollaufbau des UNVERAENDERTEN Bildes waere reine, nutzlose Alterung - # (Fixrunde 3, F3): ohne diese Absicherung entstand im eingeschwungenen Ruhezustand ein - # endloser 10-Minuten-schlafen/10-Minuten-wach-Zyklus (33% Bias-Anteil) rein aus der - # 30-Minuten-Uhr, obwohl sich der Inhalt nie aenderte - genau das, was I4 verhindern - # sollte. - partials_since_full = 0 + planner = RefreshPlanner(time.monotonic()) while True: state = build_state() @@ -360,41 +449,18 @@ def run_daemon(revision: str) -> None: mode = state.get("mode") now = time.monotonic() - mode_changed = mode != last_mode - stale = partials_since_full > 0 and (now - last_full) >= FULL_REFRESH_INTERVAL_S - - if current_lines != last_lines or mode_changed or stale: - image = render.compose(state) - if mode_changed or stale: - panel.draw_full(image) - last_full = now - partials_since_full = 0 - else: - upgraded = panel.draw_partial(image) - if upgraded: - # Panel.draw_partial() hat selbst zu einem Vollaufbau aufgewertet - # (Panel war nicht wach, siehe Panel-Klasse) - das zaehlt fuer die - # 30-Minuten-Uhr wie ein regulaerer Vollaufbau (Fixrunde 3, F4), sonst - # kaeme der naechste "stale"-Vollaufbau zu frueh. - last_full = now - partials_since_full = 0 - else: - partials_since_full += 1 - last_draw = now - last_lines = current_lines - last_mode = mode - elif (now - last_draw) >= SLEEP_AFTER_IDLE_S: - # Nach laengerer Ruhe schlafen legen (I4: die Pixel bleiben - e-Paper ist - # bistabil - aber ohne dauerhafte Spannung altert das Panel deutlich - # weniger). Panel.sleep() ist idempotent, ein wiederholter Aufruf hier - # (solange weiterhin nichts passiert) ist also unschaedlich. + action = planner.decide(now, current_lines, mode) + if action == "full": + panel.draw_full(render.compose(state)) + planner.record_draw(now, current_lines, mode, was_full=True) + elif action == "partial": + # Panel.draw_partial() wertet sich selbst zu einem Vollaufbau auf, wenn das + # Panel nicht wach ist (siehe Panel-Klasse) - das meldet es hier zurueck. + upgraded = panel.draw_partial(render.compose(state)) + planner.record_draw(now, current_lines, mode, was_full=upgraded) + elif action == "sleep": panel.sleep() - # Nach dem Schlafen wertet Panel.draw_partial() den naechsten Aufbau ohnehin - # zu einem Vollaufbau auf - der 30-Minuten-Zaehler soll dieses "natuerliche" - # Zuruecksetzen nicht durch einen zusaetzlichen, inhaltslosen "stale"- - # Vollaufbau vorwegnehmen, falls vor dem Einschlafen schon Teilaufbauten - # stattgefunden hatten (Fixrunde 3, F3, zweiter Teil). - partials_since_full = 0 + planner.record_sleep(now) time.sleep(POLL_INTERVAL_S) diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 811e196..e38eaf5 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -450,3 +450,211 @@ def test_panel_sleep_sends_no_commands_when_already_asleep(monkeypatch): panel.sleep() # bereits asleep - darf keine weiteren Kommandos senden assert recorder.commands == [] + + +def test_draw_partial_reports_whether_it_upgraded_itself(monkeypatch): + """Fixrunde 3, F4 - vom Reviewer per Mutation als ungetestet belegt: gibt + draw_partial() nach dem Aufwerten `False` statt `True` zurueck, blieben alle 21 Tests + gruen. Der Rueckgabewert ist aber die einzige Information, an der RefreshPlanner + erkennt, dass die 30-Minuten-Uhr neu beginnt - ohne ihn kaeme der naechste + Ghosting-Ausgleich zu frueh und alterte das Panel ohne Not.""" + _install_recorder(monkeypatch) + panel = display.Panel("V3") + image = render.compose({"mode": "off"}) + + panel.clear() + assert panel.draw_partial(image) is False, "waches Panel: kein Vollaufbau noetig" + + panel.sleep() + assert panel.draw_partial(image) is True, ( + "nach sleep() wertet draw_partial() intern zu einem Vollaufbau auf - das muss es " + "auch zurueckmelden") + assert panel.draw_partial(image) is False, "danach ist das Panel wieder wach" + + +def test_compose_survives_a_newline_inside_a_value(): + """Fixrunde 3, F5 - ebenfalls per Mutation als ungetestet belegt (`line = raw_line` + liess alle Tests gruen): draw.textlength() wirft ValueError("can't measure length of + multiline text"), sobald eine Zeile ein "\\n" enthaelt. product_name kommt aus der + Lampenerkennung, ist also letztlich Netzwerkdatum - und ein ValueError hier faengt + main()s `except (DisplayUnavailable, ImportError)` NICHT ab, das waere ein echter + Absturz des Dienstes.""" + state = {"mode": "migration", "phase": "auto", "served": 0, "recent": [], + "last_seen": {"product_name": "Klyqa\nGU10", "target": "gu10_rgb", + "variant": "prod", "lamp_version": "V1"}} + + image = render.compose(state) # vor F5: ValueError + + assert image.size == (render.PANEL_WIDTH_PX, render.PANEL_HEIGHT_PX) + + +# --- RefreshPlanner: die Zeitsteuerung ohne Schleife durchspielen (Fixrunde 4) --------- +# +# run_daemon() hatte null Testabdeckung - die gesamte Zeitsteuerung steckte in einer +# Endlosschleife. Sie liegt jetzt in display.RefreshPlanner und laesst sich hier als +# Zeitreihe durchspielen: pro Poll-Runde decide() fragen, die Aktion "ausfuehren" (das +# Panel-Verhalten wird dabei nur so weit nachgebildet, wie es die Entscheidung beeinflusst: +# ein Teilaufbau auf einem schlafenden Panel wertet sich selbst zu einem Vollaufbau auf, +# siehe Panel.draw_partial()) und das Ergebnis zurueckmelden - genau die Reihenfolge, die +# run_daemon() ebenfalls verwendet. + +SIX_HOURS_S = 6 * 60 * 60 + + +def _run_schedule(content_at, duration_s, mode="recovery", + poll=display.POLL_INTERVAL_S, planner=None): + """Spielt `duration_s` Sekunden Anzeigebetrieb durch. `content_at(t)` liefert die + Zeilen, die build_state()/render.lines() zum Zeitpunkt t liefern wuerden. + + Gibt (Protokoll, Wachsekunden) zurueck; Protokoll ist eine Liste von (t, Aktion).""" + planner = planner if planner is not None else display.RefreshPlanner(0.0) + log: list[tuple[float, str]] = [] + awake = True # run_daemon() ruft direkt nach dem Start panel.clear() + awake_s = 0.0 + t = 0.0 + while t <= duration_s: + lines = content_at(t) + action = planner.decide(t, lines, mode) + if action == "full": + planner.record_draw(t, lines, mode, was_full=True) + awake = True + elif action == "partial": + # Panel.draw_partial() wertet auf einem schlafenden Panel selbst zu einem + # Vollaufbau auf und meldet das zurueck (F4, siehe Test oben) - im Protokoll + # als "wake-full" unterschieden, weil so ein Aufbau das Panel eben NICHT nur + # teilweise auffrischt. + upgraded = not awake + planner.record_draw(t, lines, mode, was_full=upgraded) + awake = True + if upgraded: + action = "wake-full" + elif action == "sleep": + planner.record_sleep(t) + awake = False + log.append((t, action)) + if awake: + awake_s += poll + t += poll + return log, awake_s + + +def _times(log, action): + return [t for t, a in log if a == action] + + +def _changing_every(period_s): + """Inhalt, der sich alle `period_s` Sekunden aendert (und sonst gleich bleibt).""" + return lambda t: ["RETTUNG", f"Ausgeliefert: {int(t // period_s)}"] + + +def test_static_content_never_triggers_another_full_refresh(monkeypatch): + """Fixrunde 3, F3 - vom Reviewer per Mutation als ungetestet belegt: nimmt man die + Bedingung `partials_since_full > 0` aus `stale` heraus, bleiben alle 21 Tests gruen. + Genau diese Bedingung verhindert aber den 33-%-Bias-Zyklus: bei UNVERAENDERTEM Inhalt + gibt es keine Geisterbilder aus Teilaufbauten auszugleichen, ein Vollaufbau alle 30 + Minuten waere reine Alterung - und weckt das Panel jedesmal fuer weitere 10 Minuten + auf.""" + log, awake_s = _run_schedule(lambda t: ["RETTUNG", "Ausgeliefert: 0"], SIX_HOURS_S) + + assert _times(log, "full") == [0.0], ( + f"nur der Erstaufbau darf voll sein, tatsaechlich: {_times(log, 'full')}") + assert _times(log, "partial") == [] + assert _times(log, "sleep") == [600.0], "genau einmal einschlafen, dann Ruhe" + assert awake_s <= 700, f"Panel war {awake_s} s wach - erwartet nur die ersten 10 min" + + +def test_change_every_3_minutes_stays_awake_and_compensates_every_30_minutes(): + log, _ = _run_schedule(_changing_every(3 * 60), SIX_HOURS_S) + + fulls = _times(log, "full") + assert _times(log, "sleep") == [], "alle 3 min Verkehr - das Panel darf nicht schlafen" + assert len(_times(log, "partial")) > 100, "die Aenderungen muessen Teilaufbauten sein" + assert len(fulls) >= 10, f"Ghosting-Ausgleich alle 30 min fehlt: {fulls}" + gaps = [b - a for a, b in zip(fulls, fulls[1:])] + assert all(gap >= display.FULL_REFRESH_INTERVAL_S for gap in gaps), ( + f"Vollaufbauten zu dicht: {fulls}") + + +def test_change_every_9_minutes_still_never_sleeps(): + """9 Minuten liegen knapp unter SLEEP_AFTER_IDLE_S (10 min) - das Panel bleibt wach, + die Teilaufbauten sammeln sich, und die 30-Minuten-Uhr gleicht sie aus.""" + log, _ = _run_schedule(_changing_every(9 * 60), SIX_HOURS_S) + + fulls = _times(log, "full") + assert _times(log, "sleep") == [] + assert _times(log, "partial"), "Aenderungen bei wachem Panel sind Teilaufbauten" + assert len(fulls) >= 5, f"Ghosting-Ausgleich fehlt: {fulls}" + gaps = [b - a for a, b in zip(fulls, fulls[1:])] + assert all(gap >= display.FULL_REFRESH_INTERVAL_S for gap in gaps) + + +def test_change_every_11_minutes_sleeps_between_and_wakes_with_a_full_refresh(): + """11 Minuten liegen knapp ueber SLEEP_AFTER_IDLE_S: das Panel schlaeft zwischen zwei + Aenderungen ein, und der naechste Aufbau wird von Panel.draw_partial() selbst zu einem + Vollaufbau aufgewertet - es darf also gar kein Teilaufbau mehr entstehen, und die + 30-Minuten-Uhr darf nie zusaetzlich feuern (jeder Aufbau IST schon ein Vollaufbau).""" + log, _ = _run_schedule(_changing_every(11 * 60), SIX_HOURS_S) + + assert _times(log, "partial") == [], ( + "nach dem Schlafen wertet das Panel jeden Aufbau selbst zu einem Vollaufbau auf") + assert len(_times(log, "wake-full")) == SIX_HOURS_S // (11 * 60), ( + f"pro Aenderung ein Aufbau erwartet: {_times(log, 'wake-full')}") + assert len(_times(log, "sleep")) >= 15, "zwischen den Aenderungen muss geschlafen werden" + # Nur der Erstaufbau ist ein regulaerer Vollaufbau: jeder weitere Aufbau IST wegen der + # Aufwertung schon vollstaendig, ein zusaetzlicher "stale"-Vollaufbau waere ein Zeichen, + # dass die Aufwertung nicht als Vollaufbau verbucht wurde (F4). + assert _times(log, "full") == [0.0], f"unerwartete Vollaufbauten: {_times(log, 'full')}" + + +def test_burst_then_quiet_still_gets_its_ghosting_compensation(): + """Fixrunde 4, N1 - der Dauerzustand, den jemand am laengsten ansieht: ein Buendel von + 30 Teilaufbauten in 2,5 Minuten (Migration mehrerer Lampen), danach stundenlang Ruhe. + + Die Fassung aus Fixrunde 3 setzte partials_since_full beim Einschlafen auf 0; das + Panel schlief 10 Minuten nach dem Buendel mit dem entstandenen Geisterbild ein, stale + konnte danach nie mehr feuern, und genau dieses Bild stand die restlichen rund 5 h 50 + min unveraendert da. Der faellige Ausgleich muss stattdessen nachgeholt werden - und + das Panel danach wieder zur Ruhe kommen.""" + def content_at(t): + # 30 Aenderungen im 5-Sekunden-Takt (t = 5 .. 150), danach unveraendert. + step = min(int(t // display.POLL_INTERVAL_S), 30) + return ["MIGRATION", f"Ausgeliefert: {step}"] + + log, awake_s = _run_schedule(content_at, SIX_HOURS_S, mode="migration") + + assert len(_times(log, "partial")) == 30, ( + f"das Buendel muss aus Teilaufbauten bestehen: {_times(log, 'partial')}") + + fulls = _times(log, "full") + assert len(fulls) == 2, f"Erstaufbau + genau ein Ausgleich erwartet, war: {fulls}" + ausgleich = fulls[1] + assert display.FULL_REFRESH_INTERVAL_S <= ausgleich <= 35 * 60, ( + f"Ghosting-Ausgleich faellt aus oder kommt zu spaet (t={ausgleich})") + + # ... und danach wirklich Ruhe: einmal nach dem Buendel, einmal nach dem Ausgleich. + assert len(_times(log, "sleep")) == 2, f"unruhiger Schlaf: {_times(log, 'sleep')}" + assert awake_s <= 30 * 60, ( + f"Panel war {awake_s} s von {SIX_HOURS_S} s wach - der Ausgleich darf nicht in " + "einen Wachzyklus ausarten") + + +def test_planner_counts_an_upgraded_partial_as_a_full_refresh(): + """Direkt an der Zustandsmaschine (F4): meldet der Aufrufer eine von + Panel.draw_partial() selbst aufgewertete Teilaktualisierung als Vollaufbau, beginnt die + 30-Minuten-Uhr neu und es gibt nichts mehr auszugleichen.""" + planner = display.RefreshPlanner(0.0) + planner.record_draw(0.0, ["a"], "recovery", was_full=True) + planner.record_draw(10.0, ["b"], "recovery", was_full=False) + assert planner.partials_since_full == 1 + + planner.record_draw(20.0, ["c"], "recovery", was_full=True) # aufgewerteter Teilaufbau + + assert planner.partials_since_full == 0 + assert planner.decide(20.0 + display.FULL_REFRESH_INTERVAL_S, ["c"], "recovery") != "full" + + +def test_planner_draws_fully_on_mode_change(): + planner = display.RefreshPlanner(0.0) + planner.record_draw(0.0, ["RETTUNG"], "recovery", was_full=True) + + assert planner.decide(5.0, ["MIGRATION"], "migration") == "full" From 22f92988dd72b55437f7f42f6c75054f405a63ca Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 22:20:11 +0200 Subject: [PATCH 123/150] test(station): run_daemon selbst treiben statt eine Kopie der Schleife Die Verdrahtung zwischen RefreshPlanner und Panel - Aktion auf Panel-Methode, was_full=upgraded, record_sleep(), Reihenfolge decide/ausfuehren/zurueckmelden - ueberlebte jede Mutation, weil der Test eine nachgebaute Schleife trieb. Am schlimmsten: der "full"-Zweig auf draw_partial() umgebogen haette bedeutet, dass es auf einem wachen Panel nie wieder einen echten Vollaufbau gibt. run_daemon() nimmt jetzt Uhr, Panel und Zustandsquelle entgegen (Vorgaben unveraendert) und wird im Test mit Fake-Uhr und protokollierendem Panel-Doppel direkt getrieben; die Nachbildung _run_schedule() entfaellt ersatzlos. Dazu: RefreshPlanner liest die Intervallkonstanten wieder pro Entscheidung statt sie als Default-Argument zur Importzeit zu binden, record_sleep() verliert den toten now-Parameter. --- tools/station/display/display.py | 55 +++++-- tools/station/tests/test_render.py | 252 ++++++++++++++++++++--------- 2 files changed, 214 insertions(+), 93 deletions(-) diff --git a/tools/station/display/display.py b/tools/station/display/display.py index 83eb9e0..23ee305 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -328,10 +328,15 @@ class RefreshPlanner: _NO_MODE = object() # garantiert ungleich jedem echten Modus beim ersten Lauf def __init__(self, now: float, - full_refresh_interval_s: float = FULL_REFRESH_INTERVAL_S, - sleep_after_idle_s: float = SLEEP_AFTER_IDLE_S) -> None: - self.full_refresh_interval_s = full_refresh_interval_s - self.sleep_after_idle_s = sleep_after_idle_s + full_refresh_interval_s: float | None = None, + sleep_after_idle_s: float | None = None) -> None: + # Bewusst None statt der Modulkonstanten als Vorgabewert (Fixrunde 5): ein + # Default-Argument wird zur Importzeit ausgewertet, ein spaeteres + # monkeypatch.setattr(display, "FULL_REFRESH_INTERVAL_S", ...) haette dann keine + # Wirkung mehr auf neu gebaute Planer - die alte Schleife las die Konstanten pro + # Runde. Die Properties unten lesen sie weiterhin bei jeder Entscheidung. + self._full_refresh_interval_s = full_refresh_interval_s + self._sleep_after_idle_s = sleep_after_idle_s self.last_lines: list[str] | None = None self.last_mode: object = self._NO_MODE self.last_full = now @@ -363,6 +368,16 @@ class RefreshPlanner: self.partials_since_full = 0 self.asleep = False + @property + def full_refresh_interval_s(self) -> float: + return (FULL_REFRESH_INTERVAL_S if self._full_refresh_interval_s is None + else self._full_refresh_interval_s) + + @property + def sleep_after_idle_s(self) -> float: + return (SLEEP_AFTER_IDLE_S if self._sleep_after_idle_s is None + else self._sleep_after_idle_s) + def decide(self, now: float, lines: list[str], mode: object) -> str: """Was jetzt zu tun ist. Aendert nichts - das Ergebnis wird per record_*() zurueckgemeldet.""" @@ -395,7 +410,10 @@ class RefreshPlanner: else: self.partials_since_full += 1 - def record_sleep(self, now: float) -> None: + def record_sleep(self) -> None: + """Kein `now`-Parameter (Fixrunde 5): das Einschlafen verschiebt weder die + 30-Minuten-Uhr noch last_draw - es merkt sich nur, dass ein weiteres + panel.sleep() nichts mehr zu tun haette.""" self.asleep = True @@ -434,20 +452,31 @@ def run_clear(revision: str) -> None: log.info("Panel geleert (EPD_REVISION=%s)", revision) -def run_daemon(revision: str) -> None: +def run_daemon(revision: str, clock=time, panel: "Panel | None" = None, + read_state=build_state) -> None: """Endlosschleife: alle POLL_INTERVAL_S Sekunden den Zustand einlesen und die von RefreshPlanner entschiedene Aktion ausfuehren. Hier steht bewusst KEINE Zeitlogik mehr (Fixrunde 4) - die steckt vollstaendig in RefreshPlanner und ist dort ohne Schleife, - ohne time.sleep() und ohne Hardware pruefbar.""" - panel = Panel(revision) + ohne time.sleep() und ohne Hardware pruefbar. + + `clock` (braucht monotonic() und sleep()), `panel` und `read_state` sind einschleusbar, + damit die VERDRAHTUNG hier - welche Aktion welche Panel-Methode ruft, dass die + Selbst-Aufwertung von draw_partial() zurueckgemeldet wird, dass das Einschlafen + zurueckgemeldet wird, und die Reihenfolge decide -> ausfuehren -> zurueckmelden - + selbst pruefbar ist und nicht nur eine Kopie davon (Fixrunde 5, N2: alle drei + Mutationen an genau diesen Zeilen blieben vorher gruen, weil der Test eine nachgebaute + Schleife statt dieser hier trieb). Im Betrieb bleiben die Vorgaben: echte Uhr, echtes + Panel, echtes build_state().""" + if panel is None: + panel = Panel(revision) panel.clear() - planner = RefreshPlanner(time.monotonic()) + planner = RefreshPlanner(clock.monotonic()) while True: - state = build_state() + state = read_state() current_lines = render.lines(state) mode = state.get("mode") - now = time.monotonic() + now = clock.monotonic() action = planner.decide(now, current_lines, mode) if action == "full": @@ -460,9 +489,9 @@ def run_daemon(revision: str) -> None: planner.record_draw(now, current_lines, mode, was_full=upgraded) elif action == "sleep": panel.sleep() - planner.record_sleep(now) + planner.record_sleep() - time.sleep(POLL_INTERVAL_S) + clock.sleep(POLL_INTERVAL_S) def main() -> None: diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index e38eaf5..68db65d 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -488,87 +488,150 @@ def test_compose_survives_a_newline_inside_a_value(): assert image.size == (render.PANEL_WIDTH_PX, render.PANEL_HEIGHT_PX) -# --- RefreshPlanner: die Zeitsteuerung ohne Schleife durchspielen (Fixrunde 4) --------- +# --- run_daemon() gegen eine Fake-Uhr durchspielen (Fixrunde 4, verschaerft in 5) ----- # # run_daemon() hatte null Testabdeckung - die gesamte Zeitsteuerung steckte in einer -# Endlosschleife. Sie liegt jetzt in display.RefreshPlanner und laesst sich hier als -# Zeitreihe durchspielen: pro Poll-Runde decide() fragen, die Aktion "ausfuehren" (das -# Panel-Verhalten wird dabei nur so weit nachgebildet, wie es die Entscheidung beeinflusst: -# ein Teilaufbau auf einem schlafenden Panel wertet sich selbst zu einem Vollaufbau auf, -# siehe Panel.draw_partial()) und das Ergebnis zurueckmelden - genau die Reihenfolge, die -# run_daemon() ebenfalls verwendet. +# Endlosschleife. Sie liegt jetzt in display.RefreshPlanner, aber die ENTSCHEIDUNG allein +# zu pruefen reicht nicht: der Reviewer hat in Fixrunde 5 gezeigt, dass alle drei +# Mutationen an der VERDRAHTUNG zwischen Planer und Panel gruen blieben, solange der Test +# eine nachgebaute Schleife statt run_daemon() selbst trieb (was_full=upgraded verworfen, +# record_sleep() gestrichen, "full" auf draw_partial() umgebogen - letzteres haette +# bedeutet: nie wieder ein echter Vollaufbau auf wachem Panel). +# +# Deshalb wird hier das ECHTE run_daemon() getrieben - mit eingeschleuster Fake-Uhr und +# einem protokollierenden Panel-Doppel. Die frueher hier stehende Nachbildung +# (_run_schedule) ist ersatzlos entfallen, damit es nur eine Fassung der Schleife gibt. SIX_HOURS_S = 6 * 60 * 60 -def _run_schedule(content_at, duration_s, mode="recovery", - poll=display.POLL_INTERVAL_S, planner=None): - """Spielt `duration_s` Sekunden Anzeigebetrieb durch. `content_at(t)` liefert die - Zeilen, die build_state()/render.lines() zum Zeitpunkt t liefern wuerden. - - Gibt (Protokoll, Wachsekunden) zurueck; Protokoll ist eine Liste von (t, Aktion).""" - planner = planner if planner is not None else display.RefreshPlanner(0.0) - log: list[tuple[float, str]] = [] - awake = True # run_daemon() ruft direkt nach dem Start panel.clear() - awake_s = 0.0 - t = 0.0 - while t <= duration_s: - lines = content_at(t) - action = planner.decide(t, lines, mode) - if action == "full": - planner.record_draw(t, lines, mode, was_full=True) - awake = True - elif action == "partial": - # Panel.draw_partial() wertet auf einem schlafenden Panel selbst zu einem - # Vollaufbau auf und meldet das zurueck (F4, siehe Test oben) - im Protokoll - # als "wake-full" unterschieden, weil so ein Aufbau das Panel eben NICHT nur - # teilweise auffrischt. - upgraded = not awake - planner.record_draw(t, lines, mode, was_full=upgraded) - awake = True - if upgraded: - action = "wake-full" - elif action == "sleep": - planner.record_sleep(t) - awake = False - log.append((t, action)) - if awake: - awake_s += poll - t += poll - return log, awake_s +class _StopDaemon(Exception): + """Bricht run_daemon()s Endlosschleife ab, sobald die simulierte Zeit abgelaufen ist.""" -def _times(log, action): - return [t for t, a in log if a == action] +class _FakeClock: + """Ersetzt das time-Modul in run_daemon(): monotonic() liefert die simulierte Zeit, + sleep() laesst sie springen, statt tatsaechlich zu warten.""" + + def __init__(self, until_s: float) -> None: + self.t = 0.0 + self.until_s = until_s + + def monotonic(self) -> float: + return self.t + + def sleep(self, seconds: float) -> None: + self.t += seconds + if self.t > self.until_s: + raise _StopDaemon -def _changing_every(period_s): - """Inhalt, der sich alle `period_s` Sekunden aendert (und sonst gleich bleibt).""" - return lambda t: ["RETTUNG", f"Ausgeliefert: {int(t // period_s)}"] +class _RecordingPanel: + """Panel-Doppel: protokolliert jeden Aufruf mit Zeitstempel und bildet genau das + Verhalten des echten Panels nach, das die Zeitsteuerung beeinflusst - die + Selbst-Aufwertung von draw_partial() auf einem schlafenden Panel (dort als "wake-full" + protokolliert, weil ein solcher Aufbau eben NICHT nur teilweise auffrischt, siehe + test_draw_partial_reports_whether_it_upgraded_itself fuer den Nachweis am echten + Panel) und die Idempotenz von sleep() (ein Aufruf auf einem schon schlafenden Panel + wird als "sleep-noop" protokolliert: unschaedlich, aber ein Zeichen, dass run_daemon() + das Einschlafen nicht zurueckgemeldet hat).""" + + def __init__(self, clock: _FakeClock) -> None: + self.clock = clock + self.calls: list[tuple[float, str]] = [] + self.awake_s = 0.0 + self._awake = False + self._awake_since = 0.0 + + def _record(self, name: str) -> None: + self.calls.append((self.clock.monotonic(), name)) + + def _wake(self) -> None: + if not self._awake: + self._awake = True + self._awake_since = self.clock.monotonic() + + def clear(self) -> None: + self._record("clear") + self._wake() + + def draw_full(self, image) -> None: + assert image is not None + self._record("full") + self._wake() + + def draw_partial(self, image) -> bool: + assert image is not None + if not self._awake: + self._record("wake-full") + self._wake() + return True + self._record("partial") + return False + + def sleep(self) -> None: + if not self._awake: + self._record("sleep-noop") + return + self._record("sleep") + self.awake_s += self.clock.monotonic() - self._awake_since + self._awake = False + + def _finish(self, end_t: float) -> None: + if self._awake: + self.awake_s += end_t - self._awake_since -def test_static_content_never_triggers_another_full_refresh(monkeypatch): +def _drive_daemon(state_at, duration_s: float) -> _RecordingPanel: + """Laesst das echte display.run_daemon() `duration_s` simulierte Sekunden laufen. + `state_at(t)` liefert den Zustand, den build_state() zum Zeitpunkt t liefern wuerde.""" + clock = _FakeClock(duration_s) + panel = _RecordingPanel(clock) + try: + display.run_daemon("V3", clock=clock, panel=panel, + read_state=lambda: state_at(clock.monotonic())) + except _StopDaemon: + pass + panel._finish(clock.monotonic()) + return panel + + +def _times(panel: _RecordingPanel, call: str) -> list[float]: + return [t for t, name in panel.calls if name == call] + + +def _changing_every(period_s, mode="recovery"): + """Zustand, der sich alle `period_s` Sekunden aendert (und sonst gleich bleibt).""" + return lambda t: {"mode": mode, "served": int(t // period_s), "recent": []} + + +def test_static_content_never_triggers_another_full_refresh(): """Fixrunde 3, F3 - vom Reviewer per Mutation als ungetestet belegt: nimmt man die - Bedingung `partials_since_full > 0` aus `stale` heraus, bleiben alle 21 Tests gruen. + Bedingung `partials_since_full > 0` aus `stale` heraus, bleiben alle Tests gruen. Genau diese Bedingung verhindert aber den 33-%-Bias-Zyklus: bei UNVERAENDERTEM Inhalt gibt es keine Geisterbilder aus Teilaufbauten auszugleichen, ein Vollaufbau alle 30 Minuten waere reine Alterung - und weckt das Panel jedesmal fuer weitere 10 Minuten auf.""" - log, awake_s = _run_schedule(lambda t: ["RETTUNG", "Ausgeliefert: 0"], SIX_HOURS_S) + panel = _drive_daemon(lambda t: {"mode": "recovery", "served": 0, "recent": []}, + SIX_HOURS_S) - assert _times(log, "full") == [0.0], ( - f"nur der Erstaufbau darf voll sein, tatsaechlich: {_times(log, 'full')}") - assert _times(log, "partial") == [] - assert _times(log, "sleep") == [600.0], "genau einmal einschlafen, dann Ruhe" - assert awake_s <= 700, f"Panel war {awake_s} s wach - erwartet nur die ersten 10 min" + assert _times(panel, "full") == [0.0], ( + f"nur der Erstaufbau darf voll sein, tatsaechlich: {_times(panel, 'full')}") + assert _times(panel, "partial") == [] + assert _times(panel, "sleep") == [600.0], "genau einmal einschlafen, dann Ruhe" + assert _times(panel, "sleep-noop") == [], ( + "run_daemon() hat das Einschlafen nicht zurueckgemeldet - panel.sleep() wird " + "seitdem in jeder Runde erneut gerufen") + assert panel.awake_s <= 700, ( + f"Panel war {panel.awake_s} s wach - erwartet nur die ersten 10 min") def test_change_every_3_minutes_stays_awake_and_compensates_every_30_minutes(): - log, _ = _run_schedule(_changing_every(3 * 60), SIX_HOURS_S) + panel = _drive_daemon(_changing_every(3 * 60), SIX_HOURS_S) - fulls = _times(log, "full") - assert _times(log, "sleep") == [], "alle 3 min Verkehr - das Panel darf nicht schlafen" - assert len(_times(log, "partial")) > 100, "die Aenderungen muessen Teilaufbauten sein" + fulls = _times(panel, "full") + assert _times(panel, "sleep") == [], "alle 3 min Verkehr - das Panel darf nicht schlafen" + assert len(_times(panel, "partial")) > 100, "die Aenderungen muessen Teilaufbauten sein" assert len(fulls) >= 10, f"Ghosting-Ausgleich alle 30 min fehlt: {fulls}" gaps = [b - a for a, b in zip(fulls, fulls[1:])] assert all(gap >= display.FULL_REFRESH_INTERVAL_S for gap in gaps), ( @@ -578,11 +641,11 @@ def test_change_every_3_minutes_stays_awake_and_compensates_every_30_minutes(): def test_change_every_9_minutes_still_never_sleeps(): """9 Minuten liegen knapp unter SLEEP_AFTER_IDLE_S (10 min) - das Panel bleibt wach, die Teilaufbauten sammeln sich, und die 30-Minuten-Uhr gleicht sie aus.""" - log, _ = _run_schedule(_changing_every(9 * 60), SIX_HOURS_S) + panel = _drive_daemon(_changing_every(9 * 60), SIX_HOURS_S) - fulls = _times(log, "full") - assert _times(log, "sleep") == [] - assert _times(log, "partial"), "Aenderungen bei wachem Panel sind Teilaufbauten" + fulls = _times(panel, "full") + assert _times(panel, "sleep") == [] + assert _times(panel, "partial"), "Aenderungen bei wachem Panel sind Teilaufbauten" assert len(fulls) >= 5, f"Ghosting-Ausgleich fehlt: {fulls}" gaps = [b - a for a, b in zip(fulls, fulls[1:])] assert all(gap >= display.FULL_REFRESH_INTERVAL_S for gap in gaps) @@ -592,18 +655,24 @@ def test_change_every_11_minutes_sleeps_between_and_wakes_with_a_full_refresh(): """11 Minuten liegen knapp ueber SLEEP_AFTER_IDLE_S: das Panel schlaeft zwischen zwei Aenderungen ein, und der naechste Aufbau wird von Panel.draw_partial() selbst zu einem Vollaufbau aufgewertet - es darf also gar kein Teilaufbau mehr entstehen, und die - 30-Minuten-Uhr darf nie zusaetzlich feuern (jeder Aufbau IST schon ein Vollaufbau).""" - log, _ = _run_schedule(_changing_every(11 * 60), SIX_HOURS_S) + 30-Minuten-Uhr darf nie zusaetzlich feuern (jeder Aufbau IST schon ein Vollaufbau). - assert _times(log, "partial") == [], ( + Genau hier faellt auch auf, wenn run_daemon() die Rueckmeldung der Aufwertung + (was_full=upgraded) verwirft: dann zaehlen die Aufwertungen als Teilaufbauten und die + 30-Minuten-Uhr schiebt zusaetzliche Vollaufbauten ein (Fixrunde 5, N2).""" + panel = _drive_daemon(_changing_every(11 * 60), SIX_HOURS_S) + + assert _times(panel, "partial") == [], ( "nach dem Schlafen wertet das Panel jeden Aufbau selbst zu einem Vollaufbau auf") - assert len(_times(log, "wake-full")) == SIX_HOURS_S // (11 * 60), ( - f"pro Aenderung ein Aufbau erwartet: {_times(log, 'wake-full')}") - assert len(_times(log, "sleep")) >= 15, "zwischen den Aenderungen muss geschlafen werden" + assert len(_times(panel, "wake-full")) == SIX_HOURS_S // (11 * 60), ( + f"pro Aenderung ein Aufbau erwartet: {_times(panel, 'wake-full')}") + assert len(_times(panel, "sleep")) >= 15, "zwischen den Aenderungen muss geschlafen werden" + assert _times(panel, "sleep-noop") == [] # Nur der Erstaufbau ist ein regulaerer Vollaufbau: jeder weitere Aufbau IST wegen der # Aufwertung schon vollstaendig, ein zusaetzlicher "stale"-Vollaufbau waere ein Zeichen, # dass die Aufwertung nicht als Vollaufbau verbucht wurde (F4). - assert _times(log, "full") == [0.0], f"unerwartete Vollaufbauten: {_times(log, 'full')}" + assert _times(panel, "full") == [0.0], ( + f"unerwartete Vollaufbauten: {_times(panel, 'full')}") def test_burst_then_quiet_still_gets_its_ghosting_compensation(): @@ -615,27 +684,50 @@ def test_burst_then_quiet_still_gets_its_ghosting_compensation(): konnte danach nie mehr feuern, und genau dieses Bild stand die restlichen rund 5 h 50 min unveraendert da. Der faellige Ausgleich muss stattdessen nachgeholt werden - und das Panel danach wieder zur Ruhe kommen.""" - def content_at(t): + def state_at(t): # 30 Aenderungen im 5-Sekunden-Takt (t = 5 .. 150), danach unveraendert. step = min(int(t // display.POLL_INTERVAL_S), 30) - return ["MIGRATION", f"Ausgeliefert: {step}"] + return {"mode": "migration", "served": step, "recent": []} - log, awake_s = _run_schedule(content_at, SIX_HOURS_S, mode="migration") + panel = _drive_daemon(state_at, SIX_HOURS_S) - assert len(_times(log, "partial")) == 30, ( - f"das Buendel muss aus Teilaufbauten bestehen: {_times(log, 'partial')}") + assert len(_times(panel, "partial")) == 30, ( + f"das Buendel muss aus Teilaufbauten bestehen: {_times(panel, 'partial')}") - fulls = _times(log, "full") + fulls = _times(panel, "full") assert len(fulls) == 2, f"Erstaufbau + genau ein Ausgleich erwartet, war: {fulls}" ausgleich = fulls[1] assert display.FULL_REFRESH_INTERVAL_S <= ausgleich <= 35 * 60, ( f"Ghosting-Ausgleich faellt aus oder kommt zu spaet (t={ausgleich})") # ... und danach wirklich Ruhe: einmal nach dem Buendel, einmal nach dem Ausgleich. - assert len(_times(log, "sleep")) == 2, f"unruhiger Schlaf: {_times(log, 'sleep')}" - assert awake_s <= 30 * 60, ( - f"Panel war {awake_s} s von {SIX_HOURS_S} s wach - der Ausgleich darf nicht in " - "einen Wachzyklus ausarten") + assert len(_times(panel, "sleep")) == 2, f"unruhiger Schlaf: {_times(panel, 'sleep')}" + assert panel.awake_s <= 30 * 60, ( + f"Panel war {panel.awake_s} s von {SIX_HOURS_S} s wach - der Ausgleich darf nicht " + "in einen Wachzyklus ausarten") + + +def test_daemon_draws_fully_on_mode_change_and_partially_on_content_change(): + """Die Zuordnung Aktion -> Panel-Methode direkt an run_daemon() (Fixrunde 5, N2): + ruft der "full"-Zweig panel.draw_partial() statt panel.draw_full(), gaebe es auf einem + wachen Panel NIE wieder einen echten Vollaufbau - Moduswechsel wuerden nur teilweise + gezeichnet und der Ghosting-Ausgleich waere wirkungslos. Kurzer Verlauf, damit die + Zuordnung Zeitpunkt fuer Zeitpunkt nachvollziehbar ist.""" + def state_at(t): + if t < 60: + return {"mode": "recovery", "served": 0, "recent": []} + if t < 120: + return {"mode": "recovery", "served": 1, "recent": []} # nur Inhalt + return {"mode": "migration", "served": 1, "recent": []} # Moduswechsel + + panel = _drive_daemon(state_at, 180) + + assert panel.calls == [ + (0.0, "clear"), + (0.0, "full"), # Erstaufbau (Modus zum ersten Mal bekannt) + (60.0, "partial"), # nur der Inhalt hat sich geaendert + (120.0, "full"), # Moduswechsel - muss ein Vollaufbau sein + ], panel.calls def test_planner_counts_an_upgraded_partial_as_a_full_refresh(): From cc398ef57ab1a97c1a8aa9add487ee15feadb2e1 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sun, 13 Sep 2026 23:18:59 +0200 Subject: [PATCH 124/150] feat(station): Anzeige ausliefern, Migrationsprofil, display-Befehle Schritt 8 des Plans: die fertige E-Paper-Anzeige wird endlich ausgeliefert und verdrahtet, dazu zwei liegengebliebene Dinge. - deploy.sh/setup_pi.sh uebertragen display/ (mit Panel-Treibern und HERKUNFT.md) und ninlight-display.service; setup_pi.sh installiert und aktiviert die Unit und zieht python3-pil nach - ohne Pillow beendet sich der Anzeigedienst still mit 0, das faellt sonst nirgends auf. - setup_pi.sh legt auch das Profil fuer den Migrations-Zugangspunkt an (qconnex-firmware, von der alten Klyqa-Firmware fest vorgegeben, gleiche Adresse wie das Rettungsprofil). Ohne das scheiterte "migrate on" auf einem frisch eingerichteten Pi an need_profile. Bestehende Profile bleiben unangetastet. - "ninlight-station display test|clear|off"; der Hilfetext erklaert die Revisionsbestimmung, denn EPD_REVISION laesst sich nur am Testbild bestimmen. - "off" nimmt die Anzeige mit: erst Dienst stoppen, dann Panel leeren (sonst schreibt der Daemon binnen 5 s sein altes Bild zurueck) und beides vor ap_switch, das bei haengendem NetworkManager abbricht. Ein stehengebliebenes "MIGRATION" wuerde einen Betriebszustand behaupten, den es nicht mehr gibt. - test_host.sh sammelt tools/mkimage als Ganzes ein: die 19 Tests in test_nl_mkimage.py liefen in keiner Suite mehr mit, darunter die Wache fuer die Python-3.8-Randbedingung des Build-Containers. Ein neuer Test haelt die Regel fest, dass keine Testdatei mehr herausfallen kann. - Nebenbei: die Temp-Datei mit dem WLAN-Passwort wird wieder zuverlaessig aufgeraeumt, und leere Konfigurationswerte legen kein kaputtes Profil an. --- tools/mkimage/tests/test_suite_coverage.py | 86 ++++++++++ tools/station/deploy.sh | 33 +++- tools/station/ninlight-station | 57 ++++++- tools/station/setup_pi.sh | 189 ++++++++++++++++----- tools/station/tests/test_cli.sh | 177 ++++++++++++++++++- tools/station/tests/test_deploy.sh | 108 ++++++++++++ tools/station/tests/test_setup_pi.sh | 185 +++++++++++++++++++- tools/test_host.sh | 14 +- 8 files changed, 795 insertions(+), 54 deletions(-) create mode 100644 tools/mkimage/tests/test_suite_coverage.py diff --git a/tools/mkimage/tests/test_suite_coverage.py b/tools/mkimage/tests/test_suite_coverage.py new file mode 100644 index 0000000..11580d5 --- /dev/null +++ b/tools/mkimage/tests/test_suite_coverage.py @@ -0,0 +1,86 @@ +"""Wache dagegen, dass eine Testdatei aus allen Suiten herausfaellt. + +Genau das war schon einmal passiert: tools/test_host.sh rief pytest auf +tools/mkimage/tests und tools/station/tests auf - tools/mkimage/test_nl_mkimage.py +liegt aber DIREKT in tools/mkimage/ und lief damit in keiner Suite mehr mit. 19 Tests +waren still verschwunden, darunter test_stays_python38_compatible, die Wache fuer die +bindende 3.8-Randbedingung des Docker-Build-Containers (`str | None` in nl_mkimage.py +hatte den Build schon einmal gebrochen). + +Eine stumm ausgefallene Testdatei ist schlimmer als eine fehlende: die Suite bleibt +gruen und behauptet damit eine Deckung, die es nicht gibt. Deshalb wird hier nicht ein +einzelner Pfad festgenagelt, sondern die Regel selbst - JEDE test_*.py unter tools/ +muss von einem der Pfade erfasst sein, die tools/test_host.sh an pytest uebergibt. +Eine neue Testdatei an einer neuen Stelle faellt damit sofort auf, statt erst dann, +wenn jemand sie vermisst. +""" +import re +from pathlib import Path + +TOOLS_DIR = Path(__file__).resolve().parents[2] +REPO_ROOT = TOOLS_DIR.parent +TEST_HOST = TOOLS_DIR / "test_host.sh" + +# Verzeichnisse, die keine Tests DIESES Projekts enthalten: virtuelle Umgebungen und +# installierte Fremdpakete bringen eigene test_*.py mit, die hier nichts zu suchen haben. +IGNORED_PARTS = (".venv", "__pycache__", "site-packages", "node_modules", ".git") + + +def pytest_paths(): + """Die Pfade, die tools/test_host.sh an pytest uebergibt, als aufgeloeste Pfade.""" + text = TEST_HOST.read_text() + match = re.search(r"^\s*python3 -m pytest (.+?)(?:\s+-\S+)*\s*$", text, re.M) + assert match, "in tools/test_host.sh ist kein 'python3 -m pytest'-Aufruf zu finden" + paths = [] + for raw in match.group(1).split(): + arg = raw.strip('"') + if arg.startswith("-"): + continue + paths.append(Path(arg.replace("$ROOT", str(REPO_ROOT))).resolve()) + assert paths, "der pytest-Aufruf in tools/test_host.sh uebergibt keine Pfade" + return paths + + +def own_test_files(): + return sorted( + p.resolve() + for p in TOOLS_DIR.rglob("test_*.py") + if not any(part in IGNORED_PARTS for part in p.parts) + ) + + +def test_every_test_file_is_reached_by_test_host(): + roots = pytest_paths() + orphans = [] + for test_file in own_test_files(): + if not any(test_file == r or r in test_file.parents for r in roots): + orphans.append(test_file.relative_to(REPO_ROOT)) + assert not orphans, ( + "diese Testdateien laufen in keiner Suite mit - tools/test_host.sh sammelt sie " + "nicht ein: {}. Entweder den Pfad in test_host.sh erweitern oder die Datei " + "dorthin verschieben, wo sie eingesammelt wird.".format( + ", ".join(str(o) for o in orphans) + ) + ) + + +def test_python38_guard_is_among_them(): + """Die konkrete Datei, deren Ausfall den Build-Bruch damals durchgelassen haette.""" + guard = (TOOLS_DIR / "mkimage" / "test_nl_mkimage.py").resolve() + assert guard.exists(), "tools/mkimage/test_nl_mkimage.py fehlt" + roots = pytest_paths() + assert any(guard == r or r in guard.parents for r in roots), ( + "tools/mkimage/test_nl_mkimage.py wird von tools/test_host.sh nicht eingesammelt " + "- darin steht test_stays_python38_compatible, die einzige Wache gegen erneute " + "3.10-Syntax in nl_mkimage.py (der Docker-Build-Container hat Python 3.8.10)" + ) + + +def test_vermin_is_installed(): + """vermin fehlt -> harter Fehlschlag, kein Skip. + + Eine Wache, die sich beim Fehlen ihres Werkzeugs stillschweigend abschaltet, haette + den Build-Bruch von damals genauso durchgelassen wie gar kein Test. Nachinstallieren: + pip install vermin (oder pip3 install --user vermin). + """ + import vermin # noqa: F401 diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index 3c7b231..d5502ba 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -202,7 +202,14 @@ echo "== uebertragen ==" # ausgelieferte Verzeichnis. # -e: rsync soll fuer den Transport zum Pi denselben ssh wie oben benutzen. rsync -a -c --delete -e "$SSH_BIN" "$STAGE/" "$HOST:/tmp/ninlight-images/" -rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" +rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" \ + "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HOST:/tmp/" +# Das Anzeigeverzeichnis als Ganzes (render.py, display.py, epdconfig.py, die beiden +# uebernommenen Panel-Treiber und HERKUNFT.md - der Herkunftsnachweis gehoert mit auf +# den Pi, nicht nur ins Repo). --delete gilt hier nur fuer die fluechtige Ablage unter +# /tmp; __pycache__ bleibt draussen, sonst landen die .pyc dieses Rechners (andere +# Python-Version als auf dem Pi) im Zielverzeichnis und bleiben dort liegen. +rsync -a -c --delete --exclude=__pycache__ -e "$SSH_BIN" "$HERE/display/" "$HOST:/tmp/ninlight-display/" # Pfade als Variablen mit Default: so kann ein Test die Installation gegen # ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen lassen. "$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" "$MIGRATE_COMPLETE" <<'REMOTE' @@ -212,6 +219,7 @@ MIGRATE_COMPLETE=$2 ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} +DISPLAY_UNIT_PATH=${NINLIGHT_DISPLAY_UNIT:-/etc/systemd/system/ninlight-display.service} sudo install -d -m 0755 "$ROOT_DIR/images" if [ "$FULL_SYNC" -eq 1 ]; then @@ -333,7 +341,14 @@ sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" sudo install -m 0644 /tmp/products.py "$ROOT_DIR/products.py" sudo install -m 0755 /tmp/ninlight-station "$BIN_PATH" sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH" -rm -rf /tmp/ninlight-images /tmp/server.py /tmp/products.py /tmp/ninlight-station /tmp/ninlight-station.service +# Anzeige: Verzeichnis und Unit. --delete ist hier richtig (anders als bei den Images): +# display/ ist reiner Code aus dem Repo, es gibt keinen Bestand, der einen Deploy +# ueberleben muesste - eine im Repo geloeschte Datei muss auch hier verschwinden. +sudo install -d -m 0755 "$ROOT_DIR/display" +sudo rsync -a -c --delete /tmp/ninlight-display/ "$ROOT_DIR/display/" +sudo install -m 0644 /tmp/ninlight-display.service "$DISPLAY_UNIT_PATH" +rm -rf /tmp/ninlight-images /tmp/server.py /tmp/products.py /tmp/ninlight-station /tmp/ninlight-station.service \ + /tmp/ninlight-display /tmp/ninlight-display.service sudo systemctl daemon-reload if systemctl is-active --quiet ninlight-station; then if sudo systemctl restart ninlight-station; then @@ -345,6 +360,20 @@ if systemctl is-active --quiet ninlight-station; then else echo " Dienst laeuft nicht (Station ist aus)" fi +# Die Anzeige laeuft mit dem gerade ersetzten Code weiter, bis sie neu gestartet wird. +# Ein Fehlschlag ist hier aber ausdruecklich KEIN Deploy-Fehler (anders als oben beim +# Hauptdienst): die Anzeige ist nirgends Voraussetzung fuer eine Migration, und ein +# Deploy, der wegen des Panels rot wird, verleitet nur dazu, den echten Fehler oben zu +# uebersehen. +if systemctl is-active --quiet ninlight-display; then + if sudo systemctl restart ninlight-display; then + echo " Anzeigedienst neu gestartet" + else + echo " Warnung: Anzeigedienst lief, Neustart ist fehlgeschlagen - journalctl -u ninlight-display" >&2 + fi +else + echo " Anzeigedienst laeuft nicht" +fi REMOTE echo "fertig." diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 3e6628d..abf57ed 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -257,8 +257,41 @@ migrate_on() { echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." } +# Ruft display.py mit einem Schalter auf. EPD_REVISION steht in $CONF und wird von +# ". $CONF" nur als Shell-Variable gesetzt, nicht exportiert - "env VAR=..." reicht sie +# an den Python-Prozess durch. "sudo", weil /dev/spidev0.0 und die GPIO-Leitungen root +# gehoeren (der Anzeigedienst laeuft aus demselben Grund als root). +display_run() { + sudo env EPD_REVISION="$EPD_REVISION" python3 "$STATION_LIB/display/display.py" "$1" +} + +# Nimmt die Anzeige mit aus. Reihenfolge: erst den Dienst stoppen, dann das Panel leeren - +# umgekehrt zeichnet der noch laufende Daemon binnen 5 s sein altes Bild wieder darauf. +# +# Warum leeren und nicht "Station aus" stehen lassen: der Anzeigedienst erkennt den +# ausgeschalteten Zustand zwar selbst (systemctl is-active), aber ein laufender +# Anzeigedienst nach "off" bedeutet, dass "off" eben NICHT alles abgeschaltet hat. Ein +# leeres Panel behauptet dagegen gar nichts - und das ist bei einer Station, die Lampen +# ungefragt Firmware unterschieben kann, die einzig ungefaehrliche Richtung: ein +# stehengebliebenes "MIGRATION"-Bild wuerde einen Betriebszustand behaupten, den es nicht +# mehr gibt, waehrend ein leeres Panel hoechstens nach "aus" aussieht - was es ist. +display_off() { + sudo systemctl stop ninlight-display 2>/dev/null || true + # Kein Abbruch, wenn das Panel fehlt: display.py beendet sich dann selbst mit 0, und + # ein Fehler an der Anzeige darf das Ausschalten der Station nie aufhalten. + display_run --clear || echo "Warnung: Panel konnte nicht geleert werden - Anzeige pruefen" >&2 +} + full_off() { sudo systemctl stop ninlight-station 2>/dev/null || true + # VOR ap_switch, nicht danach: ap_switch "" bricht bei einem haengenden + # NetworkManager mit Fehler ab, und unter "set -e" kaeme danach nichts mehr. Genau + # dann - Zugangspunkt laesst sich nicht abschalten - waere die Anzeige sonst der + # letzte Rest von "off", der nie ausgefuehrt wird: Daemon laeuft weiter, Panel zeigt + # unveraendert "MIGRATION". Ein leeres Panel behauptet dagegen nichts; die Wahrheit + # ueber den noch offenen Zugangspunkt steht in ap_switch's Fehlermeldung, wo man sie + # liest, statt auf einem Panel, das man auch fuer aktuell halten koennte. + display_off ap_switch "" if nmcli -t -f NAME con show | grep -Fqx "$HOME_CONNECTION"; then if sudo nmcli con up "$HOME_CONNECTION" >/dev/null; then @@ -439,8 +472,27 @@ PY exit 0 } +display_cmd() { + case "${1:-}" in + test) display_run --test ;; + clear) display_run --clear ;; + off) display_off ;; + *) usage; exit 2 ;; + esac +} + usage() { - echo "Aufruf: ninlight-station on|recovery {on|off}|migrate {on [--listen]|off}|arm []|listen|off|status" >&2 + echo "Aufruf: ninlight-station on|recovery {on|off}|migrate {on [--listen]|off}|arm []|listen|display {test|clear|off}|off|status" >&2 + echo "" >&2 + echo " display test Testbild aufs Panel zeichnen. Das ist der erste Handgriff bei der" >&2 + echo " Hardware-Abnahme und die EINZIGE Moeglichkeit, die Panel-Revision" >&2 + echo " zu bestimmen: die steht am Geraet nirgends. Bleibt das Bild leer," >&2 + echo " verzerrt oder verschoben, ist EPD_REVISION in" >&2 + echo " /etc/ninlight-station.conf falsch - auf die andere Revision" >&2 + echo " umstellen (V2 statt V3 oder umgekehrt), das Panel kurz" >&2 + echo " stromlos machen und erneut 'display test'." >&2 + echo " display clear Panel leeren." >&2 + echo " display off Anzeigedienst stoppen und das Panel leeren (macht 'off' mit)." >&2 } case "${1:-}" in @@ -467,6 +519,9 @@ case "${1:-}" in listen) listen_cmd ;; + display) + display_cmd "${2:-}" + ;; off) full_off ;; diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index e9903dd..fbab5d4 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -77,8 +77,31 @@ fi rm -f "$TEMPLATE" REMOTE -echo "== Server, CLI und Unit installieren ==" -"$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:$STAGE_DIR/" +echo "== Pakete ==" +# Pillow kommt auf dem Pi bewusst aus dem Distributionspaket (python3-pil), nicht per +# pip: tools/station/ laeuft dort unter dem System-Python 3.13, und Debian 13 laesst ein +# "pip install" ins System-Python gar nicht mehr zu (PEP 668). Ohne Pillow zeichnet die +# Anzeige nicht - display.py faengt den ImportError ab und beendet sich still mit 0 +# (Absicht, damit ein fehlendes Panel nie eine Migration verhindert). Genau deshalb muss +# das Paket HIER kommen: sonst faellt es nirgends auf. +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e +if python3 -c 'import PIL' 2>/dev/null; then + echo " python3-pil ist bereits vorhanden" +else + sudo apt-get install -y python3-pil + echo " python3-pil installiert" +fi +REMOTE + +echo "== Server, CLI und Units installieren ==" +"$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" \ + "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HOST:$STAGE_DIR/" +# Das Anzeigeverzeichnis als Ganzes: render.py, display.py, epdconfig.py, die beiden +# uebernommenen Panel-Treiber und HERKUNFT.md (der Herkunftsnachweis gehoert mit auf den +# Pi). "scp -r" statt rsync, weil setup_pi.sh - anders als deploy.sh - ausdruecklich nur +# ssh/scp voraussetzt; __pycache__ wird auf der Gegenseite weggeraeumt (unten). +"$SCP_BIN" -q -r "$HERE/display" "$HOST:$STAGE_DIR/ninlight-display" # Pfade als Variablen mit Default (wie in deploy.sh): so kann ein Test die # Installation gegen ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen # lassen. @@ -88,17 +111,40 @@ STAGE_DIR=${NINLIGHT_STAGE_DIR:-/tmp} ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} -sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images" +DISPLAY_UNIT_PATH=${NINLIGHT_DISPLAY_UNIT:-/etc/systemd/system/ninlight-display.service} +sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images" "$ROOT_DIR/display" sudo install -m 0755 "$STAGE_DIR/server.py" "$ROOT_DIR/server.py" sudo install -m 0644 "$STAGE_DIR/products.py" "$ROOT_DIR/products.py" sudo install -m 0755 "$STAGE_DIR/ninlight-station" "$BIN_PATH" sudo install -m 0644 "$STAGE_DIR/ninlight-station.service" "$UNIT_PATH" -rm -f "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" "$STAGE_DIR/ninlight-station.service" +# Anzeige: display.py ist ausfuehrbar (die Unit ruft es zwar ueber /usr/bin/python3 auf, +# "ninlight-station display test" aber auch von Hand), der Rest ist importierter Code. +rm -rf "$STAGE_DIR/ninlight-display/__pycache__" +for f in "$STAGE_DIR"/ninlight-display/*; do + [ -f "$f" ] || continue + case "$(basename "$f")" in + display.py) sudo install -m 0755 "$f" "$ROOT_DIR/display/$(basename "$f")" ;; + *) sudo install -m 0644 "$f" "$ROOT_DIR/display/$(basename "$f")" ;; + esac +done +sudo install -m 0644 "$STAGE_DIR/ninlight-display.service" "$DISPLAY_UNIT_PATH" +rm -rf "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" \ + "$STAGE_DIR/ninlight-station.service" "$STAGE_DIR/ninlight-display.service" \ + "$STAGE_DIR/ninlight-display" sudo systemctl daemon-reload -echo " installiert" +# Die Anzeige wird aktiviert (startet also beim Booten mit) UND jetzt gestartet - anders +# als die Station selbst, die absichtlich aus bleibt: die Anzeige sendet nichts, sie +# zeigt nur an, und "Station aus" ist genau das, was nach dem Einrichten auf dem Panel +# stehen soll. Fehlt Panel oder Pillow, beendet sich der Dienst still mit 0 (siehe +# display.py) - deshalb "|| true": ein fehlendes Panel darf setup_pi.sh nicht abbrechen. +sudo systemctl enable ninlight-display.service >/dev/null 2>&1 || \ + echo " Warnung: 'systemctl enable ninlight-display' fehlgeschlagen" >&2 +sudo systemctl restart ninlight-display.service >/dev/null 2>&1 || \ + echo " Warnung: 'systemctl restart ninlight-display' fehlgeschlagen" >&2 +echo " installiert (Anzeigedienst aktiviert)" REMOTE -echo "== NetworkManager-Profil ==" +echo "== NetworkManager-Profile ==" # Zwei Dinge, die man an einer echten Lampenstation nicht falsch machen darf: # - /etc/ninlight-station.conf ist 0600 root:root (das Passwort soll nicht # jeder lesen koennen). Der SSH-Benutzer ist aber nicht root, direktes @@ -116,47 +162,91 @@ set -e CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf} NM_DIR=${NINLIGHT_NM_DIR:-/etc/NetworkManager/system-connections} eval "$(sudo cat "$CONF")" -CONN_FILE="$NM_DIR/${AP_CONNECTION}.nmconnection" -if sudo test -f "$CONN_FILE"; then - UUID=$(sudo sed -n 's/^uuid=//p' "$CONN_FILE" | head -n1) - ACTION="aktualisiert" -else - # /proc/sys/kernel/random/uuid gibt es nur unter Linux (dem Pi) - der Fallback - # auf uuidgen ist es, was einen Testlauf gegen den lokalen Stub (macOS) ueberhaupt - # ermoeglicht, aendert aber nichts am Verhalten auf dem Pi selbst. - UUID=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen | tr '[:upper:]' '[:lower:]') - ACTION="angelegt" -fi -TMP=$(mktemp) -trap 'rm -f "$TMP"' EXIT -{ - printf '%s\n' '[connection]' - printf 'id=%s\n' "$AP_CONNECTION" - printf 'uuid=%s\n' "$UUID" - printf '%s\n' 'type=wifi' - printf '%s\n' 'interface-name=wlan0' - printf '%s\n' 'autoconnect=false' - printf '\n%s\n' '[wifi]' - printf '%s\n' 'mode=ap' - printf 'ssid=%s\n' "$SSID" - printf '%s\n' 'band=bg' - printf '%s\n' 'channel=6' - printf '\n%s\n' '[wifi-security]' - printf '%s\n' 'key-mgmt=wpa-psk' - printf '%s\n' 'proto=rsn' - printf '%s\n' 'pairwise=ccmp;' - printf '%s\n' 'group=ccmp;' - printf 'psk=%s\n' "$PSK" - printf '\n%s\n' '[ipv4]' - printf '%s\n' 'method=shared' - printf '%s\n' 'address1=192.168.8.1/24' - printf '\n%s\n' '[ipv6]' - printf '%s\n' 'method=disabled' -} >"$TMP" -sudo install -o root -g root -m 0600 "$TMP" "$CONN_FILE" -rm -f "$TMP" -sudo nmcli connection load "$CONN_FILE" >/dev/null -echo " Profil $AP_CONNECTION $ACTION" + +# Die Keyfiles werden erst in einer Temp-Datei aufgebaut und dann per "install" +# an ihren Platz gelegt - dazwischen liegt das WLAN-Passwort in einer Datei, die +# noch nicht 0600 root:root ist. Ein Abbruch (set -e) genau dort darf sie nicht +# liegen lassen. Ein EXIT-Trap in write_profile() selbst waere dafuer untauglich: +# die Funktion laeuft zweimal, der zweite trap ueberschriebe den ersten, und +# "$tmp" ist "local" - beim Auswerten des Traps am Skriptende gaebe es die +# Variable gar nicht mehr. Deshalb ein gemeinsames Verzeichnis mit genau einem +# Trap darum herum (0700, mktemp -d legt es so an). +PROFILE_TMPDIR=$(mktemp -d) +trap 'rm -rf "$PROFILE_TMPDIR"' EXIT + +# $1 = Profilname, $2 = SSID, $3 = PSK, $4 = "update" (bestehendes Profil neu schreiben, +# UUID behalten) oder "keep" (bestehendes Profil unangetastet lassen). +# +# Beide Profile bekommen dieselbe Adresse 192.168.8.1/24 auf wlan0 - genau deshalb +# schliessen sie sich gegenseitig aus, und genau deshalb prueft ninlight-station's +# ap_switch() nach jedem Umschalten nach, dass wirklich nur eines aktiv ist. +write_profile() { + local id=$1 ssid=$2 psk=$3 mode=$4 + # Ein leerer Profilname wuerde eine Datei ".nmconnection" anlegen, eine leere SSID + # oder ein leeres PSK ein Profil, das NetworkManager beim Hochfahren stumm ablehnt - + # in beiden Faellen faellt es erst auf, wenn jemand mit einer toten Lampe vor der + # Station steht. Der Konfigurationsschritt oben ergaenzt fehlende Schluessel aus der + # Vorlage; leer sein koennen sie trotzdem (von Hand geleert). + if [ -z "$id" ] || [ -z "$ssid" ] || [ -z "$psk" ]; then + echo "Fehler: Profilname, SSID oder PSK ist leer - $CONF pruefen (Profil '$id')" >&2 + return 1 + fi + local conn_file="$NM_DIR/${id}.nmconnection" + local uuid action tmp + if sudo test -f "$conn_file"; then + if [ "$mode" = "keep" ]; then + # Der Migrations-AP ist auf manchen Pis von Hand angelegt worden (SSID/PSK sind + # von der alten Klyqa-Firmware fest vorgegeben, es gibt also nichts zu variieren). + # Ein bestehendes Profil wird deshalb nicht ueberschrieben - es koennte + # handjustierte Felder (Kanal, Band, Regulierungsdomaene) tragen, die dieses + # Skript gar nicht kennt. + echo " Profil $id existiert bereits - unveraendert gelassen" + return 0 + fi + uuid=$(sudo sed -n 's/^uuid=//p' "$conn_file" | head -n1) + action="aktualisiert" + else + # /proc/sys/kernel/random/uuid gibt es nur unter Linux (dem Pi) - der Fallback + # auf uuidgen ist es, was einen Testlauf gegen den lokalen Stub (macOS) ueberhaupt + # ermoeglicht, aendert aber nichts am Verhalten auf dem Pi selbst. + uuid=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen | tr '[:upper:]' '[:lower:]') + action="angelegt" + fi + tmp=$(mktemp "$PROFILE_TMPDIR/profile.XXXXXX") + { + printf '%s\n' '[connection]' + printf 'id=%s\n' "$id" + printf 'uuid=%s\n' "$uuid" + printf '%s\n' 'type=wifi' + printf '%s\n' 'interface-name=wlan0' + printf '%s\n' 'autoconnect=false' + printf '\n%s\n' '[wifi]' + printf '%s\n' 'mode=ap' + printf 'ssid=%s\n' "$ssid" + printf '%s\n' 'band=bg' + printf '%s\n' 'channel=6' + printf '\n%s\n' '[wifi-security]' + printf '%s\n' 'key-mgmt=wpa-psk' + printf '%s\n' 'proto=rsn' + printf '%s\n' 'pairwise=ccmp;' + printf '%s\n' 'group=ccmp;' + printf 'psk=%s\n' "$psk" + printf '\n%s\n' '[ipv4]' + printf '%s\n' 'method=shared' + printf '%s\n' 'address1=192.168.8.1/24' + printf '\n%s\n' '[ipv6]' + printf '%s\n' 'method=disabled' + } >"$tmp" + sudo install -o root -g root -m 0600 "$tmp" "$conn_file" + rm -f "$tmp" + sudo nmcli connection load "$conn_file" >/dev/null + echo " Profil $id $action" +} + +write_profile "$AP_CONNECTION" "$SSID" "$PSK" update +# Ohne dieses zweite Profil scheitert "ninlight-station migrate on" auf einem frisch +# eingerichteten Pi an need_profile "$MIGRATE_AP_CONNECTION". +write_profile "$MIGRATE_AP_CONNECTION" "$MIGRATE_SSID" "$MIGRATE_PSK" keep REMOTE cat <<'EOF' @@ -164,6 +254,11 @@ cat <<'EOF' Fertig. Die Station ist absichtlich noch AUS. Naechste Schritte: + ssh ninlight-station display test + Testbild aufs E-Paper - bleibt es leer oder + verzerrt, steht EPD_REVISION in + /etc/ninlight-station.conf auf der falschen + Panel-Revision (V2 statt V3 oder umgekehrt) tools/station/deploy.sh Images ausliefern ssh ninlight-station on Station einschalten ssh ninlight-station off wieder ausschalten (nicht vergessen) diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index 873f764..fbd70e6 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -164,9 +164,31 @@ EOF setup_sandbox() { local sandbox=$1 unitfile=${2:-$REAL_UNIT} rm -rf "$sandbox" - mkdir -p "$sandbox/run" "$sandbox/images/migrate" "$sandbox/bin" "$sandbox/nmstate" + mkdir -p "$sandbox/run" "$sandbox/images/migrate" "$sandbox/bin" "$sandbox/nmstate" \ + "$sandbox/lib/display" echo "dummy" >"$sandbox/images/ninlight_foo_ota.img" + # NINLIGHT_STATION_LIB zeigt auf diese Sandbox statt auf tools/station selbst. + # products.py wird gebraucht (arm prueft das Ziel dagegen) und deshalb echt kopiert; + # display/display.py ist ein Rekorder, der seine Argumente in DIESELBE cmd.log + # schreibt wie die nmcli-/systemctl-Stubs. Nur so laesst sich die REIHENFOLGE + # "erst Dienst stoppen, dann Panel leeren" ueberhaupt pruefen - mit zwei getrennten + # Logs waere sie nicht rekonstruierbar. + # + # Der Rekorder ersetzt das echte display.py absichtlich: geprueft wird hier, WAS + # ninlight-station aufruft (Pfad, Schalter, Reihenfolge, Umgang mit dem Rueckgabe- + # wert), nicht was display.py daraus macht - das deckt tests/test_render.py ab. + cp "$STATION_DIR/products.py" "$sandbox/lib/products.py" + cat >"$sandbox/lib/display/display.py" <"))) +sys.exit(int(os.environ.get("FAKE_DISPLAY_EXIT", "0"))) +EOF + cat >"$sandbox/station.conf" <"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" display test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fq "STUB display.py --test" "$SBX/cmd.log" && pass "display.py wurde mit --test aufgerufen" || fail "kein '--test'-Aufruf im Log: $(cat "$SBX/cmd.log")" +# Der Testbild-Aufruf ist die einzige Moeglichkeit, die Panel-Revision zu bestimmen - +# er ist wertlos, wenn er nicht die Revision aus der Konfiguration durchreicht: +# EPD_REVISION wird per ". $CONF" nur als Shell-Variable gesetzt, nicht exportiert. +grep -Fq "STUB display.py --test (EPD_REVISION=V3)" "$SBX/cmd.log" && pass "EPD_REVISION aus der Konfiguration erreicht den Python-Prozess" || fail "EPD_REVISION kam nicht durch: $(cat "$SBX/cmd.log")" +# Nicht gleichzeitig den Anzeigedienst umbringen: 'display test' ist ein Handgriff bei +# der Abnahme, kein Ausschalten. +grep -Fq "systemctl stop ninlight-display" "$SBX/cmd.log" && fail "'display test' stoppt den Anzeigedienst - das soll nur 'display off'/'off'" || pass "'display test' laesst den Anzeigedienst in Ruhe" + +echo +echo "=== Fall D2: 'display clear' ruft display.py mit --clear auf ===" +SBX="$TMPROOT/tD2" +setup_sandbox "$SBX" +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" display clear 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fq "STUB display.py --clear" "$SBX/cmd.log" && pass "display.py wurde mit --clear aufgerufen" || fail "kein '--clear'-Aufruf im Log: $(cat "$SBX/cmd.log")" +grep -Fq "STUB display.py --test" "$SBX/cmd.log" && fail "'display clear' hat --test aufgerufen" || pass "kein --test bei 'display clear'" + +echo +echo "=== Fall D3 (Task 6b, Punkt 4): 'display off' stoppt den Dienst VOR dem Leeren des Panels ===" +# Umgekehrte Reihenfolge waere wirkungslos: der Daemon fragt alle 5 s nach und +# schriebe sein altes Bild binnen dieser Zeit wieder aufs Panel. +SBX="$TMPROOT/tD3" +setup_sandbox "$SBX" +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" display off 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +STOP_LINE=$(grep -n "STUB systemctl stop ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +CLEAR_LINE=$(grep -n "STUB display.py --clear" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +if [ -n "$STOP_LINE" ] && [ -n "$CLEAR_LINE" ] && [ "$STOP_LINE" -lt "$CLEAR_LINE" ]; then + pass "Anzeigedienst wird gestoppt, BEVOR das Panel geleert wird (stop=$STOP_LINE, clear=$CLEAR_LINE)" +else + fail "Reihenfolge falsch oder Aufruf fehlt (stop=$STOP_LINE, clear=$CLEAR_LINE): $(cat "$SBX/cmd.log")" +fi + +echo +echo "=== Fall D4 (Task 6b, Punkt 4): 'off' nimmt die Anzeige mit - Dienst gestoppt, Panel geleert ===" +# Ohne das behauptet das stehengebliebene Bild einen Betriebszustand ("MIGRATION"), +# den es nicht mehr gibt - bei einer Station, die Lampen ungefragt Firmware +# unterschieben kann, die gefaehrliche Richtung. +SBX="$TMPROOT/tD4" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" migrate on >/dev/null 2>&1 +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" off 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fq "STUB systemctl stop ninlight-display" "$SBX/cmd.log" && pass "'off' stoppt den Anzeigedienst" || fail "'off' stoppt den Anzeigedienst nicht: $(cat "$SBX/cmd.log")" +grep -Fq "STUB display.py --clear" "$SBX/cmd.log" && pass "'off' leert das Panel" || fail "'off' leert das Panel nicht: $(cat "$SBX/cmd.log")" +STOP_LINE=$(grep -n "STUB systemctl stop ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +CLEAR_LINE=$(grep -n "STUB display.py --clear" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +if [ -n "$STOP_LINE" ] && [ -n "$CLEAR_LINE" ] && [ "$STOP_LINE" -lt "$CLEAR_LINE" ]; then + pass "auch bei 'off': erst stoppen, dann leeren" +else + fail "Reihenfolge bei 'off' falsch (stop=$STOP_LINE, clear=$CLEAR_LINE): $(cat "$SBX/cmd.log")" +fi +# Das Panel muss geleert sein, BEVOR ap_switch das Ergebnis von "off" verkuenden kann - +# und vor allem, bevor ap_switch bei haengendem NetworkManager abbricht (Fall D6). +UP_LINE=$(grep -n "nmcli con up home-wifi" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +if [ -n "$UP_LINE" ] && [ "$CLEAR_LINE" -lt "$UP_LINE" ]; then + pass "Panel ist geleert, bevor wlan0 ins Heim-WLAN zurueckgeschaltet wird" +else + fail "Panel wurde erst nach dem Zurueckschalten geleert (clear=$CLEAR_LINE, up=$UP_LINE)" +fi + +echo +echo "=== Fall D5: ein Fehler beim Leeren des Panels haelt 'off' nicht auf ===" +# Ein fehlendes/kaputtes Panel darf das Ausschalten der Station nie blockieren - +# sonst bliebe ausgerechnet der Zugangspunkt an, weil die Anzeige klemmt. +SBX="$TMPROOT/tD5" +setup_sandbox "$SBX" +: >"$SBX/cmd.log" +set +e +OUT=$(FAKE_DISPLAY_EXIT=1 run_cli "$SBX" off 2>"$SBX/stderr.log") +RC=$? +set -e +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlgeschlagenem Leeren" || fail "Exit-Code $RC, stderr: $ERR" +echo "$ERR" | grep -q "Warnung" && pass "Warnung ueber das nicht geleerte Panel steht auf stderr" || fail "keine Warnung auf stderr: $ERR" +grep -Fq "nmcli con up home-wifi" "$SBX/cmd.log" && pass "'off' hat trotzdem bis zum Zurueckschalten ins Heim-WLAN durchgezogen" || fail "'off' ist am Anzeigefehler haengengeblieben: $(cat "$SBX/cmd.log")" + +echo +echo "=== Fall D6: haengender NetworkManager - das Panel wird trotzdem noch geleert ===" +# ap_switch "" bricht hier mit Fehler ab; unter "set -e" laeuft danach nichts mehr. +# Genau deshalb steht display_off VOR ap_switch: sonst bliebe ein "MIGRATION"-Bild +# stehen, waehrend der Zugangspunkt noch sendet und niemand mehr etwas abschaltet. +SBX="$TMPROOT/tD6" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +run_cli "$SBX" migrate on >/dev/null 2>&1 +: >"$SBX/cmd.log" +set +e +OUT=$(FAKE_NMCLI_STUCK=1 run_cli "$SBX" off 2>"$SBX/stderr.log") +RC=$? +set -e +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 (der Zugangspunkt liess sich nicht abschalten)" || fail "Exit-Code 0, obwohl der Zugangspunkt noch aktiv ist: $OUT" +grep -Fq "STUB display.py --clear" "$SBX/cmd.log" && pass "Panel wurde trotz haengendem NetworkManager geleert" || fail "Panel blieb stehen, waehrend der Zugangspunkt noch sendet: $(cat "$SBX/cmd.log")" + +echo +echo "=== Fall D7: 'display' ohne/mit unbekanntem Unterbefehl -> Hilfetext, Exit 2 ===" +SBX="$TMPROOT/tD7" +setup_sandbox "$SBX" +# Anlegen, bevor geprueft wird: eine fehlende Logdatei wuerde die "kein Aufruf"-Zusage +# unten sonst durch ein grep-Versagen erfuellen statt durch das Verhalten des Skripts. +: >"$SBX/cmd.log" +for ARG in "" "quatsch"; do + set +e + # shellcheck disable=SC2086 + OUT=$(run_cli "$SBX" display $ARG 2>&1) + RC=$? + set -e + [ "$RC" -eq 2 ] && pass "Exit-Code 2 fuer 'display ${ARG:-}'" || fail "Exit-Code $RC fuer 'display ${ARG:-}': $OUT" + echo "$OUT" | grep -q "display {test|clear|off}" && pass "Hilfetext nennt die Unterbefehle ('display ${ARG:-}')" || fail "Hilfetext fehlt: $OUT" +done +grep -Fq "STUB display.py" "$SBX/cmd.log" && fail "ein unbekannter Unterbefehl hat display.py trotzdem aufgerufen" || pass "kein display.py-Aufruf bei unbekanntem Unterbefehl" + +echo +echo "=== Fall D8 (Task 6b, Punkt 3): der Hilfetext erklaert die Revisionsbestimmung ===" +# Die Panel-Revision steht am Geraet nirgends; 'display test' ist die einzige +# Moeglichkeit, sie zu bestimmen, und das muss dort stehen, wo man beim ersten +# Handgriff der Hardware-Abnahme hinsieht. +SBX="$TMPROOT/tD8" +setup_sandbox "$SBX" +set +e +HELP=$(run_cli "$SBX" quatsch 2>&1) +set -e +echo "$HELP" | grep -q "EPD_REVISION" && pass "Hilfetext nennt EPD_REVISION" || fail "Hilfetext nennt EPD_REVISION nicht: $HELP" +echo "$HELP" | grep -q "ninlight-station.conf" && pass "Hilfetext nennt die Datei, in der die Revision steht" || fail "Hilfetext nennt /etc/ninlight-station.conf nicht: $HELP" +echo "$HELP" | grep -qE "V2|V3" && pass "Hilfetext nennt die beiden moeglichen Revisionen" || fail "Hilfetext nennt V2/V3 nicht: $HELP" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index 6e80ae4..9a66534 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -51,6 +51,17 @@ setup_sandbox() { echo "# dummy" >"$sandbox/tools/station/products.py" echo "# dummy" >"$sandbox/tools/station/ninlight-station" echo "# dummy" >"$sandbox/tools/station/ninlight-station.service" + echo "# dummy" >"$sandbox/tools/station/ninlight-display.service" + # display/ wird ECHT kopiert, nicht durch Attrappen ersetzt: die Zusage aus Task 6b + # ist, dass die beiden uebernommenen Panel-Treiber UND der Herkunftsnachweis + # HERKUNFT.md mit auf den Pi gehen. Mit selbst erfundenen Dateinamen im Sandbox-Repo + # wuerde der Test genau das nicht mehr zeigen - er pruefte nur noch sich selbst. + cp -R "$STATION_DIR/display" "$sandbox/tools/station/display" + rm -rf "$sandbox/tools/station/display/__pycache__" + # Ein __pycache__ mit .pyc dieses Rechners (andere Python-Version als der Pi) - es + # darf nicht mit uebertragen werden. + mkdir -p "$sandbox/tools/station/display/__pycache__" + echo "pyc-muell" >"$sandbox/tools/station/display/__pycache__/render.cpython-999.pyc" # Fake nl_mkimage.py --verify: verhaelt sich wie das echte Tool (eine Zeile # " " auf stdout bei Erfolg, Exit 0; eine Zeile auf @@ -111,9 +122,22 @@ echo "STUB sudo \$*" >>"$log" exec "\$@" EOF + # Der Stub unterscheidet nach Unit: deploy.sh behandelt den Anzeigedienst + # ausdruecklich anders als den Hauptdienst (ein fehlgeschlagener Neustart der + # Anzeige ist KEIN Deploy-Fehler). Mit einem Schalter fuer beide Units zusammen + # liesse sich genau dieser Unterschied nicht pruefen. cat >"$bindir/systemctl" <>"$log" +case "\$*" in + *ninlight-display*) + case "\$1" in + is-active) exit "\${FAKE_DISPLAY_ACTIVE_EXIT:-1}" ;; + restart) exit "\${FAKE_DISPLAY_RESTART_EXIT:-0}" ;; + *) exit 0 ;; + esac + ;; +esac case "\$1" in is-active) exit "\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-1}" ;; restart) exit "\${FAKE_SYSTEMCTL_RESTART_EXIT:-0}" ;; @@ -146,6 +170,7 @@ run_deploy() { NINLIGHT_STATION_ROOT="$SANDBOX_STATE/opt/ninlight-station" \ NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ + NINLIGHT_DISPLAY_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-display.service" \ NINLIGHT_SIGN_DIR="$sign_dir" \ "$sandbox/tools/station/deploy.sh" "$@" } @@ -948,6 +973,89 @@ for label in truncated empty array null string null-entry; do fi done +echo +echo "=== Szenario 22 (Task 6b, Punkt 1): deploy.sh liefert Anzeigeverzeichnis und Anzeige-Unit mit aus ===" +SANDBOX="$TMPROOT/s22"; STUBBIN="$TMPROOT/bin22"; LOG="$TMPROOT/s22.log"; SANDBOX_STATE="$TMPROOT/s22-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +# Bestand auf dem "Pi": eine Datei, die es im Repo nicht mehr gibt. display/ ist reiner +# Code aus dem Repo - was dort verschwindet, muss auch hier verschwinden, sonst laedt +# ein zurueckgebliebenes altes Modul weiter mit. +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/display" +echo "alt" >"$SANDBOX_STATE/opt/ninlight-station/display/veraltet.py" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +DISP="$SANDBOX_STATE/opt/ninlight-station/display" +for F in display.py render.py epdconfig.py epd2in13_V2.py epd2in13_V3.py HERKUNFT.md; do + [ -f "$DISP/$F" ] && pass "display/$F ausgeliefert" || fail "display/$F fehlt unter $DISP" +done +[ -f "$SANDBOX_STATE/etc/systemd/system/ninlight-display.service" ] && pass "ninlight-display.service ausgeliefert" || fail "Anzeige-Unit fehlt - sie waere weiter toter Text im Repo" +[ ! -f "$DISP/veraltet.py" ] && pass "im Repo geloeschte Datei verschwindet auch auf dem Pi (--delete)" || fail "veraltet.py blieb im Zielverzeichnis liegen" +[ ! -d "$DISP/__pycache__" ] && pass "kein __pycache__ dieses Rechners auf dem Pi gelandet" || fail "__pycache__ wurde mit uebertragen: $(ls "$DISP/__pycache__")" +# Die Anzeige lief nicht (Stub-Default) - dann wird sie auch nicht neu gestartet, aber +# der Deploy sagt es. +echo "$OUT" | grep -q "Anzeigedienst laeuft nicht" && pass "Deploy meldet, dass der Anzeigedienst nicht laeuft" || fail "Ausgabe erwaehnt den Anzeigedienst nicht: $OUT" +# Die fluechtige Ablage wird wieder aufgeraeumt (sie liegt, wie bei server.py & Co., +# im echten /tmp - deploy.sh's REMOTE-Block kennt dafuer keine Sandbox-Variable). +[ ! -d /tmp/ninlight-display ] && pass "die fluechtige Ablage /tmp/ninlight-display wurde aufgeraeumt" || fail "/tmp/ninlight-display blieb liegen" + +echo +echo "=== Szenario 23: laeuft die Anzeige, wird sie nach dem Deploy neu gestartet ===" +# Ohne Neustart liefe sie mit dem gerade ersetzten Code weiter. +SANDBOX="$TMPROOT/s23"; STUBBIN="$TMPROOT/bin23"; LOG="$TMPROOT/s23.log"; SANDBOX_STATE="$TMPROOT/s23-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(FAKE_DISPLAY_ACTIVE_EXIT=0 run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fq "systemctl restart ninlight-display" "$LOG" && pass "Anzeigedienst wurde neu gestartet" || fail "kein 'systemctl restart ninlight-display' im Log: $(cat "$LOG")" +echo "$OUT" | grep -q "Anzeigedienst neu gestartet" && pass "Ausgabe meldet den Neustart" || fail "Ausgabe meldet den Neustart nicht: $OUT" + +echo +echo "=== Szenario 24: ein fehlgeschlagener Neustart der ANZEIGE ist kein Deploy-Fehler ===" +# Die Anzeige ist nirgends Voraussetzung fuer eine Migration. Ein Deploy, der wegen des +# Panels rot wird, verleitet nur dazu, einen echten Fehler zu uebersehen. Beim +# HAUPTdienst ist es umgekehrt - das prueft das Szenario darunter zur Abgrenzung. +SANDBOX="$TMPROOT/s24"; STUBBIN="$TMPROOT/bin24"; LOG="$TMPROOT/s24.log"; SANDBOX_STATE="$TMPROOT/s24-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(FAKE_DISPLAY_ACTIVE_EXIT=0 FAKE_DISPLAY_RESTART_EXIT=1 run_deploy "$SANDBOX" pi-test 2>"$TMPROOT/s24.err") +RC=$? +set -e +ERR=$(cat "$TMPROOT/s24.err") +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlgeschlagenem Anzeige-Neustart" || fail "Exit-Code $RC - der Deploy wurde wegen der Anzeige rot: $ERR" +echo "$ERR" | grep -q "Warnung" && pass "Warnung (keine Fehlermeldung) auf stderr" || fail "keine Warnung auf stderr: $ERR" +[ -f "$SANDBOX_STATE/opt/ninlight-station/server.py" ] && pass "der Deploy selbst lief vollstaendig durch" || fail "server.py fehlt - der Deploy brach ab" + +echo +echo "=== Szenario 25 (Abgrenzung): ein fehlgeschlagener Neustart des HAUPTdienstes ist sehr wohl ein Deploy-Fehler ===" +SANDBOX="$TMPROOT/s25"; STUBBIN="$TMPROOT/bin25"; LOG="$TMPROOT/s25.log"; SANDBOX_STATE="$TMPROOT/s25-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(FAKE_SYSTEMCTL_ACTIVE_EXIT=0 FAKE_SYSTEMCTL_RESTART_EXIT=1 run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 beim Hauptdienst" || fail "Exit-Code 0 - ein toter Hauptdienst ginge unbemerkt durch: $OUT" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index 1c18547..2d9ec1b 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -55,12 +55,41 @@ echo "STUB scp \$*" >>"$log" args=() for a in "\$@"; do case "\$a" in + # "-r" muss durchgereicht werden (als cp -R): setup_pi.sh uebertraegt das + # Anzeigeverzeichnis als Ganzes, und ein Stub, der das Flag verschluckt, laesst + # "cp" an einem Verzeichnis scheitern - der Test wuerde dann den Stub anzeigen, + # nicht das Skript. Alle anderen Optionen (-q) bleiben weiterhin draussen. + -r|-R) args+=("-R") ;; -*) continue ;; *:*) args+=("\${a#*:}") ;; *) args+=("\$a") ;; esac done exec cp "\${args[@]}" +EOF + + # apt-get gibt es auf dem Entwicklungsrechner nicht - und selbst auf einem Debian + # duerfte ein Test es nie wirklich aufrufen. Der Stub protokolliert nur. + cat >"$bindir/apt-get" <>"$log" +exit "\${FAKE_APT_EXIT:-0}" +EOF + + # Steht fuer das python3 DES PI, nicht fuer das des Testrechners: nur die + # Pillow-Probe wird beantwortet (steuerbar per FAKE_PIL_MISSING - auf diesem Rechner + # ist Pillow sonst je nach Installation zufaellig vorhanden oder nicht, und der + # Paketschritt waere damit gar nicht pruefbar). Jeder andere Aufruf laeuft durch das + # echte python3. + cat >"$bindir/python3" <>"$log" +for a in "\$@"; do + case "\$a" in + *"import PIL"*) exit "\${FAKE_PIL_MISSING:-0}" ;; + esac +done +exec "$(command -v python3)" "\$@" EOF # "sudo install -o root -g root ..." (im NetworkManager-Keyfile-Schritt) braucht @@ -104,7 +133,8 @@ fi exit 0 EOF - chmod +x "$bindir"/ssh "$bindir"/scp "$bindir"/sudo "$bindir"/systemctl "$bindir"/nmcli + chmod +x "$bindir"/ssh "$bindir"/scp "$bindir"/sudo "$bindir"/systemctl "$bindir"/nmcli \ + "$bindir"/apt-get "$bindir"/python3 } # $1 = Sandbox-Wurzel fuer die "Systempfade" @@ -133,6 +163,7 @@ run_setup() { NINLIGHT_STATION_ROOT="$state/opt/ninlight-station" \ NINLIGHT_STATION_BIN="$state/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$state/etc/systemd/system/ninlight-station.service" \ + NINLIGHT_DISPLAY_UNIT="$state/etc/systemd/system/ninlight-display.service" \ NINLIGHT_NM_DIR="$state/etc/NetworkManager/system-connections" \ "$STATION_DIR/setup_pi.sh" pi-test } @@ -247,6 +278,158 @@ for KEY in MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do grep -Eq "^${KEY}=" "$CONF_OUT4" && pass "fehlender Schluessel $KEY wurde trotzdem ergaenzt" || fail "Schluessel $KEY fehlt weiterhin" done +echo +echo "=== Szenario 5 (Task 6b, Punkt 1): Anzeige wird ausgeliefert, installiert und aktiviert ===" +STATE="$TMPROOT/s5-state"; STUBBIN="$TMPROOT/bin5"; LOG="$TMPROOT/s5.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +DISP="$STATE/opt/ninlight-station/display" +# Die Panel-Treiber und der Herkunftsnachweis gehoeren mit auf den Pi, nicht nur ins +# Repo: HERKUNFT.md ist der Nachweis, woher der uebernommene Waveshare-Code stammt. +for F in display.py render.py epdconfig.py epd2in13_V2.py epd2in13_V3.py HERKUNFT.md; do + [ -f "$DISP/$F" ] && pass "display/$F ausgeliefert" || fail "display/$F fehlt unter $DISP" +done +[ -x "$DISP/display.py" ] && pass "display.py ist ausfuehrbar (Handbetrieb 'display test')" || fail "display.py ist nicht ausfuehrbar" +MODE=$(file_mode "$DISP/render.py") +[ "$MODE" = "644" ] && pass "importierter Code ist 0644" || fail "render.py hat Rechte $MODE statt 644" +[ ! -d "$DISP/__pycache__" ] && pass "kein __pycache__ auf dem Pi gelandet" || fail "__pycache__ wurde mitkopiert" +[ -f "$STATE/etc/systemd/system/ninlight-display.service" ] && pass "ninlight-display.service installiert" || fail "Anzeige-Unit fehlt - die Unit waere weiter toter Text im Repo" +# Ohne "enable" startet die Anzeige nach einem Stromausfall nicht wieder mit. +grep -Fq "systemctl enable ninlight-display.service" "$LOG" && pass "Anzeigedienst wurde aktiviert" || fail "kein 'systemctl enable ninlight-display' im Log" +grep -Fq "systemctl restart ninlight-display.service" "$LOG" && pass "Anzeigedienst wurde gestartet" || fail "kein 'systemctl restart ninlight-display' im Log" +# Die Station selbst bleibt nach dem Einrichten absichtlich AUS - nur die Anzeige +# laeuft. Ein versehentliches "enable ninlight-station" waere ein Sicherheitsfehler: +# der Zugangspunkt kaeme nach jedem Boot von allein hoch. +grep -Fq "systemctl enable ninlight-station" "$LOG" && fail "setup_pi.sh aktiviert den Stationsdienst - der Zugangspunkt kaeme nach jedem Boot von allein hoch" || pass "Stationsdienst bleibt deaktiviert (Station ist nach dem Einrichten aus)" + +echo +echo "=== Szenario 6 (Task 6b, Punkt 1): python3-pil wird nachgezogen, wenn Pillow fehlt ===" +# Ohne Pillow beendet sich der Anzeigedienst still mit 0 (Absicht) - das faellt +# nirgends auf, also muss das Paket hier kommen. +STATE="$TMPROOT/s6-state"; STUBBIN="$TMPROOT/bin6"; LOG="$TMPROOT/s6.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(FAKE_PIL_MISSING=1 run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fq "apt-get install -y python3-pil" "$LOG" && pass "python3-pil wird per apt nachinstalliert" || fail "kein 'apt-get install -y python3-pil' im Log" + +echo +echo "=== Szenario 7: ist Pillow schon da, wird apt gar nicht erst bemueht ===" +STATE="$TMPROOT/s7-state"; STUBBIN="$TMPROOT/bin7"; LOG="$TMPROOT/s7.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fq "apt-get" "$LOG" && fail "apt-get wurde trotz vorhandenem Pillow aufgerufen" || pass "kein apt-Aufruf, wenn Pillow schon vorhanden ist" + +echo +echo "=== Szenario 8 (Task 6b, Punkt 2): das Profil fuer den Migrations-Zugangspunkt wird angelegt ===" +# Ohne dieses zweite Profil scheitert "ninlight-station migrate on" auf einem frisch +# eingerichteten Pi an need_profile "$MIGRATE_AP_CONNECTION". +# +# Geprueft wird der Zustand aus Szenario 7 (ein vollstaendiger Lauf auf einem frischen +# Pi, $STATE/$LOG zeigen noch dorthin) - deshalb hier kein eigener Lauf. +NMDIR="$STATE/etc/NetworkManager/system-connections" +MIGRATE_FILE="$NMDIR/qconnex-firmware.nmconnection" +[ -f "$MIGRATE_FILE" ] && pass "qconnex-firmware.nmconnection angelegt" || fail "Migrationsprofil fehlt in $NMDIR: $(ls "$NMDIR")" +[ -f "$NMDIR/ninlight-recovery.nmconnection" ] && pass "Rettungsprofil weiterhin angelegt" || fail "Rettungsprofil fehlt" +# SSID und PSK sind von der alten Klyqa-Firmware fest vorgegeben - weicht eines ab, +# findet keine alte Lampe die Migrationsstation. +grep -Fqx "ssid=qconnex-firmware" "$MIGRATE_FILE" && pass "SSID ist die von der Klyqa-Firmware vorgegebene" || fail "falsche SSID: $(grep '^ssid=' "$MIGRATE_FILE")" +grep -Fqx "psk=qconnex-firmware" "$MIGRATE_FILE" && pass "PSK ist der von der Klyqa-Firmware vorgegebene" || fail "falscher PSK" +grep -Fqx "mode=ap" "$MIGRATE_FILE" && pass "Profil ist ein Zugangspunkt" || fail "mode=ap fehlt" +grep -Fqx "method=shared" "$MIGRATE_FILE" && pass "ipv4.method ist 'shared'" || fail "method=shared fehlt" +# Dieselbe Adresse wie das Rettungsprofil - genau deshalb schliessen sich beide aus, +# und genau darauf verlaesst sich ap_switch() in ninlight-station. +grep -Fqx "address1=192.168.8.1/24" "$MIGRATE_FILE" && pass "Adresse 192.168.8.1/24 (wie das Rettungsprofil)" || fail "falsche Adresse: $(grep '^address1=' "$MIGRATE_FILE")" +grep -Fqx "autoconnect=false" "$MIGRATE_FILE" && pass "kein Selbststart beim Booten" || fail "autoconnect ist nicht false - der Migrations-AP kaeme nach jedem Boot von allein hoch" +MODE=$(file_mode "$MIGRATE_FILE") +[ "$MODE" = "600" ] && pass "Keyfile ist 0600 (das Passwort steht darin)" || fail "Keyfile hat Rechte $MODE statt 600" +# Das Passwort darf nie als Kommandozeilenargument an nmcli gehen (in "ps" sichtbar). +grep -Fq "nmcli connection load" "$LOG" && pass "Profil wird per 'connection load' eingelesen" || fail "kein 'nmcli connection load' im Log" +grep -Eq "STUB nmcli .*(psk|wifi-sec)" "$LOG" && fail "das Passwort wurde als nmcli-Argument uebergeben - in 'ps' fuer jeden lokalen Benutzer sichtbar" || pass "kein Passwort in einer nmcli-Kommandozeile" + +echo +echo "=== Szenario 9 (Task 6b, Punkt 2): ein bestehendes Migrationsprofil bleibt unangetastet ===" +STATE="$TMPROOT/s9-state"; STUBBIN="$TMPROOT/bin9"; LOG="$TMPROOT/s9.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +NMDIR9="$STATE/etc/NetworkManager/system-connections" +# Von Hand angelegt, mit handjustierten Feldern (anderer Kanal), die dieses Skript +# gar nicht kennt. +cat >"$NMDIR9/qconnex-firmware.nmconnection" <<'NM' +[connection] +id=qconnex-firmware +uuid=11111111-2222-3333-4444-555555555555 +type=wifi +[wifi] +mode=ap +ssid=qconnex-firmware +channel=11 +NM +chmod 0600 "$NMDIR9/qconnex-firmware.nmconnection" +BEFORE=$(cat "$NMDIR9/qconnex-firmware.nmconnection") +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +if [ "$BEFORE" = "$(cat "$NMDIR9/qconnex-firmware.nmconnection")" ]; then + pass "bestehendes Migrationsprofil wurde Byte fuer Byte unveraendert gelassen" +else + fail "bestehendes Migrationsprofil wurde ueberschrieben: $(cat "$NMDIR9/qconnex-firmware.nmconnection")" +fi +echo "$OUT" | grep -q "unveraendert gelassen" && pass "Ausgabe sagt, dass das Profil unveraendert blieb" || fail "Ausgabe erwaehnt das nicht: $OUT" +# Das Rettungsprofil dagegen wird bewusst neu geschrieben (SSID/PSK koennen sich +# geaendert haben) - aber unter Beibehaltung der UUID. +[ -f "$NMDIR9/ninlight-recovery.nmconnection" ] && pass "Rettungsprofil trotzdem angelegt" || fail "Rettungsprofil fehlt" + +echo +echo "=== Szenario 10: ein leerer Konfigurationswert legt kein kaputtes Profil an ===" +STATE="$TMPROOT/s10-state"; STUBBIN="$TMPROOT/bin10"; LOG="$TMPROOT/s10.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +cat >"$STATE/etc/ninlight-station.conf" <<'CONF' +SSID='eigene-ssid' +PSK='eigenes-passwort' +IMAGE_DIR='/opt/ninlight-station/images' +HOME_CONNECTION='preconfigured' +AP_CONNECTION='ninlight-recovery' +MIGRATE_SSID='qconnex-firmware' +MIGRATE_PSK='' +MIGRATE_AP_CONNECTION='qconnex-firmware' +EPD_REVISION='V3' +CONF +chmod 0600 "$STATE/etc/ninlight-station.conf" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 bei leerem PSK" || fail "Exit-Code 0 trotz leerem Migrations-PSK: $OUT" +echo "$OUT" | grep -qi "leer" && pass "Fehlermeldung nennt den leeren Wert" || fail "Fehlermeldung fehlt: $OUT" +NMDIR10="$STATE/etc/NetworkManager/system-connections" +if [ -f "$NMDIR10/qconnex-firmware.nmconnection" ]; then + fail "trotz leerem PSK wurde ein Migrationsprofil geschrieben" +else + pass "kein Migrationsprofil mit leerem PSK angelegt" +fi + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/test_host.sh b/tools/test_host.sh index ae6e337..edefecc 100755 --- a/tools/test_host.sh +++ b/tools/test_host.sh @@ -7,9 +7,21 @@ cmake --build "$ROOT/build/host" ctest --test-dir "$ROOT/build/host" --output-on-failure "$@" if python3 -c 'import pytest' 2>/dev/null; then - python3 -m pytest "$ROOT/tools/mkimage/tests" "$ROOT/tools/station/tests" -q + # tools/mkimage als GANZES Verzeichnis, nicht nur tools/mkimage/tests: die 19 Tests in + # tools/mkimage/test_nl_mkimage.py liegen direkt daneben und liefen sonst in keiner + # Suite mit - darunter test_stays_python38_compatible, die Wache fuer die bindende + # 3.8-Randbedingung des Docker-Build-Containers. + # + # Diese Wache braucht das Paket "vermin" und schlaegt ohne es HART fehl (kein Skip): + # eine Wache, die sich beim Fehlen ihres Werkzeugs stillschweigend abschaltet, haette + # den Build-Bruch von damals genauso durchgelassen wie gar kein Test. Fehlt vermin, + # nachinstallieren: + # pip install vermin (oder: pip3 install --user vermin) + python3 -m pytest "$ROOT/tools/mkimage" "$ROOT/tools/station/tests" -q else echo "Hinweis: pytest fehlt - Python-Tests uebersprungen" >&2 + echo " Installation: pip install pytest vermin (vermin braucht" >&2 + echo " tools/mkimage/test_nl_mkimage.py::test_stays_python38_compatible)" >&2 fi "$ROOT/tools/station/tests/test_cli.sh" From 7fdccbce7217ac4f42bd3da96dd1bc9330c9f3c2 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 00:03:49 +0200 Subject: [PATCH 125/150] fix(station): Anzeige-Installation absichern, apt darf nur warnen Fixrunde 1 zu Task 6b. - setup_pi.sh raeumt die Ablage vor dem scp ab und bricht ab, wenn display.py dort fehlt. Ein Reststand aus einem abgebrochenen Lauf liess "scp -r" ins vorhandene Ziel hineinkopieren; die Installationsschleife ueberspringt Verzeichnisse und fand nichts - der Lauf meldete Erfolg, aktivierte den Anzeigedienst und hinterliess ein leeres display/: 203/EXEC im 5-Sekunden-Takt bei schwarzem Panel. - Der Paketschritt steht jetzt hinter der Installation und warnt nur. Eine Migrationsstation steht typischerweise nicht am Netz; unter set -e davor hiess ein gescheitertes apt: gar keine Station statt einer Station ohne Panel. "apt-get update" kommt vor dem install, der Anzeigedienst startet nach dem Paketschritt. - setup_pi.sh raeumt display/ auf dem Pi wie deploy.sh auf (im Repo geloeschte Dateien verschwinden), der Trap haengt auch an INT/TERM/HUP, und mktemp bekommt eine Vorlage: ohne sie beachtet BSD-mktemp TMPDIR nicht, und die Temp-Datei landete je nach Plattform woanders. - test_host.sh ruft test_deploy.sh auf - 95 Zusagen liefen nur bei Handaufruf, darunter die Deploy-Haelfte der Anzeige-Auslieferung. Der Coverage-Test deckt jetzt auch die Shell-Suiten ab und zaehlt dabei nur nicht auskommentierte Zeilen. - Neue Szenarien fuer alle vier Punkte, darunter die Zusage, dass beim gescheiterten Schreiben eines Profils keine Datei mit dem WLAN-Passwort liegenbleibt. --- tools/mkimage/tests/test_suite_coverage.py | 39 ++++- tools/station/setup_pi.sh | 117 ++++++++++--- tools/station/tests/test_cli.sh | 9 + tools/station/tests/test_setup_pi.sh | 185 ++++++++++++++++++++- tools/test_host.sh | 6 + 5 files changed, 325 insertions(+), 31 deletions(-) diff --git a/tools/mkimage/tests/test_suite_coverage.py b/tools/mkimage/tests/test_suite_coverage.py index 11580d5..dfd53a8 100644 --- a/tools/mkimage/tests/test_suite_coverage.py +++ b/tools/mkimage/tests/test_suite_coverage.py @@ -64,6 +64,34 @@ def test_every_test_file_is_reached_by_test_host(): ) +def test_every_shell_suite_is_called_by_test_host(): + """Dieselbe Regel fuer die Shell-Suiten (Task 6b F1, I3). + + test_deploy.sh war in tools/test_host.sh nicht aufgefuehrt und lief damit nur bei + Handaufruf - 95 Zusagen, die niemand mehr sah. Die .py-Regel oben deckt genau diese + Haelfte nicht ab, weil die Shell-Suiten nicht ueber pytest laufen, sondern einzeln + aufgerufen werden. + """ + # Nur AUSFUEHRBARE Zeilen zaehlen, keine Kommentare: der Kommentarblock ueber den + # Aufrufen nennt test_deploy.sh beim Namen, und eine blosse Textsuche waere damit + # auch dann erfuellt, wenn die Aufrufzeile fehlt - der Test waere gruen, ohne etwas + # zu pruefen (genau die Falle, die er verhindern soll). + lines = [ + line.strip() + for line in TEST_HOST.read_text().splitlines() + if line.strip() and not line.strip().startswith("#") + ] + missing = [] + for suite in sorted((TOOLS_DIR / "station" / "tests").glob("test_*.sh")): + # So, wie test_host.sh sie aufruft: "$ROOT/tools/station/tests/" + if not any(suite.name in line for line in lines): + missing.append(suite.relative_to(REPO_ROOT)) + assert not missing, ( + "diese Shell-Suiten ruft tools/test_host.sh nicht auf, sie laufen also nur bei " + "Handaufruf: {}".format(", ".join(str(m) for m in missing)) + ) + + def test_python38_guard_is_among_them(): """Die konkrete Datei, deren Ausfall den Build-Bruch damals durchgelassen haette.""" guard = (TOOLS_DIR / "mkimage" / "test_nl_mkimage.py").resolve() @@ -76,11 +104,6 @@ def test_python38_guard_is_among_them(): ) -def test_vermin_is_installed(): - """vermin fehlt -> harter Fehlschlag, kein Skip. - - Eine Wache, die sich beim Fehlen ihres Werkzeugs stillschweigend abschaltet, haette - den Build-Bruch von damals genauso durchgelassen wie gar kein Test. Nachinstallieren: - pip install vermin (oder pip3 install --user vermin). - """ - import vermin # noqa: F401 +# Kein eigener "ist vermin installiert"-Test hier: test_nl_mkimage.py importiert vermin +# ohnehin hart (kein Skip), und ein fehlendes Paket faellt dort mit derselben Deutlichkeit +# auf. Zwei Tests fuer dieselbe Zusage sind nur zwei Stellen zum Pflegen. diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index fbab5d4..a6a0f3f 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -77,24 +77,21 @@ fi rm -f "$TEMPLATE" REMOTE -echo "== Pakete ==" -# Pillow kommt auf dem Pi bewusst aus dem Distributionspaket (python3-pil), nicht per -# pip: tools/station/ laeuft dort unter dem System-Python 3.13, und Debian 13 laesst ein -# "pip install" ins System-Python gar nicht mehr zu (PEP 668). Ohne Pillow zeichnet die -# Anzeige nicht - display.py faengt den ImportError ab und beendet sich still mit 0 -# (Absicht, damit ein fehlendes Panel nie eine Migration verhindert). Genau deshalb muss -# das Paket HIER kommen: sonst faellt es nirgends auf. +echo "== Server, CLI und Units installieren ==" +# Reststand der Ablage wegraeumen, BEVOR das scp laeuft (Task 6b F1, I1). "scp -r quelle +# ziel" kopiert das Quellverzeichnis IN ein bereits vorhandenes Ziel hinein - aus +# einem liegengebliebenen "…/ninlight-display/" wird dann "…/ninlight-display/display/", +# die Installationsschleife unten ueberspringt Verzeichnisse und findet gar nichts mehr. +# Ergebnis waere ein Lauf, der Erfolg meldet, den Anzeigedienst aktiviert und ein leeres +# $ROOT_DIR/display hinterlaesst: 203/EXEC im 5-Sekunden-Takt, Panel schwarz. Genau so +# ein Reststand entsteht, wenn ein frueherer Lauf nach dem scp abbricht (sudo-Timeout, +# Platte voll, Strg-C) - die Aufraeumzeile unten laeuft dann nicht mehr. Idempotent +# heisst, dass auch dieser Fall glattgeht (siehe Kopf dieser Datei). "$SSH_BIN" "$HOST" bash -s <<'REMOTE' set -e -if python3 -c 'import PIL' 2>/dev/null; then - echo " python3-pil ist bereits vorhanden" -else - sudo apt-get install -y python3-pil - echo " python3-pil installiert" -fi +STAGE_DIR=${NINLIGHT_STAGE_DIR:-/tmp} +rm -rf "$STAGE_DIR/ninlight-display" REMOTE - -echo "== Server, CLI und Units installieren ==" "$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" \ "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HOST:$STAGE_DIR/" # Das Anzeigeverzeichnis als Ganzes: render.py, display.py, epdconfig.py, die beiden @@ -120,6 +117,15 @@ sudo install -m 0644 "$STAGE_DIR/ninlight-station.service" "$UNIT_PATH" # Anzeige: display.py ist ausfuehrbar (die Unit ruft es zwar ueber /usr/bin/python3 auf, # "ninlight-station display test" aber auch von Hand), der Rest ist importierter Code. rm -rf "$STAGE_DIR/ninlight-display/__pycache__" +# Harte Vorbedingung (Task 6b F1, I1): ohne display.py in der Ablage hat das scp nicht +# geliefert, was es sollte - abgebrochen, falsch verschachtelt oder gar nicht gelaufen. +# Die Schleife darunter wuerde das stillschweigend als "nichts zu tun" verbuchen, der +# Lauf meldete Erfolg und der aktivierte Anzeigedienst liefe gegen eine nicht +# existierende Datei. Lieber hier laut abbrechen. +if [ ! -f "$STAGE_DIR/ninlight-display/display.py" ]; then + echo "Fehler: $STAGE_DIR/ninlight-display/display.py fehlt - die Uebertragung des Anzeigeverzeichnisses ist fehlgeschlagen. Nichts installiert." >&2 + exit 1 +fi for f in "$STAGE_DIR"/ninlight-display/*; do [ -f "$f" ] || continue case "$(basename "$f")" in @@ -127,21 +133,76 @@ for f in "$STAGE_DIR"/ninlight-display/*; do *) sudo install -m 0644 "$f" "$ROOT_DIR/display/$(basename "$f")" ;; esac done +# Was im Repo geloescht wurde, muss auch hier verschwinden - dasselbe Argument wie in +# deploy.sh (dort per "rsync --delete"): display/ ist reiner Code aus dem Repo, es gibt +# keinen Bestand, der einen Lauf ueberleben muesste, und ein zurueckgebliebenes altes +# Modul wuerde weiter mit importiert. rsync steht hier nicht zur Verfuegung (setup_pi.sh +# setzt ausdruecklich nur ssh/scp voraus), also von Hand. +for f in "$ROOT_DIR"/display/*; do + [ -e "$f" ] || continue + if [ ! -e "$STAGE_DIR/ninlight-display/$(basename "$f")" ]; then + sudo rm -rf "$f" + echo " veraltete Datei entfernt: display/$(basename "$f")" + fi +done sudo install -m 0644 "$STAGE_DIR/ninlight-display.service" "$DISPLAY_UNIT_PATH" rm -rf "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" \ "$STAGE_DIR/ninlight-station.service" "$STAGE_DIR/ninlight-display.service" \ "$STAGE_DIR/ninlight-display" sudo systemctl daemon-reload +echo " installiert" +REMOTE + +echo "== Pakete ==" +# Pillow kommt auf dem Pi bewusst aus dem Distributionspaket (python3-pil), nicht per +# pip: tools/station/ laeuft dort unter dem System-Python 3.13, und Debian 13 laesst ein +# "pip install" ins System-Python gar nicht mehr zu (PEP 668). Ohne Pillow zeichnet die +# Anzeige nicht - display.py faengt den ImportError ab und beendet sich still mit 0 +# (Absicht, damit ein fehlendes Panel nie eine Migration verhindert). Genau deshalb muss +# das Paket ueberhaupt hier kommen: sonst faellt es nirgends auf. +# +# Aber NACH der Installation und nur als Warnung (Task 6b F1, I4): eine Migrationsstation +# steht typischerweise nicht am Netz, und mit veralteten Paketlisten oder ohne Internet +# scheitert apt. Unter "set -e" vor der Installation haette das den Lauf abgebrochen, +# bevor server.py, das CLI, beide Units und beide NetworkManager-Profile installiert sind +# - man haette dann GAR KEINE Station statt einer Station ohne Panel. Das waere die +# einzige harte Stelle in einer sonst durchgehend weichen Kette gewesen (display.py +# beendet sich mit 0, deploy.sh warnt nur, enable/restart unten warnen nur). +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e +if python3 -c 'import PIL' 2>/dev/null; then + echo " python3-pil ist bereits vorhanden" +else + # "update" zuerst: auf einem laenger nicht gepflegten Pi zeigen die Paketlisten auf + # Versionen, die es im Archiv nicht mehr gibt, und "install" scheitert mit 404. + # Beide Aufrufe duerfen nur warnen - siehe oben. + if sudo apt-get update >/dev/null 2>&1 && sudo apt-get install -y python3-pil; then + echo " python3-pil installiert" + else + echo " Warnung: python3-pil liess sich nicht installieren (kein Netz? veraltete Paketlisten?)." >&2 + echo " Die Station ist vollstaendig eingerichtet, das E-Paper bleibt aber leer," >&2 + echo " bis 'sudo apt install python3-pil' durchlaeuft und der Anzeigedienst neu startet." >&2 + fi +fi +REMOTE + +echo "== Anzeigedienst aktivieren ==" # Die Anzeige wird aktiviert (startet also beim Booten mit) UND jetzt gestartet - anders # als die Station selbst, die absichtlich aus bleibt: die Anzeige sendet nichts, sie # zeigt nur an, und "Station aus" ist genau das, was nach dem Einrichten auf dem Panel -# stehen soll. Fehlt Panel oder Pillow, beendet sich der Dienst still mit 0 (siehe -# display.py) - deshalb "|| true": ein fehlendes Panel darf setup_pi.sh nicht abbrechen. +# stehen soll. Erst hier, nach dem Paketschritt: so startet der Dienst bereits mit +# vorhandenem Pillow, statt sich beim ersten Zeichnen still mit 0 zu beenden und bis zum +# naechsten manuellen Neustart nichts anzuzeigen. +# +# Fehlt Panel oder Pillow, beendet sich der Dienst still mit 0 (siehe display.py) - +# deshalb nur Warnungen: ein fehlendes Panel darf setup_pi.sh nicht abbrechen. +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e sudo systemctl enable ninlight-display.service >/dev/null 2>&1 || \ - echo " Warnung: 'systemctl enable ninlight-display' fehlgeschlagen" >&2 + echo " Warnung: 'systemctl enable ninlight-display' fehlgeschlagen - die Anzeige startet nach einem Neustart des Pi nicht von allein mit" >&2 sudo systemctl restart ninlight-display.service >/dev/null 2>&1 || \ - echo " Warnung: 'systemctl restart ninlight-display' fehlgeschlagen" >&2 -echo " installiert (Anzeigedienst aktiviert)" + echo " Warnung: 'systemctl restart ninlight-display' fehlgeschlagen - journalctl -u ninlight-display" >&2 +echo " Anzeigedienst aktiviert" REMOTE echo "== NetworkManager-Profile ==" @@ -171,8 +232,20 @@ eval "$(sudo cat "$CONF")" # "$tmp" ist "local" - beim Auswerten des Traps am Skriptende gaebe es die # Variable gar nicht mehr. Deshalb ein gemeinsames Verzeichnis mit genau einem # Trap darum herum (0700, mktemp -d legt es so an). -PROFILE_TMPDIR=$(mktemp -d) -trap 'rm -rf "$PROFILE_TMPDIR"' EXIT +# +# Auch auf INT/TERM/HUP, nicht nur EXIT: bash fuehrt den EXIT-Trap zwar nach einem +# abgefangenen Signal aus, aber nur, wenn das Signal ueberhaupt abgefangen wird - ohne +# eigenen Handler beendet ein Strg-C (INT) oder ein abbrechendes ssh (HUP) die Shell +# sofort, und der EXIT-Trap laeuft nie. Genau dann bliebe die Datei liegen. +# +# Mit ausdruecklicher Vorlage unter ${TMPDIR:-/tmp}, nicht als blankes "mktemp -d": +# BSD-mktemp (macOS, wo diese Suite auch laeuft) beachtet TMPDIR ohne Vorlage gar nicht +# und legt unabhaengig davon unter /var/folders/... an, GNU-mktemp (der Pi) dagegen +# schon. Ohne Vorlage landet das Verzeichnis also je nach Plattform woanders - ein Test, +# der nachsieht, ob wirklich nichts liegengeblieben ist, sucht dann auf macOS an der +# falschen Stelle und ist gruen, ohne etwas geprueft zu haben. +PROFILE_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/ninlight-profile.XXXXXX") +trap 'rm -rf "$PROFILE_TMPDIR"' EXIT INT TERM HUP # $1 = Profilname, $2 = SSID, $3 = PSK, $4 = "update" (bestehendes Profil neu schreiben, # UUID behalten) oder "keep" (bestehendes Profil unangetastet lassen). diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index fbd70e6..2651214 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -136,6 +136,15 @@ case "\$1" in # RuntimeDirectoryPreserve=restart in der Unit leert systemd das # RuntimeDirectory bei JEDEM Stop/Restart, nicht nur beim allerersten Start, # und legt es leer wieder an. + # + # ACHTUNG, Stub-Erfindung (Task 6b F1): die Regel wird hier auf JEDE + # Unit angewandt, also auch auf ninlight-display - die traegt aber gar kein + # RuntimeDirectory=, ein "stop ninlight-display" raeumt in Wirklichkeit nichts + # weg. \$UNIT_FILE ist immer ninlight-station.service, unabhaengig davon, welche + # Unit im Aufruf steht. Heute folgenlos (kein Testfall stoppt die Anzeige und + # erwartet danach noch Dateien in RUN_DIR), aber niemand darf sich darauf + # verlassen: wer kuenftig prueft, was ein "display off" mit RUN_DIR macht, muss + # den Stub vorher nach Unit unterscheiden lassen. if ! grep -q '^RuntimeDirectoryPreserve=restart' "\$UNIT_FILE" 2>/dev/null; then rm -rf "\$RUN_DIR" mkdir -p "\$RUN_DIR" diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index 2d9ec1b..7e3f410 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -52,6 +52,14 @@ EOF cat >"$bindir/scp" <>"$log" +# Testschalter (Task 6b F1, I1): laesst die Uebertragung des Anzeigeverzeichnisses +# scheitern, ohne dass scp selbst einen Fehler meldet - genau das Bild eines +# abgebrochenen oder falsch verschachtelten "scp -r". +for a in "\$@"; do + case "\$a" in + *ninlight-display) [ -n "\${FAKE_SCP_SKIP_DIR:-}" ] && exit 0 ;; + esac +done args=() for a in "\$@"; do case "\$a" in @@ -73,7 +81,14 @@ EOF cat >"$bindir/apt-get" <>"$log" -exit "\${FAKE_APT_EXIT:-0}" +# Getrennt schaltbar: der Fall, den I4 beschreibt, ist "Paketlisten lassen sich +# auffrischen, das Paket ist trotzdem nicht zu holen" (404). Ein Stub, der schon +# "update" scheitern laesst, kaeme bei "install" nie an. +case "\$1" in + update) exit "\${FAKE_APT_UPDATE_EXIT:-0}" ;; + install) exit "\${FAKE_APT_EXIT:-0}" ;; +esac +exit 0 EOF # Steht fuer das python3 DES PI, nicht fuer das des Testrechners: nur die @@ -99,6 +114,14 @@ EOF cat >"$bindir/sudo" <>"$log" +# Testschalter (Task 6b F1, I2): laesst ein bestimmtes "sudo install" scheitern - +# gebraucht, um den Abbruchpfad in write_profile() zu erreichen, auf dem die +# Temp-Datei mit dem WLAN-Passwort sonst liegenbliebe. +if [ -n "\${FAKE_SUDO_INSTALL_FAIL:-}" ]; then + case "\$*" in + install*"\$FAKE_SUDO_INSTALL_FAIL"*) echo "install: simulierter Fehler" >&2; exit 1 ;; + esac +fi if [ "\$1" = "install" ]; then args=() skip=0 @@ -117,7 +140,11 @@ EOF cat >"$bindir/systemctl" <>"$log" +# Testschalter: laesst "enable"/"restart" scheitern (beides darf setup_pi.sh nur +# warnen lassen, nie abbrechen). case "\$1" in + enable) exit "\${FAKE_SYSTEMCTL_ENABLE_EXIT:-0}" ;; + restart) exit "\${FAKE_SYSTEMCTL_RESTART_EXIT:-0}" ;; is-active) exit "\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-0}" ;; *) exit 0 ;; esac @@ -155,7 +182,13 @@ setup_state() { # "ninlight-station.conf.example" oder "server.py". run_setup() { local state=$1 stubbin=$2 + # TMPDIR in die Sandbox: write_profile() legt seine Temp-Dateien (mit dem WLAN- + # Passwort darin) per "mktemp -d" unter TMPDIR an. Nur so kann Szenario 12 danach + # nachsehen, ob wirklich nichts liegengeblieben ist - und nebenbei fasst der Test + # damit auch dafuer nicht das echte /tmp des Entwicklungsrechners an. + mkdir -p "$state/tmpdir" PATH="$stubbin:$PATH" \ + TMPDIR="$state/tmpdir" \ NINLIGHT_SSH_BIN="$stubbin/ssh" \ NINLIGHT_SCP_BIN="$stubbin/scp" \ NINLIGHT_STAGE_DIR="$state/tmp" \ @@ -430,6 +463,156 @@ else pass "kein Migrationsprofil mit leerem PSK angelegt" fi +echo +echo "=== Szenario 11 (Task 6b F1, I1): Reststand in der Ablage fuehrt nicht zu einer stillen Nullinstallation ===" +# Ein frueherer Lauf ist nach dem scp abgebrochen (sudo-Timeout, Platte voll, Strg-C), +# die Aufraeumzeile lief nicht. "scp -r quelle ziel" kopiert dann IN das vorhandene Ziel +# hinein (…/ninlight-display/display/), die Installationsschleife ueberspringt +# Verzeichnisse und findet nichts - der Lauf meldete frueher Erfolg und aktivierte einen +# Dienst, dessen ExecStart-Datei gar nicht existiert (203/EXEC im 5-Sekunden-Takt). +STATE="$TMPROOT/s11-state"; STUBBIN="$TMPROOT/bin11"; LOG="$TMPROOT/s11.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$STATE/tmp/ninlight-display" +echo "rest" >"$STATE/tmp/ninlight-display/uebrig.py" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +DISP11="$STATE/opt/ninlight-station/display" +[ -f "$DISP11/display.py" ] && pass "display.py trotz Reststand installiert" || fail "Nullinstallation: $DISP11 enthaelt $(ls -A "$DISP11" 2>/dev/null | tr '\n' ' ')" +[ -f "$DISP11/epd2in13_V3.py" ] && pass "Panel-Treiber trotz Reststand installiert" || fail "Panel-Treiber fehlt" +[ ! -d "$DISP11/display" ] && pass "kein verschachteltes display/display/ entstanden" || fail "verschachteltes Verzeichnis angelegt" +[ ! -f "$DISP11/uebrig.py" ] && pass "der Reststand des Vorlaufs wurde nicht mitinstalliert" || fail "uebrig.py aus dem Reststand landete auf dem Pi" + +echo +echo "=== Szenario 12 (Task 6b F1, I1): scheitert die Uebertragung, wird laut abgebrochen statt still 'installiert' gemeldet ===" +STATE="$TMPROOT/s12-state"; STUBBIN="$TMPROOT/bin12"; LOG="$TMPROOT/s12.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(FAKE_SCP_SKIP_DIR=1 run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0" || fail "Exit-Code 0 trotz fehlgeschlagener Uebertragung: $OUT" +echo "$OUT" | grep -q "display.py fehlt" && pass "Fehlermeldung nennt die fehlende Datei" || fail "Fehlermeldung fehlt: $OUT" +# Das Schlimmste waere ein aktivierter Dienst ohne ExecStart-Datei. +grep -Fq "systemctl enable ninlight-display" "$LOG" && fail "der Anzeigedienst wurde trotz fehlender Dateien aktiviert - 203/EXEC im Neustart-Takt" || pass "kein aktivierter Dienst ohne Dateien" + +echo +echo "=== Szenario 13 (Task 6b F1, I1): eine im Repo geloeschte Datei verschwindet auch auf dem Pi ===" +# Dasselbe Argument wie in deploy.sh: display/ ist reiner Code aus dem Repo, ein +# zurueckgebliebenes altes Modul wuerde weiter mit importiert. +STATE="$TMPROOT/s13-state"; STUBBIN="$TMPROOT/bin13"; LOG="$TMPROOT/s13.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$STATE/opt/ninlight-station/display" +echo "alt" >"$STATE/opt/ninlight-station/display/veraltet.py" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ ! -f "$STATE/opt/ninlight-station/display/veraltet.py" ] && pass "veraltete Datei entfernt" || fail "veraltet.py blieb liegen" +[ -f "$STATE/opt/ninlight-station/display/render.py" ] && pass "der aktuelle Code ist trotzdem da" || fail "render.py fehlt" + +echo +echo "=== Szenario 14 (Task 6b F1, I2): scheitert das Schreiben eines Profils, bleibt keine Temp-Datei mit dem Passwort liegen ===" +# Zwischen "mktemp" und "install" steht das WLAN-Passwort in einer Datei unter TMPDIR. +# Ein Abbruch genau dort (set -e) darf sie nicht zuruecklassen - dafuer gibt es den Trap +# um das gemeinsame mktemp-Verzeichnis. Ohne ihn faellt das sonst nirgends auf. +STATE="$TMPROOT/s14-state"; STUBBIN="$TMPROOT/bin14"; LOG="$TMPROOT/s14.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +cat >"$STATE/etc/ninlight-station.conf" <<'CONF' +SSID='eigene-ssid' +PSK='streng-geheimes-passwort' +IMAGE_DIR='/opt/ninlight-station/images' +HOME_CONNECTION='preconfigured' +AP_CONNECTION='ninlight-recovery' +MIGRATE_SSID='qconnex-firmware' +MIGRATE_PSK='qconnex-firmware' +MIGRATE_AP_CONNECTION='qconnex-firmware' +EPD_REVISION='V3' +CONF +chmod 0600 "$STATE/etc/ninlight-station.conf" +: >"$LOG" +set +e +OUT=$(FAKE_SUDO_INSTALL_FAIL=.nmconnection run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 (das Profil liess sich nicht schreiben)" || fail "Exit-Code 0 trotz fehlgeschlagenem install: $OUT" +LEFTOVER=$(find "$STATE/tmpdir" -type f 2>/dev/null | head -n5) +[ -z "$LEFTOVER" ] && pass "keine Temp-Datei unter TMPDIR zurueckgeblieben" || fail "liegengeblieben: $LEFTOVER" +# Der eigentliche Schaden waere das Passwort in einer Datei, die niemand mehr aufraeumt. +if grep -rlq "streng-geheimes-passwort" "$STATE/tmpdir" 2>/dev/null; then + fail "das WLAN-Passwort steht in einer liegengebliebenen Datei unter $STATE/tmpdir" +else + pass "das WLAN-Passwort steht in keiner liegengebliebenen Datei" +fi + +echo +echo "=== Szenario 15 (Task 6b F1, I4): scheitert apt, ist die Station trotzdem vollstaendig eingerichtet ===" +# Eine Migrationsstation steht typischerweise nicht am Netz. Ein harter Abbruch hier +# hiesse: gar keine Station statt einer Station ohne Panel. +STATE="$TMPROOT/s15-state"; STUBBIN="$TMPROOT/bin15"; LOG="$TMPROOT/s15.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(FAKE_PIL_MISSING=1 FAKE_APT_EXIT=100 run_setup "$STATE" "$STUBBIN" 2>"$TMPROOT/s15.err") +RC=$? +set -e +ERR=$(cat "$TMPROOT/s15.err") +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz gescheitertem apt" || fail "Exit-Code $RC - kein Netz haette die ganze Einrichtung verhindert: $ERR" +echo "$ERR" | grep -q "Warnung" && pass "Warnung (keine Fehlermeldung) auf stderr" || fail "keine Warnung auf stderr: $ERR" +[ -x "$STATE/opt/ninlight-station/server.py" ] && pass "server.py installiert" || fail "server.py fehlt" +[ -x "$STATE/usr/local/bin/ninlight-station" ] && pass "CLI installiert" || fail "CLI fehlt" +[ -f "$STATE/etc/systemd/system/ninlight-station.service" ] && pass "Stations-Unit installiert" || fail "Stations-Unit fehlt" +[ -f "$STATE/etc/systemd/system/ninlight-display.service" ] && pass "Anzeige-Unit installiert" || fail "Anzeige-Unit fehlt" +[ -f "$STATE/opt/ninlight-station/display/display.py" ] && pass "Anzeigecode installiert" || fail "Anzeigecode fehlt" +NMDIR15="$STATE/etc/NetworkManager/system-connections" +[ -f "$NMDIR15/ninlight-recovery.nmconnection" ] && pass "Rettungsprofil angelegt" || fail "Rettungsprofil fehlt" +[ -f "$NMDIR15/qconnex-firmware.nmconnection" ] && pass "Migrationsprofil angelegt" || fail "Migrationsprofil fehlt" +# Reihenfolge: apt kommt NACH der Installation, sonst haette ein Fehlschlag alles davor +# mitgerissen. +INST_LINE=$({ grep -n "install .*ninlight-station.service" "$LOG" | head -n1 | cut -d: -f1; } || true) +APT_LINE=$({ grep -n "apt-get install" "$LOG" | head -n1 | cut -d: -f1; } || true) +if [ -n "$INST_LINE" ] && [ -n "$APT_LINE" ] && [ "$INST_LINE" -lt "$APT_LINE" ]; then + pass "der Paketschritt laeuft nach der Installation (inst=$INST_LINE, apt=$APT_LINE)" +else + fail "der Paketschritt laeuft weiterhin vor der Installation (inst=$INST_LINE, apt=$APT_LINE)" +fi +# Und der Anzeigedienst startet erst nach dem Paketschritt - sonst liefe er beim +# allerersten Zeichnen ohne Pillow und beendete sich still mit 0. +RESTART_LINE=$({ grep -n "systemctl restart ninlight-display" "$LOG" | head -n1 | cut -d: -f1; } || true) +if [ -n "$RESTART_LINE" ] && [ "$APT_LINE" -lt "$RESTART_LINE" ]; then + pass "der Anzeigedienst startet nach dem Paketschritt (apt=$APT_LINE, restart=$RESTART_LINE)" +else + fail "der Anzeigedienst startet vor dem Paketschritt (apt=$APT_LINE, restart=$RESTART_LINE)" +fi +grep -Fq "apt-get update" "$LOG" && pass "die Paketlisten werden vorher aufgefrischt" || fail "kein 'apt-get update' vor dem install" + +echo +echo "=== Szenario 16: ein fehlgeschlagenes enable/restart des Anzeigedienstes warnt nur ===" +STATE="$TMPROOT/s16-state"; STUBBIN="$TMPROOT/bin16"; LOG="$TMPROOT/s16.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(FAKE_SYSTEMCTL_ENABLE_EXIT=1 FAKE_SYSTEMCTL_RESTART_EXIT=1 run_setup "$STATE" "$STUBBIN" 2>"$TMPROOT/s16.err") +RC=$? +set -e +ERR=$(cat "$TMPROOT/s16.err") +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlgeschlagenem enable/restart" || fail "Exit-Code $RC: $ERR" +echo "$ERR" | grep -q "systemctl enable ninlight-display" && pass "Warnung zu 'enable' auf stderr" || fail "keine enable-Warnung: $ERR" +echo "$ERR" | grep -q "systemctl restart ninlight-display" && pass "Warnung zu 'restart' auf stderr" || fail "keine restart-Warnung: $ERR" +[ -f "$STATE/etc/NetworkManager/system-connections/qconnex-firmware.nmconnection" ] && pass "der Lauf ging danach normal weiter (Profile angelegt)" || fail "der Lauf brach nach der Warnung ab" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/test_host.sh b/tools/test_host.sh index edefecc..d6b9696 100755 --- a/tools/test_host.sh +++ b/tools/test_host.sh @@ -24,5 +24,11 @@ else echo " tools/mkimage/test_nl_mkimage.py::test_stays_python38_compatible)" >&2 fi +# Alle Shell-Suiten unter tools/station/tests/ - test_deploy.sh fehlte hier und lief +# damit nur bei Handaufruf (Task 6b F1, I3): dieselbe Klasse von Luecke wie die aus +# keiner Suite gefallene test_nl_mkimage.py, nur eine Ebene hoeher. Mit ihr liefen 95 +# Zusagen nirgends mit, darunter die komplette Deploy-Haelfte der Anzeige-Auslieferung. +# tools/mkimage/tests/test_suite_coverage.py haelt fest, dass hier keine Suite fehlt. "$ROOT/tools/station/tests/test_cli.sh" +"$ROOT/tools/station/tests/test_deploy.sh" "$ROOT/tools/station/tests/test_setup_pi.sh" From 47532a6820bee600a316d14fba295b3bcdfa60b8 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 00:45:04 +0200 Subject: [PATCH 126/150] fix(station): ehrliche Abbruchmeldung, apt-update darf scheitern Fixrunde 2 zu Task 6b. - Die Vorbedingung "display.py liegt in der Ablage" steht jetzt VOR dem ersten sudo install. Vorher stand sie dahinter, und die Meldung "Nichts installiert" war schlicht unwahr: server.py, products.py, das CLI und die Stations-Unit lagen dann schon auf dem Pi. Ausgerechnet an dem Werkbank-Moment, fuer den die Pruefung gedacht ist, log das Skript den Bediener an. Die Ablage raeumt ein EXIT-Trap jetzt auf beiden Wegen aus dem Block ab, nicht nur im Erfolgsfall. - "apt-get update" ist keine Voraussetzung mehr fuer "apt-get install": update meldet schon dann einen Fehler, wenn eine einzige Quelle nicht erreichbar ist, und ein Pi, auf dem das Paket aus dem Cache gekommen waere, bekam so still kein Pillow. - Die Signalhandler beenden jetzt selbst (130/143/129). Ein Handler ohne exit laesst bash weiterlaufen - ein Strg-C konnte in "fertig, Exit 0" muenden. Die Begruendung im Kommentar war falsch und ist korrigiert: zum Aufraeumen haette der EXIT-Trap allein genuegt. - test_host.sh ruft die Shell-Suiten aus einer Liste auf, und der Coverage-Test prueft diese Liste gegen das Dateisystem statt im Skripttext nach Dateinamen zu suchen - ein nachgestellter Kommentar hebelte die alte Regel aus. Er deckt jetzt ganz tools/ ab, nicht nur station/tests. - Neue Szenarien: Wahrheitsgehalt der Abbruchmeldung, apt-update-Fall, und ein Strg-C, das aus den Trap-Zeilen des Skripts selbst geprueft wird. --- tools/mkimage/tests/test_suite_coverage.py | 67 ++++++++++++++------ tools/station/setup_pi.sh | 71 ++++++++++++++++------ tools/station/tests/test_setup_pi.sh | 70 +++++++++++++++++++++ tools/test_host.sh | 26 +++++--- 4 files changed, 189 insertions(+), 45 deletions(-) diff --git a/tools/mkimage/tests/test_suite_coverage.py b/tools/mkimage/tests/test_suite_coverage.py index dfd53a8..3908551 100644 --- a/tools/mkimage/tests/test_suite_coverage.py +++ b/tools/mkimage/tests/test_suite_coverage.py @@ -41,14 +41,42 @@ def pytest_paths(): return paths -def own_test_files(): +def own_test_files(suffix=".py"): return sorted( p.resolve() - for p in TOOLS_DIR.rglob("test_*.py") + for p in TOOLS_DIR.rglob("test_*" + suffix) if not any(part in IGNORED_PARTS for part in p.parts) ) +def listed_shell_suites(): + """Die Eintraege des SHELL_SUITES-Feldes in tools/test_host.sh, aufgeloest. + + Ausdruecklich gegen das FELD geprueft und nicht per Textsuche nach Dateinamen: eine + Textsuche ist schon durch einen nachgestellten Kommentar ("echo x # test_deploy.sh"), + ein ": \"test_deploy.sh\"" oder einen Heredoc-Inhalt erfuellt - sie belegt nur, dass + der Name irgendwo vorkommt, nicht dass die Suite laeuft (Task 6b F2). + """ + text = TEST_HOST.read_text() + block = re.search(r"^SHELL_SUITES=\((.*?)^\)", text, re.M | re.S) + assert block, ( + "in tools/test_host.sh gibt es kein 'SHELL_SUITES=(...)'-Feld mehr - die " + "Vollstaendigkeitspruefung der Shell-Suiten haengt daran" + ) + # Und das Feld muss auch wirklich abgearbeitet werden, sonst ist es nur Deko. + assert re.search(r'for\s+\w+\s+in\s+"\$\{SHELL_SUITES\[@\]\}"', text), ( + "tools/test_host.sh arbeitet SHELL_SUITES nicht in einer Schleife ab - die " + "aufgefuehrten Suiten wuerden gar nicht laufen" + ) + paths = [] + for raw in block.group(1).split(): + arg = raw.strip('"') + if not arg or arg.startswith("#"): + continue + paths.append(Path(arg.replace("$ROOT", str(REPO_ROOT))).resolve()) + return paths + + def test_every_test_file_is_reached_by_test_host(): roots = pytest_paths() orphans = [] @@ -72,23 +100,28 @@ def test_every_shell_suite_is_called_by_test_host(): Haelfte nicht ab, weil die Shell-Suiten nicht ueber pytest laufen, sondern einzeln aufgerufen werden. """ - # Nur AUSFUEHRBARE Zeilen zaehlen, keine Kommentare: der Kommentarblock ueber den - # Aufrufen nennt test_deploy.sh beim Namen, und eine blosse Textsuche waere damit - # auch dann erfuellt, wenn die Aufrufzeile fehlt - der Test waere gruen, ohne etwas - # zu pruefen (genau die Falle, die er verhindern soll). - lines = [ - line.strip() - for line in TEST_HOST.read_text().splitlines() - if line.strip() and not line.strip().startswith("#") + listed = listed_shell_suites() + # Symmetrisch zur .py-Regel: das GANZE tools/ wird durchsucht, nicht nur + # tools/station/tests - eine kuenftige Shell-Suite an einer neuen Stelle faellt sonst + # weiterhin durch (Task 6b F2). + # tools/test_host.sh heisst selbst "test_*.sh", ist aber der Aufrufer und keine + # Suite - es kann sich schlecht selbst auffuehren. + missing = [ + s.relative_to(REPO_ROOT) + for s in own_test_files(".sh") + if s != TEST_HOST.resolve() and s not in listed ] - missing = [] - for suite in sorted((TOOLS_DIR / "station" / "tests").glob("test_*.sh")): - # So, wie test_host.sh sie aufruft: "$ROOT/tools/station/tests/" - if not any(suite.name in line for line in lines): - missing.append(suite.relative_to(REPO_ROOT)) assert not missing, ( - "diese Shell-Suiten ruft tools/test_host.sh nicht auf, sie laufen also nur bei " - "Handaufruf: {}".format(", ".join(str(m) for m in missing)) + "diese Shell-Suiten stehen nicht in SHELL_SUITES von tools/test_host.sh, sie " + "laufen also nur bei Handaufruf: {}".format(", ".join(str(m) for m in missing)) + ) + # Und andersherum: ein Eintrag, den es nicht mehr gibt, laesst test_host.sh beim + # naechsten Lauf hart scheitern - das faellt hier schon vorher auf. + verschwunden = [str(p) for p in listed if not p.exists()] + assert not verschwunden, ( + "SHELL_SUITES in tools/test_host.sh nennt Dateien, die es nicht gibt: {}".format( + ", ".join(verschwunden) + ) ) diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index a6a0f3f..a71a600 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -109,6 +109,35 @@ ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} DISPLAY_UNIT_PATH=${NINLIGHT_DISPLAY_UNIT:-/etc/systemd/system/ninlight-display.service} + +# Die Ablage wird in JEDEM Fall abgeraeumt, auch wenn dieser Block vorzeitig aussteigt +# (Task 6b F2, N1). Vorher stand das "rm -rf" nur am Blockende und wurde beim Abbruch +# uebersprungen - die uebertragenen Dateien blieben unter /tmp liegen und waren beim +# naechsten Lauf genau der Reststand, gegen den der Block eigentlich absichert. +cleanup_stage() { + rm -rf "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" \ + "$STAGE_DIR/ninlight-station.service" "$STAGE_DIR/ninlight-display.service" \ + "$STAGE_DIR/ninlight-display" +} +trap cleanup_stage EXIT + +rm -rf "$STAGE_DIR/ninlight-display/__pycache__" +# Harte Vorbedingung, und zwar VOR dem ersten "sudo install" (Task 6b F2, N1): ohne +# display.py in der Ablage hat das scp nicht geliefert, was es sollte - abgebrochen, +# falsch verschachtelt oder gar nicht gelaufen. Die Installationsschleife weiter unten +# wuerde das stillschweigend als "nichts zu tun" verbuchen, der Lauf meldete Erfolg und +# der aktivierte Anzeigedienst liefe gegen eine nicht existierende Datei. +# +# Die Pruefung stand zuerst NACH den vier "sudo install" darunter - die Meldung "Nichts +# installiert" war damit schlicht unwahr: server.py, products.py, das CLI und die +# Stations-Unit lagen dann bereits auf dem Pi. Ausgerechnet an dem Werkbank-Moment, fuer +# den diese Pruefung gedacht ist, log das Skript den Bediener an. Jetzt stimmt der Satz. +if [ ! -f "$STAGE_DIR/ninlight-display/display.py" ]; then + echo "Fehler: $STAGE_DIR/ninlight-display/display.py fehlt - die Uebertragung des Anzeigeverzeichnisses ist fehlgeschlagen." >&2 + echo " Nichts installiert: $ROOT_DIR, $BIN_PATH und beide Units sind unveraendert. Ablage aufgeraeumt, Lauf einfach wiederholen." >&2 + exit 1 +fi + sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images" "$ROOT_DIR/display" sudo install -m 0755 "$STAGE_DIR/server.py" "$ROOT_DIR/server.py" sudo install -m 0644 "$STAGE_DIR/products.py" "$ROOT_DIR/products.py" @@ -116,16 +145,6 @@ sudo install -m 0755 "$STAGE_DIR/ninlight-station" "$BIN_PATH" sudo install -m 0644 "$STAGE_DIR/ninlight-station.service" "$UNIT_PATH" # Anzeige: display.py ist ausfuehrbar (die Unit ruft es zwar ueber /usr/bin/python3 auf, # "ninlight-station display test" aber auch von Hand), der Rest ist importierter Code. -rm -rf "$STAGE_DIR/ninlight-display/__pycache__" -# Harte Vorbedingung (Task 6b F1, I1): ohne display.py in der Ablage hat das scp nicht -# geliefert, was es sollte - abgebrochen, falsch verschachtelt oder gar nicht gelaufen. -# Die Schleife darunter wuerde das stillschweigend als "nichts zu tun" verbuchen, der -# Lauf meldete Erfolg und der aktivierte Anzeigedienst liefe gegen eine nicht -# existierende Datei. Lieber hier laut abbrechen. -if [ ! -f "$STAGE_DIR/ninlight-display/display.py" ]; then - echo "Fehler: $STAGE_DIR/ninlight-display/display.py fehlt - die Uebertragung des Anzeigeverzeichnisses ist fehlgeschlagen. Nichts installiert." >&2 - exit 1 -fi for f in "$STAGE_DIR"/ninlight-display/*; do [ -f "$f" ] || continue case "$(basename "$f")" in @@ -146,9 +165,7 @@ for f in "$ROOT_DIR"/display/*; do fi done sudo install -m 0644 "$STAGE_DIR/ninlight-display.service" "$DISPLAY_UNIT_PATH" -rm -rf "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" \ - "$STAGE_DIR/ninlight-station.service" "$STAGE_DIR/ninlight-display.service" \ - "$STAGE_DIR/ninlight-display" +# Das Aufraeumen der Ablage macht der EXIT-Trap oben - auf beiden Wegen aus diesem Block. sudo systemctl daemon-reload echo " installiert" REMOTE @@ -175,8 +192,15 @@ if python3 -c 'import PIL' 2>/dev/null; then else # "update" zuerst: auf einem laenger nicht gepflegten Pi zeigen die Paketlisten auf # Versionen, die es im Archiv nicht mehr gibt, und "install" scheitert mit 404. - # Beide Aufrufe duerfen nur warnen - siehe oben. - if sudo apt-get update >/dev/null 2>&1 && sudo apt-get install -y python3-pil; then + # + # Aber "|| true", und "install" laeuft UNABHAENGIG davon (Task 6b F2, N2): "apt-get + # update" liefert schon dann einen Fehler, wenn eine einzige Quelle nicht erreichbar + # ist - eine veraltete Drittanbieter-Liste, teilweises Netz. Mit "update && install" + # bekaeme ein Pi, auf dem "install" aus dem Paketcache oder den erreichbaren Quellen + # muehelos durchgelaufen waere, still kein Pillow. Das Auffrischen ist eine + # Verbesserung der Chancen, keine Voraussetzung. + sudo apt-get update >/dev/null 2>&1 || true + if sudo apt-get install -y python3-pil; then echo " python3-pil installiert" else echo " Warnung: python3-pil liess sich nicht installieren (kein Netz? veraltete Paketlisten?)." >&2 @@ -233,10 +257,14 @@ eval "$(sudo cat "$CONF")" # Variable gar nicht mehr. Deshalb ein gemeinsames Verzeichnis mit genau einem # Trap darum herum (0700, mktemp -d legt es so an). # -# Auch auf INT/TERM/HUP, nicht nur EXIT: bash fuehrt den EXIT-Trap zwar nach einem -# abgefangenen Signal aus, aber nur, wenn das Signal ueberhaupt abgefangen wird - ohne -# eigenen Handler beendet ein Strg-C (INT) oder ein abbrechendes ssh (HUP) die Shell -# sofort, und der EXIT-Trap laeuft nie. Genau dann bliebe die Datei liegen. +# Zur Reichweite des Traps (Task 6b F2, N3 - die Begruendung hier war vorher falsch): +# der EXIT-Trap allein WUERDE bei einem Strg-C schon reichen, bash fuehrt ihn auch beim +# Abbruch durch ein nicht abgefangenes SIGINT aus (Exit 130). Der Grund fuer die +# Signalhandler ist ein anderer: sie sollen den Exit-Code ehrlich halten. Ein Handler +# fuer INT/TERM, der nicht selbst beendet, laesst bash naemlich NACH dem Handler +# weiterlaufen - ein Strg-C mitten im Schreiben der Profile koennte dann in "fertig, +# Exit 0" muenden, obwohl der Lauf abgebrochen wurde. Deshalb raeumt jeder Handler auf +# und beendet danach ausdruecklich mit dem ueblichen Signal-Code (128+n). # # Mit ausdruecklicher Vorlage unter ${TMPDIR:-/tmp}, nicht als blankes "mktemp -d": # BSD-mktemp (macOS, wo diese Suite auch laeuft) beachtet TMPDIR ohne Vorlage gar nicht @@ -245,7 +273,10 @@ eval "$(sudo cat "$CONF")" # der nachsieht, ob wirklich nichts liegengeblieben ist, sucht dann auf macOS an der # falschen Stelle und ist gruen, ohne etwas geprueft zu haben. PROFILE_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/ninlight-profile.XXXXXX") -trap 'rm -rf "$PROFILE_TMPDIR"' EXIT INT TERM HUP +trap 'rm -rf "$PROFILE_TMPDIR"' EXIT +trap 'rm -rf "$PROFILE_TMPDIR"; exit 130' INT +trap 'rm -rf "$PROFILE_TMPDIR"; exit 143' TERM +trap 'rm -rf "$PROFILE_TMPDIR"; exit 129' HUP # $1 = Profilname, $2 = SSID, $3 = PSK, $4 = "update" (bestehendes Profil neu schreiben, # UUID behalten) oder "keep" (bestehendes Profil unangetastet lassen). diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index 7e3f410..f8c0624 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -501,6 +501,21 @@ set -e echo "$OUT" | grep -q "display.py fehlt" && pass "Fehlermeldung nennt die fehlende Datei" || fail "Fehlermeldung fehlt: $OUT" # Das Schlimmste waere ein aktivierter Dienst ohne ExecStart-Datei. grep -Fq "systemctl enable ninlight-display" "$LOG" && fail "der Anzeigedienst wurde trotz fehlender Dateien aktiviert - 203/EXEC im Neustart-Takt" || pass "kein aktivierter Dienst ohne Dateien" +# WAHRHEITSGEHALT der Meldung, nicht ihr Wortlaut (Task 6b F2, N1): "Nichts installiert" +# muss auch stimmen. Vorher stand die Pruefung hinter vier "sudo install", und auf dem Pi +# lagen server.py, products.py, das CLI und die Stations-Unit bereits - ausgerechnet an +# dem Werkbank-Moment, fuer den die Pruefung gedacht ist, log das Skript den Bediener an. +echo "$OUT" | grep -q "Nichts installiert" && pass "die Meldung behauptet, nichts installiert zu haben" || fail "Meldung ohne diese Zusage: $OUT" +for F in "$STATE/opt/ninlight-station/server.py" "$STATE/opt/ninlight-station/products.py" \ + "$STATE/usr/local/bin/ninlight-station" \ + "$STATE/etc/systemd/system/ninlight-station.service" \ + "$STATE/etc/systemd/system/ninlight-display.service"; do + [ ! -e "$F" ] && pass "wirklich nicht installiert: $(basename "$F")" || fail "die Meldung luegt - $F liegt auf dem Pi" +done +# Und die Ablage darf nicht liegenbleiben: sonst ist sie beim naechsten Lauf genau der +# Reststand, gegen den Szenario 11 absichert. +REST=$(find "$STATE/tmp" -mindepth 1 2>/dev/null | head -n5) +[ -z "$REST" ] && pass "die Ablage wurde auch im Fehlerfall aufgeraeumt" || fail "Reststand in der Ablage: $REST" echo echo "=== Szenario 13 (Task 6b F1, I1): eine im Repo geloeschte Datei verschwindet auch auf dem Pi ===" @@ -613,6 +628,61 @@ echo "$ERR" | grep -q "systemctl enable ninlight-display" && pass "Warnung zu 'e echo "$ERR" | grep -q "systemctl restart ninlight-display" && pass "Warnung zu 'restart' auf stderr" || fail "keine restart-Warnung: $ERR" [ -f "$STATE/etc/NetworkManager/system-connections/qconnex-firmware.nmconnection" ] && pass "der Lauf ging danach normal weiter (Profile angelegt)" || fail "der Lauf brach nach der Warnung ab" +echo +echo "=== Szenario 17 (Task 6b F2, N2): ein gescheitertes 'apt-get update' verhindert die Installation von Pillow nicht ===" +# "apt-get update" liefert schon dann einen Fehler, wenn eine einzige Quelle nicht +# erreichbar ist (veraltete Drittanbieter-Liste, teilweises Netz). Mit "update && +# install" bekaeme ein Pi, auf dem "install" aus dem Cache muehelos durchgelaufen waere, +# still kein Pillow - und genau das Stillsein war der Grund, das Paket ueberhaupt hier +# nachzuziehen. +STATE="$TMPROOT/s17-state"; STUBBIN="$TMPROOT/bin17"; LOG="$TMPROOT/s17.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(FAKE_PIL_MISSING=1 FAKE_APT_UPDATE_EXIT=100 run_setup "$STATE" "$STUBBIN" 2>"$TMPROOT/s17.err") +RC=$? +set -e +ERR=$(cat "$TMPROOT/s17.err") +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $ERR" +grep -Fq "apt-get install -y python3-pil" "$LOG" && pass "'install' wird trotz gescheitertem 'update' versucht" || fail "'install' wurde nach dem gescheiterten 'update' gar nicht mehr versucht: $(grep apt-get "$LOG")" +echo "$OUT" | grep -q "python3-pil installiert" && pass "Pillow wurde installiert" || fail "Ausgabe meldet die Installation nicht: $OUT" +echo "$ERR" | grep -q "Warnung" && fail "es wurde gewarnt, obwohl das Paket installiert werden konnte: $ERR" || pass "keine unnoetige Warnung" + +echo +echo "=== Szenario 18 (Task 6b F2, N3): ein Strg-C waehrend des Profilschreibens endet nicht als 'erfolgreich' ===" +# Ein INT/TERM-Handler, der nicht selbst beendet, laesst bash NACH dem Handler +# weiterlaufen - der Lauf liefe bis "fertig" durch und endete mit 0, obwohl er +# abgebrochen wurde. (Der EXIT-Trap allein wuerde zum Aufraeumen genuegen; hier geht es +# um den ehrlichen Exit-Code.) +# +# Die Trap-Zeilen werden aus setup_pi.sh HERAUSGELESEN, nicht hier nachgebaut: sonst +# prueft der Test seine eigene Kopie und bleibt gruen, wenn das Skript sie aendert. +TRAPS=$(grep -E "^trap '.*PROFILE_TMPDIR.*' (EXIT|INT|TERM|HUP)$" "$STATION_DIR/setup_pi.sh") +TRAP_COUNT=$(printf '%s\n' "$TRAPS" | grep -c . || true) +[ "$TRAP_COUNT" -ge 2 ] && pass "die Trap-Zeilen liessen sich aus setup_pi.sh auslesen ($TRAP_COUNT Stueck)" || fail "keine Trap-Zeilen in setup_pi.sh gefunden - Szenario 18 prueft sonst nichts" +SIGPROBE="$TMPROOT/sigprobe.sh" +{ + echo '#!/usr/bin/env bash' + echo 'set -eu' + echo 'PROFILE_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/ninlight-sigprobe.XXXXXX")' + printf '%s\n' "$TRAPS" + echo 'printf "%s" "$PROFILE_TMPDIR" >"$1"' + echo 'touch "$PROFILE_TMPDIR/profile.geheim"' + # Das Signal an den eigenen Prozess: deterministisch, ohne Wartezeiten. + echo 'kill -INT $$' + echo 'echo WEITERGELAUFEN' +} >"$SIGPROBE" +chmod +x "$SIGPROBE" +set +e +PROBE_OUT=$(TMPDIR="$TMPROOT" "$SIGPROBE" "$TMPROOT/sigprobe.dir" 2>&1) +PROBE_RC=$? +set -e +PROBE_DIR=$(cat "$TMPROOT/sigprobe.dir" 2>/dev/null || true) +echo "$PROBE_OUT" | grep -q WEITERGELAUFEN && fail "nach dem Strg-C lief das Skript weiter - der Lauf koennte mit 'fertig' und Exit 0 enden" || pass "das Skript laeuft nach dem Strg-C nicht weiter" +[ "$PROBE_RC" -eq 130 ] && pass "Exit-Code 130 (128+SIGINT), nicht 0" || fail "Exit-Code $PROBE_RC statt 130" +[ -n "$PROBE_DIR" ] && [ ! -d "$PROBE_DIR" ] && pass "das Temp-Verzeichnis mit dem Passwort wurde dabei aufgeraeumt" || fail "Temp-Verzeichnis blieb liegen: $PROBE_DIR" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/test_host.sh b/tools/test_host.sh index d6b9696..eb38429 100755 --- a/tools/test_host.sh +++ b/tools/test_host.sh @@ -24,11 +24,21 @@ else echo " tools/mkimage/test_nl_mkimage.py::test_stays_python38_compatible)" >&2 fi -# Alle Shell-Suiten unter tools/station/tests/ - test_deploy.sh fehlte hier und lief -# damit nur bei Handaufruf (Task 6b F1, I3): dieselbe Klasse von Luecke wie die aus -# keiner Suite gefallene test_nl_mkimage.py, nur eine Ebene hoeher. Mit ihr liefen 95 -# Zusagen nirgends mit, darunter die komplette Deploy-Haelfte der Anzeige-Auslieferung. -# tools/mkimage/tests/test_suite_coverage.py haelt fest, dass hier keine Suite fehlt. -"$ROOT/tools/station/tests/test_cli.sh" -"$ROOT/tools/station/tests/test_deploy.sh" -"$ROOT/tools/station/tests/test_setup_pi.sh" +# Alle Shell-Suiten - test_deploy.sh fehlte hier und lief damit nur bei Handaufruf +# (Task 6b F1, I3): dieselbe Klasse von Luecke wie die aus keiner Suite gefallene +# test_nl_mkimage.py, nur eine Ebene hoeher. Mit ihr liefen 95 Zusagen nirgends mit, +# darunter die komplette Deploy-Haelfte der Anzeige-Auslieferung. +# +# Als ausdrueckliche Liste, nicht als lose Folge von Aufrufen (Task 6b F2): so gibt es +# EINE Stelle, die vollstaendig sein muss, und +# tools/mkimage/tests/test_suite_coverage.py kann sie gegen das Dateisystem pruefen, +# statt im Skripttext nach Dateinamen zu suchen - eine Textsuche haelt schon ein +# nachgestellter Kommentar zum Narren. +SHELL_SUITES=( + "$ROOT/tools/station/tests/test_cli.sh" + "$ROOT/tools/station/tests/test_deploy.sh" + "$ROOT/tools/station/tests/test_setup_pi.sh" +) +for suite in "${SHELL_SUITES[@]}"; do + "$suite" +done From 46b0a198d93869567ab3528a251e4e66523fd777 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 01:32:53 +0200 Subject: [PATCH 127/150] fix(display-service): HOME/WorkingDirectory fuer lgpio unter systemd setzen Am Pi diagnostiziert: unter systemd ist HOME leer und cwd "/", lgpio legt seine Benachrichtigungs-Pipe aber unter "$HOME/.lgd-nfy0" an - das wird "//.lgd-nfy0" und scheitert an ProtectSystem=strict. gpiozero faellt bis zur experimentellen NativeFactory durch, die dann mit EINVAL abbricht; display.py faengt das als DisplayUnavailable ab und beendet sich mit 0, sodass der Ausfall wie ein fehlendes Panel aussieht statt wie die eigentliche Sandbox-Ursache. RuntimeDirectory=ninlight-display, WorkingDirectory= und Environment=HOME= zeigen jetzt auf ein eigenes, beschreibbares /run/ninlight-display - bewusst getrennt vom RuntimeDirectory der Hauptunit, da dieser Dienst dort nur liest. Szenario 19 in test_setup_pi.sh prueft die drei Zeilen in der ausgelieferten Unit; per Mutationsprobe bestaetigt, dass das Entfernen jeder einzelnen davon den Test rot werden laesst. --- tools/station/ninlight-display.service | 23 ++++++++++++++++ tools/station/tests/test_setup_pi.sh | 36 ++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/tools/station/ninlight-display.service b/tools/station/ninlight-display.service index 0c81a5a..7415261 100644 --- a/tools/station/ninlight-display.service +++ b/tools/station/ninlight-display.service @@ -33,6 +33,29 @@ Environment=NINLIGHT_STATION_CONF=/etc/ninlight-station.conf # trotzdem (mit den Environment=-Vorgaben oben) statt mit einem harten Fehler - "die # Anzeige darf nirgends Voraussetzung sein" gilt auch fuer ihre eigene Konfiguration. EnvironmentFile=-/etc/ninlight-station.conf +# RuntimeDirectory=/WorkingDirectory=/Environment=HOME= zusammen beheben einen Fund aus +# der Hardware-Abnahme: lgpio (gpiozero's bevorzugte Pin-Factory) legt beim Start eine +# Benachrichtigungs-Pipe unter "$HOME/.lgd-nfy0" an. Unter systemd ist HOME per Vorgabe +# LEER und das Arbeitsverzeichnis "/" - der Pfad wird "//.lgd-nfy0", und ProtectSystem= +# strict (unten) macht "/" schreibgeschuetzt. lgpio scheitert dann mit "Can't set +# permissions", gpiozero faellt ueber rpigpio/pigpio bis zur experimentellen NativeFactory +# durch, und DIE scheitert ihrerseits mit [Errno 22] Invalid argument - display.py faengt +# das korrekt als DisplayUnavailable ab und beendet sich mit 0 (siehe Kommentar zu +# Restart=on-failure unten), aber das Panel bleibt schwarz und das Journal sieht aus wie +# "kein Panel angeschlossen", obwohl die Hardware in Ordnung ist. Am Geraet nachgemessen +# (systemd-run mit denselben zwei Property=, gegen ein Shell-echo von $HOME und pwd). +# +# RuntimeDirectory=ninlight-display ist ABSICHTLICH ein eigenes Verzeichnis, nicht das +# RuntimeDirectory=ninlight-station der Hauptunit: dieser Dienst liest von dort nur +# (status.json/control.json ueber NINLIGHT_RUN_DIR, s.o.), er soll dort nichts anlegen +# koennen. Schriebe die Anzeige in fremden Laufzeitzustand, waere das eine unnoetige +# Kopplung an dessen Lebenszyklus (siehe die Begruendung zu ReadOnlyPaths= weiter unten: +# server.py's RuntimeDirectory wird beim Stop/Neustart geleert). systemd legt +# /run/ninlight-display beim Start dieser Unit selbst an (Vorgabe 0755, vom User dieses +# Dienstes - hier root, s.u. - beschreibbar) und raeumt es beim Stop wieder auf. +RuntimeDirectory=ninlight-display +WorkingDirectory=/run/ninlight-display +Environment=HOME=/run/ninlight-display ExecStart=/usr/bin/python3 /opt/ninlight-station/display/display.py # display.py faengt ein fehlendes/abgestuerztes Panel als DisplayUnavailable ab, diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index f8c0624..2f3b398 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -683,6 +683,42 @@ echo "$PROBE_OUT" | grep -q WEITERGELAUFEN && fail "nach dem Strg-C lief das Skr [ "$PROBE_RC" -eq 130 ] && pass "Exit-Code 130 (128+SIGINT), nicht 0" || fail "Exit-Code $PROBE_RC statt 130" [ -n "$PROBE_DIR" ] && [ ! -d "$PROBE_DIR" ] && pass "das Temp-Verzeichnis mit dem Passwort wurde dabei aufgeraeumt" || fail "Temp-Verzeichnis blieb liegen: $PROBE_DIR" +echo +echo "=== Szenario 19 (Hardware-Abnahme): der Anzeigedienst bekommt ein beschreibbares Arbeitsverzeichnis und ein gesetztes HOME ===" +# Befund vom echten Pi: unter systemd ist HOME leer und das Arbeitsverzeichnis "/" - +# lgpio legt seine Benachrichtigungs-Pipe unter "$HOME/.lgd-nfy0" an, das wird +# "//.lgd-nfy0", und ProtectSystem=strict (s.u.) macht "/" schreibgeschuetzt. lgpio +# scheitert, gpiozero faellt bis zur experimentellen NativeFactory durch, und die +# scheitert ihrerseits - das Panel bleibt schwarz, obwohl die Hardware in Ordnung ist. +# Kein systemd auf diesem (macOS-)Testrechner, also keine echte Namensraum-Pruefung - +# aber die Unit ist eine Textdatei, und "RuntimeDirectory=/WorkingDirectory=/ +# Environment=HOME= sind gesetzt" laesst sich daran pruefen, ob nun im Repo direkt +# oder (hier) in der von setup_pi.sh ausgelieferten Kopie - Szenario 12 bewacht +# schon, dass beide je identisch sind. +STATE="$TMPROOT/s19-state"; STUBBIN="$TMPROOT/bin19"; LOG="$TMPROOT/s19.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +INSTALLED_DISPLAY_UNIT="$STATE/etc/systemd/system/ninlight-display.service" +if [ -f "$INSTALLED_DISPLAY_UNIT" ]; then + grep -Eq '^RuntimeDirectory=ninlight-display$' "$INSTALLED_DISPLAY_UNIT" \ + && pass "RuntimeDirectory=ninlight-display gesetzt (eigenes Verzeichnis, nicht das der Hauptunit)" \ + || fail "RuntimeDirectory=ninlight-display fehlt - lgpio findet kein beschreibbares HOME unter systemd" + grep -Eq '^WorkingDirectory=/run/ninlight-display$' "$INSTALLED_DISPLAY_UNIT" \ + && pass "WorkingDirectory=/run/ninlight-display gesetzt" \ + || fail "WorkingDirectory=/run/ninlight-display fehlt" + grep -Eq '^Environment=HOME=/run/ninlight-display$' "$INSTALLED_DISPLAY_UNIT" \ + && pass "Environment=HOME=/run/ninlight-display gesetzt" \ + || fail "Environment=HOME=/run/ninlight-display fehlt - lgpio legt seine Pipe sonst unter //.lgd-nfy0 an (schreibgeschuetzt durch ProtectSystem=strict)" +else + fail "ninlight-display.service wurde nicht installiert - Szenario 19 kann nichts pruefen" +fi + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From bb39105ac470d0bd8490da729d6f2dd7fe2b29e7 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 02:24:14 +0200 Subject: [PATCH 128/150] feat(display): grosse Schrift, invertierter Kopf, GPIO-Uebergabe Zwei Befunde der Hardware-Abnahme. 1) Die Schrift war zu klein: compose() zeichnete alles in ImageFont.load_default() (rund 10 px) - auf 250x122 aus zwei Metern nicht lesbar. blocks() vergibt jetzt Rollen (header/hero/detail/big), compose() setzt jede Rolle eigen: Modus fett und weiss auf schwarzem Balken, die wichtigste Zeile gross (Migration: erkannte Lampe, Rettung: SSID), Beiwerk klein, "Station aus" gross und mittig. Die Schrift kommt per ImageFont.truetype() aus FONT_SEARCH_PATHS mit DejaVu (/usr/share/fonts/truetype/dejavu/) als Hauptweg - auf beiden Pis vorhanden. load_default(size=...) scheidet aus: das gibt es erst ab Pillow 10.1, Pi 2 hat 9.4.0. Letzter Rueckfall bleibt load_default() ohne Argument, damit eine fehlende Schrift die Anzeige (und damit nie eine Migration) aufhaelt. lines() bleibt ohne Pillow importierbar, die Breitenkuerzung misst weiter in Pixeln. 2) "display test" scheiterte bei laufendem Anzeigedienst mit einem rohen "lgpio.error: 'GPIO busy'": lgpio.error ist keine gpiozero.GPIOZeroError und fiel durch die Ausnahmeliste in epdconfig.module_init(). Sie ist jetzt aufgefuehrt - weiterhin gezielt, TypeError/AttributeError/NameError bleiben Programmierfehler - und die Meldung nennt bei belegten Leitungen den Anzeigedienst. "display test|clear" stoppt den Dienst fuer die Dauer des Handgriffs und startet ihn danach wieder, aber nur, wenn er vorher lief. --- tools/station/display/epdconfig.py | 71 ++++++-- tools/station/display/render.py | 249 ++++++++++++++++++++----- tools/station/ninlight-station | 45 ++++- tools/station/tests/test_cli.sh | 102 ++++++++--- tools/station/tests/test_render.py | 282 ++++++++++++++++++++++++++++- 5 files changed, 667 insertions(+), 82 deletions(-) diff --git a/tools/station/display/epdconfig.py b/tools/station/display/epdconfig.py index 875aff7..93f9ade 100644 --- a/tools/station/display/epdconfig.py +++ b/tools/station/display/epdconfig.py @@ -35,6 +35,15 @@ try: except ImportError: # pragma: no cover - dito spidev = None +# lgpio ist die Pin-Factory, die gpiozero auf beiden Pis benutzt (Debian 13 trixie wie +# Bookworm). Sie meldet einen belegten Pin nicht als gpiozero-Ausnahme, sondern als +# lgpio.error - siehe _hardware_error_types(). Auf der Entwicklungsmaschine fehlt das +# Modul; dann bleibt es None und die Fehlerliste kommt ohne es aus. +try: + import lgpio +except ImportError: # pragma: no cover - auf dem Pi immer vorhanden + lgpio = None + class DisplayUnavailable(Exception): """Kein Panel ansprechbar: Bibliothek fehlt, /dev/spidev0.0 fehlt, oder der @@ -89,6 +98,52 @@ def spi_writebyte2(data) -> None: _spi.writebytes2(data) +def _busy_hint(error: BaseException) -> str: + """Zusatz fuer die Fehlermeldung, wenn die Leitungen belegt sind. "GPIO busy" heisst + auf dieser Station praktisch immer "der Anzeigedienst haelt die Pins" - und trifft + ausgerechnet den Befehl, den der Hilfetext als ersten Handgriff der Abnahme nennt. + Die Meldung soll den Bediener also nicht zum Suchen schicken, sondern sagen, was los + ist. Leer, wenn es nicht danach aussieht - ein erfundener Hinweis waere schlimmer als + keiner.""" + if "busy" in str(error).lower(): + return (" - haelt der Anzeigedienst die Leitungen? " + "Pruefen mit 'systemctl status ninlight-display'") + return "" + + +def _hardware_error_types() -> tuple: + """Die Ausnahmen, die als "keine/blockierte Hardware" gelten und damit zu + DisplayUnavailable werden. Bewusst eine ENUMERATION, kein `except Exception`: ein + echter Programmierfehler an anderer Stelle (TypeError, AttributeError, NameError) + soll weiterhin als das erkennbar bleiben, was er ist, statt hier als "Panel fehlt" + getarnt zu werden (Fixrunde 1, C2, zweiter Teil). + + gpiozero.GPIOZeroError (nicht nur GPIODeviceError, Fixrunde 2, N4): die PinError- + Familie (PinInvalidPin, PinUnsupported, ... - z.B. bei falscher/fehlender + Pin-Factory-Zuordnung) haengt direkt unter GPIOZeroError, nicht unter + GPIODeviceError - per `gpiozero.PinInvalidPin.__mro__` nachgeprueft. BadPinFactory + erbt zusaetzlich von ImportError und war dadurch schon abgedeckt. + + lgpio.error (Hardware-Abnahme): laeuft der Anzeigedienst, haelt er die GPIO-Leitungen, + und ein gleichzeitiges "ninlight-station display test" bekommt beim Anlegen des ersten + Pins ein `lgpio.error: 'GPIO busy'`. Das ist KEINE gpiozero.GPIOZeroError - gpiozero + reicht die Ausnahme seiner Pin-Factory unveraendert durch - und fiel deshalb durch + diese Liste hindurch: statt der vorgesehenen einen Protokollzeile gab es einen rohen + Stapelabzug und rc=1. lgpio.error erbt direkt von Exception, die drei + Programmierfehler oben bleiben davon also unberuehrt. + + Wird bei JEDEM Aufruf neu gebaut statt einmal beim Import: nur so wirkt ein + monkeypatch.setattr(epdconfig, "lgpio", ...) im Test ueberhaupt (dasselbe Muster wie + bei gpiozero/spidev in tools/station/tests/test_render.py).""" + types: tuple = (OSError, ImportError) + if gpiozero is not None: + types = types + (gpiozero.GPIOZeroError,) + lgpio_error = getattr(lgpio, "error", None) if lgpio is not None else None + if isinstance(lgpio_error, type) and issubclass(lgpio_error, BaseException): + types = types + (lgpio_error,) + return types + + def module_init() -> int: """Oeffnet GPIO-Pins und SPI-Geraet. Wirft DisplayUnavailable statt einer rohen Bibliotheksausnahme, damit display.py genau eine Ausnahmeklasse abfangen muss. @@ -114,18 +169,7 @@ def module_init() -> int: if not os.path.exists(spi_path): raise DisplayUnavailable(f"{spi_path} fehlt - ist SPI aktiviert (raspi-config)?") - # Nur Fehler, die tatsaechlich von einer fehlenden/blockierten Hardware kommen, werden - # zu DisplayUnavailable - alles andere (ein echter Programmierfehler an anderer - # Stelle) soll weiterhin als das erkennbar bleiben, was es ist, statt hier als - # "Panel fehlt" getarnt zu werden (Fixrunde 1, C2, zweiter Teil). gpiozero.GPIOZeroError - # (nicht nur GPIODeviceError, Fixrunde 2, N4): die PinError-Familie (PinInvalidPin, - # PinUnsupported, ... - z.B. bei falscher/fehlender Pin-Factory-Zuordnung) haengt - # direkt unter GPIOZeroError, nicht unter GPIODeviceError - per - # `gpiozero.PinInvalidPin.__mro__` nachgeprueft. BadPinFactory erbt zusaetzlich von - # ImportError und war dadurch schon abgedeckt. - hardware_errors: tuple[type[BaseException], ...] = (OSError, ImportError) - if gpiozero is not None: - hardware_errors = hardware_errors + (gpiozero.GPIOZeroError,) + hardware_errors = _hardware_error_types() try: _rst_pin = gpiozero.DigitalOutputDevice(RST_PIN) @@ -138,7 +182,8 @@ def module_init() -> int: _spi.mode = 0b00 except hardware_errors as e: module_exit() - raise DisplayUnavailable(f"GPIO/SPI-Initialisierung fehlgeschlagen: {e}") from e + raise DisplayUnavailable( + f"GPIO/SPI-Initialisierung fehlgeschlagen: {e}{_busy_hint(e)}") from e return 0 diff --git a/tools/station/display/render.py b/tools/station/display/render.py index 6af297f..c45177c 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -1,11 +1,16 @@ """Textzerlegung und Bildaufbau fuer die E-Paper-Anzeige der ninlight-Station. -lines() ist bewusst ohne Pillow und ohne Hardwarezugriff importierbar und testbar (siehe +blocks()/lines() sind bewusst ohne Pillow und ohne Hardwarezugriff importierbar und testbar (siehe tools/station/tests/test_render.py) - compose() importiert Pillow erst innerhalb der Funktion, damit ein fehlendes PIL (das erst per "apt install python3-pil" auf den Pi kommt, siehe setup_pi.sh) render.py selbst nicht unbenutzbar macht. -Das Panel ist PANEL_WIDTH_PX x PANEL_HEIGHT_PX (250x122), quer verwendet. lines() liefert +Die Schrift kommt aus FONT_SEARCH_PATHS (DejaVu auf beiden Pis) statt aus +ImageFont.load_default(): default ist rund 10 px hoch und auf 250x122 aus zwei Metern +nicht lesbar (Befund der Hardware-Abnahme) - siehe _load_font(), warum der +naheliegende Weg load_default(size=...) auf Pi 2 (Pillow 9.4.0) ausscheidet. + +Das Panel ist PANEL_WIDTH_PX x PANEL_HEIGHT_PX (250x122), quer verwendet. blocks() liefert UNGEKUERZTE Zeilen - die Breitenkuerzung (mit "..." statt Umbruch, Spec: eine zu lange Zeile darf das Layout nicht sprengen) passiert erst in compose(), per _truncate_to_width() gegen die tatsaechliche, schriftabhaengige Pixelbreite (PIL.ImageFont.load_default() ist @@ -33,7 +38,72 @@ from typing import Callable PANEL_WIDTH_PX = 250 PANEL_HEIGHT_PX = 122 TEXT_MARGIN_PX = 2 -LINE_HEIGHT_PX = 14 + +# --- Rollen und ihre Groessen ------------------------------------------------------ +# +# Bis zur Hardware-Abnahme zeichnete compose() alle Zeilen in derselben +# ImageFont.load_default() (rund 10 px hoch) - auf 250x122 war das Kleingedrucktes und +# aus zwei Metern nicht mehr lesbar (Befund Michaels am Geraet). blocks() vergibt +# deshalb jeder Zeile eine ROLLE, und compose() setzt jede Rolle anders: +# +# header - Modus, fett, WEISS AUF SCHWARZEM BALKEN ueber die volle Panelbreite. +# Invertiert kostet auf e-Paper nichts (bistabil, kein Stromverbrauch pro +# Pixel) und trennt die Kopfzeile auf einen Blick vom Rest. +# hero - die eine wichtigste Information des Zustands (Migration: der erkannte +# Produktname; Rettung: die SSID), fett. +# detail - Beiwerk (Ziel/Variante, Lampenversion, Zaehler, letzte Auslieferung). +# big - Zustaende fast ohne Inhalt ("Station aus", "Status unbekannt"): gross und +# mittig, damit man quer durch den Raum sieht, dass nichts laeuft. +ROLE_HEADER = "header" +ROLE_HERO = "hero" +ROLE_DETAIL = "detail" +ROLE_BIG = "big" + +HEADER_FONT_PX = 20 +HERO_FONT_PX = 16 +DETAIL_FONT_PX = 11 +BIG_FONT_PX = 26 + +# Zeilenhoehen so gewaehlt, dass der vollste Zustand (Migration mit erkannter Lampe: +# Kopfbalken + grosse Zeile + sechs Beiwerkzeilen) exakt auf die 122 px passt und die +# letzte Zeile - "zuletzt ausgeliefert" - nicht mehr hinten abfaellt: +# 22 + 2 + 20 + 6*13 = 122. +HEADER_BAR_HEIGHT_PX = 22 +HERO_LINE_HEIGHT_PX = 20 +DETAIL_LINE_HEIGHT_PX = 13 + +# Suchkette fuer eine echte, skalierbare Schrift, in dieser Reihenfolge probiert +# (Regular, Bold). ImageFont.load_default(size=...) waere der naheliegende Weg, ist aber +# erst ab Pillow 10.1 vorhanden - Pi 2 (Raspberry Pi OS Bookworm) hat Pillow 9.4.0, und +# dort wirft load_default(size=...) TypeError ("unexpected keyword argument 'size'"), am +# Geraet gegengeprueft. Deshalb fuehrt der Hauptweg ueber ImageFont.truetype() auf eine +# Datei im Dateisystem: /usr/share/fonts/truetype/dejavu/ liegt auf BEIDEN Pis +# (Debian 13 trixie wie Bookworm) und ist damit der einzige Pfad, auf den es im Betrieb +# ankommt. Alles darunter existiert nur, damit compose() auch auf Rechnern ohne den +# Debian-Pfad eine skalierbare Schrift findet - insbesondere auf der macOS- +# Entwicklungsmaschine, wo tools/station/tests/test_render.py laufen muss. +# +# Letzter Rueckfall (in _load_font(), nicht in dieser Liste) ist load_default() OHNE +# Argument: dann ist die Schrift wieder klein, aber nichts stuerzt ab - die Zusage +# "eine fehlende Schrift darf die Anzeige (und damit nie eine Migration) aufhalten" +# gilt unveraendert. +FONT_SEARCH_PATHS = ( + # Debian/Raspberry Pi OS - der Betriebsfall auf beiden Pis. + ("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", + "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"), + # Andere Linux-Distributionen legen dieselbe Schrift woanders ab. + ("/usr/share/fonts/dejavu/DejaVuSans.ttf", + "/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf"), + ("/usr/share/fonts/TTF/DejaVuSans.ttf", + "/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"), + # DejaVu per Homebrew (macOS), falls vorhanden. + ("/opt/homebrew/share/fonts/DejaVuSans.ttf", + "/opt/homebrew/share/fonts/DejaVuSans-Bold.ttf"), + # macOS-Bordmittel - nur fuer die Entwicklungsmaschine, nie fuer den Pi. + ("/System/Library/Fonts/Supplemental/Arial.ttf", + "/System/Library/Fonts/Supplemental/Arial Bold.ttf"), + ("/Library/Fonts/Arial.ttf", "/Library/Fonts/Arial Bold.ttf"), +) MODE_LABELS = {"recovery": "RETTUNG", "migration": "MIGRATION"} # "armed" wird gesondert behandelt (braucht armed_target im Text), deshalb hier nicht @@ -49,74 +119,103 @@ def _hhmm(ts: object) -> str: return "--:--" -def lines(state: dict) -> list[str]: - """Zerlegt den zusammengefuehrten Zustand in UNGEKUERZTE Textzeilen fuers Panel (siehe - Modul-Docstring, warum die Kuerzung hier bewusst nicht passiert). Wirft nie - - fehlende/falsch geformte Felder werden ausgelassen statt eine Ausnahme zu werfen.""" +def blocks(state: dict) -> list[tuple[str, str]]: + """Zerlegt den zusammengefuehrten Zustand in UNGEKUERZTE Textzeilen mit ihrer ROLLE + (siehe Rollen-Kommentar oben, und den Modul-Docstring, warum die Kuerzung hier bewusst + nicht passiert). Wirft nie - fehlende/falsch geformte Felder werden ausgelassen statt + eine Ausnahme zu werfen. + + Bleibt wie lines() ohne Pillow importierbar und testbar: hier entsteht nur Text, kein + Bild - die Rolle ist ein reiner Bezeichner, die zugehoerigen Schriftgroessen kennt + ausschliesslich compose().""" state = state if isinstance(state, dict) else {} mode = state.get("mode") - out: list[str] = [] + out: list[tuple[str, str]] = [] if mode == "off": # Die Station ist komplett aus (kein status.json vorhanden) - display.py setzt # diesen Modus selbst, server.py kennt ihn nicht. Bewusst keine weiteren Zeilen # (insbesondere keine SSID - die waere aus einer alten Konfigurationsdatei und - # gerade nicht aktiv). - out.append("Station aus") + # gerade nicht aktiv). Genau deshalb ROLE_BIG: die Zeile ist der ganze Inhalt. + out.append(("Station aus", ROLE_BIG)) return out if mode not in ("recovery", "migration"): # Unbekannter oder fehlender Modus, z.B. ein frisch aufgesetzter Pi, auf dem noch # nichts geschrieben wurde. Lieber ehrlich "unbekannt" zeigen als einen Modus zu # unterstellen. - out.append(f"Status unbekannt ({mode})" if mode else "Status unbekannt") + out.append((f"Status unbekannt ({mode})" if mode else "Status unbekannt", ROLE_BIG)) return out - out.append(MODE_LABELS[mode]) + out.append((MODE_LABELS[mode], ROLE_HEADER)) ssid = state.get("ssid") - if ssid: - out.append(f"SSID: {ssid}") - if mode == "migration": + if mode == "recovery": + # Im Rettungsmodus ist die SSID das, was der Bediener am Geraet braucht (er muss + # sich mit genau diesem Netz verbinden) - deshalb gross und ohne "SSID: "-Praefix, + # das den Platz nur wegnaehme. + if ssid: + out.append((str(ssid), ROLE_HERO)) + else: # Phase kommt aus control.json, nicht aus status.json (das schreibt der Server # nur beim Start und nach einer Auslieferung) - display.py reicht sie hier als # Teil des zusammengefuehrten Zustands herein. Im Rettungsmodus gibt es keine # Phase, deshalb dieser Block nur hier drin. - phase = state.get("phase") - if phase == "armed": - target = state.get("armed_target") or "?" - out.append(f"FREIGEGEBEN: {target}") - elif phase in PHASE_LABELS: - out.append(PHASE_LABELS[phase]) - last_seen = state.get("last_seen") if not isinstance(last_seen, dict): last_seen = {} product_name = last_seen.get("product_name") + + # Reihenfolge bewusst anders als frueher (da stand die SSID als zweite Zeile): + # gross kommt das, was sich gerade aendert und worauf man wartet - die erkannte + # Lampe. Solange keine erkannt ist, ruecke die SSID nach oben, damit die grosse + # Zeile nie leer bleibt. + if product_name: + out.append((str(product_name), ROLE_HERO)) + elif ssid: + out.append((str(ssid), ROLE_HERO)) + + phase = state.get("phase") + if phase == "armed": + target = state.get("armed_target") or "?" + out.append((f"FREIGEGEBEN: {target}", ROLE_DETAIL)) + elif phase in PHASE_LABELS: + out.append((PHASE_LABELS[phase], ROLE_DETAIL)) + if product_name: - out.append(str(product_name)) target = last_seen.get("target") variant = last_seen.get("variant") if target or variant: - out.append(f"{target or '?'} ({variant or '?'})") + out.append((f"{target or '?'} ({variant or '?'})", ROLE_DETAIL)) lamp_version = last_seen.get("lamp_version") if lamp_version: - out.append(f"FW: {lamp_version}") + out.append((f"FW: {lamp_version}", ROLE_DETAIL)) + if ssid: + # Nur hier, wo die grosse Zeile schon vergeben ist - sonst stuende die + # SSID zweimal auf dem Panel. + out.append((f"SSID: {ssid}", ROLE_DETAIL)) served = state.get("served") - out.append(f"Ausgeliefert: {served if isinstance(served, int) else 0}") + out.append((f"Ausgeliefert: {served if isinstance(served, int) else 0}", ROLE_DETAIL)) recent = state.get("recent") if isinstance(recent, list) and recent: last = recent[-1] if isinstance(last, dict): - out.append(f"{_hhmm(last.get('ts'))} {last.get('target') or '?'} " - f"{last.get('status') or '?'}") + out.append((f"{_hhmm(last.get('ts'))} {last.get('target') or '?'} " + f"{last.get('status') or '?'}", ROLE_DETAIL)) return out +def lines(state: dict) -> list[str]: + """Nur die Texte aus blocks(), ohne Rollen - unveraenderte Schnittstelle fuer + display.run_daemon(), das damit erkennt, ob sich der Inhalt geaendert hat (die Rolle + ist dafuer unerheblich, sie haengt nur am Modus). Wie blocks() ohne Pillow benutzbar.""" + return [text for text, _role in blocks(state)] + + def _truncate_to_width(text: str, measure: Callable[[str], float], max_width_px: float) -> str: """Kuerzt `text` mit "…" am Ende, bis `measure(text)` nicht mehr breiter als `max_width_px` ist - nie umgebrochen, immer von rechts gekuerzt. @@ -137,33 +236,97 @@ def _truncate_to_width(text: str, measure: Callable[[str], float], max_width_px: return (trimmed + ellipsis) if trimmed else ellipsis +def _load_font(image_font, size_px: int, bold: bool = False): + """Laedt eine skalierbare Schrift der Groesse `size_px` aus FONT_SEARCH_PATHS - der + erste Pfad, der sich oeffnen laesst, gewinnt. + + `image_font` ist das PIL.ImageFont-MODUL, hereingereicht statt hier importiert: so + bleibt render.py (ausserhalb von compose()) ohne Pillow importierbar - dasselbe + Muster wie die Messfunktion in _truncate_to_width() - und die Kette laesst sich mit + einem nachgebauten Modul pruefen, ohne Schriftdateien anzulegen oder Pillow + herunterzustufen. + + Gefangen wird OSError (Datei fehlt/unlesbar/kein gueltiges Schriftformat) UND + ImportError: ein ohne FreeType gebautes Pillow laesst truetype() mit genau dieser + Ausnahme scheitern - dann darf die Anzeige nicht ausfallen, sondern faellt bis auf + load_default() durch. + + load_default() wird BEWUSST OHNE size-Argument gerufen: das gibt es erst ab + Pillow 10.1, Pi 2 hat 9.4.0 (siehe FONT_SEARCH_PATHS). Ein `size=`-Aufruf waere dort + ein TypeError mitten im Zeichnen - also genau in dem Pfad, der eigentlich der letzte + Rettungsanker ist.""" + for regular_path, bold_path in FONT_SEARCH_PATHS: + path = bold_path if bold else regular_path + try: + return image_font.truetype(path, size_px) + except (OSError, ImportError): + continue + return image_font.load_default() + + +def _draw_centered(draw, text: str, font, top: int, height: int, fill: int) -> None: + """Zeichnet `text` waagerecht mittig aufs Panel und senkrecht mittig in das Band + [top, top+height). Misst mit textbbox() statt mit der nominellen Schriftgroesse: die + tatsaechliche Tinte einer Zeile ist je nach Zeichen (Ober-/Unterlaengen) deutlich + kleiner als die Zeilenhoehe, und ein nach Zeilenhoehe zentrierter Text sieht auf + einem 122 px hohen Panel sichtbar zu tief aus.""" + bbox = draw.textbbox((0, 0), text, font=font) + x = (PANEL_WIDTH_PX - (bbox[2] - bbox[0])) // 2 - bbox[0] + y = top + (height - (bbox[3] - bbox[1])) // 2 - bbox[1] + draw.text((x, y), text, font=font, fill=fill) + + def compose(state: dict): - """Baut das Panel-Bild (PANEL_WIDTH_PX x PANEL_HEIGHT_PX, 1-Bit) aus lines(). + """Baut das Panel-Bild (PANEL_WIDTH_PX x PANEL_HEIGHT_PX, 1-Bit) aus blocks(). Importiert Pillow erst hier - siehe Modul-Docstring.""" from PIL import Image, ImageDraw, ImageFont image = Image.new("1", (PANEL_WIDTH_PX, PANEL_HEIGHT_PX), 255) draw = ImageDraw.Draw(image) - font = ImageFont.load_default() + fonts = { + ROLE_HEADER: _load_font(ImageFont, HEADER_FONT_PX, bold=True), + ROLE_HERO: _load_font(ImageFont, HERO_FONT_PX, bold=True), + ROLE_DETAIL: _load_font(ImageFont, DETAIL_FONT_PX), + ROLE_BIG: _load_font(ImageFont, BIG_FONT_PX, bold=True), + } max_width_px = PANEL_WIDTH_PX - 2 * TEXT_MARGIN_PX - def measure(text: str) -> float: - return draw.textlength(text, font=font) - - y = TEXT_MARGIN_PX - for raw_line in lines(state): - if y > PANEL_HEIGHT_PX - LINE_HEIGHT_PX: - break + def fit(raw_text: str, font) -> str: # draw.textlength() wirft ValueError("can't measure length of multiline text"), # sobald eine Zeile ein "\n" enthaelt (mit Pillow 12 nachgeprueft) - waere von # main()s except (DisplayUnavailable, ImportError) nicht gefangen (Fixrunde 3, - # F5). lines() erzeugt selbst kein "\n", aber Werte aus status.json/last_seen + # F5). blocks() erzeugt selbst kein "\n", aber Werte aus status.json/last_seen # (z.B. product_name) sind letztlich Daten von der Lampe/dem Netzwerk, kein - # Wert, den lines() selbst kontrolliert - ein Zeilenumbruch dort waere ohnehin + # Wert, den blocks() selbst kontrolliert - ein Zeilenumbruch dort waere ohnehin # nur ein zerstoertes Layout, kein sinnvoller Inhalt. - line = raw_line.replace("\n", " ") - fitted = _truncate_to_width(line, measure=measure, max_width_px=max_width_px) - draw.text((TEXT_MARGIN_PX, y), fitted, font=font, fill=0) - y += LINE_HEIGHT_PX + text = raw_text.replace("\n", " ") + return _truncate_to_width( + text, measure=lambda t: draw.textlength(t, font=font), + max_width_px=max_width_px) + + y = TEXT_MARGIN_PX + for raw_text, role in blocks(state): + font = fonts[role] + text = fit(raw_text, font) + + if role == ROLE_BIG: + # Einziger Block dieses Zustands (siehe blocks()) - er bekommt das ganze + # Panel. + _draw_centered(draw, text, font, top=0, height=PANEL_HEIGHT_PX, fill=0) + continue + + if role == ROLE_HEADER: + draw.rectangle((0, 0, PANEL_WIDTH_PX - 1, HEADER_BAR_HEIGHT_PX - 1), fill=0) + _draw_centered(draw, text, font, top=0, height=HEADER_BAR_HEIGHT_PX, fill=255) + y = HEADER_BAR_HEIGHT_PX + TEXT_MARGIN_PX + continue + + line_height = HERO_LINE_HEIGHT_PX if role == ROLE_HERO else DETAIL_LINE_HEIGHT_PX + if y + line_height > PANEL_HEIGHT_PX: + # Passt nicht mehr aufs Panel - abschneiden statt ueberzeichnen. Die + # Reihenfolge in blocks() ist deshalb nach Wichtigkeit sortiert. + break + draw.text((TEXT_MARGIN_PX, y), text, font=font, fill=0) + y += line_height return image diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index abf57ed..4c07923 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -265,6 +265,43 @@ display_run() { sudo env EPD_REVISION="$EPD_REVISION" python3 "$STATION_LIB/display/display.py" "$1" } +# Handbetrieb am Panel ("display test"/"display clear"), waehrend der Anzeigedienst +# laufen kann. Beide wollen dieselben GPIO-Leitungen, und die kann nur einer haben: bei +# laufendem Dienst scheiterte der Handgriff mit "lgpio.error: 'GPIO busy'", rc=1. +# +# Warum stoppen-und-wieder-starten statt "brich mit einer Anweisung ab": 'display test' +# ist laut Hilfetext der ERSTE Handgriff der Hardware-Abnahme und die einzige +# Moeglichkeit, die Panel-Revision zu bestimmen. Ein Abbruch wuerde also ausgerechnet dem +# Abnehmer, der die Station gerade zum ersten Mal in der Hand hat, eine Hausaufgabe +# stellen ("erst den Dienst stoppen, dann nochmal") - und zwar bei jedem einzelnen +# Versuch, denn die Revision bestimmt man durch Ausprobieren beider Werte. Das Stoppen +# ist ausserdem gefahrlos: der Dienst zeichnet nur, er ist an keiner Migration beteiligt. +# +# Der Dienst wird NUR wieder gestartet, wenn er vorher lief - sonst haette ein +# Abnahme-Handgriff den Dienst nebenbei eingeschaltet und den Zustand der Station +# veraendert, was 'off' zuvor bewusst hergestellt hatte. Deshalb "is-active" vorher +# fragen statt blind "stop" und "start". +# +# Rueckgabewert ist der von display.py - der Handgriff muss weiterhin rot werden, wenn +# das Panel nicht antwortet; das Wiederanfahren des Dienstes darf ihn weder verdecken +# noch (bei eigenem Fehlschlag) einen Panel-Erfolg in einen Fehler verwandeln. +display_handover() { + _was_active=0 + if systemctl is-active --quiet ninlight-display 2>/dev/null; then + _was_active=1 + sudo systemctl stop ninlight-display 2>/dev/null || true + fi + + _rc=0 + display_run "$1" || _rc=$? + + if [ "$_was_active" -eq 1 ]; then + sudo systemctl start ninlight-display 2>/dev/null \ + || echo "Warnung: Anzeigedienst konnte nicht wieder gestartet werden - 'systemctl status ninlight-display' pruefen" >&2 + fi + return "$_rc" +} + # Nimmt die Anzeige mit aus. Reihenfolge: erst den Dienst stoppen, dann das Panel leeren - # umgekehrt zeichnet der noch laufende Daemon binnen 5 s sein altes Bild wieder darauf. # @@ -474,8 +511,8 @@ PY display_cmd() { case "${1:-}" in - test) display_run --test ;; - clear) display_run --clear ;; + test) display_handover --test ;; + clear) display_handover --clear ;; off) display_off ;; *) usage; exit 2 ;; esac @@ -491,7 +528,9 @@ usage() { echo " /etc/ninlight-station.conf falsch - auf die andere Revision" >&2 echo " umstellen (V2 statt V3 oder umgekehrt), das Panel kurz" >&2 echo " stromlos machen und erneut 'display test'." >&2 - echo " display clear Panel leeren." >&2 + echo " Ein laufender Anzeigedienst wird dafuer kurz gestoppt und" >&2 + echo " danach wieder gestartet (er haelt sonst die GPIO-Leitungen)." >&2 + echo " display clear Panel leeren (ebenfalls mit kurzer Unterbrechung des Dienstes)." >&2 echo " display off Anzeigedienst stoppen und das Panel leeren (macht 'off' mit)." >&2 } diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index 2651214..375d32a 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -123,12 +123,21 @@ RUN_DIR="$rundir" UNIT_FILE="$unitfile" case "\$1" in is-active) - if [ "\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-0}" -eq 0 ]; then + # Getrennt schaltbar je Unit: der Handbetrieb am Panel fragt nach ninlight-display + # (laeuft der Anzeigedienst?), alles andere nach ninlight-station. Die Unit ist + # immer das letzte Argument ("is-active --quiet " bzw. "is-active "). + unit="\${!#}" + if [ "\$unit" = "ninlight-display" ]; then + code="\${FAKE_DISPLAY_SERVICE_ACTIVE_EXIT:-0}" + else + code="\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-0}" + fi + if [ "\$code" -eq 0 ]; then echo active exit 0 else echo inactive - exit "\${FAKE_SYSTEMCTL_ACTIVE_EXIT}" + exit "\$code" fi ;; restart|stop) @@ -137,15 +146,14 @@ case "\$1" in # RuntimeDirectory bei JEDEM Stop/Restart, nicht nur beim allerersten Start, # und legt es leer wieder an. # - # ACHTUNG, Stub-Erfindung (Task 6b F1): die Regel wird hier auf JEDE - # Unit angewandt, also auch auf ninlight-display - die traegt aber gar kein - # RuntimeDirectory=, ein "stop ninlight-display" raeumt in Wirklichkeit nichts - # weg. \$UNIT_FILE ist immer ninlight-station.service, unabhaengig davon, welche - # Unit im Aufruf steht. Heute folgenlos (kein Testfall stoppt die Anzeige und - # erwartet danach noch Dateien in RUN_DIR), aber niemand darf sich darauf - # verlassen: wer kuenftig prueft, was ein "display off" mit RUN_DIR macht, muss - # den Stub vorher nach Unit unterscheiden lassen. - if ! grep -q '^RuntimeDirectoryPreserve=restart' "\$UNIT_FILE" 2>/dev/null; then + # Die Regel gilt nur fuer ninlight-station - \$UNIT_FILE ist deren Unit-Datei. + # ninlight-display traegt gar kein RuntimeDirectory=, ein "stop ninlight-display" + # raeumt in Wirklichkeit nichts weg (frueher tat der Stub das trotzdem fuer JEDE + # Unit; harmlos, solange kein Testfall die Anzeige stoppt und danach noch Dateien + # in RUN_DIR erwartet - genau das tun 'display test'/'display clear' jetzt aber, + # die den Anzeigedienst waehrend des Handgriffs stoppen). + if [ "\$2" = "ninlight-station" ] \ + && ! grep -q '^RuntimeDirectoryPreserve=restart' "\$UNIT_FILE" 2>/dev/null; then rm -rf "\$RUN_DIR" mkdir -p "\$RUN_DIR" fi @@ -274,8 +282,8 @@ OUT=$(run_cli "$SBX" migrate on 2>&1) RC=$? set -e [ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" -DOWN_LINE=$(grep -n "nmcli con down ninlight-recovery" "$SBX/cmd.log" | head -n1 | cut -d: -f1) -UP_LINE=$(grep -n "nmcli con up qconnex-firmware" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +DOWN_LINE=$(grep -n "nmcli con down ninlight-recovery" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) +UP_LINE=$(grep -n "nmcli con up qconnex-firmware" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) if [ -n "$DOWN_LINE" ] && [ -n "$UP_LINE" ] && [ "$DOWN_LINE" -lt "$UP_LINE" ]; then pass "ninlight-recovery wird VOR qconnex-firmware heruntergefahren" else @@ -595,9 +603,25 @@ grep -Fq "STUB display.py --test" "$SBX/cmd.log" && pass "display.py wurde mit - # er ist wertlos, wenn er nicht die Revision aus der Konfiguration durchreicht: # EPD_REVISION wird per ". $CONF" nur als Shell-Variable gesetzt, nicht exportiert. grep -Fq "STUB display.py --test (EPD_REVISION=V3)" "$SBX/cmd.log" && pass "EPD_REVISION aus der Konfiguration erreicht den Python-Prozess" || fail "EPD_REVISION kam nicht durch: $(cat "$SBX/cmd.log")" -# Nicht gleichzeitig den Anzeigedienst umbringen: 'display test' ist ein Handgriff bei -# der Abnahme, kein Ausschalten. -grep -Fq "systemctl stop ninlight-display" "$SBX/cmd.log" && fail "'display test' stoppt den Anzeigedienst - das soll nur 'display off'/'off'" || pass "'display test' laesst den Anzeigedienst in Ruhe" +# Der Anzeigedienst haelt die GPIO-Leitungen; ohne ihn kurz zu stoppen scheitert der +# Handgriff am Geraet mit "lgpio.error: 'GPIO busy'" (Hardware-Abnahme). Frueher stand +# hier die umgekehrte Zusage ("laesst den Anzeigedienst in Ruhe") - die war genau der +# Grund, warum der erste Handgriff der Abnahme nicht funktionierte. +STOP_LINE=$(grep -n "STUB systemctl stop ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) +TEST_LINE=$(grep -n "STUB display.py --test" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) +START_LINE=$(grep -n "STUB systemctl start ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) +if [ -n "$STOP_LINE" ] && [ -n "$TEST_LINE" ] && [ "$STOP_LINE" -lt "$TEST_LINE" ]; then + pass "der Anzeigedienst wird gestoppt, BEVOR display.py die GPIO-Leitungen anfasst" +else + fail "Anzeigedienst nicht (rechtzeitig) gestoppt (stop=$STOP_LINE, test=$TEST_LINE): $(cat "$SBX/cmd.log")" +fi +# Und danach wieder an: ein Abnahme-Handgriff darf die Station nicht dauerhaft ohne +# Anzeige zuruecklassen. +if [ -n "$START_LINE" ] && [ -n "$TEST_LINE" ] && [ "$TEST_LINE" -lt "$START_LINE" ]; then + pass "der Anzeigedienst laeuft danach wieder (start=$START_LINE)" +else + fail "Anzeigedienst wurde nicht wieder gestartet (test=$TEST_LINE, start=$START_LINE): $(cat "$SBX/cmd.log")" +fi echo echo "=== Fall D2: 'display clear' ruft display.py mit --clear auf ===" @@ -611,6 +635,42 @@ set -e [ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" grep -Fq "STUB display.py --clear" "$SBX/cmd.log" && pass "display.py wurde mit --clear aufgerufen" || fail "kein '--clear'-Aufruf im Log: $(cat "$SBX/cmd.log")" grep -Fq "STUB display.py --test" "$SBX/cmd.log" && fail "'display clear' hat --test aufgerufen" || pass "kein --test bei 'display clear'" +# Dieselbe GPIO-Kollision wie bei 'display test' - auch 'display clear' braucht die +# Leitungen fuer sich. +grep -Fq "STUB systemctl stop ninlight-display" "$SBX/cmd.log" && pass "'display clear' stoppt den Anzeigedienst fuer die Dauer" || fail "'display clear' laesst den Anzeigedienst laufen - das gibt 'GPIO busy': $(cat "$SBX/cmd.log")" +grep -Fq "STUB systemctl start ninlight-display" "$SBX/cmd.log" && pass "'display clear' startet den Anzeigedienst danach wieder" || fail "'display clear' laesst den Anzeigedienst aus: $(cat "$SBX/cmd.log")" + +echo +echo "=== Fall D2a: war der Anzeigedienst aus, bleibt er nach 'display test' aus ===" +# Sonst haette ein Abnahme-Handgriff die Station nebenbei umgeschaltet: nach +# 'ninlight-station off' ist der Anzeigedienst bewusst gestoppt, und 'display test' +# darf ihn nicht wieder hochziehen. +SBX="$TMPROOT/tD2a" +setup_sandbox "$SBX" +: >"$SBX/cmd.log" +set +e +OUT=$(FAKE_DISPLAY_SERVICE_ACTIVE_EXIT=3 run_cli "$SBX" display test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fq "STUB display.py --test" "$SBX/cmd.log" && pass "Testbild wurde trotzdem gezeichnet" || fail "kein '--test'-Aufruf: $(cat "$SBX/cmd.log")" +grep -Fq "STUB systemctl start ninlight-display" "$SBX/cmd.log" && fail "'display test' hat den Anzeigedienst eingeschaltet, obwohl er aus war" || pass "kein 'start' fuer einen Dienst, der gar nicht lief" +grep -Fq "STUB systemctl stop ninlight-display" "$SBX/cmd.log" && fail "'display test' stoppt einen Dienst, der gar nicht laeuft" || pass "kein ueberfluessiges 'stop'" + +echo +echo "=== Fall D2b: scheitert das Panel, meldet 'display test' das - der Dienst laeuft trotzdem wieder ===" +# Der Rueckgabewert von display.py ist das Ergebnis des Handgriffs; ein erfolgreiches +# Wiederanfahren des Dienstes darf ihn nicht in eine Erfolgsmeldung verwandeln. Und +# umgekehrt darf ein gescheitertes Testbild den Dienst nicht dauerhaft aus lassen. +SBX="$TMPROOT/tD2b" +setup_sandbox "$SBX" +: >"$SBX/cmd.log" +set +e +OUT=$(FAKE_DISPLAY_EXIT=1 run_cli "$SBX" display test 2>&1) +RC=$? +set -e +[ "$RC" -eq 1 ] && pass "Exit-Code 1 wird durchgereicht" || fail "Exit-Code $RC statt 1: $OUT" +grep -Fq "STUB systemctl start ninlight-display" "$SBX/cmd.log" && pass "Anzeigedienst laeuft trotz Panel-Fehler wieder" || fail "Anzeigedienst blieb nach dem Fehler aus: $(cat "$SBX/cmd.log")" echo echo "=== Fall D3 (Task 6b, Punkt 4): 'display off' stoppt den Dienst VOR dem Leeren des Panels ===" @@ -624,8 +684,8 @@ OUT=$(run_cli "$SBX" display off 2>&1) RC=$? set -e [ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" -STOP_LINE=$(grep -n "STUB systemctl stop ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1) -CLEAR_LINE=$(grep -n "STUB display.py --clear" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +STOP_LINE=$(grep -n "STUB systemctl stop ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) +CLEAR_LINE=$(grep -n "STUB display.py --clear" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) if [ -n "$STOP_LINE" ] && [ -n "$CLEAR_LINE" ] && [ "$STOP_LINE" -lt "$CLEAR_LINE" ]; then pass "Anzeigedienst wird gestoppt, BEVOR das Panel geleert wird (stop=$STOP_LINE, clear=$CLEAR_LINE)" else @@ -649,8 +709,8 @@ set -e [ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" grep -Fq "STUB systemctl stop ninlight-display" "$SBX/cmd.log" && pass "'off' stoppt den Anzeigedienst" || fail "'off' stoppt den Anzeigedienst nicht: $(cat "$SBX/cmd.log")" grep -Fq "STUB display.py --clear" "$SBX/cmd.log" && pass "'off' leert das Panel" || fail "'off' leert das Panel nicht: $(cat "$SBX/cmd.log")" -STOP_LINE=$(grep -n "STUB systemctl stop ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1) -CLEAR_LINE=$(grep -n "STUB display.py --clear" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +STOP_LINE=$(grep -n "STUB systemctl stop ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) +CLEAR_LINE=$(grep -n "STUB display.py --clear" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) if [ -n "$STOP_LINE" ] && [ -n "$CLEAR_LINE" ] && [ "$STOP_LINE" -lt "$CLEAR_LINE" ]; then pass "auch bei 'off': erst stoppen, dann leeren" else @@ -658,7 +718,7 @@ else fi # Das Panel muss geleert sein, BEVOR ap_switch das Ergebnis von "off" verkuenden kann - # und vor allem, bevor ap_switch bei haengendem NetworkManager abbricht (Fall D6). -UP_LINE=$(grep -n "nmcli con up home-wifi" "$SBX/cmd.log" | head -n1 | cut -d: -f1) +UP_LINE=$(grep -n "nmcli con up home-wifi" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) if [ -n "$UP_LINE" ] && [ "$CLEAR_LINE" -lt "$UP_LINE" ]; then pass "Panel ist geleert, bevor wlan0 ins Heim-WLAN zurueckgeschaltet wird" else diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 68db65d..59c1f4a 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -2,6 +2,8 @@ import json import sys from pathlib import Path +import pytest + sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "display")) import display # noqa: E402 @@ -75,7 +77,12 @@ def test_compose_does_not_draw_into_the_last_column_for_long_lines(): (empirisch mit echtem Pillow bestaetigt: die rechte Kante liegt bei ca. Spalte 237, deutlich vor Spalte 249). Mit deaktivierter Kuerzung fuellt der zeichenweise laengere Text die Zeile bis zum Rand - ebenfalls empirisch bestaetigt (die letzte Spalte - enthaelt dann einen schwarzen Pixel).""" + enthaelt dann einen schwarzen Pixel). + + Geprueft wird erst UNTERHALB des Kopfbalkens: der ist seit der Schriftumstellung + absichtlich vollflaechig schwarz (weisse Schrift auf schwarzem Grund) und faerbt die + letzte Spalte in seinen eigenen Zeilen mit - das ist Layout, keine ueberlaufende + Zeile. Die absichtlich zu lange Zeile (der Produktname) steht ohnehin darunter.""" state = {"mode": "migration", "phase": "listen", "served": 0, "recent": [], "ssid": "qconnex-firmware", "last_seen": {"product_name": "W" * 200, "target": "e27_rgb", @@ -85,10 +92,207 @@ def test_compose_does_not_draw_into_the_last_column_for_long_lines(): pixels = image.load() last_col = render.PANEL_WIDTH_PX - 1 - black_in_last_column = any(pixels[last_col, y] == 0 for y in range(render.PANEL_HEIGHT_PX)) + black_in_last_column = any( + pixels[last_col, y] == 0 + for y in range(render.HEADER_BAR_HEIGHT_PX, render.PANEL_HEIGHT_PX)) assert not black_in_last_column, "Text erreicht die letzte Spalte - Kuerzung wirkt nicht" +# --- Schriftgroesse/Layout (Hardware-Abnahme: "die Schrift ist zu klein") ----------- +# +# Alle Tests hier messen die TINTE im tatsaechlich gezeichneten Bild, nicht die +# Konstanten oben: eine Zusage ueber die Schriftgroesse, die nur die Konstante liest, +# haelt niemanden auf, der die Schriftsuche entfernt und wieder auf +# ImageFont.load_default() (rund 10 px) zurueckfaellt - genau die Mutation, gegen die +# diese Tests wachen sollen. + +def _ink_box(image, y0=0, y1=None): + """(links, oben, rechts, unten) der schwarzen Pixel im Band [y0, y1), oder None.""" + y1 = image.height if y1 is None else y1 + pixels = image.load() + xs, ys = [], [] + for y in range(y0, y1): + for x in range(image.width): + if pixels[x, y] == 0: + xs.append(x) + ys.append(y) + if not xs: + return None + return (min(xs), min(ys), max(xs), max(ys)) + + +def _migration_state(): + return {"mode": "migration", "phase": "armed", "armed_target": "gu10_rgb", + "served": 3, "ssid": "qconnex-firmware", + "last_seen": {"product_name": "Klyqa GU10 RGB", "target": "gu10_rgb", + "variant": "prod", "lamp_version": "V1.1.0.1138"}, + "recent": [{"target": "gu10_rgb", "status": "ok", "ts": "2026-09-13T13:04:02Z"}]} + + +def test_off_screen_is_drawn_big_and_centered(): + """"Station aus" hat fast keinen Inhalt und muss aus zwei Metern lesbar sein. + + Gemessen mit echtem Pillow: mit der Schriftsuche ist die Tinte 19 px hoch und links + wie rechts 56 px vom Rand entfernt; faellt compose() auf load_default() zurueck, sind + es 8 px - die Schwelle 14 trennt beide Faelle eindeutig.""" + image = render.compose({"mode": "off"}) + box = _ink_box(image) + assert box is not None, "gar nichts gezeichnet" + + height = box[3] - box[1] + 1 + assert height >= 14, f"Schrift nur {height} px hoch - Schriftsuche wirkt nicht" + + left, right = box[0], render.PANEL_WIDTH_PX - 1 - box[2] + assert abs(left - right) <= 3, f"nicht waagerecht mittig (links {left}, rechts {right})" + top, bottom = box[1], render.PANEL_HEIGHT_PX - 1 - box[3] + assert abs(top - bottom) <= 4, f"nicht senkrecht mittig (oben {top}, unten {bottom})" + + +def test_header_is_a_black_bar_with_white_text(): + """Die Kopfzeile ist invertiert: schwarzer Balken ueber die volle Breite, Modus in + Weiss darin. Geprueft an einer Zeile mitten im Balken - die muss ueberwiegend schwarz + sein UND weisse Pixel enthalten (sonst stuende dort kein Text, sondern nur ein + Balken).""" + image = render.compose(_migration_state()) + pixels = image.load() + row = render.HEADER_BAR_HEIGHT_PX // 2 + + black = sum(1 for x in range(render.PANEL_WIDTH_PX) if pixels[x, row] == 0) + white = sum(1 for x in range(render.PANEL_WIDTH_PX) if pixels[x, row] != 0) + assert black > 150, f"Kopfzeile ist kein schwarzer Balken (nur {black} schwarze Pixel)" + assert white >= 5, "keine weisse Schrift im Balken - der Balken uebermalt den Modus" + + +def test_hero_line_is_bigger_than_the_details(): + """Die wichtigste Zeile (hier: der erkannte Produktname) muss sich vom Beiwerk + abheben - sonst ist die Rollenvergabe in blocks() folgenlos. Gemessen: 15 px Tinte + fuer die grosse Zeile gegen 10 px fuer die erste Beiwerkzeile.""" + image = render.compose(_migration_state()) + hero_top = render.HEADER_BAR_HEIGHT_PX + render.TEXT_MARGIN_PX + hero_bottom = hero_top + render.HERO_LINE_HEIGHT_PX + + hero = _ink_box(image, hero_top, hero_bottom) + detail = _ink_box(image, hero_bottom, hero_bottom + render.DETAIL_LINE_HEIGHT_PX) + assert hero is not None and detail is not None + + hero_height = hero[3] - hero[1] + 1 + detail_height = detail[3] - detail[1] + 1 + assert hero_height >= detail_height + 3, ( + f"grosse Zeile ({hero_height} px) hebt sich nicht vom Beiwerk " + f"({detail_height} px) ab") + + +def test_the_last_delivery_line_still_fits_on_the_panel(): + """Der vollste Zustand (Kopfbalken, grosse Zeile, sechs Beiwerkzeilen) muss + vollstaendig aufs Panel passen - mit groesserer Schrift ist das keine + Selbstverstaendlichkeit mehr, und ausgerechnet die letzte Zeile ("zuletzt + ausgeliefert") faellt sonst stumm hinten herunter.""" + state = _migration_state() + expected = render.lines(state) + assert len(expected) == 8, expected # sonst misst dieser Test etwas anderes + + image = render.compose(state) + last_line_top = (render.HEADER_BAR_HEIGHT_PX + render.TEXT_MARGIN_PX + + render.HERO_LINE_HEIGHT_PX + 5 * render.DETAIL_LINE_HEIGHT_PX) + assert _ink_box(image, last_line_top, render.PANEL_HEIGHT_PX) is not None, ( + "letzte Zeile fehlt - sie passt nicht mehr aufs Panel") + + +def test_compose_still_works_without_any_font_file(monkeypatch): + """Die Zusage "eine fehlende Schrift haelt die Anzeige nicht auf" gilt unveraendert: + ohne einen einzigen Treffer in der Suchkette faellt compose() auf load_default() + zurueck und zeichnet - klein, aber vorhanden.""" + monkeypatch.setattr(render, "FONT_SEARCH_PATHS", ()) + image = render.compose({"mode": "off"}) + assert _ink_box(image) is not None + + +# --- Die Schriftsuche selbst, ohne echte Schriftdateien und ohne altes Pillow ------- + +class _FakeImageFontModule: + """Nachbau der zwei ImageFont-Funktionen, die _load_font() benutzt. + + `available` sind die Pfade, die es "gibt"; jeder andere truetype()-Aufruf scheitert + mit OSError, genau wie Pillow es bei einer fehlenden Datei tut. + + load_default() bildet Pillow 9.4.0 auf Pi 2 nach: KEIN size-Argument (das gibt es + erst ab 10.1). Ein `size=`-Aufruf schlaegt hier deshalb mit demselben TypeError fehl + wie am Geraet - so laesst sich die 9.4-Lage pruefen, ohne Pillow herunterzustufen.""" + + def __init__(self, available=()): + self.available = set(available) + self.truetype_calls = [] + self.load_default_calls = 0 + + def truetype(self, path, size): + self.truetype_calls.append((path, size)) + if path not in self.available: + raise OSError(f"cannot open resource: {path}") + return ("truetype", path, size) + + def load_default(self, *args, **kwargs): + self.load_default_calls += 1 + if args or kwargs: + raise TypeError( + "load_default() got an unexpected keyword argument 'size'") + return ("default",) + + +DEJAVU_REGULAR = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" +DEJAVU_BOLD = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" + + +def test_font_search_takes_dejavu_first_on_the_pi(): + """Auf beiden Pis liegt DejaVu unter /usr/share/fonts/truetype/dejavu/ - das ist der + Hauptweg, und er muss VOR jedem Rueckfall kommen. Rot, sobald jemand die Suche + entfernt und wieder direkt auf load_default() geht.""" + image_font = _FakeImageFontModule(available=(DEJAVU_REGULAR, DEJAVU_BOLD)) + + regular = render._load_font(image_font, 11) + bold = render._load_font(image_font, 20, bold=True) + + assert regular == ("truetype", DEJAVU_REGULAR, 11) + assert bold == ("truetype", DEJAVU_BOLD, 20) + assert image_font.truetype_calls[0] == (DEJAVU_REGULAR, 11), \ + "DejaVu wurde nicht zuerst probiert" + assert image_font.load_default_calls == 0, \ + "load_default() benutzt, obwohl DejaVu da ist - die Schrift waere wieder winzig" + + +def test_font_search_falls_through_to_a_later_candidate(): + """Auf der Entwicklungsmaschine (macOS) gibt es den Debian-Pfad nicht - dann muss die + Kette weiterlaufen statt sofort auf load_default() zu fallen, sonst laeuft der Test + oben dort gegen eine 10-px-Schrift.""" + arial = "/System/Library/Fonts/Supplemental/Arial.ttf" + image_font = _FakeImageFontModule(available=(arial,)) + + assert render._load_font(image_font, 16) == ("truetype", arial, 16) + assert image_font.load_default_calls == 0 + + +def test_font_search_ends_at_load_default_without_a_size_argument(): + """Der letzte Rueckfall auf einem Rechner ohne jede Schriftdatei. Entscheidend ist, + dass load_default() OHNE size gerufen wird: auf Pi 2 (Pillow 9.4.0) waere + load_default(size=...) ein TypeError - der Fake wirft ihn hier genauso.""" + image_font = _FakeImageFontModule() + + assert render._load_font(image_font, 26, bold=True) == ("default",) + assert image_font.load_default_calls == 1 + assert len(image_font.truetype_calls) == len(render.FONT_SEARCH_PATHS) + + +def test_font_search_survives_a_pillow_without_freetype(): + """Ein ohne FreeType gebautes Pillow laesst truetype() mit ImportError scheitern - + auch das darf die Anzeige nicht umbringen.""" + + class _NoFreetype(_FakeImageFontModule): + def truetype(self, path, size): + raise ImportError("The _imagingft C module is not installed") + + image_font = _NoFreetype() + assert render._load_font(image_font, 20) == ("default",) + + def test_missing_fields_do_not_raise(): """status.json kann jederzeit halb geschrieben oder aus einer aelteren Version sein.""" for state in ({}, {"mode": "migration"}, {"mode": "migration", "last_seen": {}}): @@ -243,6 +447,80 @@ def _stub_hardware(monkeypatch): monkeypatch.setattr(epdconfig, "_busy_pin", None) +# --- "GPIO busy" (Hardware-Abnahme, zweiter Befund) --------------------------------- + +class _FakeLgpioModule: + """Nur das, was epdconfig von lgpio kennt: die Ausnahmeklasse. Wie im echten Modul + erbt sie direkt von Exception - deshalb faengt eine Liste, die sie enthaelt, keinen + TypeError/AttributeError/NameError mit ein.""" + + class error(Exception): + pass + + +def _gpiozero_raising(exception): + """Ein gpiozero-Ersatz, dessen erster Pin-Aufbau `exception` wirft.""" + + class _Raising: + GPIOZeroError = _FakeGpiozeroModule.GPIOZeroError + DigitalInputDevice = _FakeDigitalInputDevice + + @staticmethod + def DigitalOutputDevice(pin): + raise exception + + return _Raising + + +def test_lgpio_gpio_busy_becomes_display_unavailable(monkeypatch): + """Der Befund vom Geraet: laeuft der Anzeigedienst, haelt er die GPIO-Leitungen, und + "ninlight-station display test" bekam ein rohes `lgpio.error: 'GPIO busy'` samt + Stapelabzug und rc=1 - lgpio.error ist KEINE gpiozero.GPIOZeroError und fiel durch + die Ausnahmeliste in module_init(). Erwartet ist stattdessen DisplayUnavailable, + das display.main() zu genau einer Protokollzeile und sys.exit(0) macht.""" + _stub_hardware(monkeypatch) + monkeypatch.setattr(epdconfig, "lgpio", _FakeLgpioModule) + monkeypatch.setattr(epdconfig, "gpiozero", + _gpiozero_raising(_FakeLgpioModule.error("'GPIO busy'"))) + + with pytest.raises(epdconfig.DisplayUnavailable) as excinfo: + epdconfig.module_init() + + message = str(excinfo.value) + assert "GPIO busy" in message + # Und die Meldung muss dem Bediener sagen, was hier praktisch immer los ist, statt + # ihn den lgpio-Fehler googeln zu lassen. + assert "ninlight-display" in message, f"kein Hinweis auf den Anzeigedienst: {message}" + + +def test_gpio_busy_hint_stays_away_from_unrelated_errors(monkeypatch): + """Der Hinweis auf den Anzeigedienst gilt nur fuer belegte Leitungen - bei einem + anderen Fehler waere er eine Falschaussage, die den Bediener in die falsche Richtung + schickt.""" + _stub_hardware(monkeypatch) + monkeypatch.setattr(epdconfig, "lgpio", _FakeLgpioModule) + monkeypatch.setattr(epdconfig, "gpiozero", + _gpiozero_raising(OSError("kein solches Geraet"))) + + with pytest.raises(epdconfig.DisplayUnavailable) as excinfo: + epdconfig.module_init() + + assert "ninlight-display" not in str(excinfo.value) + + +def test_programming_errors_are_still_not_disguised_as_missing_hardware(monkeypatch): + """Die Ausnahmeliste bleibt gezielt (Fixrunde 1, C2): ein TypeError ist ein + Programmierfehler und darf nicht als "Panel fehlt" durchgehen - sonst verschwindet er + hinter einer INFO-Zeile und sys.exit(0).""" + _stub_hardware(monkeypatch) + monkeypatch.setattr(epdconfig, "lgpio", _FakeLgpioModule) + monkeypatch.setattr(epdconfig, "gpiozero", + _gpiozero_raising(TypeError("falsches Argument"))) + + with pytest.raises(TypeError): + epdconfig.module_init() + + def test_v3_partial_draw_after_sleep_does_not_crash(monkeypatch): """Regressionstest zu Fixrunde 2, N1 - genau der vom Reviewer nachgestellte Ablauf: Panel("V3") -> clear -> draw_full -> sleep -> draw_partial. Vorher stuerzte das mit From 03d6328dd53b0bb85c812f54826b409225d21af3 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 03:11:32 +0200 Subject: [PATCH 129/150] fix(display): Phase im Kopfbalken, Rand unten, Wants-Fehler behoben Drei Befunde der Hardware-Abnahme: die Migrationsphase stand als eigene kleine Zeile unter dem Lampennamen und ging dort unter - sie steht jetzt rechtsbuendig neben dem Modus in der invertierten Kopfzeile (mit Kuerzung/kleinerer Schrift/Weglassen als gestaffelte Antwort auf eine zu lange Phase, nie mit Ueberlappung). Die letzte Beiwerkzeile klebte am unteren Panelrand - durch das Verschieben der Phase in den Kopf bleiben nur noch fuenf statt sechs Beiwerkzeilen, zusaetzlich durchgesetzt per BOTTOM_MARGIN_PX in der Abbruchbedingung von compose(). Ausserdem ein am Geraet nachgewiesener Fund: ninlight-display.service trug Wants=ninlight-station.service NEBEN dem gewollten After= - Wants= aktiviert die genannte Unit aber, statt nur zu ordnen, und zog den absichtlich deaktivierten Stationsdienst bei jedem Boot ohne Zugangspunkt in einen "failed"-Zustand. Wants= entfernt, After= bleibt. --- tools/station/display/render.py | 164 +++++++++++++++++++------ tools/station/ninlight-display.service | 17 ++- tools/station/tests/test_render.py | 162 +++++++++++++++++++++++- tools/station/tests/test_setup_pi.sh | 20 +++ 4 files changed, 319 insertions(+), 44 deletions(-) diff --git a/tools/station/display/render.py b/tools/station/display/render.py index c45177c..59aa7b7 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -46,32 +46,64 @@ TEXT_MARGIN_PX = 2 # aus zwei Metern nicht mehr lesbar (Befund Michaels am Geraet). blocks() vergibt # deshalb jeder Zeile eine ROLLE, und compose() setzt jede Rolle anders: # -# header - Modus, fett, WEISS AUF SCHWARZEM BALKEN ueber die volle Panelbreite. -# Invertiert kostet auf e-Paper nichts (bistabil, kein Stromverbrauch pro -# Pixel) und trennt die Kopfzeile auf einen Blick vom Rest. -# hero - die eine wichtigste Information des Zustands (Migration: der erkannte -# Produktname; Rettung: die SSID), fett. -# detail - Beiwerk (Ziel/Variante, Lampenversion, Zaehler, letzte Auslieferung). -# big - Zustaende fast ohne Inhalt ("Station aus", "Status unbekannt"): gross und -# mittig, damit man quer durch den Raum sieht, dass nichts laeuft. +# header - Modus, fett, WEISS AUF SCHWARZEM BALKEN ueber die volle +# Panelbreite. Invertiert kostet auf e-Paper nichts (bistabil, kein +# Stromverbrauch pro Pixel) und trennt die Kopfzeile auf einen Blick +# vom Rest. +# header_phase - die Phase (AUTO/HORCHT/FREIGEGEBEN: ...), NUR im Migrationsmodus und +# nur, wenn eine Phase bekannt ist. Steht in derselben Kopfzeile wie +# header, rechtsbuendig, in kleinerer Schrift (zweite Hardware-Abnahme: +# die Phase ging als eigene kleine Zeile unter dem Lampennamen unter). +# Rettung/aus kennen keine Phase - dort bleibt die Kopfzeile +# unveraendert (nur header, mittig, wie zuvor). +# hero - die eine wichtigste Information des Zustands (Migration: der +# erkannte Produktname; Rettung: die SSID), fett. +# detail - Beiwerk (Ziel/Variante, Lampenversion, Zaehler, letzte Auslieferung). +# big - Zustaende fast ohne Inhalt ("Station aus", "Status unbekannt"): +# gross und mittig, damit man quer durch den Raum sieht, dass nichts +# laeuft. ROLE_HEADER = "header" +ROLE_HEADER_PHASE = "header_phase" ROLE_HERO = "hero" ROLE_DETAIL = "detail" ROLE_BIG = "big" HEADER_FONT_PX = 20 +HEADER_PHASE_FONT_PX = 13 HERO_FONT_PX = 16 DETAIL_FONT_PX = 11 BIG_FONT_PX = 26 # Zeilenhoehen so gewaehlt, dass der vollste Zustand (Migration mit erkannter Lampe: -# Kopfbalken + grosse Zeile + sechs Beiwerkzeilen) exakt auf die 122 px passt und die -# letzte Zeile - "zuletzt ausgeliefert" - nicht mehr hinten abfaellt: -# 22 + 2 + 20 + 6*13 = 122. +# Kopfbalken + grosse Zeile + fuenf Beiwerkzeilen) auf die 122 px passt, mit sichtbarem +# Abstand nach unten uebrig (naechster Kommentar, BOTTOM_MARGIN_PX): +# 22 + 2 + 20 + 5*13 = 109. Die Phase (frueher eine sechste Beiwerkzeile: "AUTO"/ +# "FREIGEGEBEN: ...") steht seit der zweiten Hardware-Abnahme in der Kopfzeile +# (ROLE_HEADER_PHASE) statt als eigene Zeile - das ist der Grund, warum nur noch fuenf +# statt sechs Beiwerkzeilen uebrig bleiben, nicht eine nachtraeglich erfundene Kuerzung. HEADER_BAR_HEIGHT_PX = 22 HERO_LINE_HEIGHT_PX = 20 DETAIL_LINE_HEIGHT_PX = 13 +# Hardware-Abnahme, zweiter Befund: auf dem echten Panel frisst ein Rahmen den unteren +# Rand, der volleste Zustand (s.o., endet bei 109 px) wirkte dort gedraengt. compose() +# bricht Beiwerkzeilen deshalb schon ab, bevor sie in die letzten BOTTOM_MARGIN_PX +# Zeilen hineinragen wuerden (siehe die Abbruchbedingung dort) - mit den obigen 109 px +# bleibt dabei auch im vollsten Zustand jede Zeile sichtbar, es wird nichts abgeschnitten. +BOTTOM_MARGIN_PX = 10 + +# Mindestabstand zwischen dem Ende des Modus-Texts und dem Anfang der Phase in der +# Kopfzeile (beide weiss auf demselben schwarzen Balken) - ohne diesen Puffer koennten +# beide Texte bei einer langen Phase (z.B. "FREIGEGEBEN: ") +# ineinanderlaufen. +HEADER_PHASE_GAP_PX = 6 +# Bleibt nach Modus-Text und Puffer weniger als das fuer die Phase uebrig, wird sie ganz +# weggelassen statt ein einzelnes "…" hinzuzeichnen - das waere ohnehin keine Auskunft +# mehr wert (dritte Option aus der Abnahme: kuerzen, verkleinern oder weglassen; die +# ersten beiden greifen fuer die im Betrieb vorkommenden Werte immer, das hier ist nur +# das Sicherheitsnetz fuer einen theoretisch sehr langen Zielnamen). +HEADER_PHASE_MIN_WIDTH_PX = 24 + # Suchkette fuer eine echte, skalierbare Schrift, in dieser Reihenfolge probiert # (Regular, Bold). ImageFont.load_default(size=...) waere der naheliegende Weg, ist aber # erst ab Pillow 10.1 vorhanden - Pi 2 (Raspberry Pi OS Bookworm) hat Pillow 9.4.0, und @@ -152,9 +184,13 @@ def blocks(state: dict) -> list[tuple[str, str]]: ssid = state.get("ssid") if mode == "recovery": - # Im Rettungsmodus ist die SSID das, was der Bediener am Geraet braucht (er muss - # sich mit genau diesem Netz verbinden) - deshalb gross und ohne "SSID: "-Praefix, - # das den Platz nur wegnaehme. + # Im Rettungsmodus gibt es keine Phase - kein ROLE_HEADER_PHASE-Block, die + # Kopfzeile bleibt also nur der Modus (compose() zentriert sie dann weiterhin + # wie vor der zweiten Hardware-Abnahme, siehe dort). + # + # Die SSID ist das, was der Bediener am Geraet braucht (er muss sich mit genau + # diesem Netz verbinden) - deshalb gross und ohne "SSID: "-Praefix, das den + # Platz nur wegnaehme. if ssid: out.append((str(ssid), ROLE_HERO)) else: @@ -162,6 +198,19 @@ def blocks(state: dict) -> list[tuple[str, str]]: # nur beim Start und nach einer Auslieferung) - display.py reicht sie hier als # Teil des zusammengefuehrten Zustands herein. Im Rettungsmodus gibt es keine # Phase, deshalb dieser Block nur hier drin. + # + # Seit der zweiten Hardware-Abnahme steht die Phase NICHT mehr als eigene + # Beiwerkzeile unter dem Lampennamen (dort ging sie unter), sondern direkt + # NEBEN dem Modus in der Kopfzeile (ROLE_HEADER_PHASE, rechtsbuendig) - deshalb + # wird sie hier direkt nach dem ROLE_HEADER-Block angehaengt, nicht erst nach + # der grossen Zeile. + phase = state.get("phase") + if phase == "armed": + target = state.get("armed_target") or "?" + out.append((f"FREIGEGEBEN: {target}", ROLE_HEADER_PHASE)) + elif phase in PHASE_LABELS: + out.append((PHASE_LABELS[phase], ROLE_HEADER_PHASE)) + last_seen = state.get("last_seen") if not isinstance(last_seen, dict): last_seen = {} @@ -176,13 +225,6 @@ def blocks(state: dict) -> list[tuple[str, str]]: elif ssid: out.append((str(ssid), ROLE_HERO)) - phase = state.get("phase") - if phase == "armed": - target = state.get("armed_target") or "?" - out.append((f"FREIGEGEBEN: {target}", ROLE_DETAIL)) - elif phase in PHASE_LABELS: - out.append((PHASE_LABELS[phase], ROLE_DETAIL)) - if product_name: target = last_seen.get("target") variant = last_seen.get("variant") @@ -264,16 +306,32 @@ def _load_font(image_font, size_px: int, bold: bool = False): return image_font.load_default() -def _draw_centered(draw, text: str, font, top: int, height: int, fill: int) -> None: - """Zeichnet `text` waagerecht mittig aufs Panel und senkrecht mittig in das Band - [top, top+height). Misst mit textbbox() statt mit der nominellen Schriftgroesse: die - tatsaechliche Tinte einer Zeile ist je nach Zeichen (Ober-/Unterlaengen) deutlich - kleiner als die Zeilenhoehe, und ein nach Zeilenhoehe zentrierter Text sieht auf - einem 122 px hohen Panel sichtbar zu tief aus.""" +def _draw_text(draw, text: str, font, top: int, height: int, fill: int, + align: str = "center") -> int: + """Zeichnet `text` senkrecht mittig in das Band [top, top+height) und waagerecht + nach `align`: "center" (Panelmitte - bisheriges Verhalten, fuer ROLE_BIG und die + Kopfzeile ohne Phase), "left" (linker Rand + TEXT_MARGIN_PX - der Modus, sobald eine + Phase daneben steht) oder "right" (rechter Rand - TEXT_MARGIN_PX - die Phase). + + Misst mit textbbox() statt mit der nominellen Schriftgroesse: die tatsaechliche + Tinte einer Zeile ist je nach Zeichen (Ober-/Unterlaengen) deutlich kleiner als die + Zeilenhoehe, und ein nach Zeilenhoehe zentrierter Text sieht auf einem 122 px hohen + Panel sichtbar zu tief aus. + + Gibt die gezeichnete Tintenbreite zurueck - compose() braucht sie fuer den Modus in + der Kopfzeile, um daraus auszurechnen, wie viel Platz rechts noch fuer die Phase + uebrig ist.""" bbox = draw.textbbox((0, 0), text, font=font) - x = (PANEL_WIDTH_PX - (bbox[2] - bbox[0])) // 2 - bbox[0] + ink_width = bbox[2] - bbox[0] + if align == "left": + visual_x = TEXT_MARGIN_PX + elif align == "right": + visual_x = PANEL_WIDTH_PX - TEXT_MARGIN_PX - ink_width + else: + visual_x = (PANEL_WIDTH_PX - ink_width) // 2 y = top + (height - (bbox[3] - bbox[1])) // 2 - bbox[1] - draw.text((x, y), text, font=font, fill=fill) + draw.text((visual_x - bbox[0], y), text, font=font, fill=fill) + return ink_width def compose(state: dict): @@ -285,13 +343,14 @@ def compose(state: dict): draw = ImageDraw.Draw(image) fonts = { ROLE_HEADER: _load_font(ImageFont, HEADER_FONT_PX, bold=True), + ROLE_HEADER_PHASE: _load_font(ImageFont, HEADER_PHASE_FONT_PX, bold=True), ROLE_HERO: _load_font(ImageFont, HERO_FONT_PX, bold=True), ROLE_DETAIL: _load_font(ImageFont, DETAIL_FONT_PX), ROLE_BIG: _load_font(ImageFont, BIG_FONT_PX, bold=True), } max_width_px = PANEL_WIDTH_PX - 2 * TEXT_MARGIN_PX - def fit(raw_text: str, font) -> str: + def fit(raw_text: str, font, width_px: float = max_width_px) -> str: # draw.textlength() wirft ValueError("can't measure length of multiline text"), # sobald eine Zeile ein "\n" enthaelt (mit Pillow 12 nachgeprueft) - waere von # main()s except (DisplayUnavailable, ImportError) nicht gefangen (Fixrunde 3, @@ -302,28 +361,59 @@ def compose(state: dict): text = raw_text.replace("\n", " ") return _truncate_to_width( text, measure=lambda t: draw.textlength(t, font=font), - max_width_px=max_width_px) + max_width_px=width_px) + + block_list = blocks(state) + # Nur wenn eine Phase folgt, ruckt der Modus in der Kopfzeile nach links (siehe + # Rollen-Kommentar oben) - Rettung und "aus" kennen keinen ROLE_HEADER_PHASE-Block + # und behalten die zentrierte Kopfzeile von vor der zweiten Hardware-Abnahme. + has_header_phase = any(role == ROLE_HEADER_PHASE for _, role in block_list) + header_mode_right_edge_px = 0.0 y = TEXT_MARGIN_PX - for raw_text, role in blocks(state): + for raw_text, role in block_list: font = fonts[role] - text = fit(raw_text, font) if role == ROLE_BIG: # Einziger Block dieses Zustands (siehe blocks()) - er bekommt das ganze # Panel. - _draw_centered(draw, text, font, top=0, height=PANEL_HEIGHT_PX, fill=0) + text = fit(raw_text, font) + _draw_text(draw, text, font, top=0, height=PANEL_HEIGHT_PX, fill=0, + align="center") continue if role == ROLE_HEADER: + text = fit(raw_text, font) draw.rectangle((0, 0, PANEL_WIDTH_PX - 1, HEADER_BAR_HEIGHT_PX - 1), fill=0) - _draw_centered(draw, text, font, top=0, height=HEADER_BAR_HEIGHT_PX, fill=255) + align = "left" if has_header_phase else "center" + ink_width = _draw_text(draw, text, font, top=0, height=HEADER_BAR_HEIGHT_PX, + fill=255, align=align) + if align == "left": + header_mode_right_edge_px = TEXT_MARGIN_PX + ink_width y = HEADER_BAR_HEIGHT_PX + TEXT_MARGIN_PX continue + if role == ROLE_HEADER_PHASE: + # Rechtsbuendig NEBEN dem Modus (der links steht, s.o.) im selben Balken - + # gemessen ab dem tatsaechlichen rechten Rand des Modus-Texts, nicht ab + # einer angenommenen festen Breite: "MIGRATION" und "RETTUNG" sind + # unterschiedlich breit, und nur "MIGRATION" hat je eine Phase daneben. + available_px = (PANEL_WIDTH_PX - TEXT_MARGIN_PX - header_mode_right_edge_px + - HEADER_PHASE_GAP_PX) + if available_px >= HEADER_PHASE_MIN_WIDTH_PX: + text = fit(raw_text, font, width_px=available_px) + _draw_text(draw, text, font, top=0, height=HEADER_BAR_HEIGHT_PX, + fill=255, align="right") + # sonst: Phase weglassen (dritte Option aus der Abnahme) - bei so wenig + # Platz waere selbst ein gekuerztes "…" keine Auskunft mehr wert, und ein + # Ueberlappen mit dem Modus ist nicht erlaubt. + continue + + text = fit(raw_text, font) line_height = HERO_LINE_HEIGHT_PX if role == ROLE_HERO else DETAIL_LINE_HEIGHT_PX - if y + line_height > PANEL_HEIGHT_PX: - # Passt nicht mehr aufs Panel - abschneiden statt ueberzeichnen. Die + if y + line_height > PANEL_HEIGHT_PX - BOTTOM_MARGIN_PX: + # Passt nicht mehr aufs Panel, ohne in den fuer BOTTOM_MARGIN_PX + # reservierten unteren Rand zu ragen - abschneiden statt ueberzeichnen. Die # Reihenfolge in blocks() ist deshalb nach Wichtigkeit sortiert. break draw.text((TEXT_MARGIN_PX, y), text, font=font, fill=0) diff --git a/tools/station/ninlight-display.service b/tools/station/ninlight-display.service index 7415261..d42a1ad 100644 --- a/tools/station/ninlight-display.service +++ b/tools/station/ninlight-display.service @@ -1,12 +1,25 @@ [Unit] Description=ninlight E-Paper-Anzeige (Status der Migrations-/Rettungsstation) -# Nur eine Anzeige fuer den Hauptdienst - Wants=/After=, NICHT Requires=/BindsTo=: ein +# Nur eine Anzeige fuer den Hauptdienst - After=, NICHT Requires=/BindsTo=/Wants=: ein # fehlendes oder abgestuerztes Panel darf server.py niemals mitreissen, und server.py # darf umgekehrt starten, auch wenn dieser Dienst fehlt oder deaktiviert ist. Siehe # display.py's Modul-Docstring: die Anzeige ist nirgends Voraussetzung fuer die # Migration. +# +# KEIN Wants=ninlight-station.service (stand hier zuerst, Fund der Hardware-Abnahme auf +# Pi 2): Wants= ist keine reine Reihenfolgeangabe, sondern AKTIVIERT die genannte Unit +# beim Start dieser Unit, unabhaengig davon, ob sie enabled ist - "ninlight-station" ist +# absichtlich disabled (siehe deren Kommentar: die Station ist per Vorgabe aus), wurde +# aber trotzdem bei jedem Boot mitgestartet, weil dieser (enabled) Anzeigedienst sie ueber +# Wants= mitzog. Ohne Zugangspunkt kann server.py 192.168.8.1 nicht binden, stirbt, wird +# von Restart=always dort mehrfach neu versucht und bleibt auf "failed" haengen - das +# flutet das Journal bei jedem Boot und laesst "ninlight-station status" faelschlich +# einen Fehler ("failed" statt "inactive") vermuten, wo absichtlich nichts laeuft. +# After= allein ordnet nur die Startreihenfolge, FALLS beide Units ohnehin gestartet +# werden (z.B. wenn jemand die Station von Hand einschaltet) - und genau das war hier +# gemeint, nicht "zieh die Station mit hoch". Nicht aus falscher Vorsicht wieder +# einbauen: der Nebeneffekt ist genau der oben beschriebene, kein Sicherheitsnetz. After=ninlight-station.service -Wants=ninlight-station.service # Gehoeren seit systemd 229 ins [Unit], nicht ins [Service] (dort standen sie zuerst - # Fixrunde 1, I1: systemd ignoriert sie dort mit "Unknown key name", die Vorgabe (5 # Starts/10s) greift dann bei RestartSec=5 nie, und genau das sollte die Deckelung unten diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 59c1f4a..20b5f91 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -183,21 +183,173 @@ def test_hero_line_is_bigger_than_the_details(): def test_the_last_delivery_line_still_fits_on_the_panel(): - """Der vollste Zustand (Kopfbalken, grosse Zeile, sechs Beiwerkzeilen) muss - vollstaendig aufs Panel passen - mit groesserer Schrift ist das keine - Selbstverstaendlichkeit mehr, und ausgerechnet die letzte Zeile ("zuletzt - ausgeliefert") faellt sonst stumm hinten herunter.""" + """Der vollste Zustand (Kopfbalken mit Modus+Phase, grosse Zeile, fuenf + Beiwerkzeilen) muss vollstaendig aufs Panel passen - mit groesserer Schrift ist das + keine Selbstverstaendlichkeit mehr, und ausgerechnet die letzte Zeile ("zuletzt + ausgeliefert") faellt sonst stumm hinten herunter. Die Phase steckt seit der + zweiten Hardware-Abnahme im Kopfbalken (ROLE_HEADER_PHASE) statt als eigene + Beiwerkzeile - deshalb acht Bloecke insgesamt, aber nur noch fuenf Beiwerkzeilen + vor der letzten.""" state = _migration_state() expected = render.lines(state) assert len(expected) == 8, expected # sonst misst dieser Test etwas anderes image = render.compose(state) last_line_top = (render.HEADER_BAR_HEIGHT_PX + render.TEXT_MARGIN_PX - + render.HERO_LINE_HEIGHT_PX + 5 * render.DETAIL_LINE_HEIGHT_PX) + + render.HERO_LINE_HEIGHT_PX + 4 * render.DETAIL_LINE_HEIGHT_PX) assert _ink_box(image, last_line_top, render.PANEL_HEIGHT_PX) is not None, ( "letzte Zeile fehlt - sie passt nicht mehr aufs Panel") +def test_bottom_margin_stays_free_of_ink_even_in_the_fullest_state(): + """Zweite Hardware-Abnahme, Befund 2: der Rahmen des echten Panels frisst den + unteren Rand, der volleste Zustand wirkte dort gedraengt. Geprueft an echten + gezeichneten Pixeln (nicht an nachgerechneten Koordinaten): die letzten + BOTTOM_MARGIN_PX Zeilen duerfen im volleren Zustand keine einzige schwarze Tinte + enthalten, sonst waere der Abstand nur auf dem Papier vorhanden.""" + image = render.compose(_migration_state()) + pixels = image.load() + margin_top = render.PANEL_HEIGHT_PX - render.BOTTOM_MARGIN_PX + + ink_in_margin = any( + pixels[x, y] == 0 + for y in range(margin_top, render.PANEL_HEIGHT_PX) + for x in range(render.PANEL_WIDTH_PX)) + assert not ink_in_margin, ( + f"Text reicht bis in die letzten {render.BOTTOM_MARGIN_PX}px - kein sichtbarer " + "Rand mehr") + + +def test_compose_stops_drawing_details_before_the_bottom_margin(monkeypatch): + """Direkter Test der Abbruchbedingung in compose(): der heutige volleste Zustand + (fuenf Beiwerkzeilen) erreicht die 122 px ohnehin nicht mehr (s.o.) - eine einzelne + Zeile weniger oder mehr an der Faltkante der Abbruchbedingung ist dort nur 1-2px + Tinte, kein verlässlicher Ausschlag. Dieser Test ersetzt blocks() deshalb durch eine + synthetische Liste mit zehn Beiwerkzeilen - deutlich mehr, als aufs Panel passen - + und ZAEHLT die tatsaechlich gezeichneten Zeilen (als getrennte Tinten-Cluster + unterhalb des Kopfbalkens), statt nur zu prüfen, ob irgendein Pixel in den letzten + Zeilen liegt: mit der Abbruchbedingung "> PANEL_HEIGHT_PX - BOTTOM_MARGIN_PX" werden + hier sechs Zeilen gezeichnet, ohne den reservierten Rand ("> PANEL_HEIGHT_PX" allein, + die Mutation) waeren es sieben - am Geraet nachgemessen (siehe Mutationsprobe im + Abnahmebericht).""" + synthetic = [("MIGRATION", render.ROLE_HEADER)] + [ + (f"Zeile {i}", render.ROLE_DETAIL) for i in range(10)] + monkeypatch.setattr(render, "blocks", lambda state: synthetic) + + image = render.compose({"mode": "migration"}) + pixels = image.load() + + rows_with_ink = [ + any(pixels[x, y] == 0 for x in range(render.PANEL_WIDTH_PX)) + for y in range(render.HEADER_BAR_HEIGHT_PX, render.PANEL_HEIGHT_PX)] + drawn_lines = sum( + 1 for i, has_ink in enumerate(rows_with_ink) + if has_ink and not (i > 0 and rows_with_ink[i - 1])) + + assert drawn_lines == 6, ( + f"{drawn_lines} Beiwerkzeilen gezeichnet, sechs erwartet - " + "BOTTOM_MARGIN_PX wird in der Abbruchbedingung nicht mehr beruecksichtigt") + + margin_top = render.PANEL_HEIGHT_PX - render.BOTTOM_MARGIN_PX + ink_in_margin = any( + pixels[x, y] == 0 + for y in range(margin_top, render.PANEL_HEIGHT_PX) + for x in range(render.PANEL_WIDTH_PX)) + assert not ink_in_margin, ( + "eine ueberlaufende Beiwerkzeile wurde trotzdem in den reservierten unteren " + "Rand gezeichnet") + + +# --- Kopfzeile: Phase neben dem Modus (zweite Hardware-Abnahme, Befund 1) ----------- +# +# Frueher stand die Phase als eigene kleine Beiwerkzeile unter dem Lampennamen und ging +# dort unter. Jetzt steht sie rechtsbuendig im selben schwarzen Balken wie der Modus, +# der dafuer nach links ruecken darf. Rettung/aus kennen keine Phase und behalten die +# alte, zentrierte Kopfzeile. + +def _header_white_columns(image): + """Spalten (x) im Kopfbalken, die irgendwo eine weisse (Text-)Zelle enthalten.""" + pixels = image.load() + return [x for x in range(render.PANEL_WIDTH_PX) + if any(pixels[x, y] == 255 for y in range(render.HEADER_BAR_HEIGHT_PX))] + + +def test_phase_appears_right_aligned_next_to_the_mode_in_the_header(): + """Grundfall: Modus links, Phase rechts, beide im selben Balken sichtbar - nicht mehr + als eigene Zeile darunter.""" + state = _migration_state() + state["phase"] = "auto" + del state["armed_target"] + lines_out = render.lines(state) + assert lines_out[0] == "MIGRATION" + assert lines_out[1] == "AUTO" + + image = render.compose(state) + white_cols = _header_white_columns(image) + assert white_cols, "Kopfzeile zeigt keinen Text" + # Modus (links) und Phase (rechts): die Schrift muss deutlich in der linken UND in + # der rechten Panelhaelfte stehen - stuende alles zentriert (die alte Darstellung), + # waere die rechte Haelfte praktisch leer. + left_half = [x for x in white_cols if x < render.PANEL_WIDTH_PX // 2] + right_half = [x for x in white_cols if x >= render.PANEL_WIDTH_PX // 2] + assert left_half, "kein Modus-Text in der linken Kopfzeilenhaelfte" + assert right_half, "keine Phase in der rechten Kopfzeilenhaelfte" + # Und die Phase muss tatsaechlich an den rechten Rand reichen (rechtsbuendig). + assert max(white_cols) >= render.PANEL_WIDTH_PX - render.TEXT_MARGIN_PX - 10 + + +def test_recovery_header_stays_centered_without_a_phase(): + """Rettung kennt keine Phase - die Kopfzeile bleibt wie vor der zweiten + Hardware-Abnahme: der Modus steht waagerecht mittig, nicht links.""" + image = render.compose({"mode": "recovery", "ssid": "ninlight-recovery", + "served": 0, "recent": []}) + white_cols = _header_white_columns(image) + assert white_cols + left_gap = white_cols[0] + right_gap = render.PANEL_WIDTH_PX - 1 - white_cols[-1] + assert abs(left_gap - right_gap) <= 3, ( + f"Modus nicht mehr mittig (links {left_gap}, rechts {right_gap}) - " + "Rettung darf die Kopfzeile nicht umbauen") + + +def test_long_armed_target_does_not_overlap_the_mode_in_the_header(): + """Mutationswache fuer Befund 1: eine sehr lange Phasenbezeichnung ("FREIGEGEBEN: + ") darf den Modus links nicht ueberdecken. Geprueft an echten + Pixeln, nicht an Koordinaten: zwischen dem Ende des Modus-Texts und dem Anfang der + Phase muss eine durchgehend schwarze Luecke bleiben, die deutlich breiter ist als + eine normale Buchstabenluecke innerhalb eines Worts (die bei einer echten + Ueberlappung/einem fehlenden Kuerzungs-Clamp verschwinden wuerde, weil die Phase + dann weit nach links unter/ueber den Modus gezeichnet wird).""" + state = _migration_state() + state["phase"] = "armed" + state["armed_target"] = "x" * 80 + + image = render.compose(state) + pixels = image.load() + header_rows = range(render.HEADER_BAR_HEIGHT_PX) + white_cols = [x for x in range(render.PANEL_WIDTH_PX) + if any(pixels[x, y] == 255 for y in header_rows)] + assert white_cols + + def is_black_column(x): + return all(pixels[x, y] != 255 for y in header_rows) + + first, last = white_cols[0], white_cols[-1] + best_gap = 0 + run = 0 + for x in range(first, last + 1): + if is_black_column(x): + run += 1 + best_gap = max(best_gap, run) + else: + run = 0 + assert best_gap >= render.HEADER_PHASE_GAP_PX - 1, ( + f"kein durchgehender Zwischenraum von mindestens " + f"{render.HEADER_PHASE_GAP_PX - 1}px zwischen Modus und Phase gefunden " + f"(groesste Luecke: {best_gap}px) - Modus und Phase ueberlappen sich" + ) + + def test_compose_still_works_without_any_font_file(monkeypatch): """Die Zusage "eine fehlende Schrift haelt die Anzeige nicht auf" gilt unveraendert: ohne einen einzigen Treffer in der Suchkette faellt compose() auf load_default() diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index 2f3b398..e921dc3 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -719,6 +719,26 @@ else fail "ninlight-display.service wurde nicht installiert - Szenario 19 kann nichts pruefen" fi +echo "=== Szenario 20 (Hardware-Abnahme, Pi 2): Wants= zieht den Stationsdienst nicht mehr mit hoch ===" +# Befund am Geraet: ninlight-station ist absichtlich disabled (die Station ist per +# Vorgabe aus), wurde aber bei jedem Boot trotzdem gestartet, weil dieser (enabled) +# Anzeigedienst per Wants=ninlight-station.service die andere Unit AKTIVIERT hat - Wants= +# ordnet nicht nur, es startet die genannte Unit mit. Ohne Zugangspunkt kann server.py +# 192.168.8.1 nicht binden, stirbt, wird von Restart=always mehrfach neu versucht und +# bleibt auf "failed" haengen (flutet das Journal bei jedem Boot). Gemeint war reine +# Reihenfolge - dafuer reicht After= allein, das nur ordnet, FALLS beide Units ohnehin +# starten, und aktiviert nichts von sich aus. +if [ -f "$INSTALLED_DISPLAY_UNIT" ]; then + grep -Eq '^Wants=ninlight-station\.service$' "$INSTALLED_DISPLAY_UNIT" \ + && fail "Wants=ninlight-station.service steht noch in der ausgelieferten Unit - das zieht die (absichtlich disabled) Station bei jedem Boot mit hoch" \ + || pass "kein Wants=ninlight-station.service mehr in der ausgelieferten Unit" + grep -Eq '^After=ninlight-station\.service$' "$INSTALLED_DISPLAY_UNIT" \ + && pass "After=ninlight-station.service bleibt (reine Reihenfolge, falls beide ohnehin starten)" \ + || fail "After=ninlight-station.service fehlt - die Startreihenfolge waere nicht mehr garantiert" +else + fail "ninlight-display.service wurde nicht installiert - Szenario 20 kann nichts pruefen" +fi + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From e77d3da292cf69527b537446db5af6d38303dec4 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 14:13:56 +0200 Subject: [PATCH 130/150] fix(station): SPI in setup_pi.sh automatisch einschalten Auf frischem Raspberry Pi OS ist SPI aus, das E-Paper bleibt schwarz mit irrefuehrender "nicht verfuegbar"-Meldung. setup_pi.sh erkennt jetzt beide config.txt-Pfade, normalisiert dtparam=spi=on (fehlend/auskommentiert/off), sichert vorher nach config.txt.bak-ninlight und weist nur bei echter Aenderung auf den noetigen Neustart hin. --- tools/station/setup_pi.sh | 124 +++++++++++++++++++- tools/station/tests/test_setup_pi.sh | 169 ++++++++++++++++++++++++++- 2 files changed, 290 insertions(+), 3 deletions(-) diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index a71a600..a504572 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -210,6 +210,112 @@ else fi REMOTE +echo "== SPI aktivieren ==" +# Auf einem frischen Raspberry Pi OS ist SPI ausgeschaltet: dtparam=spi=on steht +# auskommentiert in der Boot-Konfiguration, /dev/spidev0.0 fehlt, und der Anzeigedienst +# meldet "E-Paper-Anzeige nicht verfuegbar" - das sieht nach fehlender Hardware aus, +# ist aber nur ein abgeschalteter Bus. Ohne diesen Schritt haette jede frische Station +# ein schwarzes Panel, bis jemand von Hand nachhilft (wie hier gestern geschehen). +# +# Die Aenderung wirkt erst nach einem Neustart der Boot-Konfiguration - das Skript +# startet den Pi NICHT selbst neu (ein unerwarteter Reboot mitten in einer Fernsitzung +# waere ueberraschender als ein Hinweis am Ende), meldet ueber den Exit-Code des +# Remote-Blocks aber, ob einer noetig war (10 = geaendert, 0 = schon an, 20 = keine +# Boot-Konfiguration gefunden). $SPI_REBOOT_NEEDED steuert unten, ob der Hinweis in den +# naechsten Schritten erscheint - nur dann, wenn wirklich etwas geaendert wurde. +SPI_REBOOT_NEEDED=0 +set +e +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e +# Ueberschreibbar wie die anderen NINLIGHT_*-Pfade oben: auf dem Pi bleibt es bei den +# echten Pfaden, ein Test kann hier gegen Sandbox-Dateien statt gegen echte +# Systempfade laufen. +SPIDEV=${NINLIGHT_SPIDEV_PATH:-/dev/spidev0.0} +CONFIG_NEW=${NINLIGHT_BOOT_CONFIG_NEW:-/boot/firmware/config.txt} +CONFIG_OLD=${NINLIGHT_BOOT_CONFIG_OLD:-/boot/config.txt} + +# Existiert das Geraeteknoten schon, ist SPI aktiv - unabhaengig davon, was in der +# Boot-Konfiguration steht (es kann auch per Overlay oder einem anderen Mechanismus +# kommen). Dann kein Eingriff und kein Neustart-Hinweis. +if [ -e "$SPIDEV" ]; then + echo " SPI ist bereits aktiv ($SPIDEV vorhanden)" + exit 0 +fi + +# Aktuelle Raspberry-Pi-OS-Images nutzen /boot/firmware/config.txt, aeltere +# /boot/config.txt - beide werden geprueft, die neuere zuerst. Gibt es keinen von +# beiden, ist das kein Grund abzubrechen: ein anderes Boot-Verfahren (oder gar kein +# Raspberry Pi OS) ist moeglich, und die Anzeige ist nirgends Voraussetzung fuer eine +# funktionierende Station (dasselbe Prinzip wie beim apt-Schritt und beim +# Anzeigedienst oben). +CONFIG="" +if sudo test -f "$CONFIG_NEW"; then + CONFIG="$CONFIG_NEW" +elif sudo test -f "$CONFIG_OLD"; then + CONFIG="$CONFIG_OLD" +fi + +if [ -z "$CONFIG" ]; then + echo "Warnung: weder $CONFIG_NEW noch $CONFIG_OLD gefunden - vermutlich kein" >&2 + echo " Raspberry Pi OS oder ein anderes Boot-Verfahren. SPI konnte nicht" >&2 + echo " geprueft werden - bleibt das E-Paper schwarz, ist das der" >&2 + echo " wahrscheinlichste Grund." >&2 + exit 20 +fi + +# Eine aktive (nicht auskommentierte) "dtparam=spi=on"-Zeile heisst: schon an - dann +# nichts tun und keinen Neustart verlangen, den es nicht braucht. +if sudo grep -Eq '^[[:space:]]*dtparam=spi=on[[:space:]]*$' "$CONFIG"; then + echo " SPI ist in $CONFIG bereits eingeschaltet" + exit 0 +fi + +BACKUP="${CONFIG}.bak-ninlight" +if sudo test -f "$BACKUP"; then + # Nicht ueberschreiben: sonst waere beim zweiten Lauf (z.B. weil zwischendurch von + # Hand wieder auf "off" gestellt wurde) der urspruengliche Originalzustand weg. + echo " Sicherung $BACKUP existiert bereits - wird nicht ueberschrieben" +else + sudo cp -p "$CONFIG" "$BACKUP" + echo " Sicherung angelegt: $BACKUP" +fi + +# Drei Faelle bleiben: die Zeile fehlt ganz, sie steht auskommentiert da, oder sie +# steht aktiv auf "off" (z.B. von Hand deaktiviert). In allen dreien wird auf +# "dtparam=spi=on" normalisiert - eine vorhandene Zeile wird ERSETZT statt eine +# zweite anzuhaengen, sonst stuenden am Ende zwei widerspruechliche +# dtparam=spi-Zeilen in der Datei und es waere Zufall, welche NetworkManager beim +# naechsten Boot zuletzt liest. +# +# Kein "sed -i": dieser Block laeuft zwar auf dem Pi (GNU) unter "REMOTE", aber der +# Stub-Test fuehrt ihn woertlich auf dem Entwicklungsrechner (macOS/BSD) aus - "sed +# -i" braucht dort ein Suffix-Argument, GNU-sed keins, und die falsche Reihenfolge +# war in diesem Projekt schon zweimal ein Fehler. Deshalb sed nur als Filter +# (Eingabe->Ausgabe), das Ergebnis geht ueber eine Temp-Datei und "sudo install" an +# den Zielpfad - das funktioniert auf beiden Plattformen identisch. +if sudo grep -Eq '^[[:space:]]*#?[[:space:]]*dtparam=spi=(on|off)[[:space:]]*$' "$CONFIG"; then + TMP_CONFIG=$(mktemp "${TMPDIR:-/tmp}/ninlight-spi-config.XXXXXX") + sudo sed -E 's/^[[:space:]]*#?[[:space:]]*dtparam=spi=(on|off)[[:space:]]*$/dtparam=spi=on/' "$CONFIG" >"$TMP_CONFIG" + sudo install -m 0644 -o root -g root "$TMP_CONFIG" "$CONFIG" + rm -f "$TMP_CONFIG" + echo " $CONFIG: dtparam=spi=on gesetzt (vorhandene Zeile aktiviert)" +else + printf '%s\n' 'dtparam=spi=on' | sudo tee -a "$CONFIG" >/dev/null + echo " $CONFIG: dtparam=spi=on ergaenzt" +fi + +# Signalisiert dem Aufrufer, dass ein Neustart noetig ist (siehe oben). +exit 10 +REMOTE +SPI_RC=$? +set -e +case "$SPI_RC" in + 0) ;; + 10) SPI_REBOOT_NEEDED=1 ;; + 20) ;; + *) echo "Warnung: SPI-Aktivierung ergab einen unerwarteten Fehler (rc=$SPI_RC) - /boot/firmware/config.txt bzw. /boot/config.txt von Hand pruefen." >&2 ;; +esac + echo "== Anzeigedienst aktivieren ==" # Die Anzeige wird aktiviert (startet also beim Booten mit) UND jetzt gestartet - anders # als die Station selbst, die absichtlich aus bleibt: die Anzeige sendet nichts, sie @@ -353,10 +459,24 @@ write_profile "$AP_CONNECTION" "$SSID" "$PSK" update write_profile "$MIGRATE_AP_CONNECTION" "$MIGRATE_SSID" "$MIGRATE_PSK" keep REMOTE -cat <<'EOF' +# Der Neustart-Hinweis steht an eigener, auffaelliger Stelle VOR den "Naechsten +# Schritten" - nicht dazwischengemischt - und nur, wenn oben wirklich etwas an der +# Boot-Konfiguration geaendert wurde (SPI_REBOOT_NEEDED). Ein Hinweis bei jedem Lauf, +# auch wenn SPI laengst an ist, wuerde nach ein paar Durchlaeufen ignoriert. +SPI_NOTE="" +if [ "$SPI_REBOOT_NEEDED" -eq 1 ]; then + SPI_NOTE=' +WICHTIG: SPI wurde soeben erst eingeschaltet (Aenderung an der Boot-Konfiguration) - + das wirkt erst nach einem Neustart. Bis dahin bleibt das E-Paper schwarz, + auch wenn sonst alles richtig eingerichtet ist. + ssh sudo reboot +' +fi + +cat < ninlight-station display test Testbild aufs E-Paper - bleibt es leer oder diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index e921dc3..d644c28 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -169,7 +169,12 @@ setup_state() { local state=$1 rm -rf "$state" mkdir -p "$state/etc" "$state/opt" "$state/usr/local/bin" "$state/etc/systemd/system" \ - "$state/etc/NetworkManager/system-connections" "$state/tmp" + "$state/etc/NetworkManager/system-connections" "$state/tmp" \ + "$state/boot/firmware" "$state/dev" + # $state/boot/firmware/config.txt und $state/boot/config.txt existieren absichtlich + # NICHT von selbst (ein Szenario legt sie an, wenn es sie braucht) - so deckt der + # Standardzustand genau den Fall "kein Boot-Verfahren gefunden" ab, ohne dass jedes + # Szenario das extra anfordern muesste. } # Fuehrt setup_pi.sh mit gestubbtem PATH und auf die Sandbox umgebogenen @@ -198,6 +203,9 @@ run_setup() { NINLIGHT_STATION_UNIT="$state/etc/systemd/system/ninlight-station.service" \ NINLIGHT_DISPLAY_UNIT="$state/etc/systemd/system/ninlight-display.service" \ NINLIGHT_NM_DIR="$state/etc/NetworkManager/system-connections" \ + NINLIGHT_BOOT_CONFIG_NEW="$state/boot/firmware/config.txt" \ + NINLIGHT_BOOT_CONFIG_OLD="$state/boot/config.txt" \ + NINLIGHT_SPIDEV_PATH="$state/dev/spidev0.0" \ "$STATION_DIR/setup_pi.sh" pi-test } @@ -638,6 +646,10 @@ echo "=== Szenario 17 (Task 6b F2, N2): ein gescheitertes 'apt-get update' verhi STATE="$TMPROOT/s17-state"; STUBBIN="$TMPROOT/bin17"; LOG="$TMPROOT/s17.log" setup_state "$STATE" setup_stubbin "$STUBBIN" "$LOG" +# SPI schon aktiv: dieses Szenario prueft ausschliesslich den Paketschritt, eine +# fehlende Boot-Konfiguration wuerde eine zweite, hier voellig unbeteiligte Warnung +# erzeugen (Szenario 26 deckt genau die ab). +printf 'dtparam=spi=on\n' >"$STATE/boot/firmware/config.txt" : >"$LOG" set +e OUT=$(FAKE_PIL_MISSING=1 FAKE_APT_UPDATE_EXIT=100 run_setup "$STATE" "$STUBBIN" 2>"$TMPROOT/s17.err") @@ -739,6 +751,161 @@ else fail "ninlight-display.service wurde nicht installiert - Szenario 20 kann nichts pruefen" fi +echo +echo "=== Szenario 21 (Hardware-Abnahme, SPI): fehlende dtparam=spi-Zeile wird ergaenzt ===" +# Auf einem frischen Pi ist SPI aus, es gibt aber schon eine config.txt (mit anderem +# Inhalt) ohne jede dtparam=spi-Zeile - der haeufigste Fall auf einem frisch +# geflashten Image. +STATE="$TMPROOT/s21-state"; STUBBIN="$TMPROOT/bin21"; LOG="$TMPROOT/s21.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +CONFIG21="$STATE/boot/firmware/config.txt" +cat >"$CONFIG21" <<'CFG' +# For more options and information see +# http://rptl.io/configtxt +dtparam=i2c_arm=on +dtparam=audio=on +CFG +BEFORE21=$(cat "$CONFIG21") +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fqx "dtparam=spi=on" "$CONFIG21" && pass "dtparam=spi=on wurde ergaenzt" || fail "dtparam=spi=on fehlt weiterhin: $(cat "$CONFIG21")" +grep -Fq "dtparam=i2c_arm=on" "$CONFIG21" && pass "der Rest der Datei blieb erhalten" || fail "vorhandener Inhalt ging verloren" +[ -f "${CONFIG21}.bak-ninlight" ] && pass "Sicherung angelegt" || fail "keine Sicherung unter ${CONFIG21}.bak-ninlight" +[ "$(cat "${CONFIG21}.bak-ninlight")" = "$BEFORE21" ] && pass "Sicherung entspricht dem Originalzustand" || fail "Sicherung weicht vom Original ab" +echo "$OUT" | grep -q "WICHTIG: SPI wurde soeben erst eingeschaltet" && pass "Neustart-Hinweis erscheint" || fail "kein Neustart-Hinweis in der Ausgabe: $OUT" +echo "$OUT" | grep -q "sudo reboot" && pass "der Hinweis nennt den Neustart-Befehl" || fail "kein 'sudo reboot' im Hinweis" + +echo +echo "=== Szenario 22 (Hardware-Abnahme, SPI): eine auskommentierte Zeile wird aktiviert ===" +STATE="$TMPROOT/s22-state"; STUBBIN="$TMPROOT/bin22"; LOG="$TMPROOT/s22.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +CONFIG22="$STATE/boot/firmware/config.txt" +cat >"$CONFIG22" <<'CFG' +dtparam=audio=on +#dtparam=spi=on +dtparam=i2c_arm=on +CFG +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fqx "dtparam=spi=on" "$CONFIG22" && pass "die Zeile ist jetzt aktiv" || fail "dtparam=spi=on steht nicht aktiv in der Datei: $(cat "$CONFIG22")" +COUNT22=$(grep -c 'dtparam=spi=on' "$CONFIG22") +[ "$COUNT22" -eq 1 ] && pass "die Zeile wurde ersetzt, nicht verdoppelt" || fail "dtparam=spi=on steht $COUNT22 mal in der Datei" +[ -f "${CONFIG22}.bak-ninlight" ] && pass "Sicherung angelegt" || fail "keine Sicherung angelegt" +echo "$OUT" | grep -q "WICHTIG: SPI wurde soeben erst eingeschaltet" && pass "Neustart-Hinweis erscheint" || fail "kein Neustart-Hinweis: $OUT" + +echo +echo "=== Szenario 23 (Hardware-Abnahme, SPI): 'dtparam=spi=off' wird umgeschaltet ===" +# An eine explizit deaktivierte Zeile ist genauso zu denken wie an eine +# auskommentierte - beide sind "nicht an", beide muessen normalisiert werden. +STATE="$TMPROOT/s23-state"; STUBBIN="$TMPROOT/bin23"; LOG="$TMPROOT/s23.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +CONFIG23="$STATE/boot/firmware/config.txt" +printf 'dtparam=spi=off\n' >"$CONFIG23" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fqx "dtparam=spi=on" "$CONFIG23" && pass "'off' wurde auf 'on' umgeschaltet" || fail "Zeile weiterhin nicht aktiv: $(cat "$CONFIG23")" +echo "$OUT" | grep -q "WICHTIG: SPI wurde soeben erst eingeschaltet" && pass "Neustart-Hinweis erscheint" || fail "kein Neustart-Hinweis: $OUT" + +echo +echo "=== Szenario 24 (Hardware-Abnahme, SPI): schon aktiv -> echtes No-Op, kein Neustart-Hinweis ===" +STATE="$TMPROOT/s24-state"; STUBBIN="$TMPROOT/bin24"; LOG="$TMPROOT/s24.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +CONFIG24="$STATE/boot/firmware/config.txt" +cat >"$CONFIG24" <<'CFG' +dtparam=audio=on +dtparam=spi=on +CFG +BEFORE24=$(cat "$CONFIG24") +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ "$(cat "$CONFIG24")" = "$BEFORE24" ] && pass "die Datei blieb byteweise unveraendert" || fail "die Datei wurde trotz bereits aktivem SPI angefasst" +[ ! -f "${CONFIG24}.bak-ninlight" ] && pass "keine Sicherung angelegt (es gab nichts zu sichern)" || fail "eine Sicherung wurde angelegt, obwohl nichts geaendert wurde" +echo "$OUT" | grep -q "bereits eingeschaltet" && pass "meldet ruhig, dass SPI schon an ist" || fail "keine Bestaetigung in der Ausgabe: $OUT" +echo "$OUT" | grep -q "WICHTIG: SPI wurde soeben erst eingeschaltet" && fail "Neustart-Hinweis erscheint, obwohl nichts geaendert wurde" || pass "kein Neustart-Hinweis" + +echo +echo "=== Szenario 25 (Hardware-Abnahme, SPI): /dev/spidev0.0 existiert schon -> SPI gilt als an, egal was in config.txt steht ===" +# Kann per Overlay oder einem anderen Mechanismus aktiviert worden sein - die Datei +# ist dann nicht die Wahrheit. +STATE="$TMPROOT/s25-state"; STUBBIN="$TMPROOT/bin25"; LOG="$TMPROOT/s25.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$(dirname "$STATE/dev/spidev0.0")" +: >"$STATE/dev/spidev0.0" +CONFIG25="$STATE/boot/firmware/config.txt" +printf '#dtparam=spi=on\n' >"$CONFIG25" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fqx "#dtparam=spi=on" "$CONFIG25" && pass "config.txt blieb unangetastet (spidev0.0 war entscheidend, nicht die Datei)" || fail "config.txt wurde trotz vorhandenem spidev0.0 geaendert: $(cat "$CONFIG25")" +[ ! -f "${CONFIG25}.bak-ninlight" ] && pass "keine Sicherung angelegt" || fail "eine Sicherung wurde angelegt, obwohl spidev0.0 schon da war" +echo "$OUT" | grep -q "SPI ist bereits aktiv" && pass "meldet, dass SPI ueber das Geraet schon aktiv ist" || fail "keine passende Meldung: $OUT" +echo "$OUT" | grep -q "WICHTIG: SPI wurde soeben erst eingeschaltet" && fail "Neustart-Hinweis erscheint, obwohl nichts geaendert wurde" || pass "kein Neustart-Hinweis" + +echo +echo "=== Szenario 26 (Hardware-Abnahme, SPI): weder config.txt noch config.txt (alt) vorhanden -> Warnung, kein Abbruch ===" +# Kein Raspberry Pi OS oder ein anderes Boot-Verfahren - die Anzeige ist nirgends +# Voraussetzung fuer eine funktionierende Station, dasselbe Prinzip wie beim +# apt-Schritt. +STATE="$TMPROOT/s26-state"; STUBBIN="$TMPROOT/bin26"; LOG="$TMPROOT/s26.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>"$TMPROOT/s26.err") +RC=$? +set -e +ERR=$(cat "$TMPROOT/s26.err") +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlender Boot-Konfiguration" || fail "Exit-Code $RC: $ERR" +echo "$ERR" | grep -qi "Warnung" && pass "Warnung auf stderr" || fail "keine Warnung: $ERR" +echo "$ERR" | grep -q "weder" && pass "Warnung nennt beide gesuchten Pfade" || fail "Warnung erwaehnt die Pfade nicht: $ERR" +[ -x "$STATE/opt/ninlight-station/server.py" ] && pass "die Station wurde trotzdem vollstaendig eingerichtet" || fail "server.py fehlt - ein fehlendes Boot-Verfahren haette die Einrichtung nicht verhindern duerfen" +echo "$OUT" | grep -q "WICHTIG: SPI wurde soeben erst eingeschaltet" && fail "Neustart-Hinweis erscheint, obwohl SPI gar nicht geprueft werden konnte" || pass "kein Neustart-Hinweis" + +echo +echo "=== Szenario 27 (Hardware-Abnahme, SPI): eine bestehende Sicherung wird nicht ueberschrieben ===" +# Ein zweiter Lauf, nachdem SPI zwischendurch von Hand wieder ausgeschaltet wurde - +# ohne diese Absicherung waere der allererste Originalzustand nach dem zweiten Lauf +# weg. +STATE="$TMPROOT/s27-state"; STUBBIN="$TMPROOT/bin27"; LOG="$TMPROOT/s27.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +CONFIG27="$STATE/boot/firmware/config.txt" +printf '#dtparam=spi=on\n' >"$CONFIG27" +printf 'urspruenglicher-originalzustand\n' >"${CONFIG27}.bak-ninlight" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -Fqx "dtparam=spi=on" "$CONFIG27" && pass "die Zeile wurde trotzdem aktiviert" || fail "dtparam=spi=on fehlt: $(cat "$CONFIG27")" +[ "$(cat "${CONFIG27}.bak-ninlight")" = "urspruenglicher-originalzustand" ] && pass "die bestehende Sicherung blieb unangetastet" || fail "die Sicherung wurde ueberschrieben: $(cat "${CONFIG27}.bak-ninlight")" +echo "$OUT" | grep -q "existiert bereits - wird nicht ueberschrieben" && pass "Ausgabe erwaehnt, dass die Sicherung nicht ueberschrieben wurde" || fail "keine entsprechende Meldung: $OUT" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From f105a0eb69144f85d1ae80ee8da526a009f9828f Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 15:12:45 +0200 Subject: [PATCH 131/150] fix(display): Freigabe-Ziel lesbar, Tests schriftunabhaengig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sechs Review-Befunde an e2d836a/2aab1b0. C1: die Pixelschwellen in test_render.py waren gegen Pillow 12/Arial kalibriert und auf dem Pi wie auf Debian 13 (DejaVu) rot - "> 150 schwarze Pixel in Zeile 11" sind dort nur 143. Die Zusagen ueber den Kopfbalken sind jetzt Anteile und Symmetrien statt Pixelzahlen (mindestens 55 % des Balkens schwarz, weisse Schrift links UND rechts der Mitte, Balken ueber die volle Breite). Zwei weitere schriftabhaengige Schwellen ebenso: die grosse Zeile hebt sich ueber das VERHAELTNIS der Tintenhoehen vom Beiwerk ab, und die Randgrenze zaehlt Zeilenbaender statt Tinten-Cluster. I2: im Zustand "armed" stand "FREIGEGEBEN: " in der Kopfzeile, wo 112 px Platz sind und der Text 182 px misst - gezeichnet wurde "FREIGEGEBE…", bei jedem Ziel gleich. Im Kopf steht jetzt nur noch das Label, der Zielname als eigene Beiwerkzeile ueber die volle Breite; die SSID-Zeile gibt dafuer in "armed" ihren Platz ab. Geprueft wird das gezeichnete Bild (Pixelvergleich gegen eine Referenzzeile), nicht lines(). I3: der Kommentar an display_handover() begruendete die Rueckgabewert-Durchreichung damit, dass der Handgriff bei stummem Panel rot werde. Das stimmt nicht - display.py beendet sich bei fehlendem Panel/SPI/Pillow mit 0. Der Kommentar sagt jetzt, dass man die Revision am Panel beurteilt und nicht am Exit-Code. I4: zwischen "systemctl stop" und "systemctl start" gab es keinen Signalhandler - ein Strg-C waehrend "display test" (der dokumentierte Weg, die Revision zu bestimmen, bei falscher Revision haengt das Panel) liess den Anzeigedienst gestoppt zurueck. trap auf INT/TERM/HUP, das Wiederanfahren in display_service_resume(), Handler beenden selbst mit 128 + Signalnummer. M2: HEADER_PHASE_MIN_WIDTH_PX bleibt als Sicherheitsnetz, ist jetzt aber getestet statt toter Code. M4: compose() baute je Aufruf fuenf Schriften neu. Prozess-Cache (mit ImageFont-Modul und FONT_SEARCH_PATHS im Schluessel, damit die Tests nicht verfaelscht werden) und faules Laden je Rolle: 25,0 ms -> 7,0 ms. Die Pixeltests laufen zusaetzlich unter Pillow 11.1 mit echtem DejaVu und unter fuenf weiteren Schriften. Sieben Mutationsproben angewandt, angesehen und zurueckgenommen. tools/test_host.sh gruen. --- tools/station/display/render.py | 123 +++++++++-- tools/station/ninlight-station | 42 +++- tools/station/tests/test_cli.sh | 80 ++++++++ tools/station/tests/test_render.py | 320 ++++++++++++++++++++++++++--- 4 files changed, 516 insertions(+), 49 deletions(-) diff --git a/tools/station/display/render.py b/tools/station/display/render.py index 59aa7b7..239fbca 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -50,12 +50,14 @@ TEXT_MARGIN_PX = 2 # Panelbreite. Invertiert kostet auf e-Paper nichts (bistabil, kein # Stromverbrauch pro Pixel) und trennt die Kopfzeile auf einen Blick # vom Rest. -# header_phase - die Phase (AUTO/HORCHT/FREIGEGEBEN: ...), NUR im Migrationsmodus und +# header_phase - die Phase (AUTO/HORCHT/FREIGEGEBEN), NUR im Migrationsmodus und # nur, wenn eine Phase bekannt ist. Steht in derselben Kopfzeile wie # header, rechtsbuendig, in kleinerer Schrift (zweite Hardware-Abnahme: # die Phase ging als eigene kleine Zeile unter dem Lampennamen unter). # Rettung/aus kennen keine Phase - dort bleibt die Kopfzeile -# unveraendert (nur header, mittig, wie zuvor). +# unveraendert (nur header, mittig, wie zuvor). NUR das kurze Label - +# der Zielname der Freigabe steht als eigene Beiwerkzeile, siehe +# ARMED_DETAIL_PREFIX. # hero - die eine wichtigste Information des Zustands (Migration: der # erkannte Produktname; Rettung: die SSID), fett. # detail - Beiwerk (Ziel/Variante, Lampenversion, Zaehler, letzte Auslieferung). @@ -100,8 +102,19 @@ HEADER_PHASE_GAP_PX = 6 # Bleibt nach Modus-Text und Puffer weniger als das fuer die Phase uebrig, wird sie ganz # weggelassen statt ein einzelnes "…" hinzuzeichnen - das waere ohnehin keine Auskunft # mehr wert (dritte Option aus der Abnahme: kuerzen, verkleinern oder weglassen; die -# ersten beiden greifen fuer die im Betrieb vorkommenden Werte immer, das hier ist nur -# das Sicherheitsnetz fuer einen theoretisch sehr langen Zielnamen). +# ersten beiden greifen fuer die im Betrieb vorkommenden Werte immer). +# +# Dritte Hardware-Abnahme, Befund M2: der Reviewer hat zu Recht angemerkt, dass dieser +# Zweig mit den heutigen Werten nicht feuern KANN - der Modus-Text im Migrationsmodus ist +# die Konstante "MIGRATION" (MODE_LABELS), available_px ist damit fest (112 px DejaVu, +# 127 px Arial) und nie < 24. Der Zweig bleibt trotzdem, aber er ist jetzt getestet +# (test_header_phase_is_dropped_when_too_little_room_is_left): was ihn aufweckt, ist +# nicht ein langer Zielname (den gibt es seit Befund I2 im Kopf gar nicht mehr), sondern +# ein schmalerer Rest neben dem Modus - eine groessere HEADER_FONT_PX, eine deutlich +# breitere Schrift, ein kuenftiger dritter Modus mit laengerem Namen oder ein groesserer +# HEADER_PHASE_GAP_PX. Ohne den Zweig waere die Folge kein fehlendes Wort, sondern ein +# "…", das sich mit dem Modus ueberlagert; das ist der Grund, warum hier ein +# totgeglaubtes Sicherheitsnetz billiger ist als sein Fehlen. HEADER_PHASE_MIN_WIDTH_PX = 24 # Suchkette fuer eine echte, skalierbare Schrift, in dieser Reihenfolge probiert @@ -138,10 +151,22 @@ FONT_SEARCH_PATHS = ( ) MODE_LABELS = {"recovery": "RETTUNG", "migration": "MIGRATION"} -# "armed" wird gesondert behandelt (braucht armed_target im Text), deshalb hier nicht -# gelistet. +# "armed" wird gesondert behandelt (es zieht zusaetzlich eine Beiwerkzeile mit dem +# Zielnamen nach sich), deshalb hier nicht gelistet. PHASE_LABELS = {"auto": "AUTO", "listen": "HORCHT"} +# Dritte Hardware-Abnahme, Befund I2: in der Kopfzeile stand frueher +# "FREIGEGEBEN: " - der Platz neben dem Modus reicht dafuer nicht (mit DejaVu +# auf dem Pi bleiben 112 px, der Text misst 182 px), gezeichnet wurde also "FREIGEGEBE…": +# ein Wortfragment, das bei JEDEM Ziel gleich aussieht. Ausgerechnet in "armed" muss der +# Bediener aber sehen, WELCHES Ziel freigegeben ist, sonst gibt er im Zweifel die falsche +# Lampe frei. Deshalb im Kopf nur noch das kurze Label (das auch bei einer deutlich +# breiteren Schrift hoechstens am Ende beschnitten wird, ohne dass dabei eine Auskunft +# verloren geht - der Zustand "freigegeben" ist der einzige mit diesem Label) und der +# Zielname als eigene Beiwerkzeile ueber die volle Panelbreite. +ARMED_LABEL = "FREIGEGEBEN" +ARMED_DETAIL_PREFIX = "Freigabe: " + def _hhmm(ts: object) -> str: """"2026-09-13T13:04:02Z" -> "13:04". Alles andere (fehlt, falsch geformt, aus einer @@ -205,9 +230,13 @@ def blocks(state: dict) -> list[tuple[str, str]]: # wird sie hier direkt nach dem ROLE_HEADER-Block angehaengt, nicht erst nach # der grossen Zeile. phase = state.get("phase") + armed_target = None if phase == "armed": - target = state.get("armed_target") or "?" - out.append((f"FREIGEGEBEN: {target}", ROLE_HEADER_PHASE)) + # Nur das Label in den Kopf, der Zielname kommt weiter unten als eigene + # Beiwerkzeile (siehe ARMED_DETAIL_PREFIX) - in den Kopf passt er nicht, + # und dort war er bis zur dritten Abnahme faktisch unsichtbar. + out.append((ARMED_LABEL, ROLE_HEADER_PHASE)) + armed_target = str(state.get("armed_target") or "?") elif phase in PHASE_LABELS: out.append((PHASE_LABELS[phase], ROLE_HEADER_PHASE)) @@ -225,6 +254,13 @@ def blocks(state: dict) -> list[tuple[str, str]]: elif ssid: out.append((str(ssid), ROLE_HERO)) + if armed_target is not None: + # ERSTE Beiwerkzeile, also die, die als letzte der unteren Randgrenze zum + # Opfer fiele (compose() bricht von unten ab, blocks() ist nach Wichtigkeit + # sortiert): in "armed" ist das freigegebene Ziel die Auskunft, wegen der + # der Bediener ueberhaupt aufs Panel sieht. + out.append((f"{ARMED_DETAIL_PREFIX}{armed_target}", ROLE_DETAIL)) + if product_name: target = last_seen.get("target") variant = last_seen.get("variant") @@ -233,9 +269,16 @@ def blocks(state: dict) -> list[tuple[str, str]]: lamp_version = last_seen.get("lamp_version") if lamp_version: out.append((f"FW: {lamp_version}", ROLE_DETAIL)) - if ssid: + if ssid and armed_target is None: # Nur hier, wo die grosse Zeile schon vergeben ist - sonst stuende die # SSID zweimal auf dem Panel. + # + # Und nur ohne Freigabezeile: aufs Panel passen neben der grossen Zeile + # fuenf Beiwerkzeilen (siehe BOTTOM_MARGIN_PX), die Freigabezeile ist die + # sechste. Von den beiden gibt die SSID nach - sie ist die Auskunft, mit + # der man das Netz FINDET, und in "armed" hat die Lampe es laengst + # gefunden (sie wurde ja erkannt, sonst gaebe es kein product_name); + # ohne erkannte Lampe steht die SSID ohnehin als grosse Zeile da. out.append((f"SSID: {ssid}", ROLE_DETAIL)) served = state.get("served") @@ -278,9 +321,25 @@ def _truncate_to_width(text: str, measure: Callable[[str], float], max_width_px: return (trimmed + ellipsis) if trimmed else ellipsis +# Prozess-Cache fuer _load_font(). Dritte Hardware-Abnahme, Befund M4: compose() baute je +# Aufruf FUENF Schriften neu (auf der Entwicklungsmaschine gemessen: 25,0 ms je compose(), +# davon 21,2 ms Schriftaufrufe - auf einem ARMv6-Kern ohne NEON grob 0,5-1 s, und das auf +# demselben Kern, auf dem server.py die Lampen bedient, bei JEDER ausgelieferten Lampe, +# weil sich der Zaehler aendert). +# +# Der Schluessel enthaelt das hereingereichte ImageFont-MODUL und FONT_SEARCH_PATHS, nicht +# nur Groesse/Fettung: beide werden in den Tests ersetzt (nachgebautes Modul, +# monkeypatch auf eine leere oder andere Suchkette), und ein Cache, der das ignoriert, +# wuerde einem Test die Schrift eines anderen unterschieben - genau der Grund, aus dem +# Task 7b noch bewusst auf einen Cache verzichtet hatte. +_FONT_CACHE: dict = {} + + def _load_font(image_font, size_px: int, bold: bool = False): """Laedt eine skalierbare Schrift der Groesse `size_px` aus FONT_SEARCH_PATHS - der - erste Pfad, der sich oeffnen laesst, gewinnt. + erste Pfad, der sich oeffnen laesst, gewinnt. Das Ergebnis wird prozessweit + zwischengespeichert (siehe _FONT_CACHE): dieselbe Schrift in derselben Groesse wird + je Prozess genau einmal von der Platte gelesen und geparst. `image_font` ist das PIL.ImageFont-MODUL, hereingereicht statt hier importiert: so bleibt render.py (ausserhalb von compose()) ohne Pillow importierbar - dasselbe @@ -297,13 +356,26 @@ def _load_font(image_font, size_px: int, bold: bool = False): Pillow 10.1, Pi 2 hat 9.4.0 (siehe FONT_SEARCH_PATHS). Ein `size=`-Aufruf waere dort ein TypeError mitten im Zeichnen - also genau in dem Pfad, der eigentlich der letzte Rettungsanker ist.""" + # Das Modul selbst (nicht seine id()) im Schluessel: Module und die nachgebauten + # Testmodule sind ueber ihre Identitaet hashbar, und solange der Schluessel das + # Objekt festhaelt, kann keine recycelte id() zwei verschiedene Module + # zusammenwerfen. + key = (image_font, size_px, bold, FONT_SEARCH_PATHS) + if key in _FONT_CACHE: + return _FONT_CACHE[key] + for regular_path, bold_path in FONT_SEARCH_PATHS: path = bold_path if bold else regular_path try: - return image_font.truetype(path, size_px) + font = image_font.truetype(path, size_px) + break except (OSError, ImportError): continue - return image_font.load_default() + else: + font = image_font.load_default() + + _FONT_CACHE[key] = font + return font def _draw_text(draw, text: str, font, top: int, height: int, fill: int, @@ -341,13 +413,26 @@ def compose(state: dict): image = Image.new("1", (PANEL_WIDTH_PX, PANEL_HEIGHT_PX), 255) draw = ImageDraw.Draw(image) - fonts = { - ROLE_HEADER: _load_font(ImageFont, HEADER_FONT_PX, bold=True), - ROLE_HEADER_PHASE: _load_font(ImageFont, HEADER_PHASE_FONT_PX, bold=True), - ROLE_HERO: _load_font(ImageFont, HERO_FONT_PX, bold=True), - ROLE_DETAIL: _load_font(ImageFont, DETAIL_FONT_PX), - ROLE_BIG: _load_font(ImageFont, BIG_FONT_PX, bold=True), + + # Je Rolle FAUL geladen, nicht alle fuenf im Voraus (Befund M4): "Station aus" und + # "Status unbekannt" zeichnen genau eine Rolle (ROLE_BIG) - vier der fuenf Schriften + # waeren dort reine Ladezeit. Zusammen mit _FONT_CACHE kostet jede Rolle im Betrieb + # hoechstens einmal je Prozess etwas, danach nur noch einen Woerterbuchzugriff. + role_fonts = { + ROLE_HEADER: (HEADER_FONT_PX, True), + ROLE_HEADER_PHASE: (HEADER_PHASE_FONT_PX, True), + ROLE_HERO: (HERO_FONT_PX, True), + ROLE_DETAIL: (DETAIL_FONT_PX, False), + ROLE_BIG: (BIG_FONT_PX, True), } + loaded: dict = {} + + def font_for(role: str): + if role not in loaded: + size_px, bold = role_fonts[role] + loaded[role] = _load_font(ImageFont, size_px, bold=bold) + return loaded[role] + max_width_px = PANEL_WIDTH_PX - 2 * TEXT_MARGIN_PX def fit(raw_text: str, font, width_px: float = max_width_px) -> str: @@ -372,7 +457,7 @@ def compose(state: dict): y = TEXT_MARGIN_PX for raw_text, role in block_list: - font = fonts[role] + font = font_for(role) if role == ROLE_BIG: # Einziger Block dieses Zustands (siehe blocks()) - er bekommt das ganze diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 4c07923..9dcbdd9 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -282,9 +282,24 @@ display_run() { # veraendert, was 'off' zuvor bewusst hergestellt hatte. Deshalb "is-active" vorher # fragen statt blind "stop" und "start". # -# Rueckgabewert ist der von display.py - der Handgriff muss weiterhin rot werden, wenn -# das Panel nicht antwortet; das Wiederanfahren des Dienstes darf ihn weder verdecken -# noch (bei eigenem Fehlschlag) einen Panel-Erfolg in einen Fehler verwandeln. +# Der Rueckgabewert von display.py wird unveraendert durchgereicht, und das +# Wiederanfahren des Dienstes darf ihn weder verdecken noch (bei eigenem Fehlschlag) in +# einen Fehler verwandeln. +# +# Was dieser Rueckgabewert NICHT ist: eine Aussage ueber das Panel. display.py's main() +# faengt DisplayUnavailable/ImportError ab, protokolliert eine Zeile und beendet sich mit +# sys.exit(0) - die ueber mehrere Fixrunden erkaempfte Zusage "die Anzeige ist nirgends +# Voraussetzung". Fehlendes Panel, fehlendes SPI, fehlendes Pillow, lgpio 'GPIO busy': +# alles rc=0. Rot wird der Handgriff nur bei einem echten Absturz (ein Traceback kommt +# unveraendert durch). Ob die Revision stimmt, beurteilst du deshalb AM PANEL - zeigt es +# ein sauberes, seitenrichtiges Testbild? - und nie am Exit-Code; "Exit 0" heisst hier +# ausdruecklich nicht "Panel in Ordnung". +display_service_resume() { + [ "${_was_active:-0}" -eq 1 ] || return 0 + sudo systemctl start ninlight-display 2>/dev/null \ + || echo "Warnung: Anzeigedienst konnte nicht wieder gestartet werden - 'systemctl status ninlight-display' pruefen" >&2 +} + display_handover() { _was_active=0 if systemctl is-active --quiet ninlight-display 2>/dev/null; then @@ -292,13 +307,26 @@ display_handover() { sudo systemctl stop ninlight-display 2>/dev/null || true fi + # Zwischen "stop" und "start" darf uns kein Signal aus der Hand nehmen, den Dienst + # wieder anzufahren. Der Fall ist nicht theoretisch: die Abnahmeprozedur in + # display.py's Modul-Docstring sagt ausdruecklich, dass bei falscher Revision + # ReadBusy() NIE zurueckkehrt - der Abnehmer probiert EPD_REVISION=V3, das Panel + # haengt, er drueckt Strg-C. Ohne Handler stirbt display_handover mit, der + # Anzeigedienst bleibt aus, das Panel bleibt schwarz, und mitten in der Lampenabnahme + # sieht das aus wie "Panel kaputt". + # + # Die Handler beenden SELBST (130/143/129 = 128 + Signalnummer): ein Handler ohne + # "exit" laesst die Shell hinter dem unterbrochenen Kommando weiterlaufen - in diesem + # Projekt schon einmal ein Befund (setup_pi.sh, Profilschreiben). + trap 'display_service_resume; exit 130' INT + trap 'display_service_resume; exit 143' TERM + trap 'display_service_resume; exit 129' HUP + _rc=0 display_run "$1" || _rc=$? - if [ "$_was_active" -eq 1 ]; then - sudo systemctl start ninlight-display 2>/dev/null \ - || echo "Warnung: Anzeigedienst konnte nicht wieder gestartet werden - 'systemctl status ninlight-display' pruefen" >&2 - fi + trap - INT TERM HUP + display_service_resume return "$_rc" } diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index 375d32a..fbd298d 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -198,11 +198,35 @@ setup_sandbox() { cp "$STATION_DIR/products.py" "$sandbox/lib/products.py" cat >"$sandbox/lib/display/display.py" <"))) + +# Testschalter fuer Fall D2c: stellt das Strg-C des Abnehmers nach, waehrend display.py +# laeuft (bei falscher Panel-Revision kehrt ReadBusy() nie zurueck - das ist der +# dokumentierte Weg, die Revision zu bestimmen, und damit der Regelfall fuer ein Strg-C). +# +# Nachgestellt wird, was das Terminal wirklich tut: das Signal geht an die GANZE +# Vordergrund-Prozessgruppe, also an die ninlight-station-Shell UND an diesen Prozess. +# Der Elternprozess ist dabei die Shell selbst ("sudo" und "env" sind Stub bzw. +# exec-Aufruf, der Python-Prozess haengt also direkt unter ihr). +# +# Dass dieser Prozess AM SIGNAL STIRBT (Standardbehandlung) statt sich mit 128+n zu +# beenden, ist kein Detail: eine Shell unterscheidet beides. Beendet sich das Kind +# normal, verwirft /bin/sh (bash im sh-Modus, macOS) das inzwischen zugestellte SIGINT +# und laeuft weiter - der Testfall waere dann auch ohne Signalhandler in +# display_handover() gruen und wuerde gar nichts pruefen (beim Schreiben dieses Tests +# genau so passiert). +signal_name = os.environ.get("FAKE_DISPLAY_SIGNAL", "") +if signal_name: + number = getattr(signal, "SIG" + signal_name) + os.kill(os.getppid(), number) + signal.signal(number, signal.SIG_DFL) + os.kill(os.getpid(), number) + sys.exit(int(os.environ.get("FAKE_DISPLAY_EXIT", "0"))) EOF @@ -672,6 +696,62 @@ set -e [ "$RC" -eq 1 ] && pass "Exit-Code 1 wird durchgereicht" || fail "Exit-Code $RC statt 1: $OUT" grep -Fq "STUB systemctl start ninlight-display" "$SBX/cmd.log" && pass "Anzeigedienst laeuft trotz Panel-Fehler wieder" || fail "Anzeigedienst blieb nach dem Fehler aus: $(cat "$SBX/cmd.log")" +echo +echo "=== Fall D2c: Strg-C waehrend 'display test' laesst den Anzeigedienst NICHT gestoppt zurueck ===" +# Der Ablauf aus der Abnahme: bei falscher Revision kehrt ReadBusy() nie zurueck +# (display.py-Docstring), der Abnehmer bricht mit Strg-C ab. Ohne Signalhandler zwischen +# "stop" und "start" stirbt display_handover mit - der Anzeigedienst bleibt aus, das +# Panel schwarz, und das sieht mitten in der Lampenabnahme aus wie "Panel kaputt". +# Geprueft wird fuer alle drei Signale, die das ausloesen koennen. +for SIG_NAME in INT TERM HUP; do + case "$SIG_NAME" in + INT) EXPECT_RC=130 ;; + TERM) EXPECT_RC=143 ;; + HUP) EXPECT_RC=129 ;; + esac + SBX="$TMPROOT/tD2c$SIG_NAME" + setup_sandbox "$SBX" + : >"$SBX/cmd.log" + set +e + OUT=$(FAKE_DISPLAY_SIGNAL="$SIG_NAME" run_cli "$SBX" display test 2>&1) + RC=$? + set -e + grep -Fq "STUB systemctl stop ninlight-display" "$SBX/cmd.log" \ + && pass "SIG$SIG_NAME: der Anzeigedienst war fuer den Handgriff gestoppt (Vorbedingung)" \ + || fail "SIG$SIG_NAME: Vorbedingung fehlt, der Dienst wurde gar nicht erst gestoppt: $(cat "$SBX/cmd.log")" + # GENAU einmal gestartet. Die Anzahl, nicht nur das Vorhandensein: der Handler muss + # selbst beenden (in diesem Projekt schon einmal ein Befund, setup_pi.sh). Ohne "exit" + # laeuft die Shell hinter dem unterbrochenen Kommando einfach weiter und faehrt den + # Dienst ein zweites Mal an - der Exit-Code sieht dabei zufaellig richtig aus (er ist + # dann der des am Signal gestorbenen Kindes), die doppelte Startzeile nicht. Genau + # diese Mutation ("trap ohne exit") war gegen eine blosse Vorhandensein-Pruefung + # gruen. + START_COUNT=$(grep -c "STUB systemctl start ninlight-display" "$SBX/cmd.log" || true) + [ "$START_COUNT" -eq 1 ] \ + && pass "SIG$SIG_NAME: der Anzeigedienst wird trotz Abbruch genau einmal wieder gestartet" \ + || fail "SIG$SIG_NAME: $START_COUNT Startversuche (1 erwartet) - Anzeigedienst bleibt aus oder der Handler beendet nicht selbst: $(cat "$SBX/cmd.log")" + [ "$RC" -eq "$EXPECT_RC" ] \ + && pass "SIG$SIG_NAME: Exit-Code $EXPECT_RC (128 + Signalnummer)" \ + || fail "SIG$SIG_NAME: Exit-Code $RC statt $EXPECT_RC: $OUT" +done + +echo +echo "=== Fall D2d: war der Anzeigedienst aus, startet ihn auch ein Strg-C nicht ===" +# Spiegelbild zu D2a: der Signalhandler darf den Zustand nicht anders behandeln als der +# normale Ausgang - sonst schaltete ein abgebrochener Handgriff die Anzeige ein, die +# 'off' zuvor bewusst ausgeschaltet hat. +SBX="$TMPROOT/tD2d" +setup_sandbox "$SBX" +: >"$SBX/cmd.log" +set +e +OUT=$(FAKE_DISPLAY_SERVICE_ACTIVE_EXIT=3 FAKE_DISPLAY_SIGNAL=INT run_cli "$SBX" display test 2>&1) +RC=$? +set -e +[ "$RC" -eq 130 ] && pass "Exit-Code 130 auch ohne laufenden Anzeigedienst" || fail "Exit-Code $RC statt 130: $OUT" +grep -Fq "STUB systemctl start ninlight-display" "$SBX/cmd.log" \ + && fail "der Signalhandler hat den Anzeigedienst eingeschaltet, obwohl er aus war: $(cat "$SBX/cmd.log")" \ + || pass "kein 'start' fuer einen Dienst, der gar nicht lief" + echo echo "=== Fall D3 (Task 6b, Punkt 4): 'display off' stoppt den Dienst VOR dem Leeren des Panels ===" # Umgekehrte Reihenfolge waere wirkungslos: der Daemon fragt alle 5 s nach und diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 20b5f91..425d49c 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -148,25 +148,94 @@ def test_off_screen_is_drawn_big_and_centered(): assert abs(top - bottom) <= 4, f"nicht senkrecht mittig (oben {top}, unten {bottom})" -def test_header_is_a_black_bar_with_white_text(): - """Die Kopfzeile ist invertiert: schwarzer Balken ueber die volle Breite, Modus in - Weiss darin. Geprueft an einer Zeile mitten im Balken - die muss ueberwiegend schwarz - sein UND weisse Pixel enthalten (sonst stuende dort kein Text, sondern nur ein - Balken).""" - image = render.compose(_migration_state()) - pixels = image.load() - row = render.HEADER_BAR_HEIGHT_PX // 2 +HEADER_MIN_BLACK_SHARE = 0.55 - black = sum(1 for x in range(render.PANEL_WIDTH_PX) if pixels[x, row] == 0) - white = sum(1 for x in range(render.PANEL_WIDTH_PX) if pixels[x, row] != 0) - assert black > 150, f"Kopfzeile ist kein schwarzer Balken (nur {black} schwarze Pixel)" - assert white >= 5, "keine weisse Schrift im Balken - der Balken uebermalt den Modus" + +def _assert_header_bar_with_text_on_both_sides(image, hint=""): + """Die Zusage ueber den Kopfbalken, SCHRIFTUNABHAENGIG formuliert (dritte + Hardware-Abnahme, Befund C1). + + Vorher stand hier eine absolute Pixelzahl ("> 150 schwarze Pixel in Zeile 11"), gegen + Pillow 12 mit ARIAL auf der Entwicklungsmaschine kalibriert. Auf dem Pi und auf der + Debian-13-Maschine zeichnet DejaVu, das merklich breiter laeuft - dort blieben 149 + schwarze Pixel uebrig und die Suite war rot, ohne dass sich am Code etwas geaendert + hatte. Die Schwelle einfach auf 145 nachzuziehen haette nur gegen die naechste + Schrift kalibriert. + + Stattdessen drei Zusagen, die keine Schrift kennen: + * ANTEIL statt Anzahl, und ueber den GANZEN Balken statt ueber eine einzelne Zeile: + mindestens 55 % des Balkens sind schwarz. Ueber sechs sehr verschiedene Schriften + gemessen (Arial, Arial Black, Verdana, Courier New, Impact, Georgia) liegt der + Anteil zwischen 69 % und 93 %, mit load_default() bei 93 % - die Schwelle trennt + also "Balken mit Schrift darin" von "kein Balken" (ohne Rechteck: rund 5 %), ohne + an einer Schriftbreite zu haengen. Eine einzelne Zeile allein taugt dafuer nicht: + dieselben sechs Schriften streuen in Zeile 11 zwischen 43 % und 69 %. + * Weisse Schrift LINKS UND RECHTS der Panelmitte: der Modus steht links, die Phase + rechts. Faellt eine von beiden aus (oder uebermalt der Balken den Text), ist die + betreffende Haelfte leer. + * Der Balken reicht ueber die volle Breite: die aeussersten Spalten sind + durchgehend schwarz - sie liegen vor TEXT_MARGIN_PX, dort kann keine Schrift + stehen.""" + pixels = image.load() + height = render.HEADER_BAR_HEIGHT_PX + width = render.PANEL_WIDTH_PX + middle = width // 2 + + black = sum(1 for y in range(height) for x in range(width) if pixels[x, y] == 0) + share = black / float(height * width) + assert share >= HEADER_MIN_BLACK_SHARE, ( + f"Kopfzeile ist kein schwarzer Balken (nur {share:.0%} schwarz){hint}") + + white_left = sum(1 for y in range(height) for x in range(middle) if pixels[x, y] != 0) + white_right = sum(1 for y in range(height) for x in range(middle, width) + if pixels[x, y] != 0) + assert white_left >= 5, f"kein Modus-Text links im Balken{hint}" + assert white_right >= 5, f"keine Phase rechts im Balken{hint}" + + for x in (0, width - 1): + assert all(pixels[x, y] == 0 for y in range(height)), ( + f"Spalte {x} des Kopfbalkens ist nicht durchgehend schwarz{hint}") + + +def test_header_is_a_black_bar_with_white_text(): + """Die Kopfzeile ist invertiert: schwarzer Balken ueber die volle Breite, Modus links + und Phase rechts in Weiss darin.""" + _assert_header_bar_with_text_on_both_sides(render.compose(_migration_state())) + + +def test_header_promise_survives_a_noticeably_wider_font(monkeypatch): + """Gegenprobe zu C1: dieselbe Zusage muss auch dann halten, wenn die Schrift deutlich + mehr Platz braucht als die hier gefundene. + + Statt eine zweite Schriftdatei vorauszusetzen (DejaVu gibt es auf der macOS- + Entwicklungsmaschine nicht, Arial nicht auf dem Pi - ein Test, der die eine oder die + andere braucht, waere auf je einer der beiden Maschinen nur ein Skip) wird dieselbe + Schrift 25 % groesser gezogen: das ist genau der Effekt, den eine breitere Schrift auf + diese Zusage hat, und es laeuft auf jeder Maschine gleich. Der Abstand DejaVu-zu-Arial + liegt bei rund 13 % Breite ("MIGRATION": 128 px gegen 113 px), hier also mit Puffer + ueberschritten. Dass die Phase dabei gekuerzt wird, ist erlaubt - sie darf nur nicht + verschwinden oder den Modus ueberlaufen.""" + monkeypatch.setattr(render, "HEADER_FONT_PX", int(render.HEADER_FONT_PX * 1.25)) + monkeypatch.setattr(render, "HEADER_PHASE_FONT_PX", + int(render.HEADER_PHASE_FONT_PX * 1.25)) + + _assert_header_bar_with_text_on_both_sides( + render.compose(_migration_state()), hint=" (mit 25 % breiterer Schrift)") def test_hero_line_is_bigger_than_the_details(): """Die wichtigste Zeile (hier: der erkannte Produktname) muss sich vom Beiwerk - abheben - sonst ist die Rollenvergabe in blocks() folgenlos. Gemessen: 15 px Tinte - fuer die grosse Zeile gegen 10 px fuer die erste Beiwerkzeile.""" + abheben - sonst ist die Rollenvergabe in blocks() folgenlos. + + Als VERHAELTNIS der Tintenhoehen, nicht als absoluter Pixelabstand (dritte + Hardware-Abnahme, C1: auch die uebrigen Schwellen dieser Datei schriftunabhaengig + machen). Die Zusage kommt aus dem Verhaeltnis der Schriftgroessen - + HERO_FONT_PX/DETAIL_FONT_PX = 16/11 = 1,45 - und das ueberlebt einen + Schriftwechsel, ein "mindestens 3 px mehr" nicht: ueber sieben Schriften gemessen + liegt das Verhaeltnis zwischen 1,30 (Courier New) und 1,60 (Verdana), der absolute + Abstand dagegen zwischen 3 px und 6 px - mit 3 px waere Courier New genau auf der + Kante. Mit load_default() (beide Rollen rund 8 px) liegt das Verhaeltnis bei 1,0, + der Test also weiterhin rot, wenn die Schriftsuche ausfaellt.""" image = render.compose(_migration_state()) hero_top = render.HEADER_BAR_HEIGHT_PX + render.TEXT_MARGIN_PX hero_bottom = hero_top + render.HERO_LINE_HEIGHT_PX @@ -177,9 +246,10 @@ def test_hero_line_is_bigger_than_the_details(): hero_height = hero[3] - hero[1] + 1 detail_height = detail[3] - detail[1] + 1 - assert hero_height >= detail_height + 3, ( + assert hero_height >= detail_height * 1.2, ( f"grosse Zeile ({hero_height} px) hebt sich nicht vom Beiwerk " - f"({detail_height} px) ab") + f"({detail_height} px) ab - Verhaeltnis " + f"{hero_height / float(detail_height):.2f}, erwartet mindestens 1,20") def test_the_last_delivery_line_still_fits_on_the_panel(): @@ -239,12 +309,22 @@ def test_compose_stops_drawing_details_before_the_bottom_margin(monkeypatch): image = render.compose({"mode": "migration"}) pixels = image.load() - rows_with_ink = [ - any(pixels[x, y] == 0 for x in range(render.PANEL_WIDTH_PX)) - for y in range(render.HEADER_BAR_HEIGHT_PX, render.PANEL_HEIGHT_PX)] + # Je Zeile IHR eigenes Band gezaehlt, nicht zusammenhaengende Tinten-Cluster + # (dritte Hardware-Abnahme, C1: "auch anderswo schriftabhaengige Schwellen suchen"): + # die Clusterzaehlung setzte stillschweigend voraus, dass zwischen zwei Zeilen eine + # komplett tintenfreie Pixelzeile bleibt. Das ist eine Aussage ueber die + # Schrifthoehe bei DETAIL_LINE_HEIGHT_PX Zeilenabstand - mit einer hoeher bauenden + # Schrift waeren zwei Zeilen zu einem Cluster verschmolzen und der Test rot, ohne + # dass am Code etwas falsch waere. Bandweise gezaehlt ist es egal, ob sich die + # Zeilen beruehren. + first_top = render.HEADER_BAR_HEIGHT_PX + render.TEXT_MARGIN_PX + bands = range((render.PANEL_HEIGHT_PX - first_top) // render.DETAIL_LINE_HEIGHT_PX) drawn_lines = sum( - 1 for i, has_ink in enumerate(rows_with_ink) - if has_ink and not (i > 0 and rows_with_ink[i - 1])) + 1 for i in bands + if any(pixels[x, y] == 0 + for y in range(first_top + i * render.DETAIL_LINE_HEIGHT_PX, + first_top + (i + 1) * render.DETAIL_LINE_HEIGHT_PX) + for x in range(render.PANEL_WIDTH_PX))) assert drawn_lines == 6, ( f"{drawn_lines} Beiwerkzeilen gezeichnet, sechs erwartet - " @@ -350,6 +430,115 @@ def test_long_armed_target_does_not_overlap_the_mode_in_the_header(): ) +def test_header_phase_is_dropped_when_too_little_room_is_left(monkeypatch): + """Deckt den "Weglassen"-Zweig ab (dritte Hardware-Abnahme, Befund M2): bleibt neben + dem Modus weniger als HEADER_PHASE_MIN_WIDTH_PX uebrig, wird die Phase ganz + weggelassen statt ein einsames, rechtsbuendiges "…" zu zeichnen, das sich mit dem + Modus ueberlagert. + + Mit den heutigen Werten kann der Zweig nicht von selbst feuern - der Modus-Text im + Migrationsmodus ist die Konstante "MIGRATION", available_px also fest (112 px DejaVu, + 127 px Arial). Hier wird deshalb der Mindestabstand hochgedreht: das ist der + direkteste, von jeder Schrift unabhaengige Weg, available_px unter die Schwelle zu + druecken, und genau das ist die Bedingung, um die es geht.""" + monkeypatch.setattr(render, "HEADER_PHASE_GAP_PX", 200) + + image = render.compose(_migration_state()) + pixels = image.load() + header_rows = range(render.HEADER_BAR_HEIGHT_PX) + white_cols = [x for x in range(render.PANEL_WIDTH_PX) + if any(pixels[x, y] == 255 for y in header_rows)] + + assert white_cols, "der Modus fehlt jetzt auch" + # Vorbedingung: der Modus selbst reicht nicht in den geprueften rechten Streifen - + # sonst wuerde der Test etwas anderes messen als "die Phase fehlt". + reserved_from = render.PANEL_WIDTH_PX - render.HEADER_PHASE_MIN_WIDTH_PX + assert max(white_cols) < reserved_from - 4, ( + f"der Modus-Text reicht bis Spalte {max(white_cols)} - der Test kann so nicht " + "zwischen Modus und Phase unterscheiden") + assert not [x for x in white_cols if x >= reserved_from], ( + "die Phase wurde trotz fehlenden Platzes gezeichnet - rechtsbuendig, also " + "quer ueber den Modus") + + +# --- Schriftladen: Prozess-Cache und faules Laden (Befund M4) ------------------------ +# +# compose() baute je Aufruf fuenf Schriften neu: auf der Entwicklungsmaschine 21,2 ms von +# 25,0 ms, auf einem ARMv6-Kern grob 0,5-1 s - bei JEDER ausgelieferten Lampe, auf +# demselben Kern wie server.py. + +def test_font_cache_reuses_the_same_font_object(): + """Zweimal dieselbe Rolle -> die Schriftdatei wird nur einmal gelesen.""" + image_font = _FakeImageFontModule(available=(DEJAVU_REGULAR, DEJAVU_BOLD)) + + first = render._load_font(image_font, 11) + second = render._load_font(image_font, 11) + + assert first is second + assert len(image_font.truetype_calls) == 1, ( + f"Schrift mehrfach geladen: {image_font.truetype_calls}") + + +def test_font_cache_keeps_different_modules_and_search_paths_apart(monkeypatch): + """Der Cache-Schluessel muss das hereingereichte ImageFont-Modul UND + FONT_SEARCH_PATHS enthalten - beides wird in den Tests ersetzt. Ein Cache, der nur + Groesse/Fettung kennt, wuerde z.B. test_compose_still_works_without_any_font_file + (leere Suchkette) eine vorher geladene echte Schrift unterschieben und die Zusage + "eine fehlende Schrift haelt nichts auf" stillschweigend entwerten.""" + first_module = _FakeImageFontModule(available=(DEJAVU_REGULAR,)) + second_module = _FakeImageFontModule(available=(DEJAVU_REGULAR,)) + + render._load_font(first_module, 11) + render._load_font(second_module, 11) + assert len(second_module.truetype_calls) == 1, "Cache reicht Modulgrenzen hinueber" + + monkeypatch.setattr(render, "FONT_SEARCH_PATHS", ()) + assert render._load_font(first_module, 11) == ("default",), ( + "leere Suchkette liefert eine zwischengespeicherte Schrift von vorher") + + +def test_compose_loads_only_the_roles_it_actually_draws(monkeypatch): + """"Station aus" zeichnet genau eine Rolle - die anderen vier Schriften duerfen gar + nicht erst gebaut werden (faules Laden, Befund M4).""" + real_load_font = render._load_font + requested = [] + + def spy(image_font, size_px, bold=False): + requested.append(size_px) + return real_load_font(image_font, size_px, bold=bold) + + monkeypatch.setattr(render, "_load_font", spy) + render.compose({"mode": "off"}) + + assert requested == [render.BIG_FONT_PX], ( + f"compose() hat {len(requested)} Schriften angefordert, gezeichnet wird eine: " + f"{requested}") + + +def test_compose_reads_no_font_file_a_second_time(monkeypatch): + """Der Prozess-Cache am echten Pillow: der zweite compose()-Aufruf desselben + Prozesses fasst keine Schriftdatei mehr an. Das ist die Zusage, an der auf dem Pi die + Zeichenzeit haengt - der Daemon zeichnet bei jeder ausgelieferten Lampe neu.""" + from PIL import ImageFont + + state = _migration_state() + render.compose(state) # Cache fuellen + + truetype_calls = [] + real_truetype = ImageFont.truetype + + def counting_truetype(*args, **kwargs): + truetype_calls.append(args) + return real_truetype(*args, **kwargs) + + monkeypatch.setattr(ImageFont, "truetype", counting_truetype) + render.compose(state) + + assert truetype_calls == [], ( + f"Schriften werden bei jedem compose() neu von der Platte gelesen: " + f"{truetype_calls}") + + def test_compose_still_works_without_any_font_file(monkeypatch): """Die Zusage "eine fehlende Schrift haelt die Anzeige nicht auf" gilt unveraendert: ohne einen einzigen Treffer in der Suchkette faellt compose() auf load_default() @@ -451,10 +640,95 @@ def test_missing_fields_do_not_raise(): assert render.lines(state) -def test_armed_phase_is_visible(): +# --- "armed": WELCHE Lampe ist freigegeben? (dritte Hardware-Abnahme, Befund I2) ----- +# +# Frueher stand "FREIGEGEBEN: " als Phase im Kopfbalken. Neben dem Modus bleiben +# dort 112 px (DejaVu) - der Text misst 182 px, gezeichnet wurde "FREIGEGEBE…", also ein +# Wortfragment, das bei jedem Ziel gleich aussieht. Der alte Test dazu +# (test_armed_phase_is_visible) prueft lines(), also den UNGEKUERZTEN Text, und war +# deshalb gruen, waehrend auf dem Panel nichts davon zu lesen war. Die Tests hier pruefen +# das GEZEICHNETE BILD. + +def _armed_state(): + """Wie _migration_state(), aber mit einem Produktnamen OHNE Unterlaengen: die + Bildvergleiche unten schneiden die erste Beiwerkzeile zeilengenau aus, und ein 'y' + oder 'q' der grossen Zeile darueber ragte in diesen Ausschnitt hinein.""" + state = _migration_state() + state["last_seen"] = dict(state["last_seen"], product_name="KLYQA GU10 RGB") + return state + + +def _detail_band(index): + """(oben, unten) der `index`-ten Beiwerkzeile unter der grossen Zeile.""" + top = (render.HEADER_BAR_HEIGHT_PX + render.TEXT_MARGIN_PX + + render.HERO_LINE_HEIGHT_PX + index * render.DETAIL_LINE_HEIGHT_PX) + return top, top + render.DETAIL_LINE_HEIGHT_PX + + +def _band_pixels(image, top, bottom): + pixels = image.load() + return [tuple(pixels[x, y] for x in range(render.PANEL_WIDTH_PX)) + for y in range(top, bottom)] + + +def _reference_detail_line(text, top): + """Zeichnet `text` allein, mit derselben Schrift und an derselben Stelle, die + compose() fuer eine Beiwerkzeile benutzt. Der Vergleich mit dem echten Bild ist damit + schriftunabhaengig: er sagt "genau dieser Text, ungekuerzt", ohne eine Pixelbreite zu + behaupten.""" + from PIL import Image, ImageDraw, ImageFont + + image = Image.new("1", (render.PANEL_WIDTH_PX, render.PANEL_HEIGHT_PX), 255) + draw = ImageDraw.Draw(image) + draw.text((render.TEXT_MARGIN_PX, top), text, + font=render._load_font(ImageFont, render.DETAIL_FONT_PX), fill=0) + return image + + +def test_armed_target_is_drawn_in_full_as_its_own_line(): + """Der Zielname muss vollstaendig und lesbar auf dem Panel stehen - als eigene + Beiwerkzeile ueber die volle Breite, nicht als Rest eines im Kopf abgeschnittenen + Texts. Geprueft durch Pixelvergleich mit einer unabhaengig gezeichneten Referenzzeile: + kuerzt compose() den Namen (oder laesst die Zeile ganz weg), stimmt der Ausschnitt + nicht mehr ueberein.""" + state = _armed_state() + expected = render.ARMED_DETAIL_PREFIX + state["armed_target"] + assert expected in render.lines(state), render.lines(state) + + top, bottom = _detail_band(0) + drawn = _band_pixels(render.compose(state), top, bottom) + reference = _band_pixels(_reference_detail_line(expected, top), top, bottom) + + assert drawn == reference, ( + f"die erste Beiwerkzeile zeigt nicht ungekuerzt {expected!r} - der Bediener " + "kann nicht sehen, welche Lampe freigegeben ist") + + +def test_header_does_not_depend_on_the_armed_target_name(): + """Die Gegenrichtung, und die eigentliche Mutationswache fuer I2: der Kopfbalken darf + den Zielnamen gar nicht mehr enthalten. Das laesst sich ohne jede Schriftannahme + pruefen - zwei sehr verschiedene Zielnamen muessen denselben Kopfbalken ergeben. + Zeichnet ihn jemand wieder in den Kopf, unterscheiden sich die beiden Balken (oder, + bei fruehem Abschneiden, spaetestens beim langen Namen).""" + short = _armed_state() + long_name = _armed_state() + long_name["armed_target"] = "sehr_langer_zielname_" * 4 + + band = (0, render.HEADER_BAR_HEIGHT_PX) + assert (_band_pixels(render.compose(short), *band) + == _band_pixels(render.compose(long_name), *band)), ( + "der Kopfbalken haengt am Zielnamen - dort ist kein Platz dafuer, er wird " + "abgeschnitten und ist bei jedem Ziel gleich unlesbar") + + +def test_armed_phase_label_is_visible(): + """Das kurze Label bleibt (es sagt, DASS etwas freigegeben ist) - nur der Zielname + steht nicht mehr daneben.""" lines = render.lines({"mode": "migration", "phase": "armed", "armed_target": "g95_cw", "served": 0, "recent": [], "ssid": "qconnex-firmware"}) - assert "FREIGEGEBEN" in "\n".join(lines) + text = "\n".join(lines) + assert render.ARMED_LABEL in text + assert "g95_cw" in text # --- display.build_state() (Fixrunde 1, I3) --------------------------------------- From 1f01b29b8ed9f571d792c40afcc4bb65873c6b8a Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 19:39:42 +0200 Subject: [PATCH 132/150] feat(station): optionaler Zeitschalter schaltet die Station selbst ab "on"/"recovery on"/"migrate on" nehmen jetzt "--for " (1-240): die Station schaltet sich danach selbsttaetig ab und kehrt ins Heim-WLAN zurueck. Das hilft, wenn man ueber WLAN angemeldet ist - "on" macht wlan0 zum Zugangspunkt und reisst jede SSH-Sitzung ab. Ausdruecklich optional: ohne "--for" entsteht kein Zeitschalter und alles verhaelt sich exakt wie bisher. Dazu "renew []" (Uhr zurueckstellen), "hold" (abbestellen, Station bleibt an) und eine Restzeit in "status"; "off" raeumt einen laufenden Zeitschalter mit ab. Umgesetzt als transienter systemd-Timer unter festem Unit-Namen (systemd-run), Zustand in RUN_DIR/autooff.json. Vor dem Abschalten wird per "ss" geprueft, ob noch eine Verbindung auf Port 80 aus dem Stationsnetz offen ist - dann wird um rund eine Minute verschoben, hoechstens zehnmal (Begruendung im Kommentar). Das Panel zeigt den Zeitschalter als eigene Zeile, aber nur wenn einer laeuft. --- tools/station/display/display.py | 32 +++ tools/station/display/render.py | 17 ++ tools/station/ninlight-station | 391 +++++++++++++++++++++++++++-- tools/station/tests/test_cli.sh | 302 +++++++++++++++++++++- tools/station/tests/test_render.py | 116 +++++++++ 5 files changed, 841 insertions(+), 17 deletions(-) diff --git a/tools/station/display/display.py b/tools/station/display/display.py index 23ee305..d32775b 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -113,6 +113,32 @@ def _read_conf(path: Path) -> dict[str, str]: return values +def _read_autooff_minutes(run_dir: Path, now: float | None = None): + """Restzeit des optionalen Zeitschalters in ganzen Minuten (aufgerundet), oder None, + wenn gerade keiner laeuft. + + Quelle ist RUN_DIR/autooff.json, die "ninlight-station --for/renew" schreibt und + "hold"/"off" wieder loescht (siehe dort). Ein Zeitschalter, den man am Panel nicht + sieht, waere schlimmer als keiner: man glaubt, die Station laeuft, und sie ist + laengst aus. + + "deadline" ist eine Unix-Zeit (der Zeitschalter selbst haengt an systemds monotoner + Uhr, die Anzeige rechnet in Wanduhrzeit) - eine Zeitverstellung verschiebt also die + angezeigte Restzeit, nicht das tatsaechliche Abschalten. Rundet auf: solange noch + eine Sekunde uebrig ist, steht dort "1 min" und nicht "0 min".""" + data = _read_json(run_dir / "autooff.json") + deadline = data.get("deadline") + if isinstance(deadline, bool) or not isinstance(deadline, (int, float)): + return None + remaining_s = deadline - (time.time() if now is None else now) + if remaining_s <= 0: + # Der Termin ist da (oder der Lauf verschiebt gerade) - "0 min" ist hier die + # ehrliche Auskunft, nicht das Verschwinden der Zeile. + return 0 + whole, rest = divmod(remaining_s, 60) + return int(whole) + (1 if rest else 0) + + def _read_mode_file(run_dir: Path) -> str: """Modus, in dem der Dienst laut RUN_DIR/mode tatsaechlich gestartet wurde. Fehlt die Datei oder ist ihr Inhalt kein bekannter Modus, gilt "recovery" - derselbe Vorgabewert @@ -185,6 +211,12 @@ def build_state(run_dir: Path = RUN_DIR, conf_path: Path = CONF_PATH, state["phase"] = phase if phase in ("auto", "listen", "armed") else "auto" state["armed_target"] = control.get("armed_target") + # Der Zeitschalter ist optional und gilt in beiden Modi gleichermassen - ohne ihn + # bleibt das Feld weg und render.blocks() zeichnet keine Zeile dafuer. + autooff_minutes = _read_autooff_minutes(run_dir) + if autooff_minutes is not None: + state["autooff_minutes"] = autooff_minutes + return state diff --git a/tools/station/display/render.py b/tools/station/display/render.py index 239fbca..c3c4489 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -281,6 +281,23 @@ def blocks(state: dict) -> list[tuple[str, str]]: # ohne erkannte Lampe steht die SSID ohnehin als grosse Zeile da. out.append((f"SSID: {ssid}", ROLE_DETAIL)) + # Der optionale Zeitschalter, in BEIDEN Modi und vor den reinen Zaehlerzeilen: ein + # Zeitschalter, den man am Panel nicht sieht, ist schlimmer als keiner - man glaubt, + # die Station laeuft, und sie ist laengst aus. Fehlt er (der Regelfall, denn er + # entsteht nur mit "--for"), fehlt die Zeile ersatzlos. + # + # Er steht damit VOR "Ausgeliefert"/"zuletzt ausgeliefert": im vollsten Zustand + # passen fuenf Beiwerkzeilen aufs Panel (siehe BOTTOM_MARGIN_PX), die sechste faellt + # unten heraus. Von diesen beiden Auskuenften ist "wann geht die Station aus" + # diejenige, nach der man ueberhaupt aufs Panel sieht - der Zaehler der letzten + # Auslieferung steht auch noch in "ninlight-station status". + autooff_minutes = state.get("autooff_minutes") + if isinstance(autooff_minutes, int) and not isinstance(autooff_minutes, bool): + if autooff_minutes > 0: + out.append(("Auto-Aus in {} min".format(autooff_minutes), ROLE_DETAIL)) + else: + out.append(("Auto-Aus steht an", ROLE_DETAIL)) + served = state.get("served") out.append((f"Ausgeliefert: {served if isinstance(served, int) else 0}", ROLE_DETAIL)) diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 9dcbdd9..a10304b 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -22,6 +22,56 @@ CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf} RUN_DIR=${NINLIGHT_RUN_DIR:-/run/ninlight-station} STATION_LIB=${NINLIGHT_STATION_LIB:-/opt/ninlight-station} +# --- Zeitschalter ("Auto-Aus") ----------------------------------------------------- +# +# Rein OPTIONAL: nur "--for " legt einen an. Ohne die Angabe passiert hier +# nichts, und die Station verhaelt sich exakt wie vorher - der Regelfall ist der Pi am +# Kabel, dem niemand den Zugangspunkt unter der laufenden Arbeit wegschalten soll. +# +# Wozu er dann gut ist: "on" macht wlan0 zum Zugangspunkt und reisst damit JEDE +# SSH-Sitzung ueber WLAN ab. Wer den Pi nicht am Kabel hat, sperrt sich damit aus und +# kommt nur noch ueber Tastatur und Monitor zurueck. Der Zeitschalter schaltet die +# Station nach der angeforderten Zeit selbsttaetig ab (= zurueck ins Heim-WLAN). +# +# Umgesetzt mit einem transienten systemd-Timer unter FESTEM Unit-Namen (systemd-run +# --unit=...): so sind "renew"/"hold"/"off" schlichte systemctl-Aufrufe auf einen +# bekannten Namen, und es braucht keine zusaetzliche Unit-Datei im Repo. +AUTOOFF_UNIT=${NINLIGHT_AUTOOFF_UNIT:-ninlight-station-autooff} +# Eigene Zustandsdatei neben mode/control.json, im selben RUN_DIR und mit denselben +# Rechten (0644): sie ist die Quelle fuer "status", fuer "renew" ohne Argument (welche +# Dauer war zuletzt angefordert?) und fuer die Panelzeile - display.py liest sie. Sie ist +# zugleich die Antwort auf "laeuft ueberhaupt einer?": jeder Weg, der den Timer anlegt +# oder abraeumt, schreibt bzw. loescht sie in derselben Funktion. +AUTOOFF_FILE="$RUN_DIR/autooff.json" +# Abstand zwischen zwei Anlaeufen, wenn gerade ausgeliefert wird ("in etwa einer Minute +# erneut versuchen"). Der Timer bringt ihn selbst mit (--on-unit-inactive), es wird also +# nichts nachgelegt - siehe autooff_arm(). +AUTOOFF_RETRY_S=${NINLIGHT_AUTOOFF_RETRY_S:-60} +# Obergrenze fuers Verschieben. 10 Anlaeufe x 1 min = rund 10 Minuten Aufschub. +# +# Begruendung der Zahl: eine Auslieferung ist ein einzelner HTTP-GET ueber ein halbes +# Megabyte im eigenen WLAN - Sekunden, keine Minuten. Eine Verbindung, die nach zehn +# Minuten noch offen steht, liefert nicht mehr aus, sie haengt (Lampe abgestuerzt, +# Funkloch, halboffener Socket, den der Kernel noch als ESTABLISHED fuehrt). Ohne +# Obergrenze wuerde genau so eine haengende Verbindung den Zeitschalter dauerhaft +# aushebeln - und damit ausgerechnet den Fall herbeifuehren, gegen den er angefordert +# wurde: der Nutzer sitzt ohne Kabel da und kommt nicht mehr an den Pi. Zehn Minuten +# sind lang genug, dass keine echte Auslieferung abgeschnitten wird, und kurz genug, +# dass der Zeitschalter seine Zusage noch einloest. +AUTOOFF_MAX_DEFERRALS=${NINLIGHT_AUTOOFF_MAX_DEFERRALS:-10} +# Das Netz des Zugangspunkts (server.py haengt auf 192.168.8.1:80) - daran wird erkannt, +# ob gerade eine Lampe laedt, ohne dafuer etwas am Server zu aendern. +AUTOOFF_STATION_NET=192.168.8.0/24 +# Sinnvolle Grenzen fuer --for/renew. +AUTOOFF_MIN_MINUTES=1 +AUTOOFF_MAX_MINUTES=240 + +# Absoluter Pfad auf dieses Skript - der Timer ruft es spaeter ohne unser $PWD auf. +case "$0" in + /*) SELF=$0 ;; + *) SELF=$(cd "$(dirname "$0")" && pwd)/$(basename "$0") ;; +esac + if [ -r "$CONF" ]; then # shellcheck disable=SC1090 . "$CONF" @@ -132,6 +182,180 @@ write_control() { sudo mv -f "$_tmp" "$RUN_DIR/control.json" } +# --- Zeitschalter: Zustandsdatei, Anlegen, Abraeumen, Pruefung ----------------------- + +# Prueft eine Minutenangabe, BEVOR irgendetwas eingeschaltet wird (Zusage: eine +# ungueltige Angabe schaltet gar nichts an). Gibt 0 zurueck, wenn sie taugt. +autooff_check_minutes() { + case "${1:-}" in + '' ) + echo "Fehler: '--for' braucht eine Angabe in Minuten ($AUTOOFF_MIN_MINUTES-$AUTOOFF_MAX_MINUTES)" >&2 + return 1 + ;; + *[!0-9]* ) + echo "Fehler: '$1' ist keine Minutenangabe - erlaubt sind ganze Zahlen von $AUTOOFF_MIN_MINUTES bis $AUTOOFF_MAX_MINUTES" >&2 + return 1 + ;; + esac + if [ "$1" -lt "$AUTOOFF_MIN_MINUTES" ] || [ "$1" -gt "$AUTOOFF_MAX_MINUTES" ]; then + echo "Fehler: $1 Minuten liegen ausserhalb von $AUTOOFF_MIN_MINUTES-$AUTOOFF_MAX_MINUTES" >&2 + return 1 + fi +} + +# Schreibt $AUTOOFF_FILE atomar mit 0644 - dasselbe Muster und dieselben Rechte wie +# write_control(), damit display.py (laeuft als root) und das CLI (laeuft als normaler +# Benutzer) sie beide lesen koennen. +# +# $1 = angeforderte Dauer in Minuten (bleibt ueber Verschiebungen hinweg unveraendert - +# "renew" ohne Argument stellt genau darauf zurueck), $2 = Zeitpunkt des naechsten +# Anlaufs als Unix-Zeit, $3 = Zahl der bisherigen Verschiebungen. +autooff_write() { + sudo mkdir -p "$RUN_DIR" + _tmp="$RUN_DIR/.autooff-$$.json" + _json=$(printf '{"minutes": %s, "deadline": %s, "deferrals": %s}' "$1" "$2" "$3") + printf '%s' "$_json" | sudo tee "$_tmp" >/dev/null && sudo chmod 0644 "$_tmp" || return 1 + sudo mv -f "$_tmp" "$RUN_DIR/autooff.json" +} + +# Gibt " " aus, oder scheitert (Rueckgabewert 1), +# wenn kein Zeitschalter vorgemerkt ist bzw. die Datei unbrauchbar ist. +autooff_read() { + [ -e "$AUTOOFF_FILE" ] || return 1 + python3 - "$AUTOOFF_FILE" <<'PY' +import json +import sys + +try: + with open(sys.argv[1]) as f: + d = json.load(f) +except (OSError, ValueError): + sys.exit(1) +try: + print(int(d["minutes"]), int(d["deadline"]), int(d.get("deferrals", 0))) +except (KeyError, TypeError, ValueError): + sys.exit(1) +PY +} + +# Haelt NUR den Timer an, nie die gleichnamige .service-Einheit: wenn diese Funktion aus +# dem gefeuerten Timer heraus laeuft (autooff_fire -> full_off), ist genau diese Einheit +# der eigene Prozess - ein "systemctl stop $AUTOOFF_UNIT" ohne Endung loeste sich zur +# .service auf und wuerde sich mitten im Abschalten selbst umbringen. +autooff_stop_unit() { + sudo systemctl stop "$AUTOOFF_UNIT.timer" >/dev/null 2>&1 || true + # Damit ein spaeteres systemd-run denselben Namen wieder vergeben kann, auch wenn ein + # frueherer Lauf im Zustand "failed" haengengeblieben ist. + sudo systemctl reset-failed "$AUTOOFF_UNIT.timer" "$AUTOOFF_UNIT.service" >/dev/null 2>&1 || true +} + +# Timer anhalten UND die Vormerkung loeschen - "es laeuft keiner mehr". +autooff_clear() { + autooff_stop_unit + sudo rm -f "$AUTOOFF_FILE" +} + +# Legt den Zeitschalter an (oder erneuert ihn). +# $1 = angeforderte Dauer in Minuten, $2 = Verzoegerung des naechsten Anlaufs in +# Sekunden, $3 = Zahl der bisherigen Verschiebungen. +# +# --on-unit-inactive sorgt dafuer, dass der Timer nach JEDEM Lauf der Aktion erneut +# feuert. Das ist die Verschiebung "in etwa einer Minute" (siehe autooff_fire): der +# gefeuerte Lauf kann sie nicht selbst nachlegen, weil er unter demselben Unit-Namen +# laeuft und systemd-run einen belegten Namen ablehnt. Wird nicht verschoben, endet der +# Lauf in full_off -> autooff_clear, und der Timer ist weg, bevor er ein zweites Mal +# feuern koennte. +autooff_arm() { + _req=$1 + _delay=$2 + _deferrals=$3 + autooff_stop_unit + set -- --unit="$AUTOOFF_UNIT" \ + --description="ninlight-station: Zeitschalter (Auto-Aus)" \ + --on-active="${_delay}s" \ + --on-unit-inactive="${AUTOOFF_RETRY_S}s" + # Die Testumgebung (tests/test_cli.sh) laeuft gegen eine Sandbox statt gegen + # /etc und /run; der spaeter gefeuerte Lauf haette davon sonst nichts. Im Betrieb + # sind diese Variablen nicht gesetzt und es wird nichts durchgereicht. + if [ -n "${NINLIGHT_STATION_CONF:-}" ]; then + set -- "$@" "--setenv=NINLIGHT_STATION_CONF=$NINLIGHT_STATION_CONF" + fi + if [ -n "${NINLIGHT_RUN_DIR:-}" ]; then + set -- "$@" "--setenv=NINLIGHT_RUN_DIR=$NINLIGHT_RUN_DIR" + fi + if [ -n "${NINLIGHT_STATION_LIB:-}" ]; then + set -- "$@" "--setenv=NINLIGHT_STATION_LIB=$NINLIGHT_STATION_LIB" + fi + if ! sudo systemd-run "$@" "$SELF" __autooff >/dev/null; then + echo "Fehler: Zeitschalter liess sich nicht anlegen (systemd-run '$AUTOOFF_UNIT') - die Station bleibt AN, 'ninlight-station off' nicht vergessen." >&2 + return 1 + fi + autooff_write "$_req" "$(( $(date +%s) + _delay ))" "$_deferrals" || { + echo "Fehler: $AUTOOFF_FILE konnte nicht geschrieben werden - Zeitschalter wieder abgeraeumt" >&2 + autooff_clear + return 1 + } +} + +# Laeuft gerade eine Auslieferung? Geprueft an den offenen TCP-Verbindungen auf Port 80 +# aus dem Stationsnetz - ohne jede Aenderung an server.py. +# +# "ss" liegt auf Debian in /usr/sbin; der gefeuerte Timer-Lauf bringt nicht zwingend +# denselben $PATH mit wie eine Anmeldesitzung, deshalb die beiden festen Pfade als +# Rueckfall. +autooff_busy() { + _ss=$(command -v ss 2>/dev/null || true) + if [ -z "$_ss" ]; then + for _cand in /usr/sbin/ss /sbin/ss; do + if [ -x "$_cand" ]; then + _ss=$_cand + break + fi + done + fi + if [ -z "$_ss" ]; then + # Nicht pruefbar ist nicht dasselbe wie "es laeuft etwas": waere das hier ein + # "ja, beschaeftigt", haette ein fehlendes ss den Zeitschalter bis zur + # Obergrenze ausgehebelt - und der Zeitschalter ist angefordert worden, WEIL der + # Nutzer sonst nicht mehr an den Pi kommt. + echo "Warnung: 'ss' nicht gefunden - laufende Auslieferungen konnten nicht geprueft werden" >&2 + return 1 + fi + _conns=$("$_ss" -Htn state established "( sport = :80 )" dst "$AUTOOFF_STATION_NET" 2>/dev/null | grep -c . || true) + [ "${_conns:-0}" -gt 0 ] +} + +# Die Aktion des Timers. Absichtlich ein eigener (im Hilfetext nicht gefuehrter) +# Unterbefehl statt schlicht "off": das Verschieben bei laufender Auslieferung gehoert +# NUR hierher. Wer von Hand "off" tippt, will sofort aus - und nicht, dass ihm das CLI +# das Abschalten zehn Minuten lang verweigert. Ausgeschaltet wird am Ende trotzdem +# genau das, was "off" tut (full_off), nicht ein zweiter, aehnlicher Weg. +autooff_fire() { + if ! _info=$(autooff_read); then + # Niemand hat mehr einen Zeitschalter vorgemerkt (z.B. "hold" oder ein + # zwischenzeitliches "off") - dann hier nichts abschalten, nur den eigenen Timer + # abraeumen. Sonst faellt dieser Lauf in eine Station hinein, die laengst aus + # oder aus einem anderen Grund wieder an ist. + autooff_stop_unit + exit 0 + fi + # Absichtliches Wortsplitting: autooff_read() liefert drei Zahlen, durch Leerzeichen getrennt. + # shellcheck disable=SC2086 + set -- $_info + _req=$1 + _deferrals=$3 + if autooff_busy; then + if [ "$_deferrals" -lt "$AUTOOFF_MAX_DEFERRALS" ]; then + _deferrals=$((_deferrals + 1)) + autooff_write "$_req" "$(( $(date +%s) + AUTOOFF_RETRY_S ))" "$_deferrals" + echo "Zeitschalter verschoben ($_deferrals/$AUTOOFF_MAX_DEFERRALS): eine Auslieferung laeuft noch" + exit 0 + fi + echo "Warnung: $AUTOOFF_MAX_DEFERRALS Verschiebungen erreicht - es steht immer noch eine Verbindung offen, sie haengt vermutlich. Station wird jetzt trotzdem abgeschaltet." >&2 + fi + full_off +} + # Startet (neu) und wartet kurz auf Lebenszeichen, dann prueft es nach, dass der # eben gestartete Prozess wirklich den beabsichtigten Modus/die beabsichtigte # Phase sieht. @@ -213,7 +437,43 @@ PY fi } +# Liest "--for " aus den Argumenten und legt die Dauer in $_for_minutes ab +# (leer = kein Zeitschalter, die Vorgabe). Wird VOR jedem Einschalten aufgerufen, damit +# eine ungueltige Angabe nichts anschaltet. +# $1 = das Kommando fuer die Fehlermeldung, Rest = die Argumente. +parse_for_option() { + _for_minutes="" + _for_seen=0 + shift + while [ $# -gt 0 ]; do + case "$1" in + --for) + shift + _for_minutes=${1:-} + _for_seen=1 + autooff_check_minutes "$_for_minutes" || exit 2 + ;; + *) + usage + exit 2 + ;; + esac + [ $# -gt 0 ] && shift || true + done + [ "$_for_seen" -eq 1 ] || _for_minutes="" +} + +# Legt den angeforderten Zeitschalter an - NUR wenn "--for" dabei war. Ohne die Angabe +# passiert hier nichts: das ist die Vorgabe und der Grund, warum der Pi am Kabel von +# dieser Funktion nie etwas merkt. +autooff_arm_if_requested() { + [ -n "$_for_minutes" ] || return 0 + autooff_arm "$_for_minutes" "$((_for_minutes * 60))" 0 || exit 1 + echo "Zeitschalter: Auto-Aus in $_for_minutes min ('ninlight-station renew' verlaengert, 'hold' bestellt ab)" +} + recovery_on() { + parse_for_option recovery "$@" need_profile "$AP_CONNECTION" n=$(images | wc -l | tr -d ' ') [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2 @@ -229,17 +489,34 @@ recovery_on() { echo "Warnung: PSK ist noch der oeffentliche Default 'ninlight-recovery' - eigenes Passwort in /etc/ninlight-station.conf setzen." >&2 fi echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." + autooff_arm_if_requested } migrate_on() { - # $1 = "--listen" oder leer. + # Argumente: "--listen" und/oder "--for ", beide optional und in beliebiger + # Reihenfolge. Beides wird VOLLSTAENDIG geprueft, bevor irgendein Profil hochkommt. _phase=auto - if [ "${1:-}" = "--listen" ]; then - _phase=listen - elif [ -n "${1:-}" ]; then - usage - exit 2 - fi + _for_minutes="" + _for_seen=0 + while [ $# -gt 0 ]; do + case "$1" in + --listen) + _phase=listen + ;; + --for) + shift + _for_minutes=${1:-} + _for_seen=1 + autooff_check_minutes "$_for_minutes" || exit 2 + ;; + *) + usage + exit 2 + ;; + esac + [ $# -gt 0 ] && shift || true + done + [ "$_for_seen" -eq 1 ] || _for_minutes="" need_profile "$MIGRATE_AP_CONNECTION" n=$(migrate_images | wc -l | tr -d ' ') [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR/migrate enthaelt keine Migrationsimages - die Station antwortet auf alles mit 404" >&2 @@ -255,6 +532,7 @@ migrate_on() { echo "Migration an: SSID $MIGRATE_SSID auf 192.168.8.1, Phase $_phase, $n Migrationsimage(s)" echo "Achtung: das Passwort dieses Zugangspunkts ist aus der alten Firmware oeffentlich bekannt - siehe README.md." >&2 echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." + autooff_arm_if_requested } # Ruft display.py mit einem Schalter auf. EPD_REVISION steht in $CONF und wird von @@ -348,6 +626,12 @@ display_off() { } full_off() { + # ZUERST: ein noch vorgemerkter Zeitschalter muss mit weg, sonst feuert er spaeter in + # eine Station hinein, die laengst aus ist - oder, schlimmer, in eine, die inzwischen + # jemand anders wieder eingeschaltet hat. Ganz vorne, damit auch ein spaeter + # scheiterndes "off" (haengender NetworkManager, siehe ap_switch) ihn nicht + # stehenlaesst. + autooff_clear sudo systemctl stop ninlight-station 2>/dev/null || true # VOR ap_switch, nicht danach: ap_switch "" bricht bei einem haengenden # NetworkManager mit Fehler ab, und unter "set -e" kaeme danach nichts mehr. Genau @@ -420,6 +704,55 @@ PY echo "Freigegeben: $_target" } +# "renew []" - die Uhr zurueckstellen. Ohne Argument auf dieselbe Dauer, die +# zuletzt angefordert wurde (nicht auf die Restzeit und auch nicht auf eine Vorgabe). +# +# Laeuft keiner, ist das ein Fehler und AUSDRUECKLICH kein stillschweigendes Neuanlegen: +# "renew" auf einem Pi am Kabel wuerde sonst genau den Zeitschalter herbeifuehren, den +# der Nutzer nie wollte. +renew_cmd() { + _arg=${1:-} + if ! _info=$(autooff_read); then + echo "Fehler: kein Zeitschalter aktiv - 'renew' stellt nur einen laufenden zurueck. Einen neuen gibt es nur mit 'ninlight-station on --for '." >&2 + exit 1 + fi + # Absichtliches Wortsplitting: autooff_read() liefert drei Zahlen, durch Leerzeichen getrennt. + # shellcheck disable=SC2086 + set -- $_info + _minutes=$1 + if [ -n "$_arg" ]; then + autooff_check_minutes "$_arg" || exit 2 + _minutes=$_arg + fi + autooff_arm "$_minutes" "$((_minutes * 60))" 0 || exit 1 + echo "Zeitschalter zurueckgestellt: Auto-Aus in $_minutes min" +} + +# "hold" - den Zeitschalter abbestellen. Die Station bleibt an, bis jemand "off" sagt. +hold_cmd() { + if ! autooff_read >/dev/null; then + echo "Fehler: kein Zeitschalter aktiv - es gibt nichts abzubestellen." >&2 + exit 1 + fi + autooff_clear + echo "Zeitschalter abbestellt - die Station bleibt an, bis 'ninlight-station off'." +} + +# Restzeit fuer "status", oder leer, wenn kein Zeitschalter laeuft (dann steht in der +# Ausgabe gar nichts davon - eine Zeile "kein Zeitschalter aktiv" kostet nur Platz). +autooff_status_line() { + _info=$(autooff_read) || return 0 + # Absichtliches Wortsplitting: autooff_read() liefert drei Zahlen, durch Leerzeichen getrennt. + # shellcheck disable=SC2086 + set -- $_info + _rest=$(( ($2 - $(date +%s) + 59) / 60 )) + if [ "$_rest" -gt 0 ]; then + printf 'Auto-Aus: in %s min (angefordert: %s min)\n' "$_rest" "$1" + else + printf 'Auto-Aus: steht an (angefordert: %s min)\n' "$1" + fi +} + status_cmd() { # "systemctl is-active" schreibt den Zustand ("inactive", "failed", ...) auf stdout und # liefert dabei einen Rueckgabewert ungleich 0, sobald der Dienst nicht "active" ist - @@ -513,6 +846,7 @@ PY printf 'Phase : %s\n' "$_phase" printf 'Erkannt: %s\n' "$_erkannt" printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || true)" + autooff_status_line printf 'wlan0 : %s\n' "$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2)" printf 'Adresse: %s\n' "$(ip -4 -br addr show wlan0 2>/dev/null | awk '{print $3}')" echo "Images :" @@ -547,8 +881,16 @@ display_cmd() { } usage() { - echo "Aufruf: ninlight-station on|recovery {on|off}|migrate {on [--listen]|off}|arm []|listen|display {test|clear|off}|off|status" >&2 + echo "Aufruf: ninlight-station on [--for ]|recovery {on [--for ]|off}|migrate {on [--listen] [--for ]|off}|arm []|listen|renew []|hold|display {test|clear|off}|off|status" >&2 echo "" >&2 + echo " --for Zeitschalter: die Station schaltet sich nach selbst" >&2 + echo " wieder aus (zurueck ins Heim-WLAN). OPTIONAL - ohne '--for'" >&2 + echo " bleibt sie an, bis jemand 'off' sagt. Nuetzlich, wenn du ueber" >&2 + echo " WLAN angemeldet bist: 'on' macht wlan0 zum Zugangspunkt und" >&2 + echo " reisst die SSH-Sitzung ab. Erlaubt: 1-240." >&2 + echo " renew [] Uhr zurueckstellen (ohne Angabe auf die zuletzt angeforderte" >&2 + echo " Dauer). Laeuft keiner, ist das ein Fehler." >&2 + echo " hold Zeitschalter abbestellen, Station bleibt an." >&2 echo " display test Testbild aufs Panel zeichnen. Das ist der erste Handgriff bei der" >&2 echo " Hardware-Abnahme und die EINZIGE Moeglichkeit, die Panel-Revision" >&2 echo " zu bestimmen: die steht am Geraet nirgends. Bleibt das Bild leer," >&2 @@ -562,32 +904,49 @@ usage() { echo " display off Anzeigedienst stoppen und das Panel leeren (macht 'off' mit)." >&2 } -case "${1:-}" in +_cmd=${1:-} +[ $# -gt 0 ] && shift || true +case "$_cmd" in on) - recovery_on + recovery_on "$@" ;; recovery) - case "${2:-}" in - on) recovery_on ;; + _sub=${1:-} + [ $# -gt 0 ] && shift || true + case "$_sub" in + on) recovery_on "$@" ;; off) full_off ;; *) usage; exit 2 ;; esac ;; migrate) - case "${2:-}" in - on) migrate_on "${3:-}" ;; + _sub=${1:-} + [ $# -gt 0 ] && shift || true + case "$_sub" in + on) migrate_on "$@" ;; off) full_off ;; *) usage; exit 2 ;; esac ;; arm) - arm_cmd "${2:-}" + arm_cmd "${1:-}" ;; listen) listen_cmd ;; + renew) + renew_cmd "${1:-}" + ;; + hold) + hold_cmd + ;; + __autooff) + # Nicht im Hilfetext: das ist die Aktion des Zeitschalters, kein Handgriff fuer + # Menschen (siehe autooff_fire). + autooff_fire + ;; display) - display_cmd "${2:-}" + display_cmd "${1:-}" ;; off) full_off diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index fbd298d..f504772 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -172,7 +172,29 @@ echo "STUB sudo \$*" >>"$log" exec "\$@" EOF - chmod +x "$bindir"/nmcli "$bindir"/systemctl "$bindir"/sudo + # Der Zeitschalter legt seinen Timer per "systemd-run --unit=..." an; der Stub + # protokolliert nur, was angefordert wurde (der ECHTE Beleg dafuer, dass ein + # Zeitschalter existiert bzw. nicht existiert, ist unten immer die Zustandsdatei + # $RUN_DIR/autooff.json und das Verhalten des Skripts, nicht diese Zeile). + cat >"$bindir/systemd-run" <>"$log" +exit "\${FAKE_SYSTEMD_RUN_EXIT:-0}" +EOF + + # "ss" beantwortet die Frage "laeuft gerade eine Auslieferung?" - mit + # FAKE_SS_BUSY=1 gibt es eine offene Verbindung aus dem Stationsnetz auf Port 80. + cat >"$bindir/ss" <>"$log" +if [ -n "\${FAKE_SS_BUSY:-}" ]; then + echo "ESTAB 0 0 192.168.8.1:80 192.168.8.42:52344" +fi +exit 0 +EOF + + chmod +x "$bindir"/nmcli "$bindir"/systemctl "$bindir"/sudo \ + "$bindir"/systemd-run "$bindir"/ss } # --- Sandbox fuer einen Testfall: eigene conf/RUN_DIR/IMAGE_DIR/nmcli-Zustand --- @@ -871,6 +893,284 @@ echo "$HELP" | grep -q "EPD_REVISION" && pass "Hilfetext nennt EPD_REVISION" || echo "$HELP" | grep -q "ninlight-station.conf" && pass "Hilfetext nennt die Datei, in der die Revision steht" || fail "Hilfetext nennt /etc/ninlight-station.conf nicht: $HELP" echo "$HELP" | grep -qE "V2|V3" && pass "Hilfetext nennt die beiden moeglichen Revisionen" || fail "Hilfetext nennt V2/V3 nicht: $HELP" +echo +echo "########## Zeitschalter (Auto-Aus) ##########" +# Geprueft wird durchgehend das ERGEBNIS: die Zustandsdatei $RUN_DIR/autooff.json (sie +# ist im Skript die einzige Quelle dafuer, ob ein Zeitschalter laeuft), der +# Rueckgabewert, die Ausgabe und der nmcli-/systemd-Zustand - nicht die Interna der +# Stubs. + +autooff_field() { + # $1 = Pfad zu autooff.json, $2 = Feldname + python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get(sys.argv[2]))" "$1" "$2" 2>/dev/null \ + || echo "FEHLT" +} +write_autooff() { + # $1 = sandbox, $2 = minutes, $3 = deadline-Offset in Sekunden ab jetzt, $4 = deferrals + python3 - "$1/run/autooff.json" "$2" "$3" "$4" <<'PY' +import json, sys, time +path, minutes, offset, deferrals = sys.argv[1:5] +with open(path, "w") as fh: + json.dump({"minutes": int(minutes), + "deadline": int(time.time()) + int(offset), + "deferrals": int(deferrals)}, fh) +PY +} + +echo "=== Fall Z1 (die wichtigste Zusage): 'on' OHNE --for legt keinen Zeitschalter an ===" +# Der Regelfall ist der Pi am Kabel - dem darf niemand den Zugangspunkt unter der +# laufenden Arbeit wegschalten, nur weil jemand vorsichtig sein wollte. +SBX="$TMPROOT/tZ1" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" on 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ ! -e "$SBX/run/autooff.json" ] && pass "keine Zeitschalter-Zustandsdatei angelegt" || fail "ohne --for entstand trotzdem ein Zeitschalter: $(cat "$SBX/run/autooff.json")" +grep -q "systemd-run" "$SBX/cmd.log" && fail "ohne --for wurde trotzdem ein Timer angelegt: $(grep systemd-run "$SBX/cmd.log")" || pass "kein systemd-run-Aufruf" +echo "$OUT" | grep -qi "Auto-Aus" && fail "Ausgabe verspricht einen Zeitschalter, den es nicht gibt: $OUT" || pass "Ausgabe erwaehnt keinen Zeitschalter" +OUT=$(run_cli "$SBX" status 2>&1) +echo "$OUT" | grep -qi "Auto-Aus" && fail "'status' zeigt eine Zeitschalter-Zeile, obwohl keiner laeuft: $OUT" || pass "'status' schweigt ueber den Zeitschalter" + +echo +echo "=== Fall Z2: 'on --for 30' legt einen Zeitschalter an ===" +SBX="$TMPROOT/tZ2" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" on --for 30 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ -e "$SBX/run/autooff.json" ] && pass "Zustandsdatei angelegt" || fail "keine autooff.json angelegt: $(cat "$SBX/cmd.log")" +[ "$(autooff_field "$SBX/run/autooff.json" minutes)" = "30" ] && pass "angeforderte Dauer 30 min gemerkt" || fail "minutes falsch: $(cat "$SBX/run/autooff.json")" +[ "$(autooff_field "$SBX/run/autooff.json" deferrals)" = "0" ] && pass "noch keine Verschiebung" || fail "deferrals falsch: $(cat "$SBX/run/autooff.json")" +[ "$(file_mode "$SBX/run/autooff.json")" = "644" ] && pass "autooff.json ist 0644 (display.py muss sie lesen)" || fail "falsche Rechte: $(file_mode "$SBX/run/autooff.json")" +grep -q "systemd-run .*--unit=ninlight-station-autooff" "$SBX/cmd.log" && pass "Timer unter festem Unit-Namen angelegt" || fail "kein systemd-run mit festem Unit-Namen: $(cat "$SBX/cmd.log")" +grep -q -- "--on-active=1800s" "$SBX/cmd.log" && pass "Timer feuert nach 30 min" || fail "falsche Laufzeit: $(grep systemd-run "$SBX/cmd.log")" +grep -q "__autooff" "$SBX/cmd.log" && pass "Timer ruft das CLI mit seiner Abschaltaktion" || fail "Timer ruft nichts Abschaltendes: $(grep systemd-run "$SBX/cmd.log")" +[ "$(cat "$SBX/run/mode")" = "recovery" ] && pass "die Station ist trotzdem ganz normal an" || fail "Modusdatei falsch" +OUT=$(run_cli "$SBX" status 2>&1) +echo "$OUT" | grep -qE "Auto-Aus: in (29|30) min" && pass "'status' zeigt die Restzeit" || fail "'status' zeigt keine brauchbare Restzeit: $OUT" + +echo +echo "=== Fall Z3: ungueltige Minutenangaben schalten GAR NICHTS ein ===" +# Geprueft wird nicht nur der Rueckgabewert: nach einer abgelehnten Angabe darf weder ein +# Zugangspunkt hochgefahren noch der Dienst neu gestartet noch eine Modusdatei +# geschrieben worden sein. +for BAD in "0" "241" "abc" "-5" "1.5" ""; do + SBX="$TMPROOT/tZ3-${BAD:-leer}" + setup_sandbox "$SBX" + # Logdatei anlegen, BEVOR geprueft wird (wie in Fall D7): eine fehlende Datei wuerde + # die "hat nichts eingeschaltet"-Zusagen unten sonst durch ein grep-Versagen erfuellen + # statt durch das Verhalten des Skripts. + : >"$SBX/cmd.log" + set +e + if [ -z "$BAD" ]; then + OUT=$(run_cli "$SBX" on --for 2>"$SBX/stderr.log") # --for ohne Wert + else + OUT=$(run_cli "$SBX" on --for "$BAD" 2>"$SBX/stderr.log") + fi + RC=$? + set -e + ERR=$(cat "$SBX/stderr.log") + [ "$RC" -ne 0 ] && pass "'--for ${BAD:-}' wird abgelehnt (Exit $RC)" || fail "'--for ${BAD:-}' wurde angenommen: $OUT" + echo "$ERR" | grep -qi "fehler" && pass "'--for ${BAD:-}': Fehlermeldung auf stderr" || fail "'--for ${BAD:-}': keine Fehlermeldung: $ERR" + grep -q "nmcli con up" "$SBX/cmd.log" && fail "'--for ${BAD:-}' hat trotzdem einen Zugangspunkt hochgefahren: $(cat "$SBX/cmd.log")" || pass "'--for ${BAD:-}': kein Zugangspunkt hochgefahren" + grep -q "systemctl restart" "$SBX/cmd.log" && fail "'--for ${BAD:-}' hat den Dienst gestartet" || pass "'--for ${BAD:-}': Dienst nicht gestartet" + [ ! -e "$SBX/run/mode" ] && pass "'--for ${BAD:-}': keine Modusdatei geschrieben" || fail "'--for ${BAD:-}' hat eine Modusdatei geschrieben: $(cat "$SBX/run/mode")" + [ ! -e "$SBX/run/autooff.json" ] && pass "'--for ${BAD:-}': kein Zeitschalter angelegt" || fail "'--for ${BAD:-}': Zeitschalter trotz ungueltiger Angabe" +done + +echo +echo "=== Fall Z4: 'migrate on --listen --for 5' - beides zusammen ===" +SBX="$TMPROOT/tZ4" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +set +e +OUT=$(run_cli "$SBX" migrate on --listen --for 5 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ "$(control_phase "$SBX/run/control.json")" = "listen" ] && pass "phase=listen bleibt erhalten" || fail "phase falsch: $(cat "$SBX/run/control.json" 2>&1)" +[ "$(autooff_field "$SBX/run/autooff.json" minutes)" = "5" ] && pass "Zeitschalter ueber 5 min angelegt" || fail "Zeitschalter falsch/fehlt: $(cat "$SBX/run/autooff.json" 2>&1)" +# Umgekehrte Reihenfolge der Schalter muss genauso gehen. +SBX="$TMPROOT/tZ4b" +setup_sandbox "$SBX" +echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +set +e +OUT=$(run_cli "$SBX" migrate on --for 5 --listen 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && [ "$(control_phase "$SBX/run/control.json")" = "listen" ] \ + && pass "Schalterreihenfolge ist egal" || fail "'--for 5 --listen' scheiterte (Exit $RC): $OUT" + +echo +echo "=== Fall Z5 (nicht verhandelbar): der Zeitschalter schaltet NICHT in eine laufende Auslieferung ===" +SBX="$TMPROOT/tZ5" +setup_sandbox "$SBX" +run_cli "$SBX" on --for 30 >/dev/null 2>&1 +: >"$SBX/cmd.log" +set +e +OUT=$(FAKE_SS_BUSY=1 run_cli "$SBX" __autooff 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +grep -q "systemctl stop ninlight-station$" "$SBX/cmd.log" && fail "die Station wurde mitten in der Auslieferung abgeschaltet: $(cat "$SBX/cmd.log")" || pass "die Station bleibt an" +grep -q "nmcli con down" "$SBX/cmd.log" && fail "der Zugangspunkt wurde mitten in der Auslieferung heruntergefahren" || pass "der Zugangspunkt bleibt oben" +[ "$(autooff_field "$SBX/run/autooff.json" deferrals)" = "1" ] && pass "Verschiebung gezaehlt (1)" || fail "deferrals nicht hochgezaehlt: $(cat "$SBX/run/autooff.json" 2>&1)" +[ "$(autooff_field "$SBX/run/autooff.json" minutes)" = "30" ] && pass "die angeforderte Dauer bleibt fuer 'renew' erhalten" || fail "minutes wurde ueberschrieben: $(cat "$SBX/run/autooff.json")" +echo "$OUT" | grep -qi "verschoben" && pass "die Verschiebung wird gemeldet" || fail "keine Meldung ueber die Verschiebung: $OUT" +# Gegenprobe: dieselbe Lage ohne laufende Verbindung schaltet sehr wohl ab. +SBX="$TMPROOT/tZ5b" +setup_sandbox "$SBX" +run_cli "$SBX" on --for 30 >/dev/null 2>&1 +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" __autooff 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Gegenprobe: Exit-Code 0" || fail "Gegenprobe: Exit-Code $RC: $OUT" +grep -q "systemctl stop ninlight-station$" "$SBX/cmd.log" && pass "ohne laufende Auslieferung schaltet der Zeitschalter ab" || fail "der Zeitschalter schaltet gar nicht ab: $(cat "$SBX/cmd.log")" +ACTIVE_AFTER=$(cat "$SBX/nmstate/active") +echo "$ACTIVE_AFTER" | grep -qx "home-wifi" && pass "wlan0 ist zurueck im Heim-WLAN" || fail "nicht zurueck im Heim-WLAN: $ACTIVE_AFTER" +[ ! -e "$SBX/run/autooff.json" ] && pass "der abgelaufene Zeitschalter ist abgeraeumt" || fail "Zustandsdatei blieb liegen" +# Die Pruefung muss die richtige Frage stellen: offene Verbindungen auf Port 80 aus dem +# Stationsnetz. +grep -q "STUB ss .*:80" "$SBX/cmd.log" && pass "geprueft wird Port 80" || fail "die Verbindungspruefung fragt nicht nach Port 80: $(grep 'STUB ss' "$SBX/cmd.log")" +grep -q "STUB ss .*192.168.8.0/24" "$SBX/cmd.log" && pass "geprueft wird das Stationsnetz 192.168.8.0/24" || fail "die Verbindungspruefung fragt nicht nach dem Stationsnetz: $(grep 'STUB ss' "$SBX/cmd.log")" + +echo +echo "=== Fall Z6: die Obergrenze fuers Verschieben greift - eine haengende Verbindung haelt den Zeitschalter nicht ewig auf ===" +SBX="$TMPROOT/tZ6" +setup_sandbox "$SBX" +run_cli "$SBX" on --for 30 >/dev/null 2>&1 +write_autooff "$SBX" 30 60 10 # 10 Verschiebungen sind die Obergrenze +: >"$SBX/cmd.log" +set +e +OUT=$(FAKE_SS_BUSY=1 run_cli "$SBX" __autooff 2>"$SBX/stderr.log") +RC=$? +set -e +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT $ERR" +grep -q "systemctl stop ninlight-station$" "$SBX/cmd.log" && pass "an der Obergrenze wird trotz offener Verbindung abgeschaltet" || fail "die haengende Verbindung hebelt den Zeitschalter dauerhaft aus: $(cat "$SBX/cmd.log")" +echo "$ERR" | grep -qi "warnung" && pass "das Abschalten trotz offener Verbindung wird gemeldet" || fail "keine Warnung: $ERR" +[ ! -e "$SBX/run/autooff.json" ] && pass "Zustandsdatei abgeraeumt" || fail "Zustandsdatei blieb liegen" +# Eine Verschiebung UNTER der Grenze verschiebt dagegen weiter (sonst koennte die +# Grenzpruefung auch einfach immer abschalten). +SBX="$TMPROOT/tZ6b" +setup_sandbox "$SBX" +run_cli "$SBX" on --for 30 >/dev/null 2>&1 +write_autooff "$SBX" 30 60 9 +: >"$SBX/cmd.log" +set +e +FAKE_SS_BUSY=1 run_cli "$SBX" __autooff >/dev/null 2>&1 +set -e +grep -q "systemctl stop ninlight-station$" "$SBX/cmd.log" && fail "eine Verschiebung unter der Grenze hat schon abgeschaltet" || pass "unter der Grenze wird weiter verschoben" +[ "$(autooff_field "$SBX/run/autooff.json" deferrals)" = "10" ] && pass "Zaehler steht jetzt auf der Grenze (10)" || fail "deferrals falsch: $(cat "$SBX/run/autooff.json" 2>&1)" + +echo +echo "=== Fall Z7: 'off' raeumt einen laufenden Zeitschalter mit ab ===" +# Sonst feuert er spaeter in eine Station hinein, die laengst aus ist - oder inzwischen +# wieder an. +SBX="$TMPROOT/tZ7" +setup_sandbox "$SBX" +run_cli "$SBX" on --for 30 >/dev/null 2>&1 +[ -e "$SBX/run/autooff.json" ] || fail "Vorbedingung: kein Zeitschalter angelegt" +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" off 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +[ ! -e "$SBX/run/autooff.json" ] && pass "Zeitschalter-Zustandsdatei ist weg" || fail "'off' liess den Zeitschalter stehen: $(cat "$SBX/run/autooff.json")" +grep -q "systemctl stop ninlight-station-autooff.timer" "$SBX/cmd.log" && pass "der Timer selbst wurde angehalten" || fail "der systemd-Timer wurde nicht angehalten: $(cat "$SBX/cmd.log")" +grep -q "systemctl stop ninlight-station-autooff$" "$SBX/cmd.log" && fail "es wurde die .service statt der .timer gestoppt - das brächte den Zeitschalter-Lauf um sich selbst" || pass "die gleichnamige .service wird nicht gestoppt" + +echo +echo "=== Fall Z8: 'renew'/'hold' ohne laufenden Zeitschalter sind klare Fehler, kein stillschweigendes Neuanlegen ===" +for CMD in renew hold; do + SBX="$TMPROOT/tZ8-$CMD" + setup_sandbox "$SBX" + run_cli "$SBX" on >/dev/null 2>&1 # Station an, aber OHNE --for + : >"$SBX/cmd.log" + set +e + OUT=$(run_cli "$SBX" "$CMD" 2>"$SBX/stderr.log") + RC=$? + set -e + ERR=$(cat "$SBX/stderr.log") + [ "$RC" -ne 0 ] && pass "'$CMD' ohne Zeitschalter: Exit-Code $RC" || fail "'$CMD' meldete Erfolg: $OUT" + echo "$ERR" | grep -qi "kein Zeitschalter" && pass "'$CMD': klare Meldung auf stderr" || fail "'$CMD': unklare Meldung: $ERR" + [ ! -e "$SBX/run/autooff.json" ] && pass "'$CMD' hat keinen Zeitschalter angelegt" || fail "'$CMD' hat stillschweigend einen angelegt: $(cat "$SBX/run/autooff.json")" + grep -q "systemd-run" "$SBX/cmd.log" && fail "'$CMD' hat einen Timer angelegt" || pass "'$CMD': kein systemd-run" +done + +echo +echo "=== Fall Z9: 'renew' stellt die Uhr zurueck - ohne Argument auf die zuletzt angeforderte Dauer ===" +SBX="$TMPROOT/tZ9" +setup_sandbox "$SBX" +run_cli "$SBX" on --for 30 >/dev/null 2>&1 +write_autooff "$SBX" 30 120 4 # Restzeit fast abgelaufen, schon viermal verschoben +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" renew 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +[ "$(autooff_field "$SBX/run/autooff.json" minutes)" = "30" ] && pass "dieselbe Dauer wie zuletzt angefordert (30)" || fail "minutes falsch: $(cat "$SBX/run/autooff.json")" +[ "$(autooff_field "$SBX/run/autooff.json" deferrals)" = "0" ] && pass "Verschiebungszaehler zurueckgesetzt" || fail "deferrals nicht zurueckgesetzt: $(cat "$SBX/run/autooff.json")" +STATUS=$(run_cli "$SBX" status 2>&1) +echo "$STATUS" | grep -qE "Auto-Aus: in (29|30) min" && pass "die Restzeit steht wieder bei 30 min" || fail "Restzeit nicht zurueckgestellt: $STATUS" +grep -q -- "--on-active=1800s" "$SBX/cmd.log" && pass "der Timer wurde neu gestellt" || fail "kein neuer Timer: $(cat "$SBX/cmd.log")" +# Mit Argument gilt das Argument - und ungueltige Angaben werden auch hier abgelehnt. +set +e +OUT=$(run_cli "$SBX" renew 90 2>&1); RC=$? +set -e +[ "$RC" -eq 0 ] && [ "$(autooff_field "$SBX/run/autooff.json" minutes)" = "90" ] && pass "'renew 90' stellt auf 90 min" || fail "'renew 90' scheiterte (Exit $RC): $OUT" +set +e +OUT=$(run_cli "$SBX" renew 999 2>"$SBX/stderr.log"); RC=$? +set -e +[ "$RC" -ne 0 ] && [ "$(autooff_field "$SBX/run/autooff.json" minutes)" = "90" ] && pass "'renew 999' wird abgelehnt, der laufende Zeitschalter bleibt unveraendert" || fail "'renew 999' wurde angenommen (Exit $RC): $(cat "$SBX/run/autooff.json")" + +echo +echo "=== Fall Z10: 'hold' bestellt ab - die Station bleibt an ===" +SBX="$TMPROOT/tZ10" +setup_sandbox "$SBX" +run_cli "$SBX" on --for 30 >/dev/null 2>&1 +: >"$SBX/cmd.log" +set +e +OUT=$(run_cli "$SBX" hold 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +[ ! -e "$SBX/run/autooff.json" ] && pass "Zeitschalter abbestellt" || fail "Zeitschalter laeuft weiter: $(cat "$SBX/run/autooff.json")" +grep -q "systemctl stop ninlight-station-autooff.timer" "$SBX/cmd.log" && pass "der Timer wurde angehalten" || fail "Timer nicht angehalten: $(cat "$SBX/cmd.log")" +grep -q "systemctl stop ninlight-station$" "$SBX/cmd.log" && fail "'hold' hat die Station abgeschaltet" || pass "die Station bleibt an" +ACTIVE_AFTER=$(cat "$SBX/nmstate/active") +[ "$ACTIVE_AFTER" = "ninlight-recovery" ] && pass "der Zugangspunkt laeuft unveraendert weiter" || fail "Zugangspunkt-Zustand veraendert: $ACTIVE_AFTER" +# Und ein danach doch noch feuernder Timer-Lauf schaltet nichts ab. +: >"$SBX/cmd.log" +set +e +run_cli "$SBX" __autooff >/dev/null 2>&1 +set -e +grep -q "systemctl stop ninlight-station$" "$SBX/cmd.log" && fail "ein abbestellter Zeitschalter hat trotzdem abgeschaltet: $(cat "$SBX/cmd.log")" || pass "ein abbestellter Zeitschalter schaltet nichts mehr ab" + +echo +echo "=== Fall Z11: scheitert systemd-run, bleibt das nicht unbemerkt ===" +# Sonst glaubte der Nutzer an einen Zeitschalter, den es nie gab - und genau darauf +# hat er sich verlassen, als er die SSH-Sitzung ueber WLAN aufgab. +SBX="$TMPROOT/tZ11" +setup_sandbox "$SBX" +set +e +OUT=$(FAKE_SYSTEMD_RUN_EXIT=1 run_cli "$SBX" on --for 30 2>"$SBX/stderr.log") +RC=$? +set -e +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -ne 0 ] && pass "Exit-Code != 0" || fail "Exit-Code 0, obwohl der Timer nicht angelegt werden konnte: $OUT" +echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung auf stderr" || fail "keine Fehlermeldung: $ERR" +[ ! -e "$SBX/run/autooff.json" ] && pass "keine Zustandsdatei, die einen Zeitschalter vortaeuscht" || fail "autooff.json trotz gescheitertem Timer: $(cat "$SBX/run/autooff.json")" +echo "$ERR" | grep -q "off" && pass "die Meldung sagt, dass die Station AN bleibt" || fail "die Meldung verschweigt den Zustand der Station: $ERR" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 425d49c..57951da 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -1,5 +1,6 @@ import json import sys +import time from pathlib import Path import pytest @@ -731,6 +732,121 @@ def test_armed_phase_label_is_visible(): assert "g95_cw" in text +# --- Zeitschalter am Panel (Task 7e) ------------------------------------------------ +# +# Ein Zeitschalter, den man am Panel nicht sieht, ist schlimmer als keiner: man glaubt, +# die Station laeuft, und sie ist laengst aus. Umgekehrt darf ohne Zeitschalter (der +# Regelfall - er entsteht nur mit "--for") keine Zeile dafuer erscheinen. + +def test_autooff_line_appears_only_with_a_running_timer(): + without = render.lines(_migration_state()) + assert not any("Auto-Aus" in line for line in without), without + + state = dict(_migration_state(), autooff_minutes=7) + with_timer = render.lines(state) + assert "Auto-Aus in 7 min" in with_timer, with_timer + + +def test_autooff_line_also_appears_in_recovery_mode(): + """Der Zeitschalter haengt nicht am Migrationsmodus - im Rettungsmodus reisst "on" + die SSH-Sitzung genauso ab.""" + lines = render.lines({"mode": "recovery", "ssid": "ninlight-recovery", "served": 0, + "recent": [], "autooff_minutes": 42}) + assert "Auto-Aus in 42 min" in lines, lines + + +def test_autooff_line_is_actually_drawn_on_the_panel(): + """Nicht nur in lines(): die Zeile muss im vollsten Zustand auch tatsaechlich aufs + Panel kommen und nicht unten herausfallen (compose() bricht Beiwerkzeilen vor dem + unteren Rand ab). Geprueft per Pixelvergleich gegen eine unabhaengig gezeichnete + Referenzzeile - schriftunabhaengig und ohne behauptete Pixelbreiten.""" + state = dict(_armed_state(), autooff_minutes=7) + expected = "Auto-Aus in 7 min" + # Die wievielte BEIWERKZEILE ist es? (Kopfbalken, Phase im Kopf und grosse Zeile + # sind eigene Rollen und belegen keine Beiwerkzeile.) + details = [text for text, role in render.blocks(state) if role == render.ROLE_DETAIL] + assert expected in details, details + index = details.index(expected) + + top, bottom = _detail_band(index) + drawn = _band_pixels(render.compose(state), top, bottom) + reference = _band_pixels(_reference_detail_line(expected, top), top, bottom) + + assert drawn == reference, ( + "die Zeitschalter-Zeile steht nicht (ungekuerzt) auf dem Panel - der Bediener " + "sieht nicht, dass sich die Station gleich abschaltet") + + +def test_autooff_line_says_so_when_the_deadline_has_arrived(): + lines = render.lines({"mode": "recovery", "ssid": "x", "served": 0, "recent": [], + "autooff_minutes": 0}) + assert "Auto-Aus steht an" in lines, lines + + +def test_build_state_reads_the_autooff_file(tmp_path, monkeypatch): + monkeypatch.setattr(display, "_service_active", lambda service_name: True) + (tmp_path / "mode").write_text("recovery") + conf_path = tmp_path / "station.conf" + conf_path.write_text("SSID='ninlight-recovery'\n") + # Noch 6 min 1 s - aufgerundet 7, wie es "ninlight-station status" auch rechnet. + (tmp_path / "autooff.json").write_text(json.dumps( + {"minutes": 30, "deadline": time.time() + 361, "deferrals": 0})) + + state = display.build_state(run_dir=tmp_path, conf_path=conf_path, service_name="x") + + assert state["autooff_minutes"] == 7 + assert "Auto-Aus in 7 min" in render.lines(state) + + +def test_build_state_without_autooff_file_has_no_timer_field(tmp_path, monkeypatch): + monkeypatch.setattr(display, "_service_active", lambda service_name: True) + (tmp_path / "mode").write_text("recovery") + conf_path = tmp_path / "station.conf" + conf_path.write_text("SSID='ninlight-recovery'\n") + + state = display.build_state(run_dir=tmp_path, conf_path=conf_path, service_name="x") + + assert "autooff_minutes" not in state + assert not any("Auto-Aus" in line for line in render.lines(state)) + + +def test_broken_autooff_file_is_treated_as_no_timer(tmp_path): + """Dieselbe Zusage wie fuer status.json/control.json: halb geschrieben, kaputt oder + aus einer aelteren Fassung darf keine Ausnahme werfen - und vor allem keine + erfundene Restzeit zeigen.""" + (tmp_path / "autooff.json").write_text("{kein json") + assert display._read_autooff_minutes(tmp_path) is None + + (tmp_path / "autooff.json").write_text(json.dumps({"minutes": 30})) + assert display._read_autooff_minutes(tmp_path) is None + + (tmp_path / "autooff.json").write_text(json.dumps({"deadline": "gleich"})) + assert display._read_autooff_minutes(tmp_path) is None + + +def test_autooff_remaining_time_rounds_up(tmp_path): + (tmp_path / "autooff.json").write_text(json.dumps({"deadline": 1000.0})) + assert display._read_autooff_minutes(tmp_path, now=1000.0 - 600) == 10 + assert display._read_autooff_minutes(tmp_path, now=1000.0 - 601) == 11 + assert display._read_autooff_minutes(tmp_path, now=1000.0 - 1) == 1 + assert display._read_autooff_minutes(tmp_path, now=1000.0) == 0 + assert display._read_autooff_minutes(tmp_path, now=1000.0 + 90) == 0 + + +def test_a_switched_off_station_shows_no_timer(tmp_path, monkeypatch): + """"off" raeumt die Zustandsdatei ab - aber selbst wenn sie durch einen Absturz + liegen bliebe, darf neben "Station aus" keine Restzeit stehen.""" + monkeypatch.setattr(display, "_service_active", lambda service_name: False) + (tmp_path / "autooff.json").write_text(json.dumps( + {"minutes": 30, "deadline": time.time() + 600, "deferrals": 0})) + + state = display.build_state(run_dir=tmp_path, conf_path=tmp_path / "station.conf", + service_name="x") + + assert state == {"mode": "off"} + assert not any("Auto-Aus" in line for line in render.lines(state)) + + # --- display.build_state() (Fixrunde 1, I3) --------------------------------------- # # Dieselbe Unterscheidung, die im CLI (tools/station/ninlight-station, status_cmd) zweimal From 5f3cf25e41e36911a3126543840f84312ce27e65 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 20:53:01 +0200 Subject: [PATCH 133/150] docs: Abnahme der Migrationsstation an einer echten GU10 RGB --- docs/hardware-checklist.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 211534f..a968553 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -96,3 +96,36 @@ sieht pro Boot einen vollen Transfer. - [x] G95 (Dev-Build, Product-ID `-klyqa.lighting.cw-ww.g95-dev`) mit dem **Entwicklungs**schlüssel signiert → angenommen, geflasht, läuft ninlight als `g95_cw` - [x] GU10 (Produktionsbuild, Product-ID `-klyqa.lighting.rgb-cw-ww.gu10`) mit dem **Produktions**schlüssel signiert → angenommen, geflasht, läuft ninlight als `gu10_rgb` - [x] Migrierte Lampe lässt sich anschließend über das normale ninlight-OTA aktualisieren (GU10 auf 0.3.8) + +## Migrationsstation (Plan 4c) — Abnahme 2026-09-14 + +Station: Raspberry Pi Zero W Rev 1.1, Raspberry Pi OS Bookworm 12 (Python 3.11.2, Pillow 9.4.0, +armv6), Waveshare 2,13" E-Paper **Revision V3** als HAT, ein Taster auf GPIO23 (noch ohne Funktion). + +- [x] `setup_pi.sh` richtet einen **nackten** Pi vollständig ein — Pakete, SPI, beide + NetworkManager-Profile, Server, CLI, Anzeigedienst. Zweimal von Grund auf durchgelaufen. +- [x] SPI wird automatisch eingeschaltet, der Neustart-Hinweis erscheint nur bei echter Änderung. +- [x] `deploy.sh` liefert 8 OTA-Images und 16 signierte Migrationsimages aus. +- [x] Panel-Revision am Testbild bestimmt: **V3**. +- [x] Horchphase: GU10 RGB meldet sich, wird erkannt, bekommt **404** — es floss kein Byte. + Protokoll: `/qcx_firmware_-klyqa.lighting.rgb-cw-ww.gu10_V1.1.1.103_ota_light.img 404 0B` + Daraus abgeleitet: Ziel `gu10_rgb`, Variante **prod** (kein `-dev`), Lampenversion V1.1.1.103. +- [x] Erkennung am Panel sichtbar, vom Bediener bestätigt. +- [x] Zeitschalter holt die Station selbsttätig ins Heim-WLAN zurück (3 und 15 Minuten geprüft). +- [x] `arm gu10_rgb` freigegeben, Lampe migriert, startet neu, läuft mit `0.3.8-24-geaaef`, + per mDNS auffindbar und über REST steuerbar. `last_ota_error: null`. +- [x] Migrierte Lampe meldet `color: true`, `color_temp: [2700, 6500]`, `transition_default: 300`, + `wake_on_change: false`. +- [ ] Ausschaltfade ohne Farbrest an der frisch migrierten Lampe — steht aus. +- [ ] **Nicht geprüft und weiterhin offen: E27 RGB, E14 (RGB und CW), GU10 CW.** Von diesen + Varianten lag kein Exemplar vor. Ihre Pinbelegung stammt aus der alten Firmware und war + nie an echter Hardware verifiziert. + +**Befund während der Abnahme, noch offen:** `ninlight-station off` stoppt den Anzeigedienst, +aber kein `on`-Pfad startet ihn wieder — nach dem ersten `off` (das auch der Zeitschalter +auslöst) bleibt das Panel bis zum Neustart des Pi dunkel. Reparatur läuft. + +**Hardware-Lehre:** Vier Kernel-Paniken an einem Tag, alle mit derselben Fehleradresse und +unabhängig von Karte und Konfiguration, kamen von einem Mehrfach-Netzteil, das sein Budget +dynamisch neu verhandelt. Mit einem eigenen Netzteil: `throttled=0x0` auch unter Schreiblast. +Die zerschossene erste SD-Karte war die Folge, nicht die Ursache. From d1535433ced29eca8aaea722bc0f1f685db7dfea Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 21:09:50 +0200 Subject: [PATCH 134/150] fix(station): Anzeigedienst bleibt ueber on/off hinweg durchgehend an 'off' stoppte bisher den Anzeigedienst und leerte das Panel, aber kein 'on'-Pfad hat ihn je wieder gestartet - ab dem zweiten Ein/Aus blieb das Panel bis zum naechsten Pi-Neustart dunkel. Die Begruendung dafuer trug nicht: der Anzeigedienst erkennt den ausgeschalteten Zustand selbst (systemctl is-active ninlight-station) und zeichnet von sich aus "Station aus". 'off' fasst den Anzeigedienst jetzt nicht mehr an; er laeuft durchgehend (wie seit dem Booten ohnehin) und zeigt den jeweils aktuellen Zustand. display_off() bleibt fuer den expliziten Diagnosebefehl 'ninlight-station display off' erhalten. Enthaelt zusaetzlich die schon im Arbeitsbaum liegende PHASE_LABELS-Korrektur in render.py (HORCHT -> LISTEN). --- tools/station/display/render.py | 4 +- tools/station/ninlight-station | 55 +++++++++++++------ tools/station/tests/test_cli.sh | 97 ++++++++++++++++++--------------- 3 files changed, 93 insertions(+), 63 deletions(-) diff --git a/tools/station/display/render.py b/tools/station/display/render.py index c3c4489..fb9bc84 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -50,7 +50,7 @@ TEXT_MARGIN_PX = 2 # Panelbreite. Invertiert kostet auf e-Paper nichts (bistabil, kein # Stromverbrauch pro Pixel) und trennt die Kopfzeile auf einen Blick # vom Rest. -# header_phase - die Phase (AUTO/HORCHT/FREIGEGEBEN), NUR im Migrationsmodus und +# header_phase - die Phase (AUTO/LISTEN/FREIGEGEBEN), NUR im Migrationsmodus und # nur, wenn eine Phase bekannt ist. Steht in derselben Kopfzeile wie # header, rechtsbuendig, in kleinerer Schrift (zweite Hardware-Abnahme: # die Phase ging als eigene kleine Zeile unter dem Lampennamen unter). @@ -153,7 +153,7 @@ FONT_SEARCH_PATHS = ( MODE_LABELS = {"recovery": "RETTUNG", "migration": "MIGRATION"} # "armed" wird gesondert behandelt (es zieht zusaetzlich eine Beiwerkzeile mit dem # Zielnamen nach sich), deshalb hier nicht gelistet. -PHASE_LABELS = {"auto": "AUTO", "listen": "HORCHT"} +PHASE_LABELS = {"auto": "AUTO", "listen": "LISTEN"} # Dritte Hardware-Abnahme, Befund I2: in der Kopfzeile stand frueher # "FREIGEGEBEN: " - der Platz neben dem Modus reicht dafuer nicht (mit DejaVu diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index a10304b..5d1f805 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -608,16 +608,36 @@ display_handover() { return "$_rc" } -# Nimmt die Anzeige mit aus. Reihenfolge: erst den Dienst stoppen, dann das Panel leeren - -# umgekehrt zeichnet der noch laufende Daemon binnen 5 s sein altes Bild wieder darauf. +# Stoppt den Anzeigedienst dauerhaft und leert danach das Panel. Reihenfolge: erst der +# Dienst, dann das Leeren - umgekehrt zeichnet der noch laufende Daemon binnen 5 s sein +# altes Bild wieder darauf. # -# Warum leeren und nicht "Station aus" stehen lassen: der Anzeigedienst erkennt den -# ausgeschalteten Zustand zwar selbst (systemctl is-active), aber ein laufender -# Anzeigedienst nach "off" bedeutet, dass "off" eben NICHT alles abgeschaltet hat. Ein -# leeres Panel behauptet dagegen gar nichts - und das ist bei einer Station, die Lampen -# ungefragt Firmware unterschieben kann, die einzig ungefaehrliche Richtung: ein -# stehengebliebenes "MIGRATION"-Bild wuerde einen Betriebszustand behaupten, den es nicht -# mehr gibt, waehrend ein leeres Panel hoechstens nach "aus" aussieht - was es ist. +# Frueher hing diese Funktion auch in full_off() (also in jedem "off") und stuetzte das +# auf die Annahme, ein weiterlaufender Anzeigedienst wuerde nach "off" ein stehen- +# gebliebenes "MIGRATION"-Bild zeigen, das einen Betriebszustand behauptet, den es nicht +# mehr gibt - ein leeres Panel sei da die einzig ungefaehrliche Richtung. Die Praemisse +# stimmt nicht: der Anzeigedienst erkennt den ausgeschalteten Zustand selbst +# (_service_active() in display.py fragt "systemctl is-active ninlight-station") und +# zeichnet dann von sich aus "Station aus" - am Geraet so bestaetigt. Es gab also nie ein +# stehenbleibendes Bild, gegen das zu schuetzen waere. Der tatsaechliche Effekt war ein +# anderer, ungewollter: der Dienst blieb nach dem ersten "off" gestoppt, und KEIN "on"-Pfad +# (recovery_on/migrate_on/die Kurzform "on") hat ihn je wieder gestartet - ab dem zweiten +# Durchgang blieb das Panel bis zum naechsten Neustart des Pi dunkel, obwohl die Station +# wieder lief. +# +# Jetzt laesst full_off() den Anzeigedienst laufen (er startet ohnehin beim Booten und +# zeichnet den jeweils aktuellen Zustand selbst) - symmetrisch, ohne dass "on" ihn eigens +# wiederanfahren muesste, und ohne dass "off" ihn extra stoppen muesste. Diese Funktion +# bleibt nur fuer den EXPLIZITEN Diagnosebefehl 'ninlight-station display off' erhalten, +# mit dem man den Dienst bewusst und dauerhaft stilllegen kann (z.B. um das Panel zu +# schonen) - dafuer braucht es weiterhin Stop+Leeren in dieser Reihenfolge. +# +# Preis der Entscheidung, den Dienst durchlaufen zu lassen: er ruft alle +# POLL_INTERVAL_S=5s (display.py) 'systemctl is-active' auf - dauerhaft, auch waehrend die +# Station aus ist, nicht nur waehrend sie an ist wie bisher. Das ist ein einzelner lokaler +# D-Bus-Aufruf ohne Netzwerk; auf einem Pi Zero W (ARMv6, ein Kern) kostet ein Fork+Exec +# davon alle 5 s spuerbar, aber nicht ernsthaft etwas - derselbe Takt laeuft schon waehrend +# jedes "an"-Zustands, und ein ansonsten untaetiger Kern hat davon reichlich Luft. Vertretbar. display_off() { sudo systemctl stop ninlight-display 2>/dev/null || true # Kein Abbruch, wenn das Panel fehlt: display.py beendet sich dann selbst mit 0, und @@ -633,14 +653,11 @@ full_off() { # stehenlaesst. autooff_clear sudo systemctl stop ninlight-station 2>/dev/null || true - # VOR ap_switch, nicht danach: ap_switch "" bricht bei einem haengenden - # NetworkManager mit Fehler ab, und unter "set -e" kaeme danach nichts mehr. Genau - # dann - Zugangspunkt laesst sich nicht abschalten - waere die Anzeige sonst der - # letzte Rest von "off", der nie ausgefuehrt wird: Daemon laeuft weiter, Panel zeigt - # unveraendert "MIGRATION". Ein leeres Panel behauptet dagegen nichts; die Wahrheit - # ueber den noch offenen Zugangspunkt steht in ap_switch's Fehlermeldung, wo man sie - # liest, statt auf einem Panel, das man auch fuer aktuell halten koennte. - display_off + # Am Anzeigedienst wird hier bewusst NICHTS getan (siehe display_off()'s Kommentar): + # er laeuft weiter und zeichnet "Station aus" von selbst, sobald sein naechster Poll + # (spaetestens 5 s) "systemctl is-active ninlight-station" als inaktiv sieht. Das + # macht "off" unabhaengig vom Anzeigedienst - ob der installiert, aktiv oder gerade + # gescheitert ist, hat auf das Abschalten der Station keinen Einfluss. ap_switch "" if nmcli -t -f NAME con show | grep -Fqx "$HOME_CONNECTION"; then if sudo nmcli con up "$HOME_CONNECTION" >/dev/null; then @@ -901,7 +918,9 @@ usage() { echo " Ein laufender Anzeigedienst wird dafuer kurz gestoppt und" >&2 echo " danach wieder gestartet (er haelt sonst die GPIO-Leitungen)." >&2 echo " display clear Panel leeren (ebenfalls mit kurzer Unterbrechung des Dienstes)." >&2 - echo " display off Anzeigedienst stoppen und das Panel leeren (macht 'off' mit)." >&2 + echo " display off Anzeigedienst dauerhaft stoppen und das Panel leeren. 'on'/'off'" >&2 + echo " der Station brauchen das nicht - der Anzeigedienst laeuft" >&2 + echo " unabhaengig davon durch und zeichnet den Zustand selbst." >&2 } _cmd=${1:-} diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index f504772..e3a362a 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -140,6 +140,16 @@ case "\$1" in exit "\$code" fi ;; + start) + # Testschalter fuer die Resilienz-Faelle: bildet nach, dass "ninlight-display" + # gar nicht installiert oder absichtlich maskiert ist (systemctl gibt dann einen + # Fehler zurueck statt still zu tun, als waere alles gut). + if [ "\$2" = "ninlight-display" ] && [ -n "\${FAKE_DISPLAY_SERVICE_MISSING:-}" ]; then + echo "Failed to start ninlight-display.service: Unit not found." >&2 + exit 5 + fi + exit 0 + ;; restart|stop) # Bildet systemds RuntimeDirectory-Verhalten nach (Fixrunde 1, C1): ohne # RuntimeDirectoryPreserve=restart in der Unit leert systemd das @@ -157,6 +167,10 @@ case "\$1" in rm -rf "\$RUN_DIR" mkdir -p "\$RUN_DIR" fi + if [ "\$2" = "ninlight-display" ] && [ -n "\${FAKE_DISPLAY_SERVICE_MISSING:-}" ]; then + echo "Failed to stop ninlight-display.service: Unit not found." >&2 + exit 5 + fi if [ "\$1" = "restart" ]; then exit "\${FAKE_SYSTEMCTL_RESTART_EXIT:-0}" fi @@ -795,10 +809,14 @@ else fi echo -echo "=== Fall D4 (Task 6b, Punkt 4): 'off' nimmt die Anzeige mit - Dienst gestoppt, Panel geleert ===" -# Ohne das behauptet das stehengebliebene Bild einen Betriebszustand ("MIGRATION"), -# den es nicht mehr gibt - bei einer Station, die Lampen ungefragt Firmware -# unterschieben kann, die gefaehrliche Richtung. +echo "=== Fall D4 (Befund Task 7f): 'off' fasst den Anzeigedienst nicht mehr an ===" +# Frueher stoppte 'off' den Anzeigedienst und leerte das Panel - mit der Begruendung, +# ein stehengebliebenes "MIGRATION"-Bild wuerde einen Betriebszustand behaupten, den es +# nicht mehr gibt. Die Begruendung trug nicht: der Anzeigedienst erkennt den +# ausgeschalteten Zustand selbst (systemctl is-active ninlight-station) und zeichnet dann +# "Station aus". Das eigentliche Ergebnis des Stoppens war ein anderes: kein "on"-Pfad hat +# den Dienst je wieder gestartet, und das Panel blieb ab dem zweiten Durchgang bis zum +# naechsten Pi-Neustart dunkel. Jetzt laesst 'off' den Dienst laufen. SBX="$TMPROOT/tD4" setup_sandbox "$SBX" echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" @@ -809,57 +827,50 @@ OUT=$(run_cli "$SBX" off 2>&1) RC=$? set -e [ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" -grep -Fq "STUB systemctl stop ninlight-display" "$SBX/cmd.log" && pass "'off' stoppt den Anzeigedienst" || fail "'off' stoppt den Anzeigedienst nicht: $(cat "$SBX/cmd.log")" -grep -Fq "STUB display.py --clear" "$SBX/cmd.log" && pass "'off' leert das Panel" || fail "'off' leert das Panel nicht: $(cat "$SBX/cmd.log")" -STOP_LINE=$(grep -n "STUB systemctl stop ninlight-display" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) -CLEAR_LINE=$(grep -n "STUB display.py --clear" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) -if [ -n "$STOP_LINE" ] && [ -n "$CLEAR_LINE" ] && [ "$STOP_LINE" -lt "$CLEAR_LINE" ]; then - pass "auch bei 'off': erst stoppen, dann leeren" -else - fail "Reihenfolge bei 'off' falsch (stop=$STOP_LINE, clear=$CLEAR_LINE): $(cat "$SBX/cmd.log")" -fi -# Das Panel muss geleert sein, BEVOR ap_switch das Ergebnis von "off" verkuenden kann - -# und vor allem, bevor ap_switch bei haengendem NetworkManager abbricht (Fall D6). -UP_LINE=$(grep -n "nmcli con up home-wifi" "$SBX/cmd.log" | head -n1 | cut -d: -f1 || true) -if [ -n "$UP_LINE" ] && [ "$CLEAR_LINE" -lt "$UP_LINE" ]; then - pass "Panel ist geleert, bevor wlan0 ins Heim-WLAN zurueckgeschaltet wird" -else - fail "Panel wurde erst nach dem Zurueckschalten geleert (clear=$CLEAR_LINE, up=$UP_LINE)" -fi +grep -Fq "STUB systemctl stop ninlight-display" "$SBX/cmd.log" && fail "'off' stoppt den Anzeigedienst weiterhin: $(cat "$SBX/cmd.log")" || pass "'off' stoppt den Anzeigedienst nicht mehr" +grep -Fq "STUB display.py --clear" "$SBX/cmd.log" && fail "'off' leert weiterhin das Panel: $(cat "$SBX/cmd.log")" || pass "'off' leert das Panel nicht mehr (der Dienst zeichnet 'Station aus' selbst)" echo -echo "=== Fall D5: ein Fehler beim Leeren des Panels haelt 'off' nicht auf ===" -# Ein fehlendes/kaputtes Panel darf das Ausschalten der Station nie blockieren - -# sonst bliebe ausgerechnet der Zugangspunkt an, weil die Anzeige klemmt. -SBX="$TMPROOT/tD5" +echo "=== Fall D4b (die Zusage aus Task 7f): 'off' und ein erneutes 'on' lassen den Anzeigedienst durchgehend an ===" +# Genau das ist die Zusage: nach 'off' und einem erneuten 'on' ist die Anzeige wieder in +# Betrieb - und zwar OHNE dass irgendwer den Dienst zwischendurch neu gestartet haben +# muesste, weil er nie gestoppt wurde. Ein/Aus/Ein durchspielen und ueber die gesamte +# Sequenz pruefen, dass 'ninlight-display' kein einziges Mal gestoppt wird. +SBX="$TMPROOT/tD4b" setup_sandbox "$SBX" : >"$SBX/cmd.log" set +e -OUT=$(FAKE_DISPLAY_EXIT=1 run_cli "$SBX" off 2>"$SBX/stderr.log") -RC=$? +run_cli "$SBX" on >/dev/null 2>&1 +RC1=$? +run_cli "$SBX" off >/dev/null 2>&1 +RC2=$? +run_cli "$SBX" on >/dev/null 2>&1 +RC3=$? set -e -ERR=$(cat "$SBX/stderr.log") -[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlgeschlagenem Leeren" || fail "Exit-Code $RC, stderr: $ERR" -echo "$ERR" | grep -q "Warnung" && pass "Warnung ueber das nicht geleerte Panel steht auf stderr" || fail "keine Warnung auf stderr: $ERR" -grep -Fq "nmcli con up home-wifi" "$SBX/cmd.log" && pass "'off' hat trotzdem bis zum Zurueckschalten ins Heim-WLAN durchgezogen" || fail "'off' ist am Anzeigefehler haengengeblieben: $(cat "$SBX/cmd.log")" +[ "$RC1" -eq 0 ] && [ "$RC2" -eq 0 ] && [ "$RC3" -eq 0 ] \ + && pass "on/off/on liefern alle Exit-Code 0" \ + || fail "on/off/on: Exit-Codes $RC1/$RC2/$RC3, Log: $(cat "$SBX/cmd.log")" +grep -Fq "STUB systemctl stop ninlight-display" "$SBX/cmd.log" \ + && fail "der Anzeigedienst wurde waehrend on/off/on gestoppt - nach 'off' bliebe das Panel dunkel: $(cat "$SBX/cmd.log")" \ + || pass "der Anzeigedienst blieb ueber on/off/on hinweg durchgehend an" echo -echo "=== Fall D6: haengender NetworkManager - das Panel wird trotzdem noch geleert ===" -# ap_switch "" bricht hier mit Fehler ab; unter "set -e" laeuft danach nichts mehr. -# Genau deshalb steht display_off VOR ap_switch: sonst bliebe ein "MIGRATION"-Bild -# stehen, waehrend der Zugangspunkt noch sendet und niemand mehr etwas abschaltet. -SBX="$TMPROOT/tD6" +echo "=== Fall D9: ein fehlender/scheiternder Anzeigedienst haelt weder 'on' noch 'off' auf ===" +# FAKE_DISPLAY_SERVICE_MISSING bildet nach, dass 'ninlight-display' gar nicht installiert +# oder absichtlich deaktiviert ist (systemctl meldet dann einen Fehler statt still +# mitzuspielen). Das darf 'on' und 'off' nicht scheitern lassen - eine tragende Zusage +# dieses Projekts. +SBX="$TMPROOT/tD9" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" -run_cli "$SBX" migrate on >/dev/null 2>&1 : >"$SBX/cmd.log" set +e -OUT=$(FAKE_NMCLI_STUCK=1 run_cli "$SBX" off 2>"$SBX/stderr.log") -RC=$? +OUT_ON=$(FAKE_DISPLAY_SERVICE_MISSING=1 FAKE_DISPLAY_SERVICE_ACTIVE_EXIT=4 run_cli "$SBX" on 2>&1) +RC_ON=$? +OUT_OFF=$(FAKE_DISPLAY_SERVICE_MISSING=1 FAKE_DISPLAY_SERVICE_ACTIVE_EXIT=4 run_cli "$SBX" off 2>&1) +RC_OFF=$? set -e -ERR=$(cat "$SBX/stderr.log") -[ "$RC" -ne 0 ] && pass "Exit-Code != 0 (der Zugangspunkt liess sich nicht abschalten)" || fail "Exit-Code 0, obwohl der Zugangspunkt noch aktiv ist: $OUT" -grep -Fq "STUB display.py --clear" "$SBX/cmd.log" && pass "Panel wurde trotz haengendem NetworkManager geleert" || fail "Panel blieb stehen, waehrend der Zugangspunkt noch sendet: $(cat "$SBX/cmd.log")" +[ "$RC_ON" -eq 0 ] && pass "'on' gelingt trotz fehlendem Anzeigedienst" || fail "'on' scheitert am fehlenden Anzeigedienst, Exit $RC_ON: $OUT_ON" +[ "$RC_OFF" -eq 0 ] && pass "'off' gelingt trotz fehlendem Anzeigedienst" || fail "'off' scheitert am fehlenden Anzeigedienst, Exit $RC_OFF: $OUT_OFF" echo echo "=== Fall D7: 'display' ohne/mit unbekanntem Unterbefehl -> Hilfetext, Exit 2 ===" From f0f6894bc96cd646d343cc9aa2982806d83f6484 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 21:10:46 +0200 Subject: [PATCH 135/150] docs: Ausschaltfade an der migrierten GU10 bestaetigt --- docs/hardware-checklist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index a968553..34d4afb 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -116,7 +116,7 @@ armv6), Waveshare 2,13" E-Paper **Revision V3** als HAT, ein Taster auf GPIO23 ( per mDNS auffindbar und über REST steuerbar. `last_ota_error: null`. - [x] Migrierte Lampe meldet `color: true`, `color_temp: [2700, 6500]`, `transition_default: 300`, `wake_on_change: false`. -- [ ] Ausschaltfade ohne Farbrest an der frisch migrierten Lampe — steht aus. +- [x] Ausschaltfade ohne Farbrest an der frisch migrierten Lampe — 2026-09-14 bestätigt. - [ ] **Nicht geprüft und weiterhin offen: E27 RGB, E14 (RGB und CW), GU10 CW.** Von diesen Varianten lag kein Exemplar vor. Ihre Pinbelegung stammt aus der alten Firmware und war nie an echter Hardware verifiziert. From 82a394255452604791c8c521810eee9991bf9071 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 21:50:37 +0200 Subject: [PATCH 136/150] feat(station): Bedienung ueber einen Taster mit Panel-Rueckmeldung Ein Taster auf GPIO23 (active low, 50 ms entprellt) bedient die Station am Geraet: kurz oeffnet das Menue bzw. blaettert, gehalten waehlt (1,5 s), bricht ab (3 s, feuert sofort) und faehrt herunter (10 s, nur ueber die sichtbare Abbruchstufe hinweg). Menue schliesst nach 20 s, Rueckfrage verfaellt nach 10 s als Abbruch; jede veraendernde Auswahl bekommt eine Rueckfrage. Noetig, weil "on"/"migrate on" wlan0 zum Zugangspunkt macht und damit jede SSH-Sitzung abreisst - bisher blieb dort nur warten oder Stecker ziehen. "Migration starten" startet ueber das Menue IMMER mit --listen: der Migrations-AP hat ein oeffentlich bekanntes Passwort, ein Tastendruck im Automatikmodus wuerde jede alte Klyqa-Lampe in Reichweite ungefragt migrieren. Der Tasterdienst fasst den SPI-Bus nicht an (sonst "GPIO busy"), sondern schreibt seinen Bedienzustand atomar mit 0644 nach /run/ninlight-button/ui.json - bewusst nicht ins RuntimeDirectory der Hauptunit, das systemd beim "off" aufraeumt. Der Anzeigedienst liest die Datei jede halbe Sekunde (der teure build_state()-Fork bleibt beim 5-Sekunden-Takt) und zeichnet sie als Teilaufbau, kostet dem Panel also keinen Vollaufbau je Tastendruck. Fehlt gpiozero oder der Taster, gibt es genau eine Protokollzeile und sys.exit(0) - dieselbe Zusage wie bei der Anzeige. Ausliefern/Einrichten folgt in einem eigenen Durchgang. --- tools/station/README.md | 35 ++ tools/station/button/button.py | 579 ++++++++++++++++++++++++++ tools/station/display/display.py | 84 +++- tools/station/display/render.py | 40 ++ tools/station/ninlight-button.service | 75 ++++ tools/station/tests/test_button.py | 461 ++++++++++++++++++++ tools/station/tests/test_render.py | 123 +++++- 7 files changed, 1378 insertions(+), 19 deletions(-) create mode 100644 tools/station/button/button.py create mode 100644 tools/station/ninlight-button.service create mode 100644 tools/station/tests/test_button.py diff --git a/tools/station/README.md b/tools/station/README.md index fb6394f..4a61299 100644 --- a/tools/station/README.md +++ b/tools/station/README.md @@ -25,6 +25,41 @@ würde dort nur mit einer Fehlermeldung enden. ssh ninlight-station status Zustand und letzte Auslieferungen ssh ninlight-station off ausschalten +## Taster am Gerät + +Sobald die Station läuft, wird `wlan0` zum Zugangspunkt und **jede SSH-Sitzung reißt ab**. +Für genau diesen Zustand gibt es einen Taster auf **GPIO23**; die Rückmeldung kommt aufs +E-Paper. Ein Taster, zwei Zusammenhänge: + +| | kurz (unter 1 s) | gehalten | +|----------------------|------------------|-------------------------------------------------------| +| Ruhe (Statusbild) | Menü öffnen | 3 s → Abbruch (Station aus), weiter 10 s → Herunterfahren | +| Menü offen | nächster Eintrag | 1,5 s → Eintrag auswählen | +| Rückfrage | abbrechen | 1,5 s → bestätigen | + +Der Abbruch feuert **bei 3 s**, nicht erst beim Loslassen — danach läuft auf dem Panel ein +Countdown bis zum Herunterfahren, den Loslassen jederzeit abbricht. Das Menü schließt nach +20 s ohne Eingabe, eine Rückfrage verfällt nach 10 s als Abbruch. Die Einträge hängen vom +Zustand ab (Station aus / Rettung läuft / Migration läuft); jede verändernde Auswahl +bekommt eine Rückfrage. + +**„Migration starten" startet über das Menü immer in der Horchphase** (`--listen`), nie +automatisch: der Migrations-Zugangspunkt hat ein öffentlich bekanntes Passwort, und ein +Tastendruck, der ihn im Automatikmodus hochfährt, würde jede alte Klyqa-Lampe in +Funkreichweite ungefragt migrieren, auch fremde. Wer den Automatikmodus will, nimmt die +Kommandozeile. + +Der Taster ist nirgends Voraussetzung: fehlt `gpiozero` oder der Taster, protokolliert der +Dienst eine Zeile und beendet sich ohne Fehler. Er fasst den SPI-Bus **nicht** an (zwei +Prozesse auf denselben Leitungen ergeben `GPIO busy`), sondern schreibt seinen +Bedienzustand nach `/run/ninlight-button/ui.json`; der Anzeigedienst liest ihn und +zeichnet. Ausgeführt wird ausschließlich über das CLI. + +Dienst und Unit liegen in `tools/station/button/` bzw. +`tools/station/ninlight-button.service` — das Ausliefern und Einrichten (`deploy.sh`, +`setup_pi.sh`) kommt in einem eigenen Durchgang, bis dahin installiert der Aufsetzweg den +Tasterdienst noch nicht. + SSID und Passwort stehen in `/etc/ninlight-station.conf` auf dem Pi und müssen zu den Build-Parametern `NINLIGHT_RECOVERY_SSID` und `NINLIGHT_RECOVERY_PASSWORD` aus `config.local.cmake` passen. Weicht eines ab, findet die Lampe die Station nicht. diff --git a/tools/station/button/button.py b/tools/station/button/button.py new file mode 100644 index 0000000..6d06ec3 --- /dev/null +++ b/tools/station/button/button.py @@ -0,0 +1,579 @@ +#!/usr/bin/env python3 +"""Tasterdienst der ninlight-Station: EIN Taster auf GPIO23 (active low, interner +Pull-up), Rueckmeldung ueber das E-Paper. + +Wozu das gut ist: "ninlight-station on"/"migrate on" macht wlan0 zum Zugangspunkt und +reisst damit JEDE SSH-Sitzung ueber WLAN ab. Genau in diesem Zustand hatte der Bediener +am Geraet bisher keinerlei Eingriff - es blieb warten oder Stecker ziehen, und Letzteres +hat an diesem Pi schon eine SD-Karte gekostet. Der Taster ist der Ausweg: Abbruch +(Station aus) und, ueber eine sichtbare Zwischenstufe, ein sauberes Herunterfahren. + +Bedienung (ein Taster, zwei Zusammenhaenge - die Bedeutung haengt daran, ob das Menue +offen ist): + + kurz (< 1 s) gehalten + Ruhe Menue oeffnen 3 s -> Abbruch, weiter 10 s -> Herunterfahren + Menue offen naechster Eintrag 1,5 s -> Eintrag auswaehlen + Rueckfrage abbrechen 1,5 s -> bestaetigen + +Der Abbruch feuert BEI 3 s, nicht erst beim Loslassen: der Bediener soll die Wirkung am +Panel sehen. Haelt er weiter, laeuft dort ein Countdown bis 10 s (Herunterfahren); +Loslassen bricht diese zweite Stufe jederzeit ab. Das Herunterfahren ist damit nur ueber +eine sichtbare Zustandsaenderung hinweg erreichbar - nicht aus Versehen. + +DER TASTERDIENST FASST DEN SPI-BUS NICHT AN. Zwei Prozesse auf denselben GPIO-/SPI- +Leitungen enden in "lgpio.error: 'GPIO busy'" (am Geraet passiert, als +"ninlight-station display test" neben dem laufenden Anzeigedienst lief). Deshalb schreibt +dieser Dienst nur seinen Bedienzustand nach UI_FILE, und der Anzeigedienst liest ihn und +zeichnet (siehe display.read_ui()/render.blocks()). + +Alle veraendernden Aktionen laufen ueber das vorhandene CLI (/usr/local/bin/ +ninlight-station), nie ueber nachgebaute Logik: nur das CLI kennt die Profilumschaltung, +die Nachpruefungen ("es darf nie mehr als ein Stationsprofil aktiv sein") und den +Zeitschalter. + +SICHERHEITSREGEL: "Migration starten" startet ueber das Menue IMMER in der Horchphase +("migrate on --listen"), nie automatisch. Der Migrations-Zugangspunkt hat ein oeffentlich +bekanntes Passwort (es steht in der alten Klyqa-Firmware); ein Tastendruck, der ihn im +Automatikmodus hochfaehrt, wuerde jede alte Klyqa-Lampe in Funkreichweite ungefragt +migrieren, auch fremde. Die bequeme Geste muss die sichere sein - wer den Automatikmodus +will, nimmt die Kommandozeile, dort ist die Absicht eindeutig. + +Der Taster ist NIRGENDS Voraussetzung: fehlt gpiozero, fehlt der Taster oder ist GPIO23 +belegt, schreibt main() GENAU EINE Protokollzeile und beendet sich mit sys.exit(0) - +dieselbe Zusage (und derselbe Grund: Restart=on-failure darf daraus keine Neustartschleife +machen) wie bei der Anzeige, siehe display.py's Modul-Docstring. Und ein Fehler am Taster +darf niemals eine laufende Migration stoeren oder das Ein-/Ausschalten aufhalten - deshalb +faengt jeder Schreib- und Ausfuehrungspfad hier seine Fehler selbst ab. +""" +from __future__ import annotations + +import json +import logging +import math +import os +import subprocess +import sys +import time +from pathlib import Path + +log = logging.getLogger("ninlight-button") + +# --- Zeiten und Pfade --------------------------------------------------------------- + +BUTTON_PIN = int(os.environ.get("NINLIGHT_BUTTON_PIN", "23")) +# 50 ms statt der naheliegenden 20: ein sehr kurzer Tipper kam am Geraet durch eine +# 20-ms-Entprellung noch durch und oeffnete ungewollt das Menue. Die Entprellung macht +# gpiozero selbst (bounce_time), nicht diese Schleife - siehe open_button(). +DEBOUNCE_S = 0.05 +# Abtastrate der Hauptschleife. Deutlich feiner als die Entprellung, damit eine Flanke +# nicht erst eine halbe Entprellzeit spaeter gesehen wird; ein Durchlauf ohne Aenderung +# kostet nur einen gpiozero-Registerzugriff und einen Woerterbuchvergleich (UiWriter +# schreibt nur bei Aenderung). +TICK_S = 0.02 + +SHORT_PRESS_MAX_S = 1.0 +SELECT_HOLD_S = 1.5 # Menue: Eintrag waehlen / Rueckfrage: bestaetigen +CANCEL_HOLD_S = 3.0 # Ruhe: Abbruch (Station aus) +SHUTDOWN_HOLD_S = 10.0 # Ruhe: Herunterfahren (nur ueber die Abbruchstufe hinweg) +MENU_TIMEOUT_S = 20.0 # ohne Eingabe zurueck aufs Statusbild +CONFIRM_TIMEOUT_S = 10.0 # Rueckfrage verfaellt als Abbruch +RESULT_S = 4.0 # wie lange die Rueckmeldung einer Aktion stehen bleibt + +# Dieselben Umgebungsvariablen wie ninlight-station/display.py, damit Tests und +# Handbetrieb ohne echtes /run bzw. /etc auskommen. +RUN_DIR = Path(os.environ.get("NINLIGHT_RUN_DIR", "/run/ninlight-station")) +# BEWUSST NICHT /run/ninlight-station/ui.json (das war der Vorschlag): dieses Verzeichnis +# ist das RuntimeDirectory der Hauptunit, und systemd raeumt es beim regulaeren Stop von +# ninlight-station auf - also ausgerechnet im Zustand "Station aus", in dem das Menue am +# dringendsten gebraucht wird (dort liegen "Rettung starten"/"Migration starten"). Die +# Datei liegt deshalb im eigenen RuntimeDirectory dieses Dienstes; sie verschwindet damit +# genau dann, wenn dieser Dienst endet - und ein stehengebliebenes Menue kann den +# Anzeigedienst nicht ueberdauern. +UI_FILE = Path(os.environ.get("NINLIGHT_UI_FILE", "/run/ninlight-button/ui.json")) +STATION_CLI = os.environ.get("NINLIGHT_STATION_BIN", "/usr/local/bin/ninlight-station") +STATION_SERVICE = os.environ.get("NINLIGHT_STATION_SERVICE", "ninlight-station") +# Das Herunterfahren geht NICHT ueber das CLI (das kennt es nicht) - es ist die einzige +# Aktion dieses Dienstes, die an systemd selbst geht. +SHUTDOWN_ARGV = ("systemctl", "poweroff") +# Obergrenze fuer einen CLI-Aufruf. "recovery on"/"migrate on" schalten NetworkManager- +# Profile um und pruefen danach nach; das dauert Sekunden, nicht Minuten. Ohne Deckel +# wuerde ein haengender NetworkManager die Bedienung dauerhaft einfrieren - mit ihm +# bleibt der Taster spaetestens danach wieder bedienbar. +ACTION_TIMEOUT_S = 120 + + +class ButtonUnavailable(Exception): + """Kein Taster da (keine Bibliothek, kein Pin, Pin belegt) - main() macht daraus + genau eine Protokollzeile und sys.exit(0).""" + + +# --- Aktionen ----------------------------------------------------------------------- + + +class Action: + """Was ein Menueeintrag bzw. die Haltegeste ausloest. + + `confirm` = braucht eine Rueckfrage. Zusage: JEDE veraendernde Auswahl bekommt eine + ("Anzeige auffrischen" veraendert nichts und braucht keine).""" + + def __init__(self, label: str, argv=(), kind: str = "cli", confirm: bool = True, + done_text: str | None = None) -> None: + self.label = label + self.argv = tuple(argv) + self.kind = kind + self.confirm = confirm + self.done_text = done_text or label + + def __repr__(self) -> str: # nur fuer Testausgaben + return "Action({!r}, {!r}, kind={!r})".format(self.label, self.argv, self.kind) + + +def action_shutdown() -> Action: + return Action("Herunterfahren", kind="shutdown", confirm=True, + done_text="Faehrt herunter") + + +def action_cancel() -> Action: + """Die Haltegeste in Ruhe: Station aus. Bewusst ohne Rueckfrage - sie IST die + Rueckfrage (3 s halten), und sie ist der Notausgang, der ohne Umweg wirken muss.""" + return Action("Station aus", ("off",), confirm=False, done_text="Station aus") + + +def menu_actions(state: dict) -> list[Action]: + """Die Menueeintraege fuer den gegebenen Stationszustand - nur das, was gerade + sinnvoll ist. Reine Funktion ueber dem von station_state() gelesenen Zustand.""" + running = bool(state.get("running")) + mode = state.get("mode") + target = state.get("target") + + if not running: + return [ + Action("Rettung starten", ("recovery", "on"), done_text="Rettung laeuft"), + # IMMER --listen (siehe Modul-Docstring, Sicherheitsregel). Wer den + # Automatikmodus will, nimmt die Kommandozeile. + Action("Migration starten", ("migrate", "on", "--listen"), + done_text="Migration: horcht"), + action_shutdown(), + ] + + items: list[Action] = [] + if mode == "migration" and target: + # Nur wenn last_seen wirklich ein Ziel kennt - ohne erkannte Lampe gibt es + # nichts freizugeben, und "arm" ohne Ziel wuerde das CLI ohnehin abweisen. + items.append(Action("Freigabe: {}".format(target), ("arm", str(target)), + done_text="Freigegeben: {}".format(target))) + items.append(Action("Station aus", ("off",), done_text="Station aus")) + # Veraendert nichts am Betrieb - einzige Auswahl ohne Rueckfrage. + items.append(Action("Anzeige auffrischen", ("display", "test"), confirm=False, + done_text="Anzeige aufgefrischt")) + items.append(action_shutdown()) + return items + + +class CliActions: + """Fuehrt eine Action aus - ueber das CLI bzw. systemctl, nie durch Nachbauen deren + Logik. Gibt zurueck, ob es geklappt hat; wirft nie: ein Fehler hier darf weder den + Tasterdienst beenden noch eine laufende Migration stoeren.""" + + def __init__(self, cli: str = STATION_CLI, shutdown_argv=SHUTDOWN_ARGV, + runner=subprocess.run) -> None: + self.cli = cli + self.shutdown_argv = tuple(shutdown_argv) + self.runner = runner + + def argv_for(self, action: Action) -> list[str]: + if action.kind == "shutdown": + return list(self.shutdown_argv) + return [self.cli] + list(action.argv) + + def run(self, action: Action) -> bool: + argv = self.argv_for(action) + log.info("Taster fuehrt aus: %s", " ".join(argv)) + try: + result = self.runner(argv, check=False, timeout=ACTION_TIMEOUT_S) + except (OSError, subprocess.SubprocessError) as e: + log.warning("Aktion '%s' fehlgeschlagen: %s", action.label, e) + return False + rc = getattr(result, "returncode", 0) + if rc != 0: + log.warning("Aktion '%s' endete mit Rueckgabewert %s", action.label, rc) + return rc == 0 + + +# --- Stationszustand (nur lesen) ---------------------------------------------------- + + +def _read_json(path: Path) -> dict: + try: + data = json.loads(path.read_text()) + except (OSError, ValueError): + return {} + return data if isinstance(data, dict) else {} + + +def _service_active(service_name: str = STATION_SERVICE) -> bool: + try: + result = subprocess.run(["systemctl", "is-active", "--quiet", service_name], + timeout=5, check=False) + except (OSError, subprocess.SubprocessError): + # Ohne systemd (Handlauf) lieber "laeuft" annehmen als das Menue mit + # "Rettung starten" anzubieten, waehrend die Station in Wahrheit laeuft. + return True + return result.returncode == 0 + + +def station_state(run_dir: Path = RUN_DIR, service_name: str = STATION_SERVICE) -> dict: + """Der Zustand, von dem die Menueeintraege abhaengen. Liest dieselben Quellen wie + display.build_state() - RUN_DIR/mode (nicht status.json's "mode"-Feld, siehe dort) + und status.json fuer das zuletzt erkannte Ziel - dupliziert aber keine Logik des + CLI: geschaltet wird ausschliesslich ueber das CLI.""" + running = _service_active(service_name) + try: + mode = (run_dir / "mode").read_text().strip() + except OSError: + mode = "recovery" + if mode not in ("recovery", "migration"): + mode = "recovery" + status = _read_json(run_dir / "status.json") + last_seen = status.get("last_seen") + target = last_seen.get("target") if isinstance(last_seen, dict) else None + return {"running": running, "mode": mode, + "target": target if isinstance(target, str) and target else None} + + +# --- Bedienzustand in eine Datei schreiben ------------------------------------------ + + +class UiWriter: + """Schreibt den Bedienzustand atomar mit 0644 nach UI_FILE - dasselbe Muster und + dieselben Rechte wie status.json/control.json (Temp-Datei im selben Verzeichnis, + Rechte setzen, dann umbenennen). Beides sind Muster, deren Verletzung in diesem + Projekt schon mehrfach Fehler verursacht hat: ohne 0644 kann der Anzeigedienst die + Datei nicht lesen, ohne das Umbenennen sieht er sie halb geschrieben. + + Schreibt nur bei Aenderung (die Schleife laeuft alle TICK_S) und wirft nie.""" + + def __init__(self, path: Path = UI_FILE) -> None: + self.path = Path(path) + self._last: dict | None = None + self._failed_logged = False + + def write(self, view: dict) -> bool: + """True, wenn tatsaechlich geschrieben wurde.""" + if view == self._last: + return False + payload = json.dumps(view, ensure_ascii=False, sort_keys=True) + tmp = self.path.with_name("{}.tmp-{}".format(self.path.name, os.getpid())) + try: + self.path.parent.mkdir(parents=True, exist_ok=True) + tmp.write_text(payload) + os.chmod(tmp, 0o644) + os.replace(tmp, self.path) + except OSError as e: + # Nur einmal protokollieren: die Schleife kaeme sonst alle TICK_S wieder + # hierher und wuerde das Journal fluten. Der Taster bleibt trotzdem + # bedienbar, nur die Rueckmeldung am Panel fehlt. + if not self._failed_logged: + log.warning("Bedienzustand konnte nicht geschrieben werden (%s): %s", + self.path, e) + self._failed_logged = True + try: + tmp.unlink() + except OSError: + pass + return False + self._last = dict(view) + return True + + +# --- Die Zustandsfuehrung ----------------------------------------------------------- + + +def _countdown(total_s: float, held_s: float) -> int: + """Ganze Sekunden bis `total_s`, aufgerundet und nie negativ - "noch 6" solange auch + nur eine Zehntelsekunde davon uebrig ist.""" + return max(0, int(math.ceil(total_s - held_s))) + + +class ButtonUI: + """Die gesamte Bedienlogik - welcher Druck was bedeutet, welche Zeitschwelle was + ausloest, was auf dem Panel steht - als reine Zustandsmaschine ohne Schleife, ohne + time.sleep() und ohne GPIO. Dasselbe Muster wie display.RefreshPlanner, aus demselben + Grund: so laesst sich ein kompletter Bedienverlauf in Millisekunden durchspielen. + + Benutzung pro Runde: update(now, pressed) -> Action oder None; eine zurueckgegebene + Action fuehrt der Aufrufer aus und meldet das Ergebnis per done() zurueck. view(now) + liefert jederzeit den Bedienzustand fuer die Anzeige. + + `pressed` ist der ENTPRELLTE Pegel (gpiozero, bounce_time), nicht eine Flanke - alle + Schwellen rechnen deshalb gegen den Zeitpunkt der letzten steigenden Flanke. Das ist + auch der Grund, warum ein blockierender CLI-Aufruf mitten in einer Haltegeste nichts + kaputtmacht: nach der Rueckkehr wird derselbe Pegel wieder gegen dieselbe Uhr + gemessen.""" + + def __init__(self, now: float = 0.0, read_state=station_state) -> None: + self._read_state = read_state + self._pressed = False + self._press_t = now + # Diese Haltegeste hat schon etwas ausgeloest - ein spaeteres Loslassen darf + # dann nicht zusaetzlich als "kurzer Druck" gelten. + self._consumed = False + # 0 = nichts, 1 = Abbruch gefeuert, zweite Stufe (Herunterfahren) laeuft. + self._hold_stage = 0 + # In welchem Zusammenhang diese Haltegeste BEGONNEN hat ("idle", "menu", + # "confirm"). Die Haltegeste wird danach beurteilt, NICHT nach dem inzwischen + # erreichten Bildschirm: waehlt man im Menue einen Eintrag (1,5 s), steht danach + # "Bitte warten"/die Rueckfrage - haelt der Bediener einfach weiter, duerfte das + # nicht plotzlich als die Ruhe-Haltegeste gelten und bei 3 s die Station + # abschalten. Genau das tat eine fruehere Fassung. + self._press_screen = "idle" + self.screen = "idle" + self.items: list[Action] = [] + self.index = 0 + self.pending: Action | None = None + self.deadline: float | None = None + self.busy: Action | None = None + self.result: tuple[str, bool] | None = None + self.result_until: float | None = None + + # -- Eingabe --------------------------------------------------------------------- + + def update(self, now: float, pressed: bool) -> Action | None: + action = None + if pressed and not self._pressed: + self._pressed = True + self._press_t = now + self._consumed = False + self._press_screen = self.screen if self.screen in ("menu", "confirm") else "idle" + elif pressed and self._pressed: + action = self._held(now) + elif self._pressed and not pressed: + self._pressed = False + held = now - self._press_t + if not self._consumed and held < SHORT_PRESS_MAX_S: + action = self._short(now) + # Loslassen bricht die zweite Stufe (Herunterfahren) jederzeit ab. + self._hold_stage = 0 + self._expire(now) + else: + self._expire(now) + if action is not None: + self.busy = action + self.screen = "busy" + self.pending = None + self.deadline = None + return action + + def _short(self, now: float) -> Action | None: + self.result = None + self.result_until = None + if self.screen == "menu": + if self.items: + self.index = (self.index + 1) % len(self.items) + self.deadline = now + MENU_TIMEOUT_S + return None + if self.screen == "confirm": + # Abbrechen - zurueck ins Menue, nicht aufs Statusbild: der Bediener ist + # noch am Bedienen und will meist einen anderen Eintrag. + self.pending = None + self.screen = "menu" + self.deadline = now + MENU_TIMEOUT_S + return None + # Ruhe (auch waehrend eine Rueckmeldung steht): Menue oeffnen. + self._open_menu(now) + return None + + def _open_menu(self, now: float) -> None: + try: + state = self._read_state() + except Exception as e: # noqa: BLE001 - siehe Modul-Docstring: nie stoeren + log.warning("Stationszustand nicht lesbar, Menue bleibt zu: %s", e) + return + self.items = menu_actions(state) + if not self.items: + return + self.index = 0 + self.screen = "menu" + self.deadline = now + MENU_TIMEOUT_S + + def _held(self, now: float) -> Action | None: + held = now - self._press_t + # Nach dem Zusammenhang BEIM DRUCK beurteilt, nicht nach dem aktuellen + # Bildschirm - siehe _press_screen. + if self._press_screen == "menu": + if held >= SELECT_HOLD_S and not self._consumed and self.items: + self._consumed = True + return self._select(now, self.items[self.index]) + return None + if self._press_screen == "confirm": + if held >= SELECT_HOLD_S and not self._consumed: + self._consumed = True + return self.pending + return None + # Ruhe (oder eine stehende Rueckmeldung/ein gerade gelaufener Abbruch): + # zweistufige Haltegeste. + if self._hold_stage == 1 and held >= SHUTDOWN_HOLD_S and not self._consumed: + self._consumed = True + return action_shutdown() + if self._hold_stage == 0 and held >= CANCEL_HOLD_S: + # Feuert BEI 3 s, nicht erst beim Loslassen - und setzt _consumed NICHT: + # die zweite Stufe muss aus derselben Geste noch erreichbar bleiben. + self._hold_stage = 1 + self.result = None + self.result_until = None + return action_cancel() + return None + + def _select(self, now: float, action: Action) -> Action | None: + if not action.confirm: + return action + self.pending = action + self.screen = "confirm" + self.deadline = now + CONFIRM_TIMEOUT_S + return None + + def _expire(self, now: float) -> None: + if self.result_until is not None and now >= self.result_until: + self.result = None + self.result_until = None + if self.deadline is None or now < self.deadline: + return + if self.screen == "menu": + # Ein Geraet, das im Menue stehenbleibt, soll nicht aussehen, als sei es + # beschaeftigt - zurueck aufs Statusbild. + self.screen = "idle" + self.items = [] + self.deadline = None + elif self.screen == "confirm": + # Verfaellt als ABBRUCH - eine Rueckfrage, die von selbst zustimmt, waere + # das Gegenteil einer Rueckfrage. + self.pending = None + self.screen = "idle" + self.items = [] + self.deadline = None + + def done(self, now: float, action: Action, ok: bool) -> None: + """Ergebnis einer ausgefuehrten Aktion - setzt die Rueckmeldung fuers Panel.""" + self.busy = None + self.screen = "idle" + self.items = [] + self.pending = None + self.deadline = None + self.result = (action.done_text if ok else "{}: Fehler".format(action.label), ok) + self.result_until = now + RESULT_S + + # -- Ausgabe --------------------------------------------------------------------- + + def view(self, now: float) -> dict: + """Der Bedienzustand, wie ihn der Anzeigedienst zeichnet. "active" sagt ihm, dass + gerade eine Bedienung laeuft (er sieht dann schneller nach).""" + if (self._pressed and self._press_screen == "idle" + and self.screen in ("idle", "busy")): + held = now - self._press_t + if self._hold_stage == 1: + return _view("hold", "STATION AUS", + "Herunterfahren in {} s".format( + _countdown(SHUTDOWN_HOLD_S, held)), + ["Weiter halten - Loslassen bricht ab"]) + if held >= SHORT_PRESS_MAX_S: + return _view("hold", "HALTEN", + "Abbruch in {} s".format(_countdown(CANCEL_HOLD_S, held)), + ["Weiter halten - Loslassen bricht ab"]) + if self.screen == "menu" and self.items: + selected = self.items[self.index] + rest = self.items[self.index + 1:] + self.items[:self.index] + return _view("menu", "MENUE", "> " + selected.label, + [a.label for a in rest] + ["kurz: weiter, halten: waehlen"]) + if self.screen == "confirm" and self.pending is not None: + return _view("confirm", "RUECKFRAGE", self.pending.label, + ["halten (1,5 s): ja", "kurz: nein"]) + if self.screen == "busy" and self.busy is not None: + return _view("busy", "BITTE WARTEN", self.busy.label, []) + if self.result is not None: + text, ok = self.result + return _view("result", "FERTIG" if ok else "FEHLER", text, []) + return {"view": "idle", "active": False} + + +def _view(view: str, title: str, hero: str, details: list[str]) -> dict: + return {"view": view, "title": title, "hero": hero, "details": list(details), + "active": True} + + +# --- Hardware und Hauptschleife ----------------------------------------------------- + + +def open_button(pin: int = BUTTON_PIN): + """Der einzige Hardwarezugriff dieses Dienstes: ein Eingangspin. KEIN SPI, kein + Zeichenpin - siehe Modul-Docstring ("GPIO busy"). + + Entprellt gpiozero selbst (bounce_time): ein Tipper, der kuerzer ist als + DEBOUNCE_S, kommt gar nicht erst als Druck an.""" + try: + import gpiozero + except ImportError as e: + raise ButtonUnavailable("gpiozero fehlt ({})".format(e)) + try: + return gpiozero.Button(pin, pull_up=True, bounce_time=DEBOUNCE_S) + except Exception as e: # noqa: BLE001 + # Bewusst breit: ein belegter Pin kommt je nach Pin-Factory als + # gpiozero.GPIOPinInUse, als roher lgpio.error ('GPIO busy') oder als OSError + # zurueck - keine gemeinsame Oberklasse, die hier greifen wuerde. Alles davon + # heisst dasselbe: kein Taster, also lautlos ohne Fehler beenden, statt eine + # Neustartschleife auszuloesen. + raise ButtonUnavailable("GPIO{} nicht verfuegbar ({})".format(pin, e)) + + +def run_daemon(button=None, ui: "ButtonUI | None" = None, writer: "UiWriter | None" = None, + actions: "CliActions | None" = None, clock=time, + read_state=station_state) -> None: + """Endlosschleife: Pegel lesen, ButtonUI fragen, eine zurueckgegebene Aktion + ausfuehren, Ergebnis zurueckmelden, Bedienzustand schreiben. + + `button`, `ui`, `writer`, `actions` und `clock` sind einschleusbar, damit die + VERDRAHTUNG hier selbst pruefbar ist und nicht nur eine Kopie davon (display.py hatte + genau diesen Fehler: die Tests trieben eine nachgebaute Schleife, und Mutationen an + den echten Zeilen blieben gruen). Im Betrieb bleiben die Vorgaben. + + Die Reihenfolge ist wesentlich: der Bedienzustand wird VOR dem (sekundenlangen) + CLI-Aufruf geschrieben, sonst steht auf dem Panel bis zu dessen Ende noch das Menue + und der Bediener haelt das Geraet fuer eingefroren.""" + if button is None: + button = open_button() + if writer is None: + writer = UiWriter() + if actions is None: + actions = CliActions() + if ui is None: + ui = ButtonUI(clock.monotonic(), read_state=read_state) + + log.info("Taster bereit (GPIO%s), Bedienzustand in %s", BUTTON_PIN, writer.path) + writer.write(ui.view(clock.monotonic())) + + while True: + now = clock.monotonic() + action = ui.update(now, bool(button.is_pressed)) + if action is not None: + writer.write(ui.view(now)) + ok = actions.run(action) + ui.done(clock.monotonic(), action, ok) + writer.write(ui.view(clock.monotonic())) + clock.sleep(TICK_S) + + +def main() -> None: + logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") + try: + run_daemon() + except ButtonUnavailable as e: + # GENAU EINE Zeile, dann sys.exit(0) - siehe Modul-Docstring. Kein Fehler, damit + # Restart=on-failure daraus keine Neustartschleife macht und das Journal flutet. + log.info("Taster nicht verfuegbar, beende ohne Fehler: %s", e) + sys.exit(0) + except KeyboardInterrupt: + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/tools/station/display/display.py b/tools/station/display/display.py index d32775b..a8765a1 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -51,8 +51,27 @@ log = logging.getLogger("ninlight-display") RUN_DIR = Path(os.environ.get("NINLIGHT_RUN_DIR", "/run/ninlight-station")) CONF_PATH = Path(os.environ.get("NINLIGHT_STATION_CONF", "/etc/ninlight-station.conf")) SERVICE_NAME = os.environ.get("NINLIGHT_STATION_SERVICE", "ninlight-station") +# Bedienzustand des Tasterdienstes (tools/station/button/button.py). NUR GELESEN: der +# Tasterdienst fasst den SPI-Bus nicht an und dieser Dienst kein GPIO23 - zwei Prozesse +# auf denselben Leitungen enden in "lgpio.error: 'GPIO busy'". Die Datei liegt im +# RuntimeDirectory DES TASTERDIENSTES (nicht in RUN_DIR): RUN_DIR ist das +# RuntimeDirectory der Hauptunit und verschwindet beim regulaeren "off" - also +# ausgerechnet dann, wenn das Menue "Rettung starten"/"Migration starten" anbietet. +UI_FILE = Path(os.environ.get("NINLIGHT_UI_FILE", "/run/ninlight-button/ui.json")) POLL_INTERVAL_S = 5 +# Solange eine Bedienung laeuft, waere ein 5-Sekunden-Takt unbenutzbar (ein Tastendruck +# bis zu 5 s ohne Reaktion). Die Schleife tickt deshalb in diesem Takt und sieht bei +# JEDEM Tick in der Bedienzustandsdatei nach - auch im Ruhezustand, sonst waere +# ausgerechnet der erste Druck (der das Menue oeffnet) der langsamste. Das kostet eine +# kleine Dateilesung je halber Sekunde; der teure Teil des Zustands (build_state() mit +# seinem "systemctl is-active"-Fork) bleibt beim 5-Sekunden-Takt. +# +# Am Panel gemessen kommt zu dieser halben Sekunde noch der Teilaufbau (rund 0,3 s) - +# also grob 0,5-0,8 s je Druck. Das ist spuerbar, aber bedienbar: die Geste ist +# "druecken und hinsehen", nicht "tippen wie auf einer Tastatur", und die Haltegesten +# (1,5/3/10 s) liegen alle weit ueber dieser Verzoegerung. +UI_POLL_INTERVAL_S = 0.5 # E-Paper altert mit jedem Bildaufbau - ein Vollaufbau nur bei Moduswechsel und # mindestens alle 30 Minuten, sonst Teilaktualisierung (siehe Modul-Docstring der # Panel-Treiber: haeufige Teilaktualisierungen ohne gelegentlichen Vollaufbau lassen @@ -220,6 +239,21 @@ def build_state(run_dir: Path = RUN_DIR, conf_path: Path = CONF_PATH, return state +def read_ui(ui_path: Path = UI_FILE) -> dict: + """Der Bedienzustand des Tasterdienstes, oder {} wenn gerade keine Bedienung laeuft + (Datei fehlt, ist unlesbar, halb geschrieben, aus einer aelteren Fassung oder meldet + ausdruecklich "idle"). + + Jedes Feld wird defensiv gelesen: die Datei kommt aus einem FREMDEN Prozess, der + abgestuerzt, aelter oder neuer sein kann - ein Fehler hier darf die Anzeige nicht + aufhalten und schon gar nicht eine Migration stoeren.""" + data = _read_json(ui_path) + view = data.get("view") + if not data.get("active") or not isinstance(view, str) or view == "idle": + return {} + return data + + def _load_epd_module(revision: str): """Laedt den passenden Panel-Treiber. Nicht ueber ein Dict aus Modulnamen, damit ein statischer Import-Checker (und ein Mensch, der `grep -n epd2in13` tippt) beide @@ -485,30 +519,48 @@ def run_clear(revision: str) -> None: def run_daemon(revision: str, clock=time, panel: "Panel | None" = None, - read_state=build_state) -> None: - """Endlosschleife: alle POLL_INTERVAL_S Sekunden den Zustand einlesen und die von - RefreshPlanner entschiedene Aktion ausfuehren. Hier steht bewusst KEINE Zeitlogik mehr - (Fixrunde 4) - die steckt vollstaendig in RefreshPlanner und ist dort ohne Schleife, - ohne time.sleep() und ohne Hardware pruefbar. + read_state=build_state, read_ui=read_ui) -> None: + """Endlosschleife: alle UI_POLL_INTERVAL_S Sekunden den Bedienzustand und alle + POLL_INTERVAL_S Sekunden den Stationszustand einlesen und die von RefreshPlanner + entschiedene Aktion ausfuehren. Hier steht bewusst KEINE Zeitlogik zur Panelalterung + mehr (Fixrunde 4) - die steckt vollstaendig in RefreshPlanner und ist dort ohne + Schleife, ohne time.sleep() und ohne Hardware pruefbar. - `clock` (braucht monotonic() und sleep()), `panel` und `read_state` sind einschleusbar, - damit die VERDRAHTUNG hier - welche Aktion welche Panel-Methode ruft, dass die - Selbst-Aufwertung von draw_partial() zurueckgemeldet wird, dass das Einschlafen - zurueckgemeldet wird, und die Reihenfolge decide -> ausfuehren -> zurueckmelden - - selbst pruefbar ist und nicht nur eine Kopie davon (Fixrunde 5, N2: alle drei - Mutationen an genau diesen Zeilen blieben vorher gruen, weil der Test eine nachgebaute - Schleife statt dieser hier trieb). Im Betrieb bleiben die Vorgaben: echte Uhr, echtes - Panel, echtes build_state().""" + Die zwei Takte: der teure Teil (build_state() forkt fuer "systemctl is-active") + bleibt beim 5-Sekunden-Takt, der billige (eine kleine Datei lesen) laeuft in + UI_POLL_INTERVAL_S, damit eine Bedienung am Taster ueberhaupt benutzbar ist. Ein + laufender Bedienzustand ERSETZT das Statusbild (render.blocks() zweigt dafuer ganz + vorne ab); der Modus bleibt derselbe, sodass das Bild ueber Teilaufbauten kommt und + ein Tastendruck dem Panel keinen Vollaufbau kostet. + + `clock` (braucht monotonic() und sleep()), `panel`, `read_state` und `read_ui` sind + einschleusbar, damit die VERDRAHTUNG hier - welche Aktion welche Panel-Methode ruft, + dass die Selbst-Aufwertung von draw_partial() zurueckgemeldet wird, dass das + Einschlafen zurueckgemeldet wird, und die Reihenfolge decide -> ausfuehren -> + zurueckmelden - selbst pruefbar ist und nicht nur eine Kopie davon (Fixrunde 5, N2: + alle drei Mutationen an genau diesen Zeilen blieben vorher gruen, weil der Test eine + nachgebaute Schleife statt dieser hier trieb). Im Betrieb bleiben die Vorgaben: echte + Uhr, echtes Panel, echtes build_state()/read_ui().""" if panel is None: panel = Panel(revision) panel.clear() planner = RefreshPlanner(clock.monotonic()) + base_state = read_state() + last_state_poll = clock.monotonic() while True: - state = read_state() + now = clock.monotonic() + if now - last_state_poll >= POLL_INTERVAL_S: + base_state = read_state() + last_state_poll = now + ui = read_ui() + if ui: + state = dict(base_state) + state["ui"] = ui + else: + state = base_state current_lines = render.lines(state) mode = state.get("mode") - now = clock.monotonic() action = planner.decide(now, current_lines, mode) if action == "full": @@ -523,7 +575,7 @@ def run_daemon(revision: str, clock=time, panel: "Panel | None" = None, panel.sleep() planner.record_sleep() - clock.sleep(POLL_INTERVAL_S) + clock.sleep(UI_POLL_INTERVAL_S) def main() -> None: diff --git a/tools/station/display/render.py b/tools/station/display/render.py index fb9bc84..ae678a8 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -186,6 +186,16 @@ def blocks(state: dict) -> list[tuple[str, str]]: Bild - die Rolle ist ein reiner Bezeichner, die zugehoerigen Schriftgroessen kennt ausschliesslich compose().""" state = state if isinstance(state, dict) else {} + + # Laeuft gerade eine Bedienung am Taster, ERSETZT sie das Statusbild komplett - eine + # Bedienung, die neben dem Statusbild um Platz kaempft, waere auf 250x122 unlesbar, + # und in diesem Moment ist der Bedienzustand die einzige Auskunft, auf die der + # Bediener wartet. display.read_ui() liefert {} (und damit kein "ui"), sobald nichts + # laeuft - dann geht es unveraendert weiter wie zuvor. + ui = state.get("ui") + if isinstance(ui, dict) and ui.get("view") not in (None, "", "idle"): + return _ui_blocks(ui) + mode = state.get("mode") out: list[tuple[str, str]] = [] @@ -311,6 +321,36 @@ def blocks(state: dict) -> list[tuple[str, str]]: return out +def _ui_blocks(ui: dict) -> list[tuple[str, str]]: + """Der Bedienzustand des Tasterdienstes in Zeilen mit Rolle. Bewusst DUMM: welcher + Text bei welcher Geste erscheint, entscheidet allein der Tasterdienst (dort ist es + ohne Hardware pruefbar) - hier wird er nur noch den vorhandenen Rollen zugeordnet, + damit Kopfbalken, Schriftgroessen, Breitenkuerzung und unterer Rand dieselben bleiben + wie im Statusbild. + + Jedes Feld wird auf seinen Typ geprueft: die Datei kommt aus einem anderen Prozess + (der aelter, neuer oder halb geschrieben sein kann) - genau wie status.json darf sie + die Anzeige nie zum Absturz bringen.""" + out: list[tuple[str, str]] = [] + title = ui.get("title") + if isinstance(title, str) and title: + out.append((title, ROLE_HEADER)) + hero = ui.get("hero") + if isinstance(hero, str) and hero: + out.append((hero, ROLE_HERO)) + details = ui.get("details") + if isinstance(details, list): + for detail in details: + if isinstance(detail, str) and detail: + out.append((detail, ROLE_DETAIL)) + if not out: + # Es laeuft eine Bedienung, aber die Datei traegt keinen brauchbaren Text + # (abgeschnitten, fremde Fassung). Lieber gross sagen, dass bedient wird, als + # ein leeres Panel zu zeigen, das nach "haengt" aussieht. + out.append(("Bedienung", ROLE_BIG)) + return out + + def lines(state: dict) -> list[str]: """Nur die Texte aus blocks(), ohne Rollen - unveraenderte Schnittstelle fuer display.run_daemon(), das damit erkennt, ob sich der Inhalt geaendert hat (die Rolle diff --git a/tools/station/ninlight-button.service b/tools/station/ninlight-button.service new file mode 100644 index 0000000..a198a0e --- /dev/null +++ b/tools/station/ninlight-button.service @@ -0,0 +1,75 @@ +[Unit] +Description=ninlight Taster (Bedienung der Station ueber GPIO23) +# Wie beim Anzeigedienst nur After=, NICHT Requires=/BindsTo=/Wants=: ein fehlender oder +# abgestuerzter Taster darf server.py niemals mitreissen, und server.py muss starten +# koennen, auch wenn dieser Dienst fehlt oder deaktiviert ist. KEIN +# Wants=ninlight-station.service - das wuerde die absichtlich deaktivierte Station bei +# jedem Boot mit hochziehen (Fund der Hardware-Abnahme, ausfuehrlich in +# ninlight-display.service begruendet). +After=ninlight-station.service +# Gehoeren seit systemd 229 ins [Unit], nicht ins [Service] (dort werden sie mit +# "Unknown key name" ignoriert, und die Deckelung unten greift dann nie). +StartLimitIntervalSec=60 +StartLimitBurst=5 + +[Service] +Type=simple +Environment=NINLIGHT_RUN_DIR=/run/ninlight-station +Environment=NINLIGHT_UI_FILE=/run/ninlight-button/ui.json +# "-"-Praefix VOR dem Dateipfad (nicht vor dem Schluesselnamen - so herum ist es ein +# ungueltiger Schluessel und systemd verwirft die Zeile ganz): ohne die +# Konfigurationsdatei startet der Taster trotzdem. +EnvironmentFile=-/etc/ninlight-station.conf +# Eigenes RuntimeDirectory, aus zwei Gruenden: +# +# 1. Hier liegt ui.json. Es darf NICHT in /run/ninlight-station liegen (dem +# RuntimeDirectory der Hauptunit): systemd raeumt das beim regulaeren Stop von +# ninlight-station auf - also ausgerechnet im Zustand "Station aus", in dem das Menue +# ("Rettung starten"/"Migration starten") am dringendsten gebraucht wird. So +# verschwindet ui.json genau dann, wenn dieser Dienst endet, und ein +# stehengebliebenes Menue kann ihn nicht ueberdauern. +# 2. lgpio (gpiozero's bevorzugte Pin-Factory) legt beim Start "$HOME/.lgd-nfy0" an. +# Unter systemd ist HOME per Vorgabe LEER und das Arbeitsverzeichnis "/" - der Pfad +# wird "//.lgd-nfy0" und scheitert. Am Geraet fuer den Anzeigedienst nachgemessen, +# siehe dort; derselbe Stolperstein trifft jeden gpiozero-Dienst. +RuntimeDirectory=ninlight-button +RuntimeDirectoryMode=0755 +WorkingDirectory=/run/ninlight-button +Environment=HOME=/run/ninlight-button +ExecStart=/usr/bin/python3 /opt/ninlight-station/button/button.py + +# button.py faengt einen fehlenden Taster (keine Bibliothek, kein Pin, Pin belegt) als +# ButtonUnavailable ab, protokolliert eine Zeile und beendet sich mit sys.exit(0) - das +# ist nie ein "Fehlschlag" im systemd-Sinne und loest kein Restart= aus. on-failure +# (nicht always) deckelt damit genau das Richtige: echte Abstuerze werden neu versucht, +# ein dauerhaft fehlender Taster nicht. +Restart=on-failure +RestartSec=5 + +# BEWUSST KEIN NoNewPrivileges=/ProtectSystem=strict/ProtectHome= hier - anders als beim +# Anzeigedienst, und das ist kein Versehen: +# +# Dieser Dienst ruft /usr/local/bin/ninlight-station auf, und dessen einziger Zweck ist +# es, NetworkManager-Profile umzuschalten, Units zu starten/stoppen und nach +# /run/ninlight-station zu schreiben - alles ueber "sudo". Kindprozesse erben den +# Namensraum und das no_new_privs-Flag dieser Unit: mit ProtectSystem=strict waere +# /run/ninlight-station fuer das CLI schreibgeschuetzt (mode/control.json liessen sich +# nicht mehr schreiben), und no_new_privs laesst kein setuid-Binary mehr zu (sudo). Eine +# Absicherung, die genau die Aktionen unmoeglich macht, wegen derer es den Dienst gibt, +# ist keine. Die Angriffsflaeche ist stattdessen eng gehalten: EIN Eingangspin, kein +# Netzwerk, keine Eingabe ausser der Taste selbst, und ausgefuehrt wird ausschliesslich +# die feste Liste von CLI-Unterbefehlen aus button.py. +MemoryMax=64M + +# Laeuft als root (wie Haupt- und Anzeigedienst) fuer GPIO23 und fuer das CLI - kein +# SupplementaryGroups=gpio: auf reinem Debian gibt es die Gruppe oft gar nicht, und eine +# fehlende Gruppe ist fuer systemd ein harter Startfehler (216/GROUP), ausgerechnet in +# einer Unit, die nie stoeren darf. + +# KEIN Zugriff auf SPI und keine Zeichenleitung: der Tasterdienst fasst den Panel-Bus +# nicht an (zwei Prozesse darauf ergeben "lgpio.error: 'GPIO busy'"). Er schreibt seinen +# Bedienzustand nach /run/ninlight-button/ui.json, der Anzeigedienst liest ihn und +# zeichnet. + +[Install] +WantedBy=multi-user.target diff --git a/tools/station/tests/test_button.py b/tools/station/tests/test_button.py new file mode 100644 index 0000000..1322c5b --- /dev/null +++ b/tools/station/tests/test_button.py @@ -0,0 +1,461 @@ +"""Tests fuer den Tasterdienst (tools/station/button/button.py). + +Die Bedienlogik ist reine Zustandsfuehrung und wird hier OHNE Hardware geprueft - aber +bewusst NICHT, indem der Ablauf im Test nachgebaut wird: getrieben wird die ECHTE +Hauptschleife button.run_daemon() mit einer Fake-Uhr, einem nach Zeitplan gedrueckten +Taster-Doppel, dem ECHTEN CliActions (nur dessen subprocess.run ist ersetzt) und einem +protokollierenden Schreiber. + +Der Grund steht in display.py/test_render.py: bei der Zeitsteuerung der Anzeige trieben +die Tests eine KOPIE der Verdrahtung zwischen Zustandsmaschine und Ausfuehrung, und man +konnte die eigentliche Aktion durch eine falsche ersetzen, ohne dass ein Test rot wurde. +Deshalb laeuft hier alles ueber dieselbe Schleife, die auch auf dem Pi laeuft.""" +import json +import os +import subprocess +import sys +import types +from pathlib import Path + +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "button")) + +import button # noqa: E402 + + +# --- Zustaende, von denen die Menueeintraege abhaengen ------------------------------- + +STATE_OFF = {"running": False, "mode": "recovery", "target": None} +STATE_RECOVERY = {"running": True, "mode": "recovery", "target": None} +STATE_MIGRATION = {"running": True, "mode": "migration", "target": "gu10_rgb"} +STATE_MIGRATION_BLIND = {"running": True, "mode": "migration", "target": None} + + +# --- Fake-Uhr, Taster-Doppel, Protokoll --------------------------------------------- + + +class _StopDaemon(Exception): + """Bricht run_daemon()s Endlosschleife ab, sobald die simulierte Zeit abgelaufen ist.""" + + +class _FakeClock: + def __init__(self, until_s: float) -> None: + self.t = 0.0 + self.until_s = until_s + + def monotonic(self) -> float: + return self.t + + def advance(self, seconds: float) -> None: + self.t += seconds + + def sleep(self, seconds: float) -> None: + self.t += seconds + if self.t > self.until_s: + raise _StopDaemon + + +class _Recorder: + """Gemeinsame Zeitleiste von Schreiber und Ausfuehrung - nur so faellt auf, wenn die + Reihenfolge in run_daemon() kippt (z.B. das "Bitte warten" erst NACH dem + sekundenlangen CLI-Aufruf geschrieben wuerde).""" + + def __init__(self, clock: _FakeClock) -> None: + self.clock = clock + self.events: list[tuple[float, str, object]] = [] + + def note(self, kind: str, payload: object) -> None: + self.events.append((self.clock.monotonic(), kind, payload)) + + def views(self) -> list[tuple[float, dict]]: + return [(t, p) for t, k, p in self.events if k == "view"] + + def runs(self) -> list[tuple[float, list]]: + return [(t, p) for t, k, p in self.events if k == "run"] + + def argvs(self) -> list[list]: + return [p for _t, p in self.runs()] + + def kinds(self) -> list[str]: + return [v.get("view") for _t, v in self.views()] + + def first_view(self, name: str): + for t, v in self.views(): + if v.get("view") == name: + return t, v + return None + + +class _ScriptedButton: + """Taster-Doppel: gedrueckt genau waehrend der angegebenen Zeitfenster. `is_pressed` + ist wie bei gpiozero ein PEGEL (die Entprellung macht dort bounce_time), keine + Flanke - genau das, was run_daemon() abfragt.""" + + def __init__(self, clock: _FakeClock, presses) -> None: + self.clock = clock + self.presses = list(presses) + + @property + def is_pressed(self) -> bool: + t = self.clock.monotonic() + return any(start <= t < end for start, end in self.presses) + + +class _RecordingWriter: + """Schreiber-Doppel: protokolliert jeden tatsaechlich geschriebenen Bedienzustand. + Dedupliziert wie der echte UiWriter, damit die Zeitleiste die ANZEIGEWECHSEL zeigt + und nicht 50 identische Zeilen je Sekunde.""" + + def __init__(self, rec: _Recorder) -> None: + self.rec = rec + self.path = Path("/dev/null") + self._last = None + + def write(self, view: dict) -> bool: + if view == self._last: + return False + self._last = dict(view) + self.rec.note("view", dict(view)) + return True + + +def _fake_runner(clock: _FakeClock, rec: _Recorder, duration_s: float = 0.0, rc: int = 0): + """Ersetzt subprocess.run in CliActions - der Rest von CliActions (und damit die + ZUSAMMENSETZUNG der Kommandozeile, auf die es sicherheitsrelevant ankommt) ist echt.""" + + def run(argv, check=False, timeout=None): + rec.note("run", list(argv)) + # Ein CLI-Aufruf dauert Sekunden (Profilumschaltung) - die simulierte Uhr muss + # das mitmachen, sonst pruefen die Tests eine Schleife, die es so nicht gibt. + clock.advance(duration_s) + return types.SimpleNamespace(returncode=rc) + + return run + + +def _drive(presses, duration_s: float, state=STATE_OFF, action_duration_s: float = 0.0, + rc: int = 0) -> _Recorder: + """Laesst das echte button.run_daemon() `duration_s` simulierte Sekunden laufen.""" + clock = _FakeClock(duration_s) + rec = _Recorder(clock) + actions = button.CliActions(cli="/usr/local/bin/ninlight-station", + runner=_fake_runner(clock, rec, action_duration_s, rc)) + try: + button.run_daemon(button=_ScriptedButton(clock, presses), + writer=_RecordingWriter(rec), actions=actions, clock=clock, + read_state=lambda: dict(state)) + except _StopDaemon: + pass + return rec + + +def _short(at: float): + """Ein kurzer Druck (deutlich unter SHORT_PRESS_MAX_S, deutlich ueber DEBOUNCE_S).""" + return (at, at + 0.2) + + +# --- Menueaufbau -------------------------------------------------------------------- + + +def _labels(state): + return [a.label for a in button.menu_actions(state)] + + +def test_menu_off_offers_starting_and_shutdown(): + assert _labels(STATE_OFF) == ["Rettung starten", "Migration starten", "Herunterfahren"] + + +def test_menu_in_recovery_offers_stopping_refreshing_and_shutdown(): + assert _labels(STATE_RECOVERY) == ["Station aus", "Anzeige auffrischen", + "Herunterfahren"] + + +def test_menu_in_migration_offers_the_detected_target(): + assert _labels(STATE_MIGRATION) == ["Freigabe: gu10_rgb", "Station aus", + "Anzeige auffrischen", "Herunterfahren"] + + +def test_menu_in_migration_without_a_detected_target_offers_no_release(): + """Ohne last_seen gibt es nichts freizugeben - der Eintrag faellt ganz weg, statt + eine Freigabe ins Leere anzubieten (die das CLI ohnehin abweisen wuerde).""" + assert "Freigabe: " not in " ".join(_labels(STATE_MIGRATION_BLIND)) + assert _labels(STATE_MIGRATION_BLIND) == ["Station aus", "Anzeige auffrischen", + "Herunterfahren"] + + +def test_only_the_refresh_entry_skips_the_confirmation(): + """Zusage: JEDE veraendernde Auswahl bekommt eine Rueckfrage. "Anzeige auffrischen" + veraendert nichts und braucht keine.""" + for state in (STATE_OFF, STATE_RECOVERY, STATE_MIGRATION): + for action in button.menu_actions(state): + assert action.confirm is (action.label != "Anzeige auffrischen"), action.label + + +def test_actions_run_through_the_station_cli(): + """Die Aktionen gehen ueber das vorhandene CLI, nicht durch nachgebaute Logik - und + das Herunterfahren als einzige an systemd selbst.""" + actions = button.CliActions(cli="/usr/local/bin/ninlight-station") + assert actions.argv_for(button.menu_actions(STATE_OFF)[0]) == [ + "/usr/local/bin/ninlight-station", "recovery", "on"] + assert actions.argv_for(button.action_shutdown()) == ["systemctl", "poweroff"] + + +# --- Bedienung, getrieben durch die echte Hauptschleife ------------------------------ + + +def test_a_short_press_opens_the_menu(): + rec = _drive([_short(1.0)], 4.0) + assert rec.argvs() == [] + t, view = rec.first_view("menu") + assert view["hero"] == "> Rettung starten" + assert view["active"] is True + # Spuerbar, aber bedienbar: das Menue steht binnen eines Schleifentakts nach dem + # Loslassen (die Verzoegerung am Panel kommt aus dessen eigenem Takt, siehe + # display.UI_POLL_INTERVAL_S). + assert 1.2 <= t <= 1.3 + + +def test_a_short_press_steps_to_the_next_entry(): + rec = _drive([_short(1.0), _short(2.0)], 4.0) + assert rec.argvs() == [] + assert rec.views()[-1][1]["hero"] == "> Migration starten" + + +def test_a_short_press_wraps_around_the_last_entry(): + rec = _drive([_short(1.0), _short(2.0), _short(3.0), _short(4.0)], 6.0) + assert rec.views()[-1][1]["hero"] == "> Rettung starten" + + +def test_holding_one_and_a_half_seconds_selects_the_entry(): + rec = _drive([_short(1.0), (2.0, 4.0)], 6.0) + t, view = rec.first_view("confirm") + assert view["hero"] == "Rettung starten" + assert 3.5 <= t <= 3.6 + # Die Auswahl allein fuehrt noch nichts aus - dafuer ist die Rueckfrage da. + assert rec.argvs() == [] + + +def test_holding_in_the_confirmation_executes_the_entry(): + rec = _drive([_short(1.0), (2.0, 4.0), (5.0, 7.0)], 9.0) + assert rec.argvs() == [["/usr/local/bin/ninlight-station", "recovery", "on"]] + assert 6.5 <= rec.runs()[0][0] <= 6.6 + + +def test_a_short_press_in_the_confirmation_cancels_and_returns_to_the_menu(): + rec = _drive([_short(1.0), (2.0, 4.0), _short(5.0)], 8.0) + assert rec.argvs() == [] + assert rec.views()[-1][1]["view"] == "menu" + + +def test_the_refresh_entry_runs_without_a_confirmation(): + rec = _drive([_short(1.0), _short(2.0), (3.0, 5.0)], 7.0, state=STATE_RECOVERY) + assert rec.argvs() == [["/usr/local/bin/ninlight-station", "display", "test"]] + assert "confirm" not in rec.kinds() + + +def test_starting_the_migration_always_listens(): + """Sicherheitsregel, nicht verhandelbar: der Migrations-Zugangspunkt hat ein + oeffentlich bekanntes Passwort - ein Tastendruck, der ihn im Automatikmodus + hochfaehrt, wuerde jede alte Klyqa-Lampe in Funkreichweite ungefragt migrieren, auch + fremde.""" + rec = _drive([_short(1.0), _short(2.0), (3.0, 5.0), (6.0, 8.0)], 10.0) + assert rec.argvs() == [["/usr/local/bin/ninlight-station", "migrate", "on", + "--listen"]] + assert "--listen" in rec.argvs()[0] + + +def test_the_cancel_fires_at_three_seconds_while_the_button_is_still_held(): + """Der Abbruch feuert BEI 3 s, nicht erst beim Loslassen - der Bediener soll die + Wirkung am Panel sehen, waehrend er noch haelt.""" + rec = _drive([(1.0, 8.0)], 10.0) + assert rec.argvs() == [["/usr/local/bin/ninlight-station", "off"]] + t = rec.runs()[0][0] + assert 4.0 <= t <= 4.1 + # ... und danach laeuft am Panel der Countdown der zweiten Stufe. + later = [v for vt, v in rec.views() if vt > t] + assert any("Herunterfahren in" in v.get("hero", "") for v in later) + + +def test_releasing_between_three_and_ten_seconds_does_not_shut_down(): + rec = _drive([(1.0, 7.0)], 12.0) + assert rec.argvs() == [["/usr/local/bin/ninlight-station", "off"]] + assert ["systemctl", "poweroff"] not in rec.argvs() + + +def test_holding_ten_seconds_shuts_down(): + rec = _drive([(1.0, 12.0)], 14.0) + assert rec.argvs() == [["/usr/local/bin/ninlight-station", "off"], + ["systemctl", "poweroff"]] + assert 11.0 <= rec.runs()[1][0] <= 11.1 + + +def test_the_shutdown_countdown_is_visible_before_it_happens(): + """Das Herunterfahren ist nur ueber eine SICHTBARE Zustandsaenderung hinweg + erreichbar - der Countdown muss also vorher auf dem Panel gestanden haben.""" + rec = _drive([(1.0, 12.0)], 14.0) + heroes = [v.get("hero", "") for t, v in rec.views() if t < 11.0] + assert "Herunterfahren in 6 s" in heroes + assert "Herunterfahren in 1 s" in heroes + + +def test_a_blocking_action_does_not_break_the_hold_gesture(): + """Der Abbruch-Aufruf blockiert die Schleife sekundenlang (Profilumschaltung). Danach + wird derselbe PEGEL gegen dieselbe Uhr gemessen - die zweite Stufe muss trotzdem + erreichbar bleiben.""" + rec = _drive([(1.0, 12.0)], 14.0, action_duration_s=2.0) + assert rec.argvs()[-1] == ["systemctl", "poweroff"] + + +def test_the_waiting_screen_is_written_before_the_slow_call(): + rec = _drive([_short(1.0), _short(2.0), (3.0, 5.0)], 8.0, state=STATE_RECOVERY, + action_duration_s=4.0) + kinds = [k for _t, k, _p in rec.events] + run_at = kinds.index("run") + assert rec.events[run_at - 1][2].get("view") == "busy" + + +def test_the_menu_closes_after_twenty_seconds_without_input(): + rec = _drive([_short(1.0)], 26.0) + assert rec.kinds()[-1] == "idle" + closed_at = [t for t, v in rec.views() if v.get("view") == "idle" and t > 2.0][0] + assert 21.2 <= closed_at <= 21.4 + + +def test_the_confirmation_expires_after_ten_seconds_as_a_cancel(): + rec = _drive([_short(1.0), (2.0, 4.0)], 18.0) + assert rec.argvs() == [] + assert rec.kinds()[-1] == "idle" + expired_at = [t for t, v in rec.views() if v.get("view") == "idle" and t > 4.0][0] + assert 13.5 <= expired_at <= 13.7 + + +def test_an_expired_confirmation_leaves_nothing_pending(): + """Der Abbruch muss die vorgemerkte Aktion auch wirklich vergessen - eine Aktion, die + nach dem Verfall noch in der Maschine liegt, ist genau das, was eine Rueckfrage + verhindern soll.""" + ui = button.ButtonUI(0.0, read_state=lambda: dict(STATE_OFF)) + ui.update(1.0, True) + ui.update(1.2, False) # kurzer Druck: Menue auf + ui.update(2.0, True) + ui.update(3.6, True) # 1,6 s gehalten: Eintrag gewaehlt -> Rueckfrage + ui.update(3.8, False) + assert ui.screen == "confirm" and ui.pending is not None + + ui.update(3.6 + button.CONFIRM_TIMEOUT_S + 0.1, False) + + assert ui.screen == "idle" + assert ui.pending is None + + +def test_a_failed_action_says_so_on_the_panel(): + rec = _drive([_short(1.0), (2.0, 4.0), (5.0, 7.0)], 9.0, rc=1) + assert any(v.get("view") == "result" and v.get("title") == "FEHLER" + for _t, v in rec.views()) + + +def test_nothing_happens_without_any_press(): + rec = _drive([], 30.0) + assert rec.argvs() == [] + assert rec.kinds() == ["idle"] + + +# --- Bedienzustandsdatei ------------------------------------------------------------ + + +def test_the_ui_file_is_written_atomically_with_0644(tmp_path): + """Dasselbe Muster und dieselben Rechte wie status.json/control.json - ohne 0644 + kann der Anzeigedienst die Datei nicht lesen, ohne das Umbenennen sieht er sie halb + geschrieben.""" + path = tmp_path / "run" / "ui.json" + writer = button.UiWriter(path) + view = {"view": "menu", "title": "MENUE", "hero": "> Station aus", "details": [], + "active": True} + # Unter einer strengen umask geschrieben: die Rechte muessen ausdruecklich gesetzt + # werden, nicht zufaellig aus der umask des Dienstes fallen - sonst laege die Zusage + # "der Anzeigedienst kann die Datei lesen" an einer Umgebungsvariablen des Systems. + old_umask = os.umask(0o077) + try: + assert writer.write(view) is True + finally: + os.umask(old_umask) + assert json.loads(path.read_text()) == view + assert oct(os.stat(path).st_mode & 0o777) == "0o644" + # Keine Temp-Datei zurueckgelassen (sie wuerde sonst mit jedem Neustart ein weiteres + # Bruchstueck im RuntimeDirectory hinterlassen). + assert [p.name for p in path.parent.iterdir()] == ["ui.json"] + + +def test_the_ui_file_is_only_written_on_change(tmp_path): + writer = button.UiWriter(tmp_path / "ui.json") + view = {"view": "idle", "active": False} + assert writer.write(view) is True + assert writer.write(dict(view)) is False + + +def test_an_unwritable_ui_file_is_survived_and_logged_once(tmp_path, caplog): + """Ein Fehler am Taster darf niemals eine laufende Migration stoeren - und eine Zeile + je Schleifentakt wuerde das Journal fluten.""" + blocker = tmp_path / "blocker" + blocker.write_text("kein Verzeichnis") + writer = button.UiWriter(blocker / "ui.json") + with caplog.at_level("WARNING", logger="ninlight-button"): + assert writer.write({"view": "menu", "active": True}) is False + assert writer.write({"view": "confirm", "active": True}) is False + assert len(caplog.records) == 1 + + +# --- Der Taster ist nirgends Voraussetzung ------------------------------------------ + + +def test_missing_gpiozero_logs_exactly_one_line_and_exits_zero(monkeypatch, caplog): + """Dieselbe Zusage wie bei der Anzeige (dort seit fuenf Fixrunden): kein Taster -> + GENAU EINE Protokollzeile und sys.exit(0), kein Fehler - sonst startet systemd + (Restart=on-failure) den Dienst endlos neu und flutet das Journal.""" + monkeypatch.setitem(sys.modules, "gpiozero", None) + with caplog.at_level("INFO", logger="ninlight-button"): + with pytest.raises(SystemExit) as excinfo: + button.main() + assert excinfo.value.code == 0 + assert len(caplog.records) == 1 + assert "gpiozero" in caplog.records[0].getMessage() + + +def test_a_busy_pin_is_treated_like_a_missing_button(monkeypatch, caplog): + """"GPIO busy" kommt je nach Pin-Factory als gpiozero-Fehler, als roher lgpio.error + oder als OSError - alles davon heisst "kein Taster", nicht "abstuerzen".""" + class _Busy(Exception): + pass + + fake = types.SimpleNamespace( + Button=lambda *a, **k: (_ for _ in ()).throw(_Busy("GPIO busy"))) + monkeypatch.setitem(sys.modules, "gpiozero", fake) + with caplog.at_level("INFO", logger="ninlight-button"): + with pytest.raises(SystemExit) as excinfo: + button.main() + assert excinfo.value.code == 0 + assert len(caplog.records) == 1 + + +def test_a_failing_cli_call_never_escapes(monkeypatch): + """subprocess-Fehler (Binary fehlt, Timeout) duerfen den Dienst nicht beenden.""" + def boom(argv, check=False, timeout=None): + raise OSError("kein CLI da") + + actions = button.CliActions(runner=boom) + assert actions.run(button.action_cancel()) is False + + def slow(argv, check=False, timeout=None): + raise subprocess.TimeoutExpired(argv, timeout) + + assert button.CliActions(runner=slow).run(button.action_cancel()) is False + + +def test_an_unreadable_station_state_keeps_the_menu_closed(caplog): + """Menueeintraege haengen am Stationszustand. Ist der nicht zu ermitteln, bleibt das + Menue zu, statt einen falschen Zustand zu behaupten - und der Dienst laeuft weiter.""" + ui = button.ButtonUI(0.0, read_state=lambda: (_ for _ in ()).throw(RuntimeError("weg"))) + with caplog.at_level("WARNING", logger="ninlight-button"): + ui.update(0.0, True) + assert ui.update(0.2, False) is None + assert ui.view(0.3) == {"view": "idle", "active": False} diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 57951da..6f83f15 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -1402,14 +1402,20 @@ class _RecordingPanel: self.awake_s += end_t - self._awake_since -def _drive_daemon(state_at, duration_s: float) -> _RecordingPanel: +def _drive_daemon(state_at, duration_s: float, ui_at=None) -> _RecordingPanel: """Laesst das echte display.run_daemon() `duration_s` simulierte Sekunden laufen. - `state_at(t)` liefert den Zustand, den build_state() zum Zeitpunkt t liefern wuerde.""" + `state_at(t)` liefert den Zustand, den build_state() zum Zeitpunkt t liefern wuerde, + `ui_at(t)` den Bedienzustand, den read_ui() dort liefern wuerde (Vorgabe: keiner - + dann verhaelt sich die Schleife wie ohne Taster).""" clock = _FakeClock(duration_s) panel = _RecordingPanel(clock) + if ui_at is None: + def ui_at(t): + return {} try: display.run_daemon("V3", clock=clock, panel=panel, - read_state=lambda: state_at(clock.monotonic())) + read_state=lambda: state_at(clock.monotonic()), + read_ui=lambda: ui_at(clock.monotonic())) except _StopDaemon: pass panel._finish(clock.monotonic()) @@ -1570,3 +1576,114 @@ def test_planner_draws_fully_on_mode_change(): planner.record_draw(0.0, ["RETTUNG"], "recovery", was_full=True) assert planner.decide(5.0, ["MIGRATION"], "migration") == "full" + + +# --- Kopplung an den Tasterdienst ---------------------------------------------------- +# +# Der Tasterdienst (tools/station/button/button.py) fasst den SPI-Bus NICHT an - zwei +# Prozesse auf denselben GPIO-/SPI-Leitungen ergeben "lgpio.error: 'GPIO busy'". Er +# schreibt stattdessen seinen Bedienzustand in eine Datei, die hier gelesen und gezeichnet +# wird. Geprueft wird deshalb beides: dass ein Bedienzustand das Statusbild ersetzt, und +# dass die ECHTE Schleife ihn schnell genug aufgreift, um bedienbar zu sein. + +_MENU_UI = {"view": "menu", "title": "MENUE", "hero": "> Station aus", + "details": ["Anzeige auffrischen", "Herunterfahren"], "active": True} + + +def test_the_ui_file_lives_outside_the_stations_runtime_directory(): + """Nicht in /run/ninlight-station: systemd raeumt dieses RuntimeDirectory beim + regulaeren Stop der Hauptunit auf - also ausgerechnet im Zustand "Station aus", in dem + das Menue "Rettung starten"/"Migration starten" anbietet.""" + assert str(display.UI_FILE) == "/run/ninlight-button/ui.json" + assert not str(display.UI_FILE).startswith("/run/ninlight-station/") + + +def test_a_running_operation_replaces_the_status_screen(): + lines = render.lines({"mode": "recovery", "ssid": "ninlight-recovery", "served": 7, + "recent": [], "ui": _MENU_UI}) + assert lines == ["MENUE", "> Station aus", "Anzeige auffrischen", "Herunterfahren"] + assert not any("ninlight-recovery" in l for l in lines) + + +def test_the_menu_uses_the_same_roles_as_the_status_screen(): + """Kopfbalken, grosse Zeile, Beiwerk - damit Schriftgroessen, Breitenkuerzung und + unterer Rand dieselben bleiben wie im Statusbild.""" + roles = [role for _text, role in render.blocks({"mode": "recovery", "ui": _MENU_UI})] + assert roles[:2] == [render.ROLE_HEADER, render.ROLE_HERO] + assert set(roles[2:]) <= {render.ROLE_DETAIL} + + +def test_the_menu_is_actually_drawn_on_the_panel(): + image = render.compose({"mode": "recovery", "ssid": "ninlight-recovery", + "ui": _MENU_UI}) + reference = render.compose({"mode": "recovery", "ssid": "ninlight-recovery"}) + assert list(image.getdata()) != list(reference.getdata()) + assert _ink_box(image) is not None + + +def test_a_broken_ui_file_does_not_break_the_display(): + """Die Datei kommt aus einem fremden Prozess, der abgestuerzt, aelter oder neuer sein + kann - genau wie status.json darf sie die Anzeige nie zum Absturz bringen.""" + lines = render.lines({"mode": "recovery", "ui": {"view": "menu", "title": 17, + "hero": None, "details": "nope"}}) + assert lines == ["Bedienung"] + render.compose({"mode": "recovery", "ui": {"view": "menu"}}) + + +def test_read_ui_ignores_a_missing_broken_or_idle_file(tmp_path): + path = tmp_path / "ui.json" + assert display.read_ui(path) == {} + path.write_text("{kein json") + assert display.read_ui(path) == {} + path.write_text(json.dumps({"view": "idle", "active": False})) + assert display.read_ui(path) == {} + # Auch ein Menue, das sich selbst nicht mehr als laufend meldet, zaehlt nicht - so + # kann ein stehengebliebener Bedienzustand das Statusbild nicht dauerhaft verdecken. + path.write_text(json.dumps({"view": "menu", "active": False})) + assert display.read_ui(path) == {} + path.write_text(json.dumps(_MENU_UI)) + assert display.read_ui(path)["hero"] == "> Station aus" + + +def test_the_daemon_shows_an_operation_within_half_a_second(): + """Solange eine Bedienung laeuft, muss die Anzeige schneller nachsehen als in ihrem + ueblichen 5-Sekunden-Takt - sonst ist das Menue unbenutzbar.""" + panel = _drive_daemon(lambda t: {"mode": "recovery", "served": 0, "recent": []}, + duration_s=20.0, + ui_at=lambda t: _MENU_UI if t >= 3.0 else {}) + drawn = [t for t, name in panel.calls if name in ("partial", "full", "wake-full") + and t >= 3.0] + assert drawn, "das Menue wurde ueberhaupt nicht gezeichnet" + assert drawn[0] <= 3.5 + + +def test_a_keypress_costs_the_panel_no_full_refresh(): + """Alterungsschutz: Vollaufbauten sind teuer, und das Panel soll nicht bei jedem + Tastendruck einen bekommen. Der Modus bleibt ueber eine Bedienung hinweg derselbe, + also kommt jeder Bedienschritt als Teilaufbau.""" + views = [dict(_MENU_UI, hero="> Eintrag {}".format(i)) for i in range(4)] + + def ui_at(t): + if t < 3.0: + return {} + return views[min(int((t - 3.0) // 2), len(views) - 1)] + + panel = _drive_daemon(lambda t: {"mode": "recovery", "served": 0, "recent": []}, + duration_s=12.0, ui_at=ui_at) + assert len(_times(panel, "partial")) >= 4 + # Genau einer: der allererste Bildaufbau nach dem Start. + assert _times(panel, "full") == [0.0] + + +def test_the_expensive_station_state_is_still_only_read_every_five_seconds(): + """Der schnelle Takt gilt NUR fuer die kleine Bedienzustandsdatei. build_state() + forkt fuer "systemctl is-active" - das zehnmal so oft zu tun waere auf einem + ARMv6-Kern spuerbar und voellig unnoetig.""" + calls = [] + + def state_at(t): + calls.append(t) + return {"mode": "recovery", "served": 0, "recent": []} + + _drive_daemon(state_at, duration_s=10.0) + assert calls == [0.0, 5.0, 10.0] From 24b37ee58f9e576d6de92a7fdf5ff253ac9d7a37 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 22:34:56 +0200 Subject: [PATCH 137/150] feat(station): Tasterdienst per deploy.sh/setup_pi.sh ausliefern --- tools/station/README.md | 7 +- tools/station/deploy.sh | 25 ++++- tools/station/ninlight-station | 3 + tools/station/setup_pi.sh | 64 ++++++++++-- tools/station/tests/test_deploy.sh | 56 +++++++++++ tools/station/tests/test_setup_pi.sh | 143 ++++++++++++++++++++++++++- 6 files changed, 286 insertions(+), 12 deletions(-) diff --git a/tools/station/README.md b/tools/station/README.md index 4a61299..77d3563 100644 --- a/tools/station/README.md +++ b/tools/station/README.md @@ -56,9 +56,10 @@ Bedienzustand nach `/run/ninlight-button/ui.json`; der Anzeigedienst liest ihn u zeichnet. Ausgeführt wird ausschließlich über das CLI. Dienst und Unit liegen in `tools/station/button/` bzw. -`tools/station/ninlight-button.service` — das Ausliefern und Einrichten (`deploy.sh`, -`setup_pi.sh`) kommt in einem eigenen Durchgang, bis dahin installiert der Aufsetzweg den -Tasterdienst noch nicht. +`tools/station/ninlight-button.service`. `setup_pi.sh` installiert, aktiviert und startet +den Tasterdienst wie den Anzeigedienst; `deploy.sh` liefert `button/` bei jedem Deploy mit +aus. Ein fehlgeschlagenes Aktivieren/Starten ist dabei nur eine Warnung — eine Station +ohne Taster muss vollständig benutzbar bleiben. SSID und Passwort stehen in `/etc/ninlight-station.conf` auf dem Pi und müssen zu den Build-Parametern `NINLIGHT_RECOVERY_SSID` und `NINLIGHT_RECOVERY_PASSWORD` aus diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index d5502ba..6de09fb 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -203,13 +203,17 @@ echo "== uebertragen ==" # -e: rsync soll fuer den Transport zum Pi denselben ssh wie oben benutzen. rsync -a -c --delete -e "$SSH_BIN" "$STAGE/" "$HOST:/tmp/ninlight-images/" rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" \ - "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HOST:/tmp/" + "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HERE/ninlight-button.service" \ + "$HOST:/tmp/" # Das Anzeigeverzeichnis als Ganzes (render.py, display.py, epdconfig.py, die beiden # uebernommenen Panel-Treiber und HERKUNFT.md - der Herkunftsnachweis gehoert mit auf # den Pi, nicht nur ins Repo). --delete gilt hier nur fuer die fluechtige Ablage unter # /tmp; __pycache__ bleibt draussen, sonst landen die .pyc dieses Rechners (andere # Python-Version als auf dem Pi) im Zielverzeichnis und bleiben dort liegen. rsync -a -c --delete --exclude=__pycache__ -e "$SSH_BIN" "$HERE/display/" "$HOST:/tmp/ninlight-display/" +# Das Tasterverzeichnis genauso wie das Anzeigeverzeichnis: als Ganzes, ohne +# __pycache__ dieses Rechners. +rsync -a -c --delete --exclude=__pycache__ -e "$SSH_BIN" "$HERE/button/" "$HOST:/tmp/ninlight-button/" # Pfade als Variablen mit Default: so kann ein Test die Installation gegen # ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen lassen. "$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" "$MIGRATE_COMPLETE" <<'REMOTE' @@ -220,6 +224,7 @@ ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} DISPLAY_UNIT_PATH=${NINLIGHT_DISPLAY_UNIT:-/etc/systemd/system/ninlight-display.service} +BUTTON_UNIT_PATH=${NINLIGHT_BUTTON_UNIT:-/etc/systemd/system/ninlight-button.service} sudo install -d -m 0755 "$ROOT_DIR/images" if [ "$FULL_SYNC" -eq 1 ]; then @@ -347,8 +352,13 @@ sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH" sudo install -d -m 0755 "$ROOT_DIR/display" sudo rsync -a -c --delete /tmp/ninlight-display/ "$ROOT_DIR/display/" sudo install -m 0644 /tmp/ninlight-display.service "$DISPLAY_UNIT_PATH" +# Taster: dieselbe Begruendung wie bei der Anzeige - button/ ist reiner Code aus dem +# Repo, es gibt keinen Bestand, der einen Deploy ueberleben muesste. +sudo install -d -m 0755 "$ROOT_DIR/button" +sudo rsync -a -c --delete /tmp/ninlight-button/ "$ROOT_DIR/button/" +sudo install -m 0644 /tmp/ninlight-button.service "$BUTTON_UNIT_PATH" rm -rf /tmp/ninlight-images /tmp/server.py /tmp/products.py /tmp/ninlight-station /tmp/ninlight-station.service \ - /tmp/ninlight-display /tmp/ninlight-display.service + /tmp/ninlight-display /tmp/ninlight-display.service /tmp/ninlight-button /tmp/ninlight-button.service sudo systemctl daemon-reload if systemctl is-active --quiet ninlight-station; then if sudo systemctl restart ninlight-station; then @@ -374,6 +384,17 @@ if systemctl is-active --quiet ninlight-display; then else echo " Anzeigedienst laeuft nicht" fi +# Taster: dieselbe weiche Behandlung wie die Anzeige (siehe deren Begruendung oben) - er +# ist nirgends Voraussetzung fuer eine Migration, ein Deploy darf daran nicht scheitern. +if systemctl is-active --quiet ninlight-button; then + if sudo systemctl restart ninlight-button; then + echo " Tasterdienst neu gestartet" + else + echo " Warnung: Tasterdienst lief, Neustart ist fehlgeschlagen - journalctl -u ninlight-button" >&2 + fi +else + echo " Tasterdienst laeuft nicht" +fi REMOTE echo "fertig." diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 5d1f805..25f40fa 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -921,6 +921,9 @@ usage() { echo " display off Anzeigedienst dauerhaft stoppen und das Panel leeren. 'on'/'off'" >&2 echo " der Station brauchen das nicht - der Anzeigedienst laeuft" >&2 echo " unabhaengig davon durch und zeichnet den Zustand selbst." >&2 + echo "" >&2 + echo " Taster (GPIO23): kurzer Druck oeffnet am Geraet ein Menue ueber genau diese" >&2 + echo " Unterbefehle, 3 s gehalten brechen eine laufende Migration/Rettung ab." >&2 } _cmd=${1:-} diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index a504572..cb58833 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -90,15 +90,20 @@ echo "== Server, CLI und Units installieren ==" "$SSH_BIN" "$HOST" bash -s <<'REMOTE' set -e STAGE_DIR=${NINLIGHT_STAGE_DIR:-/tmp} -rm -rf "$STAGE_DIR/ninlight-display" +rm -rf "$STAGE_DIR/ninlight-display" "$STAGE_DIR/ninlight-button" REMOTE "$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" \ - "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HOST:$STAGE_DIR/" + "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HERE/ninlight-button.service" \ + "$HOST:$STAGE_DIR/" # Das Anzeigeverzeichnis als Ganzes: render.py, display.py, epdconfig.py, die beiden # uebernommenen Panel-Treiber und HERKUNFT.md (der Herkunftsnachweis gehoert mit auf den # Pi). "scp -r" statt rsync, weil setup_pi.sh - anders als deploy.sh - ausdruecklich nur # ssh/scp voraussetzt; __pycache__ wird auf der Gegenseite weggeraeumt (unten). "$SCP_BIN" -q -r "$HERE/display" "$HOST:$STAGE_DIR/ninlight-display" +# Das Tasterverzeichnis genauso: als Ganzes, mit demselben "vorher aufraeumen"-Schutz +# gegen ein "scp -r" auf ein bereits vorhandenes Ziel (siehe Kommentar oben zu +# ninlight-display). +"$SCP_BIN" -q -r "$HERE/button" "$HOST:$STAGE_DIR/ninlight-button" # Pfade als Variablen mit Default (wie in deploy.sh): so kann ein Test die # Installation gegen ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen # lassen. @@ -109,6 +114,7 @@ ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} DISPLAY_UNIT_PATH=${NINLIGHT_DISPLAY_UNIT:-/etc/systemd/system/ninlight-display.service} +BUTTON_UNIT_PATH=${NINLIGHT_BUTTON_UNIT:-/etc/systemd/system/ninlight-button.service} # Die Ablage wird in JEDEM Fall abgeraeumt, auch wenn dieser Block vorzeitig aussteigt # (Task 6b F2, N1). Vorher stand das "rm -rf" nur am Blockende und wurde beim Abbruch @@ -117,11 +123,12 @@ DISPLAY_UNIT_PATH=${NINLIGHT_DISPLAY_UNIT:-/etc/systemd/system/ninlight-display. cleanup_stage() { rm -rf "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" \ "$STAGE_DIR/ninlight-station.service" "$STAGE_DIR/ninlight-display.service" \ - "$STAGE_DIR/ninlight-display" + "$STAGE_DIR/ninlight-display" "$STAGE_DIR/ninlight-button.service" \ + "$STAGE_DIR/ninlight-button" } trap cleanup_stage EXIT -rm -rf "$STAGE_DIR/ninlight-display/__pycache__" +rm -rf "$STAGE_DIR/ninlight-display/__pycache__" "$STAGE_DIR/ninlight-button/__pycache__" # Harte Vorbedingung, und zwar VOR dem ersten "sudo install" (Task 6b F2, N1): ohne # display.py in der Ablage hat das scp nicht geliefert, was es sollte - abgebrochen, # falsch verschachtelt oder gar nicht gelaufen. Die Installationsschleife weiter unten @@ -134,11 +141,19 @@ rm -rf "$STAGE_DIR/ninlight-display/__pycache__" # den diese Pruefung gedacht ist, log das Skript den Bediener an. Jetzt stimmt der Satz. if [ ! -f "$STAGE_DIR/ninlight-display/display.py" ]; then echo "Fehler: $STAGE_DIR/ninlight-display/display.py fehlt - die Uebertragung des Anzeigeverzeichnisses ist fehlgeschlagen." >&2 - echo " Nichts installiert: $ROOT_DIR, $BIN_PATH und beide Units sind unveraendert. Ablage aufgeraeumt, Lauf einfach wiederholen." >&2 + echo " Nichts installiert: $ROOT_DIR, $BIN_PATH und alle drei Units sind unveraendert. Ablage aufgeraeumt, Lauf einfach wiederholen." >&2 + exit 1 +fi +# Dieselbe harte Vorbedingung wie bei display.py, aus demselben Grund: ohne button.py in +# der Ablage hat das scp nicht geliefert, was es sollte, und die Installationsschleife +# unten wuerde das stillschweigend als "nichts zu tun" verbuchen. +if [ ! -f "$STAGE_DIR/ninlight-button/button.py" ]; then + echo "Fehler: $STAGE_DIR/ninlight-button/button.py fehlt - die Uebertragung des Tasterverzeichnisses ist fehlgeschlagen." >&2 + echo " Nichts installiert: $ROOT_DIR, $BIN_PATH und alle drei Units sind unveraendert. Ablage aufgeraeumt, Lauf einfach wiederholen." >&2 exit 1 fi -sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images" "$ROOT_DIR/display" +sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images" "$ROOT_DIR/display" "$ROOT_DIR/button" sudo install -m 0755 "$STAGE_DIR/server.py" "$ROOT_DIR/server.py" sudo install -m 0644 "$STAGE_DIR/products.py" "$ROOT_DIR/products.py" sudo install -m 0755 "$STAGE_DIR/ninlight-station" "$BIN_PATH" @@ -165,6 +180,25 @@ for f in "$ROOT_DIR"/display/*; do fi done sudo install -m 0644 "$STAGE_DIR/ninlight-display.service" "$DISPLAY_UNIT_PATH" +# Taster: dieselbe Behandlung wie die Anzeige - button.py ist ausfuehrbar (die Unit ruft +# es zwar ueber /usr/bin/python3 auf, von Hand pruefen laesst es sich trotzdem), der Rest +# (aktuell nichts weiter) waere importierter Code. +for f in "$STAGE_DIR"/ninlight-button/*; do + [ -f "$f" ] || continue + case "$(basename "$f")" in + button.py) sudo install -m 0755 "$f" "$ROOT_DIR/button/$(basename "$f")" ;; + *) sudo install -m 0644 "$f" "$ROOT_DIR/button/$(basename "$f")" ;; + esac +done +# Dasselbe Argument wie bei display/: button/ ist reiner Code aus dem Repo. +for f in "$ROOT_DIR"/button/*; do + [ -e "$f" ] || continue + if [ ! -e "$STAGE_DIR/ninlight-button/$(basename "$f")" ]; then + sudo rm -rf "$f" + echo " veraltete Datei entfernt: button/$(basename "$f")" + fi +done +sudo install -m 0644 "$STAGE_DIR/ninlight-button.service" "$BUTTON_UNIT_PATH" # Das Aufraeumen der Ablage macht der EXIT-Trap oben - auf beiden Wegen aus diesem Block. sudo systemctl daemon-reload echo " installiert" @@ -335,6 +369,21 @@ sudo systemctl restart ninlight-display.service >/dev/null 2>&1 || \ echo " Anzeigedienst aktiviert" REMOTE +echo "== Tasterdienst aktivieren ==" +# Dieselbe weiche Behandlung wie die Anzeige, aus demselben Grund: der Taster ist +# nirgends Voraussetzung. Fehlt gpiozero, fehlt der Taster oder ist GPIO23 belegt, +# beendet sich button.py still mit 0 (siehe dessen Modul-Docstring) - ein +# fehlgeschlagenes enable/start darf setup_pi.sh trotzdem nicht abbrechen, denn eine +# Station ohne Taster muss vollstaendig benutzbar bleiben. +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e +sudo systemctl enable ninlight-button.service >/dev/null 2>&1 || \ + echo " Warnung: 'systemctl enable ninlight-button' fehlgeschlagen - der Taster startet nach einem Neustart des Pi nicht von allein mit" >&2 +sudo systemctl restart ninlight-button.service >/dev/null 2>&1 || \ + echo " Warnung: 'systemctl restart ninlight-button' fehlgeschlagen - journalctl -u ninlight-button" >&2 +echo " Tasterdienst aktiviert" +REMOTE + echo "== NetworkManager-Profile ==" # Zwei Dinge, die man an einer echten Lampenstation nicht falsch machen darf: # - /etc/ninlight-station.conf ist 0600 root:root (das Passwort soll nicht @@ -486,4 +535,7 @@ Naechste Schritte: tools/station/deploy.sh Images ausliefern ssh ninlight-station on Station einschalten ssh ninlight-station off wieder ausschalten (nicht vergessen) + +Der Taster auf GPIO23 ist mit eingerichtet: kurzer Druck oeffnet das Menue am Geraet, +3 s gehalten brechen eine laufende Migration/Rettung ab. EOF diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index 9a66534..db37bcd 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -52,6 +52,7 @@ setup_sandbox() { echo "# dummy" >"$sandbox/tools/station/ninlight-station" echo "# dummy" >"$sandbox/tools/station/ninlight-station.service" echo "# dummy" >"$sandbox/tools/station/ninlight-display.service" + echo "# dummy" >"$sandbox/tools/station/ninlight-button.service" # display/ wird ECHT kopiert, nicht durch Attrappen ersetzt: die Zusage aus Task 6b # ist, dass die beiden uebernommenen Panel-Treiber UND der Herkunftsnachweis # HERKUNFT.md mit auf den Pi gehen. Mit selbst erfundenen Dateinamen im Sandbox-Repo @@ -62,6 +63,11 @@ setup_sandbox() { # darf nicht mit uebertragen werden. mkdir -p "$sandbox/tools/station/display/__pycache__" echo "pyc-muell" >"$sandbox/tools/station/display/__pycache__/render.cpython-999.pyc" + # button/ ebenso echt kopiert, gleiches Argument wie bei display/ oben. + cp -R "$STATION_DIR/button" "$sandbox/tools/station/button" + rm -rf "$sandbox/tools/station/button/__pycache__" + mkdir -p "$sandbox/tools/station/button/__pycache__" + echo "pyc-muell" >"$sandbox/tools/station/button/__pycache__/button.cpython-999.pyc" # Fake nl_mkimage.py --verify: verhaelt sich wie das echte Tool (eine Zeile # " " auf stdout bei Erfolg, Exit 0; eine Zeile auf @@ -137,6 +143,13 @@ case "\$*" in *) exit 0 ;; esac ;; + *ninlight-button*) + case "\$1" in + is-active) exit "\${FAKE_BUTTON_ACTIVE_EXIT:-1}" ;; + restart) exit "\${FAKE_BUTTON_RESTART_EXIT:-0}" ;; + *) exit 0 ;; + esac + ;; esac case "\$1" in is-active) exit "\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-1}" ;; @@ -171,6 +184,7 @@ run_deploy() { NINLIGHT_STATION_BIN="$SANDBOX_STATE/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ NINLIGHT_DISPLAY_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-display.service" \ + NINLIGHT_BUTTON_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-button.service" \ NINLIGHT_SIGN_DIR="$sign_dir" \ "$sandbox/tools/station/deploy.sh" "$@" } @@ -1056,6 +1070,48 @@ RC=$? set -e [ "$RC" -ne 0 ] && pass "Exit-Code != 0 beim Hauptdienst" || fail "Exit-Code 0 - ein toter Hauptdienst ginge unbemerkt durch: $OUT" +echo +echo "=== Szenario 26: deploy.sh liefert das Tasterverzeichnis und die Taster-Unit mit aus ===" +SANDBOX="$TMPROOT/s26"; STUBBIN="$TMPROOT/bin26"; LOG="$TMPROOT/s26.log"; SANDBOX_STATE="$TMPROOT/s26-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +# Bestand auf dem "Pi": eine Datei, die es im Repo nicht mehr gibt - muss beim Deploy +# verschwinden (dasselbe Argument wie bei display/ in Szenario 22). +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/button" +echo "alt" >"$SANDBOX_STATE/opt/ninlight-station/button/veraltet.py" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +BTN="$SANDBOX_STATE/opt/ninlight-station/button" +[ -f "$BTN/button.py" ] && pass "button/button.py ausgeliefert" || fail "button/button.py fehlt unter $BTN" +[ -f "$SANDBOX_STATE/etc/systemd/system/ninlight-button.service" ] && pass "ninlight-button.service ausgeliefert" || fail "Taster-Unit fehlt - sie waere weiter toter Text im Repo" +[ ! -f "$BTN/veraltet.py" ] && pass "im Repo geloeschte Datei verschwindet auch auf dem Pi (--delete)" || fail "veraltet.py blieb im Zielverzeichnis liegen" +[ ! -d "$BTN/__pycache__" ] && pass "kein __pycache__ dieses Rechners auf dem Pi gelandet" || fail "__pycache__ wurde mit uebertragen: $(ls "$BTN/__pycache__")" +echo "$OUT" | grep -q "Tasterdienst laeuft nicht" && pass "Deploy meldet, dass der Tasterdienst nicht laeuft" || fail "Ausgabe erwaehnt den Tasterdienst nicht: $OUT" +[ ! -d /tmp/ninlight-button ] && pass "die fluechtige Ablage /tmp/ninlight-button wurde aufgeraeumt" || fail "/tmp/ninlight-button blieb liegen" + +echo +echo "=== Szenario 27: ein fehlgeschlagener Neustart des TASTERdienstes ist kein Deploy-Fehler ===" +SANDBOX="$TMPROOT/s27"; STUBBIN="$TMPROOT/bin27"; LOG="$TMPROOT/s27.log"; SANDBOX_STATE="$TMPROOT/s27-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(FAKE_BUTTON_ACTIVE_EXIT=0 FAKE_BUTTON_RESTART_EXIT=1 run_deploy "$SANDBOX" pi-test 2>"$TMPROOT/s27.err") +RC=$? +set -e +ERR=$(cat "$TMPROOT/s27.err") +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlgeschlagenem Taster-Neustart" || fail "Exit-Code $RC - der Deploy wurde wegen des Tasters rot: $ERR" +echo "$ERR" | grep -q "Warnung" && pass "Warnung (keine Fehlermeldung) auf stderr" || fail "keine Warnung auf stderr: $ERR" +[ -f "$SANDBOX_STATE/opt/ninlight-station/server.py" ] && pass "der Deploy selbst lief vollstaendig durch" || fail "server.py fehlt - der Deploy brach ab" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index d644c28..d9c4e62 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -58,6 +58,7 @@ echo "STUB scp \$*" >>"$log" for a in "\$@"; do case "\$a" in *ninlight-display) [ -n "\${FAKE_SCP_SKIP_DIR:-}" ] && exit 0 ;; + *ninlight-button) [ -n "\${FAKE_SCP_SKIP_BUTTON_DIR:-}" ] && exit 0 ;; esac done args=() @@ -141,7 +142,19 @@ EOF #!/usr/bin/env bash echo "STUB systemctl \$*" >>"$log" # Testschalter: laesst "enable"/"restart" scheitern (beides darf setup_pi.sh nur -# warnen lassen, nie abbrechen). +# warnen lassen, nie abbrechen). Der Taster hat eigene Schalter (FAKE_BUTTON_*), damit +# ein Szenario NUR ihn scheitern lassen kann, ohne die Anzeige mitzureissen - sonst +# liesse sich nicht zeigen, dass ein fehlgeschlagenes enable/start des Tasters fuer +# sich genommen keinen Abbruch ausloest. +case "\$*" in + *ninlight-button*) + case "\$1" in + enable) exit "\${FAKE_BUTTON_ENABLE_EXIT:-0}" ;; + restart) exit "\${FAKE_BUTTON_RESTART_EXIT:-0}" ;; + *) exit 0 ;; + esac + ;; +esac case "\$1" in enable) exit "\${FAKE_SYSTEMCTL_ENABLE_EXIT:-0}" ;; restart) exit "\${FAKE_SYSTEMCTL_RESTART_EXIT:-0}" ;; @@ -202,6 +215,7 @@ run_setup() { NINLIGHT_STATION_BIN="$state/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$state/etc/systemd/system/ninlight-station.service" \ NINLIGHT_DISPLAY_UNIT="$state/etc/systemd/system/ninlight-display.service" \ + NINLIGHT_BUTTON_UNIT="$state/etc/systemd/system/ninlight-button.service" \ NINLIGHT_NM_DIR="$state/etc/NetworkManager/system-connections" \ NINLIGHT_BOOT_CONFIG_NEW="$state/boot/firmware/config.txt" \ NINLIGHT_BOOT_CONFIG_OLD="$state/boot/config.txt" \ @@ -906,6 +920,133 @@ grep -Fqx "dtparam=spi=on" "$CONFIG27" && pass "die Zeile wurde trotzdem aktivie [ "$(cat "${CONFIG27}.bak-ninlight")" = "urspruenglicher-originalzustand" ] && pass "die bestehende Sicherung blieb unangetastet" || fail "die Sicherung wurde ueberschrieben: $(cat "${CONFIG27}.bak-ninlight")" echo "$OUT" | grep -q "existiert bereits - wird nicht ueberschrieben" && pass "Ausgabe erwaehnt, dass die Sicherung nicht ueberschrieben wurde" || fail "keine entsprechende Meldung: $OUT" +echo +echo "=== Szenario 28 (Task 8b): Taster wird ausgeliefert, installiert und aktiviert ===" +STATE="$TMPROOT/s28-state"; STUBBIN="$TMPROOT/bin28"; LOG="$TMPROOT/s28.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +BTN28="$STATE/opt/ninlight-station/button" +[ -f "$BTN28/button.py" ] && pass "button/button.py ausgeliefert" || fail "button/button.py fehlt unter $BTN28" +[ -x "$BTN28/button.py" ] && pass "button.py ist ausfuehrbar" || fail "button.py ist nicht ausfuehrbar" +[ ! -d "$BTN28/__pycache__" ] && pass "kein __pycache__ auf dem Pi gelandet" || fail "__pycache__ wurde mitkopiert" +[ -f "$STATE/etc/systemd/system/ninlight-button.service" ] && pass "ninlight-button.service installiert" || fail "Taster-Unit fehlt - sie waere weiter toter Text im Repo" +grep -Fq "systemctl enable ninlight-button.service" "$LOG" && pass "Tasterdienst wurde aktiviert" || fail "kein 'systemctl enable ninlight-button' im Log" +grep -Fq "systemctl restart ninlight-button.service" "$LOG" && pass "Tasterdienst wurde gestartet" || fail "kein 'systemctl restart ninlight-button' im Log" +# Die Station selbst bleibt weiterhin aus - derselbe Sicherheitsfehler waere hier +# genauso schwerwiegend wie in Szenario 5. +grep -Fq "systemctl enable ninlight-station" "$LOG" && fail "setup_pi.sh aktiviert den Stationsdienst" || pass "Stationsdienst bleibt deaktiviert" + +echo +echo "=== Szenario 29 (Task 8b): Reststand in der Ablage fuehrt beim Taster nicht zu einer stillen Nullinstallation ===" +# Dasselbe Muster wie Szenario 11 fuer die Anzeige, hier fuer button/: ein +# liegengebliebenes "…/ninlight-button/" laesst ein "scp -r" hineinkopieren statt +# darauf, die Installationsschleife faende dann nichts. +STATE="$TMPROOT/s29-state"; STUBBIN="$TMPROOT/bin29"; LOG="$TMPROOT/s29.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$STATE/tmp/ninlight-button" +echo "rest" >"$STATE/tmp/ninlight-button/uebrig.py" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +BTN29="$STATE/opt/ninlight-station/button" +[ -f "$BTN29/button.py" ] && pass "button.py trotz Reststand installiert" || fail "Nullinstallation: $BTN29 enthaelt $(ls -A "$BTN29" 2>/dev/null | tr '\n' ' ')" +[ ! -d "$BTN29/button" ] && pass "kein verschachteltes button/button/ entstanden" || fail "verschachteltes Verzeichnis angelegt" +[ ! -f "$BTN29/uebrig.py" ] && pass "der Reststand des Vorlaufs wurde nicht mitinstalliert" || fail "uebrig.py aus dem Reststand landete auf dem Pi" + +echo +echo "=== Szenario 30 (Task 8b): scheitert die Uebertragung des Tastercodes, wird laut abgebrochen ===" +STATE="$TMPROOT/s30-state"; STUBBIN="$TMPROOT/bin30"; LOG="$TMPROOT/s30.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(FAKE_SCP_SKIP_BUTTON_DIR=1 run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0" || fail "Exit-Code 0 trotz fehlgeschlagener Uebertragung: $OUT" +echo "$OUT" | grep -q "button.py fehlt" && pass "Fehlermeldung nennt die fehlende Datei" || fail "Fehlermeldung fehlt: $OUT" +grep -Fq "systemctl enable ninlight-button" "$LOG" && fail "der Tasterdienst wurde trotz fehlender Dateien aktiviert" || pass "kein aktivierter Tasterdienst ohne Dateien" +for F in "$STATE/opt/ninlight-station/server.py" "$STATE/opt/ninlight-station/products.py" \ + "$STATE/usr/local/bin/ninlight-station" \ + "$STATE/etc/systemd/system/ninlight-station.service" \ + "$STATE/etc/systemd/system/ninlight-display.service" \ + "$STATE/etc/systemd/system/ninlight-button.service"; do + [ ! -e "$F" ] && pass "wirklich nicht installiert: $(basename "$F")" || fail "die Meldung luegt - $F liegt auf dem Pi" +done +REST30=$(find "$STATE/tmp" -mindepth 1 2>/dev/null | head -n5) +[ -z "$REST30" ] && pass "die Ablage wurde auch im Fehlerfall aufgeraeumt" || fail "Reststand in der Ablage: $REST30" + +echo +echo "=== Szenario 31 (Task 8b): eine im Repo geloeschte Datei verschwindet auch aus button/ auf dem Pi ===" +STATE="$TMPROOT/s31-state"; STUBBIN="$TMPROOT/bin31"; LOG="$TMPROOT/s31.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$STATE/opt/ninlight-station/button" +echo "alt" >"$STATE/opt/ninlight-station/button/veraltet.py" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ ! -f "$STATE/opt/ninlight-station/button/veraltet.py" ] && pass "veraltete Datei entfernt" || fail "veraltet.py blieb liegen" +[ -f "$STATE/opt/ninlight-station/button/button.py" ] && pass "der aktuelle Code ist trotzdem da" || fail "button.py fehlt" + +echo +echo "=== Szenario 32 (Task 8b): ein fehlgeschlagenes enable/restart NUR des Tasterdienstes warnt und bricht nicht ab ===" +# Eigene Schalter (FAKE_BUTTON_*), damit die Anzeige dabei unbeteiligt bleibt - das +# zeigt, dass der Taster fuer sich genommen keine Voraussetzung ist. +STATE="$TMPROOT/s32-state"; STUBBIN="$TMPROOT/bin32"; LOG="$TMPROOT/s32.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(FAKE_BUTTON_ENABLE_EXIT=1 FAKE_BUTTON_RESTART_EXIT=1 run_setup "$STATE" "$STUBBIN" 2>"$TMPROOT/s32.err") +RC=$? +set -e +ERR=$(cat "$TMPROOT/s32.err") +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlgeschlagenem enable/restart des Tasters" || fail "Exit-Code $RC: $ERR" +echo "$ERR" | grep -q "systemctl enable ninlight-button" && pass "Warnung zu 'enable' auf stderr" || fail "keine enable-Warnung: $ERR" +echo "$ERR" | grep -q "systemctl restart ninlight-button" && pass "Warnung zu 'restart' auf stderr" || fail "keine restart-Warnung: $ERR" +grep -Fq "systemctl enable ninlight-display.service" "$LOG" && pass "die Anzeige wurde trotzdem aktiviert - der Taster reisst sie nicht mit" || fail "die Anzeige-Aktivierung blieb aus" +[ -f "$STATE/etc/NetworkManager/system-connections/qconnex-firmware.nmconnection" ] && pass "der Lauf ging danach normal weiter (Profile angelegt)" || fail "der Lauf brach nach der Warnung ab" + +echo +echo "=== Szenario 33 (Task 8b): ein zweiter Lauf ist ein echtes No-Op ===" +# Idempotenz ist die Kopfzusage von setup_pi.sh (siehe Kommentar am Dateianfang) - hier +# konkret fuer den neu hinzugekommenen Taster geprueft: ein zweiter Lauf auf einem +# bereits eingerichteten Pi darf button/ und dessen Unit nicht veraendern. +STATE="$TMPROOT/s33-state"; STUBBIN="$TMPROOT/bin33"; LOG="$TMPROOT/s33.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +run_setup "$STATE" "$STUBBIN" >/dev/null 2>&1 +RC1=$? +set -e +[ "$RC1" -eq 0 ] || fail "erster Lauf schon fehlgeschlagen (rc=$RC1)" +SUM_BEFORE=$(find "$STATE/opt/ninlight-station/button" "$STATE/etc/systemd/system/ninlight-button.service" -type f -exec cksum {} \; | sort) +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC2=$? +set -e +[ "$RC2" -eq 0 ] && pass "zweiter Lauf: Exit-Code 0" || fail "zweiter Lauf: Exit-Code $RC2, Ausgabe: $OUT" +SUM_AFTER=$(find "$STATE/opt/ninlight-station/button" "$STATE/etc/systemd/system/ninlight-button.service" -type f -exec cksum {} \; | sort) +[ "$SUM_BEFORE" = "$SUM_AFTER" ] && pass "button/ und die Taster-Unit sind nach dem zweiten Lauf byte-identisch" || fail "der zweite Lauf hat etwas an button/ veraendert" +grep -Fq "systemctl enable ninlight-button.service" "$LOG" && pass "der zweite Lauf aktiviert den Tasterdienst erneut (idempotent, kein Fehler)" || fail "kein erneutes 'systemctl enable ninlight-button' im zweiten Lauf" +echo "$OUT" | grep -q "veraltete Datei entfernt: button/" && fail "der zweite Lauf meldet eine veraltete Datei, obwohl nichts geloescht wurde" || pass "keine falsche 'veraltete Datei entfernt'-Meldung im zweiten Lauf" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From 6f0137908c173741f0834fc849c4313568dde163 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Mon, 14 Sep 2026 23:59:38 +0200 Subject: [PATCH 138/150] fix: SM2135EH-Weissreste beim Wechsel zurueck auf Farbe loeschen Der RGB-Zweig von nl_sm2135eh_set() schrieb Stromregister, Kanalbyte und die drei RGB-Werte, liess SM_ADDR_WY (0xC5) aber unangetastet. Der Chip behielt die zuletzt gesetzte Weiss-Mischung, und beim direkten Wechsel Weiss -> Farbe (ohne Zwischenschritt ueber dunkel) schaelte sich der Weissanteil beim Ausblenden der Farbe wieder frei - an einer migrierten GU10 RGB gemessen. Das ist dieselbe Klasse Fehler wie in 00d5a4c fuer die Gegenrichtung behoben (WY-Zweig schrieb nie die RGB-Register), nur bislang unentdeckt in dieser Richtung. Der RGB-Zweig schreibt jetzt zusaetzlich WY mit 0/0 - ein zweiter Bus-Transfer pro Aktualisierung, symmetrisch zum WY-Zweig, der ebenfalls zwei Transfers braucht. --- src/drivers/nl_sm2135eh.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/drivers/nl_sm2135eh.c b/src/drivers/nl_sm2135eh.c index 5a14e07..50081d2 100644 --- a/src/drivers/nl_sm2135eh.c +++ b/src/drivers/nl_sm2135eh.c @@ -87,6 +87,15 @@ void nl_sm2135eh_set(const uint8_t ch[NL_CH_COUNT]) uint8_t chan = dark ? SM_CHANNEL_STBY : 0x00u; uint8_t d[6] = {SM_ADDR_CURRENT, s_current, chan, ch[NL_CH_B], ch[NL_CH_G], ch[NL_CH_R]}; sm_send(d, 6); + /* The WY data registers are cleared here too, even though this is the RGB branch: + the chip keeps whatever white value was last written, and a direct white -> colour + change never passes through the all-dark frame above. The stale white then bleeds + through as soon as the colour dims - the same defect as the colour -> white case + below (fixed in 00d5a4c, 2026-09-13), just mirrored: that fix made the WY branch + also write the (always-zero) RGB registers, this makes the RGB branch also write + the (always-zero) WY registers. Both directions had it; only one was caught first. */ + uint8_t w[3] = {SM_ADDR_WY, 0, 0}; + sm_send(w, 3); } else { /* The RGB data registers are written along with the channel select, even though they are always zero here: the chip keeps whatever colour was last written, and a direct From 6325ce073f011fdde02052fa2bc661955e1ba874 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 00:24:29 +0200 Subject: [PATCH 139/150] fix(station): Signiervariante aus Produkt-ID und Version bestimmen Eine Entwicklungs-G95 meldete sich am 2026-09-15 mit einer Produkt-ID OHNE "-dev", aber einer Version MIT "-dev". products.py leitete die Variante ausschliesslich aus der Produkt-ID ab, die Lampe galt als Produktionsgeraet, bekam das produktionssigniertes Image und lehnte es ab - ihr Bootloader prueft gegen den Entwicklungsschluessel. Die Variante ist damit keine Eigenschaft der Produkt-ID, sondern der Anfrage. PRODUCTS bildet nur noch auf Geraetetyp und Anzeigename ab; beide Schreibweisen der Produkt-ID fuehren auf denselben Eintrag. classify_variant() in parse_request() wertet Produkt-ID UND Version aus: "-dev" an einer der beiden Stellen bedeutet Entwicklungsgeraet, in der Version nur als ganzes Wort ("-devel"/"-dev2" zaehlen nicht). Damit eine Fehlklassifikation nicht wieder erst an der Lampe auffaellt, fuehren status.json (last_seen und jeder recent-Eintrag) und das Panel die Quelle der Einstufung mit: "g95_cw (dev/Ver)", "dev/ID", "dev/ID+Ver" oder "prod". Tests: die drei am Geraet mitgeschnittenen Anfragen plus die vierte denkbare Kombination, die Wortgrenze in der Version, zwei Ende-zu-Ende-Tests im Server (dev-Image statt prod-Image) und die Panel-Beschriftung. --- tools/station/display/render.py | 22 +++++++- tools/station/products.py | 71 +++++++++++++++++++----- tools/station/server.py | 21 +++++--- tools/station/tests/test_products.py | 81 +++++++++++++++++++++++----- tools/station/tests/test_render.py | 20 +++++++ tools/station/tests/test_server.py | 34 ++++++++++++ 6 files changed, 216 insertions(+), 33 deletions(-) diff --git a/tools/station/display/render.py b/tools/station/display/render.py index ae678a8..fb7dc83 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -167,6 +167,25 @@ PHASE_LABELS = {"auto": "AUTO", "listen": "LISTEN"} ARMED_LABEL = "FREIGEGEBEN" ARMED_DETAIL_PREFIX = "Freigabe: " +# Woraus die Station die Signiervariante abgeleitet hat (products.classify_variant): +# "-dev" kann in der Produkt-ID stehen, in der Version, oder in beiden. Die Quelle steht +# mit auf dem Panel, weil genau hier am 2026-09-15 ein falsch signiertes Image +# ausgeliefert wurde: wer vor der Freigabe sieht, dass "dev" nur aus der Version kommt +# (oder dass "prod" bedeutet: nirgends ein "-dev"), kann eine Fehlklassifikation abfangen, +# bevor die Lampe das Image ablehnt. Kurzformen, weil die Beiwerkzeile schmal ist. +VARIANT_SOURCE_LABELS = {"both": "ID+Ver", "product_id": "ID", "version": "Ver"} + + +def _variant_label(last_seen: dict) -> str: + """"dev/Ver", "dev/ID", "dev/ID+Ver", "prod" - die Quelle nur, wo sie etwas aussagt. + Bei "prod" gibt es keine ("none"), und eine aeltere Serverfassung ohne das Feld + liefert wie bisher nur die Variante.""" + variant = str(last_seen.get("variant") or "?") + label = VARIANT_SOURCE_LABELS.get(last_seen.get("variant_source")) + if not label: + return variant + return variant + "/" + label + def _hhmm(ts: object) -> str: """"2026-09-13T13:04:02Z" -> "13:04". Alles andere (fehlt, falsch geformt, aus einer @@ -275,7 +294,8 @@ def blocks(state: dict) -> list[tuple[str, str]]: target = last_seen.get("target") variant = last_seen.get("variant") if target or variant: - out.append((f"{target or '?'} ({variant or '?'})", ROLE_DETAIL)) + out.append(("{} ({})".format(target or "?", _variant_label(last_seen)), + ROLE_DETAIL)) lamp_version = last_seen.get("lamp_version") if lamp_version: out.append((f"FW: {lamp_version}", ROLE_DETAIL)) diff --git a/tools/station/products.py b/tools/station/products.py index 1d0d8c3..110d516 100644 --- a/tools/station/products.py +++ b/tools/station/products.py @@ -1,17 +1,37 @@ """Produkt-IDs der alten Klyqa-Firmware und ihre Zuordnung zu ninlight-Zielen. Vollstaendig uebernommen aus qcl_product_info.c der alten Firmware -(~/workspace/qcx-w600-firmware). Der "-dev"-Anhang entscheidet zugleich ueber den -Signierschluessel: Lampen mit Dev-Produkt-ID akzeptieren nur den Entwicklungs-, -Produktionslampen nur den Produktionsschluessel. +(~/workspace/qcx-w600-firmware). + +Die Variante (dev/prod) entscheidet ueber den Signierschluessel: Lampen mit +Entwicklungs-Bootloader akzeptieren nur den Entwicklungs-, Produktionslampen nur den +Produktionsschluessel. Sie ist aber KEINE Eigenschaft der Produkt-ID, sondern eine +Eigenschaft der ANFRAGE - das ist der Befund vom 2026-09-15 an echter Hardware: + + /qcx_firmware_-klyqa.lighting.cw-ww.g95_V1.1.1.2357-dev_ota_light.img + +Dieselbe Entwicklungs-G95, aber das "-dev" steht nur in der VERSION, nicht in der +Produkt-ID. Die Station hielt sie damit fuer eine Produktionslampe, lieferte das +produktionssignierte Image aus, und die Lampe lehnte es ab. Die Machbarkeitsprobe im +September hatte eine andere Entwicklungs-G95 mit "-dev" an BEIDEN Stellen gesehen, eine +Produktionslampe an keiner - alle drei Formen kommen also vor. + +Regel seither: "-dev" an einer der beiden Stellen (Produkt-ID oder Version) bedeutet +Entwicklungsgeraet. PRODUCTS bildet nur noch auf Geraetetyp und Anzeigename ab, beide +Schreibweisen der Produkt-ID fuehren auf denselben Eintrag; die Variante ermittelt +parse_request() aus beiden Teilen und meldet in .variant_source mit, WORAUS sie stammt +("both", "product_id", "version", "none"), damit eine Fehlklassifikation vor der Freigabe +am Panel und in status.json sichtbar ist statt erst an der Lampe. """ import re from collections import namedtuple -Product = namedtuple("Product", "product_id target variant name") -ParsedRequest = namedtuple("ParsedRequest", "product_id lamp_version product") +Product = namedtuple("Product", "product_id target name") +ParsedRequest = namedtuple( + "ParsedRequest", "product_id lamp_version product variant variant_source") -# (Produkt-ID ohne "@", Ziel, Anzeigename) - die Dev-Variante wird darunter erzeugt. +# (Produkt-ID ohne "@", Ziel, Anzeigename) - die "-dev"-Schreibweise wird darunter +# als zweiter Schluessel auf denselben Eintrag erzeugt. _BASE = [ ("klyqa.lighting.rgb-cw-ww.e27", "e27_rgb", "Klyqa E27 RGB CW/WW"), ("klyqa.lighting.rgb-cw-ww.e14", "e14_rgb", "Klyqa E14 RGB CW/WW"), @@ -25,6 +45,9 @@ _BASE = [ _UNSAFE = re.compile(r"[^0-9A-Za-z+\-_.]") # Die Produkt-ID darf kein "_" enthalten - daran trennen wir Version und ID. _PATH = re.compile(r"^/qcx_firmware_([^/_]+)_([^/_]+)_ota_light\.img$") +# "-dev" in der Version, aber nicht als Anfang eines laengeren Wortes ("-devel", "-dev2"): +# erlaubt sind danach nur Ende oder ein Trennzeichen wie "." (z.B. "V1.1-dev.3"). +_DEV_IN_VERSION = re.compile(r"-dev(?![0-9A-Za-z])") def websafe(product_id): @@ -36,30 +59,54 @@ def websafe(product_id): def _build(): t = {} for pid, target, name in _BASE: - for suffix, variant in (("", "prod"), ("-dev", "dev")): - key = websafe("@" + pid + suffix) - t[key] = Product(key, target, variant, name) + base_key = websafe("@" + pid) + product = Product(base_key, target, name) + # Beide Schreibweisen zeigen auf DENSELBEN Eintrag - "-dev" an der Produkt-ID + # sagt seit dem Hardwarebefund nichts mehr ueber das Ziel, nur noch (mit) etwas + # ueber die Variante, und die ermittelt parse_request(). + t[base_key] = product + t[websafe("@" + pid + "-dev")] = product # Das EVK hat nur eine Produktions-ID. Im Dev-Build meldet es sich laut # qcl_product_info.c als "@klyqa.lighting.rgb-cw-ww.e27-dev" - die EVK-Zeile ist dort # auskommentiert und durch die E27 ersetzt. Ein dev-signiertes EVK ist damit von einer # dev-signierten E27 RGB NICHT unterscheidbar und bekommt deren Image. Das EVK wird # ohnehin seriell geflasht; die Zeile steht nur der Vollstaendigkeit halber hier. + # Meldet es eine "-dev"-Version, gilt auch hier die Regel: dann wird evk_dev.img + # gesucht (und mangels Datei sauber mit 404 abgelehnt), nicht das falsch signierte. key = websafe("@klyqa.lighting.w600.evk1.2") - t[key] = Product(key, "evk", "prod", "W600 EVK v1.2") + t[key] = Product(key, "evk", "W600 EVK v1.2") return t PRODUCTS = _build() +def classify_variant(product_id, lamp_version): + """(variant, variant_source) aus Produkt-ID UND Version. + + "-dev" an einer der beiden Stellen bedeutet Entwicklungsgeraet; variant_source sagt, + an welcher es stand: "both", "product_id", "version" oder - bei prod - "none".""" + in_id = product_id.endswith("-dev") + in_version = bool(_DEV_IN_VERSION.search(lamp_version)) + if in_id and in_version: + return "dev", "both" + if in_id: + return "dev", "product_id" + if in_version: + return "dev", "version" + return "prod", "none" + + def parse_request(path): """Zerlegt einen angefragten Pfad. Gibt None zurueck, wenn er nicht dem Muster der alten Firmware entspricht, sonst ParsedRequest - dessen .product ist None, wenn die - Produkt-ID unbekannt ist. Unbekannt heisst 404, nicht raten.""" + Produkt-ID unbekannt ist. Unbekannt heisst 404, nicht raten. .variant/.variant_source + stehen auch bei unbekannter Produkt-ID, sie haengen nicht an der Tabelle.""" m = _PATH.match(path) if not m: return None pid, version = m.group(1), m.group(2) if pid != websafe(pid) or "/" in pid or ".." in pid.split("."): return None - return ParsedRequest(pid, version, PRODUCTS.get(pid)) + variant, source = classify_variant(pid, version) + return ParsedRequest(pid, version, PRODUCTS.get(pid), variant, source) diff --git a/tools/station/server.py b/tools/station/server.py index f9658fb..11d9cb1 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -259,7 +259,8 @@ class Station: def record_migration(self, ip: str, target: str | None, variant: str | None, product_id: str, lamp_version: str, product_name: str | None, - version: str, nbytes: int, status: str) -> None: + version: str, nbytes: int, status: str, + variant_source: str | None = None) -> None: """Protokolliert eine Migrationsanfrage. Anders als record() oben wird hier auch dann ein Eintrag angelegt (Status "listen"/"unknown"/"notfound"), wenn nichts ausgeliefert wurde - das ist der ganze Zweck der Horchphase. last_seen wird nur bei @@ -269,13 +270,15 @@ class Station: if status == "ok": self.served += 1 entry = {"ts": now_iso(), "ip": ip, "target": target, "variant": variant, + "variant_source": variant_source, "product_id": product_id, "lamp_version": lamp_version, "version": version, "bytes": nbytes, "status": status} self.recent.append(entry) del self.recent[:-RECENT_MAX] if target is not None: self.last_seen = {"product_id": product_id, "target": target, - "variant": variant, "product_name": product_name, + "variant": variant, "variant_source": variant_source, + "product_name": product_name, "lamp_version": lamp_version, "ip": ip, "ts": entry["ts"]} self.write_state() @@ -395,7 +398,7 @@ class Handler(BaseHTTPRequestHandler): self.reject(404, "not found") self.log(404, 0, started) return - pid, lamp_version, product = parsed + pid, lamp_version, product, variant, variant_source = parsed ip = self.client_address[0] if product is None: self.station.record_migration(ip, None, None, pid, lamp_version, None, "?", 0, @@ -404,7 +407,11 @@ class Handler(BaseHTTPRequestHandler): self.log(404, 0, started) return - target, variant, name = product.target, product.variant, product.name + # variant/variant_source kommen aus parse_request(), nicht mehr aus der + # Produkttabelle: das "-dev" kann in der Produkt-ID stehen, in der Version, oder in + # beiden (Hardwarebefund 2026-09-15, siehe products.py). Das entscheidet, welches + # der beiden unterschiedlich signierten Images ausgeliefert wird. + target, name = product.target, product.name version = self.station.image_version(target, variant) # claim_delivery() liest die Phase UND verbraucht eine armed-Freigabe atomar unter # station.lock (Fixrunde 1, C1) - zwischen dem Lesen und dem Verbrauchen darf keine @@ -415,7 +422,7 @@ class Handler(BaseHTTPRequestHandler): # last_seen wird trotzdem gesetzt - das ist der ganze Zweck der Horchphase: # die Anzeige soll zeigen, welches Modell da ist, auch wenn nichts rausgeht. self.station.record_migration(ip, target, variant, pid, lamp_version, name, - version, 0, "listen") + version, 0, "listen", variant_source) self.reject(404, "listening only") self.log(404, 0, started) return @@ -427,7 +434,7 @@ class Handler(BaseHTTPRequestHandler): # dass es gar kein Image gibt - das darf nicht auf Kosten der Freigabe gehen. self.station.restore_armed(target) self.station.record_migration(ip, target, variant, pid, lamp_version, name, - version, 0, "notfound") + version, 0, "notfound", variant_source) self.reject(404, "no image for this device") self.log(404, 0, started) return @@ -440,7 +447,7 @@ class Handler(BaseHTTPRequestHandler): # (das ist der Punkt von C1) - bei einem Abbruch wird das hier zurueckgenommen. self.station.restore_armed(target) self.station.record_migration(ip, target, variant, pid, lamp_version, name, version, - sent, status) + sent, status, variant_source) def _serve_file(self, path: Path, started: float) -> tuple[int, str]: """Sendet `path` mit Range-Unterstuetzung; liefert (gesendete Bytes, Status). diff --git a/tools/station/tests/test_products.py b/tools/station/tests/test_products.py index 6a41ea3..c6c7a28 100644 --- a/tools/station/tests/test_products.py +++ b/tools/station/tests/test_products.py @@ -7,43 +7,98 @@ import products # noqa: E402 def test_table_is_complete(): - """Acht Geraete mal zwei Varianten, minus das EVK, das keine eigene Dev-ID hat.""" + """Sieben Geraete in beiden Schreibweisen der Produkt-ID, plus das EVK, das nur eine + Produktions-ID hat.""" assert len(products.PRODUCTS) == 15 targets = {p.target for p in products.PRODUCTS.values()} assert targets == {"evk", "e27_rgb", "e14_rgb", "gu10_rgb", "e27_cw", "e14_cw", "gu10_cw", "g95_cw"} -def test_production_id_maps_to_prod_variant(): +def test_table_carries_no_variant(): + """Die Variante ist eine Eigenschaft der Anfrage, nicht der Produkt-ID - sie darf gar + nicht mehr aus der Tabelle zu holen sein (Hardwarebefund 2026-09-15).""" + assert not hasattr(products.PRODUCTS["-klyqa.lighting.cw-ww.g95"], "variant") + assert "variant" not in products.Product._fields + + +def test_both_spellings_map_to_the_same_device(): + with_dev = products.PRODUCTS["-klyqa.lighting.cw-ww.g95-dev"] + without = products.PRODUCTS["-klyqa.lighting.cw-ww.g95"] + assert with_dev.target == without.target == "g95_cw" + assert with_dev.name == without.name == "Klyqa G95 Vintage CW/WW" + + +def test_production_id_maps_to_device_and_name(): p = products.PRODUCTS["-klyqa.lighting.rgb-cw-ww.gu10"] - assert (p.target, p.variant) == ("gu10_rgb", "prod") + assert p.target == "gu10_rgb" assert p.name == "Klyqa GU10 RGB CW/WW" -def test_dev_suffix_selects_dev_variant(): - p = products.PRODUCTS["-klyqa.lighting.cw-ww.g95-dev"] - assert (p.target, p.variant) == ("g95_cw", "dev") - - -def test_parses_real_request_from_g95(): - """Am 2026-09-13 an einer echten G95 protokolliert.""" +def test_parses_real_request_from_g95_dev_in_both_places(): + """Am 2026-09-13 an einer echten Entwicklungs-G95 protokolliert: "-dev" in Produkt-ID + UND Version.""" r = products.parse_request( "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1.1.1.1800-dev_ota_light.img") assert r is not None assert r.product_id == "-klyqa.lighting.cw-ww.g95-dev" assert r.lamp_version == "V1.1.1.1800-dev" assert r.product.target == "g95_cw" - assert r.product.variant == "dev" + assert (r.variant, r.variant_source) == ("dev", "both") -def test_parses_real_request_from_gu10(): +def test_parses_real_request_from_g95_dev_only_in_version(): + """Am 2026-09-15 an einer echten Entwicklungs-G95 protokolliert - die Anfrage, an der + die Station bisher scheiterte: Produkt-ID OHNE "-dev", Version MIT. Sie muss trotzdem + als Entwicklungsgeraet gelten, sonst geht das produktionssignierte Image raus.""" + r = products.parse_request( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95_V1.1.1.2357-dev_ota_light.img") + assert r is not None + assert r.product_id == "-klyqa.lighting.cw-ww.g95" + assert r.lamp_version == "V1.1.1.2357-dev" + assert r.product.target == "g95_cw" + assert (r.variant, r.variant_source) == ("dev", "version") + + +def test_parses_real_request_from_gu10_without_any_dev(): + """Die erfolgreich migrierte Produktionslampe: nirgends ein "-dev".""" r = products.parse_request( "/qcx_firmware_-klyqa.lighting.rgb-cw-ww.gu10_V1.1.0.1138_ota_light.img") assert r.product.target == "gu10_rgb" - assert r.product.variant == "prod" + assert (r.variant, r.variant_source) == ("prod", "none") assert r.lamp_version == "V1.1.0.1138" +def test_dev_only_in_product_id_is_dev(): + """Die vierte denkbare Kombination - so noch nicht am Geraet gesehen, aber die + Machbarkeitsprobe zeigt, dass die Produkt-ID das "-dev" tragen kann, und nichts + garantiert, dass die Version es dann auch tut.""" + r = products.parse_request( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1.1.1.1800_ota_light.img") + assert r.product.target == "g95_cw" + assert (r.variant, r.variant_source) == ("dev", "product_id") + + +def test_dev_must_be_a_whole_word_in_the_version(): + """"-devel"/"-dev2" sind kein Entwicklungskennzeichen - sonst wuerde eine + Produktionslampe mit so einer Version das dev-signierte Image bekommen.""" + for version in ("V1.1.1.2357-devel", "V1.1.1.2357-dev2"): + r = products.parse_request( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95_" + version + "_ota_light.img") + assert (r.variant, r.variant_source) == ("prod", "none"), version + # ... ein "-dev" vor einem Trennzeichen dagegen schon. + r = products.parse_request( + "/qcx_firmware_-klyqa.lighting.cw-ww.g95_V1.1-dev.3_ota_light.img") + assert (r.variant, r.variant_source) == ("dev", "version") + + +def test_variant_is_classified_even_for_unknown_product_ids(): + r = products.parse_request( + "/qcx_firmware_-acme.lightbulb.xxl_V9.9-dev_ota_light.img") + assert r.product is None + assert (r.variant, r.variant_source) == ("dev", "version") + + def test_unknown_product_id_parses_but_has_no_product(): r = products.parse_request( "/qcx_firmware_-acme.lightbulb.xxl_V9.9_ota_light.img") diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 6f83f15..cef9132 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -33,6 +33,26 @@ def test_migration_shows_detected_lamp(): assert "13:04" in text +def test_migration_shows_where_the_dev_classification_came_from(): + """Der Hardwarebefund vom 2026-09-15: die Station lieferte ein falsch signiertes Image + aus, ohne dass am Panel etwas darauf hindeutete. Steht die Quelle der Einstufung + daneben, sieht der Bediener VOR der Freigabe, worauf sie beruht.""" + def variant_line(last_seen): + state = {"mode": "migration", "phase": "listen", "served": 0, "recent": [], + "ssid": "qconnex-firmware", "last_seen": last_seen} + return [l for l in render.lines(state) if l.startswith("g95_cw (")][0] + + base = {"product_name": "Klyqa G95 Vintage CW/WW", "target": "g95_cw", + "lamp_version": "V1.1.1.2357-dev"} + assert variant_line(dict(base, variant="dev", variant_source="version")) == "g95_cw (dev/Ver)" + assert variant_line(dict(base, variant="dev", variant_source="product_id")) == "g95_cw (dev/ID)" + assert variant_line(dict(base, variant="dev", variant_source="both")) == "g95_cw (dev/ID+Ver)" + # prod hat keine Quelle - da gibt es nirgends ein "-dev", das man anzeigen koennte. + assert variant_line(dict(base, variant="prod", variant_source="none")) == "g95_cw (prod)" + # Eine aeltere Serverfassung ohne das Feld bleibt darstellbar. + assert variant_line(dict(base, variant="dev")) == "g95_cw (dev)" + + def test_recovery_mode_without_traffic(): lines = render.lines({"mode": "recovery", "ssid": "ninlight-recovery", "served": 0, "recent": []}) diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index 18aa001..89c53b6 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -594,6 +594,40 @@ def test_migration_auto_serves_known_product(migration_server): assert st["last_seen"]["variant"] == "dev" +def test_migration_dev_only_in_version_serves_the_dev_image(migration_server): + """Der Hardwarebefund vom 2026-09-15: Produkt-ID OHNE "-dev", Version MIT. Frueher + ging hier das produktionssignierte Image raus (200, und die Lampe lehnte es ab) - + jetzt muss das dev-signierte gehen, und status.json muss die Quelle mitfuehren.""" + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"DEV" * 1000) + (imgdir / "migrate" / "g95_cw_prod.img").write_bytes(b"PROD" * 1000) + r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95_V1.1.1.2357-dev_ota_light.img") + assert r.status == 200 + assert r.body == b"DEV" * 1000 + st = srv.status(lambda s: s.get("last_seen") is not None) + assert st["last_seen"]["target"] == "g95_cw" + assert st["last_seen"]["variant"] == "dev" + assert st["last_seen"]["variant_source"] == "version" + assert st["recent"][-1]["variant"] == "dev" + assert st["recent"][-1]["variant_source"] == "version" + + +def test_migration_production_lamp_still_gets_the_prod_image(migration_server): + """Die erfolgreich migrierte GU10: nirgends ein "-dev", also prod - und die Quelle + "none", damit am Panel/in status.json steht, worauf die Einstufung beruht.""" + srv, imgdir = migration_server + (imgdir / "migrate").mkdir(exist_ok=True) + (imgdir / "migrate" / "gu10_rgb_dev.img").write_bytes(b"DEV" * 1000) + (imgdir / "migrate" / "gu10_rgb_prod.img").write_bytes(b"PROD" * 1000) + r = srv.get("/qcx_firmware_-klyqa.lighting.rgb-cw-ww.gu10_V1.1.0.1138_ota_light.img") + assert r.status == 200 + assert r.body == b"PROD" * 1000 + st = srv.status(lambda s: s.get("last_seen") is not None) + assert st["last_seen"]["variant"] == "prod" + assert st["last_seen"]["variant_source"] == "none" + + def test_migration_listen_records_but_serves_nothing(migration_server): srv, imgdir = migration_server (imgdir / "migrate").mkdir(exist_ok=True) From 2fe2f7d9f4fcc583f28fb7fb2f4061474765ae2b Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 01:07:44 +0200 Subject: [PATCH 140/150] feat(tools): Auslieferungsarchiv ohne WLAN-Zugangsdaten Baut alle acht Ziele ueber den wiederverwendbaren Bauweg tools/build_nocreds.sh (config.local.cmake liegt per trap beiseite, damit sie nicht als set() jedes -D schlaegt) und packt ota/, migrate/ (prod+dev signiert), flash/, bruno/, onboard.sh, README.md und SHA256SUMS nach dist/ninlight-.tar.gz. Vor dem Packen wird jede Datei binaer gegen SSID, WLAN-Passwort und Token aus config.local.cmake geprueft; ein Fund bricht den Lauf ab. --- tools/build_nocreds.sh | 77 ++++++ tools/release.sh | 309 ++++++++++++++++++++++ tools/test_host.sh | 1 + tools/tests/test_release.sh | 496 ++++++++++++++++++++++++++++++++++++ 4 files changed, 883 insertions(+) create mode 100755 tools/build_nocreds.sh create mode 100755 tools/release.sh create mode 100755 tools/tests/test_release.sh diff --git a/tools/build_nocreds.sh b/tools/build_nocreds.sh new file mode 100755 index 0000000..7eb6434 --- /dev/null +++ b/tools/build_nocreds.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# Baut Firmwareabzuege OHNE einkompilierte WLAN-Zugangsdaten: die Lampen starten damit +# im Onboarding-AP (ninlight-), statt sich in ein fremdes WLAN einzuwaehlen. +# +# Aufruf: tools/build_nocreds.sh [ziel ...] (ohne Argumente: alle acht Ziele) +# +# Ergebnis: je Ziel ein Build-Verzeichnis unter $NINLIGHT_NOCREDS_BUILD_REL (Vorgabe +# "build-nocreds", relativ zum Repo-Wurzelverzeichnis) mit den ueblichen Artefakten +# (ninlight__gz.img, ninlight__ota.img, ...). Bewusst NICHT build/: die +# dortigen Abzuege sind die des Entwicklers - mit seinem WLAN - und bleiben unangetastet. +# +# Dieses Skript ist ausdruecklich zum Wiederverwenden da: tools/release.sh baut damit das +# Auslieferungsarchiv, und die Migrationsstation wird damit die Wahl "Lampe startet im +# Onboarding-AP" bedienen. Deshalb steht die Logik hier und nicht in release.sh. +# +# Umgebungsvariablen: +# NINLIGHT_DOCKER Build-Vorspann (Vorgabe tools/docker.sh); Tests stubben ihn +# NINLIGHT_NOCREDS_BUILD_REL Ausgabeverzeichnis relativ zur Repo-Wurzel +set -euo pipefail + +ROOT=$(cd "$(dirname "$0")/.." && pwd) +DOCKER=${NINLIGHT_DOCKER:-$ROOT/tools/docker.sh} +BUILD_REL=${NINLIGHT_NOCREDS_BUILD_REL:-build-nocreds} + +DEVICES=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw) +if [ $# -gt 0 ]; then + TARGETS=("$@") +else + TARGETS=("${DEVICES[@]}") +fi +for t in "${TARGETS[@]}"; do + ok=0 + for d in "${DEVICES[@]}"; do [ "$t" = "$d" ] && ok=1; done + [ "$ok" -eq 1 ] || { echo "Fehler: unbekanntes Ziel '$t' (bekannt: ${DEVICES[*]})" >&2; exit 1; } +done + +# config.local.cmake wird in CMakeLists.txt mit gewoehnlichem set() eingebunden und +# SCHLAEGT damit jedes -D auf der Kommandozeile. Ein Bau ohne Zugangsdaten ist deshalb +# nur moeglich, wenn die Datei fuer die Dauer des Baus beiseite liegt. In ihr stehen die +# echte SSID, das WLAN-Passwort und das API-Token - sie muss auch dann zurueckkommen, +# wenn der Bau abbricht oder jemand Strg-C drueckt. Darum ein trap auf EXIT UND auf +# INT/TERM (EXIT allein laeuft bei einem Signal zwar auch, aber der Exit-Code soll das +# Signal widerspiegeln, statt als Erfolg durchzugehen). +LOCAL="$ROOT/config.local.cmake" +STASH_DIR="" +restore_local() { + [ -n "$STASH_DIR" ] || return 0 + if [ -f "$STASH_DIR/config.local.cmake" ]; then + cp -p "$STASH_DIR/config.local.cmake" "$LOCAL" || { + echo "ACHTUNG: config.local.cmake konnte nicht zurueckgelegt werden - Kopie liegt unter $STASH_DIR" >&2 + STASH_DIR="" + return 1 + } + fi + rm -rf "$STASH_DIR" + STASH_DIR="" +} +if [ -f "$LOCAL" ]; then + STASH_DIR=$(mktemp -d) + cp -p "$LOCAL" "$STASH_DIR/config.local.cmake" + trap 'restore_local' EXIT + trap 'restore_local; trap - INT; kill -INT $$' INT + trap 'restore_local; exit 143' TERM + rm -f "$LOCAL" + echo "config.local.cmake liegt fuer diesen Bau beiseite (keine WLAN-Zugangsdaten im Abzug)" +fi + +for t in "${TARGETS[@]}"; do + echo "== $t (ohne Zugangsdaten)" + # Relative Pfade, weil der Bau ueblicherweise im Container laeuft, der das Repo unter + # /work einhaengt - absolute Hostpfade gaebe es dort nicht. + "$DOCKER" cmake -S . -B "$BUILD_REL/$t" -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi.cmake -DNINLIGHT_DEVICE="$t" >/dev/null + "$DOCKER" cmake --build "$BUILD_REL/$t" +done + +echo "fertig - Abzuege ohne Zugangsdaten unter $ROOT/$BUILD_REL" diff --git a/tools/release.sh b/tools/release.sh new file mode 100755 index 0000000..1e958da --- /dev/null +++ b/tools/release.sh @@ -0,0 +1,309 @@ +#!/usr/bin/env bash +# Baut ein Auslieferungsarchiv: alle acht Ziele, OHNE einkompilierte WLAN-Zugangsdaten +# (die Lampen starten also im Onboarding-AP), in allen drei Bildformen - OTA, Migration +# (dev und prod signiert) und serielle Erstbespielung. +# +# Aufruf: tools/release.sh +# Ergebnis: dist/ninlight-.tar.gz +# +# Umgebungsvariablen: +# NINLIGHT_SIGN_DIR Schluesselverzeichnis (Vorgabe ~/workspace/fw-sign) +# NINLIGHT_NOCREDS_BUILD_REL Build-Verzeichnis relativ zur Repo-Wurzel (s. build_nocreds.sh) +# NINLIGHT_DOCKER Build-Vorspann (s. build_nocreds.sh); Tests stubben ihn +set -euo pipefail + +VERSION=${1:?Aufruf: tools/release.sh } +ROOT=$(cd "$(dirname "$0")/.." && pwd) +TARGETS=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw) +BUILD_REL=${NINLIGHT_NOCREDS_BUILD_REL:-build-nocreds} +BUILD_DIR="$ROOT/$BUILD_REL" +NAME="ninlight-$VERSION" +TGZ="$ROOT/dist/$NAME.tar.gz" + +# Ein vorhandenes Archiv wird NICHT ueberschrieben: eine schon weitergegebene Version +# still durch einen anderen Inhalt zu ersetzen, ist der Fehler, den man am spaetesten +# bemerkt. +[ -e "$TGZ" ] && { echo "Fehler: $TGZ existiert bereits - Version erhoehen oder Datei entfernen" >&2; exit 1; } + +# sha256: GNU zuerst (Debian/Pi), BSD/macOS als Rueckfall. +if command -v sha256sum >/dev/null 2>&1; then + SHA_CMD="sha256sum" +elif command -v shasum >/dev/null 2>&1; then + SHA_CMD="shasum -a 256" +else + echo "Fehler: weder sha256sum noch shasum gefunden" >&2; exit 1 +fi + +echo "== Firmware ohne Zugangsdaten bauen ==" +# Der Bauweg liegt bewusst in einem eigenen, wiederverwendbaren Skript (die +# Migrationsstation braucht dieselben Abzuege) - inklusive des Beiseitelegens von +# config.local.cmake, das jedes -D schlaegt. +"$ROOT/tools/build_nocreds.sh" "${TARGETS[@]}" + +STAGE=$(mktemp -d) +trap 'rm -rf "$STAGE"' EXIT +OUT="$STAGE/$NAME" +mkdir -p "$OUT/ota" "$OUT/flash" + +echo "== Images einsammeln ==" +for t in "${TARGETS[@]}"; do + ota="$BUILD_DIR/$t/ninlight_${t}_ota.img" + gz="$BUILD_DIR/$t/ninlight_${t}_gz.img" + [ -f "$ota" ] || { echo "Fehler: $ota fehlt" >&2; exit 1; } + [ -f "$gz" ] || { echo "Fehler: $gz fehlt" >&2; exit 1; } + cp "$ota" "$OUT/ota/" + cp "$gz" "$OUT/flash/" +done + +echo "== Migrationsimages signieren ==" +# Wie tools/station/deploy.sh: die Schluessel werden nur gelesen, sie gehoeren nie ins +# Repo, nie auf den Pi und schon gar nicht ins Archiv. Fehlt das Verzeichnis oder ein +# einzelner Schluessel, entfaellt die jeweilige Variante mit einer Meldung - das Archiv +# entsteht trotzdem, und README.md sagt dann, warum migrate/ fehlt. +# ${HOME:-} statt $HOME, damit `set -u` unter env -i/cron nicht unnoetig abbricht. +SIGN_DIR=${NINLIGHT_SIGN_DIR:-${HOME:-}/workspace/fw-sign} +KEY_PROD="$SIGN_DIR/QCX_Device_CA/qcx_firmware_signing_key.pem" +KEY_DEV="$SIGN_DIR/fw_sign.pem" +NL_SIGN="$ROOT/tools/mkimage/nl_sign.py" +MIGRATE_NOTE="" +HAVE_PROD=0 +HAVE_DEV=0 +if [ -d "$SIGN_DIR" ]; then + if [ -f "$KEY_PROD" ]; then HAVE_PROD=1; else + echo "Hinweis: Produktionsschluessel fehlt - keine _prod.img" >&2 + fi + if [ -f "$KEY_DEV" ]; then HAVE_DEV=1; else + echo "Hinweis: Entwicklungsschluessel fehlt - keine _dev.img" >&2 + fi +else + echo "Hinweis: Schluesselverzeichnis $SIGN_DIR nicht gefunden - keine Migrationsimages erzeugt" >&2 +fi +if [ "$HAVE_PROD" -eq 1 ] || [ "$HAVE_DEV" -eq 1 ]; then + mkdir -p "$OUT/migrate" + for t in "${TARGETS[@]}"; do + # Signiert wird IMMER das rohe _gz.img, niemals das fertige _ota.img: beide belegen + # denselben 512-Byte-Block am Dateiende (Signatur bzw. Trailer). + gz="$BUILD_DIR/$t/ninlight_${t}_gz.img" + if [ "$HAVE_PROD" -eq 1 ]; then + python3 "$NL_SIGN" sign --key "$KEY_PROD" --in "$gz" --out "$OUT/migrate/${t}_prod.img" \ + || { echo "Fehler: Signieren von $t (prod) fehlgeschlagen" >&2; exit 1; } + fi + if [ "$HAVE_DEV" -eq 1 ]; then + python3 "$NL_SIGN" sign --key "$KEY_DEV" --in "$gz" --out "$OUT/migrate/${t}_dev.img" \ + || { echo "Fehler: Signieren von $t (dev) fehlgeschlagen" >&2; exit 1; } + fi + done +fi +if [ "$HAVE_PROD" -eq 0 ] && [ "$HAVE_DEV" -eq 0 ]; then + MIGRATE_NOTE="**\`migrate/\` fehlt in diesem Archiv.** Beim Bauen war kein Signierschluessel +erreichbar (\`$SIGN_DIR\`). Ohne Signatur nimmt die alte Klyqa-Firmware kein Image an; +alte Lampen lassen sich mit diesem Archiv also nicht migrieren. Die Schluessel gehoeren +nie in ein Archiv - wer sie hat, baut sich das Archiv mit \`tools/release.sh\` neu." +elif [ "$HAVE_PROD" -eq 0 ]; then + MIGRATE_NOTE="**In \`migrate/\` fehlen die \`_prod.img\`.** Beim Bauen war nur der +Entwicklungsschluessel erreichbar. Lampen mit Produktionssignatur lassen sich damit nicht migrieren." +elif [ "$HAVE_DEV" -eq 0 ]; then + MIGRATE_NOTE="**In \`migrate/\` fehlen die \`_dev.img\`.** Beim Bauen war nur der +Produktionsschluessel erreichbar. Lampen mit Entwicklungssignatur lassen sich damit nicht migrieren." +fi + +echo "== Bruno-Collection, Onboarding-Skript und README ==" +mkdir -p "$OUT/bruno" +cp "$ROOT/bruno/ninlight.yml" "$OUT/bruno/" + +cat >"$OUT/onboard.sh" <<'ONBOARD' +#!/usr/bin/env bash +# Bringt eine frisch geflashte ninlight-Lampe ins WLAN. +# +# Aufruf: ./onboard.sh [wlan-passwort] +# +# VORHER VON HAND (das kann dir kein Skript portabel abnehmen - sich per Skript mit +# einem WLAN zu verbinden ist auf jedem Betriebssystem anders): +# 1. Lampe einschalten. Ohne gespeicherte Zugangsdaten pulsiert sie warmweiss und +# spannt den Zugangspunkt "ninlight-" auf (Passwort: ninlight). +# 2. Diesen Rechner mit genau diesem WLAN verbinden. +# 3. Dieses Skript aufrufen. +# 4. Danach wieder ins normale WLAN wechseln; die Lampe ist per mDNS +# (ninlight-.local, Port 3333) erreichbar. +# +# Optionen: +# --host Adresse der Lampe im AP (Vorgabe 192.168.4.1) +# --port REST-Port (Vorgabe 3333) +# --token API-Token. Im unkonfigurierten Onboarding-AP NICHT noetig (der +# Zugang ist dort offen). Noetig im Fallback-AP, den eine Lampe +# aufspannt, die bereits Zugangsdaten hat (Router > 5 min weg) - +# dort ist system/settings wie ueberall tokengeschuetzt. +# Vorgabewert der Firmware: aabbccddeeff0011223344 +set -euo pipefail + +command -v curl >/dev/null 2>&1 || { echo "Fehler: curl wird gebraucht" >&2; exit 1; } +command -v python3 >/dev/null 2>&1 || { echo "Fehler: python3 wird gebraucht (JSON-Body)" >&2; exit 1; } + +HOST=192.168.4.1 +PORT=3333 +TOKEN="" +SSID="" +PASSWORD="" +# Hilfetext ist der Kommentarblock oben - keine feste Zeilennummer, die beim naechsten +# Satz im Kopf stillschweigend falsch wird. +usage() { awk 'NR>1 && /^#/ {sub(/^# ?/, ""); print; next} NR>1 {exit}' "$0"; } + +while [ $# -gt 0 ]; do + case "$1" in + --host) HOST=${2:?--host braucht einen Wert}; shift 2 ;; + --port) PORT=${2:?--port braucht einen Wert}; shift 2 ;; + --token) TOKEN=${2:?--token braucht einen Wert}; shift 2 ;; + -h|--help) usage; exit 0 ;; + -*) echo "Unbekannte Option: $1" >&2; usage >&2; exit 2 ;; + *) if [ -z "$SSID" ]; then SSID=$1; else PASSWORD=$1; fi; shift ;; + esac +done +[ -n "$SSID" ] || { echo "Fehler: SSID fehlt" >&2; usage >&2; exit 2; } + +URL="http://$HOST:$PORT/api/v1/system/settings" +BODY=$(SSID="$SSID" PASSWORD="$PASSWORD" python3 -c ' +import json, os +print(json.dumps({"type": "wifi", "ssid": os.environ["SSID"], "password": os.environ["PASSWORD"]}))') + +set -- -X PUT "$URL" -H "Content-Type: application/json" -d "$BODY" --max-time 15 +[ -n "$TOKEN" ] && set -- "$@" -H "Authorization: $TOKEN" +echo "== WLAN setzen ($URL) ==" +curl -fsS "$@" || { + echo "Fehler: die Lampe hat die Zugangsdaten nicht angenommen." >&2 + echo " 401 -> Fallback-AP: --token mitgeben." >&2 + echo " keine Verbindung -> bist du wirklich im WLAN ninlight-?" >&2 + exit 1 +} +echo +echo "== Verbindung pruefen (system/info) ==" +# Die Lampe braucht ein paar Sekunden, bis sie im neuen WLAN haengt; danach schliesst +# der AP nach 60 s. wifi.sta_ip zeigt die neue Adresse, wifi.error den Grund, falls es +# nicht geklappt hat (z.B. falsches Passwort). Wartezeit fuer Tests ueberschreibbar. +sleep "${NINLIGHT_ONBOARD_WAIT:-8}" +curl -fsS "http://$HOST:$PORT/api/v1/system/info" --max-time 15 || { + echo "Hinweis: system/info nicht mehr erreichbar - meist ein gutes Zeichen (der AP ist" >&2 + echo " zu, die Lampe haengt im neuen WLAN). Im normalen WLAN nachsehen." >&2 +} +echo +ONBOARD +chmod +x "$OUT/onboard.sh" + +cat >"$OUT/README.md" <\` (Passwort \`ninlight\`) und +wird mit \`onboard.sh\` ins WLAN gebracht. + +Ziele: evk, e27_rgb, e14_rgb, gu10_rgb, e27_cw, e14_cw, gu10_cw, g95_cw. + +## Was liegt wo + +| Verzeichnis | Inhalt | wofuer | +|---|---|---| +| \`ota/\` | \`ninlight__ota.img\` | Update einer Lampe, auf der ninlight schon laeuft (REST \`fw_update\`), und die Auslieferung an die Rettungsstation | +| \`migrate/\` | \`_prod.img\`, \`_dev.img\` | Erstmigration einer Lampe, auf der noch die alte Klyqa-Firmware laeuft | +| \`flash/\` | \`ninlight__gz.img\` | serielle Erstbespielung ueber die Platine | +| \`bruno/\` | \`ninlight.yml\` | REST-Schnittstelle als Bruno-Collection | + +\`SHA256SUMS\` deckt jede Datei ab: \`sha256sum -c SHA256SUMS\` (macOS: \`shasum -a 256 -c SHA256SUMS\`). + +$MIGRATE_NOTE + +## Alte Klyqa-Lampe migrieren + +Die alte Firmware nimmt nur signierte Images an; welche Signatur (\`_prod\` oder \`_dev\`) +gilt, haengt an Produkt-ID und Version der Lampe. Der Weg laeuft ueber die +Migrationsstation (\`tools/station/\` im Repo, ein Raspberry Pi mit zwei Zugangspunkten): + +1. Station einschalten und \`ninlight-station migrate on\` (oder den Taster druecken). +2. Lampe einschalten - sie waehlt sich in den Klyqa-Zugangspunkt ein und holt das + passende Image aus \`migrate/\`. +3. Nach dem Neustart laeuft ninlight; die Lampe spannt ihren Onboarding-AP auf. + +## Frisch geflashte Lampe onboarden + + ./onboard.sh "MeinWLAN" "meinpasswort" + +Vorher von Hand: Lampe einschalten (sie pulsiert warmweiss), diesen Rechner mit dem WLAN +\`ninlight-\` verbinden (Passwort \`ninlight\`). \`./onboard.sh --help\` zeigt die +Schritte und die Optionen (\`--host\`, \`--port\`, \`--token\`). Im unkonfigurierten +Onboarding-AP ist der Zugang offen, im Fallback-AP einer bereits onboardeten Lampe ist das +Token noetig. + +Serielle Erstbespielung: \`flash/ninlight__gz.img\` mit \`tools/flash.sh\` aus dem Repo +bzw. \`wm_tool\` auf die Platine schreiben. + +## Vertrauensgrenze + +Der 512-Byte-Trailer der \`_ota.img\` prueft Geraetetyp und SHA256 - das ist eine +Integritaets- und Geraetetyppruefung, **kein kryptografisches Signaturverfahren**. Der +Download laeuft ueber \`http://\` ohne TLS, und der Rettungskanal ist nur durch ein WLAN- +Passwort geschuetzt, das mit einem oeffentlichen Default im Repo steht. Wer im selben WLAN +ist und das API-Token kennt (bzw. beim Recovery in Funkreichweite ist und das Passwort +kennt), kann der Lampe beliebige Firmware unterschieben. Das ist eine bewusste +Entscheidung dieses Projekts: Ziel ist, die Signaturpruefung der alten, abgekuendigten +Original-Firmware zu umgehen, damit die Lampen ueberhaupt weiter nutzbar bleiben - nicht, +ein neues, gehaertetes Sicherheitsmodell zu bauen. + +Die RSA-Signatur der \`migrate/\`-Images ist die der ALTEN Firmware und existiert nur, damit +die alte Firmware das Image ueberhaupt annimmt. Die privaten Schluessel liegen nie in +diesem Archiv. +README + +echo "== Auf Zugangsdaten pruefen ==" +# Doppelter Boden zum Beiseitelegen von config.local.cmake: bevor irgendetwas gepackt +# wird, wird JEDE Datei des Archivs binaer gegen die echten Werte aus +# config.local.cmake geprueft. Lieber ein abgebrochener Release als ein Archiv mit +# Michaels WLAN-Passwort bei Dritten. +LOCAL="$ROOT/config.local.cmake" +if [ -f "$LOCAL" ]; then + python3 - "$OUT" "$LOCAL" <<'PY' || exit 1 +import os +import re +import sys + +stage, local = sys.argv[1], sys.argv[2] +text = open(local, encoding="utf-8", errors="replace").read() +# Nur die drei Werte, die wirklich geheim sind. NINLIGHT_AP_PASSWORD und der +# Vorgabetoken stehen oeffentlich im Repo (README, Bruno) - sie hier mitzupruefen +# haette nur falsche Alarme erzeugt. +SECRETS = ("NINLIGHT_WIFI_SSID", "NINLIGHT_WIFI_PASSWORD", "NINLIGHT_TOKEN") +PUBLIC = {"", "ninlight", "aabbccddeeff0011223344", "ninlight-recovery", "change-me-please"} +needles = [] +for name in SECRETS: + m = re.search(r'^\s*set\s*\(\s*%s\s+"([^"]*)"' % name, text, re.M) + if m and m.group(1) not in PUBLIC: + needles.append((name, m.group(1).encode())) + +hits = [] +for root, _dirs, files in os.walk(stage): + for fn in sorted(files): + path = os.path.join(root, fn) + with open(path, "rb") as fh: + data = fh.read() + for name, needle in needles: + if needle in data: + hits.append("%s in %s" % (name, os.path.relpath(path, stage))) +if hits: + sys.stderr.write( + "Fehler: Zugangsdaten aus config.local.cmake im Archiv gefunden, nichts gepackt:\n " + + "\n ".join(hits) + "\n") + sys.exit(1) +print(" sauber (%d geprueft)" % len(needles)) +PY +fi + +echo "== SHA256SUMS ==" +( + cd "$OUT" + # $SHA_CMD absichtlich ohne Anfuehrungszeichen: "shasum -a 256" sind drei Woerter. + # shellcheck disable=SC2086 + find . -type f ! -name SHA256SUMS | sed 's|^\./||' | sort | xargs $SHA_CMD >SHA256SUMS +) + +echo "== packen ==" +mkdir -p "$ROOT/dist" +tar czf "$TGZ" -C "$STAGE" "$NAME" +echo "fertig: $TGZ" diff --git a/tools/test_host.sh b/tools/test_host.sh index eb38429..1c1852a 100755 --- a/tools/test_host.sh +++ b/tools/test_host.sh @@ -38,6 +38,7 @@ SHELL_SUITES=( "$ROOT/tools/station/tests/test_cli.sh" "$ROOT/tools/station/tests/test_deploy.sh" "$ROOT/tools/station/tests/test_setup_pi.sh" + "$ROOT/tools/tests/test_release.sh" ) for suite in "${SHELL_SUITES[@]}"; do "$suite" diff --git a/tools/tests/test_release.sh b/tools/tests/test_release.sh new file mode 100755 index 0000000..fe1ebf5 --- /dev/null +++ b/tools/tests/test_release.sh @@ -0,0 +1,496 @@ +#!/usr/bin/env bash +# Stub-Tests fuer tools/release.sh (und tools/build_nocreds.sh) - ohne Docker, ohne +# echten Firmwarebau ausfuehrbar. +# +# Gestubbt wird nur die UNTERSTE Ebene: NINLIGHT_DOCKER zeigt auf ein Skript, das sich +# wie der Bau im Container verhaelt (relative Pfade ab Repo-Wurzel, legt _gz.img und +# _ota.img ab). Alles darueber - das Beiseitelegen von config.local.cmake, das +# Signieren, README/SHA256SUMS, das Packen - laeuft echt. Wichtig dabei: der Stub +# verhaelt sich wie ein Compiler und SCHREIBT die Werte aus config.local.cmake in die +# Images, wenn die Datei zum Bauzeitpunkt noch da ist. Nur so pruefen die Faelle 5/6 +# wirklich das Skript und nicht den Stub: vergisst release.sh/build_nocreds.sh das +# Beiseitelegen, landen die Zugangsdaten tatsaechlich im Abzug. +# +# Aufruf: tools/tests/test_release.sh +set -euo pipefail + +HERE=$(cd "$(dirname "$0")" && pwd) +TOOLS_DIR=$(cd "$HERE/.." && pwd) +REPO=$(cd "$TOOLS_DIR/.." && pwd) + +# Sicherheitsnetz (wie in tools/station/tests/test_deploy.sh): NINLIGHT_SIGN_DIR zeigt +# auf diesem Rechner auf ECHTE Signierschluessel. Sie darf nicht aus der Umgebung in +# diese Suite durchsickern - jedes Szenario setzt sie hart, Testschluessel kommen ueber +# die separate Variable TEST_SIGN_DIR. +unset NINLIGHT_SIGN_DIR +unset TEST_SIGN_DIR +unset NINLIGHT_NOCREDS_BUILD_REL +unset NINLIGHT_DOCKER + +# GNU vor BSD: auf dem Pi/Debian gibt es sha256sum, auf dem Mac nur shasum. +if command -v sha256sum >/dev/null 2>&1; then + SHA_CHECK=(sha256sum -c) +else + SHA_CHECK=(shasum -a 256 -c) +fi + +TMPROOT=$(mktemp -d) +trap 'rm -rf "$TMPROOT"' EXIT + +FAILED=0 +pass() { echo "ok - $1"; } +fail() { echo "FEHLER - $1" >&2; FAILED=1; } + +TARGETS=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw) + +# Wegwerf-Zugangsdaten - niemals die echten aus dem Repo. +SECRET_SSID="TestWLAN-4b7f-geheim" +SECRET_PASS="testpasswort-4b7f" +SECRET_TOKEN="0badc0de4b7f0badc0de4b7f" + +# --- Sandbox-Repo: release.sh, build_nocreds.sh, nl_sign.py, bruno/, config.local.cmake --- +setup_sandbox() { + local sandbox=$1 + rm -rf "$sandbox" + mkdir -p "$sandbox/tools/mkimage" "$sandbox/bruno" "$sandbox/dist" + cp "$TOOLS_DIR/release.sh" "$sandbox/tools/release.sh" + cp "$TOOLS_DIR/build_nocreds.sh" "$sandbox/tools/build_nocreds.sh" + chmod +x "$sandbox/tools/release.sh" "$sandbox/tools/build_nocreds.sh" + # Das ECHTE Signierwerkzeug, nur die Schluessel sind Wegwerf-Testschluessel. + cp "$TOOLS_DIR/mkimage/nl_sign.py" "$sandbox/tools/mkimage/nl_sign.py" + # Die ECHTE Bruno-Collection: die Zusage ist, dass sie im Archiv liegt - mit einer + # erfundenen Attrappe wuerde der Test nur sich selbst pruefen. + cp "$REPO/bruno/ninlight.yml" "$sandbox/bruno/ninlight.yml" + cat >"$sandbox/config.local.cmake" <"$stub" <<'STUB' +#!/usr/bin/env bash +# Erwartet "cmake -S . -B / ..." bzw. "cmake --build /". +set -euo pipefail +cd "$NINLIGHT_STUB_ROOT" +mode="" +outdir="" +args=("$@") +for ((i = 0; i < ${#args[@]}; i++)); do + case "${args[$i]}" in + --build) mode=build; outdir=${args[$((i + 1))]} ;; + -B) mode=${mode:-configure}; outdir=${args[$((i + 1))]} ;; + esac +done +[ -n "$outdir" ] || { echo "Stub: kein Build-Verzeichnis in '$*'" >&2; exit 1; } +target=$(basename "$outdir") +mkdir -p "$outdir" +[ "$mode" = "build" ] || exit 0 + +if [ "${STUB_TERM_TARGET:-}" = "$target" ]; then + kill -TERM "$PPID" + sleep 5 + exit 1 +fi +if [ "${STUB_FAIL_TARGET:-}" = "$target" ]; then + echo "Stub: Bau von $target fehlgeschlagen" >&2 + exit 1 +fi + +# "Compiler": die Zugangsdaten aus config.local.cmake landen im Abzug - genau wie beim +# echten Bau, wo CMakeLists.txt sie per set() einkompiliert. +creds="" +if [ -f config.local.cmake ]; then + creds=$(sed -n 's/.*set(NINLIGHT_[A-Z_]* "\(.*\)").*/\1/p' config.local.cmake | tr '\n' ' ') +fi +if [ "${STUB_LEAK:-}" = "1" ]; then + creds="$STUB_LEAK_VALUE" +fi +CREDS="$creds" TARGET="$target" OUTDIR="$outdir" python3 - <<'PY' +import os + +target, outdir, creds = os.environ["TARGET"], os.environ["OUTDIR"], os.environ["CREDS"] +body = bytearray(("firmware-%s-" % target).encode() * 200) +body = body[:4096] + b"\0" * (4096 - len(body[:4096])) +blob = creds.encode() +if blob: + body[100:100 + len(blob)] = blob +gz = bytes(body) +with open(os.path.join(outdir, "ninlight_%s_gz.img" % target), "wb") as fh: + fh.write(gz) # roh, ohne NINL-Trailer - das signiert nl_sign.py +trailer = bytearray(b"\xff" * 512) +trailer[0:4] = b"NINL" +with open(os.path.join(outdir, "ninlight_%s_ota.img" % target), "wb") as fh: + fh.write(gz + bytes(trailer)) +PY +STUB + chmod +x "$stub" +} + +# Wegwerf-Testschluessel ueber nl_sign.generate_test_key() - nie echte Schluessel. +make_test_keys() { + local keydir=$1 with_prod=$2 with_dev=$3 + mkdir -p "$keydir/QCX_Device_CA" + PYTHONPATH="$TOOLS_DIR/mkimage" python3 - "$keydir" "$with_prod" "$with_dev" <<'PY' +import sys +import nl_sign + +keydir, with_prod, with_dev = sys.argv[1], sys.argv[2] == "1", sys.argv[3] == "1" +if with_prod: + k = nl_sign.generate_test_key() + with open(keydir + "/QCX_Device_CA/qcx_firmware_signing_key.pem", "wb") as fh: + fh.write(k.private_pem) + with open(keydir + "/prod_pub.der", "wb") as fh: + fh.write(k.public_der) +if with_dev: + k = nl_sign.generate_test_key() + with open(keydir + "/fw_sign.pem", "wb") as fh: + fh.write(k.private_pem) + with open(keydir + "/dev_pub.der", "wb") as fh: + fh.write(k.public_der) +PY +} + +# Ruft release.sh in der Sandbox auf. NINLIGHT_SIGN_DIR wird hier HART gesetzt, nie aus +# der geerbten Umgebung uebernommen (siehe Sicherheitsnetz oben). +run_release() { + local sandbox=$1 version=$2 + local sign_dir="${TEST_SIGN_DIR:-$sandbox/kein-schluesselverzeichnis}" + env NINLIGHT_SIGN_DIR="$sign_dir" \ + NINLIGHT_DOCKER="$sandbox/stub-docker" \ + NINLIGHT_STUB_ROOT="$sandbox" \ + STUB_FAIL_TARGET="${STUB_FAIL_TARGET:-}" \ + STUB_TERM_TARGET="${STUB_TERM_TARGET:-}" \ + STUB_LEAK="${STUB_LEAK:-}" \ + STUB_LEAK_VALUE="$SECRET_SSID" \ + "$sandbox/tools/release.sh" "$version" +} + +# Sucht die Zugangsdaten BINAER in jeder Datei unterhalb eines Verzeichnisses. +grep_secrets() { + local dir=$1 + SSID="$SECRET_SSID" PASS="$SECRET_PASS" TOKEN="$SECRET_TOKEN" python3 - "$dir" <<'PY' +import os +import sys + +root = sys.argv[1] +needles = {k: os.environ[k].encode() for k in ("SSID", "PASS", "TOKEN")} +for dirpath, _dirs, files in os.walk(root): + for fn in sorted(files): + p = os.path.join(dirpath, fn) + if os.path.islink(p): + continue + with open(p, "rb") as fh: + data = fh.read() + for name, needle in needles.items(): + if needle in data: + print("%s in %s" % (name, os.path.relpath(p, root))) +PY +} + +sha_of() { python3 -c " +import hashlib,sys +print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())" "$1"; } + +echo "=== Szenario 1: Erfolgslauf mit beiden Schluesseln ===" +SANDBOX="$TMPROOT/s1" +setup_sandbox "$SANDBOX" +setup_stub_docker "$SANDBOX/stub-docker" "$SANDBOX" +KEYS_FULL="$TMPROOT/keys-full" +make_test_keys "$KEYS_FULL" 1 1 +CONF_BEFORE=$(sha_of "$SANDBOX/config.local.cmake") +set +e +OUT=$(TEST_SIGN_DIR="$KEYS_FULL" run_release "$SANDBOX" 0.9.9 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC: $OUT"; fi +TGZ="$SANDBOX/dist/ninlight-0.9.9.tar.gz" +if [ -f "$TGZ" ]; then pass "Archiv entstanden"; else fail "kein Archiv unter $TGZ"; fi + +EXTRACT="$TMPROOT/x1" +mkdir -p "$EXTRACT" +tar xzf "$TGZ" -C "$EXTRACT" +A="$EXTRACT/ninlight-0.9.9" +if [ -d "$A" ]; then pass "Archiv entpackt nach ninlight-0.9.9/"; else fail "Verzeichnis ninlight-0.9.9/ fehlt"; fi + +echo "--- Fall 1/2/3: Verzeichnisse und Dateizahlen ---" +for d in ota migrate flash bruno; do + if [ -d "$A/$d" ]; then pass "$d/ vorhanden"; else fail "$d/ fehlt"; fi +done +for f in SHA256SUMS README.md onboard.sh bruno/ninlight.yml; do + if [ -f "$A/$f" ]; then pass "$f vorhanden"; else fail "$f fehlt"; fi +done +if [ -x "$A/onboard.sh" ]; then pass "onboard.sh ist ausfuehrbar"; else fail "onboard.sh ist nicht ausfuehrbar"; fi +N_OTA=$(find "$A/ota" -type f | wc -l | tr -d ' ') +N_FLASH=$(find "$A/flash" -type f | wc -l | tr -d ' ') +N_MIG=$(find "$A/migrate" -type f | wc -l | tr -d ' ') +[ "$N_OTA" = "8" ] && pass "ota/ enthaelt 8 Dateien" || fail "ota/ enthaelt $N_OTA statt 8" +[ "$N_FLASH" = "8" ] && pass "flash/ enthaelt 8 Dateien" || fail "flash/ enthaelt $N_FLASH statt 8" +[ "$N_MIG" = "16" ] && pass "migrate/ enthaelt 16 Dateien" || fail "migrate/ enthaelt $N_MIG statt 16" +MISS="" +for t in "${TARGETS[@]}"; do + [ -f "$A/ota/ninlight_${t}_ota.img" ] || MISS="$MISS ota/$t" + [ -f "$A/flash/ninlight_${t}_gz.img" ] || MISS="$MISS flash/$t" + [ -f "$A/migrate/${t}_prod.img" ] || MISS="$MISS migrate/${t}_prod" + [ -f "$A/migrate/${t}_dev.img" ] || MISS="$MISS migrate/${t}_dev" +done +[ -z "$MISS" ] && pass "je Ziel genau die erwarteten Dateinamen" || fail "fehlende Dateien:$MISS" + +echo "--- Migrationsimages sind echt signiert (und zwar das _gz.img) ---" +if python3 "$TOOLS_DIR/mkimage/nl_sign.py" verify --pub "$KEYS_FULL/prod_pub.der" \ + --in "$A/migrate/evk_prod.img" >/dev/null 2>&1; then + pass "evk_prod.img traegt eine gueltige Produktionssignatur" +else + fail "evk_prod.img besteht die Signaturpruefung nicht" +fi +if python3 "$TOOLS_DIR/mkimage/nl_sign.py" verify --pub "$KEYS_FULL/dev_pub.der" \ + --in "$A/migrate/g95_cw_dev.img" >/dev/null 2>&1; then + pass "g95_cw_dev.img traegt eine gueltige Entwicklungssignatur" +else + fail "g95_cw_dev.img besteht die Signaturpruefung nicht" +fi +# Die Signatur belegt denselben 512-Byte-Block wie der Trailer: signiert werden muss das +# rohe _gz.img. Ein versehentlich signiertes _ota.img waere 512 Byte laenger. +GZ_SIZE=$(python3 -c "import os,sys;print(os.path.getsize(sys.argv[1]))" "$A/flash/ninlight_evk_gz.img") +MIG_SIZE=$(python3 -c "import os,sys;print(os.path.getsize(sys.argv[1]))" "$A/migrate/evk_prod.img") +if [ "$MIG_SIZE" -eq $((GZ_SIZE + 512)) ]; then + pass "Migrationsimage = _gz.img + 512 Byte Signatur (nicht das _ota.img signiert)" +else + fail "Migrationsimage hat $MIG_SIZE Byte, erwartet $((GZ_SIZE + 512))" +fi + +echo "--- Fall 4: SHA256SUMS deckt jede Datei ab und stimmt ---" +LISTED=$(awk '{print $2}' "$A/SHA256SUMS" | sed 's|^\*||' | sort) +PRESENT=$( (cd "$A" && find . -type f ! -name SHA256SUMS | sed 's|^\./||' | sort) ) +if [ "$LISTED" = "$PRESENT" ]; then + pass "SHA256SUMS listet genau die Dateien des Archivs ($(echo "$PRESENT" | wc -l | tr -d ' '))" +else + fail "SHA256SUMS und Archivinhalt weichen ab: +$(diff <(echo "$LISTED") <(echo "$PRESENT") || true)" +fi +if (cd "$A" && "${SHA_CHECK[@]}" SHA256SUMS >/dev/null 2>&1); then + pass "Pruefsummen stimmen" +else + fail "Pruefsummenpruefung fehlgeschlagen" +fi + +echo "--- Fall 5: keine Zugangsdaten im Archiv (binaer gesucht) ---" +HITS=$(grep_secrets "$A") +if [ -z "$HITS" ]; then + pass "weder SSID noch Passwort noch Token in irgendeiner Datei des Archivs" +else + fail "Zugangsdaten im Archiv gefunden: +$HITS" +fi +# Auch im gepackten Archiv selbst (tar speichert Inhalte ungefiltert, gzip ist keine +# Verschleierung - aber eine unkomprimierbare Kopie wuerde hier auffallen). +if python3 -c " +import gzip,sys +data = gzip.open(sys.argv[1],'rb').read() +sys.exit(0 if not any(s.encode() in data for s in sys.argv[2:]) else 1)" \ + "$TGZ" "$SECRET_SSID" "$SECRET_PASS" "$SECRET_TOKEN"; then + pass "auch im entpackten tar-Strom keine Zugangsdaten" +else + fail "Zugangsdaten im tar-Strom des Archivs" +fi +# Der Stub verhaelt sich nur dann wie ein Compiler, wenn er die Werte ueberhaupt +# einbauen KANN - sonst waere Fall 5 wertlos. Gegenprobe: mit vorhandener +# config.local.cmake landen sie im Abzug. +mkdir -p "$TMPROOT/probe" +env NINLIGHT_STUB_ROOT="$SANDBOX" "$SANDBOX/stub-docker" cmake --build "$TMPROOT/probe/evk" >/dev/null +if grep_secrets "$TMPROOT/probe" | grep -q SSID; then + pass "Gegenprobe: der Stub baut die Zugangsdaten ein, solange config.local.cmake da ist" +else + fail "Gegenprobe fehlgeschlagen - der Stub baut nie Zugangsdaten ein, Fall 5 waere wertlos" +fi + +echo "--- Fall 6a: config.local.cmake nach dem Erfolgslauf unveraendert ---" +if [ -f "$SANDBOX/config.local.cmake" ] && [ "$(sha_of "$SANDBOX/config.local.cmake")" = "$CONF_BEFORE" ]; then + pass "config.local.cmake liegt unveraendert an seinem Platz" +else + fail "config.local.cmake fehlt oder wurde veraendert" +fi + +echo "--- README nennt die Vertrauensgrenze und die Verzeichnisse ---" +for phrase in "kein kryptografisches Signaturverfahren" "ohne TLS" "onboard.sh" "migrate/" "flash/"; do + if grep -qF "$phrase" "$A/README.md"; then pass "README nennt '$phrase'"; else fail "README nennt '$phrase' nicht"; fi +done + +echo "=== Szenario 2: Bau bricht mittendrin ab (Fall 6) ===" +SANDBOX="$TMPROOT/s2" +setup_sandbox "$SANDBOX" +setup_stub_docker "$SANDBOX/stub-docker" "$SANDBOX" +CONF_BEFORE=$(sha_of "$SANDBOX/config.local.cmake") +set +e +OUT=$(STUB_FAIL_TARGET=e27_cw TEST_SIGN_DIR="$KEYS_FULL" run_release "$SANDBOX" 0.9.9 2>&1) +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0 ($RC)"; else fail "Exit-Code war 0"; fi +if [ ! -e "$SANDBOX/dist/ninlight-0.9.9.tar.gz" ]; then pass "kein halbes Archiv zurueckgelassen"; else fail "Archiv trotz Abbruch entstanden"; fi +if [ -f "$SANDBOX/config.local.cmake" ] && [ "$(sha_of "$SANDBOX/config.local.cmake")" = "$CONF_BEFORE" ]; then + pass "config.local.cmake nach dem Abbruch unveraendert an seinem Platz" +else + fail "config.local.cmake nach dem Abbruch verschwunden oder veraendert" +fi + +echo "=== Szenario 3: Abbruch per Signal (SIGTERM mitten im Bau) ===" +SANDBOX="$TMPROOT/s3" +setup_sandbox "$SANDBOX" +setup_stub_docker "$SANDBOX/stub-docker" "$SANDBOX" +CONF_BEFORE=$(sha_of "$SANDBOX/config.local.cmake") +set +e +OUT=$(STUB_TERM_TARGET=gu10_rgb TEST_SIGN_DIR="$KEYS_FULL" run_release "$SANDBOX" 0.9.9 2>&1) +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0 ($RC)"; else fail "Exit-Code war 0"; fi +if [ -f "$SANDBOX/config.local.cmake" ] && [ "$(sha_of "$SANDBOX/config.local.cmake")" = "$CONF_BEFORE" ]; then + pass "config.local.cmake nach dem Signalabbruch unveraendert an seinem Platz" +else + fail "config.local.cmake nach dem Signalabbruch verschwunden oder veraendert" +fi +if [ ! -e "$SANDBOX/dist/ninlight-0.9.9.tar.gz" ]; then pass "kein Archiv nach dem Signalabbruch"; else fail "Archiv trotz Signalabbruch entstanden"; fi + +echo "=== Szenario 4: ohne Schluesselverzeichnis (Fall 3) ===" +SANDBOX="$TMPROOT/s4" +setup_sandbox "$SANDBOX" +setup_stub_docker "$SANDBOX/stub-docker" "$SANDBOX" +set +e +OUT=$(run_release "$SANDBOX" 0.9.9 2>&1) # kein TEST_SIGN_DIR -> Verzeichnis gibt es nicht +RC=$? +set -e +if [ "$RC" -eq 0 ]; then pass "Exit-Code 0 - das Archiv entsteht trotzdem"; else fail "Exit-Code $RC: $OUT"; fi +if echo "$OUT" | grep -q "Schluesselverzeichnis"; then pass "deutliche Meldung zum fehlenden Schluesselverzeichnis"; else fail "keine Meldung zum fehlenden Schluesselverzeichnis"; fi +EXTRACT="$TMPROOT/x4"; mkdir -p "$EXTRACT" +tar xzf "$SANDBOX/dist/ninlight-0.9.9.tar.gz" -C "$EXTRACT" +A4="$EXTRACT/ninlight-0.9.9" +if [ ! -d "$A4/migrate" ]; then pass "migrate/ liegt nicht vor"; else fail "migrate/ trotzdem vorhanden"; fi +if [ -d "$A4/ota" ] && [ "$(find "$A4/ota" -type f | wc -l | tr -d ' ')" = "8" ]; then + pass "ota/ trotzdem vollstaendig" +else + fail "ota/ unvollstaendig" +fi +if grep -q "migrate/\` fehlt in diesem Archiv" "$A4/README.md"; then + pass "README.md sagt, warum migrate/ fehlt" +else + fail "README.md erklaert das fehlende migrate/ nicht" +fi +if (cd "$A4" && "${SHA_CHECK[@]}" SHA256SUMS >/dev/null 2>&1); then + pass "SHA256SUMS stimmt auch ohne migrate/" +else + fail "SHA256SUMS stimmt ohne migrate/ nicht" +fi + +echo "=== Szenario 5: zweiter Lauf mit derselben Version bricht ab (Fall 7) ===" +BEFORE=$(sha_of "$SANDBOX/dist/ninlight-0.9.9.tar.gz") +set +e +OUT=$(run_release "$SANDBOX" 0.9.9 2>&1) +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0 ($RC)"; else fail "zweiter Lauf war erfolgreich"; fi +if echo "$OUT" | grep -q "existiert bereits"; then pass "Meldung 'existiert bereits'"; else fail "keine Meldung zum vorhandenen Archiv: $OUT"; fi +if [ "$(sha_of "$SANDBOX/dist/ninlight-0.9.9.tar.gz")" = "$BEFORE" ]; then + pass "vorhandenes Archiv unveraendert" +else + fail "vorhandenes Archiv wurde ueberschrieben" +fi +# Und eine andere Version laeuft weiterhin durch. +set +e +OUT=$(run_release "$SANDBOX" 0.9.10 2>&1) +RC=$? +set -e +if [ "$RC" -eq 0 ] && [ -f "$SANDBOX/dist/ninlight-0.9.10.tar.gz" ]; then + pass "andere Version laeuft normal durch" +else + fail "andere Version schlaegt fehl: $OUT" +fi + +echo "=== Szenario 6: doppelter Boden - Zugangsdaten im Abzug brechen den Lauf ab ===" +SANDBOX="$TMPROOT/s6" +setup_sandbox "$SANDBOX" +setup_stub_docker "$SANDBOX/stub-docker" "$SANDBOX" +set +e +OUT=$(STUB_LEAK=1 TEST_SIGN_DIR="$KEYS_FULL" run_release "$SANDBOX" 0.9.9 2>&1) +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "Exit-Code != 0 ($RC)"; else fail "Lauf war trotz Zugangsdaten im Abzug erfolgreich"; fi +if echo "$OUT" | grep -q "Zugangsdaten aus config.local.cmake im Archiv gefunden"; then + pass "Meldung benennt den Fund" +else + fail "keine Meldung zum Fund: $OUT" +fi +if echo "$OUT" | grep -qF "$SECRET_SSID"; then + fail "die Meldung druckt den geheimen Wert selbst" +else + pass "die Meldung druckt den Wert nicht, nur den Namen" +fi +if [ ! -e "$SANDBOX/dist/ninlight-0.9.9.tar.gz" ]; then pass "kein Archiv gepackt"; else fail "Archiv trotz Fund gepackt"; fi + +echo "=== Szenario 7: onboard.sh spricht die richtige Schnittstelle an ===" +ONB="$TMPROOT/x1/ninlight-0.9.9/onboard.sh" +STUBBIN="$TMPROOT/onb-bin" +mkdir -p "$STUBBIN" +CURLLOG="$TMPROOT/curl.log" +cat >"$STUBBIN/curl" <<'CURL' +#!/usr/bin/env bash +{ for a in "$@"; do printf '%s\n' "$a"; done; echo "--"; } >>"$CURL_LOG" +echo '{}' +CURL +chmod +x "$STUBBIN/curl" +: >"$CURLLOG" +CURL_LOG="$CURLLOG" PATH="$STUBBIN:$PATH" NINLIGHT_ONBOARD_WAIT=0 "$ONB" "MeinWLAN" "geheim123" >/dev/null +if grep -q "^http://192.168.4.1:3333/api/v1/system/settings$" "$CURLLOG"; then + pass "PUT geht an system/settings im Onboarding-AP" +else + fail "falsche URL: $(cat "$CURLLOG")" +fi +if grep -q '^PUT$' "$CURLLOG"; then pass "Methode PUT"; else fail "keine PUT-Methode"; fi +if grep -qF '{"type": "wifi", "ssid": "MeinWLAN", "password": "geheim123"}' "$CURLLOG"; then + pass "Rumpf entspricht der Bruno-Anfrage 'Set WiFi'" +else + fail "falscher Rumpf: $(cat "$CURLLOG")" +fi +if grep -q "^http://192.168.4.1:3333/api/v1/system/info$" "$CURLLOG"; then + pass "danach wird system/info abgefragt" +else + fail "system/info wird nicht abgefragt" +fi +if grep -q "^Authorization:" "$CURLLOG"; then + fail "ohne --token wird trotzdem ein Authorization-Header geschickt" +else + pass "ohne --token kein Authorization-Header (offener Onboarding-AP)" +fi +: >"$CURLLOG" +CURL_LOG="$CURLLOG" PATH="$STUBBIN:$PATH" NINLIGHT_ONBOARD_WAIT=0 "$ONB" --token abc12345 "MeinWLAN" "geheim123" >/dev/null +if grep -q "^Authorization: abc12345$" "$CURLLOG"; then + pass "mit --token wird der Header geschickt (Fallback-AP)" +else + fail "--token kommt nicht als Authorization-Header an" +fi +HELP=$("$ONB" --help) +if echo "$HELP" | grep -qi "von hand" && echo "$HELP" | grep -q "ninlight-"; then + pass "Hilfetext nennt die manuellen Schritte und den AP-Namen" +else + fail "Hilfetext erklaert die manuellen Schritte nicht: $HELP" +fi +set +e +"$ONB" >/dev/null 2>&1 +RC=$? +set -e +if [ "$RC" -ne 0 ]; then pass "ohne SSID bricht onboard.sh ab"; else fail "ohne SSID lief onboard.sh durch"; fi + +echo +if [ "$FAILED" -eq 0 ]; then + echo "alle Zusagen erfuellt" +else + echo "es gab Fehler" >&2 +fi +exit "$FAILED" From 3642672c710df38df72e95460788dcb3e6b44ca2 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 01:20:42 +0200 Subject: [PATCH 141/150] feat(station): Automatikmodus als eigener Menueeintrag am Taster Das Tastermenue bot bisher nur "Migration starten" an, fest verdrahtet auf die Horchphase; den Automatikmodus gab es nur ueber die Kommandozeile - also ausgerechnet dort nicht, wo man ihn braucht, denn "on" macht wlan0 zum Zugangspunkt und reisst jede SSH-Sitzung ab. Jetzt zwei getrennte, namentlich unterscheidbare Eintraege: "Migration: horchen" (--listen) zuerst, danach "Migration: AUTOMATIK" (ohne --listen). Der Automatikeintrag bekommt eine andere, deutlichere Rueckfrage ("ACHTUNG" statt "RUECKFRAGE", plus zwei Warnzeilen), weil der Migrations-Zugangspunkt ein oeffentlich bekanntes Passwort hat und im Automatikmodus jede alte Klyqa-Lampe in Funkreichweite ungefragt migriert wird, auch fremde. Die Bedienhinweise stehen weiterhin und zuletzt. --- tools/station/README.md | 13 +++-- tools/station/button/button.py | 78 ++++++++++++++++++++++++------ tools/station/tests/test_button.py | 52 ++++++++++++++++---- 3 files changed, 115 insertions(+), 28 deletions(-) diff --git a/tools/station/README.md b/tools/station/README.md index 77d3563..2651c25 100644 --- a/tools/station/README.md +++ b/tools/station/README.md @@ -43,11 +43,14 @@ Countdown bis zum Herunterfahren, den Loslassen jederzeit abbricht. Das Menü sc Zustand ab (Station aus / Rettung läuft / Migration läuft); jede verändernde Auswahl bekommt eine Rückfrage. -**„Migration starten" startet über das Menü immer in der Horchphase** (`--listen`), nie -automatisch: der Migrations-Zugangspunkt hat ein öffentlich bekanntes Passwort, und ein -Tastendruck, der ihn im Automatikmodus hochfährt, würde jede alte Klyqa-Lampe in -Funkreichweite ungefragt migrieren, auch fremde. Wer den Automatikmodus will, nimmt die -Kommandozeile. +**Die beiden Migrationsarten sind getrennte Einträge.** „Migration: horchen" +(`migrate on --listen`) steht zuerst und ist der naheliegende: der Zugangspunkt steht, +aber es geht nichts raus, bis du eine erkannte Lampe freigibst. „Migration: AUTOMATIK" +(`migrate on`) folgt danach und bekommt eine **andere, deutlichere Rückfrage** — der +Migrations-Zugangspunkt hat ein öffentlich bekanntes Passwort (es steht in der alten +Klyqa-Firmware), und im Automatikmodus wird jede alte Klyqa-Lampe in Funkreichweite +ungefragt migriert, auch fremde. Die Rückfrage benennt genau das, statt nur „Wirklich?" +zu fragen. Der Taster ist nirgends Voraussetzung: fehlt `gpiozero` oder der Taster, protokolliert der Dienst eine Zeile und beendet sich ohne Fehler. Er fasst den SPI-Bus **nicht** an (zwei diff --git a/tools/station/button/button.py b/tools/station/button/button.py index 6d06ec3..3e89565 100644 --- a/tools/station/button/button.py +++ b/tools/station/button/button.py @@ -32,12 +32,17 @@ ninlight-station), nie ueber nachgebaute Logik: nur das CLI kennt die Profilumsc die Nachpruefungen ("es darf nie mehr als ein Stationsprofil aktiv sein") und den Zeitschalter. -SICHERHEITSREGEL: "Migration starten" startet ueber das Menue IMMER in der Horchphase -("migrate on --listen"), nie automatisch. Der Migrations-Zugangspunkt hat ein oeffentlich -bekanntes Passwort (es steht in der alten Klyqa-Firmware); ein Tastendruck, der ihn im -Automatikmodus hochfaehrt, wuerde jede alte Klyqa-Lampe in Funkreichweite ungefragt -migrieren, auch fremde. Die bequeme Geste muss die sichere sein - wer den Automatikmodus -will, nimmt die Kommandozeile, dort ist die Absicht eindeutig. +SICHERHEITSREGEL: die beiden Migrationseintraege sind GETRENNT und namentlich +unterscheidbar. "Migration: horchen" ruft "migrate on --listen" - der Zugangspunkt steht, +aber es geht nichts raus, bis der Bediener eine erkannte Lampe freigibt; das ist der +erste und naheliegende Eintrag. "Migration: AUTOMATIK" ruft "migrate on" ohne "--listen" +und bekommt eine ANDERE, deutlichere Rueckfrage (siehe action_migrate_auto()): der +Migrations-Zugangspunkt hat ein oeffentlich bekanntes Passwort (es steht in der alten +Klyqa-Firmware), und im Automatikmodus wird jede alte Klyqa-Lampe in Funkreichweite +ungefragt migriert, auch fremde. Die bequeme Geste bleibt damit die sichere, ohne dass +der Automatikmodus - fuer eine ganze Kiste eigener Lampen am Stueck - nur noch ueber die +Kommandozeile erreichbar waere, die im Zugangspunktbetrieb gerade nicht zur Verfuegung +steht. Der Taster ist NIRGENDS Voraussetzung: fehlt gpiozero, fehlt der Taster oder ist GPIO23 belegt, schreibt main() GENAU EINE Protokollzeile und beendet sich mit sys.exit(0) - @@ -115,15 +120,26 @@ class Action: """Was ein Menueeintrag bzw. die Haltegeste ausloest. `confirm` = braucht eine Rueckfrage. Zusage: JEDE veraendernde Auswahl bekommt eine - ("Anzeige auffrischen" veraendert nichts und braucht keine).""" + ("Anzeige auffrischen" veraendert nichts und braucht keine). + + `confirm_title`/`confirm_details` machen die Rueckfrage bei Bedarf DEUTLICHER als die + uebliche. Die Vorgabe ("RUECKFRAGE" + die zwei Bedienhinweise) reicht fuer eine + Auswahl, deren Tragweite im Eintragsnamen steht ("Station aus", "Herunterfahren"). + Sie reicht NICHT fuer den Automatikmodus: was dort passiert - jede alte Klyqa-Lampe + in Funkreichweite wird ungefragt migriert, auch fremde - steht in keinem + Eintragsnamen, und ein blosses "Wirklich?" fragt nach etwas, das der Bediener in + diesem Moment gar nicht vor Augen hat.""" def __init__(self, label: str, argv=(), kind: str = "cli", confirm: bool = True, - done_text: str | None = None) -> None: + done_text: str | None = None, confirm_title: str = "RUECKFRAGE", + confirm_details=None) -> None: self.label = label self.argv = tuple(argv) self.kind = kind self.confirm = confirm self.done_text = done_text or label + self.confirm_title = confirm_title + self.confirm_details = list(confirm_details) if confirm_details else None def __repr__(self) -> str: # nur fuer Testausgaben return "Action({!r}, {!r}, kind={!r})".format(self.label, self.argv, self.kind) @@ -134,6 +150,34 @@ def action_shutdown() -> Action: done_text="Faehrt herunter") +def action_migrate_listen() -> Action: + """Der naheliegende Weg in die Migration: Zugangspunkt an, aber es geht NICHTS raus, + bis der Bediener eine erkannte Lampe freigibt ("arm"). Deshalb steht dieser Eintrag + vor dem Automatikmodus und traegt die uebliche Rueckfrage.""" + return Action("Migration: horchen", ("migrate", "on", "--listen"), + done_text="Migration: horcht") + + +def action_migrate_auto() -> Action: + """Der Automatikmodus - fuer die eigene Kiste Lampen am Stueck, ohne jede einzeln + freizugeben. Er steht hier, weil das Verabreden ueber die Kommandozeile genau dann + nicht geht, wenn man ihn braucht: "on" macht wlan0 zum Zugangspunkt und reisst jede + SSH-Sitzung ueber WLAN ab. + + Seine Rueckfrage ist ABSICHTLICH eine andere als die der uebrigen Eintraege. Der + Migrations-Zugangspunkt hat ein oeffentlich bekanntes Passwort (es steht in der alten + Klyqa-Firmware), und im Automatikmodus wird jede alte Klyqa-Lampe in Funkreichweite + ungefragt migriert - auch die des Nachbarn, der davon nichts weiss und dessen Lampe + danach nicht mehr mit seiner App spricht. Ein "Wirklich?" fragt nach etwas, das der + Bediener in diesem Moment gar nicht vor Augen hat; deshalb steht die Folge in der + Rueckfrage selbst.""" + return Action("Migration: AUTOMATIK", ("migrate", "on"), + done_text="Migration: automatisch", + confirm_title="ACHTUNG", + confirm_details=["Flasht JEDE alte Klyqa-Lampe", + "in Reichweite - auch fremde"]) + + def action_cancel() -> Action: """Die Haltegeste in Ruhe: Station aus. Bewusst ohne Rueckfrage - sie IST die Rueckfrage (3 s halten), und sie ist der Notausgang, der ohne Umweg wirken muss.""" @@ -150,10 +194,11 @@ def menu_actions(state: dict) -> list[Action]: if not running: return [ Action("Rettung starten", ("recovery", "on"), done_text="Rettung laeuft"), - # IMMER --listen (siehe Modul-Docstring, Sicherheitsregel). Wer den - # Automatikmodus will, nimmt die Kommandozeile. - Action("Migration starten", ("migrate", "on", "--listen"), - done_text="Migration: horcht"), + # Reihenfolge ist Absicht: der horchende Modus zuerst, weil er der + # naheliegende und der harmlose ist - wer nur blaettert und irgendwo + # loslaesst, landet nicht im Automatikmodus. + action_migrate_listen(), + action_migrate_auto(), action_shutdown(), ] @@ -486,8 +531,13 @@ class ButtonUI: return _view("menu", "MENUE", "> " + selected.label, [a.label for a in rest] + ["kurz: weiter, halten: waehlen"]) if self.screen == "confirm" and self.pending is not None: - return _view("confirm", "RUECKFRAGE", self.pending.label, - ["halten (1,5 s): ja", "kurz: nein"]) + # Die Bedienhinweise stehen IMMER und IMMER ZULETZT - eine Rueckfrage, bei + # der man raten muss, wie man ablehnt, ist keine. Eine eigene Warnung des + # Eintrags (confirm_details) kommt davor, damit sie zuerst gelesen wird. + details = list(self.pending.confirm_details or []) + details += ["halten (1,5 s): ja", "kurz: nein"] + return _view("confirm", self.pending.confirm_title, self.pending.label, + details) if self.screen == "busy" and self.busy is not None: return _view("busy", "BITTE WARTEN", self.busy.label, []) if self.result is not None: diff --git a/tools/station/tests/test_button.py b/tools/station/tests/test_button.py index 1322c5b..aa80edc 100644 --- a/tools/station/tests/test_button.py +++ b/tools/station/tests/test_button.py @@ -163,7 +163,15 @@ def _labels(state): def test_menu_off_offers_starting_and_shutdown(): - assert _labels(STATE_OFF) == ["Rettung starten", "Migration starten", "Herunterfahren"] + assert _labels(STATE_OFF) == ["Rettung starten", "Migration: horchen", + "Migration: AUTOMATIK", "Herunterfahren"] + + +def test_the_listening_migration_comes_before_the_automatic_one(): + """Der horchende Modus ist der erste und naheliegende Eintrag - wer nur blaettert und + irgendwo loslaesst, soll nicht im Automatikmodus landen.""" + labels = _labels(STATE_OFF) + assert labels.index("Migration: horchen") < labels.index("Migration: AUTOMATIK") def test_menu_in_recovery_offers_stopping_refreshing_and_shutdown(): @@ -219,11 +227,11 @@ def test_a_short_press_opens_the_menu(): def test_a_short_press_steps_to_the_next_entry(): rec = _drive([_short(1.0), _short(2.0)], 4.0) assert rec.argvs() == [] - assert rec.views()[-1][1]["hero"] == "> Migration starten" + assert rec.views()[-1][1]["hero"] == "> Migration: horchen" def test_a_short_press_wraps_around_the_last_entry(): - rec = _drive([_short(1.0), _short(2.0), _short(3.0), _short(4.0)], 6.0) + rec = _drive([_short(1.0), _short(2.0), _short(3.0), _short(4.0), _short(5.0)], 7.0) assert rec.views()[-1][1]["hero"] == "> Rettung starten" @@ -254,15 +262,41 @@ def test_the_refresh_entry_runs_without_a_confirmation(): assert "confirm" not in rec.kinds() -def test_starting_the_migration_always_listens(): - """Sicherheitsregel, nicht verhandelbar: der Migrations-Zugangspunkt hat ein - oeffentlich bekanntes Passwort - ein Tastendruck, der ihn im Automatikmodus - hochfaehrt, wuerde jede alte Klyqa-Lampe in Funkreichweite ungefragt migrieren, auch - fremde.""" +def test_the_listening_entry_calls_the_cli_with_listen(): + """Der zweite Eintrag ("Migration: horchen") ruft das CLI MIT --listen: der + Zugangspunkt steht, es geht aber nichts raus, bis jemand eine erkannte Lampe + freigibt.""" rec = _drive([_short(1.0), _short(2.0), (3.0, 5.0), (6.0, 8.0)], 10.0) assert rec.argvs() == [["/usr/local/bin/ninlight-station", "migrate", "on", "--listen"]] - assert "--listen" in rec.argvs()[0] + + +def test_the_automatic_entry_calls_the_cli_without_listen(): + """Der dritte Eintrag ("Migration: AUTOMATIK") ruft dasselbe CLI OHNE --listen - das + ist der ganze Unterschied zwischen den beiden Eintraegen, und er entscheidet, ob + fremde Lampen ungefragt migriert werden.""" + rec = _drive([_short(1.0), _short(2.0), _short(3.0), (4.0, 6.0), (7.0, 9.0)], 11.0) + assert rec.argvs() == [["/usr/local/bin/ninlight-station", "migrate", "on"]] + assert "--listen" not in rec.argvs()[0] + + +def test_the_automatic_entry_asks_a_louder_question_than_the_others(): + """Zusage: die Rueckfrage des Automatikmodus benennt die Folge, statt nur "Wirklich?" + zu fragen - und sie unterscheidet sich sichtbar von der der uebrigen Eintraege.""" + rec = _drive([_short(1.0), _short(2.0), _short(3.0), (4.0, 6.0)], 8.0) + _t, view = rec.first_view("confirm") + assert view["title"] == "ACHTUNG" + assert view["hero"] == "Migration: AUTOMATIK" + text = " ".join(view["details"]).lower() + assert "jede" in text and "fremde" in text + # Die Bedienhinweise bleiben trotzdem stehen, und zwar zuletzt. + assert view["details"][-2:] == ["halten (1,5 s): ja", "kurz: nein"] + + # Gegenprobe am horchenden Eintrag: uebliche Rueckfrage, keine Warnung. + rec = _drive([_short(1.0), _short(2.0), (3.0, 5.0)], 7.0) + _t, plain = rec.first_view("confirm") + assert plain["title"] == "RUECKFRAGE" + assert plain["details"] == ["halten (1,5 s): ja", "kurz: nein"] def test_the_cancel_fires_at_three_seconds_while_the_button_is_still_held(): From 300edafc3e8f6997fe8694a5727c125595f58eea Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 02:17:36 +0200 Subject: [PATCH 142/150] feat(station): Umschalter fuer Migrationsimages mit/ohne WLAN-Daten Die WLAN-Zugangsdaten stecken zur Bauzeit in der Firmware, es gibt also zwei Abzuege je Ziel. Die Station haelt jetzt beide vor - images/ migrate-nocreds (aus tools/build_nocreds.sh, Lampe startet im Onboarding-AP) und images/migrate-wlan (aus build/, Lampe haengt sofort im eigenen WLAN) - und der Bediener waehlt per Umschalter, welcher ausgeliefert wird: "ninlight-station bildsatz" oder der Menueeintrag am Taster. Der Zustand liegt in /var/lib/ninlight-station/imageset, ueberlebt Neustarts und steht dauerhaft in der Fusszeile des Panels - in jedem Modus, auch bei ausgeschalteter Station: man soll vor dem Einschalten sehen, was gilt. Vorgabe und Rueckfallwert in JEDEM Zweifelsfall ist "ohne-wlan". Im Automatikmodus wird jede Klyqa-Lampe in Reichweite migriert, auch fremde; ein Abzug ohne Zugangsdaten ist dann ein harmloser Fehlgriff, einer mit Zugangsdaten traegt das eigene WLAN-Passwort in ein fremdes Geraet. Fehlt der gewaehlte Satz (Verzeichnis weg oder leer), wird NICHTS ausgeliefert - kein stilles Ausweichen auf den anderen Satz. Der Server antwortet 404 mit Status "noimageset", CLI und Panel sagen "FEHLT". server.py und die Stations-Unit muessen mit: die Auswahl der ausgelieferten Datei ist genau die Aufgabe des Servers, und ein Symlink-Tausch haette "gewaehlt, aber nicht vorhanden" von "dieses Modell fehlt" nicht mehr unterscheidbar gemacht. --- tools/station/README.md | 35 +++++ tools/station/button/button.py | 48 +++++- tools/station/deploy.sh | 205 +++++++++++++++++-------- tools/station/display/display.py | 59 ++++++- tools/station/display/render.py | 95 +++++++++++- tools/station/ninlight-station | 152 +++++++++++++++++- tools/station/ninlight-station.service | 2 +- tools/station/server.py | 122 +++++++++++++-- tools/station/setup_pi.sh | 16 ++ tools/station/tests/test_button.py | 86 +++++++++-- tools/station/tests/test_cli.sh | 158 +++++++++++++++++-- tools/station/tests/test_deploy.sh | 204 +++++++++++++++++++----- tools/station/tests/test_render.py | 166 +++++++++++++++++++- tools/station/tests/test_server.py | 200 +++++++++++++++++++----- tools/station/tests/test_setup_pi.sh | 33 ++++ 15 files changed, 1381 insertions(+), 200 deletions(-) diff --git a/tools/station/README.md b/tools/station/README.md index 2651c25..b046325 100644 --- a/tools/station/README.md +++ b/tools/station/README.md @@ -23,8 +23,43 @@ würde dort nur mit einer Fehlermeldung enden. tools/station/deploy.sh [target] Images ausliefern ssh ninlight-station on einschalten ssh ninlight-station status Zustand und letzte Auslieferungen + ssh ninlight-station bildsatz welcher Migrations-Bildsatz gilt ssh ninlight-station off ausschalten +## Bildsatz: mit oder ohne WLAN-Zugangsdaten + +Die WLAN-Zugangsdaten stecken **zur Bauzeit** in der Firmware (`config.local.cmake`). Es +gibt deshalb zwei Abzüge je Ziel, und die Station hält beide vor: + +| Umschalterstellung | Verzeichnis auf dem Pi | gebaut mit | die migrierte Lampe … | +|---|---|---|---| +| `ohne-wlan` (**Vorgabe**) | `images/migrate-nocreds/` | `tools/build_nocreds.sh` | startet im eigenen Onboarding-Zugangspunkt und wartet auf ein WLAN | +| `mit-wlan` | `images/migrate-wlan/` | `build/` | hängt sofort im WLAN aus `config.local.cmake` | + +Umgeschaltet wird per `ninlight-station bildsatz ohne-wlan|mit-wlan` oder am Taster +(Menüeintrag „Umschalten: …"). Das ist ein **Umschalter, keine Abfrage je Migration**: der +Zustand steht in `/var/lib/ninlight-station/imageset`, überlebt Neustarts und bleibt, bis +ihn jemand ändert. Genau deshalb steht er auch **dauerhaft in der Fußzeile des Panels** — +in jedem Modus, auch bei ausgeschalteter Station: man soll vor jedem Einschalten sehen, +was gilt, statt zwanzig Lampen mit den eigenen Zugangsdaten zu flashen, weil die +Einstellung von vorletzter Woche noch stand. + +**Warum `ohne-wlan` die Vorgabe ist** (und warum jeder Zweifelsfall — Datei fehlt, +unlesbar, unbekannter Inhalt — dorthin führt): im Automatikmodus wird jede alte +Klyqa-Lampe in Funkreichweite migriert, auch fremde. Mit dem Abzug ohne Zugangsdaten ist +ein Fehlgriff ärgerlich, aber harmlos — die Lampe steht im Onboarding-AP und wartet. Mit +dem Abzug mit Zugangsdaten steht das eigene WLAN-Passwort unwiderruflich in einem fremden +Gerät. Der teurere Fehler darf nie der sein, der ohne Zutun passiert. + +**Fehlt der gewählte Satz** (nie ausgeliefert, oder `tools/build_nocreds.sh` nie gelaufen), +wird **nichts** ausgeliefert — kein stilles Ausweichen auf den anderen Satz. Der Server +antwortet mit 404 und `status: noimageset`, `ninlight-station status` sagt „FEHLT", und auf +dem Panel steht „SATZ FEHLT". „Vorhanden" heißt dabei überall dasselbe: Verzeichnis da +**und** mindestens ein Image darin. + +`deploy.sh` liefert beide Sätze aus. Fehlt `build-nocreds/`, bleibt der Satz `ohne-wlan` +auf dem Pi unangetastet (statt geleert zu werden) und der Lauf meldet, womit er entsteht. + ## Taster am Gerät Sobald die Station läuft, wird `wlan0` zum Zugangspunkt und **jede SSH-Sitzung reißt ab**. diff --git a/tools/station/button/button.py b/tools/station/button/button.py index 3e89565..6c67957 100644 --- a/tools/station/button/button.py +++ b/tools/station/button/button.py @@ -97,6 +97,14 @@ RUN_DIR = Path(os.environ.get("NINLIGHT_RUN_DIR", "/run/ninlight-station")) # Anzeigedienst nicht ueberdauern. UI_FILE = Path(os.environ.get("NINLIGHT_UI_FILE", "/run/ninlight-button/ui.json")) STATION_CLI = os.environ.get("NINLIGHT_STATION_BIN", "/usr/local/bin/ninlight-station") +# Der Umschalter "Migrationsimages mit oder ohne WLAN-Zugangsdaten" - dieselbe Datei, die +# das CLI schreibt, der Server liest und der Anzeigedienst auf dem Panel zeigt. Hier wird +# sie NUR GELESEN (fuer die Beschriftung des Menueeintrags); umgeschaltet wird wie jede +# andere Aktion ueber das CLI. +IMAGESET_FILE = Path(os.environ.get("NINLIGHT_IMAGESET_FILE", + "/var/lib/ninlight-station/imageset")) +IMAGESET_NAMES = ("ohne-wlan", "mit-wlan") +IMAGESET_DEFAULT = "ohne-wlan" STATION_SERVICE = os.environ.get("NINLIGHT_STATION_SERVICE", "ninlight-station") # Das Herunterfahren geht NICHT ueber das CLI (das kennt es nicht) - es ist die einzige # Aktion dieses Dienstes, die an systemd selbst geht. @@ -178,6 +186,25 @@ def action_migrate_auto() -> Action: "in Reichweite - auch fremde"]) +def action_imageset(current: str) -> Action: + """Der Umschalter zwischen den beiden Migrations-Bildsaetzen. Der Eintrag nennt, WOHIN + umgeschaltet wird, nicht was gerade gilt: waehrend das Menue offen ist, ersetzt es das + Statusbild samt Fusszeile - "Bildsatz: mit WLAN" waere dann nicht zu entscheiden. + + Der Weg nach "mit-wlan" bekommt die deutlichere Rueckfrage, der Weg zurueck die + uebliche: nur die eine Richtung schreibt das WLAN-Passwort des Bedieners in jede + Lampe, die sie erwischt.""" + target = "mit-wlan" if current != "mit-wlan" else "ohne-wlan" + if target == "mit-wlan": + return Action("Umschalten: mit WLAN", ("bildsatz", "mit-wlan"), + done_text="Bildsatz: mit WLAN", + confirm_title="ACHTUNG", + confirm_details=["Jede migrierte Lampe bekommt", + "dein WLAN-Passwort"]) + return Action("Umschalten: ohne WLAN", ("bildsatz", "ohne-wlan"), + done_text="Bildsatz: ohne WLAN") + + def action_cancel() -> Action: """Die Haltegeste in Ruhe: Station aus. Bewusst ohne Rueckfrage - sie IST die Rueckfrage (3 s halten), und sie ist der Notausgang, der ohne Umweg wirken muss.""" @@ -190,6 +217,7 @@ def menu_actions(state: dict) -> list[Action]: running = bool(state.get("running")) mode = state.get("mode") target = state.get("target") + imageset = state.get("imageset") or IMAGESET_DEFAULT if not running: return [ @@ -199,6 +227,9 @@ def menu_actions(state: dict) -> list[Action]: # loslaesst, landet nicht im Automatikmodus. action_migrate_listen(), action_migrate_auto(), + # Der beste Zeitpunkt zum Umschalten ist VOR dem Einschalten - deshalb steht + # der Eintrag auch (und gerade) hier. + action_imageset(imageset), action_shutdown(), ] @@ -209,6 +240,7 @@ def menu_actions(state: dict) -> list[Action]: items.append(Action("Freigabe: {}".format(target), ("arm", str(target)), done_text="Freigegeben: {}".format(target))) items.append(Action("Station aus", ("off",), done_text="Station aus")) + items.append(action_imageset(imageset)) # Veraendert nichts am Betrieb - einzige Auswahl ohne Rueckfrage. items.append(Action("Anzeige auffrischen", ("display", "test"), confirm=False, done_text="Anzeige aufgefrischt")) @@ -268,7 +300,18 @@ def _service_active(service_name: str = STATION_SERVICE) -> bool: return result.returncode == 0 -def station_state(run_dir: Path = RUN_DIR, service_name: str = STATION_SERVICE) -> dict: +def _read_imageset(path: Path = IMAGESET_FILE) -> str: + """Der eingestellte Bildsatz, oder IMAGESET_DEFAULT bei fehlender/unlesbarer Datei + bzw. unbekanntem Inhalt - dieselbe Vorgabe wie in server.py und im CLI.""" + try: + value = path.read_text().strip() + except OSError: + return IMAGESET_DEFAULT + return value if value in IMAGESET_NAMES else IMAGESET_DEFAULT + + +def station_state(run_dir: Path = RUN_DIR, service_name: str = STATION_SERVICE, + imageset_path: Path = IMAGESET_FILE) -> dict: """Der Zustand, von dem die Menueeintraege abhaengen. Liest dieselben Quellen wie display.build_state() - RUN_DIR/mode (nicht status.json's "mode"-Feld, siehe dort) und status.json fuer das zuletzt erkannte Ziel - dupliziert aber keine Logik des @@ -284,7 +327,8 @@ def station_state(run_dir: Path = RUN_DIR, service_name: str = STATION_SERVICE) last_seen = status.get("last_seen") target = last_seen.get("target") if isinstance(last_seen, dict) else None return {"running": running, "mode": mode, - "target": target if isinstance(target, str) and target else None} + "target": target if isinstance(target, str) and target else None, + "imageset": _read_imageset(imageset_path)} # --- Bedienzustand in eine Datei schreiben ------------------------------------------ diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index 6de09fb..46f4148 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -85,64 +85,70 @@ NL_SIGN="$ROOT/tools/mkimage/nl_sign.py" # Manifest blind ersetzen - sonst wuerden Images/Eintraege einer Variante verschwinden, # die dieser Lauf mangels Schluessel gar nicht neu bauen konnte (Fixrunde 1, C2/I1). MIGRATE_COMPLETE=0 +# Dasselbe fuer den Satz OHNE WLAN-Zugangsdaten (siehe unten): "vollstaendig" heisst hier +# zusaetzlich, dass fuer JEDES Ziel ein Abzug aus build-nocreds/ vorlag - fehlt auch nur +# einer, darf --delete auf dem Pi nicht laufen. +NOCREDS_COMPLETE=0 +# Wo die Abzuege OHNE einkompilierte WLAN-Zugangsdaten liegen (tools/build_nocreds.sh, +# dieselbe Variable wie dort und in tools/release.sh). +NOCREDS_BUILD_REL=${NINLIGHT_NOCREDS_BUILD_REL:-build-nocreds} -if [ -d "$SIGN_DIR" ]; then - HAVE_PROD=0 - HAVE_DEV=0 - if [ -f "$KEY_PROD" ]; then - HAVE_PROD=1 - else - echo "Hinweis: Produktionsschluessel fehlt - keine _prod.img-Migrationsimages" >&2 - fi - if [ -f "$KEY_DEV" ]; then - HAVE_DEV=1 - else - echo "Hinweis: Entwicklungsschluessel fehlt - keine _dev.img-Migrationsimages" >&2 - fi - if [ "$HAVE_PROD" -eq 1 ] || [ "$HAVE_DEV" -eq 1 ]; then - # Erst HIER, nachdem feststeht dass ueberhaupt signiert wird, STAGE/migrate anlegen - # und das fehlende _gz.img zum Abbruch machen (Fixrunde 1, C2+C3): ein - # Schluesselverzeichnis ohne jeden Schluessel darf weder ein leeres migrate/ - # erzeugen (das wuerde ein vorhandenes migrate/ auf dem Pi mit --delete leerraeumen) - # noch am Fehlen des _gz.img scheitern, wenn gar nicht signiert werden soll. - mkdir -p "$STAGE/migrate" - for f in "${FILES[@]}"; do - base=$(basename "$f") - target=${base#ninlight_}; target=${target%_ota.img} - # Signiert wird IMMER das rohe _gz.img, niemals das fertige _ota.img: beide - # belegen denselben 512-Byte-Block am Dateiende (Trailer bzw. Signatur), ein - # signiertes _ota.img waere ein Image, das die alte Lampe ablehnt oder halb - # annimmt. - gz="$ROOT/build/$target/ninlight_${target}_gz.img" - [ -f "$gz" ] || { - echo "Fehler: $gz fehlt - kein Migrationsimage fuer $target moeglich" >&2; exit 1; } - if [ "$HAVE_PROD" -eq 1 ]; then - python3 "$NL_SIGN" sign --key "$KEY_PROD" --in "$gz" --out "$STAGE/migrate/${target}_prod.img" \ - || { echo "Fehler: Signieren von $target (prod) fehlgeschlagen" >&2; exit 1; } +# Signiert einen kompletten Satz Migrationsimages und schreibt sein manifest.json. +# $1 = Quellverzeichnis der Abzuege (build bzw. build-nocreds), absolut +# $2 = Zielverzeichnis unterhalb von $STAGE (migrate-wlan bzw. migrate-nocreds) +# $3 = "strict": ein fehlendes _gz.img ist ein Abbruch. "lax": es wird uebersprungen. +# Gibt 0 zurueck, wenn der Satz VOLLSTAENDIG entstanden ist (kein Ziel uebersprungen), +# sonst 1. Legt $STAGE/$2 nur an, wenn wirklich etwas darin landet - ein leeres +# Verzeichnis wuerde auf dem Pi mit --delete einen vorhandenen Satz leerraeumen. +sign_set() { + local src_root=$1 stage_sub=$2 strictness=$3 + local complete=1 produced=0 + local f base target gz + for f in "${FILES[@]}"; do + base=$(basename "$f") + target=${base#ninlight_}; target=${target%_ota.img} + # Signiert wird IMMER das rohe _gz.img, niemals das fertige _ota.img: beide + # belegen denselben 512-Byte-Block am Dateiende (Trailer bzw. Signatur), ein + # signiertes _ota.img waere ein Image, das die alte Lampe ablehnt oder halb + # annimmt. + gz="$src_root/$target/ninlight_${target}_gz.img" + if [ ! -f "$gz" ]; then + if [ "$strictness" = "strict" ]; then + echo "Fehler: $gz fehlt - kein Migrationsimage fuer $target moeglich" >&2; exit 1 fi - if [ "$HAVE_DEV" -eq 1 ]; then - python3 "$NL_SIGN" sign --key "$KEY_DEV" --in "$gz" --out "$STAGE/migrate/${target}_dev.img" \ - || { echo "Fehler: Signieren von $target (dev) fehlgeschlagen" >&2; exit 1; } - fi - done - if [ "$HAVE_PROD" -eq 1 ] && [ "$HAVE_DEV" -eq 1 ]; then - MIGRATE_COMPLETE=1 + echo "Hinweis: $gz fehlt - $target fehlt im Bildsatz '$stage_sub'" >&2 + complete=0 + continue fi - VERSION_ARGS=() - for t in "${!NINL_VERSION[@]}"; do - VERSION_ARGS+=("$t=${NINL_VERSION[$t]}") - done - # manifest.json aus den entstandenen Dateien - Groesse und sha256 werden hier neu - # berechnet statt aus dem Signierschritt uebernommen, damit die Datei die Wahrheit - # ueber das tatsaechlich abgelegte Image sagt. Form haelt sich an die Spec - # (docs/superpowers/spec, Abschnitt 4) und damit an das, was server.py tatsaechlich - # liest. Fixrunde 2, N2: "ninlight_version" steht PRO EINTRAG, nicht (mehr nur) - # manifestweit - ein einziges Feld fuer das ganze Manifest war schon falsch, sobald - # der Merge unten (bei nur einem Schluessel oder gezieltem Deploy) alte und neue - # Eintraege mit unterschiedlichen Firmware-Versionen zusammenfuehrt. Das - # manifestweite Feld bleibt nur als Bestandskompatibilitaet erhalten und wird nur - # gesetzt, wenn wirklich alle Eintraege dieselbe Version tragen. - python3 - "$STAGE/migrate" "${VERSION_ARGS[@]}" <<'PY' + mkdir -p "$STAGE/$stage_sub" + if [ "$HAVE_PROD" -eq 1 ]; then + python3 "$NL_SIGN" sign --key "$KEY_PROD" --in "$gz" --out "$STAGE/$stage_sub/${target}_prod.img" \ + || { echo "Fehler: Signieren von $target (prod, $stage_sub) fehlgeschlagen" >&2; exit 1; } + produced=1 + fi + if [ "$HAVE_DEV" -eq 1 ]; then + python3 "$NL_SIGN" sign --key "$KEY_DEV" --in "$gz" --out "$STAGE/$stage_sub/${target}_dev.img" \ + || { echo "Fehler: Signieren von $target (dev, $stage_sub) fehlgeschlagen" >&2; exit 1; } + produced=1 + fi + done + [ "$produced" -eq 1 ] || return 1 + local VERSION_ARGS=() + local t + for t in "${!NINL_VERSION[@]}"; do + VERSION_ARGS+=("$t=${NINL_VERSION[$t]}") + done + # manifest.json aus den entstandenen Dateien - Groesse und sha256 werden hier neu + # berechnet statt aus dem Signierschritt uebernommen, damit die Datei die Wahrheit + # ueber das tatsaechlich abgelegte Image sagt. Form haelt sich an die Spec + # (docs/superpowers/spec, Abschnitt 4) und damit an das, was server.py tatsaechlich + # liest. Fixrunde 2, N2: "ninlight_version" steht PRO EINTRAG, nicht (mehr nur) + # manifestweit - ein einziges Feld fuer das ganze Manifest war schon falsch, sobald + # der Merge unten (bei nur einem Schluessel oder gezieltem Deploy) alte und neue + # Eintraege mit unterschiedlichen Firmware-Versionen zusammenfuehrt. Das + # manifestweite Feld bleibt nur als Bestandskompatibilitaet erhalten und wird nur + # gesetzt, wenn wirklich alle Eintraege dieselbe Version tragen. + python3 - "$STAGE/$stage_sub" "${VERSION_ARGS[@]}" <<'PY' import hashlib import json import os @@ -183,6 +189,51 @@ with open(os.path.join(migrate_dir, "manifest.json"), "w") as fh: json.dump(manifest, fh, indent=2, sort_keys=True) fh.write("\n") PY + [ "$complete" -eq 1 ] +} + +HAVE_PROD=0 +HAVE_DEV=0 +if [ -d "$SIGN_DIR" ]; then + if [ -f "$KEY_PROD" ]; then + HAVE_PROD=1 + else + echo "Hinweis: Produktionsschluessel fehlt - keine _prod.img-Migrationsimages" >&2 + fi + if [ -f "$KEY_DEV" ]; then + HAVE_DEV=1 + else + echo "Hinweis: Entwicklungsschluessel fehlt - keine _dev.img-Migrationsimages" >&2 + fi + if [ "$HAVE_PROD" -eq 1 ] || [ "$HAVE_DEV" -eq 1 ]; then + BOTH_KEYS=0 + [ "$HAVE_PROD" -eq 1 ] && [ "$HAVE_DEV" -eq 1 ] && BOTH_KEYS=1 + + # ZWEI Bildsaetze, weil die WLAN-Zugangsdaten zur BAUZEIT in der Firmware stecken + # und die Station beide vorhalten soll (der Bediener schaltet per + # "ninlight-station bildsatz" bzw. am Taster um): + # + # migrate-wlan/ aus build/ - Lampe haengt sofort im eigenen WLAN + # migrate-nocreds/ aus build-nocreds/ - Lampe startet im Onboarding-AP + # + # Der Satz aus build/ ist streng: dort liegen die _ota.img, die oben schon geprueft + # wurden, ein fehlendes _gz.img daneben ist ein kaputter Baum. Der Satz aus + # build-nocreds/ ist nachsichtig: er entsteht aus einem EIGENEN, optionalen Bau + # (tools/build_nocreds.sh), und ein Deploy darf nicht daran scheitern, dass der noch + # nicht gelaufen ist - dann fehlt der Satz eben, und die Station liefert in dieser + # Stellung sichtbar nichts aus, statt heimlich den anderen Satz zu nehmen. + echo " Bildsatz migrate-wlan (aus build/)" + if sign_set "$ROOT/build" migrate-wlan strict; then + [ "$BOTH_KEYS" -eq 1 ] && MIGRATE_COMPLETE=1 + fi + echo " Bildsatz migrate-nocreds (aus $NOCREDS_BUILD_REL/)" + if sign_set "$ROOT/$NOCREDS_BUILD_REL" migrate-nocreds lax; then + [ "$BOTH_KEYS" -eq 1 ] && NOCREDS_COMPLETE=1 + else + if [ ! -d "$STAGE/migrate-nocreds" ]; then + echo "Hinweis: keine Abzuege unter $ROOT/$NOCREDS_BUILD_REL - der Bildsatz 'ohne-wlan' bleibt auf dem Pi unveraendert. Erzeugen mit tools/build_nocreds.sh." >&2 + fi + fi else echo "Hinweis: $SIGN_DIR enthaelt weder Produktions- noch Entwicklungsschluessel - keine Migrationsimages erzeugt" >&2 fi @@ -216,10 +267,11 @@ rsync -a -c --delete --exclude=__pycache__ -e "$SSH_BIN" "$HERE/display/" "$HOST rsync -a -c --delete --exclude=__pycache__ -e "$SSH_BIN" "$HERE/button/" "$HOST:/tmp/ninlight-button/" # Pfade als Variablen mit Default: so kann ein Test die Installation gegen # ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen lassen. -"$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" "$MIGRATE_COMPLETE" <<'REMOTE' +"$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" "$MIGRATE_COMPLETE" "$NOCREDS_COMPLETE" <<'REMOTE' set -e FULL_SYNC=$1 MIGRATE_COMPLETE=$2 +NOCREDS_COMPLETE=$3 ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} @@ -249,26 +301,32 @@ if [ "$FULL_SYNC" -eq 1 ]; then # ausklammern, sonst wuerde --delete es leeren, wenn dieser Deploy ohne # Schluesselverzeichnis lief und /tmp/ninlight-images/migrate deshalb gar nicht # existiert. - sudo rsync -a -c --delete --exclude=/migrate /tmp/ninlight-images/ "$ROOT_DIR/images/" + sudo rsync -a -c --delete --exclude=/migrate-wlan --exclude=/migrate-nocreds /tmp/ninlight-images/ "$ROOT_DIR/images/" else # Gezielter Deploy: nur die uebertragenen Dateien ersetzen/ergaenzen, alle # anderen Geraetetypen im Zielverzeichnis bleiben erhalten (kein --delete). echo " gezielter Deploy - andere Geraetetypen im Zielverzeichnis bleiben erhalten" - sudo rsync -a -c --exclude=/migrate /tmp/ninlight-images/ "$ROOT_DIR/images/" + sudo rsync -a -c --exclude=/migrate-wlan --exclude=/migrate-nocreds /tmp/ninlight-images/ "$ROOT_DIR/images/" fi +# $1 = Name des Bildsatzverzeichnisses, $2 = 1, wenn dieser Lauf den Satz VOLLSTAENDIG +# beschreibt (beide Schluessel, alle Ziele) und --delete deshalb erlaubt ist. +# # Migrationsimages getrennt behandeln: fehlt migrate/ in der Uebertragung (weil auf # dem Entwicklungsrechner kein Schluesselverzeichnis oder gar kein Schluessel # vorhanden war), darf ein vorhandenes migrate/ auf dem Pi NICHT geleert werden - # sonst zerstoert ein Deploy ohne Schluesselzugriff die Bestueckung frueherer Deploys # (derselbe Fehler wie frueher bei den _ota.img, siehe oben, nur diesmal beim Fehlen # statt beim Ersetzen). -if [ -d /tmp/ninlight-images/migrate ]; then - sudo install -d -m 0755 "$ROOT_DIR/images/migrate" - if [ "$FULL_SYNC" -eq 1 ] && [ "$MIGRATE_COMPLETE" -eq 1 ]; then +sync_migrate_set() { +set=$1 +complete=$2 +if [ -d "/tmp/ninlight-images/$set" ]; then + sudo install -d -m 0755 "$ROOT_DIR/images/$set" + if [ "$FULL_SYNC" -eq 1 ] && [ "$complete" -eq 1 ]; then # Nur wenn dieser Lauf mit BEIDEN Schluesseln signiert hat, beschreibt # STAGE/migrate den vollstaendigen Soll-Zustand - erst dann darf --delete # ueberzaehlige Dateien wegraeumen und manifest.json blind ersetzt werden. - sudo rsync -a -c --delete /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" + sudo rsync -a -c --delete "/tmp/ninlight-images/$set/" "$ROOT_DIR/images/$set/" else # Gezielter Deploy ODER ein Voll-Deploy mit nur einem der beiden Schluessel: # in beiden Faellen kennt dieser Lauf nur einen Teil dessen, was auf dem Pi liegen @@ -278,9 +336,9 @@ if [ -d /tmp/ninlight-images/migrate ]; then # sondern mit dem, was schon da ist, zusammengefuehrt (Fixrunde 1, I1 - sonst # verschwinden die Eintraege aller Ziele/Varianten, die dieser Lauf nicht neu # gebaut hat, obwohl ihre Images unveraendert liegen bleiben). - sudo rsync -a -c --exclude=/manifest.json /tmp/ninlight-images/migrate/ "$ROOT_DIR/images/migrate/" - if [ -f /tmp/ninlight-images/migrate/manifest.json ]; then - sudo python3 - "$ROOT_DIR/images/migrate/manifest.json" /tmp/ninlight-images/migrate/manifest.json <<'MERGE' + sudo rsync -a -c --exclude=/manifest.json "/tmp/ninlight-images/$set/" "$ROOT_DIR/images/$set/" + if [ -f "/tmp/ninlight-images/$set/manifest.json" ]; then + sudo python3 - "$ROOT_DIR/images/$set/manifest.json" "/tmp/ninlight-images/$set/manifest.json" <<'MERGE' import json import sys @@ -340,7 +398,22 @@ else # sich von hier aus unterscheiden - beide Faelle liefern ein leeres migrate/ von # diesem Rechner. Die lokale Meldung (deploy.sh, "Hinweis: ...") ist bereits genau; # diese hier behauptet absichtlich keinen bestimmten Grund (Fixrunde 2, Kleinigkeit). - echo " keine Migrationsimages von diesem Rechner - migrate/ auf dem Pi bleibt unangetastet" + echo " keine Migrationsimages fuer $set von diesem Rechner - $set/ auf dem Pi bleibt unangetastet" +fi +} + +# Beide Bildsaetze. Sie sind voneinander unabhaengig: liegt nur einer von diesem Rechner +# vor (z.B. weil tools/build_nocreds.sh noch nicht gelaufen ist), bleibt der andere auf +# dem Pi unveraendert - der Umschalter zeigt dann eben auf einen Satz, der fehlt, und die +# Station liefert in dieser Stellung sichtbar nichts aus. +sync_migrate_set migrate-wlan "$MIGRATE_COMPLETE" +sync_migrate_set migrate-nocreds "$NOCREDS_COMPLETE" +# Altbestand: bis zum Umschalter hiess das einzige Verzeichnis "migrate" und enthielt die +# Abzuege MIT WLAN-Zugangsdaten. Es wird von niemandem mehr gelesen; liegen bleiben darf +# es trotzdem nicht, sonst glaubt der naechste Mensch am Pi, es sei der gueltige Satz. +if [ -d "$ROOT_DIR/images/migrate" ]; then + sudo rm -rf "$ROOT_DIR/images/migrate" + echo " Altbestand entfernt: images/migrate (heisst jetzt images/migrate-wlan)" fi sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" sudo install -m 0644 /tmp/products.py "$ROOT_DIR/products.py" diff --git a/tools/station/display/display.py b/tools/station/display/display.py index a8765a1..fb1e531 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -58,6 +58,18 @@ SERVICE_NAME = os.environ.get("NINLIGHT_STATION_SERVICE", "ninlight-station") # RuntimeDirectory der Hauptunit und verschwindet beim regulaeren "off" - also # ausgerechnet dann, wenn das Menue "Rettung starten"/"Migration starten" anbietet. UI_FILE = Path(os.environ.get("NINLIGHT_UI_FILE", "/run/ninlight-button/ui.json")) +# Der Umschalter "Migrationsimages mit oder ohne WLAN-Zugangsdaten" (siehe +# ninlight-station, bildsatz_cmd, und server.py:read_image_set). NUR GELESEN, und +# absichtlich aus derselben Datei wie der Server - nicht aus status.json: der Zustand +# muss auch dann auf dem Panel stehen, wenn die Station AUS ist. Genau davor soll er +# schuetzen ("man soll vor jedem Einschalten sehen, was gilt"), und ausgeschaltet +# schreibt server.py nichts mehr. +IMAGESET_FILE = Path(os.environ.get("NINLIGHT_IMAGESET_FILE", + "/var/lib/ninlight-station/imageset")) +# Dieselben Namen und dieselbe Vorgabe wie in server.py und im CLI - im Zweifel der +# Satz OHNE Zugangsdaten (dort steht die Begruendung). +IMAGESET_DIRS = {"ohne-wlan": "migrate-nocreds", "mit-wlan": "migrate-wlan"} +IMAGESET_DEFAULT = "ohne-wlan" POLL_INTERVAL_S = 5 # Solange eine Bedienung laeuft, waere ein 5-Sekunden-Takt unbenutzbar (ein Tastendruck @@ -197,8 +209,37 @@ def _service_active(service_name: str) -> bool: return True +def _read_imageset(imageset_path: Path, conf: dict[str, str]) -> tuple[str, bool]: + """(Name des eingestellten Bildsatzes, liegt er ueberhaupt auf dem Pi?). + + Fehlt die Datei, ist sie unlesbar oder steht etwas Unbekanntes darin, gilt + IMAGESET_DEFAULT - dieselbe Vorgabe wie in server.py und im CLI, sonst wuerde das + Panel etwas anderes behaupten als der Server tut. + + `ok` ist False, wenn der GEWAEHLTE Satz nicht auf dem Pi liegt - nach derselben + Regel wie server.py:migrate_set_present(): Verzeichnis vorhanden UND mindestens ein + Image darin. Dann liefert server.py nichts aus (Status "noimageset"), und genau das + muss am Panel stehen, statt dass der Bediener vor einer Lampe steht, die nichts + bekommt. Ohne bekanntes IMAGE_DIR (Konfigurationsdatei nicht lesbar) wird nichts + behauptet: dann gilt "ok", denn eine unbelegte Warnung ist schlechter als keine.""" + try: + value = imageset_path.read_text().strip() + except OSError: + value = "" + name = value if value in IMAGESET_DIRS else IMAGESET_DEFAULT + image_dir = conf.get("IMAGE_DIR") + if not image_dir: + return name, True + set_dir = Path(image_dir) / IMAGESET_DIRS[name] + try: + return name, any(p.name.endswith(".img") for p in set_dir.iterdir()) + except OSError: + return name, False + + def build_state(run_dir: Path = RUN_DIR, conf_path: Path = CONF_PATH, - service_name: str = SERVICE_NAME) -> dict: + service_name: str = SERVICE_NAME, + imageset_path: Path = IMAGESET_FILE) -> dict: """Baut den zusammengefuehrten Zustand, den render.lines()/compose() erwarten. Modus kommt vorrangig aus RUN_DIR/mode (Vorgabe "recovery", wie @@ -211,14 +252,22 @@ def build_state(run_dir: Path = RUN_DIR, conf_path: Path = CONF_PATH, Phase/armed_target kommen aus control.json, nur im Migrationsmodus massgeblich - der Rettungsmodus ignoriert control.json ebenso wie server.py das tut. "off" ueberschreibt - alles, wenn der Hauptdienst gar nicht laeuft.""" + alles, wenn der Hauptdienst gar nicht laeuft. + + Der Bildsatz ("imageset"/"imageset_ok") kommt in JEDEN Zustand, auch in "off" und in + den Rettungsmodus: er ist ein dauerhafter Umschalter, und man soll ihn VOR dem + Einschalten sehen - eine Einstellung von vorletzter Woche, die erst beim Blaettern im + Menue auffaellt, hat genau den Zweck verfehlt.""" + conf = _read_conf(conf_path) + imageset, imageset_ok = _read_imageset(imageset_path, conf) + if not _service_active(service_name): - return {"mode": "off"} + return {"mode": "off", "imageset": imageset, "imageset_ok": imageset_ok} mode = _read_mode_file(run_dir) status = _read_json(run_dir / "status.json") - conf = _read_conf(conf_path) - state: dict = {"mode": mode, "served": status.get("served"), "recent": status.get("recent")} + state: dict = {"mode": mode, "served": status.get("served"), "recent": status.get("recent"), + "imageset": imageset, "imageset_ok": imageset_ok} if mode == "recovery": state["ssid"] = conf.get("SSID") diff --git a/tools/station/display/render.py b/tools/station/display/render.py index fb7dc83..b123c07 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -64,17 +64,29 @@ TEXT_MARGIN_PX = 2 # big - Zustaende fast ohne Inhalt ("Station aus", "Status unbekannt"): # gross und mittig, damit man quer durch den Raum sieht, dass nichts # laeuft. +# footer - eine Zeile am UNTEREN Panelrand, ueber einen Trennstrich abgesetzt, +# unabhaengig vom Zeilenfluss darueber. Genau ein Block benutzt sie: der +# Bildsatz-Umschalter (mit oder ohne einkompilierte WLAN-Zugangsdaten). +# Er gehoert dorthin, weil er ein DAUERZUSTAND ist und nicht zum +# wechselnden Inhalt: er gilt in jedem Modus, auch bei ausgeschalteter +# Station, und man soll ihn vor dem Einschalten sehen - nicht erst, +# wenn zwanzig Lampen mit den eigenen Zugangsdaten geflasht sind. +# Waere er eine gewoehnliche Beiwerkzeile, fiele er im vollsten Zustand +# als letzte unten heraus, also ausgerechnet waehrend einer laufenden +# Migration. ROLE_HEADER = "header" ROLE_HEADER_PHASE = "header_phase" ROLE_HERO = "hero" ROLE_DETAIL = "detail" ROLE_BIG = "big" +ROLE_FOOTER = "footer" HEADER_FONT_PX = 20 HEADER_PHASE_FONT_PX = 13 HERO_FONT_PX = 16 DETAIL_FONT_PX = 11 BIG_FONT_PX = 26 +FOOTER_FONT_PX = 11 # Zeilenhoehen so gewaehlt, dass der vollste Zustand (Migration mit erkannter Lampe: # Kopfbalken + grosse Zeile + fuenf Beiwerkzeilen) auf die 122 px passt, mit sichtbarem @@ -94,6 +106,16 @@ DETAIL_LINE_HEIGHT_PX = 13 # bleibt dabei auch im vollsten Zustand jede Zeile sichtbar, es wird nichts abgeschnitten. BOTTOM_MARGIN_PX = 10 +# Das Band am unteren Rand fuer ROLE_FOOTER (Trennstrich + eine 11-px-Zeile). Es liegt +# UEBER BOTTOM_MARGIN_PX, der Rand bleibt also weiterhin tintenfrei; der Zeilenfluss +# darueber bricht entsprechend frueher ab. Das kostet im vollsten Zustand (Migration mit +# erkannter Lampe) eine Beiwerkzeile - die unterste, also die unwichtigste nach der +# Sortierung in blocks(). Der Preis ist gewollt: welcher Bildsatz gilt, ist die einzige +# Auskunft auf diesem Panel, die man BRAUCHT, bevor etwas passiert. +FOOTER_BAND_HEIGHT_PX = 14 +# Abstand des Trennstrichs zur Fusszeile. +FOOTER_RULE_GAP_PX = 2 + # Mindestabstand zwischen dem Ende des Modus-Texts und dem Anfang der Phase in der # Kopfzeile (beide weiss auf demselben schwarzen Balken) - ohne diesen Puffer koennten # beide Texte bei einer langen Phase (z.B. "FREIGEGEBEN: ") @@ -167,6 +189,17 @@ PHASE_LABELS = {"auto": "AUTO", "listen": "LISTEN"} ARMED_LABEL = "FREIGEGEBEN" ARMED_DETAIL_PREFIX = "Freigabe: " +# Der Bildsatz-Umschalter in der Fusszeile. Bewusst als Aussage ueber die LAMPE formuliert +# und nicht ueber das Verzeichnis: "migrate-wlan" sagt dem Bediener am Geraet nichts, +# "Lampe: sofort ins WLAN" sagt ihm genau das, was er wissen muss, bevor er einschaltet. +# Der gefaehrliche Zustand steht in Grossbuchstaben - er ist der, den man uebersehen soll +# und darf. +IMAGESET_LABELS = { + "ohne-wlan": "Lampe -> Onboarding-AP (ohne WLAN-Daten)", + "mit-wlan": "Lampe -> SOFORT INS EIGENE WLAN", +} +IMAGESET_MISSING_SUFFIX = " - SATZ FEHLT" + # Woraus die Station die Signiervariante abgeleitet hat (products.classify_variant): # "-dev" kann in der Produkt-ID stehen, in der Version, oder in beiden. Die Quelle steht # mit auf dem Panel, weil genau hier am 2026-09-15 ein falsch signiertes Image @@ -217,6 +250,10 @@ def blocks(state: dict) -> list[tuple[str, str]]: mode = state.get("mode") out: list[tuple[str, str]] = [] + # Die Fusszeile haengt an JEDEM Statusbild - auch an "Station aus" und an "Status + # unbekannt", die beide sofort zurueckkehren. Sie wird deshalb hier gebaut und unten + # bei jedem Rueckgabepunkt angehaengt. + footer = _imageset_blocks(state) if mode == "off": # Die Station ist komplett aus (kein status.json vorhanden) - display.py setzt @@ -224,14 +261,14 @@ def blocks(state: dict) -> list[tuple[str, str]]: # (insbesondere keine SSID - die waere aus einer alten Konfigurationsdatei und # gerade nicht aktiv). Genau deshalb ROLE_BIG: die Zeile ist der ganze Inhalt. out.append(("Station aus", ROLE_BIG)) - return out + return out + footer if mode not in ("recovery", "migration"): # Unbekannter oder fehlender Modus, z.B. ein frisch aufgesetzter Pi, auf dem noch # nichts geschrieben wurde. Lieber ehrlich "unbekannt" zeigen als einen Modus zu # unterstellen. out.append((f"Status unbekannt ({mode})" if mode else "Status unbekannt", ROLE_BIG)) - return out + return out + footer out.append((MODE_LABELS[mode], ROLE_HEADER)) @@ -338,7 +375,22 @@ def blocks(state: dict) -> list[tuple[str, str]]: out.append((f"{_hhmm(last.get('ts'))} {last.get('target') or '?'} " f"{last.get('status') or '?'}", ROLE_DETAIL)) - return out + return out + footer + + +def _imageset_blocks(state: dict) -> list[tuple[str, str]]: + """Die Fusszeile mit dem Bildsatz-Umschalter, oder [] wenn der Zustand keinen traegt + (aeltere display.py-Fassung, von Hand zusammengesetzter Zustand in einem Test). + Fehlt das Verzeichnis des GEWAEHLTEN Satzes, sagt die Zeile das - dann liefert + server.py naemlich nichts aus, und ein Panel, das trotzdem nur den Namen zeigt, + behauptet einen Betrieb, den es nicht gibt.""" + name = state.get("imageset") + label = IMAGESET_LABELS.get(name) + if not label: + return [] + if state.get("imageset_ok") is False: + label += IMAGESET_MISSING_SUFFIX + return [(label, ROLE_FOOTER)] def _ui_blocks(ui: dict) -> list[tuple[str, str]]: @@ -501,6 +553,7 @@ def compose(state: dict): ROLE_HERO: (HERO_FONT_PX, True), ROLE_DETAIL: (DETAIL_FONT_PX, False), ROLE_BIG: (BIG_FONT_PX, True), + ROLE_FOOTER: (FOOTER_FONT_PX, True), } loaded: dict = {} @@ -532,6 +585,36 @@ def compose(state: dict): has_header_phase = any(role == ROLE_HEADER_PHASE for _, role in block_list) header_mode_right_edge_px = 0.0 + # Die Fusszeile sitzt an einem FESTEN Platz am unteren Rand, nicht im Zeilenfluss - + # der Zeilenfluss darueber muss deshalb frueher abbrechen. Ohne Fusszeile bleibt die + # Grenze exakt die alte (BOTTOM_MARGIN_PX), damit sich an allen bisherigen Bildern + # nichts verschiebt. + footer_texts = [text for text, role in block_list if role == ROLE_FOOTER] + block_list = [(text, role) for text, role in block_list if role != ROLE_FOOTER] + has_footer = bool(footer_texts) + footer_top_px = PANEL_HEIGHT_PX - BOTTOM_MARGIN_PX - FOOTER_BAND_HEIGHT_PX + flow_limit_px = footer_top_px if has_footer else PANEL_HEIGHT_PX - BOTTOM_MARGIN_PX + # ROLE_BIG bekommt das ganze Panel (bisheriges Verhalten) bzw. alles oberhalb der + # Fusszeile - der untere Rand ist dort kein Abbruchkriterium, sondern nur eine + # Reserve fuer den Zeilenfluss. + big_height_px = footer_top_px if has_footer else PANEL_HEIGHT_PX + + # Die Fusszeile wird VOR dem Zeilenfluss gezeichnet und ist aus block_list bereits + # herausgenommen - sonst haette sie der "break" der Abbruchbedingung weiter unten + # verschluckt, sobald der Zeilenfluss vorher voll ist (also ausgerechnet im vollsten + # Zustand, waehrend einer laufenden Migration). Sie sitzt an festem Platz; ihre + # Reihenfolge innerhalb von blocks() spielt fuer das Bild keine Rolle. + if has_footer: + footer_font = font_for(ROLE_FOOTER) + # Trennstrich ueber die volle Breite, darunter die Zeile - so ist auf einen + # Blick klar, dass das keine weitere Beiwerkzeile ist, sondern ein Zustand, + # der unabhaengig vom Rest gilt. + draw.line((0, footer_top_px, PANEL_WIDTH_PX - 1, footer_top_px), fill=0) + _draw_text(draw, fit(footer_texts[0], footer_font), footer_font, + top=footer_top_px + FOOTER_RULE_GAP_PX, + height=FOOTER_BAND_HEIGHT_PX - FOOTER_RULE_GAP_PX, + fill=0, align="center") + y = TEXT_MARGIN_PX for raw_text, role in block_list: font = font_for(role) @@ -540,7 +623,9 @@ def compose(state: dict): # Einziger Block dieses Zustands (siehe blocks()) - er bekommt das ganze # Panel. text = fit(raw_text, font) - _draw_text(draw, text, font, top=0, height=PANEL_HEIGHT_PX, fill=0, + # Mittig im Bereich UEBER der Fusszeile (ohne Fusszeile: im ganzen Panel) - + # sonst sitzt "Station aus" sichtbar zu tief und beruehrt den Trennstrich. + _draw_text(draw, text, font, top=0, height=big_height_px, fill=0, align="center") continue @@ -573,7 +658,7 @@ def compose(state: dict): text = fit(raw_text, font) line_height = HERO_LINE_HEIGHT_PX if role == ROLE_HERO else DETAIL_LINE_HEIGHT_PX - if y + line_height > PANEL_HEIGHT_PX - BOTTOM_MARGIN_PX: + if y + line_height > flow_limit_px: # Passt nicht mehr aufs Panel, ohne in den fuer BOTTOM_MARGIN_PX # reservierten unteren Rand zu ragen - abschneiden statt ueberzeichnen. Die # Reihenfolge in blocks() ist deshalb nach Wichtigkeit sortiert. diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 25f40fa..1b47493 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -22,6 +22,31 @@ CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf} RUN_DIR=${NINLIGHT_RUN_DIR:-/run/ninlight-station} STATION_LIB=${NINLIGHT_STATION_LIB:-/opt/ninlight-station} +# --- Umschalter: Migrationsimages mit oder ohne WLAN-Zugangsdaten -------------------- +# +# Die WLAN-Zugangsdaten stecken zur BAUZEIT in der Firmware. Es liegen deshalb zwei +# Saetze Migrationsimages auf dem Pi: +# +# ohne-wlan -> $IMAGE_DIR/migrate-nocreds (tools/build_nocreds.sh): die migrierte +# Lampe startet im eigenen Onboarding-Zugangspunkt und wartet auf ein WLAN +# mit-wlan -> $IMAGE_DIR/migrate-wlan (build/): die Lampe haengt sofort im WLAN +# des Bedieners +# +# Der Umschalter ist ein ZUSTAND, keine Abfrage je Migration: er bleibt, bis jemand ihn +# aendert, und er steht dauerhaft auf dem Panel (display.py liest dieselbe Datei). +# Deshalb liegt sie NICHT in $RUN_DIR (tmpfs, weg nach jedem Neustart) und nicht in +# $CONF (0600 root:root, und setup_pi.sh traegt dort Schluessel nach) - sondern in einer +# eigenen, dauerhaften Datei mit 0644, die auch der Anzeigedienst lesen kann. +IMAGESET_FILE=${NINLIGHT_IMAGESET_FILE:-/var/lib/ninlight-station/imageset} +# Die sichere Vorgabe, und zwar in JEDEM Zweifelsfall (Datei fehlt, unlesbar, unbekannter +# Inhalt). Begruendung: im Automatikmodus wird jede alte Klyqa-Lampe in Funkreichweite +# migriert, auch fremde. Mit dem Abzug OHNE Zugangsdaten ist ein Fehlgriff aergerlich, +# aber harmlos - die Lampe steht im Onboarding-AP und wartet. Mit dem Abzug MIT +# Zugangsdaten steht das eigene WLAN-Passwort unwiderruflich in einem fremden Geraet. +# Der teurere Fehler darf nie der sein, der ohne Zutun passiert. server.py:read_image_set() +# haelt dieselbe Vorgabe unabhaengig ein. +IMAGESET_DEFAULT=ohne-wlan + # --- Zeitschalter ("Auto-Aus") ----------------------------------------------------- # # Rein OPTIONAL: nur "--for " legt einen an. Ohne die Angabe passiert hier @@ -109,8 +134,111 @@ images() { find "$IMAGE_DIR" -maxdepth 1 -name 'ninlight_*_ota.img' 2>/dev/null | sort } +# --- Bildsatz lesen, schreiben, benennen -------------------------------------------- + +# Der rohe Inhalt der Umschalterdatei (ohne Leerraum), oder leer. Nur fuer die Meldung +# unten gedacht - entschieden wird ausschliesslich mit imageset_get(). +imageset_raw() { + cat "$IMAGESET_FILE" 2>/dev/null | tr -d ' \t\r\n' || true +} + +# Der eingestellte Bildsatz, oder $IMAGESET_DEFAULT bei fehlender/unlesbarer Datei bzw. +# unbekanntem Inhalt. Gibt IMMER einen gueltigen Namen aus und scheitert nie - ein +# kaputter Umschalter darf weder "off" noch das Panel aufhalten. +imageset_get() { + _value=$(imageset_raw) + case "$_value" in + ohne-wlan|mit-wlan) printf '%s' "$_value" ;; + *) printf '%s' "$IMAGESET_DEFAULT" ;; + esac +} + +# Das Verzeichnis zum Namen des Bildsatzes ($1). +imageset_dir() { + case "$1" in + mit-wlan) printf '%s' "$IMAGE_DIR/migrate-wlan" ;; + *) printf '%s' "$IMAGE_DIR/migrate-nocreds" ;; + esac +} + +imageset_label() { + case "$1" in + mit-wlan) printf 'mit-wlan (Lampe haengt sofort im eigenen WLAN)' ;; + *) printf 'ohne-wlan (Lampe startet im Onboarding-Zugangspunkt)' ;; + esac +} + +# Schreibt den Umschalter atomar mit 0644 - dasselbe Muster wie write_mode()/ +# write_control(), aber in ein DAUERHAFTES Verzeichnis: der Zustand soll einen Neustart +# ueberleben (das ist der Sinn eines Umschalters), und der Anzeigedienst muss ihn lesen +# koennen. +imageset_set() { + sudo mkdir -p "$(dirname "$IMAGESET_FILE")" + _tmp="$(dirname "$IMAGESET_FILE")/.imageset-$$" + printf '%s\n' "$1" | sudo tee "$_tmp" >/dev/null && sudo chmod 0644 "$_tmp" || return 1 + sudo mv -f "$_tmp" "$IMAGESET_FILE" +} + migrate_images() { - find "$IMAGE_DIR/migrate" -maxdepth 1 -name '*.img' 2>/dev/null | sort + find "$(imageset_dir "$(imageset_get)")" -maxdepth 1 -name '*.img' 2>/dev/null | sort +} + +# "bildsatz" ohne Argument: nur berichten. Mit Argument: umschalten. +bildsatz_cmd() { + _want=${1:-} + _now=$(imageset_get) + if [ -z "$_want" ]; then + imageset_status_lines + return 0 + fi + case "$_want" in + ohne-wlan|mit-wlan) ;; + *) + echo "Fehler: unbekannter Bildsatz '$_want' - erlaubt sind 'ohne-wlan' und 'mit-wlan'" >&2 + exit 2 + ;; + esac + imageset_set "$_want" || { + echo "Fehler: $IMAGESET_FILE konnte nicht geschrieben werden" >&2 + exit 1 + } + echo "Bildsatz: $(imageset_label "$_want")" + [ "$_now" = "$_want" ] && echo " (war schon eingestellt)" + _dir=$(imageset_dir "$_want") + if [ "$(find "$_dir" -maxdepth 1 -name '*.img' 2>/dev/null | wc -l | tr -d ' ')" -eq 0 ]; then + # Kein stiller Rueckfall auf den anderen Satz - server.py liefert dann gar nichts + # aus (Status "noimageset"), und das Panel sagt "FEHLT". Hier trotzdem sofort + # warnen, damit niemand erst an der Lampe merkt, dass nichts kommt. + echo "Warnung: $_dir fehlt oder ist leer - in diesem Bildsatz wird NICHTS ausgeliefert. tools/station/deploy.sh erneut laufen lassen." >&2 + fi + if [ "$_want" = "mit-wlan" ]; then + echo "Achtung: migrierte Lampen haengen damit sofort im WLAN aus config.local.cmake - auch fremde Lampen, die im Automatikmodus mitgenommen werden." >&2 + fi +} + +# Die zwei Zeilen, die "status" und "bildsatz" ohne Argument gemeinsam ausgeben. +imageset_status_lines() { + _set=$(imageset_get) + _dir=$(imageset_dir "$_set") + # "Vorhanden" heisst: mindestens ein Image darin - dieselbe Regel wie + # server.py:migrate_set_present() und display.py:_read_imageset(). Ein leeres + # Verzeichnis liefert genauso nichts aus wie ein fehlendes. + _n=$(migrate_images | wc -l | tr -d ' ') + if [ "$_n" -gt 0 ]; then + _state="$_n Image(s)" + else + _state="FEHLT - es wird nichts ausgeliefert" + fi + printf 'Bildsatz: %s\n' "$(imageset_label "$_set")" + printf ' %s (%s)\n' "$_dir" "$_state" + # Steht in der Datei etwas, das kein Bildsatzname ist, gilt stillschweigend die + # Vorgabe - das ist richtig so (sicher), aber der Bediener muss es erfahren: sonst + # glaubt er, sein Eintrag von Hand haette gewirkt. + _raw=$(imageset_raw) + if [ -n "$_raw" ] && [ "$_raw" != "$_set" ]; then + printf " (unbekannte Einstellung '%s' in %s - Vorgabe gilt)\n" \ + "$_raw" "$IMAGESET_FILE" + fi } # Faehrt erst jedes andere Stationsprofil herunter, dann (falls $1 nicht leer ist) @@ -518,8 +646,10 @@ migrate_on() { done [ "$_for_seen" -eq 1 ] || _for_minutes="" need_profile "$MIGRATE_AP_CONNECTION" + _set=$(imageset_get) + _setdir=$(imageset_dir "$_set") n=$(migrate_images | wc -l | tr -d ' ') - [ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR/migrate enthaelt keine Migrationsimages - die Station antwortet auf alles mit 404" >&2 + [ "$n" -gt 0 ] || echo "Warnung: $_setdir enthaelt keine Migrationsimages - die Station antwortet auf alles mit 404" >&2 # Faehrt ninlight-recovery (falls es laeuft) herunter, BEVOR qconnex-firmware # hochkommt, und prueft danach nach, dass wirklich nur eines aktiv ist. ap_switch "$MIGRATE_AP_CONNECTION" @@ -530,6 +660,10 @@ migrate_on() { } start_and_verify "$MIGRATE_AP_CONNECTION" "migration" "$_phase" echo "Migration an: SSID $MIGRATE_SSID auf 192.168.8.1, Phase $_phase, $n Migrationsimage(s)" + # Der Bildsatz gehoert in JEDE Einschaltmeldung: er entscheidet, ob die migrierte + # Lampe das WLAN-Passwort des Bedieners mitbekommt, und er kann von vorletzter Woche + # stehen. + echo "Bildsatz: $(imageset_label "$_set")" echo "Achtung: das Passwort dieses Zugangspunkts ist aus der alten Firmware oeffentlich bekannt - siehe README.md." >&2 echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist." autooff_arm_if_requested @@ -861,6 +995,7 @@ PY fi printf 'Modus : %s\n' "$_modus" printf 'Phase : %s\n' "$_phase" + imageset_status_lines printf 'Erkannt: %s\n' "$_erkannt" printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || true)" autooff_status_line @@ -898,8 +1033,16 @@ display_cmd() { } usage() { - echo "Aufruf: ninlight-station on [--for ]|recovery {on [--for ]|off}|migrate {on [--listen] [--for ]|off}|arm []|listen|renew []|hold|display {test|clear|off}|off|status" >&2 + echo "Aufruf: ninlight-station on [--for ]|recovery {on [--for ]|off}|migrate {on [--listen] [--for ]|off}|arm []|listen|bildsatz [ohne-wlan|mit-wlan]|renew []|hold|display {test|clear|off}|off|status" >&2 echo "" >&2 + echo " bildsatz Umschalter: welcher Satz Migrationsimages ausgeliefert wird." >&2 + echo " Ohne Argument nur berichten. 'ohne-wlan' (Vorgabe) liefert die" >&2 + echo " Abzuege aus tools/build_nocreds.sh - die migrierte Lampe startet" >&2 + echo " im eigenen Onboarding-Zugangspunkt. 'mit-wlan' liefert die" >&2 + echo " Abzuege aus build/ - die Lampe haengt sofort im WLAN aus" >&2 + echo " config.local.cmake, auch wenn es eine fremde Lampe ist, die im" >&2 + echo " Automatikmodus mitgenommen wurde. Der Zustand bleibt, bis ihn" >&2 + echo " jemand aendert, und steht dauerhaft auf dem Panel." >&2 echo " --for Zeitschalter: die Station schaltet sich nach selbst" >&2 echo " wieder aus (zurueck ins Heim-WLAN). OPTIONAL - ohne '--for'" >&2 echo " bleibt sie an, bis jemand 'off' sagt. Nuetzlich, wenn du ueber" >&2 @@ -956,6 +1099,9 @@ case "$_cmd" in listen) listen_cmd ;; + bildsatz) + bildsatz_cmd "${1:-}" + ;; renew) renew_cmd "${1:-}" ;; diff --git a/tools/station/ninlight-station.service b/tools/station/ninlight-station.service index bda200e..6c32d19 100644 --- a/tools/station/ninlight-station.service +++ b/tools/station/ninlight-station.service @@ -20,7 +20,7 @@ StartLimitBurst=5 [Service] Type=simple EnvironmentFile=/etc/ninlight-station.conf -ExecStart=/usr/bin/python3 /opt/ninlight-station/server.py --image-dir ${IMAGE_DIR} --mode-file /run/ninlight-station/mode +ExecStart=/usr/bin/python3 /opt/ninlight-station/server.py --image-dir ${IMAGE_DIR} --mode-file /run/ninlight-station/mode --imageset-file /var/lib/ninlight-station/imageset Restart=always RestartSec=2 diff --git a/tools/station/server.py b/tools/station/server.py index 11d9cb1..2ec9fd1 100755 --- a/tools/station/server.py +++ b/tools/station/server.py @@ -62,10 +62,54 @@ def trailer_version(path: Path) -> str: return tail[VERSION_OFF:VERSION_OFF + VERSION_LEN].rstrip(b"\x00").decode("ascii", "replace") +# --- Bildsatz: mit oder ohne einkompilierte WLAN-Zugangsdaten ----------------------- +# +# Die WLAN-Zugangsdaten stecken zur BAUZEIT in der Firmware (config.local.cmake). Es gibt +# deshalb zwei Abzuege je Ziel, und beide liegen auf dem Pi: +# +# "ohne-wlan" -> images/migrate-nocreds/ aus tools/build_nocreds.sh: die migrierte +# Lampe startet im eigenen Onboarding-Zugangspunkt (ninlight-) +# und wartet, bis ihr jemand ein WLAN nennt. +# "mit-wlan" -> images/migrate-wlan/ aus build/: die Lampe haengt sofort im WLAN +# des Bedieners. +# +# Welcher ausgeliefert wird, entscheidet ein UMSCHALTER (ninlight-station bildsatz bzw. +# der Menueeintrag am Taster), nicht eine Abfrage je Migration: sein Zustand bleibt, bis +# ihn jemand aendert, und er steht dauerhaft auf dem Panel. +# +# Vorgabe ist "ohne-wlan", und zwar in JEDEM Zweifelsfall (Datei fehlt, unlesbar, +# unbekannter Inhalt). Begruendung: im Automatikmodus wird jede alte Klyqa-Lampe in +# Funkreichweite migriert, auch fremde. Ein Abzug OHNE Zugangsdaten ist dann ein +# aergerlicher, aber harmloser Fehlgriff - die Lampe steht im Onboarding-AP und wartet. +# Ein Abzug MIT Zugangsdaten traegt das WLAN-Passwort des Bedieners unwiderruflich in +# ein fremdes Geraet. Der teurere Fehler darf nie der sein, der ohne Zutun passiert. +IMAGE_SET_DIRS = {"ohne-wlan": "migrate-nocreds", "mit-wlan": "migrate-wlan"} +DEFAULT_IMAGE_SET = "ohne-wlan" + + +def read_image_set(path: Path | None) -> str: + """Der eingestellte Bildsatz, oder DEFAULT_IMAGE_SET, wenn die Datei fehlt, unlesbar + ist oder etwas Unbekanntes enthaelt. Wirft nie - eine kaputte Umschalterdatei darf + den Server nicht abschiessen, und sie darf vor allem nicht auf den teureren der + beiden Saetze zeigen (siehe IMAGE_SET_DIRS).""" + if path is None: + return DEFAULT_IMAGE_SET + try: + value = path.read_text().strip() + except OSError: + return DEFAULT_IMAGE_SET + return value if value in IMAGE_SET_DIRS else DEFAULT_IMAGE_SET + + class Station: def __init__(self, image_dir: Path, state_path: Path, stall_after: int | None, - mode: str = "recovery", control_path: Path | None = None) -> None: + mode: str = "recovery", control_path: Path | None = None, + imageset_path: Path | None = None) -> None: self.image_dir = image_dir + # Bei JEDER Anfrage neu gelesen, nicht einmal beim Start: der Umschalter soll + # wirken, ohne den Dienst neu zu starten - ein Neustart mitten in einer + # laufenden Auslieferung waere genau das, was die Station nicht tun darf. + self.imageset_path = imageset_path self.state_path = state_path self.stall_after = stall_after self.mode = mode @@ -77,6 +121,8 @@ class Station: # wurde - das ist es, was die Anzeige gross darstellt (Spec 5.2). self.last_seen: dict | None = None self._manifest_mtime: float | None = None + # Zu welchem Bildsatz der Cache gehoert - siehe _load_manifest(). + self._manifest_path: Path | None = None # Der geparste Inhalt von manifest.json (oder {}, wenn sie fehlt/kaputt/falsch # geformt ist) statt nur einer einzelnen Version - seit Fixrunde 2 (N2) traegt # jeder Eintrag seine eigene Version, image_version() muss also den ganzen @@ -105,24 +151,52 @@ class Station: def image_path(self, target: str) -> Path: return self.image_dir / f"ninlight_{target}_ota.img" + def image_set(self) -> str: + """Der gerade eingestellte Bildsatz ("ohne-wlan"/"mit-wlan").""" + return read_image_set(self.imageset_path) + + def migrate_dir(self) -> Path: + """Das Verzeichnis des EINGESTELLTEN Bildsatzes. Es kann fehlen (der Satz wurde + nie ausgeliefert) - dann wird NICHTS ausgeliefert, siehe Handler: auf den anderen + Satz auszuweichen waere genau der Fehlgriff, gegen den der Umschalter da ist.""" + return self.image_dir / IMAGE_SET_DIRS[self.image_set()] + def migrate_image_path(self, target: str, variant: str) -> Path: - return self.image_dir / "migrate" / f"{target}_{variant}.img" + return self.migrate_dir() / f"{target}_{variant}.img" + + def migrate_set_present(self) -> bool: + """Liegt der eingestellte Bildsatz ueberhaupt auf dem Pi? "Vorhanden" heisst: das + Verzeichnis gibt es UND es enthaelt mindestens ein Image. Ein leeres Verzeichnis + zaehlt bewusst als nicht vorhanden - es entsteht genauso leicht (angelegt, aber nie + bestueckt) und unterscheidet sich fuer den Bediener in nichts; das Panel und das + CLI beurteilen es nach derselben Regel, sonst behaupten drei Stellen dreierlei.""" + try: + return any(p.name.endswith(".img") for p in self.migrate_dir().iterdir()) + except OSError: + return False def _load_manifest(self) -> dict: - """Geparster Inhalt von /migrate/manifest.json, neu eingelesen bei - jeder Aenderung der Datei (mtime-Vergleich). Fehlt sie, ist sie unlesbar/kaputtes - JSON, oder ist ihr Wurzelelement kein Objekt, ist das Ergebnis {} - dieselbe - Formpruefung wie beim Merge in deploy.sh (Fixrunde 2, N1), damit eine falsch - geformte, aber syntaktisch gueltige Datei nicht mit einem AttributeError den - Server abschiesst.""" - path = self.image_dir / "migrate" / "manifest.json" + """Geparster Inhalt von manifest.json IM EINGESTELLTEN Bildsatz, neu eingelesen + bei jeder Aenderung der Datei (mtime-Vergleich). Fehlt sie, ist sie + unlesbar/kaputtes JSON, oder ist ihr Wurzelelement kein Objekt, ist das Ergebnis + {} - dieselbe Formpruefung wie beim Merge in deploy.sh (Fixrunde 2, N1), damit + eine falsch geformte, aber syntaktisch gueltige Datei nicht mit einem + AttributeError den Server abschiesst.""" + path = self.migrate_dir() / "manifest.json" try: mtime = path.stat().st_mtime except OSError: self._manifest_mtime = None + self._manifest_path = path self._manifest_cache = {} return self._manifest_cache - if mtime != self._manifest_mtime: + # Der Pfad gehoert mit in den Vergleich: nach einem Umschalten zeigt er auf eine + # ANDERE Datei, die zufaellig dieselbe mtime tragen kann (beide entstehen im + # selben Deploy, oft in derselben Sekunde). Ohne ihn behielte der Cache die + # Versionen des vorigen Satzes - und die stuenden dann in status.json und am + # Panel neben einem Image, das gar nicht von dort kommt. + if mtime != self._manifest_mtime or path != getattr(self, "_manifest_path", None): + self._manifest_path = path try: data = json.loads(path.read_text()) except (OSError, ValueError): @@ -428,6 +502,19 @@ class Handler(BaseHTTPRequestHandler): return path = self.station.migrate_image_path(target, variant) + if not self.station.migrate_set_present(): + # Der EINGESTELLTE Bildsatz liegt gar nicht auf dem Pi. Hier wird bewusst + # nichts ausgeliefert und auch nicht auf den anderen Satz ausgewichen: der + # Umschalter existiert genau dafuer, dass niemand versehentlich Lampen mit + # den eigenen WLAN-Zugangsdaten flasht. Ein eigener Status, damit der + # Bediener am Panel den Unterschied zu "dieses Modell fehlt" sieht. + if phase == "armed": + self.station.restore_armed(target) + self.station.record_migration(ip, target, variant, pid, lamp_version, name, + version, 0, "noimageset", variant_source) + self.reject(404, "selected image set not available") + self.log(404, 0, started) + return if not path.is_file(): if phase == "armed": # claim_delivery() hat die Freigabe schon verbraucht, bevor wir wussten, @@ -552,6 +639,10 @@ def main(argv: list[str] | None = None) -> int: "Vorgabe bei fehlender/kaputter Datei ist 'recovery'") p.add_argument("--control", type=Path, default=Path("/run/ninlight-station/control.json"), help="Steuerdatei fuer Phase/Freigabe im Migrationsmodus") + p.add_argument("--imageset-file", type=Path, default=None, + help="Datei mit 'ohne-wlan' oder 'mit-wlan' (Umschalter des " + "Migrations-Bildsatzes); Vorgabe bei fehlender/kaputter Datei " + "ist 'ohne-wlan'") p.add_argument("--stall-after", type=int, default=None, help="test aid: stop sending after N bytes but keep the connection open") a = p.parse_args(argv) @@ -561,13 +652,22 @@ def main(argv: list[str] | None = None) -> int: return 2 mode = resolve_mode(a.mode, a.mode_file) - station = Station(a.image_dir, a.state, a.stall_after, mode=mode, control_path=a.control) + station = Station(a.image_dir, a.state, a.stall_after, mode=mode, control_path=a.control, + imageset_path=a.imageset_file) httpd = ThreadingHTTPServer((a.bind, a.port), Handler) httpd.daemon_threads = True httpd.station = station # type: ignore[attr-defined] images = sorted(p.name for p in a.image_dir.glob("ninlight_*_ota.img")) print(f"{now_iso()} listening on {a.bind}:{a.port}, mode={mode}, {len(images)} image(s): " f"{', '.join(images) or 'none'}", flush=True) + if mode == "migration": + # Nur im Migrationsmodus: der Bildsatz betrifft ausschliesslich migrate/-Images. + # Im Journal, weil "warum kam das Image mit meinem WLAN?" sonst nirgends + # nachschlagbar waere - das Panel zeigt nur den JETZIGEN Zustand. + selected = station.image_set() + present = "vorhanden" if station.migrate_set_present() else "FEHLT" + print(f"{now_iso()} image set: {selected} ({station.migrate_dir()}, {present})", + flush=True) if a.stall_after is not None: print(f"{now_iso()} WARNING: --stall-after {a.stall_after} is a test aid", flush=True) try: diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index cb58833..c46cdb4 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -154,6 +154,22 @@ if [ ! -f "$STAGE_DIR/ninlight-button/button.py" ]; then fi sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images" "$ROOT_DIR/display" "$ROOT_DIR/button" +# Dauerhafter Zustand, der einen Neustart ueberleben muss - bisher nur der Umschalter des +# Migrations-Bildsatzes (/var/lib/ninlight-station/imageset). BEWUSST NICHT in /run: der +# Umschalter ist ein Zustand, den der Bediener setzt und der bis zur naechsten Aenderung +# gilt; ein tmpfs wuerde ihn bei jedem Neustart stillschweigend zuruecksetzen. +# +# 0755, weil der Anzeigedienst die Datei lesen muss (er zeigt den Zustand dauerhaft auf +# dem Panel). Die Datei SELBST wird hier nicht angelegt: fehlt sie, gilt in CLI, Server +# und Anzeige derselbe sichere Vorgabewert "ohne-wlan" - eine hier vorgeschriebene Datei +# waere eine zweite Quelle fuer denselben Wert. +# +# Die beiden Bildsatzverzeichnisse werden hier ebenfalls NICHT angelegt: ein leeres +# Verzeichnis ist fuer den Bediener nicht von einem bestueckten zu unterscheiden, und +# genau das soll das Panel melden koennen ("SATZ FEHLT"). Sie entstehen in deploy.sh, +# sobald wirklich Images hineinkommen. +STATE_DIR=${NINLIGHT_STATION_STATE_DIR:-/var/lib/ninlight-station} +sudo install -d -m 0755 "$STATE_DIR" sudo install -m 0755 "$STAGE_DIR/server.py" "$ROOT_DIR/server.py" sudo install -m 0644 "$STAGE_DIR/products.py" "$ROOT_DIR/products.py" sudo install -m 0755 "$STAGE_DIR/ninlight-station" "$BIN_PATH" diff --git a/tools/station/tests/test_button.py b/tools/station/tests/test_button.py index aa80edc..98949fe 100644 --- a/tools/station/tests/test_button.py +++ b/tools/station/tests/test_button.py @@ -26,10 +26,14 @@ import button # noqa: E402 # --- Zustaende, von denen die Menueeintraege abhaengen ------------------------------- -STATE_OFF = {"running": False, "mode": "recovery", "target": None} -STATE_RECOVERY = {"running": True, "mode": "recovery", "target": None} -STATE_MIGRATION = {"running": True, "mode": "migration", "target": "gu10_rgb"} -STATE_MIGRATION_BLIND = {"running": True, "mode": "migration", "target": None} +STATE_OFF = {"running": False, "mode": "recovery", "target": None, + "imageset": "ohne-wlan"} +STATE_RECOVERY = {"running": True, "mode": "recovery", "target": None, + "imageset": "ohne-wlan"} +STATE_MIGRATION = {"running": True, "mode": "migration", "target": "gu10_rgb", + "imageset": "ohne-wlan"} +STATE_MIGRATION_BLIND = {"running": True, "mode": "migration", "target": None, + "imageset": "ohne-wlan"} # --- Fake-Uhr, Taster-Doppel, Protokoll --------------------------------------------- @@ -164,7 +168,8 @@ def _labels(state): def test_menu_off_offers_starting_and_shutdown(): assert _labels(STATE_OFF) == ["Rettung starten", "Migration: horchen", - "Migration: AUTOMATIK", "Herunterfahren"] + "Migration: AUTOMATIK", "Umschalten: mit WLAN", + "Herunterfahren"] def test_the_listening_migration_comes_before_the_automatic_one(): @@ -175,21 +180,22 @@ def test_the_listening_migration_comes_before_the_automatic_one(): def test_menu_in_recovery_offers_stopping_refreshing_and_shutdown(): - assert _labels(STATE_RECOVERY) == ["Station aus", "Anzeige auffrischen", - "Herunterfahren"] + assert _labels(STATE_RECOVERY) == ["Station aus", "Umschalten: mit WLAN", + "Anzeige auffrischen", "Herunterfahren"] def test_menu_in_migration_offers_the_detected_target(): assert _labels(STATE_MIGRATION) == ["Freigabe: gu10_rgb", "Station aus", - "Anzeige auffrischen", "Herunterfahren"] + "Umschalten: mit WLAN", "Anzeige auffrischen", + "Herunterfahren"] def test_menu_in_migration_without_a_detected_target_offers_no_release(): """Ohne last_seen gibt es nichts freizugeben - der Eintrag faellt ganz weg, statt eine Freigabe ins Leere anzubieten (die das CLI ohnehin abweisen wuerde).""" assert "Freigabe: " not in " ".join(_labels(STATE_MIGRATION_BLIND)) - assert _labels(STATE_MIGRATION_BLIND) == ["Station aus", "Anzeige auffrischen", - "Herunterfahren"] + assert _labels(STATE_MIGRATION_BLIND) == ["Station aus", "Umschalten: mit WLAN", + "Anzeige auffrischen", "Herunterfahren"] def test_only_the_refresh_entry_skips_the_confirmation(): @@ -231,7 +237,8 @@ def test_a_short_press_steps_to_the_next_entry(): def test_a_short_press_wraps_around_the_last_entry(): - rec = _drive([_short(1.0), _short(2.0), _short(3.0), _short(4.0), _short(5.0)], 7.0) + rec = _drive([_short(1.0), _short(2.0), _short(3.0), _short(4.0), _short(5.0), + _short(6.0)], 8.0) assert rec.views()[-1][1]["hero"] == "> Rettung starten" @@ -257,7 +264,8 @@ def test_a_short_press_in_the_confirmation_cancels_and_returns_to_the_menu(): def test_the_refresh_entry_runs_without_a_confirmation(): - rec = _drive([_short(1.0), _short(2.0), (3.0, 5.0)], 7.0, state=STATE_RECOVERY) + rec = _drive([_short(1.0), _short(2.0), _short(3.0), (4.0, 6.0)], 8.0, + state=STATE_RECOVERY) assert rec.argvs() == [["/usr/local/bin/ninlight-station", "display", "test"]] assert "confirm" not in rec.kinds() @@ -342,8 +350,8 @@ def test_a_blocking_action_does_not_break_the_hold_gesture(): def test_the_waiting_screen_is_written_before_the_slow_call(): - rec = _drive([_short(1.0), _short(2.0), (3.0, 5.0)], 8.0, state=STATE_RECOVERY, - action_duration_s=4.0) + rec = _drive([_short(1.0), _short(2.0), _short(3.0), (4.0, 6.0)], 9.0, + state=STATE_RECOVERY, action_duration_s=4.0) kinds = [k for _t, k, _p in rec.events] run_at = kinds.index("run") assert rec.events[run_at - 1][2].get("view") == "busy" @@ -493,3 +501,53 @@ def test_an_unreadable_station_state_keeps_the_menu_closed(caplog): ui.update(0.0, True) assert ui.update(0.2, False) is None assert ui.view(0.3) == {"view": "idle", "active": False} + + +# --- Bildsatz-Umschalter im Menue ---------------------------------------------------- + + +def test_the_image_set_entry_names_where_it_switches_to(): + """Waehrend das Menue offen ist, ersetzt es das Statusbild samt Fusszeile - der + Eintrag muss deshalb das ZIEL nennen, nicht den jetzigen Zustand.""" + assert "Umschalten: mit WLAN" in _labels(STATE_OFF) + ohne = dict(STATE_OFF, imageset="mit-wlan") + assert "Umschalten: ohne WLAN" in _labels(ohne) + assert "Umschalten: mit WLAN" not in _labels(ohne) + + +def test_the_image_set_entry_switches_through_the_cli(): + actions = button.CliActions(cli="/usr/local/bin/ninlight-station") + assert actions.argv_for(button.action_imageset("ohne-wlan")) == [ + "/usr/local/bin/ninlight-station", "bildsatz", "mit-wlan"] + assert actions.argv_for(button.action_imageset("mit-wlan")) == [ + "/usr/local/bin/ninlight-station", "bildsatz", "ohne-wlan"] + + +def test_switching_to_wifi_credentials_asks_louder_than_switching_back(): + loud = button.action_imageset("ohne-wlan") + back = button.action_imageset("mit-wlan") + assert loud.confirm and back.confirm + assert loud.confirm_title == "ACHTUNG" + assert back.confirm_title == "RUECKFRAGE" + assert "WLAN-Passwort" in " ".join(loud.confirm_details) + assert back.confirm_details is None + + +def test_the_image_set_entry_really_runs_the_switch(tmp_path): + """Durch die echte Hauptschleife, nicht nur ueber menu_actions(): vierter Eintrag im + ausgeschalteten Zustand, Auswahl halten, Rueckfrage halten.""" + rec = _drive([_short(1.0), _short(2.0), _short(3.0), _short(4.0), + (5.0, 7.0), (8.0, 10.0)], 12.0) + assert rec.argvs() == [["/usr/local/bin/ninlight-station", "bildsatz", "mit-wlan"]] + + +def test_station_state_reads_the_same_switch_file_as_the_cli(tmp_path, monkeypatch): + monkeypatch.setattr(button, "_service_active", lambda service_name: False) + switch = tmp_path / "imageset" + state = lambda: button.station_state(run_dir=tmp_path, service_name="x", + imageset_path=switch) + assert state()["imageset"] == "ohne-wlan" # Datei fehlt -> sichere Vorgabe + switch.write_text("mit-wlan\n") + assert state()["imageset"] == "mit-wlan" + switch.write_text("quatsch") + assert state()["imageset"] == "ohne-wlan" diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index e3a362a..aa802f6 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -217,8 +217,12 @@ EOF setup_sandbox() { local sandbox=$1 unitfile=${2:-$REAL_UNIT} rm -rf "$sandbox" - mkdir -p "$sandbox/run" "$sandbox/images/migrate" "$sandbox/bin" "$sandbox/nmstate" \ - "$sandbox/lib/display" + mkdir -p "$sandbox/run" "$sandbox/images/migrate-nocreds" "$sandbox/images/migrate-wlan" \ + "$sandbox/bin" "$sandbox/nmstate" "$sandbox/lib/display" "$sandbox/var" + # Je ein Image je Bildsatz - "vorhanden" heisst fuer CLI, Server und Anzeige + # gleichermassen "mindestens ein Image darin". + echo "ohne" >"$sandbox/images/migrate-nocreds/foo_dev.img" + echo "mit" >"$sandbox/images/migrate-wlan/foo_dev.img" echo "dummy" >"$sandbox/images/ninlight_foo_ota.img" # NINLIGHT_STATION_LIB zeigt auf diese Sandbox statt auf tools/station selbst. @@ -292,6 +296,7 @@ run_cli() { NINLIGHT_STATION_CONF="$sandbox/station.conf" \ NINLIGHT_RUN_DIR="$sandbox/run" \ NINLIGHT_STATION_LIB="$sandbox/lib" \ + NINLIGHT_IMAGESET_FILE="$sandbox/var/imageset" \ "$CLI" "$@" } @@ -334,7 +339,7 @@ echo echo "=== Fall 3: 'migrate on' faehrt ninlight-recovery herunter, BEVOR qconnex-firmware hochkommt; control.json phase=auto ===" SBX="$TMPROOT/t3" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" run_cli "$SBX" on >/dev/null 2>&1 # Recovery vorher an : >"$SBX/cmd.log" set +e @@ -356,7 +361,7 @@ echo echo "=== Fall 4: 'migrate on --listen' wie Fall 3, aber phase=listen ===" SBX="$TMPROOT/t4" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" run_cli "$SBX" on >/dev/null 2>&1 set +e OUT=$(run_cli "$SBX" migrate on --listen 2>&1) @@ -369,7 +374,8 @@ echo echo "=== Fall 5: 'migrate on' ohne Migrationsimages -> Warnung auf stderr, Rueckgabewert 0 ===" SBX="$TMPROOT/t5" setup_sandbox "$SBX" -# images/migrate/ bleibt leer +# der gewaehlte Bildsatz ("ohne-wlan") wird geleert - setup_sandbox bestueckt beide +rm -f "$SBX/images/migrate-nocreds"/*.img set +e OUT=$(run_cli "$SBX" migrate on 2>"$SBX/stderr.log") RC=$? @@ -468,7 +474,7 @@ echo echo "=== Fall 12: 'status' im Migrationsmodus zeigt Modus, Phase und zuletzt erkannte Lampe ===" SBX="$TMPROOT/t12" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" run_cli "$SBX" migrate on >/dev/null 2>&1 # phase=auto in control.json, wie es der echte Ablauf waere # status.json so, wie server.py sie beim Start geschrieben haette (mode/last_seen). cat >"$SBX/run/status.json" <<'JSON' @@ -513,7 +519,7 @@ echo echo "=== Fall 15 (wichtigster Test): 'migrate on' bei laufendem Recovery-Modus laesst GENAU EIN Profil aktiv ===" SBX="$TMPROOT/t15" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" run_cli "$SBX" on >/dev/null 2>&1 ACTIVE_BEFORE=$(cat "$SBX/nmstate/active") echo "$ACTIVE_BEFORE" | grep -qx "ninlight-recovery" && pass "Vorbedingung: ninlight-recovery laeuft" || fail "Vorbedingung nicht erfuellt: $ACTIVE_BEFORE" @@ -538,7 +544,7 @@ SBX="$TMPROOT/tC1" BROKEN_UNIT="$TMPROOT/broken-ninlight-station.service" grep -v '^RuntimeDirectoryPreserve=restart' "$REAL_UNIT" >"$BROKEN_UNIT" setup_sandbox "$SBX" "$BROKEN_UNIT" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" set +e OUT=$(run_cli "$SBX" migrate on --listen 2>"$SBX/stderr.log") RC=$? @@ -555,7 +561,7 @@ ACTIVE_AFTER=$(cat "$SBX/nmstate/active") # in tools/station/ninlight-station.service tatsaechlich steht und wirkt. SBX2="$TMPROOT/tC1-fixed" setup_sandbox "$SBX2" -echo "dummy" >"$SBX2/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX2/images/migrate-nocreds/gu10_rgb_prod.img" set +e OUT2=$(run_cli "$SBX2" migrate on --listen 2>&1) RC2=$? @@ -567,7 +573,7 @@ echo echo "=== Zusatzfall I1: 'status' zeigt die Phase aus control.json, nicht die veraltete Phase aus status.json ===" SBX="$TMPROOT/tI1" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" run_cli "$SBX" migrate on --listen >/dev/null 2>&1 # status.json wird nur beim Dienststart/bei einer Auslieferung geschrieben - hier # simuliert, wie es nach dem Start aussehen wuerde (Phase "listen", wie beim Start). @@ -604,7 +610,7 @@ echo echo "=== Zusatzfall I3: ap_switch bricht ab und faehrt beide Profile herunter, wenn 'device show wlan0' nach dem Umschalten nicht passt ===" SBX="$TMPROOT/tI3" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" set +e OUT=$(FAKE_NMCLI_DEVSHOW=fremdes-profil run_cli "$SBX" migrate on 2>"$SBX/stderr.log") RC=$? @@ -619,7 +625,7 @@ echo echo "=== Zusatzfall N2 (Fixrunde 2): 'status' zeigt keine veraltete Migrationsphase mehr, nachdem in den Rettungsmodus zurueckgeschaltet wurde ===" SBX="$TMPROOT/tN2" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" run_cli "$SBX" migrate on --listen >/dev/null 2>&1 run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1 # control.json bleibt mit phase=armed liegen run_cli "$SBX" on >/dev/null 2>&1 # zurueck in den Rettungsmodus - control.json wird bewusst NICHT aufgeraeumt @@ -638,7 +644,7 @@ echo echo "=== Zusatzfall N2b (Fixrunde 2): dieselbe veraltete Freigabe, aber ohne aktualisierte status.json (Modus '-') - genau das woertliche Repro aus dem Review ===" SBX="$TMPROOT/tN2b" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" run_cli "$SBX" migrate on --listen >/dev/null 2>&1 run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1 run_cli "$SBX" on >/dev/null 2>&1 @@ -819,7 +825,7 @@ echo "=== Fall D4 (Befund Task 7f): 'off' fasst den Anzeigedienst nicht mehr an # naechsten Pi-Neustart dunkel. Jetzt laesst 'off' den Dienst laufen. SBX="$TMPROOT/tD4" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" run_cli "$SBX" migrate on >/dev/null 2>&1 : >"$SBX/cmd.log" set +e @@ -997,7 +1003,7 @@ echo echo "=== Fall Z4: 'migrate on --listen --for 5' - beides zusammen ===" SBX="$TMPROOT/tZ4" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" set +e OUT=$(run_cli "$SBX" migrate on --listen --for 5 2>&1) RC=$? @@ -1008,7 +1014,7 @@ set -e # Umgekehrte Reihenfolge der Schalter muss genauso gehen. SBX="$TMPROOT/tZ4b" setup_sandbox "$SBX" -echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img" +echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img" set +e OUT=$(run_cli "$SBX" migrate on --for 5 --listen 2>&1) RC=$? @@ -1182,6 +1188,126 @@ echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung auf stderr" || fail "kein [ ! -e "$SBX/run/autooff.json" ] && pass "keine Zustandsdatei, die einen Zeitschalter vortaeuscht" || fail "autooff.json trotz gescheitertem Timer: $(cat "$SBX/run/autooff.json")" echo "$ERR" | grep -q "off" && pass "die Meldung sagt, dass die Station AN bleibt" || fail "die Meldung verschweigt den Zustand der Station: $ERR" +echo +echo "=== Bildsatz A: die Vorgabe ohne Umschalterdatei ist der Satz OHNE WLAN-Zugangsdaten ===" +# Die sichere Richtung: ein Fehlgriff kostet einen Onboarding-Durchgang, kein +# WLAN-Passwort in einer fremden Lampe. +SBX="$TMPROOT/bs1" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" bildsatz 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +echo "$OUT" | grep -q "ohne-wlan" && pass "ohne Datei gilt 'ohne-wlan'" || fail "Vorgabe ist nicht 'ohne-wlan': $OUT" +echo "$OUT" | grep -q "migrate-nocreds" && pass "und das zugehoerige Verzeichnis wird genannt" || fail "Verzeichnis fehlt in der Ausgabe: $OUT" +[ ! -e "$SBX/var/imageset" ] && pass "das blosse Berichten legt keine Datei an" || fail "'bildsatz' ohne Argument hat geschrieben" + +echo +echo "=== Bildsatz B: umschalten schreibt die Datei atomar mit 0644 und bleibt stehen ===" +SBX="$TMPROOT/bs2" +setup_sandbox "$SBX" +set +e +OUT=$(run_cli "$SBX" bildsatz mit-wlan 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +[ "$(cat "$SBX/var/imageset" 2>/dev/null | tr -d '\n')" = "mit-wlan" ] && pass "die Datei enthaelt 'mit-wlan'" || fail "Dateiinhalt: $(cat "$SBX/var/imageset" 2>&1)" +MODE=$(file_mode "$SBX/var/imageset") +[ "$MODE" = "644" ] && pass "Rechte 0644 (der Anzeigedienst muss lesen koennen)" || fail "Rechte $MODE statt 644" +[ -z "$(find "$SBX/var" -name '.imageset-*' 2>/dev/null)" ] && pass "keine Temp-Datei liegengeblieben" || fail "Temp-Datei liegt noch da" +echo "$OUT" | grep -qi "achtung" && pass "der Wechsel zu 'mit-wlan' warnt" || fail "keine Warnung beim Wechsel zu mit-wlan: $OUT" +# ... und er BLEIBT stehen: ein zweiter, unabhaengiger Aufruf sieht ihn. +OUT2=$(run_cli "$SBX" bildsatz 2>&1) +echo "$OUT2" | grep -q "mit-wlan" && pass "der Zustand bleibt ueber Aufrufe hinweg" || fail "Zustand nicht gespeichert: $OUT2" +# ... und zurueck. +run_cli "$SBX" bildsatz ohne-wlan >/dev/null 2>&1 +[ "$(cat "$SBX/var/imageset" | tr -d '\n')" = "ohne-wlan" ] && pass "zurueckschalten geht auch" || fail "zurueckschalten hat nicht gewirkt" + +echo +echo "=== Bildsatz C: ein unbekannter Name schaltet nichts um ===" +SBX="$TMPROOT/bs3" +setup_sandbox "$SBX" +printf 'mit-wlan\n' >"$SBX/var/imageset" +set +e +ERR=$(run_cli "$SBX" bildsatz irgendwas 2>&1 >/dev/null) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0" || fail "Exit-Code 0 fuer einen unbekannten Bildsatz" +[ "$(cat "$SBX/var/imageset" | tr -d '\n')" = "mit-wlan" ] && pass "der bestehende Zustand blieb unveraendert" || fail "der Zustand wurde veraendert: $(cat "$SBX/var/imageset")" +echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung auf stderr" || fail "keine Fehlermeldung: $ERR" + +echo +echo "=== Bildsatz D: ein kaputter Dateiinhalt faellt auf die SICHERE Vorgabe zurueck ===" +# Nie auf den teureren Satz - das ist die eine Richtung, die kein Zweifelsfall nehmen darf. +SBX="$TMPROOT/bs4" +setup_sandbox "$SBX" +printf 'voellig-anderes\n' >"$SBX/var/imageset" +OUT=$(run_cli "$SBX" bildsatz 2>&1) +echo "$OUT" | grep -q "ohne-wlan" && pass "kaputter Inhalt -> 'ohne-wlan'" || fail "kaputter Inhalt fuehrte nicht auf die Vorgabe: $OUT" +echo "$OUT" | grep -q "migrate-nocreds" && pass "und das Verzeichnis des sicheren Satzes" || fail "falsches Verzeichnis: $OUT" +# Der Bediener muss erfahren, dass sein Eintrag NICHT gewirkt hat - sonst haelt er die +# Station fuer umgeschaltet. +echo "$OUT" | grep -q "unbekannte Einstellung 'voellig-anderes'" \ + && pass "die unbekannte Einstellung wird beim Namen genannt" \ + || fail "die unbekannte Einstellung wird verschwiegen: $OUT" +# Gegenprobe: bei einem GUELTIGEN Eintrag darf diese Zeile nicht erscheinen. +run_cli "$SBX" bildsatz mit-wlan >/dev/null 2>&1 +OUT=$(run_cli "$SBX" bildsatz 2>&1) +echo "$OUT" | grep -q "unbekannte Einstellung" \ + && fail "meldet 'unbekannte Einstellung' fuer einen gueltigen Eintrag: $OUT" \ + || pass "kein Fehlalarm bei gueltigem Eintrag" + +echo +echo "=== Bildsatz E: ein gewaehlter, aber fehlender Satz wird beim Umschalten UND im Status gemeldet ===" +SBX="$TMPROOT/bs5" +setup_sandbox "$SBX" +rm -rf "$SBX/images/migrate-wlan" +set +e +ERR=$(run_cli "$SBX" bildsatz mit-wlan 2>&1 >/dev/null) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Umschalten gelingt trotzdem (der Zustand ist gueltig, nur die Bestueckung fehlt)" || fail "Exit-Code $RC" +echo "$ERR" | grep -qi "warnung" && pass "Warnung beim Umschalten" || fail "keine Warnung: $ERR" +OUT=$(run_cli "$SBX" status 2>&1) +echo "$OUT" | grep -q "FEHLT" && pass "'status' sagt, dass nichts ausgeliefert wird" || fail "'status' verschweigt den fehlenden Satz: $OUT" + +echo +echo "=== Bildsatz F: ein LEERES Verzeichnis zaehlt genauso als fehlend ===" +SBX="$TMPROOT/bs6" +setup_sandbox "$SBX" +rm -f "$SBX/images/migrate-nocreds"/*.img +OUT=$(run_cli "$SBX" status 2>&1) +echo "$OUT" | grep -q "FEHLT" && pass "ein leeres Bildsatzverzeichnis wird als fehlend gemeldet" || fail "ein leerer Satz gilt als vorhanden: $OUT" + +echo +echo "=== Bildsatz G: 'migrate on' nennt den geltenden Bildsatz in der Einschaltmeldung ===" +# Er kann von vorletzter Woche stammen - wer einschaltet, muss ihn sehen. +SBX="$TMPROOT/bs7" +setup_sandbox "$SBX" +run_cli "$SBX" bildsatz mit-wlan >/dev/null 2>&1 +set +e +OUT=$(run_cli "$SBX" migrate on --listen 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +echo "$OUT" | grep -q "Bildsatz: mit-wlan" && pass "die Einschaltmeldung nennt den Bildsatz" || fail "Bildsatz fehlt in der Einschaltmeldung: $OUT" + +echo +echo "=== Bildsatz H: die Warnung 'keine Migrationsimages' meint den GEWAEHLTEN Satz ===" +SBX="$TMPROOT/bs8" +setup_sandbox "$SBX" +rm -f "$SBX/images/migrate-wlan"/*.img # nur der andere Satz ist leer +set +e +ERR=$(run_cli "$SBX" migrate on --listen 2>&1 >/dev/null) +set -e +echo "$ERR" | grep -q "keine Migrationsimages" && fail "warnt, obwohl der GEWAEHLTE Satz ('ohne-wlan') bestueckt ist: $ERR" || pass "kein Fehlalarm - gezaehlt wird im gewaehlten Satz" +run_cli "$SBX" bildsatz mit-wlan >/dev/null 2>&1 +set +e +ERR=$(run_cli "$SBX" migrate on --listen 2>&1 >/dev/null) +set -e +echo "$ERR" | grep -q "keine Migrationsimages" && pass "nach dem Umschalten warnt sie" || fail "keine Warnung, obwohl der gewaehlte Satz leer ist: $ERR" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index db37bcd..ce5722b 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -504,8 +504,8 @@ RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi GZ_SIZE=$(wc -c <"$SANDBOX/build/foo/ninlight_foo_gz.img" | tr -d ' ') -PROD_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" -DEV_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +PROD_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" +DEV_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" if [ -f "$PROD_IMG" ] && [ -f "$DEV_IMG" ]; then pass "beide Migrationsimages (prod, dev) wurden angelegt" else @@ -535,7 +535,7 @@ set +e OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e -MANIFEST="$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" +MANIFEST="$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/manifest.json" if [ "$RC" -eq 0 ] && [ -f "$MANIFEST" ]; then pass "manifest.json wurde angelegt" else @@ -592,8 +592,8 @@ if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_foo_ota.img" ]; the else fail "Rettungsimage fehlt: $OUT" fi -if [ ! -d "$SANDBOX_STATE/opt/ninlight-station/images/migrate" ] \ - || [ -z "$(ls -A "$SANDBOX_STATE/opt/ninlight-station/images/migrate" 2>/dev/null)" ]; then +if [ ! -d "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" ] \ + || [ -z "$(ls -A "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" 2>/dev/null)" ]; then pass "keine Migrationsimages entstanden" else fail "Migrationsimages entstanden, obwohl kein Schluesselverzeichnis vorhanden war" @@ -618,12 +618,12 @@ OUT=$(TEST_SIGN_DIR="$KEYDIR_DEV_ONLY" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi -if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" ]; then +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" ]; then pass "dev-Migrationsimage wurde angelegt" else fail "dev-Migrationsimage fehlt: $OUT" fi -if [ ! -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" ]; then +if [ ! -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" ]; then pass "prod-Migrationsimage entfiel wie erwartet" else fail "prod-Migrationsimage wurde trotz fehlendem Produktionsschluessel angelegt" @@ -644,15 +644,15 @@ make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" A make_gz_file "$SANDBOX/build/bar/ninlight_bar_gz.img" C echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" echo "bar-ota" >"$SANDBOX/build/bar/ninlight_bar_ota.img" -mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" -echo "vorhandenes-bar-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_prod.img" -echo "vorhandenes-bar-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_dev.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" +echo "vorhandenes-bar-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/bar_prod.img" +echo "vorhandenes-bar-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/bar_dev.img" # Manifest eines frueheren Deploys, das bar beschrieben hat - genau das darf ein # gezielter Deploy von foo nicht einfach wegschreiben (Fixrunde 1, I1). bar traegt # absichtlich eine ANDERE Firmware-Version als das, was dieser Lauf fuer foo signiert # (nl_mkimage-Stub liefert immer "1.0.0") - das Manifest nach dem Merge muss beide # Versionen unveraendert nebeneinander stehen lassen (Fixrunde 2, N2). -cat >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" <<'JSON' +cat >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/manifest.json" <<'JSON' {"built": "2026-01-01T00:00:00Z", "ninlight_version": "0.9.0", "images": [ {"target": "bar", "variant": "prod", "file": "bar_prod.img", "bytes": 20, "sha256": "x", @@ -667,14 +667,14 @@ OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi -if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" ] \ - && [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" ]; then +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" ] \ + && [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" ]; then pass "Migrationsimages fuer das angegebene Ziel (foo) wurden angelegt" else fail "Migrationsimages fuer foo fehlen: $OUT" fi -if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_prod.img" 2>/dev/null)" = "vorhandenes-bar-prod" ] \ - && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/bar_dev.img" 2>/dev/null)" = "vorhandenes-bar-dev" ]; then +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/bar_prod.img" 2>/dev/null)" = "vorhandenes-bar-prod" ] \ + && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/bar_dev.img" 2>/dev/null)" = "vorhandenes-bar-dev" ]; then pass "Migrationsimages des NICHT ausgewaehlten Geraetetyps (bar) blieben unangetastet" else fail "Migrationsimages von bar wurden veraendert oder geloescht - 4a-Fehler fuer migrate/" @@ -682,7 +682,7 @@ fi set +e MANIFEST_CHECK=$(python3 -c " import json -with open('$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json') as fh: +with open('$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/manifest.json') as fh: data = json.load(fh) entries = data.get('images', []) keys = {(e.get('target'), e.get('variant')) for e in entries} @@ -768,7 +768,7 @@ OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi -DEV_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +DEV_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" if [ -f "$DEV_IMG" ]; then set +e python3 -c " @@ -797,22 +797,22 @@ setup_sandbox "$SANDBOX" setup_stubbin "$STUBBIN" "$LOG" mkdir -p "$SANDBOX/build/foo" echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" -mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" -echo "vorhandenes-foo-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" -echo "vorhandenes-foo-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" +echo "vorhandenes-foo-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" +echo "vorhandenes-foo-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" : >"$LOG" set +e OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi -if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" 2>/dev/null)" = "vorhandenes-foo-prod" ] \ - && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" 2>/dev/null)" = "vorhandenes-foo-dev" ]; then +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" 2>/dev/null)" = "vorhandenes-foo-prod" ] \ + && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" 2>/dev/null)" = "vorhandenes-foo-dev" ]; then pass "vorhandenes migrate/ blieb beim Voll-Deploy ohne Schluesselverzeichnis unangetastet" else fail "migrate/ wurde geleert, obwohl kein Schluesselverzeichnis vorhanden war - zerstoert die Bestueckung" fi -if echo "$OUT" | grep -q "migrate/ auf dem Pi bleibt unangetastet"; then +if echo "$OUT" | grep -q "migrate-wlan/ auf dem Pi bleibt unangetastet"; then pass "Ausgabe kuendigt an, dass migrate/ unangetastet bleibt" else fail "Ausgabe erwaehnt nicht, dass migrate/ unangetastet bleibt: $OUT" @@ -831,11 +831,11 @@ setup_sandbox "$SANDBOX" setup_stubbin "$STUBBIN" "$LOG" mkdir -p "$SANDBOX/build/foo" echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" -mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" -echo "vorhandenes-foo-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" -echo "vorhandenes-foo-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" +echo "vorhandenes-foo-prod" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" +echo "vorhandenes-foo-dev" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" echo '{"built": "2026-01-01T00:00:00Z", "ninlight_version": "0.0.1", "images": []}' \ - >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" + >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/manifest.json" : >"$LOG" set +e OUT=$(TEST_SIGN_DIR="$SIGN_EMPTY" run_deploy "$SANDBOX" pi-test 2>&1) @@ -847,9 +847,9 @@ if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/ninlight_foo_ota.img" ]; the else fail "Rettungsimage fehlt: $OUT" fi -if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" 2>/dev/null)" = "vorhandenes-foo-prod" ] \ - && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" 2>/dev/null)" = "vorhandenes-foo-dev" ] \ - && grep -q '"images": \[\]' "$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json"; then +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" 2>/dev/null)" = "vorhandenes-foo-prod" ] \ + && [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" 2>/dev/null)" = "vorhandenes-foo-dev" ] \ + && grep -q '"images": \[\]' "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/manifest.json"; then pass "vorhandenes migrate/ (Bilder UND manifest.json) blieb unangetastet - Verzeichnis war da, aber leer" else fail "migrate/ wurde trotz vorhandenem, aber leerem Schluesselverzeichnis veraendert/geleert - C2 nicht behoben" @@ -868,20 +868,20 @@ setup_stubbin "$STUBBIN" "$LOG" mkdir -p "$SANDBOX/build/foo" make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" -mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" -echo "echtes-altes-prod-image" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" +echo "echtes-altes-prod-image" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" : >"$LOG" set +e OUT=$(TEST_SIGN_DIR="$KEYDIR_DEV_ONLY" run_deploy "$SANDBOX" pi-test 2>&1) # Voll-Deploy, kein Ziel-Argument RC=$? set -e if [ "$RC" -eq 0 ]; then pass "Exit-Code 0"; else fail "Exit-Code $RC, Ausgabe: $OUT"; fi -if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" ]; then +if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" ]; then pass "neues dev-Migrationsimage wurde signiert und ausgeliefert" else fail "dev-Migrationsimage fehlt: $OUT" fi -if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_prod.img" 2>/dev/null)" = "echtes-altes-prod-image" ]; then +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_prod.img" 2>/dev/null)" = "echtes-altes-prod-image" ]; then pass "das vorhandene _prod.img blieb unangetastet, obwohl dieser Voll-Deploy nur den dev-Schluessel hatte" else fail "das vorhandene _prod.img wurde beim Voll-Deploy mit nur dev-Schluessel entfernt - C2 (Teilfall) nicht behoben" @@ -907,12 +907,13 @@ set +e OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) RC=$? set -e -MANIFEST="$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" +MANIFEST="$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/manifest.json" if [ "$RC" -eq 0 ] && [ -f "$MANIFEST" ]; then pass "manifest.json wurde angelegt" else fail "manifest.json fehlt oder Exit-Code $RC: $OUT" fi +printf 'mit-wlan\n' >"$TMPROOT/s20-imageset" set +e VERSION=$(python3 -c " import importlib.util @@ -929,6 +930,9 @@ st = module.Station( stall_after=None, mode='migration', control_path=Path('$TMPROOT/s20-control.json'), + # Das Manifest dieses Szenarios gehoert zum Satz MIT WLAN-Zugangsdaten - der + # Umschalter muss also darauf stehen, sonst liest der Server im anderen Satz nach. + imageset_path=Path('$TMPROOT/s20-imageset'), ) print(st.image_version('foo', 'dev')) " 2>&1) @@ -965,8 +969,8 @@ for label in truncated empty array null string null-entry; do mkdir -p "$SANDBOX/build/foo" make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" - mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" - cp "$TMPROOT/bad-manifests/$label.json" "$SANDBOX_STATE/opt/ninlight-station/images/migrate/manifest.json" + mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" + cp "$TMPROOT/bad-manifests/$label.json" "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/manifest.json" : >"$LOG" set +e # Nur EIN Schluessel (dev) -> MIGRATE_COMPLETE=0 -> der Merge-Zweig laeuft, nicht der @@ -980,7 +984,7 @@ for label in truncated empty array null string null-entry; do fail "Bestandsmanifest '$label': Exit-Code $RC - Deploy abgebrochen. Ausgabe: $OUT" fi if [ -f "$SANDBOX_STATE/opt/ninlight-station/server.py" ] \ - && [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" ]; then + && [ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" ]; then pass "Bestandsmanifest '$label': Deploy lief vollstaendig durch (server.py installiert, foo_dev.img da)" else fail "Bestandsmanifest '$label': Deploy blieb auf halbem Weg stehen (server.py oder foo_dev.img fehlt)" @@ -1112,6 +1116,130 @@ ERR=$(cat "$TMPROOT/s27.err") echo "$ERR" | grep -q "Warnung" && pass "Warnung (keine Fehlermeldung) auf stderr" || fail "keine Warnung auf stderr: $ERR" [ -f "$SANDBOX_STATE/opt/ninlight-station/server.py" ] && pass "der Deploy selbst lief vollstaendig durch" || fail "server.py fehlt - der Deploy brach ab" +echo +echo "=== Szenario 28: beide Bildsaetze werden ausgeliefert, und zwar aus verschiedenen Bauten ===" +# Der Umschalter auf der Station ist nur so gut wie die Bestueckung: liegen beide Saetze +# nicht getrennt auf dem Pi, kann er nichts umschalten. Geprueft wird am INHALT, nicht am +# Verzeichnisnamen - die Fuellzeichen der beiden _gz.img sind verschieden, ein +# vertauschter Quellbaum faellt also auf. +SANDBOX="$TMPROOT/s28"; STUBBIN="$TMPROOT/bin28"; LOG="$TMPROOT/s28.log"; SANDBOX_STATE="$TMPROOT/s28-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" W +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +make_gz_file "$SANDBOX/build-nocreds/foo/ninlight_foo_gz.img" N +: >"$LOG" +set +e +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +WLAN_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" +NOCR_IMG="$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img" +if [ -f "$WLAN_IMG" ] && [ -f "$NOCR_IMG" ]; then + pass "beide Bildsaetze liegen getrennt auf dem Pi" +else + fail "mindestens ein Bildsatz fehlt: $OUT" +fi +if [ -f "$WLAN_IMG" ] && [ -f "$NOCR_IMG" ]; then + # Die ersten Bytes sind das Fuellzeichen des jeweiligen _gz.img. + W_HEAD=$(head -c 8 "$WLAN_IMG") + N_HEAD=$(head -c 8 "$NOCR_IMG") + [ "$W_HEAD" = "WWWWWWWW" ] && pass "migrate-wlan/ kommt wirklich aus build/" || fail "migrate-wlan/ enthaelt '$W_HEAD' statt der build/-Bytes" + [ "$N_HEAD" = "NNNNNNNN" ] && pass "migrate-nocreds/ kommt wirklich aus build-nocreds/" || fail "migrate-nocreds/ enthaelt '$N_HEAD' statt der build-nocreds/-Bytes" +fi +for m in migrate-wlan migrate-nocreds; do + if [ -f "$SANDBOX_STATE/opt/ninlight-station/images/$m/manifest.json" ]; then + pass "$m/manifest.json wurde angelegt" + else + fail "$m/manifest.json fehlt" + fi +done + +echo +echo "=== Szenario 29: ohne build-nocreds/ bleibt der Satz 'ohne-wlan' auf dem Pi unangetastet ===" +# tools/build_nocreds.sh ist ein eigener, optionaler Bau. Ein Deploy darf nicht daran +# scheitern, dass er nicht gelaufen ist - und er darf die Bestueckung frueherer Deploys +# nicht wegraeumen (derselbe Fehler wie frueher bei migrate/, siehe Szenario 17). +SANDBOX="$TMPROOT/s29"; STUBBIN="$TMPROOT/bin29"; LOG="$TMPROOT/s29.log"; SANDBOX_STATE="$TMPROOT/s29-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds" +echo "alter-nocreds-abzug" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img" +: >"$LOG" +set +e +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlendem build-nocreds/" || fail "Exit-Code $RC: $OUT" +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img" 2>/dev/null)" = "alter-nocreds-abzug" ]; then + pass "vorhandener Satz 'ohne-wlan' blieb unangetastet" +else + fail "der Satz 'ohne-wlan' wurde geleert oder ueberschrieben, obwohl build-nocreds/ fehlte" +fi +[ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" ] \ + && pass "der Satz 'mit-wlan' wurde trotzdem ausgeliefert" \ + || fail "der Satz 'mit-wlan' fehlt: $OUT" +echo "$OUT" | grep -q "build_nocreds.sh" \ + && pass "der Hinweis nennt, womit der fehlende Satz entsteht" \ + || fail "kein Hinweis auf tools/build_nocreds.sh: $OUT" + +echo +echo "=== Szenario 30: ein unvollstaendiger Satz 'ohne-wlan' raeumt nicht mit --delete auf ===" +# build-nocreds/ hat nur eines von zwei Zielen - dieser Lauf beschreibt den Sollzustand +# also NICHT vollstaendig, und --delete wuerde das zweite Ziel des vorigen Deploys +# wegraeumen. +SANDBOX="$TMPROOT/s30"; STUBBIN="$TMPROOT/bin30"; LOG="$TMPROOT/s30.log"; SANDBOX_STATE="$TMPROOT/s30-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build/bar" "$SANDBOX/build-nocreds/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" +make_gz_file "$SANDBOX/build/bar/ninlight_bar_gz.img" +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +echo "bar-ota" >"$SANDBOX/build/bar/ninlight_bar_ota.img" +make_gz_file "$SANDBOX/build-nocreds/foo/ninlight_foo_gz.img" N +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds" +echo "alter-bar-abzug" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/bar_dev.img" +: >"$LOG" +set +e +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +if [ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/bar_dev.img" 2>/dev/null)" = "alter-bar-abzug" ]; then + pass "das nicht neu gebaute Ziel blieb im Satz 'ohne-wlan' erhalten" +else + fail "das nicht neu gebaute Ziel wurde aus dem Satz 'ohne-wlan' entfernt - --delete lief trotz unvollstaendigem Satz" +fi +[ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img" ] \ + && pass "das gebaute Ziel kam dazu" || fail "das gebaute Ziel fehlt im Satz 'ohne-wlan'" + +echo +echo "=== Szenario 31: das alte, namenlose migrate/ wird beim Deploy entfernt ===" +# Bis zum Umschalter hiess das einzige Verzeichnis "migrate" und enthielt die Abzuege MIT +# Zugangsdaten. Es wird von niemandem mehr gelesen - bliebe es liegen, haelt der naechste +# Mensch am Pi es fuer den gueltigen Satz. +SANDBOX="$TMPROOT/s31"; STUBBIN="$TMPROOT/bin31"; LOG="$TMPROOT/s31.log"; SANDBOX_STATE="$TMPROOT/s31-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate" +echo "altbestand" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate/foo_dev.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +[ ! -e "$SANDBOX_STATE/opt/ninlight-station/images/migrate" ] \ + && pass "der Altbestand images/migrate wurde entfernt" \ + || fail "images/migrate liegt noch da" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index cef9132..0294222 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -861,9 +861,11 @@ def test_a_switched_off_station_shows_no_timer(tmp_path, monkeypatch): {"minutes": 30, "deadline": time.time() + 600, "deferrals": 0})) state = display.build_state(run_dir=tmp_path, conf_path=tmp_path / "station.conf", - service_name="x") + service_name="x", + imageset_path=tmp_path / "imageset") - assert state == {"mode": "off"} + assert state["mode"] == "off" + assert "autooff_minutes" not in state assert not any("Auto-Aus" in line for line in render.lines(state)) @@ -897,9 +899,13 @@ def test_build_state_off_when_service_not_active(tmp_path, monkeypatch): (tmp_path / "status.json").write_text(json.dumps({"mode": "migration", "served": 5})) state = display.build_state(run_dir=tmp_path, conf_path=tmp_path / "station.conf", - service_name="x") + service_name="x", + imageset_path=tmp_path / "imageset") - assert state == {"mode": "off"} + # "off" und sonst NICHTS aus status.json - nur der Bildsatz, der auch im + # ausgeschalteten Zustand gilt (und gerade dann gesehen werden soll). + assert state["mode"] == "off" + assert set(state) == {"mode", "imageset", "imageset_ok"} def test_build_state_mode_comes_from_mode_file_not_status_json(tmp_path, monkeypatch): @@ -1707,3 +1713,155 @@ def test_the_expensive_station_state_is_still_only_read_every_five_seconds(): _drive_daemon(state_at, duration_s=10.0) assert calls == [0.0, 5.0, 10.0] + + +# --- Bildsatz-Umschalter in der Fusszeile ------------------------------------------- +# +# Zusage: welcher Migrations-Bildsatz gilt, steht DAUERHAFT auf dem Panel - nicht nur im +# Menue. Der Zustand ueberlebt Neustarts und kann von vorletzter Woche stammen; wer ihn +# erst beim Blaettern im Menue saehe, hat womoeglich schon zwanzig Lampen mit den eigenen +# WLAN-Zugangsdaten geflasht. + +def _flat(rows): + """_band_pixels() liefert Zeilen-Tupel - hier interessieren die einzelnen Pixel.""" + return [px for row in rows for px in row] + + +def _imageset_line(state): + texts = [text for text, role in render.blocks(state) if role == render.ROLE_FOOTER] + assert len(texts) <= 1, texts + return texts[0] if texts else None + + +def test_the_image_set_is_shown_in_every_status_screen(): + for state in ({"mode": "off"}, + {"mode": "recovery", "ssid": "ninlight-recovery"}, + dict(_migration_state()), + {"mode": None}): + for name in ("ohne-wlan", "mit-wlan"): + s = dict(state, imageset=name, imageset_ok=True) + line = _imageset_line(s) + assert line == render.IMAGESET_LABELS[name], (state.get("mode"), name) + + +def test_the_two_image_sets_read_differently(): + """Die beiden Zustaende muessen sich am Panel auf einen Blick unterscheiden - eine + Fusszeile, die nur "Bildsatz gesetzt" sagt, waere wertlos.""" + assert render.IMAGESET_LABELS["ohne-wlan"] != render.IMAGESET_LABELS["mit-wlan"] + assert "WLAN" in render.IMAGESET_LABELS["mit-wlan"] + + +def test_a_missing_image_set_says_so_on_the_panel(): + state = dict(_migration_state(), imageset="mit-wlan", imageset_ok=False) + line = _imageset_line(state) + assert line is not None and "FEHLT" in line + + +def test_without_an_image_set_field_nothing_is_claimed(): + """Ein Zustand aus einer aelteren display.py-Fassung bekommt keine erfundene + Fusszeile.""" + assert _imageset_line({"mode": "off"}) is None + + +def test_the_image_set_line_is_actually_drawn_at_the_bottom(): + """Nicht nur in blocks(): auf dem Panel, unterhalb aller Beiwerkzeilen und ueber dem + unteren Rand.""" + state = dict(_migration_state(), imageset="mit-wlan", imageset_ok=True) + footer_top = (render.PANEL_HEIGHT_PX - render.BOTTOM_MARGIN_PX + - render.FOOTER_BAND_HEIGHT_PX) + + with_footer = render.compose(state) + without = render.compose({k: v for k, v in state.items() + if k not in ("imageset", "imageset_ok")}) + + bottom = render.PANEL_HEIGHT_PX - render.BOTTOM_MARGIN_PX + band = _band_pixels(with_footer, footer_top, bottom) + assert 0 in _flat(band), "im Fussband ist gar keine Tinte" + assert band != _band_pixels(without, footer_top, bottom), \ + "Fussband sieht mit und ohne Bildsatz gleich aus" + + +def test_the_image_set_line_keeps_the_bottom_margin_free(): + state = dict(_migration_state(), imageset="mit-wlan", imageset_ok=False) + image = render.compose(state) + margin = _flat(_band_pixels(image, render.PANEL_HEIGHT_PX - render.BOTTOM_MARGIN_PX, + render.PANEL_HEIGHT_PX)) + assert set(margin) <= {255}, "der untere Rand ist nicht mehr frei" + + +def test_the_image_set_line_survives_the_fullest_state(): + """Im vollsten Zustand faellt eine Beiwerkzeile heraus - aber nie die Fusszeile: + sie steht an festem Platz und nicht im Zeilenfluss.""" + state = dict(_migration_state(), imageset="mit-wlan", imageset_ok=True, + autooff_minutes=17) + assert _imageset_line(state) is not None + # ... und der Zeilenfluss endet wirklich vor dem Fussband. + image = render.compose(state) + footer_top = (render.PANEL_HEIGHT_PX - render.BOTTOM_MARGIN_PX + - render.FOOTER_BAND_HEIGHT_PX) + # Die Zeile direkt ueber dem Trennstrich darf ihn nicht beruehren. + assert set(_flat(_band_pixels(image, footer_top - 1, footer_top))) <= {255} + + +def test_build_state_reads_the_image_set_switch(tmp_path, monkeypatch): + """Dieselbe Datei, die das CLI schreibt und server.py liest - kein zweiter Weg.""" + monkeypatch.setattr(display, "_service_active", lambda service_name: True) + (tmp_path / "mode").write_text("migration") + images = tmp_path / "images" + (images / "migrate-nocreds").mkdir(parents=True) + (images / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") + (images / "migrate-wlan").mkdir() + (images / "migrate-wlan" / "g95_cw_dev.img").write_bytes(b"IMG") + conf = tmp_path / "station.conf" + conf.write_text(f"MIGRATE_SSID='qconnex-firmware'\nIMAGE_DIR='{images}'\n") + switch = tmp_path / "imageset" + + def state(): + return display.build_state(run_dir=tmp_path, conf_path=conf, service_name="x", + imageset_path=switch) + + # Ohne Datei: die sichere Vorgabe. + assert state()["imageset"] == "ohne-wlan" + switch.write_text("mit-wlan\n") + assert state()["imageset"] == "mit-wlan" + # Unbekannter Inhalt darf NIE auf den teureren Satz zeigen. + switch.write_text("irgendwas") + assert state()["imageset"] == "ohne-wlan" + + +def test_build_state_reports_a_missing_image_set(tmp_path, monkeypatch): + """"Vorhanden" heisst: Verzeichnis da UND mindestens ein Image darin - dieselbe Regel + wie in server.py, sonst sagt das Panel "in Ordnung" und die Lampe bekommt nichts.""" + monkeypatch.setattr(display, "_service_active", lambda service_name: True) + images = tmp_path / "images" + (images / "migrate-nocreds").mkdir(parents=True) + (images / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") + conf = tmp_path / "station.conf" + conf.write_text(f"IMAGE_DIR='{images}'\n") + switch = tmp_path / "imageset" + + switch.write_text("ohne-wlan") + assert display.build_state(run_dir=tmp_path, conf_path=conf, service_name="x", + imageset_path=switch)["imageset_ok"] is True + switch.write_text("mit-wlan") # migrate-wlan/ gibt es nicht + state = display.build_state(run_dir=tmp_path, conf_path=conf, service_name="x", + imageset_path=switch) + assert state["imageset_ok"] is False + assert "FEHLT" in _imageset_line(state) + + # Und ein LEERES Verzeichnis zaehlt genauso als fehlend. + (images / "migrate-wlan").mkdir() + assert display.build_state(run_dir=tmp_path, conf_path=conf, service_name="x", + imageset_path=switch)["imageset_ok"] is False + + +def test_the_display_and_the_server_agree_on_the_set_names(): + """Zwei Prozesse, eine Wahrheit: waeren die Namen oder die Vorgabe verschieden, + behauptete das Panel etwas anderes, als der Server tut.""" + import importlib.util + spec = importlib.util.spec_from_file_location( + "server_for_names", Path(__file__).resolve().parents[1] / "server.py") + server = importlib.util.module_from_spec(spec) + spec.loader.exec_module(server) + assert display.IMAGESET_DIRS == server.IMAGE_SET_DIRS + assert display.IMAGESET_DEFAULT == server.DEFAULT_IMAGE_SET diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index 89c53b6..a461fea 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -494,12 +494,16 @@ class MigrationTestServer: control_dir = tmp_path / "control" control_dir.mkdir() self.control_path = control_dir / "control.json" + # Der Umschalter des Bildsatzes. Die Datei existiert zunaechst NICHT - das ist + # der Auslieferungszustand und muss "ohne-wlan" (migrate-nocreds/) bedeuten. + self.imageset_path = tmp_path / "imageset" self.port = free_port() self.proc = subprocess.Popen( [sys.executable, str(SERVER), "--image-dir", str(self.image_dir), "--bind", "127.0.0.1", "--port", str(self.port), "--state", str(self.state_path), "--mode", mode, - "--control", str(self.control_path)], + "--control", str(self.control_path), + "--imageset-file", str(self.imageset_path)], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, ) deadline = time.time() + 10 @@ -563,6 +567,17 @@ class MigrationTestServer: def write_raw_control(self, text): self.control_path.write_text(text) + def set_image_set(self, value): + """Stellt den Umschalter so um, wie es das CLI tut: eine Datei mit dem Namen des + Bildsatzes. `None` loescht sie wieder (= Auslieferungszustand).""" + if value is None: + try: + self.imageset_path.unlink() + except FileNotFoundError: + pass + return + self.imageset_path.write_text(value) + def stop(self): self.proc.terminate() self.proc.wait(timeout=5) @@ -584,8 +599,8 @@ def migration_server(tmp_path): def test_migration_auto_serves_known_product(migration_server): srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG" * 1000) + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG" * 1000) r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1.1.1.1800-dev_ota_light.img") assert r.status == 200 assert r.body == b"IMG" * 1000 @@ -599,9 +614,9 @@ def test_migration_dev_only_in_version_serves_the_dev_image(migration_server): ging hier das produktionssignierte Image raus (200, und die Lampe lehnte es ab) - jetzt muss das dev-signierte gehen, und status.json muss die Quelle mitfuehren.""" srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"DEV" * 1000) - (imgdir / "migrate" / "g95_cw_prod.img").write_bytes(b"PROD" * 1000) + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"DEV" * 1000) + (imgdir / "migrate-nocreds" / "g95_cw_prod.img").write_bytes(b"PROD" * 1000) r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95_V1.1.1.2357-dev_ota_light.img") assert r.status == 200 assert r.body == b"DEV" * 1000 @@ -617,9 +632,9 @@ def test_migration_production_lamp_still_gets_the_prod_image(migration_server): """Die erfolgreich migrierte GU10: nirgends ein "-dev", also prod - und die Quelle "none", damit am Panel/in status.json steht, worauf die Einstufung beruht.""" srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "gu10_rgb_dev.img").write_bytes(b"DEV" * 1000) - (imgdir / "migrate" / "gu10_rgb_prod.img").write_bytes(b"PROD" * 1000) + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "gu10_rgb_dev.img").write_bytes(b"DEV" * 1000) + (imgdir / "migrate-nocreds" / "gu10_rgb_prod.img").write_bytes(b"PROD" * 1000) r = srv.get("/qcx_firmware_-klyqa.lighting.rgb-cw-ww.gu10_V1.1.0.1138_ota_light.img") assert r.status == 200 assert r.body == b"PROD" * 1000 @@ -630,8 +645,8 @@ def test_migration_production_lamp_still_gets_the_prod_image(migration_server): def test_migration_listen_records_but_serves_nothing(migration_server): srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") srv.set_control(phase="listen") r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img") assert r.status == 404 @@ -643,8 +658,8 @@ def test_migration_listen_records_but_serves_nothing(migration_server): def test_arm_serves_exactly_once_then_falls_back_to_listen(migration_server): srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") srv.set_control(phase="armed", armed_target="g95_cw") p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" assert srv.get(p).status == 200 @@ -654,8 +669,8 @@ def test_arm_serves_exactly_once_then_falls_back_to_listen(migration_server): def test_armed_for_other_target_does_not_serve(migration_server): srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") srv.set_control(phase="armed", armed_target="gu10_rgb") r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img") assert r.status == 404 @@ -671,7 +686,12 @@ def test_unknown_product_id_is_404_and_logged(migration_server): def test_missing_migrate_image_is_404(migration_server): - srv, _ = migration_server + srv, imgdir = migration_server + # Der Bildsatz ist da (er enthaelt ein anderes Modell), nur dieses eine Modell fehlt + # darin - ein anderer Zustand als ein fehlender Bildsatz (siehe + # test_a_selected_but_missing_image_set_delivers_nothing). + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.e14_V1_ota_light.img") assert r.status == 404 st = srv.status(lambda s: len(s["recent"]) >= 1) @@ -692,8 +712,8 @@ def test_migration_path_is_404_in_recovery_mode(server): def test_missing_control_file_means_auto(migration_server): srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") srv.delete_control() assert srv.get( "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img").status == 200 @@ -701,8 +721,8 @@ def test_missing_control_file_means_auto(migration_server): def test_corrupt_control_file_means_auto(migration_server): srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") srv.write_raw_control("{das ist kein json") assert srv.get( "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img").status == 200 @@ -710,8 +730,8 @@ def test_corrupt_control_file_means_auto(migration_server): def test_range_resume_on_migration_image(migration_server): srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(bytes(range(256)) * 40) + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(bytes(range(256)) * 40) p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" r = srv.get(p, headers={"Range": "bytes=1000-"}) assert r.status == 206 @@ -724,9 +744,9 @@ def test_version_comes_from_manifest(migration_server): Version traegt - Bestandskompatibilitaet mit einem Manifest aus der Zeit vor Fixrunde 2 (N2).""" srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") - (imgdir / "migrate" / "manifest.json").write_text(json.dumps({ + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") + (imgdir / "migrate-nocreds" / "manifest.json").write_text(json.dumps({ "ninlight_version": "0.3.9", "images": [{"target": "g95_cw", "variant": "dev", "file": "g95_cw_dev.img"}]})) srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img") @@ -742,10 +762,10 @@ def test_version_prefers_the_delivered_entry_over_the_manifest_wide_field(migrat darf dann nicht das manifestweite Feld fuer beide protokollieren, sondern muss die Version DES AUSGELIEFERTEN Eintrags nehmen.""" srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG-DEV") - (imgdir / "migrate" / "g95_cw_prod.img").write_bytes(b"IMG-PROD") - (imgdir / "migrate" / "manifest.json").write_text(json.dumps({ + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG-DEV") + (imgdir / "migrate-nocreds" / "g95_cw_prod.img").write_bytes(b"IMG-PROD") + (imgdir / "migrate-nocreds" / "manifest.json").write_text(json.dumps({ # Manifestweites Feld absichtlich falsch/veraltet, damit ein Zurueckfallen # darauf im Test auffallen wuerde. "ninlight_version": "1.2.0", @@ -770,8 +790,8 @@ def test_control_file_is_world_readable_even_under_a_restrictive_umask(tmp_path) finally: os.umask(old_umask) try: - (srv.image_dir / "migrate").mkdir(exist_ok=True) - (srv.image_dir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + (srv.image_dir / "migrate-nocreds").mkdir(exist_ok=True) + (srv.image_dir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") srv.set_control(phase="armed", armed_target="g95_cw") def world_readable(): @@ -799,8 +819,8 @@ def test_armed_serves_exactly_once_under_concurrency(migration_server): Verbrauchen der Freigabe atomar unter einer Sperre machen, damit von n gleichzeitigen Anfragen gegen dieselbe Freigabe hoechstens eine durchkommt.""" srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG" * 20000) + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG" * 20000) srv.set_control(phase="armed", armed_target="g95_cw") p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" @@ -835,8 +855,8 @@ def test_control_write_failure_blocks_further_armed_deliveries(migration_server) ausserdem in status.json sichtbar sein, sonst wundert sich niemand, warum "arm" nicht mehr wirkt.""" srv, imgdir = migration_server - (imgdir / "migrate").mkdir(exist_ok=True) - (imgdir / "migrate" / "g95_cw_dev.img").write_bytes(b"IMG") + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") srv.set_control(phase="armed", armed_target="g95_cw") p = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" @@ -899,3 +919,113 @@ def test_resolve_mode_garbage_file_means_recovery(tmp_path): def test_resolve_mode_default_without_mode_file_is_recovery(): module = _load_server_module() assert module.resolve_mode(None, None) == "recovery" + + +# --- Umschalter: mit oder ohne einkompilierte WLAN-Zugangsdaten --------------------- +# +# Zwei Bildsaetze liegen auf dem Pi (images/migrate-nocreds und images/migrate-wlan); +# welcher ausgeliefert wird, entscheidet allein der Umschalter. Geprueft wird deshalb +# immer an der TATSAECHLICH AUSGELIEFERTEN DATEI, nicht an einem Pfad oder einem +# Statusfeld - der Fehlgriff, gegen den der Umschalter da ist, ist ein falscher Inhalt +# in einer Lampe. + +def _both_sets(imgdir): + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"OHNE" * 100) + (imgdir / "migrate-wlan").mkdir(exist_ok=True) + (imgdir / "migrate-wlan" / "g95_cw_dev.img").write_bytes(b"MITW" * 100) + + +G95_DEV = "/qcx_firmware_-klyqa.lighting.cw-ww.g95-dev_V1_ota_light.img" + + +def test_the_default_image_set_is_the_one_without_wifi_credentials(migration_server): + """Ohne Umschalterdatei (Auslieferungszustand) geht der Abzug OHNE Zugangsdaten + raus. Die sichere Vorgabe ist die, bei der ein Fehlgriff niemandem das eigene + WLAN-Passwort in ein fremdes Geraet schreibt.""" + srv, imgdir = migration_server + _both_sets(imgdir) + r = srv.get(G95_DEV) + assert r.status == 200 + assert r.body == b"OHNE" * 100 + + +def test_the_switch_decides_which_file_is_actually_delivered(migration_server): + srv, imgdir = migration_server + _both_sets(imgdir) + srv.set_image_set("mit-wlan") + assert srv.get(G95_DEV).body == b"MITW" * 100 + # ... und wieder zurueck, ohne Dienstneustart: der Umschalter wirkt sofort. + srv.set_image_set("ohne-wlan") + assert srv.get(G95_DEV).body == b"OHNE" * 100 + + +def test_a_broken_switch_file_falls_back_to_the_safe_set(migration_server): + """Unbekannter Inhalt ist kein Grund, den teureren Satz zu nehmen.""" + srv, imgdir = migration_server + _both_sets(imgdir) + srv.set_image_set("voellig-anderes") + assert srv.get(G95_DEV).body == b"OHNE" * 100 + + +def test_a_selected_but_missing_image_set_delivers_nothing(migration_server): + """Der gewaehlte Satz liegt nicht auf dem Pi: es darf NICHTS rausgehen - insbesondere + nicht die Datei des anderen Satzes, die daneben liegt und genauso heisst.""" + srv, imgdir = migration_server + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"OHNE" * 100) + srv.set_image_set("mit-wlan") # migrate-wlan/ existiert nicht + + r = srv.get(G95_DEV) + assert r.status == 404 + assert b"OHNE" not in r.body + st = srv.status(lambda s: len(s["recent"]) >= 1) + assert st["served"] == 0 + # Eigener Status, damit am Panel/in status.json "Satz fehlt" von "dieses Modell + # fehlt" unterscheidbar bleibt. + assert st["recent"][-1]["status"] == "noimageset" + + +def test_a_missing_image_set_does_not_burn_the_release(migration_server): + """Wie beim fehlenden Einzelimage: eine Freigabe darf nicht daran verfallen, dass es + gar nichts auszuliefern gab.""" + srv, imgdir = migration_server + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + srv.set_image_set("mit-wlan") + srv.set_control(phase="armed", armed_target="g95_cw") + assert srv.get(G95_DEV).status == 404 + assert srv.read_control()["phase"] == "armed" + + +def test_the_version_comes_from_the_manifest_of_the_selected_set(migration_server): + """Nach dem Umschalten darf nicht die Version des vorigen Satzes stehenbleiben - die + beiden manifest.json entstehen im selben Deploy und koennen dieselbe mtime tragen.""" + srv, imgdir = migration_server + _both_sets(imgdir) + for name, version in (("migrate-nocreds", "0.4.0-onb"), ("migrate-wlan", "0.4.0-wlan")): + (imgdir / name / "manifest.json").write_text(json.dumps({ + "images": [{"target": "g95_cw", "variant": "dev", "file": "g95_cw_dev.img", + "ninlight_version": version}]})) + # Beiden Manifesten dieselbe mtime geben - genau der Fall, den ein reiner + # mtime-Vergleich nicht bemerkt haette. + stamp = 1600000000 + for name in ("migrate-nocreds", "migrate-wlan"): + os.utime(imgdir / name / "manifest.json", (stamp, stamp)) + + assert srv.get(G95_DEV).status == 200 + assert srv.status(lambda s: s["recent"])["recent"][-1]["version"] == "0.4.0-onb" + + srv.set_image_set("mit-wlan") + assert srv.get(G95_DEV).status == 200 + assert srv.status( + lambda s: len(s["recent"]) >= 2 + )["recent"][-1]["version"] == "0.4.0-wlan" + + +def test_read_image_set_defaults_without_a_file(tmp_path): + module = _load_server_module() + assert module.read_image_set(None) == "ohne-wlan" + assert module.read_image_set(tmp_path / "fehlt") == "ohne-wlan" + p = tmp_path / "imageset" + p.write_text("mit-wlan\n") + assert module.read_image_set(p) == "mit-wlan" diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index d9c4e62..cc877fc 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -212,6 +212,7 @@ run_setup() { NINLIGHT_STAGE_DIR="$state/tmp" \ NINLIGHT_STATION_CONF="$state/etc/ninlight-station.conf" \ NINLIGHT_STATION_ROOT="$state/opt/ninlight-station" \ + NINLIGHT_STATION_STATE_DIR="$state/var/lib/ninlight-station" \ NINLIGHT_STATION_BIN="$state/usr/local/bin/ninlight-station" \ NINLIGHT_STATION_UNIT="$state/etc/systemd/system/ninlight-station.service" \ NINLIGHT_DISPLAY_UNIT="$state/etc/systemd/system/ninlight-display.service" \ @@ -1047,6 +1048,38 @@ SUM_AFTER=$(find "$STATE/opt/ninlight-station/button" "$STATE/etc/systemd/system grep -Fq "systemctl enable ninlight-button.service" "$LOG" && pass "der zweite Lauf aktiviert den Tasterdienst erneut (idempotent, kein Fehler)" || fail "kein erneutes 'systemctl enable ninlight-button' im zweiten Lauf" echo "$OUT" | grep -q "veraltete Datei entfernt: button/" && fail "der zweite Lauf meldet eine veraltete Datei, obwohl nichts geloescht wurde" || pass "keine falsche 'veraltete Datei entfernt'-Meldung im zweiten Lauf" +echo +echo "=== Szenario: das dauerhafte Zustandsverzeichnis entsteht, die Bildsatzverzeichnisse nicht ===" +# /var/lib/ninlight-station traegt den Umschalter des Migrations-Bildsatzes - er muss +# Neustarts ueberleben, darf also nicht in /run liegen. Die beiden Bildsatzverzeichnisse +# dagegen duerfen hier NICHT entstehen: ein leeres Verzeichnis ist von einem bestueckten +# nicht zu unterscheiden, und genau das soll das Panel melden koennen ("SATZ FEHLT"). +STATE="$TMPROOT/sstate-state"; STUBBIN="$TMPROOT/binstate"; LOG="$TMPROOT/sstate.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +[ -d "$STATE/var/lib/ninlight-station" ] \ + && pass "/var/lib/ninlight-station wurde angelegt" \ + || fail "das dauerhafte Zustandsverzeichnis fehlt - der Umschalter ueberlebt keinen Neustart" +if [ -d "$STATE/var/lib/ninlight-station" ]; then + MODE=$(file_mode "$STATE/var/lib/ninlight-station") + [ "$MODE" = "755" ] && pass "Rechte 0755 (der Anzeigedienst muss den Umschalter lesen)" \ + || fail "Rechte $MODE statt 755" +fi +[ ! -e "$STATE/var/lib/ninlight-station/imageset" ] \ + && pass "die Umschalterdatei selbst wird nicht vorgeschrieben (Vorgabe gilt ohne sie)" \ + || fail "setup_pi.sh legt eine Umschalterdatei an - zweite Quelle fuer denselben Wert" +for d in migrate-wlan migrate-nocreds; do + [ ! -e "$STATE/opt/ninlight-station/images/$d" ] \ + && pass "images/$d bleibt ungeschaffen (leer waere nicht von bestueckt zu unterscheiden)" \ + || fail "images/$d wurde leer angelegt" +done + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From 4e1a965cbb448020e25ef660109ac93a143f9d30 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 03:02:21 +0200 Subject: [PATCH 143/150] feat(station): Abschlussbild beim Herunterfahren und beim Neustart E-Paper behaelt sein Bild dauerhaft, auch stromlos. Faehrt der Pi herunter, wird der Anzeigedienst mit abgeraeumt und das zuletzt gezeichnete Bild bleibt fuer immer stehen - bei der Abnahme stand dort die Rueckfrage des Tastermenues, und der ausgeschaltete Pi sah aus, als warte er noch auf eine Eingabe. Zwei neue Units, ninlight-poweroff.service (WantedBy=poweroff.target) und ninlight-reboot.service (WantedBy=reboot.target). Sie haengen am System, nicht am Tasterdienst: so greifen sie bei jedem Herunterfahren - Menue, "poweroff" per SSH, "reboot" - und nicht nur auf einem von drei Wegen. Zwei Units statt einer Fallunterscheidung, weil systemd einem ExecStart nicht sagt, wohin die Reise geht. Die Texte unterscheiden sich: "AUSGESCHALTET / Strom kann getrennt werden" gegen "NEUSTART / Strom NICHT trennen!" samt Hinweis auf die rund zwei Minuten, die der Zero W bis zum Anzeigedienst braucht. An diesem Pi hat ein Stromzug in genau dieser Wartezeit schon eine SD-Karte gekostet. Reihenfolge: die Units ordnen sich per After=ninlight-display.service hinter dessen Stop ein, und "ninlight-station display final " wartet zusaetzlich, bis systemctl is-active nicht mehr anschlaegt - dasselbe Muster wie display_handover(), nur ohne Wiederanfahren. Gibt der Dienst die Leitungen nicht frei, wird nicht gezeichnet (GPIO busy), sondern gewarnt: ein Anzeigefehler haelt das Herunterfahren nie auf. Nebenbei behoben: display_run() reichte nur "$1" an display.py weiter und haette "--final poweroff" halbiert. --- tools/station/README.md | 32 ++++++ tools/station/deploy.sh | 10 +- tools/station/display/display.py | 23 +++++ tools/station/display/render.py | 31 ++++++ tools/station/ninlight-poweroff.service | 42 ++++++++ tools/station/ninlight-reboot.service | 30 ++++++ tools/station/ninlight-station | 60 +++++++++++- tools/station/setup_pi.sh | 28 +++++- tools/station/tests/test_cli.sh | 113 ++++++++++++++++++++++ tools/station/tests/test_deploy.sh | 31 ++++++ tools/station/tests/test_render.py | 123 ++++++++++++++++++++++++ tools/station/tests/test_setup_pi.sh | 53 ++++++++++ 12 files changed, 572 insertions(+), 4 deletions(-) create mode 100644 tools/station/ninlight-poweroff.service create mode 100644 tools/station/ninlight-reboot.service diff --git a/tools/station/README.md b/tools/station/README.md index b046325..5670bd8 100644 --- a/tools/station/README.md +++ b/tools/station/README.md @@ -99,6 +99,38 @@ den Tasterdienst wie den Anzeigedienst; `deploy.sh` liefert `button/` bei jedem aus. Ein fehlgeschlagenes Aktivieren/Starten ist dabei nur eine Warnung — eine Station ohne Taster muss vollständig benutzbar bleiben. +## Abschlussbildschirm beim Herunterfahren und beim Neustart + +E-Paper behält sein Bild **dauerhaft**, auch stromlos. Fährt der Pi herunter, wird der +Anzeigedienst mit abgeräumt, und das zuletzt gezeichnete Bild bleibt für immer stehen — +bei der Abnahme stand dort die **Rückfrage** des Tastermenüs, und der ausgeschaltete Pi +sah aus, als warte er noch auf eine Eingabe. + +Dafür gibt es zwei systemd-Units, `ninlight-poweroff.service` (`WantedBy=poweroff.target`) +und `ninlight-reboot.service` (`WantedBy=reboot.target`). Sie hängen am **System**, nicht +am Tasterdienst: so greifen sie bei jedem Herunterfahren — über das Menü, über `poweroff` +per SSH, über `reboot` — und nicht nur auf einem von drei Wegen. + +**Die Texte unterscheiden sich, und das ist der Punkt:** + +| | Panel | +|---|---| +| Herunterfahren | „AUSGESCHALTET — Strom kann getrennt werden" | +| Neustart | „NEUSTART — Strom NICHT trennen!" plus der Hinweis, dass es rund 2 min dauert | + +Ein Zero W braucht gut anderthalb Minuten, bis der Anzeigedienst wieder zeichnet. Stünde +dort währenddessen „Strom kann getrennt werden", wäre das eine Aufforderung zu genau dem +Griff, der an diesem Pi schon eine SD-Karte gekostet hat. + +**Reihenfolge:** der Anzeigedienst hält die GPIO-/SPI-Leitungen; zwei Prozesse darauf +ergeben `lgpio.error: 'GPIO busy'`. Die Units ordnen sich deshalb per +`After=ninlight-display.service` hinter dessen Stop ein, und `ninlight-station display +final ` wartet zusätzlich, bis `systemctl is-active ninlight-display` +nicht mehr anschlägt — dasselbe Muster wie `display_handover()` bei `display test`, nur +ohne Wiederanfahren. Gibt der Dienst die Leitungen nicht frei, wird **nicht** gezeichnet, +sondern eine Zeile ins Journal geschrieben: ein Fehler an der Anzeige darf das +Herunterfahren nie aufhalten. + SSID und Passwort stehen in `/etc/ninlight-station.conf` auf dem Pi und müssen zu den Build-Parametern `NINLIGHT_RECOVERY_SSID` und `NINLIGHT_RECOVERY_PASSWORD` aus `config.local.cmake` passen. Weicht eines ab, findet die Lampe die Station nicht. diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index 46f4148..ee64044 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -255,6 +255,7 @@ echo "== uebertragen ==" rsync -a -c --delete -e "$SSH_BIN" "$STAGE/" "$HOST:/tmp/ninlight-images/" rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" \ "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HERE/ninlight-button.service" \ + "$HERE/ninlight-poweroff.service" "$HERE/ninlight-reboot.service" \ "$HOST:/tmp/" # Das Anzeigeverzeichnis als Ganzes (render.py, display.py, epdconfig.py, die beiden # uebernommenen Panel-Treiber und HERKUNFT.md - der Herkunftsnachweis gehoert mit auf @@ -277,6 +278,8 @@ BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} DISPLAY_UNIT_PATH=${NINLIGHT_DISPLAY_UNIT:-/etc/systemd/system/ninlight-display.service} BUTTON_UNIT_PATH=${NINLIGHT_BUTTON_UNIT:-/etc/systemd/system/ninlight-button.service} +POWEROFF_UNIT_PATH=${NINLIGHT_POWEROFF_UNIT:-/etc/systemd/system/ninlight-poweroff.service} +REBOOT_UNIT_PATH=${NINLIGHT_REBOOT_UNIT:-/etc/systemd/system/ninlight-reboot.service} sudo install -d -m 0755 "$ROOT_DIR/images" if [ "$FULL_SYNC" -eq 1 ]; then @@ -430,8 +433,13 @@ sudo install -m 0644 /tmp/ninlight-display.service "$DISPLAY_UNIT_PATH" sudo install -d -m 0755 "$ROOT_DIR/button" sudo rsync -a -c --delete /tmp/ninlight-button/ "$ROOT_DIR/button/" sudo install -m 0644 /tmp/ninlight-button.service "$BUTTON_UNIT_PATH" +# Abschlussbild beim Herunterfahren/Neustart. Nur die Dateien - aktiviert werden sie in +# setup_pi.sh (ein Deploy soll keine Units einschalten, die vorher aus waren). +sudo install -m 0644 /tmp/ninlight-poweroff.service "$POWEROFF_UNIT_PATH" +sudo install -m 0644 /tmp/ninlight-reboot.service "$REBOOT_UNIT_PATH" rm -rf /tmp/ninlight-images /tmp/server.py /tmp/products.py /tmp/ninlight-station /tmp/ninlight-station.service \ - /tmp/ninlight-display /tmp/ninlight-display.service /tmp/ninlight-button /tmp/ninlight-button.service + /tmp/ninlight-display /tmp/ninlight-display.service /tmp/ninlight-button /tmp/ninlight-button.service \ + /tmp/ninlight-poweroff.service /tmp/ninlight-reboot.service sudo systemctl daemon-reload if systemctl is-active --quiet ninlight-station; then if sudo systemctl restart ninlight-station; then diff --git a/tools/station/display/display.py b/tools/station/display/display.py index fb1e531..dd4f66c 100644 --- a/tools/station/display/display.py +++ b/tools/station/display/display.py @@ -560,6 +560,24 @@ def run_test(revision: str) -> None: log.info("Testbild gezeichnet (EPD_REVISION=%s)", revision) +def run_final(revision: str, kind: str) -> None: + """Zeichnet den Abschlussbildschirm ("poweroff"/"reboot") und legt das Panel schlafen. + + Das ist das Bild, das nach dem Herunterfahren DAUERHAFT stehen bleibt - E-Paper ist + bistabil, es haelt sein Bild auch ohne Strom. Bewusst ein VOLLAUFBAU: ein Teilaufbau + liesse Reste des vorigen Bildes stehen, und dieses eine Bild ueberdauert alle anderen. + + Aufgerufen wird das ausschliesslich ueber "ninlight-station display final " aus + einer systemd-Unit heraus, und zwar ERST, nachdem der Anzeigedienst gestoppt ist - er + haelt sonst die GPIO-/SPI-Leitungen ("lgpio.error: 'GPIO busy'"). Diese Uebergabe + macht das CLI (display_final()), nicht diese Funktion.""" + panel = Panel(revision) + panel.clear() + panel.draw_full(render.compose({"mode": "final", "final": kind})) + panel.sleep() + log.info("Abschlussbild gezeichnet (%s, EPD_REVISION=%s)", kind, revision) + + def run_clear(revision: str) -> None: panel = Panel(revision) panel.clear() @@ -634,6 +652,9 @@ def main() -> None: group = parser.add_mutually_exclusive_group() group.add_argument("--test", action="store_true", help="Testbild zeichnen und beenden") group.add_argument("--clear", action="store_true", help="Panel leeren und beenden") + group.add_argument("--final", choices=sorted(render.FINAL_LABELS), + help="Abschlussbild zeichnen und beenden (beim Herunterfahren " + "bzw. Neustart; die Texte unterscheiden sich)") args = parser.parse_args() revision = os.environ.get("EPD_REVISION", "V3") @@ -643,6 +664,8 @@ def main() -> None: run_test(revision) elif args.clear: run_clear(revision) + elif args.final: + run_final(revision, args.final) else: run_daemon(revision) except (DisplayUnavailable, ImportError) as e: diff --git a/tools/station/display/render.py b/tools/station/display/render.py index b123c07..7323add 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -200,6 +200,23 @@ IMAGESET_LABELS = { } IMAGESET_MISSING_SUFFIX = " - SATZ FEHLT" +# Der Abschlussbildschirm: was auf dem Panel stehen bleibt, wenn der Pi heruntergefaehrt +# oder neu gestartet wird. E-Paper ist bistabil - das zuletzt gezeichnete Bild bleibt fuer +# immer stehen, auch stromlos. Ohne diesen Schritt behauptet das Panel nach dem +# Ausschalten weiter den Zustand von vorher (bei der Abnahme stand dort die RUECKFRAGE, +# und der Pi sah aus, als warte er noch auf eine Eingabe). +# +# Die beiden Texte unterscheiden sich, und das ist der Punkt: beim Ausschalten DARF man +# den Stecker ziehen, beim Neustart NICHT - ein Zero W braucht gut anderthalb Minuten, bis +# der Anzeigedienst wieder zeichnet, und an genau diesem Pi hat ein Stromzug in dieser +# Wartezeit schon eine SD-Karte gekostet. +FINAL_LABELS = { + "poweroff": ("AUSGESCHALTET", "Strom kann getrennt werden", []), + "reboot": ("NEUSTART", "Strom NICHT trennen!", + ["Der Pi kommt in rund 2 min wieder.", + "Solange bleibt dieses Bild stehen."]), +} + # Woraus die Station die Signiervariante abgeleitet hat (products.classify_variant): # "-dev" kann in der Produkt-ID stehen, in der Version, oder in beiden. Die Quelle steht # mit auf dem Panel, weil genau hier am 2026-09-15 ein falsch signiertes Image @@ -250,6 +267,20 @@ def blocks(state: dict) -> list[tuple[str, str]]: mode = state.get("mode") out: list[tuple[str, str]] = [] + + if mode == "final": + # Abschlussbildschirm - KEINE Fusszeile und keine weiteren Zeilen: die Station ist + # im Begriff zu verschwinden, und die einzige Auskunft, auf die es jetzt ankommt, + # ist "darf ich den Stecker ziehen?". + label = FINAL_LABELS.get(state.get("final")) + if label is None: + # Unbekannte Spielart - lieber ehrlich nichts behaupten als "ausgeschaltet" + # zu sagen, wenn gleich wieder gebootet wird. + return [("Faehrt herunter", ROLE_BIG)] + title, hero, details = label + return ([(title, ROLE_HEADER), (hero, ROLE_HERO)] + + [(d, ROLE_DETAIL) for d in details]) + # Die Fusszeile haengt an JEDEM Statusbild - auch an "Station aus" und an "Status # unbekannt", die beide sofort zurueckkehren. Sie wird deshalb hier gebaut und unten # bei jedem Rueckgabepunkt angehaengt. diff --git a/tools/station/ninlight-poweroff.service b/tools/station/ninlight-poweroff.service new file mode 100644 index 0000000..9df449a --- /dev/null +++ b/tools/station/ninlight-poweroff.service @@ -0,0 +1,42 @@ +[Unit] +Description=ninlight-Station: "Ausgeschaltet"-Bild aufs E-Paper +Documentation=file:/opt/ninlight-station/README.md +# E-Paper ist bistabil: das zuletzt gezeichnete Bild bleibt fuer immer stehen, auch +# stromlos. Faehrt der Pi herunter, wird der Anzeigedienst mit abgeraeumt - ohne diese +# Unit behauptet das Panel danach dauerhaft den Zustand von vorher. Bei der Abnahme stand +# dort die Rueckfrage des Tastermenues, und der ausgeschaltete Pi sah aus, als warte er +# noch auf eine Eingabe. +# +# Sie haengt am SYSTEM, nicht am Tasterdienst: so greift sie bei jedem Herunterfahren - +# ueber das Tastermenue (systemctl poweroff), ueber "poweroff" per SSH, ueber jeden +# anderen Weg nach poweroff.target. Am Taster allein haetten wir die Anzeige nur auf +# einem von drei Wegen. +# +# Die Gegenstueck-Unit ninlight-reboot.service macht dasselbe fuer den Neustart, mit +# ANDEREM Text: beim Neustart darf man den Stecker gerade nicht ziehen. +DefaultDependencies=no +# Erst zeichnen, wenn der Anzeigedienst die GPIO-/SPI-Leitungen losgelassen hat: zwei +# Prozesse darauf enden in "lgpio.error: 'GPIO busy'" (in diesem Projekt schon zweimal +# passiert). "After=" auf eine Unit, die gerade gestoppt wird, ordnet deren Stop VOR den +# Start dieser hier - genau die gesuchte Reihenfolge. Das CLI wartet zusaetzlich, bis +# "systemctl is-active ninlight-display" nicht mehr anschlaegt (display_final()). +After=ninlight-display.service ninlight-button.service +# ... und noch BEVOR das System die Leitungen verliert: nach umount.target gibt es weder +# /opt/ninlight-station noch die Schriftdateien, aus denen das Bild entsteht. +Before=umount.target shutdown.target final.target + +[Service] +Type=oneshot +# Das Bild entsteht ueber das vorhandene CLI, nicht durch nachgebaute Logik: nur dort +# stehen EPD_REVISION aus /etc/ninlight-station.conf, die Uebergabe der GPIO-Leitungen +# und die Zusage "ein Fehler an der Anzeige haelt das Herunterfahren nie auf" (das CLI +# beendet sich hier immer mit 0). +ExecStart=/usr/local/bin/ninlight-station display final poweroff +# Ein Bildaufbau auf diesem Panel dauert Sekunden, nicht Minuten; dazu die Wartezeit auf +# die Freigabe der Leitungen. Der Deckel ist da, damit ein haengendes Panel das +# Herunterfahren nicht dauerhaft aufhaelt - systemds Vorgabe von 90 s waere hier eine +# sehr lange Zeit vor einem dunklen Geraet. +TimeoutStartSec=30 + +[Install] +WantedBy=poweroff.target diff --git a/tools/station/ninlight-reboot.service b/tools/station/ninlight-reboot.service new file mode 100644 index 0000000..d6ed04e --- /dev/null +++ b/tools/station/ninlight-reboot.service @@ -0,0 +1,30 @@ +[Unit] +Description=ninlight-Station: "Neustart"-Bild aufs E-Paper +Documentation=file:/opt/ninlight-station/README.md +# Das Gegenstueck zu ninlight-poweroff.service, mit einem ANDEREN Text - und das ist hier +# kein Schoenheitsfehler, sondern der Zweck der zweiten Unit: +# +# Herunterfahren -> "AUSGESCHALTET / Strom kann getrennt werden" +# Neustart -> "NEUSTART / Strom NICHT trennen!" +# +# Ein Raspberry Pi Zero W braucht gut anderthalb Minuten, bis der Anzeigedienst wieder +# zeichnet. Stuende dort waehrenddessen "Strom kann getrennt werden", waere das eine +# Aufforderung zu genau dem Griff, der an diesem Pi schon eine SD-Karte gekostet hat. +# +# Zwei Units statt einer mit Fallunterscheidung: systemd sagt einem ExecStop/ExecStart +# nicht, wohin die Reise geht - man muesste "systemctl list-jobs" nach "reboot.target" +# durchsuchen. Ueber WantedBy= ist es dagegen der Abschalt-Zieleinheit selbst entnommen +# und damit nicht zu verwechseln. +DefaultDependencies=no +# Reihenfolge und Begruendung wie in ninlight-poweroff.service (GPIO-Uebergabe, und noch +# vor umount.target zeichnen). +After=ninlight-display.service ninlight-button.service +Before=umount.target shutdown.target final.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/ninlight-station display final reboot +TimeoutStartSec=30 + +[Install] +WantedBy=reboot.target diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 1b47493..490a5ea 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -674,7 +674,11 @@ migrate_on() { # an den Python-Prozess durch. "sudo", weil /dev/spidev0.0 und die GPIO-Leitungen root # gehoeren (der Anzeigedienst laeuft aus demselben Grund als root). display_run() { - sudo env EPD_REVISION="$EPD_REVISION" python3 "$STATION_LIB/display/display.py" "$1" + # "$@", nicht "$1": das Abschlussbild wird mit ZWEI Argumenten gerufen + # ("--final poweroff"), und ein verschlucktes zweites waere hier besonders teuer - + # argparse erzwingt bei --final eine Auswahl, der Aufruf scheiterte also, und auf dem + # Panel bliebe die letzte Rueckfrage stehen. + sudo env EPD_REVISION="$EPD_REVISION" python3 "$STATION_LIB/display/display.py" "$@" } # Handbetrieb am Panel ("display test"/"display clear"), waehrend der Anzeigedienst @@ -779,6 +783,44 @@ display_off() { display_run --clear || echo "Warnung: Panel konnte nicht geleert werden - Anzeige pruefen" >&2 } +# --- Abschlussbildschirm beim Herunterfahren und beim Neustart ---------------------- +# +# E-Paper behaelt sein Bild dauerhaft, auch stromlos. Faehrt der Pi herunter, wird der +# Anzeigedienst mit abgeraeumt und das ZULETZT gezeichnete Bild bleibt fuer immer stehen - +# bei der Abnahme stand dort die Rueckfrage, und der ausgeschaltete Pi sah aus, als warte +# er noch auf eine Eingabe. +# +# Dieser Handgriff gehoert deshalb ins System (ninlight-poweroff.service / +# ninlight-reboot.service), nicht in den Tasterdienst: so greift er bei JEDEM +# Herunterfahren - ueber das Menue, ueber "poweroff" per SSH, ueber "reboot" - und nicht +# nur auf einem von drei Wegen. +# +# Wie lange auf die Freigabe der GPIO-/SPI-Leitungen gewartet wird. Die Units ordnen sich +# per "After=ninlight-display.service" HINTER dessen Stop ein; systemd erledigt das also +# schon. Die Wartezeit ist das Netz darunter (und der Weg fuer einen Handaufruf zum +# Ausprobieren): zwei Prozesse auf denselben Leitungen enden in "lgpio.error: 'GPIO busy'", +# und das ist in diesem Projekt schon zweimal passiert. +DISPLAY_RELEASE_WAIT_S=${NINLIGHT_DISPLAY_RELEASE_WAIT_S:-10} + +# $1 = poweroff|reboot. Beendet IMMER mit 0: ein Fehler an der Anzeige darf das +# Herunterfahren nie aufhalten (dieselbe Zusage wie ueberall sonst). +display_final() { + _waited=0 + while systemctl is-active --quiet ninlight-display 2>/dev/null; do + if [ "$_waited" -ge "$DISPLAY_RELEASE_WAIT_S" ]; then + # Bewusst KEIN "systemctl stop" von hier aus: das liefe mitten in der + # Abschalt-Transaktion und koennte sich mit ihr verhaken. Lieber eine ehrliche + # Zeile im Journal als ein haengendes Herunterfahren. + echo "Warnung: Anzeigedienst laeuft nach ${DISPLAY_RELEASE_WAIT_S}s noch - kein Abschlussbild (GPIO belegt)" >&2 + return 0 + fi + sleep 1 + _waited=$((_waited + 1)) + done + display_run --final "$1" || echo "Warnung: Abschlussbild konnte nicht gezeichnet werden - Anzeige pruefen" >&2 + return 0 +} + full_off() { # ZUERST: ein noch vorgemerkter Zeitschalter muss mit weg, sonst feuert er spaeter in # eine Station hinein, die laengst aus ist - oder, schlimmer, in eine, die inzwischen @@ -1028,6 +1070,12 @@ display_cmd() { test) display_handover --test ;; clear) display_handover --clear ;; off) display_off ;; + final) + case "${2:-}" in + poweroff|reboot) display_final "$2" ;; + *) echo "Fehler: 'display final' braucht 'poweroff' oder 'reboot'" >&2; exit 2 ;; + esac + ;; *) usage; exit 2 ;; esac } @@ -1061,6 +1109,14 @@ usage() { echo " Ein laufender Anzeigedienst wird dafuer kurz gestoppt und" >&2 echo " danach wieder gestartet (er haelt sonst die GPIO-Leitungen)." >&2 echo " display clear Panel leeren (ebenfalls mit kurzer Unterbrechung des Dienstes)." >&2 + echo " display final {poweroff|reboot}" >&2 + echo " Abschlussbild aufs Panel. Kein Handgriff fuer Menschen: das" >&2 + echo " rufen ninlight-poweroff.service/ninlight-reboot.service beim" >&2 + echo " Herunterfahren bzw. Neustart auf, nachdem der Anzeigedienst" >&2 + echo " die GPIO-Leitungen freigegeben hat. E-Paper behaelt sein Bild" >&2 + echo " dauerhaft - ohne diesen Schritt bliebe die letzte Rueckfrage" >&2 + echo " auf dem Panel stehen. Die Texte unterscheiden sich: beim" >&2 + echo " Neustart darf man den Stecker gerade NICHT ziehen." >&2 echo " display off Anzeigedienst dauerhaft stoppen und das Panel leeren. 'on'/'off'" >&2 echo " der Station brauchen das nicht - der Anzeigedienst laeuft" >&2 echo " unabhaengig davon durch und zeichnet den Zustand selbst." >&2 @@ -1114,7 +1170,7 @@ case "$_cmd" in autooff_fire ;; display) - display_cmd "${1:-}" + display_cmd "${1:-}" "${2:-}" ;; off) full_off diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh index c46cdb4..b568be0 100755 --- a/tools/station/setup_pi.sh +++ b/tools/station/setup_pi.sh @@ -94,6 +94,7 @@ rm -rf "$STAGE_DIR/ninlight-display" "$STAGE_DIR/ninlight-button" REMOTE "$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" \ "$HERE/ninlight-station.service" "$HERE/ninlight-display.service" "$HERE/ninlight-button.service" \ + "$HERE/ninlight-poweroff.service" "$HERE/ninlight-reboot.service" \ "$HOST:$STAGE_DIR/" # Das Anzeigeverzeichnis als Ganzes: render.py, display.py, epdconfig.py, die beiden # uebernommenen Panel-Treiber und HERKUNFT.md (der Herkunftsnachweis gehoert mit auf den @@ -115,6 +116,8 @@ BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} DISPLAY_UNIT_PATH=${NINLIGHT_DISPLAY_UNIT:-/etc/systemd/system/ninlight-display.service} BUTTON_UNIT_PATH=${NINLIGHT_BUTTON_UNIT:-/etc/systemd/system/ninlight-button.service} +POWEROFF_UNIT_PATH=${NINLIGHT_POWEROFF_UNIT:-/etc/systemd/system/ninlight-poweroff.service} +REBOOT_UNIT_PATH=${NINLIGHT_REBOOT_UNIT:-/etc/systemd/system/ninlight-reboot.service} # Die Ablage wird in JEDEM Fall abgeraeumt, auch wenn dieser Block vorzeitig aussteigt # (Task 6b F2, N1). Vorher stand das "rm -rf" nur am Blockende und wurde beim Abbruch @@ -124,7 +127,8 @@ cleanup_stage() { rm -rf "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" \ "$STAGE_DIR/ninlight-station.service" "$STAGE_DIR/ninlight-display.service" \ "$STAGE_DIR/ninlight-display" "$STAGE_DIR/ninlight-button.service" \ - "$STAGE_DIR/ninlight-button" + "$STAGE_DIR/ninlight-button" "$STAGE_DIR/ninlight-poweroff.service" \ + "$STAGE_DIR/ninlight-reboot.service" } trap cleanup_stage EXIT @@ -215,6 +219,9 @@ for f in "$ROOT_DIR"/button/*; do fi done sudo install -m 0644 "$STAGE_DIR/ninlight-button.service" "$BUTTON_UNIT_PATH" +# Abschlussbild beim Herunterfahren und beim Neustart (zwei Units, zwei Texte). +sudo install -m 0644 "$STAGE_DIR/ninlight-poweroff.service" "$POWEROFF_UNIT_PATH" +sudo install -m 0644 "$STAGE_DIR/ninlight-reboot.service" "$REBOOT_UNIT_PATH" # Das Aufraeumen der Ablage macht der EXIT-Trap oben - auf beiden Wegen aus diesem Block. sudo systemctl daemon-reload echo " installiert" @@ -400,6 +407,25 @@ sudo systemctl restart ninlight-button.service >/dev/null 2>&1 || \ echo " Tasterdienst aktiviert" REMOTE +echo "== Abschlussbild aktivieren ==" +# Zwei Units, zwei Texte: beim Herunterfahren "Strom kann getrennt werden", beim Neustart +# "Strom NICHT trennen!". Ohne sie bliebe auf dem E-Paper fuer immer das letzte Bild +# stehen - bei der Abnahme war das die Rueckfrage des Tastermenues. +# +# Dieselbe weiche Behandlung wie Anzeige und Taster: ein fehlgeschlagenes "enable" ist +# eine Warnung, kein Abbruch. Eine Station, deren Panel nach dem Ausschalten das falsche +# Bild zeigt, ist aergerlich; eine Station, die sich gar nicht einrichten laesst, ist +# unbrauchbar. "enable" allein, kein "start": diese Units tun beim Systemstart NICHTS, +# sie gehoeren zur Abschalt-Transaktion. +"$SSH_BIN" "$HOST" bash -s <<'REMOTE' +set -e +for unit in ninlight-poweroff.service ninlight-reboot.service; do + sudo systemctl enable "$unit" >/dev/null 2>&1 || \ + echo " Warnung: 'systemctl enable $unit' fehlgeschlagen - das E-Paper behaelt nach dem Herunterfahren sein letztes Bild" >&2 +done +echo " Abschlussbild aktiviert (Herunterfahren und Neustart)" +REMOTE + echo "== NetworkManager-Profile ==" # Zwei Dinge, die man an einer echten Lampenstation nicht falsch machen darf: # - /etc/ninlight-station.conf ist 0600 root:root (das Passwort soll nicht diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index aa802f6..b7c9977 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -129,6 +129,21 @@ case "\$1" in unit="\${!#}" if [ "\$unit" = "ninlight-display" ]; then code="\${FAKE_DISPLAY_SERVICE_ACTIVE_EXIT:-0}" + # Testschalter fuer den Abschlussbildschirm: der Anzeigedienst gilt fuer die + # ersten N Abfragen als AKTIV und danach als gestoppt - so, wie es beim + # Herunterfahren wirklich ablaeuft (systemd stoppt ihn, das dauert einen Moment). + # Gezaehlt wird in einer Datei, weil jeder Stub-Aufruf ein eigener Prozess ist. + if [ -n "\${FAKE_DISPLAY_ACTIVE_FOR:-}" ]; then + n=\$(cat "$rundir/.display-active-count" 2>/dev/null || echo 0) + n=\$((n + 1)) + echo "\$n" >"$rundir/.display-active-count" + if [ "\$n" -le "\${FAKE_DISPLAY_ACTIVE_FOR}" ]; then + echo active + exit 0 + fi + echo inactive + exit 3 + fi else code="\${FAKE_SYSTEMCTL_ACTIVE_EXIT:-0}" fi @@ -1308,6 +1323,104 @@ ERR=$(run_cli "$SBX" migrate on --listen 2>&1 >/dev/null) set -e echo "$ERR" | grep -q "keine Migrationsimages" && pass "nach dem Umschalten warnt sie" || fail "keine Warnung, obwohl der gewaehlte Satz leer ist: $ERR" +echo +echo "=== Abschluss A: 'display final' kennt zwei verschiedene Bilder ===" +# E-Paper behaelt sein Bild dauerhaft. Beim Ausschalten darf man den Stecker ziehen, beim +# Neustart gerade nicht - deshalb zwei Schalter, nicht einer. +SBX="$TMPROOT/fin1" +setup_sandbox "$SBX" +export FAKE_DISPLAY_SERVICE_ACTIVE_EXIT=3 # Anzeigedienst laeuft nicht +set +e +OUT=$(run_cli "$SBX" display final poweroff 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +grep -q "STUB display.py --final poweroff" "$SBX/cmd.log" && pass "display.py wird mit '--final poweroff' aufgerufen" || fail "falscher Aufruf: $(cat "$SBX/cmd.log")" +: >"$SBX/cmd.log" +run_cli "$SBX" display final reboot >/dev/null 2>&1 +grep -q "STUB display.py --final reboot" "$SBX/cmd.log" && pass "und mit '--final reboot' fuer den Neustart" || fail "falscher Aufruf: $(cat "$SBX/cmd.log")" +grep -q "poweroff" "$SBX/cmd.log" && fail "der Neustart ruft das Ausschaltbild auf" || pass "die beiden Wege werden nicht verwechselt" +unset FAKE_DISPLAY_SERVICE_ACTIVE_EXIT + +echo +echo "=== Abschluss B: ohne 'poweroff'/'reboot' passiert nichts ===" +SBX="$TMPROOT/fin2" +setup_sandbox "$SBX" +set +e +ERR=$(run_cli "$SBX" display final 2>&1 >/dev/null) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0" || fail "Exit-Code 0 ohne Spielart" +grep -q "STUB display.py" "$SBX/cmd.log" 2>/dev/null && fail "display.py wurde trotzdem aufgerufen" || pass "das Panel wurde nicht angefasst" +set +e +ERR=$(run_cli "$SBX" display final quatsch 2>&1 >/dev/null) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 fuer eine unbekannte Spielart" || fail "Exit-Code 0 fuer 'quatsch'" +grep -q "STUB display.py" "$SBX/cmd.log" 2>/dev/null && fail "display.py wurde fuer 'quatsch' aufgerufen" || pass "und auch dafuer nicht" + +echo +echo "=== Abschluss C: gezeichnet wird erst, NACHDEM der Anzeigedienst die Leitungen freigegeben hat ===" +# Zwei Prozesse auf denselben GPIO-/SPI-Leitungen enden in "lgpio.error: 'GPIO busy'" - +# in diesem Projekt schon zweimal passiert. +SBX="$TMPROOT/fin3" +setup_sandbox "$SBX" +export FAKE_DISPLAY_ACTIVE_FOR=2 # erst beim dritten Blick ist er gestoppt +set +e +OUT=$(NINLIGHT_DISPLAY_RELEASE_WAIT_S=5 run_cli "$SBX" display final poweroff 2>&1) +RC=$? +set -e +unset FAKE_DISPLAY_ACTIVE_FOR +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +grep -q "STUB display.py --final poweroff" "$SBX/cmd.log" && pass "das Abschlussbild wurde gezeichnet" || fail "gar nicht gezeichnet: $(cat "$SBX/cmd.log")" +# Die Reihenfolge in der GEMEINSAMEN Logdatei ist der Beleg: jede is-active-Abfrage steht +# vor dem display.py-Aufruf, keine danach. +# "|| true" ueberall: ohne Treffer liefert grep 1, und unter "set -e" wuerde die SUITE an +# dieser Stelle stillschweigend abbrechen, statt einen Fehler zu melden - eine Mutation, +# die das Warten ganz entfernt, waere dann scheinbar gruen (genau so passiert). +DRAW_LINE=$(grep -n "STUB display.py" "$SBX/cmd.log" 2>/dev/null | head -n1 | cut -d: -f1 || true) +LAST_CHECK=$(grep -n "systemctl is-active --quiet ninlight-display" "$SBX/cmd.log" 2>/dev/null | tail -n1 | cut -d: -f1 || true) +CHECKS=$(grep -c "systemctl is-active --quiet ninlight-display" "$SBX/cmd.log" 2>/dev/null || true) +CHECKS=${CHECKS:-0} +if [ -n "$DRAW_LINE" ] && [ -n "$LAST_CHECK" ] && [ "$LAST_CHECK" -lt "$DRAW_LINE" ]; then + pass "alle Abfragen des Anzeigedienstes stehen VOR dem Zeichnen" +else + fail "gezeichnet wurde nicht nach der Freigabe (Zeichnen Zeile '$DRAW_LINE', letzte Abfrage Zeile '$LAST_CHECK')" +fi +[ "$CHECKS" -ge 3 ] && pass "es wurde wirklich gewartet ($CHECKS Abfragen)" || fail "nur $CHECKS Abfrage(n) - es wurde gar nicht auf die Freigabe gewartet" + +echo +echo "=== Abschluss D: ein Anzeigedienst, der nicht loslaesst, haelt das Herunterfahren nicht auf ===" +# Lieber kein Abschlussbild als ein haengendes Herunterfahren - und auf keinen Fall ein +# zweiter Prozess auf den belegten Leitungen. +SBX="$TMPROOT/fin4" +setup_sandbox "$SBX" +export FAKE_DISPLAY_ACTIVE_FOR=9999 +set +e +ERR=$(NINLIGHT_DISPLAY_RELEASE_WAIT_S=2 run_cli "$SBX" display final poweroff 2>&1 >/dev/null) +RC=$? +set -e +unset FAKE_DISPLAY_ACTIVE_FOR +[ "$RC" -eq 0 ] && pass "Exit-Code 0 (ein Anzeigefehler haelt das Herunterfahren nie auf)" || fail "Exit-Code $RC: $ERR" +grep -q "STUB display.py" "$SBX/cmd.log" && fail "es wurde auf belegte Leitungen gezeichnet - 'GPIO busy'" || pass "nicht gezeichnet, solange der Dienst haelt" +echo "$ERR" | grep -qi "warnung" && pass "Warnung auf stderr" || fail "keine Warnung: $ERR" +# Und es wurde NICHT versucht, den Dienst von hier aus zu stoppen: das liefe mitten in +# der Abschalt-Transaktion. +grep -q "systemctl stop ninlight-display" "$SBX/cmd.log" && fail "'systemctl stop' mitten im Herunterfahren" || pass "kein 'systemctl stop' aus dem Abschaltschritt heraus" + +echo +echo "=== Abschluss E: ein scheiterndes display.py bleibt eine Warnung ===" +SBX="$TMPROOT/fin5" +setup_sandbox "$SBX" +export FAKE_DISPLAY_SERVICE_ACTIVE_EXIT=3 +set +e +ERR=$(FAKE_DISPLAY_EXIT=1 run_cli "$SBX" display final reboot 2>&1 >/dev/null) +RC=$? +set -e +unset FAKE_DISPLAY_SERVICE_ACTIVE_EXIT +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC - ein Anzeigefehler wuerde das Herunterfahren rot machen: $ERR" +echo "$ERR" | grep -qi "warnung" && pass "Warnung auf stderr" || fail "keine Warnung: $ERR" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index ce5722b..c5fc25d 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -53,6 +53,8 @@ setup_sandbox() { echo "# dummy" >"$sandbox/tools/station/ninlight-station.service" echo "# dummy" >"$sandbox/tools/station/ninlight-display.service" echo "# dummy" >"$sandbox/tools/station/ninlight-button.service" + echo "# dummy" >"$sandbox/tools/station/ninlight-poweroff.service" + echo "# dummy" >"$sandbox/tools/station/ninlight-reboot.service" # display/ wird ECHT kopiert, nicht durch Attrappen ersetzt: die Zusage aus Task 6b # ist, dass die beiden uebernommenen Panel-Treiber UND der Herkunftsnachweis # HERKUNFT.md mit auf den Pi gehen. Mit selbst erfundenen Dateinamen im Sandbox-Repo @@ -185,6 +187,8 @@ run_deploy() { NINLIGHT_STATION_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-station.service" \ NINLIGHT_DISPLAY_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-display.service" \ NINLIGHT_BUTTON_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-button.service" \ + NINLIGHT_POWEROFF_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-poweroff.service" \ + NINLIGHT_REBOOT_UNIT="$SANDBOX_STATE/etc/systemd/system/ninlight-reboot.service" \ NINLIGHT_SIGN_DIR="$sign_dir" \ "$sandbox/tools/station/deploy.sh" "$@" } @@ -1240,6 +1244,33 @@ set -e && pass "der Altbestand images/migrate wurde entfernt" \ || fail "images/migrate liegt noch da" +echo +echo "=== Szenario 32: die beiden Abschlussbild-Units werden mit ausgeliefert ===" +# Ohne sie bliebe auf dem E-Paper nach dem Herunterfahren fuer immer das letzte Bild +# stehen - bei der Abnahme war das die Rueckfrage des Tastermenues. +SANDBOX="$TMPROOT/s32"; STUBBIN="$TMPROOT/bin32"; LOG="$TMPROOT/s32.log"; SANDBOX_STATE="$TMPROOT/s32-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +mkdir -p "$SANDBOX/build/foo" +echo "foo-image" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +: >"$LOG" +set +e +OUT=$(run_deploy "$SANDBOX" pi-test 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT" +for u in ninlight-poweroff ninlight-reboot; do + [ -f "$SANDBOX_STATE/etc/systemd/system/$u.service" ] \ + && pass "$u.service installiert" || fail "$u.service fehlt - sie waere toter Text im Repo" +done +# Ein Deploy darf keine Unit EINSCHALTEN, die vorher aus war - das macht setup_pi.sh. +grep -Fq "systemctl enable ninlight-poweroff" "$LOG" \ + && fail "der Deploy aktiviert die Abschlussbild-Unit" \ + || pass "der Deploy aktiviert sie nicht (das ist setup_pi.sh's Aufgabe)" +# Und die Ablage ist hinterher leer. +[ ! -e /tmp/ninlight-poweroff.service ] && [ ! -e /tmp/ninlight-reboot.service ] \ + && pass "die Ablage wurde aufgeraeumt" || fail "Unit-Dateien blieben unter /tmp liegen" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 0294222..3146573 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -1865,3 +1865,126 @@ def test_the_display_and_the_server_agree_on_the_set_names(): spec.loader.exec_module(server) assert display.IMAGESET_DIRS == server.IMAGE_SET_DIRS assert display.IMAGESET_DEFAULT == server.DEFAULT_IMAGE_SET + + +# --- Abschlussbildschirm beim Herunterfahren und beim Neustart ----------------------- +# +# E-Paper ist bistabil: das zuletzt gezeichnete Bild bleibt fuer immer stehen, auch +# stromlos. Ohne dieses Bild behauptet das Panel nach dem Ausschalten weiter den Zustand +# von vorher - bei der Abnahme stand dort die Rueckfrage des Tastermenues, und der Pi sah +# aus, als warte er noch auf eine Eingabe. + +def _final_text(kind): + return " ".join(render.lines({"mode": "final", "final": kind})) + + +def test_the_shutdown_and_the_reboot_screen_differ(): + """Der wichtigste Unterschied im ganzen Bild: beim Ausschalten DARF man den Stecker + ziehen, beim Neustart NICHT (an diesem Pi hat ein Stromzug schon eine SD-Karte + gekostet).""" + off, reboot = _final_text("poweroff"), _final_text("reboot") + assert off != reboot + assert "AUSGESCHALTET" in off and "NEUSTART" in reboot + # Ausschalten: erlaubt das Trennen ausdruecklich. + assert "kann getrennt werden" in off + # Neustart: verbietet es ebenso ausdruecklich - und sagt nirgends das Gegenteil. + assert "NICHT trennen" in reboot + assert "kann getrennt werden" not in reboot + + +def test_the_reboot_screen_says_how_long_it_stays(): + """Ein Zero W braucht gut anderthalb Minuten bis zum Anzeigedienst - ohne diesen Satz + sieht das stehende Bild nach "haengt" aus, und genau dann greift jemand zum Stecker.""" + assert "min" in _final_text("reboot") + + +def test_the_final_screen_carries_nothing_else(): + """Kein Bildsatz, keine Fusszeile, kein Zaehler: die Station ist im Begriff zu + verschwinden, und die einzige Auskunft, auf die es jetzt ankommt, ist "Stecker?".""" + state = {"mode": "final", "final": "poweroff", "imageset": "mit-wlan", + "imageset_ok": True, "served": 7} + blocks = render.blocks(state) + assert not any(role == render.ROLE_FOOTER for _, role in blocks) + assert not any("Bildsatz" in text or "Ausgeliefert" in text for text, _ in blocks) + + +def test_an_unknown_final_kind_claims_neither(): + """Lieber gar nichts behaupten als "ausgeschaltet" zu sagen, wenn gleich wieder + gebootet wird.""" + text = " ".join(render.lines({"mode": "final", "final": "was-auch-immer"})) + assert "AUSGESCHALTET" not in text + assert "getrennt" not in text + + +def test_both_final_screens_are_actually_drawable(): + for kind in ("poweroff", "reboot"): + image = render.compose({"mode": "final", "final": kind}) + assert image.size == (render.PANEL_WIDTH_PX, render.PANEL_HEIGHT_PX) + assert _ink_box(image) is not None, kind + + +def test_run_final_draws_fully_and_sleeps(monkeypatch): + """Ueber die ECHTE display.run_final(): Vollaufbau (kein Teilaufbau - dieses eine Bild + ueberdauert alle anderen und darf keine Reste des vorigen tragen) und danach schlafen + legen, damit das Panel nicht unter Bias in den Ruhezustand geht.""" + calls = [] + + class _Panel: + def __init__(self, revision): + calls.append(("init", revision)) + + def clear(self): + calls.append(("clear", None)) + + def draw_full(self, image): + calls.append(("full", image)) + + def draw_partial(self, image): + calls.append(("partial", image)) + return False + + def sleep(self): + calls.append(("sleep", None)) + + monkeypatch.setattr(display, "Panel", _Panel) + display.run_final("V3", "reboot") + + kinds = [k for k, _ in calls] + assert kinds == ["init", "clear", "full", "sleep"], kinds + # ... und gezeichnet wurde wirklich das Neustart-Bild, nicht irgendeines. + image = dict((k, v) for k, v in calls)["full"] + expected = render.compose({"mode": "final", "final": "reboot"}) + assert list(image.getdata()) == list(expected.getdata()) + assert list(image.getdata()) != list( + render.compose({"mode": "final", "final": "poweroff"}).getdata()) + + +def test_display_main_passes_the_final_kind_through(monkeypatch): + """Die Verdrahtung in display.main() selbst: "--final reboot" muss beim Neustartbild + landen. Ohne diese Zusage liesse sich die Spielart in main() durch eine feste + ersetzen, ohne dass ein Test rot wuerde - und auf dem Panel stuende beim Neustart + "Strom kann getrennt werden".""" + drawn = [] + + class _Panel: + def __init__(self, revision): + pass + + def clear(self): + pass + + def draw_full(self, image): + drawn.append(image) + + def sleep(self): + pass + + monkeypatch.setattr(display, "Panel", _Panel) + monkeypatch.setenv("EPD_REVISION", "V3") + for kind in ("reboot", "poweroff"): + drawn.clear() + monkeypatch.setattr(sys, "argv", ["display.py", "--final", kind]) + display.main() + assert len(drawn) == 1 + expected = render.compose({"mode": "final", "final": kind}) + assert list(drawn[0].getdata()) == list(expected.getdata()), kind diff --git a/tools/station/tests/test_setup_pi.sh b/tools/station/tests/test_setup_pi.sh index cc877fc..cd9e109 100755 --- a/tools/station/tests/test_setup_pi.sh +++ b/tools/station/tests/test_setup_pi.sh @@ -146,6 +146,13 @@ echo "STUB systemctl \$*" >>"$log" # ein Szenario NUR ihn scheitern lassen kann, ohne die Anzeige mitzureissen - sonst # liesse sich nicht zeigen, dass ein fehlgeschlagenes enable/start des Tasters fuer # sich genommen keinen Abbruch ausloest. +# Gezielt EINE Unit scheitern lassen (Abschlussbild): FAKE_ENABLE_FAIL_SUBSTR nennt den +# Teilstring des Unit-Namens, dessen "enable" fehlschlaegt. +if [ -n "\${FAKE_ENABLE_FAIL_SUBSTR:-}" ] && [ "\$1" = "enable" ]; then + case "\$*" in + *"\$FAKE_ENABLE_FAIL_SUBSTR"*) exit 1 ;; + esac +fi case "\$*" in *ninlight-button*) case "\$1" in @@ -217,6 +224,8 @@ run_setup() { NINLIGHT_STATION_UNIT="$state/etc/systemd/system/ninlight-station.service" \ NINLIGHT_DISPLAY_UNIT="$state/etc/systemd/system/ninlight-display.service" \ NINLIGHT_BUTTON_UNIT="$state/etc/systemd/system/ninlight-button.service" \ + NINLIGHT_POWEROFF_UNIT="$state/etc/systemd/system/ninlight-poweroff.service" \ + NINLIGHT_REBOOT_UNIT="$state/etc/systemd/system/ninlight-reboot.service" \ NINLIGHT_NM_DIR="$state/etc/NetworkManager/system-connections" \ NINLIGHT_BOOT_CONFIG_NEW="$state/boot/firmware/config.txt" \ NINLIGHT_BOOT_CONFIG_OLD="$state/boot/config.txt" \ @@ -1080,6 +1089,50 @@ for d in migrate-wlan migrate-nocreds; do || fail "images/$d wurde leer angelegt" done +echo +echo "=== Szenario: der Abschlussbildschirm wird installiert UND aktiviert ===" +# Der Abschlussbildschirm gehoert ins System, nicht in den Tasterdienst: so greift er bei +# jedem Herunterfahren (Menue, "poweroff" per SSH, Neustart) und nicht nur auf einem von +# drei Wegen. Zwei Units, weil sich die Texte unterscheiden muessen. +STATE="$TMPROOT/sfin-state"; STUBBIN="$TMPROOT/binfin"; LOG="$TMPROOT/sfin.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT" +for u in ninlight-poweroff ninlight-reboot; do + [ -f "$STATE/etc/systemd/system/$u.service" ] \ + && pass "$u.service installiert" || fail "$u.service fehlt" + grep -Fq "systemctl enable $u.service" "$LOG" \ + && pass "$u.service aktiviert" || fail "kein 'systemctl enable $u' im Log" +done +# "enable", aber KEIN "start": diese Units tun beim Systemstart nichts, sie gehoeren zur +# Abschalt-Transaktion. Ein "start" wuerde das Abschlussbild mitten im Betrieb zeichnen. +grep -Fq "systemctl restart ninlight-poweroff" "$LOG" \ + && fail "die Abschlussbild-Unit wird beim Einrichten gestartet - das Bild kaeme sofort" \ + || pass "sie wird nicht gestartet, nur aktiviert" +grep -Fq "systemctl start ninlight-poweroff" "$LOG" \ + && fail "die Abschlussbild-Unit wird beim Einrichten gestartet" \ + || pass "auch kein 'systemctl start'" + +echo +echo "=== Szenario: ein fehlgeschlagenes enable des Abschlussbilds warnt nur ===" +# Eine Station, deren Panel nach dem Ausschalten das falsche Bild zeigt, ist aergerlich; +# eine, die sich gar nicht einrichten laesst, ist unbrauchbar. +STATE="$TMPROOT/sfin2-state"; STUBBIN="$TMPROOT/binfin2"; LOG="$TMPROOT/sfin2.log" +setup_state "$STATE" +setup_stubbin "$STUBBIN" "$LOG" +: >"$LOG" +set +e +OUT=$(FAKE_ENABLE_FAIL_SUBSTR=ninlight-poweroff run_setup "$STATE" "$STUBBIN" 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0 trotz fehlgeschlagenem enable" || fail "Exit-Code $RC: $OUT" +echo "$OUT" | grep -qi "warnung" && pass "Warnung in der Ausgabe" || fail "keine Warnung: $OUT" + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From c296e2626e925ff077bb35febca116e4008da545 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 03:44:50 +0200 Subject: [PATCH 144/150] fix(station): nmcli-Werte unmaskiert lesen, Doppelpunkt im Profilnamen nmcli -t maskiert Doppelpunkte in Werten als \: und cut -d: -f2 schneidet ohnehin am ersten Doppelpunkt ab. Ein Profilname mit Doppelpunkt lieferte der Nachpruefung in ap_switch() damit einen abgeschnittenen Wert - und der Fehler zeigte in die unsichere Richtung: "Station aus" gemeldet, waehrend der Zugangspunkt weitersendet. Jetzt --escape no plus cut -f2-, auch bei den beiden Profilabfragen und in der Statusausgabe. Der nmcli-Stub in test_cli.sh maskiert jetzt wie das echte nmcli, sonst waere der neue Zusatzfall M1 mit und ohne Fix gruen. --- tools/station/ninlight-station | 19 ++++++--- tools/station/tests/test_cli.sh | 69 +++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/tools/station/ninlight-station b/tools/station/ninlight-station index 490a5ea..1273c02 100755 --- a/tools/station/ninlight-station +++ b/tools/station/ninlight-station @@ -124,7 +124,7 @@ need_profile() { # $1 = NetworkManager-Profilname, der existieren muss. # -F: der Profilname ist ein Konfigurationswert, kein BRE-Muster - ein Name mit # "." oder "[" wuerde mit "grep -x" sonst falsch (oder gar nicht) treffen. - nmcli -t -f NAME con show | grep -Fqx "$1" || { + nmcli -t --escape no -f NAME con show | grep -Fqx "$1" || { echo "Fehler: NetworkManager-Profil '$1' fehlt - tools/station/setup_pi.sh ausfuehren" >&2 exit 1 } @@ -266,7 +266,15 @@ ap_switch() { # zeigt, und erst dann Erfolg melden (Fixrunde 1, I2) - sonst kann z.B. der # Migrations-AP mit oeffentlich bekanntem Passwort weitersenden, waehrend # das CLI "Station aus" behauptet. - _active=$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2) + # --escape no und "-f2-" gehoeren zusammen (Abschlussreview, M1): "nmcli -t" + # maskiert Doppelpunkte IN WERTEN als "\:", und "cut -f2" schneidet ohnehin am + # ersten Doppelpunkt ab. Ein Profilname mit Doppelpunkt ergaebe so einen + # abgeschnittenen Wert, der Vergleich unten schluege fehl - und zwar in die + # UNSICHERE Richtung: "Station aus" gemeldet, waehrend der Zugangspunkt + # weitersendet. --escape no liefert den Rohwert, -f2- nimmt den ganzen Rest der + # Zeile (die Ausgabe hat genau ein Feld, alles nach dem ersten Doppelpunkt + # gehoert zum Wert). + _active=$(nmcli -t --escape no -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2-) if [ "$_active" = "$AP_CONNECTION" ] || [ "$_active" = "$MIGRATE_AP_CONNECTION" ]; then echo "Fehler: wlan0 zeigt nach dem Ausschalten immer noch '$_active' aktiv - NetworkManager pruefen (Zugangspunkt bleibt an)" >&2 return 1 @@ -274,7 +282,8 @@ ap_switch() { return 0 fi sudo nmcli con up "$_target" >/dev/null - _active=$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2) + # --escape no / -f2- aus demselben Grund wie oben. + _active=$(nmcli -t --escape no -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2-) if [ "$_active" != "$_target" ]; then sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true sudo nmcli con down "$MIGRATE_AP_CONNECTION" >/dev/null 2>&1 || true @@ -835,7 +844,7 @@ full_off() { # macht "off" unabhaengig vom Anzeigedienst - ob der installiert, aktiv oder gerade # gescheitert ist, hat auf das Abschalten der Station keinen Einfluss. ap_switch "" - if nmcli -t -f NAME con show | grep -Fqx "$HOME_CONNECTION"; then + if nmcli -t --escape no -f NAME con show | grep -Fqx "$HOME_CONNECTION"; then if sudo nmcli con up "$HOME_CONNECTION" >/dev/null; then echo "Station aus, wlan0 zurueck im Heim-WLAN ($HOME_CONNECTION)" else @@ -1041,7 +1050,7 @@ PY printf 'Erkannt: %s\n' "$_erkannt" printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || true)" autooff_status_line - printf 'wlan0 : %s\n' "$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2)" + printf 'wlan0 : %s\n' "$(nmcli -t --escape no -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2-)" printf 'Adresse: %s\n' "$(ip -4 -br addr show wlan0 2>/dev/null | awk '{print $3}')" echo "Images :" images | while read -r f; do diff --git a/tools/station/tests/test_cli.sh b/tools/station/tests/test_cli.sh index b7c9977..437f14e 100755 --- a/tools/station/tests/test_cli.sh +++ b/tools/station/tests/test_cli.sh @@ -75,8 +75,38 @@ setup_stubbin() { echo "STUB nmcli \$*" >>"$log" NMSTATE="$nmstate" +# "--escape no" abtrennen, BEVOR die Stellungen geprueft werden - sonst verschoebe der +# Schalter alle Positionen. Und der Stub maskiert wie das echte nmcli: OHNE den Schalter +# wird in den Werten von "-t" jeder Doppelpunkt zu "\\:" (Abschlussreview, M1). Nur so +# prueft der Test das Skript und nicht die Attrappe - ein Stub, der nie maskiert, waere +# mit und ohne den Fix gruen. +ESCAPE=yes +_args=() +while [ \$# -gt 0 ]; do + case "\$1" in + --escape) ESCAPE=\$2; shift 2 ;; + *) _args+=("\$1"); shift ;; + esac +done +set -- "\${_args[@]}" + +emit() { + # \$1 = Feldname, \$2 = Rohwert + if [ "\$ESCAPE" = "no" ]; then + echo "\$1:\$2" + else + echo "\$1:\${2//:/\\\\:}" + fi +} + if [ "\$1" = "-t" ] && [ "\$2" = "-f" ] && [ "\$3" = "NAME" ] && [ "\$4" = "con" ] && [ "\$5" = "show" ]; then - cat "\$NMSTATE/profiles" 2>/dev/null + while IFS= read -r _profile; do + if [ "\$ESCAPE" = "no" ]; then + echo "\$_profile" + else + echo "\${_profile//:/\\\\:}" + fi + done <"\$NMSTATE/profiles" 2>/dev/null exit 0 fi @@ -84,11 +114,11 @@ if [ "\$1" = "-t" ] && [ "\$2" = "-f" ] && [ "\$3" = "GENERAL.CONNECTION" ] && [ # Testschalter fuer Fall I3 (Fixrunde 1): erzwingt eine Antwort, die nicht zum # zuletzt hochgefahrenen Profil passt, um ap_switch()'s Fehlerzweig zu pruefen. if [ -n "\${FAKE_NMCLI_DEVSHOW:-}" ]; then - echo "GENERAL.CONNECTION:\${FAKE_NMCLI_DEVSHOW}" + emit GENERAL.CONNECTION "\${FAKE_NMCLI_DEVSHOW}" exit 0 fi last=\$(tail -n1 "\$NMSTATE/active" 2>/dev/null) - echo "GENERAL.CONNECTION:\${last:---}" + emit GENERAL.CONNECTION "\${last:---}" exit 0 fi @@ -621,6 +651,39 @@ echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung auf stderr vorhanden" || ACTIVE_AFTER=$(cat "$SBX/nmstate/active") [ "$ACTIVE_AFTER" = "ninlight-recovery" ] && pass "ninlight-recovery ist (wie vom Stub simuliert) tatsaechlich noch aktiv" || fail "unerwarteter nmcli-Zustand: $ACTIVE_AFTER" +echo +echo "=== Zusatzfall M1 (Abschlussreview): ein Profilname MIT Doppelpunkt wird beim Nachpruefen richtig gelesen ===" +# "nmcli -t" maskiert Doppelpunkte in Werten als "\:", und "cut -d: -f2" schneidet am +# ersten Doppelpunkt ab. Ohne "--escape no" UND "-f2-" liest ap_switch() aus einem +# Profil "ninlight:recovery" nur "ninlight\" bzw. "ninlight" - der Vergleich schluege +# fehl, und zwar in die UNSICHERE Richtung: "Station aus" gemeldet, waehrend der +# Zugangspunkt weitersendet. Aufbau wie Zusatzfall I2, nur mit Doppelpunkt im Namen. +SBX="$TMPROOT/tM1" +setup_sandbox "$SBX" +sed -i.bak "s/^AP_CONNECTION=.*/AP_CONNECTION='ninlight:recovery'/" "$SBX/station.conf" +rm -f "$SBX/station.conf.bak" +printf 'ninlight:recovery\nqconnex-firmware\nhome-wifi\n' >"$SBX/nmstate/profiles" +set +e +OUT=$(run_cli "$SBX" on 2>"$SBX/stderr.log") +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "'on' kommt mit einem Doppelpunkt im Profilnamen zurecht" \ + || fail "'on' scheiterte an einem Doppelpunkt im Profilnamen (rc=$RC): $OUT $(cat "$SBX/stderr.log")" +[ "$(cat "$SBX/nmstate/active")" = "ninlight:recovery" ] \ + && pass "das Profil mit Doppelpunkt ist aktiv" || fail "unerwarteter nmcli-Zustand: $(cat "$SBX/nmstate/active")" +set +e +OUT=$(FAKE_NMCLI_STUCK=1 run_cli "$SBX" off 2>"$SBX/stderr.log") +RC=$? +set -e +ERR=$(cat "$SBX/stderr.log") +[ "$RC" -ne 0 ] \ + && pass "Exit-Code != 0 - der haengende Zugangspunkt wird trotz Doppelpunkt im Namen erkannt" \ + || fail "Exit-Code war 0, obwohl 'ninlight:recovery' weiterhin aktiv war: $OUT" +echo "$OUT" | grep -q "Station aus" && fail "CLI behauptete faelschlich 'Station aus'" \ + || pass "keine falsche 'Station aus'-Meldung" +echo "$ERR" | grep -q "ninlight:recovery" \ + && pass "die Fehlermeldung nennt den vollstaendigen Profilnamen, nicht den abgeschnittenen" \ + || fail "Fehlermeldung nennt den Profilnamen nicht vollstaendig: $ERR" echo echo "=== Zusatzfall I3: ap_switch bricht ab und faehrt beide Profile herunter, wenn 'device show wlan0' nach dem Umschalten nicht passt ===" SBX="$TMPROOT/tI3" From 95ae51bc1db7e95492aa72f1d60aadab4345532c Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 03:45:16 +0200 Subject: [PATCH 145/150] docs: Abnahmeprotokoll nennt den off/Anzeigedienst-Befund als behoben Der Absatz stand noch auf "Reparatur laeuft", obwohl 0fe9ca6 den Befund behoben hat. Das Protokoll haengt am naechsten Geraetetermin - dort darf nichts Falsches stehen. Der Befund bleibt sichtbar, ergaenzt um die Loesung und die beiden Testfaelle, die sie festhalten. --- docs/hardware-checklist.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/hardware-checklist.md b/docs/hardware-checklist.md index 34d4afb..d905123 100644 --- a/docs/hardware-checklist.md +++ b/docs/hardware-checklist.md @@ -121,9 +121,13 @@ armv6), Waveshare 2,13" E-Paper **Revision V3** als HAT, ein Taster auf GPIO23 ( Varianten lag kein Exemplar vor. Ihre Pinbelegung stammt aus der alten Firmware und war nie an echter Hardware verifiziert. -**Befund während der Abnahme, noch offen:** `ninlight-station off` stoppt den Anzeigedienst, -aber kein `on`-Pfad startet ihn wieder — nach dem ersten `off` (das auch der Zeitschalter -auslöst) bleibt das Panel bis zum Neustart des Pi dunkel. Reparatur läuft. +**Befund während der Abnahme, behoben in `0fe9ca6`:** `ninlight-station off` stoppte den +Anzeigedienst, aber kein `on`-Pfad startete ihn wieder — nach dem ersten `off` (das auch der +Zeitschalter auslöst) blieb das Panel bis zum Neustart des Pi dunkel. `off` fasst den +Anzeigedienst seitdem gar nicht mehr an: er läuft durchgehend und erkennt den ausgeschalteten +Zustand selbst (`systemctl is-active ninlight-station`), zeichnet also von sich aus +**Station aus**. Der ausdrückliche Diagnosebefehl `ninlight-station display off` bleibt erhalten. +Abgesichert durch die Fälle D4 und D4b in `tools/station/tests/test_cli.sh`. **Hardware-Lehre:** Vier Kernel-Paniken an einem Tag, alle mit derselben Fehleradresse und unabhängig von Karte und Konfiguration, kamen von einem Mehrfach-Netzteil, das sein Budget From bbe2e842b64cf8087007a89a057e4dda4406de62 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 03:46:37 +0200 Subject: [PATCH 146/150] test(station): Ausfallpfad der Anzeige in allen vier Modi festhalten Die Zusage "fehlt Panel, SPI, Pillow oder Schrift: genau eine Protokollzeile und sys.exit(0)" war ungetestet - das Abschlussreview hat sys.exit(0) durch sys.exit(1) ersetzt und alle 225 Tests blieben gruen. Die vorhandenen Tests pruefen nur, dass die richtigen Ausnahmetypen bis main() durchkommen, niemand hat main() selbst in den except-Zweig getrieben. Acht neue Faelle: DisplayUnavailable und ImportError, je fuer --test, --clear, --final und den Daemon. Mutationsproben: exit 0 -> 1 macht alle acht rot, die Ausnahmeliste auf DisplayUnavailable verengt macht die vier ImportError-Faelle rot. --- tools/station/tests/test_render.py | 76 ++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 3146573..2b43f0e 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -1988,3 +1988,79 @@ def test_display_main_passes_the_final_kind_through(monkeypatch): assert len(drawn) == 1 expected = render.compose({"mode": "final", "final": kind}) assert list(drawn[0].getdata()) == list(expected.getdata()), kind + + +# --- Die Entbehrlichkeitszusage der Anzeige (Abschlussreview, I1) -------------------- +# +# Zusage aus dem Modul-Docstring, ueber fuenf Fixrunden erkaempft: fehlt Panel, SPI, +# Pillow oder Schrift, gibt es GENAU EINE Protokollzeile und sys.exit(0). Der Grund ist +# Restart=on-failure mit RestartSec=5 in ninlight-display.service - ein Exit != 0 macht +# daraus auf dem Zero W eine Neustartschleife, die das Journal flutet, und der Sinn der +# Zusage ("ein Fehler in der Anzeige darf niemals eine Migration stoeren") faellt still +# weg. +# +# Das Abschlussreview hat belegt, dass diese Zusage ungetestet war: sys.exit(0) -> +# sys.exit(1) liess alle 225 Tests gruen. Die vorhandenen Tests pruefen nur, dass die +# richtigen Ausnahmetypen bis main() DURCHKOMMEN - niemand hat main() selbst in den +# except-Zweig getrieben und auf den Rueckgabewert gesehen. Beim Tasterdienst ist +# dasselbe laengst getestet (tests/test_button.py, test_missing_gpiozero_...). +# +# Alle vier Modi, weil jeder sein eigenes run_*() hat: --test, --clear, --final und der +# Daemon. Alle vier bauen ihr Panel selbst, ein Panel(), das schon im Konstruktor wirft, +# deckt daher jeden der vier Einstiege ab. + +_DISPLAY_MODES = [ + pytest.param(["display.py", "--test"], id="test"), + pytest.param(["display.py", "--clear"], id="clear"), + pytest.param(["display.py", "--final", "poweroff"], id="final"), + pytest.param(["display.py"], id="daemon"), +] + + +def _panel_raising(exc): + class _Panel: + def __init__(self, revision): + raise exc + return _Panel + + +@pytest.mark.parametrize("argv", _DISPLAY_MODES) +def test_missing_panel_logs_exactly_one_line_and_exits_zero(argv, monkeypatch, caplog): + """Kein Panel/SPI -> DisplayUnavailable -> eine Zeile, Exit 0 - in JEDEM Modus.""" + monkeypatch.setattr(display, "Panel", + _panel_raising(epdconfig.DisplayUnavailable("kein SPI-Geraet"))) + monkeypatch.setattr(sys, "argv", argv) + monkeypatch.setenv("EPD_REVISION", "V3") + + with caplog.at_level("INFO", logger="ninlight-display"): + with pytest.raises(SystemExit) as excinfo: + display.main() + + assert excinfo.value.code == 0, ( + f"{argv[1:] or ['daemon']}: Exit {excinfo.value.code} statt 0 - " + "Restart=on-failure macht daraus eine Neustartschleife") + assert len(caplog.records) == 1, \ + f"erwartet genau eine Protokollzeile, bekommen: {[r.getMessage() for r in caplog.records]}" + assert "kein SPI-Geraet" in caplog.records[0].getMessage() + + +@pytest.mark.parametrize("argv", _DISPLAY_MODES) +def test_missing_pillow_logs_exactly_one_line_and_exits_zero(argv, monkeypatch, caplog): + """Der zweite Ast derselben Zusage (Task 6a, C4): Pillow kommt auf dem Ziel-Pi erst + per "apt install python3-pil" nach, render.compose() importiert es erst beim + Zeichnen. Ein ModuleNotFoundError (ImportError-Unterklasse) muss deshalb genauso + enden wie ein fehlendes Panel - und nicht als Absturz.""" + monkeypatch.setattr(display, "Panel", + _panel_raising(ModuleNotFoundError("No module named 'PIL'"))) + monkeypatch.setattr(sys, "argv", argv) + monkeypatch.setenv("EPD_REVISION", "V3") + + with caplog.at_level("INFO", logger="ninlight-display"): + with pytest.raises(SystemExit) as excinfo: + display.main() + + assert excinfo.value.code == 0, ( + f"{argv[1:] or ['daemon']}: Exit {excinfo.value.code} statt 0") + assert len(caplog.records) == 1, \ + f"erwartet genau eine Protokollzeile, bekommen: {[r.getMessage() for r in caplog.records]}" + assert "PIL" in caplog.records[0].getMessage() From fa93259426a5b3d8223bfd69fe4ffb880c12c95b Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 03:48:25 +0200 Subject: [PATCH 147/150] test(station): Freigabe bleibt bei fehlendem Image und Abbruch stehen Von den drei restore_armed()-Pfaden in _do_get_migration() war nur der juengste (fehlender Bildsatz) getestet. Das Abschlussreview hat die beiden aelteren mit je einem "if False:" ausgehebelt - beide Male blieben alle 225 Tests gruen, obwohl eine Freigabe dann verfaellt, ohne dass ein Byte geflossen ist. Zwei Tests: Image fehlt im vorhandenen Bildsatz (Status notfound) und Transfer abgebrochen (--stall-after, Verbindung geschlossen, Status aborted). Beide pruefen phase UND armed_target. Beide Mutationen anschliessend gegengeprueft - je genau der zugehoerige Test wird rot. MigrationTestServer nimmt jetzt zusaetzliche Serverargumente entgegen, damit --stall-after auch auf der Migrationsroute nutzbar ist. --- tools/station/tests/test_server.py | 74 +++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py index a461fea..73cb481 100644 --- a/tools/station/tests/test_server.py +++ b/tools/station/tests/test_server.py @@ -483,7 +483,7 @@ class MigrationTestServer: kann. Jeder Aufrufer kennt sein eigenes Praedikat (das ohnehin schon als Assertion dasteht), daher kein pauschales Warten hier.""" - def __init__(self, tmp_path, mode): + def __init__(self, tmp_path, mode, *extra_args): self.image_dir = tmp_path / "images" self.image_dir.mkdir() self.state_path = tmp_path / "status.json" @@ -503,7 +503,7 @@ class MigrationTestServer: "--bind", "127.0.0.1", "--port", str(self.port), "--state", str(self.state_path), "--mode", mode, "--control", str(self.control_path), - "--imageset-file", str(self.imageset_path)], + "--imageset-file", str(self.imageset_path), *extra_args], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, ) deadline = time.time() + 10 @@ -677,6 +677,76 @@ def test_armed_for_other_target_does_not_serve(migration_server): assert srv.read_control()["phase"] == "armed" # Freigabe bleibt stehen +# --- Eine Freigabe wird NUR durch eine erfolgreiche Auslieferung verbraucht ---------- +# +# Die Zusage (Spec 3.3): claim_delivery() nimmt die Freigabe schon unter der Sperre +# weg, BEVOR ueberhaupt gesendet wird (das ist der Punkt von C1 aus Fixrunde 1). Geht +# danach nichts raus, muss restore_armed() sie zurueckgeben - sonst steht der Bediener +# vor einer Lampe, die beim naechsten Boot nichts mehr bekommt, und muss raten, warum. +# +# Es gibt drei solche Stellen in _do_get_migration(): fehlender Bildsatz, fehlendes +# Einzelimage und abgebrochener Transfer. Nur die erste (die juengste) war getestet - +# das Abschlussreview hat die beiden anderen mit je einem "if False:" ausgehebelt, ohne +# dass ein einziger der 225 Tests rot wurde. Die beiden folgenden Tests schliessen das. + + +def test_a_missing_image_does_not_burn_the_release(migration_server): + """Der Bildsatz ist da, nur DIESES Modell fehlt darin (Status "notfound"). Die + Freigabe darf daran nicht verfallen: sie war fuer genau dieses Ziel gedacht, und ein + fehlendes Image ist ein Deploy-Versaeumnis, kein Auslieferungsvorgang.""" + srv, imgdir = migration_server + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + # Der Satz existiert (ein anderes Modell liegt darin), e14_cw fehlt darin. + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"IMG") + srv.set_control(phase="armed", armed_target="e14_cw") + + r = srv.get("/qcx_firmware_-klyqa.lighting.cw-ww.e14_V1_ota_light.img") + + assert r.status == 404 + st = srv.status(lambda s: len(s["recent"]) >= 1) + assert st["recent"][-1]["status"] == "notfound" + assert st["served"] == 0 + control = srv.read_control() + assert control["phase"] == "armed", \ + "die Freigabe wurde verbraucht, obwohl es gar kein Image auszuliefern gab" + assert control["armed_target"] == "e14_cw", "das freigegebene Ziel ging verloren" + + +def test_an_aborted_transfer_does_not_burn_the_release(tmp_path): + """Bricht die Uebertragung ab (Lampe verliert Strom oder Funk mitten im Download), + muss die Freigabe stehen bleiben, damit die Lampe es beim naechsten Boot erneut + versuchen kann - sonst muss jemand vor Ort erneut freigeben, und genau diese + Nachsicht ist der Grund, warum restore_armed() ueberhaupt existiert. + + Nachgestellt wie test_aborted_transfer_is_recorded oben: --stall-after laesst den + Server nach n Byte verstummen, der Test schliesst die Verbindung danach, und + _serve_file() meldet "aborted".""" + srv = MigrationTestServer(tmp_path, "migration", "--stall-after", "256") + try: + imgdir = srv.image_dir + (imgdir / "migrate-nocreds").mkdir(exist_ok=True) + (imgdir / "migrate-nocreds" / "g95_cw_dev.img").write_bytes(b"y" * 100000) + srv.set_control(phase="armed", armed_target="g95_cw") + + sock = socket.create_connection(("127.0.0.1", srv.port), timeout=5) + try: + sock.sendall(("GET " + G95_DEV + " HTTP/1.1\r\nHost: x\r\n\r\n").encode()) + sock.recv(4096) + time.sleep(0.3) + finally: + sock.close() + + st = srv.status(lambda s: s["recent"] and s["recent"][-1]["status"] == "aborted") + assert st["recent"][-1]["bytes"] < 100000 + assert st["served"] == 0 + control = srv.read_control(lambda c: True) + assert control["phase"] == "armed", \ + "die Freigabe wurde von einem ABGEBROCHENEN Transfer verbraucht" + assert control["armed_target"] == "g95_cw", "das freigegebene Ziel ging verloren" + finally: + srv.stop() + + def test_unknown_product_id_is_404_and_logged(migration_server): srv, _ = migration_server r = srv.get("/qcx_firmware_-acme.bulb_V1_ota_light.img") From c482194503ca480d77b327158ddc57d4832dfdfa Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 03:57:08 +0200 Subject: [PATCH 148/150] fix(station): deploy.sh prueft Bildsatz 'ohne-wlan' auf Zugangsdaten release.sh prueft sein fertiges Archiv binaer gegen die Werte aus config.local.cmake und bricht ab, wenn es etwas findet. deploy.sh signierte dagegen ungeprueft, was in build-nocreds/ lag - und genau dieser Satz ist die sichere Vorgabe, die im Automatikmodus an fremde Lampen in Funkreichweite geht. Der Weg dorthin ist Bedienfehler, nicht Programmfehler: ein von Hand konfiguriertes build-nocreds/, ein gesetztes NINLIGHT_NOCREDS_BUILD_REL=build. Das Ergebnis waere, dass das Panel "ohne WLAN" anzeigt, waehrend das WLAN-Passwort des Bedieners unwiderruflich in fremde Lampen wandert. Derselbe Pruefkoerper wie in release.sh, angewandt auf $STAGE/migrate-nocreds und VOR dem Uebertragen - bei einem Treffer geht kein Byte an den Pi. Szenario 33 stellt den undichten Abzug nach (mit Gegenprobe, dass der Abzug die Zugangsdaten wirklich traegt), Szenario 34 grenzt ab, dass migrate-wlan sie weiterhin tragen darf. Mutationsproben: Pruefblock entfernt = 6 Zusagen rot (die Zugangsdaten landen beim Ziel); Pruefung auf den ganzen STAGE ausgeweitet = 4 Zusagen rot. --- tools/station/deploy.sh | 58 +++++++++++ tools/station/tests/test_deploy.sh | 148 +++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) diff --git a/tools/station/deploy.sh b/tools/station/deploy.sh index ee64044..f7ca8cd 100755 --- a/tools/station/deploy.sh +++ b/tools/station/deploy.sh @@ -241,6 +241,64 @@ else echo "Hinweis: Schluesselverzeichnis $SIGN_DIR nicht gefunden - keine Migrationsimages erzeugt" >&2 fi +# Der Bildsatz "ohne-wlan" ist die SICHERE VORGABE: er geht im Automatikmodus an jede +# alte Klyqa-Lampe in Funkreichweite, auch an fremde, und das Flashen ist nicht +# umkehrbar. tools/release.sh prueft sein fertiges Archiv deshalb binaer gegen die Werte +# aus config.local.cmake (dort "Auf Zugangsdaten pruefen") - deploy.sh signierte +# dagegen ungeprueft, was in build-nocreds/ lag. +# +# Der Weg dorthin ist Bedienfehler, nicht Programmfehler: ein von Hand konfiguriertes +# build-nocreds/, oder ein gesetztes NINLIGHT_NOCREDS_BUILD_REL=build. Das +# Ergebnis waere, dass das Panel "ohne WLAN" anzeigt, waehrend das WLAN-Passwort des +# Bedieners unwiderruflich in fremde Lampen wandert - der teure, nicht rueckholbare +# Fehler. Lieber ein abgebrochener Deploy. +# +# Geprueft wird VOR dem Uebertragen, es ist also noch nichts beim Pi angekommen. Nur +# dieser eine Satz: migrate-wlan/ SOLL die Zugangsdaten enthalten, das ist sein Zweck. +# Der Pruefkoerper ist bis auf die Meldung derselbe wie in tools/release.sh. +if [ -d "$STAGE/migrate-nocreds" ]; then + echo "== Bildsatz 'ohne-wlan' auf Zugangsdaten pruefen ==" + LOCAL="$ROOT/config.local.cmake" + if [ -f "$LOCAL" ]; then + python3 - "$STAGE/migrate-nocreds" "$LOCAL" <<'CHECKPY' || exit 1 +import os +import re +import sys + +stage, local = sys.argv[1], sys.argv[2] +text = open(local, encoding="utf-8", errors="replace").read() +# Nur die drei Werte, die wirklich geheim sind. NINLIGHT_AP_PASSWORD und der +# Vorgabetoken stehen oeffentlich im Repo (README, Bruno) - sie hier mitzupruefen +# haette nur falsche Alarme erzeugt. +SECRETS = ("NINLIGHT_WIFI_SSID", "NINLIGHT_WIFI_PASSWORD", "NINLIGHT_TOKEN") +PUBLIC = {"", "ninlight", "aabbccddeeff0011223344", "ninlight-recovery", "change-me-please"} +needles = [] +for name in SECRETS: + m = re.search(r'^\s*set\s*\(\s*%s\s+"([^"]*)"' % name, text, re.M) + if m and m.group(1) not in PUBLIC: + needles.append((name, m.group(1).encode())) + +hits = [] +for root, _dirs, files in os.walk(stage): + for fn in sorted(files): + path = os.path.join(root, fn) + with open(path, "rb") as fh: + data = fh.read() + for name, needle in needles: + if needle in data: + hits.append("%s in %s" % (name, os.path.relpath(path, stage))) +if hits: + sys.stderr.write( + "Fehler: Zugangsdaten aus config.local.cmake im Bildsatz 'ohne-wlan' gefunden, " + "nichts uebertragen:\n " + "\n ".join(hits) + "\n") + sys.exit(1) +print(" sauber (%d geprueft)" % len(needles)) +CHECKPY + else + echo " uebersprungen - $LOCAL nicht vorhanden" >&2 + fi +fi + echo "== uebertragen ==" # -c (Inhaltsvergleich per Checksumme statt Groesse+Zeitstempel): ein neues # Image kann zufaellig dieselbe Byte-Groesse wie das alte haben, und bei diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh index c5fc25d..3b65822 100755 --- a/tools/station/tests/test_deploy.sh +++ b/tools/station/tests/test_deploy.sh @@ -247,6 +247,61 @@ with open(path, 'wb') as fh: " "$path" } +# Wegwerf-Zugangsdaten fuer die Pruefung des Bildsatzes "ohne-wlan" - niemals die +# echten aus dem Repo. Dieselbe Rolle wie SECRET_* in tools/tests/test_release.sh. +SECRET_SSID="TestWLAN-9c2e-geheim" +SECRET_PASS="testpasswort-9c2e" +SECRET_TOKEN="0badc0de9c2e0badc0de9c2e" + +# Legt config.local.cmake im Sandbox-Repo an - genau die Datei, gegen die deploy.sh den +# Bildsatz "ohne-wlan" prueft. +write_local_cmake() { + local sandbox=$1 + cat >"$sandbox/config.local.cmake" < nichts uebertragen ===" SANDBOX="$TMPROOT/s1"; STUBBIN="$TMPROOT/bin1"; LOG="$TMPROOT/s1.log"; SANDBOX_STATE="$TMPROOT/s1-state" setup_sandbox "$SANDBOX" @@ -1271,6 +1326,99 @@ grep -Fq "systemctl enable ninlight-poweroff" "$LOG" \ [ ! -e /tmp/ninlight-poweroff.service ] && [ ! -e /tmp/ninlight-reboot.service ] \ && pass "die Ablage wurde aufgeraeumt" || fail "Unit-Dateien blieben unter /tmp liegen" +echo +echo "=== Szenario 33 (Abschlussreview, I3): Zugangsdaten im Bildsatz 'ohne-wlan' brechen den Deploy ab ===" +# Der Satz "ohne-wlan" ist die SICHERE VORGABE - im Automatikmodus geht er an jede alte +# Klyqa-Lampe in Funkreichweite, auch an fremde, und das Flashen ist nicht umkehrbar. +# release.sh prueft sein Archiv binaer gegen config.local.cmake; deploy.sh signierte +# bisher ungeprueft, was in build-nocreds/ lag. Ein von Hand konfiguriertes +# build-nocreds/ genuegt, damit das Panel "ohne WLAN" anzeigt, waehrend das +# WLAN-Passwort des Bedieners unwiderruflich in fremde Lampen wandert. +SANDBOX="$TMPROOT/s33"; STUBBIN="$TMPROOT/bin33"; LOG="$TMPROOT/s33.log"; SANDBOX_STATE="$TMPROOT/s33-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +write_local_cmake "$SANDBOX" +mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo" +make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" W +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +# Der undichte Abzug: enthaelt SSID, Passwort und Token. +make_gz_file_with_creds "$SANDBOX/build-nocreds/foo/ninlight_foo_gz.img" N +# GEGENPROBE (ohne sie waere dieses Szenario wertlos): der "Compiler" dieser Suite muss +# die Zugangsdaten wirklich einbauen. Genau diese Gegenprobe gibt es in test_release.sh +# fuer den Docker-Stub - dort hat sie belegt, dass Fall 5 nicht nur deshalb gruen ist, +# weil nie jemand etwas einbaut. +if tree_contains "$SANDBOX/build-nocreds" "$SECRET_PASS"; then + pass "Gegenprobe: der Abzug in build-nocreds/ enthaelt das WLAN-Passwort wirklich" +else + fail "Gegenprobe fehlgeschlagen - der Abzug enthaelt gar keine Zugangsdaten, Szenario 33 waere wertlos" +fi +# Der Pi hat schon einen (sauberen) Bestand aus einem frueheren Deploy - er darf durch +# den Abbruch weder ersetzt noch geloescht werden. +mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds" +echo "alter-sauberer-abzug" >"$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img" +: >"$LOG" +set +e +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +[ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0 - der undichte Bildsatz waere ausgeliefert worden: $OUT" +echo "$OUT" | grep -q "Zugangsdaten" \ + && pass "Fehlermeldung nennt die gefundenen Zugangsdaten" \ + || fail "keine Fehlermeldung zu Zugangsdaten: $OUT" +echo "$OUT" | grep -q "NINLIGHT_WIFI_PASSWORD" \ + && pass "Fehlermeldung nennt den betroffenen Schluessel" \ + || fail "Fehlermeldung nennt den Schluessel nicht: $OUT" +if ! grep -q "STUB ssh\|STUB rsync\|STUB scp" "$LOG"; then + pass "kein ssh/rsync/scp aufgerufen - nichts wurde uebertragen" +else + fail "es wurde trotz gefundener Zugangsdaten uebertragen: $(cat "$LOG")" +fi +[ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img")" = "alter-sauberer-abzug" ] \ + && pass "der Bestand auf dem Pi blieb unangetastet" \ + || fail "der Bestand auf dem Pi wurde veraendert" +if ! tree_contains "$SANDBOX_STATE" "$SECRET_PASS"; then + pass "kein Zugangsdatum hat es in die Sandbox-Zielablage geschafft" +else + fail "Zugangsdaten sind trotz Abbruch beim Ziel gelandet" +fi + +echo +echo "=== Szenario 34 (Abgrenzung zu 33): ein sauberer Satz 'ohne-wlan' geht raus, migrate-wlan DARF die Zugangsdaten tragen ===" +# Die Pruefung muss genau einen Satz betreffen. migrate-wlan/ enthaelt die Zugangsdaten +# absichtlich - das ist sein ganzer Zweck ("Lampe haengt sofort im eigenen WLAN"). Eine +# Pruefung, die auch ihn abweist, waere unbenutzbar und wuerde nach kurzer Zeit +# abgeschaltet. +SANDBOX="$TMPROOT/s34"; STUBBIN="$TMPROOT/bin34"; LOG="$TMPROOT/s34.log"; SANDBOX_STATE="$TMPROOT/s34-state" +setup_sandbox "$SANDBOX" +setup_stubbin "$STUBBIN" "$LOG" +write_local_cmake "$SANDBOX" +mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo" +# build/ MIT Zugangsdaten (wie im echten Betrieb), build-nocreds/ ohne. +make_gz_file_with_creds "$SANDBOX/build/foo/ninlight_foo_gz.img" W +echo "foo-ota" >"$SANDBOX/build/foo/ninlight_foo_ota.img" +make_gz_file "$SANDBOX/build-nocreds/foo/ninlight_foo_gz.img" N +: >"$LOG" +set +e +OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC - die Pruefung weist einen sauberen Satz ab: $OUT" +[ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds/foo_dev.img" ] \ + && pass "der saubere Satz 'ohne-wlan' kam an" || fail "der saubere Satz 'ohne-wlan' fehlt: $OUT" +[ -f "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan/foo_dev.img" ] \ + && pass "migrate-wlan kam trotz einkompilierter Zugangsdaten an" \ + || fail "migrate-wlan wurde faelschlich mit abgewiesen: $OUT" +if tree_contains "$SANDBOX_STATE/opt/ninlight-station/images/migrate-wlan" "$SECRET_PASS"; then + pass "Gegenprobe: migrate-wlan traegt die Zugangsdaten wirklich - die Pruefung haette anschlagen KOENNEN" +else + fail "Gegenprobe fehlgeschlagen - migrate-wlan traegt gar keine Zugangsdaten, die Abgrenzung waere wertlos" +fi +if ! tree_contains "$SANDBOX_STATE/opt/ninlight-station/images/migrate-nocreds" "$SECRET_PASS"; then + pass "und der Satz 'ohne-wlan' auf dem Pi ist frei davon" +else + fail "der Satz 'ohne-wlan' auf dem Pi enthaelt Zugangsdaten" +fi + echo if [ "$FAILED" -eq 0 ]; then echo "Alle Tests bestanden." From f4dd1386efdc614f48811ee67ec3ebefd69942a2 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 10:37:30 +0200 Subject: [PATCH 149/150] fix(display): Fusszeile des Bildsatzes passt aufs Panel, auch die Fehlt-Warnung Am Geraet kam die Zeile gekuerzt an ('... (ohne WLA...'), und mit dem Zusatz ' - SATZ FEHLT' war die Warnung in beiden Richtungen ausserhalb der Panelbreite - also genau die Zeile, die sagt, dass die Station nichts ausliefern kann. Unbemerkt blieb das, weil _load_font() auf dem Pi DejaVu nimmt und auf dem Mac bis Arial durchfaellt: dieselbe Zeile misst dort 246,0 von 246 px, auf dem Pi 283,5 px. Der neue Test misst deshalb mit 25 % Reserve gegen eine breitere Schrift. Die Fehlt-Warnung ersetzt die Zeile jetzt, statt sie zu verlaengern. --- tools/station/display/render.py | 21 +++++++++++--- tools/station/tests/test_render.py | 46 ++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/tools/station/display/render.py b/tools/station/display/render.py index 7323add..dc7ecd4 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -194,11 +194,24 @@ ARMED_DETAIL_PREFIX = "Freigabe: " # "Lampe: sofort ins WLAN" sagt ihm genau das, was er wissen muss, bevor er einschaltet. # Der gefaehrliche Zustand steht in Grossbuchstaben - er ist der, den man uebersehen soll # und darf. +# Die Zeile muss UNGEKUERZT passen, und zwar auch mit einer breiteren Schrift als der +# hier gerade installierten: am Geraet (DejaVu) kam die alte, laengere Fassung als +# "Lampe -> Onboarding-AP (ohne WLA..." an - der Zusatz in der Klammer, also gerade die +# Aussage, fiel weg. Auf dem Mac des Entwicklers (Arial, schmaler) passte dieselbe Zeile +# mit 246,0 von 246 px haarscharf; deshalb hat es niemand bemerkt. Breitenbudget siehe +# test_imageset_footer_fits_the_panel_with_room_for_a_wider_font. IMAGESET_LABELS = { - "ohne-wlan": "Lampe -> Onboarding-AP (ohne WLAN-Daten)", - "mit-wlan": "Lampe -> SOFORT INS EIGENE WLAN", + "ohne-wlan": "ohne WLAN-Daten (Onboarding-AP)", + "mit-wlan": "MIT WLAN-Daten (eigenes Netz)", +} +# Fehlt das Verzeichnis des gewaehlten Satzes, ERSETZT die Warnung die Zeile, statt sie +# zu verlaengern. Als angehaengtes Suffix (" - SATZ FEHLT", bis 2026-09-15) war sie in +# BEIDEN Richtungen und in beiden Schriften ausserhalb der Panelbreite - die einzige +# Zeile, die sagt, dass die Station gar nichts ausliefern kann, war damit nie zu sehen. +IMAGESET_MISSING_LABELS = { + "ohne-wlan": "SATZ FEHLT: ohne WLAN-Daten", + "mit-wlan": "SATZ FEHLT: MIT WLAN-Daten", } -IMAGESET_MISSING_SUFFIX = " - SATZ FEHLT" # Der Abschlussbildschirm: was auf dem Panel stehen bleibt, wenn der Pi heruntergefaehrt # oder neu gestartet wird. E-Paper ist bistabil - das zuletzt gezeichnete Bild bleibt fuer @@ -420,7 +433,7 @@ def _imageset_blocks(state: dict) -> list[tuple[str, str]]: if not label: return [] if state.get("imageset_ok") is False: - label += IMAGESET_MISSING_SUFFIX + label = IMAGESET_MISSING_LABELS[name] return [(label, ROLE_FOOTER)] diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 2b43f0e..4a98913 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -2064,3 +2064,49 @@ def test_missing_pillow_logs_exactly_one_line_and_exits_zero(argv, monkeypatch, assert len(caplog.records) == 1, \ f"erwartet genau eine Protokollzeile, bekommen: {[r.getMessage() for r in caplog.records]}" assert "PIL" in caplog.records[0].getMessage() + + +# --- Fusszeile des Bildsatz-Umschalters (Geraetetermin 2026-09-15) ------------------ +# +# Am Geraet kam die Zeile gekuerzt an: "Lampe -> Onboarding-AP (ohne WLA...". Ausgerechnet +# die Klammer, die den Zustand benennt, fiel weg - und mit dem Zusatz " - SATZ FEHLT" war +# die WARNUNG, dass der gewaehlte Satz gar nicht auf dem Pi liegt, in beiden Richtungen +# unsichtbar (367,5 px bzw. 306 px bei 246 px Platz). +# +# Warum die Testsuite das nie bemerkt hat: _load_font() nimmt den ersten Pfad, der sich +# oeffnen laesst. Auf dem Pi ist das DejaVu Sans Bold, auf dem Mac des Entwicklers faellt +# die Kette bis "Arial Bold.ttf" durch - und Arial baut schmaler. Dieselbe Zeile misst +# dort 246,0 von 246 px (haarscharf drin), auf dem Pi 283,5 px. Das Layout war damit auf +# die Schrift der ENTWICKLUNGSMASCHINE ausgelegt, nicht auf die des Zielgeraets. +# +# Dieser Test misst deshalb mit einer ausdruecklichen Marge: die Zusage lautet nicht "es +# passt mit der hier zufaellig installierten Schrift", sondern "es passt auch mit einer +# spuerbar breiteren". 25 % decken den gemessenen Abstand Arial -> DejaVu (+15 %) mit +# Reserve ab. +FOOTER_WIDTH_SAFETY = 1.25 + + +def _footer_width_px(text: str) -> float: + from PIL import Image, ImageDraw, ImageFont + draw = ImageDraw.Draw(Image.new("1", (render.PANEL_WIDTH_PX, + render.PANEL_HEIGHT_PX), 255)) + font = render._load_font(ImageFont, render.FOOTER_FONT_PX, bold=True) + return draw.textlength(text, font=font) + + +@pytest.mark.parametrize("name", sorted(render.IMAGESET_LABELS)) +@pytest.mark.parametrize("missing", [False, True]) +def test_imageset_footer_fits_the_panel_with_room_for_a_wider_font(name, missing): + """Jede Fusszeile des Umschalters - beide Saetze, mit und ohne Fehlt-Warnung - muss + ungekuerzt auf das Panel passen, auch wenn die Schrift des Zielgeraets breiter baut + als die hier installierte. Der Fehlt-Fall ist der wichtigere der vier: er sagt, dass + der GEWAEHLTE Bildsatz nicht auf dem Pi liegt, und genau der war unsichtbar.""" + text, = (t for t, role in render._imageset_blocks( + {"imageset": name, "imageset_ok": not missing}) if role == render.ROLE_FOOTER) + max_width_px = render.PANEL_WIDTH_PX - 2 * render.TEXT_MARGIN_PX + width_px = _footer_width_px(text) + + assert width_px * FOOTER_WIDTH_SAFETY <= max_width_px, ( + "Fusszeile {!r} misst {:.1f} px; mit {:.0f} %% Reserve fuer eine breitere " + "Schrift bleiben nur {:.1f} px Platz".format( + text, width_px, (FOOTER_WIDTH_SAFETY - 1) * 100, max_width_px)) From 71a579c0f902119d0a9ef63ffe01df5cee8d98d9 Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Tue, 15 Sep 2026 10:41:42 +0200 Subject: [PATCH 150/150] fix(display): Abschlussbild sagt 'Strom trennen erlaubt' - der alte Satz war abgeschnitten Zweiter Fund derselben Art wie die Bildsatz-Fusszeile: 'Strom kann getrennt werden' misst in der Hero-Schrift 262,1 px bei 246 px Platz und kam am Geraet als 'Strom kann getrennt wer...' an. Der Test deckt jetzt ALLE festen Beschriftungen ab (Abschlussbilder, beide Bildsaetze, beide Fehlt-Warnungen, Freigabe), nicht nur die zuerst entdeckte Stelle - sonst waere genau dieser zweite Fall wieder durchgerutscht. Der Wortlauttest prueft die Zusage ('trennen erlaubt' gegen 'NICHT trennen') statt des genauen Satzes. --- tools/station/display/render.py | 6 ++- tools/station/tests/test_render.py | 64 ++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/tools/station/display/render.py b/tools/station/display/render.py index dc7ecd4..6c1bdac 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -224,7 +224,11 @@ IMAGESET_MISSING_LABELS = { # der Anzeigedienst wieder zeichnet, und an genau diesem Pi hat ein Stromzug in dieser # Wartezeit schon eine SD-Karte gekostet. FINAL_LABELS = { - "poweroff": ("AUSGESCHALTET", "Strom kann getrennt werden", []), + # "Strom kann getrennt werden" war in der Hero-Schrift 262,1 px breit und kam am + # Geraet als "Strom kann getrennt wer..." an - siehe die Fusszeile weiter oben, es + # ist derselbe Fehler. Alle festen Beschriftungen haengen jetzt an + # test_fixed_labels_fit_the_panel_with_room_for_a_wider_font. + "poweroff": ("AUSGESCHALTET", "Strom trennen erlaubt", []), "reboot": ("NEUSTART", "Strom NICHT trennen!", ["Der Pi kommt in rund 2 min wieder.", "Solange bleibt dieses Bild stehen."]), diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 4a98913..2fad452 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -1885,11 +1885,13 @@ def test_the_shutdown_and_the_reboot_screen_differ(): off, reboot = _final_text("poweroff"), _final_text("reboot") assert off != reboot assert "AUSGESCHALTET" in off and "NEUSTART" in reboot - # Ausschalten: erlaubt das Trennen ausdruecklich. - assert "kann getrennt werden" in off + # Ausschalten: erlaubt das Trennen ausdruecklich. Geprueft wird die ZUSAGE, nicht + # der genaue Satz - der Wortlaut musste am 2026-09-15 kuerzer werden, weil die alte + # Fassung ("Strom kann getrennt werden", 262,1 px) auf dem Panel abgeschnitten war. + assert "trennen erlaubt" in off # Neustart: verbietet es ebenso ausdruecklich - und sagt nirgends das Gegenteil. assert "NICHT trennen" in reboot - assert "kann getrennt werden" not in reboot + assert "trennen erlaubt" not in reboot def test_the_reboot_screen_says_how_long_it_stays(): @@ -2094,19 +2096,49 @@ def _footer_width_px(text: str) -> float: return draw.textlength(text, font=font) -@pytest.mark.parametrize("name", sorted(render.IMAGESET_LABELS)) -@pytest.mark.parametrize("missing", [False, True]) -def test_imageset_footer_fits_the_panel_with_room_for_a_wider_font(name, missing): - """Jede Fusszeile des Umschalters - beide Saetze, mit und ohne Fehlt-Warnung - muss - ungekuerzt auf das Panel passen, auch wenn die Schrift des Zielgeraets breiter baut - als die hier installierte. Der Fehlt-Fall ist der wichtigere der vier: er sagt, dass - der GEWAEHLTE Bildsatz nicht auf dem Pi liegt, und genau der war unsichtbar.""" - text, = (t for t, role in render._imageset_blocks( - {"imageset": name, "imageset_ok": not missing}) if role == render.ROLE_FOOTER) +def _fixed_labels(): + """Jede fest im Code stehende Beschriftung mit der Rolle, in der sie gezeichnet wird. + Bewusst VOLLSTAENDIG und nicht auf die Fusszeile beschraenkt: der Geraetetermin hat + denselben Fehler an zwei Stellen gezeigt (Fusszeile UND das Abschlussbild "Strom kann + getrennt werden", 262,1 px bei 246 px Platz). Eine Pruefung, die nur die zuerst + entdeckte Stelle abdeckt, haette die zweite wieder durchgelassen.""" + out = [] + for kind, (title, hero, details) in render.FINAL_LABELS.items(): + out.append(("FINAL/%s/titel" % kind, title, render.ROLE_HEADER)) + out.append(("FINAL/%s/hero" % kind, hero, render.ROLE_HERO)) + for i, detail in enumerate(details): + out.append(("FINAL/%s/detail%d" % (kind, i), detail, render.ROLE_DETAIL)) + for name, label in render.IMAGESET_LABELS.items(): + out.append(("BILDSATZ/%s" % name, label, render.ROLE_FOOTER)) + for name, label in render.IMAGESET_MISSING_LABELS.items(): + out.append(("BILDSATZ-FEHLT/%s" % name, label, render.ROLE_FOOTER)) + out.append(("FREIGABE", render.ARMED_LABEL, render.ROLE_HERO)) + return out + + +_ROLE_FONTS = { + render.ROLE_HEADER: (lambda: render.HEADER_FONT_PX, True), + render.ROLE_HERO: (lambda: render.HERO_FONT_PX, True), + render.ROLE_DETAIL: (lambda: render.DETAIL_FONT_PX, False), + render.ROLE_BIG: (lambda: render.BIG_FONT_PX, True), + render.ROLE_FOOTER: (lambda: render.FOOTER_FONT_PX, True), +} + + +@pytest.mark.parametrize("source,text,role", _fixed_labels(), + ids=[x[0] for x in _fixed_labels()]) +def test_fixed_labels_fit_the_panel_with_room_for_a_wider_font(source, text, role): + """Jede feste Beschriftung muss UNGEKUERZT auf das Panel passen, auch wenn die + Schrift des Zielgeraets breiter baut als die hier installierte.""" + from PIL import Image, ImageDraw, ImageFont + draw = ImageDraw.Draw(Image.new("1", (render.PANEL_WIDTH_PX, + render.PANEL_HEIGHT_PX), 255)) + size_px, bold = _ROLE_FONTS[role] + font = render._load_font(ImageFont, size_px(), bold=bold) + width_px = draw.textlength(text, font=font) max_width_px = render.PANEL_WIDTH_PX - 2 * render.TEXT_MARGIN_PX - width_px = _footer_width_px(text) assert width_px * FOOTER_WIDTH_SAFETY <= max_width_px, ( - "Fusszeile {!r} misst {:.1f} px; mit {:.0f} %% Reserve fuer eine breitere " - "Schrift bleiben nur {:.1f} px Platz".format( - text, width_px, (FOOTER_WIDTH_SAFETY - 1) * 100, max_width_px)) + "{}: {!r} misst {:.1f} px; mit {:.0f} % Reserve fuer eine breitere Schrift " + "bleiben nur {:.1f} px Platz".format( + source, text, width_px, (FOOTER_WIDTH_SAFETY - 1) * 100, max_width_px))